Rigify: store advanced options in armature instead of window manager.
[blender-addons.git] / object_edit_linked.py
blob2c44fd9070624a7e63d251d71dcbe8bc6b0c5d4a
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 "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
28 "Scripts/Object/Edit_Linked_Library",
29 "category": "Object",
32 import bpy
33 import logging
34 import os
36 from bpy.app.handlers import persistent
38 logger = logging.getLogger('object_edit_linked')
40 settings = {
41 "original_file": "",
42 "linked_file": "",
43 "linked_objects": [],
47 @persistent
48 def linked_file_check(context: bpy.context):
49 if settings["linked_file"] != "":
50 if os.path.samefile(settings["linked_file"], bpy.data.filepath):
51 logger.info("Editing a linked library.")
52 bpy.ops.object.select_all(action='DESELECT')
53 for ob_name in settings["linked_objects"]:
54 bpy.data.objects[ob_name].select_set(True) # XXX Assumes selected object is in the active scene
55 if len(settings["linked_objects"]) == 1:
56 context.view_layer.objects.active = bpy.data.objects[settings["linked_objects"][0]]
57 else:
58 # For some reason, the linked editing session ended
59 # (failed to find a file or opened a different file
60 # before returning to the originating .blend)
61 settings["original_file"] = ""
62 settings["linked_file"] = ""
65 class OBJECT_OT_EditLinked(bpy.types.Operator):
66 """Edit Linked Library"""
67 bl_idname = "object.edit_linked"
68 bl_label = "Edit Linked Library"
70 use_autosave: bpy.props.BoolProperty(
71 name="Autosave",
72 description="Save the current file before opening the linked library",
73 default=True)
74 use_instance: bpy.props.BoolProperty(
75 name="New Blender Instance",
76 description="Open in a new Blender instance",
77 default=False)
79 @classmethod
80 def poll(cls, context: bpy.context):
81 return settings["original_file"] == "" and context.active_object is not None and (
82 (context.active_object.instance_collection and
83 context.active_object.instance_collection.library is not None) or
84 (context.active_object.proxy and
85 context.active_object.proxy.library is not None) or
86 context.active_object.library is not None)
88 def execute(self, context: bpy.context):
89 target = context.active_object
91 if target.instance_collection and target.instance_collection.library:
92 targetpath = target.instance_collection.library.filepath
93 settings["linked_objects"].extend({ob.name for ob in target.instance_collection.objects})
94 elif target.library:
95 targetpath = target.library.filepath
96 settings["linked_objects"].append(target.name)
97 elif target.proxy:
98 target = target.proxy
99 targetpath = target.library.filepath
100 settings["linked_objects"].append(target.name)
102 if targetpath:
103 logger.debug(target.name + " is linked to " + targetpath)
105 if self.use_autosave:
106 if not bpy.data.filepath:
107 # File is not saved on disk, better to abort!
108 self.report({'ERROR'}, "Current file does not exist on disk, we cannot autosave it, aborting")
109 return {'CANCELLED'}
110 bpy.ops.wm.save_mainfile()
112 settings["original_file"] = bpy.data.filepath
113 settings["linked_file"] = bpy.path.abspath(targetpath)
115 if self.use_instance:
116 import subprocess
117 try:
118 subprocess.Popen([bpy.app.binary_path, settings["linked_file"]])
119 except:
120 logger.error("Error on the new Blender instance")
121 import traceback
122 logger.error(traceback.print_exc())
123 else:
124 bpy.ops.wm.open_mainfile(filepath=settings["linked_file"])
126 logger.info("Opened linked file!")
127 else:
128 self.report({'WARNING'}, target.name + " is not linked")
129 logger.warning(target.name + " is not linked")
131 return {'FINISHED'}
134 class WM_OT_ReturnToOriginal(bpy.types.Operator):
135 """Load the original file"""
136 bl_idname = "wm.return_to_original"
137 bl_label = "Return to Original File"
139 use_autosave: bpy.props.BoolProperty(
140 name="Autosave",
141 description="Save the current file before opening original file",
142 default=True)
144 @classmethod
145 def poll(cls, context: bpy.context):
146 return (settings["original_file"] != "")
148 def execute(self, context: bpy.context):
149 if self.use_autosave:
150 bpy.ops.wm.save_mainfile()
152 bpy.ops.wm.open_mainfile(filepath=settings["original_file"])
154 settings["original_file"] = ""
155 settings["linked_objects"] = []
156 logger.info("Back to the original!")
157 return {'FINISHED'}
160 class VIEW3D_PT_PanelLinkedEdit(bpy.types.Panel):
161 bl_label = "Edit Linked Library"
162 bl_space_type = "VIEW_3D"
163 bl_region_type = 'UI'
164 bl_category = "Item"
165 bl_context = 'objectmode'
166 bl_options = {'DEFAULT_CLOSED'}
168 @classmethod
169 def poll(cls, context: bpy.context):
170 return (context.active_object is not None) or (settings["original_file"] != "")
172 def draw_common(self, scene, layout, props):
173 props.use_autosave = scene.use_autosave
174 props.use_instance = scene.use_instance
176 layout.prop(scene, "use_autosave")
177 # layout.prop(scene, "use_instance")
179 def draw(self, context: bpy.context):
180 scene = context.scene
181 layout = self.layout
182 layout.use_property_split = True
183 layout.use_property_decorate = False
184 icon = "OUTLINER_DATA_" + context.active_object.type.replace("LIGHT_PROBE", "LIGHTPROBE")
186 target = None
188 if context.active_object.proxy:
189 target = context.active_object.proxy
190 else:
191 target = context.active_object.instance_collection
193 if settings["original_file"] == "" and (
194 (target and
195 target.library is not None) or
196 context.active_object.library is not None):
198 if (target is not None):
199 props = layout.operator("object.edit_linked", icon="LINK_BLEND",
200 text="Edit Library: %s" % target.name)
201 else:
202 props = layout.operator("object.edit_linked", icon="LINK_BLEND",
203 text="Edit Library: %s" % context.active_object.name)
205 self.draw_common(scene, layout, props)
207 if (target is not None):
208 layout.label(text="Path: %s" %
209 target.library.filepath)
210 else:
211 layout.label(text="Path: %s" %
212 context.active_object.library.filepath)
214 elif settings["original_file"] != "":
216 if scene.use_instance:
217 layout.operator("wm.return_to_original",
218 text="Reload Current File",
219 icon="FILE_REFRESH").use_autosave = False
221 layout.separator()
223 # XXX - This is for nested linked assets... but it only works
224 # when launching a new Blender instance. Nested links don't
225 # currently work when using a single instance of Blender.
226 props = layout.operator("object.edit_linked",
227 text="Edit Library: %s" % context.active_object.instance_collection.name,
228 icon="LINK_BLEND")
230 self.draw_common(scene, layout, props)
232 layout.label(text="Path: %s" %
233 context.active_object.instance_collection.library.filepath)
235 else:
236 props = layout.operator("wm.return_to_original", icon="LOOP_BACK")
237 props.use_autosave = scene.use_autosave
239 layout.prop(scene, "use_autosave")
241 else:
242 layout.label(text="%s is not linked" % context.active_object.name,
243 icon=icon)
246 class TOPBAR_MT_edit_linked_submenu(bpy.types.Menu):
247 bl_label = 'Edit Linked Library'
249 def draw(self, context):
250 self.layout.separator()
251 self.layout.operator(OBJECT_OT_EditLinked.bl_idname)
252 self.layout.operator(WM_OT_ReturnToOriginal.bl_idname)
255 addon_keymaps = []
256 classes = (
257 OBJECT_OT_EditLinked,
258 WM_OT_ReturnToOriginal,
259 VIEW3D_PT_PanelLinkedEdit,
260 TOPBAR_MT_edit_linked_submenu
264 def register():
265 bpy.app.handlers.load_post.append(linked_file_check)
267 for c in classes:
268 bpy.utils.register_class(c)
270 bpy.types.Scene.use_autosave = bpy.props.BoolProperty(
271 name="Autosave",
272 description="Save the current file before opening a linked file",
273 default=True)
275 bpy.types.Scene.use_instance = bpy.props.BoolProperty(
276 name="New Blender Instance",
277 description="Open in a new Blender instance",
278 default=False)
280 # add the function to the file menu
281 bpy.types.TOPBAR_MT_file_external_data.append(TOPBAR_MT_edit_linked_submenu.draw)
286 def unregister():
288 bpy.app.handlers.load_post.remove(linked_file_check)
289 bpy.types.TOPBAR_MT_file_external_data.remove(TOPBAR_MT_edit_linked_submenu)
291 del bpy.types.Scene.use_autosave
292 del bpy.types.Scene.use_instance
295 for c in reversed(classes):
296 bpy.utils.unregister_class(c)
299 if __name__ == "__main__":
300 register()