Fix T58881: FBX error exporting tangent space
[blender-addons.git] / object_edit_linked.py
blob28a500965dd77e88ecb71ab9483e281373eae5f4
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",
23 "version": (0, 8, 1),
24 "blender": (2, 74, 0),
25 "location": "View3D > Toolshelf > Edit Linked Library",
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",
33 import bpy
34 from bpy.app.handlers import persistent
35 import os
37 settings = {
38 "original_file": "",
39 "linked_file": "",
40 "linked_objects": [],
44 @persistent
45 def linked_file_check(context):
46 if settings["linked_file"] != "":
47 if os.path.samefile(settings["linked_file"], bpy.data.filepath):
48 print("Editing a linked library.")
49 bpy.ops.object.select_all(action='DESELECT')
50 for ob_name in settings["linked_objects"]:
51 bpy.data.objects[ob_name].select = True # XXX Assumes selected object is in the active scene
52 if len(settings["linked_objects"]) == 1:
53 bpy.context.scene.objects.active = bpy.data.objects[settings["linked_objects"][0]]
54 else:
55 # For some reason, the linked editing session ended
56 # (failed to find a file or opened a different file
57 # before returning to the originating .blend)
58 settings["original_file"] = ""
59 settings["linked_file"] = ""
62 class EditLinked(bpy.types.Operator):
63 """Edit Linked Library"""
64 bl_idname = "object.edit_linked"
65 bl_label = "Edit Linked Library"
67 use_autosave = bpy.props.BoolProperty(
68 name="Autosave",
69 description="Save the current file before opening the linked library",
70 default=True)
71 use_instance = bpy.props.BoolProperty(
72 name="New Blender Instance",
73 description="Open in a new Blender instance",
74 default=False)
76 @classmethod
77 def poll(cls, context):
78 return settings["original_file"] == "" and context.active_object is not None and (
79 (context.active_object.instance_collection and
80 context.active_object.instance_collection.library is not None) or
81 (context.active_object.proxy and
82 context.active_object.proxy.library is not None) or
83 context.active_object.library is not None)
84 #return context.active_object is not None
86 def execute(self, context):
87 #print(bpy.context.active_object.library)
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 print(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 print("Error on the new Blender instance")
120 import traceback
121 traceback.print_exc()
122 else:
123 bpy.ops.wm.open_mainfile(filepath=settings["linked_file"])
125 print("Opened linked file!")
126 else:
127 self.report({'WARNING'}, target.name + " is not linked")
128 print(target.name + " is not linked")
130 return {'FINISHED'}
133 class 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):
145 return (settings["original_file"] != "")
147 def execute(self, 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 print("Back to the original!")
156 return {'FINISHED'}
159 # UI
160 # TODO:Add operators to the File menu?
161 # Hide the entire panel for non-linked objects?
162 class PanelLinkedEdit(bpy.types.Panel):
163 bl_label = "Edit Linked Library"
164 bl_space_type = "VIEW_3D"
165 bl_region_type = "TOOLS"
166 bl_category = "Relations"
167 bl_context = "objectmode"
169 @classmethod
170 def poll(cls, context):
171 return (context.active_object is not None) or (settings["original_file"] != "")
173 def draw(self, context):
174 layout = self.layout
175 scene = context.scene
176 icon = "OUTLINER_DATA_" + context.active_object.type
178 target = None
180 if context.active_object.proxy:
181 target = context.active_object.proxy
182 else:
183 target = context.active_object.instance_collection
185 if settings["original_file"] == "" and (
186 (target and
187 target.library is not None) or
188 context.active_object.library is not None):
190 if (target is not None):
191 props = layout.operator("object.edit_linked", icon="LINK_BLEND",
192 text="Edit Library: %s" % target.name)
193 else:
194 props = layout.operator("object.edit_linked", icon="LINK_BLEND",
195 text="Edit Library: %s" % context.active_object.name)
196 props.use_autosave = scene.use_autosave
197 props.use_instance = scene.use_instance
199 layout.prop(scene, "use_autosave")
200 layout.prop(scene, "use_instance")
202 if (target is not None):
203 layout.label(text="Path: %s" %
204 target.library.filepath)
205 else:
206 layout.label(text="Path: %s" %
207 context.active_object.library.filepath)
209 elif settings["original_file"] != "":
211 if scene.use_instance:
212 layout.operator("wm.return_to_original",
213 text="Reload Current File",
214 icon="FILE_REFRESH").use_autosave = False
216 layout.separator()
218 #XXX - This is for nested linked assets... but it only works
219 # when launching a new Blender instance. Nested links don't
220 # currently work when using a single instance of Blender.
221 props = layout.operator("object.edit_linked",
222 text="Edit Library: %s" % context.active_object.instance_collection.name,
223 icon="LINK_BLEND")
224 props.use_autosave = scene.use_autosave
225 props.use_instance = scene.use_instance
226 layout.prop(scene, "use_autosave")
227 layout.prop(scene, "use_instance")
229 layout.label(text="Path: %s" %
230 context.active_object.instance_collection.library.filepath)
232 else:
233 props = layout.operator("wm.return_to_original", icon="LOOP_BACK")
234 props.use_autosave = scene.use_autosave
236 layout.prop(scene, "use_autosave")
238 else:
239 layout.label(text="%s is not linked" % context.active_object.name,
240 icon=icon)
243 addon_keymaps = []
246 def register():
247 bpy.app.handlers.load_post.append(linked_file_check)
248 bpy.utils.register_class(EditLinked)
249 bpy.utils.register_class(ReturnToOriginal)
250 bpy.utils.register_class(PanelLinkedEdit)
252 # Is there a better place to store this properties?
253 bpy.types.Scene.use_autosave = bpy.props.BoolProperty(
254 name="Autosave",
255 description="Save the current file before opening a linked file",
256 default=True)
257 bpy.types.Scene.use_instance = bpy.props.BoolProperty(
258 name="New Blender Instance",
259 description="Open in a new Blender instance",
260 default=False)
262 # Keymapping (deactivated by default; activated when a library object is selected)
263 kc = bpy.context.window_manager.keyconfigs.addon
264 if kc: # don't register keymaps from command line
265 km = kc.keymaps.new(name="3D View", space_type='VIEW_3D')
266 kmi = km.keymap_items.new("object.edit_linked", 'NUMPAD_SLASH', 'PRESS', shift=True)
267 kmi.active = True
268 addon_keymaps.append((km, kmi))
269 kmi = km.keymap_items.new("wm.return_to_original", 'NUMPAD_SLASH', 'PRESS', shift=True)
270 kmi.active = True
271 addon_keymaps.append((km, kmi))
274 def unregister():
275 bpy.utils.unregister_class(EditLinked)
276 bpy.utils.unregister_class(ReturnToOriginal)
277 bpy.utils.unregister_class(PanelLinkedEdit)
278 bpy.app.handlers.load_post.remove(linked_file_check)
280 del bpy.types.Scene.use_autosave
281 del bpy.types.Scene.use_instance
283 # handle the keymap
284 for km, kmi in addon_keymaps:
285 km.keymap_items.remove(kmi)
286 addon_keymaps.clear()
289 if __name__ == "__main__":
290 register()