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 *****
21 "name": "Edit Linked Library",
22 "author": "Jason van Gumster (Fweeb), Bassam Kurdali, Pablo Vazquez",
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",
34 from bpy
.app
.handlers
import 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]]
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(
69 description
="Save the current file before opening the linked library",
71 use_instance
= bpy
.props
.BoolProperty(
72 name
="New Blender Instance",
73 description
="Open in a new Blender instance",
77 def poll(cls
, context
):
78 return settings
["original_file"] == "" and context
.active_object
is not None and (
79 (context
.active_object
.dupli_group
and
80 context
.active_object
.dupli_group
.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
.dupli_group
and target
.dupli_group
.library
:
91 targetpath
= target
.dupli_group
.library
.filepath
92 settings
["linked_objects"].extend({ob
.name
for ob
in target
.dupli_group
.objects
})
94 targetpath
= target
.library
.filepath
95 settings
["linked_objects"].append(target
.name
)
98 targetpath
= target
.library
.filepath
99 settings
["linked_objects"].append(target
.name
)
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")
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
:
117 subprocess
.Popen([bpy
.app
.binary_path
, settings
["linked_file"]])
119 print("Error on the new Blender instance")
121 traceback
.print_exc()
123 bpy
.ops
.wm
.open_mainfile(filepath
=settings
["linked_file"])
125 print("Opened linked file!")
127 self
.report({'WARNING'}, target
.name
+ " is not linked")
128 print(target
.name
+ " is not linked")
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(
140 description
="Save the current file before opening original file",
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!")
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"
170 def poll(cls
, context
):
171 return (context
.active_object
is not None) or (settings
["original_file"] != "")
173 def draw(self
, context
):
175 scene
= context
.scene
176 icon
= "OUTLINER_DATA_" + context
.active_object
.type
180 if context
.active_object
.proxy
:
181 target
= context
.active_object
.proxy
183 target
= context
.active_object
.dupli_group
185 if settings
["original_file"] == "" 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
)
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
)
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
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
.dupli_group
.name
,
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
.dupli_group
.library
.filepath
)
233 props
= layout
.operator("wm.return_to_original", icon
="LOOP_BACK")
234 props
.use_autosave
= scene
.use_autosave
236 layout
.prop(scene
, "use_autosave")
239 layout
.label(text
="%s is not linked" % context
.active_object
.name
,
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(
255 description
="Save the current file before opening a linked file",
257 bpy
.types
.Scene
.use_instance
= bpy
.props
.BoolProperty(
258 name
="New Blender Instance",
259 description
="Open in a new Blender instance",
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)
268 addon_keymaps
.append((km
, kmi
))
269 kmi
= km
.keymap_items
.new("wm.return_to_original", 'NUMPAD_SLASH', 'PRESS', shift
=True)
271 addon_keymaps
.append((km
, kmi
))
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
284 for km
, kmi
in addon_keymaps
:
285 km
.keymap_items
.remove(kmi
)
286 addon_keymaps
.clear()
289 if __name__
== "__main__":