Merge with trunk, revision 28528 - 28976.
[blender-staging.git] / release / scripts / op / image.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20
21 import bpy
22 from bpy.props import StringProperty
23
24
25 class EditExternally(bpy.types.Operator):
26     '''Edit image in an external application'''
27     bl_idname = "image.external_edit"
28     bl_label = "Image Edit Externally"
29     bl_options = {'REGISTER'}
30
31     path = StringProperty(name="File Path", description="Path to an image file", maxlen=1024, default="")
32
33     def _editor_guess(self, context):
34         import platform
35         system = platform.system()
36
37         image_editor = context.user_preferences.filepaths.image_editor
38
39         # use image editor in the preferences when available.
40         if not image_editor:
41             if system == 'Windows':
42                 image_editor = ["start"] # not tested!
43             elif system == 'Darwin':
44                 image_editor = ["open"]
45             else:
46                 image_editor = ["gimp"]
47         else:
48             if system == 'Darwin':
49                 # blender file selector treats .app as a folder
50                 # and will include a trailing backslash, so we strip it.
51                 image_editor.rstrip('\\')
52                 image_editor = ["open", "-a", image_editor]
53             else:
54                 image_editor = [image_editor]
55
56         return image_editor
57
58     def execute(self, context):
59         import subprocess
60         path = self.properties.path
61         image_editor = self._editor_guess(context)
62
63         cmd = []
64         cmd.extend(image_editor)
65         cmd.append(bpy.utils.expandpath(path))
66
67         subprocess.Popen(cmd)
68
69         return {'FINISHED'}
70
71     def invoke(self, context, event):
72         try:
73             path = context.space_data.image.filename
74         except:
75             self.report({'ERROR'}, "Image not found on disk")
76             return {'CANCELLED'}
77
78         self.properties.path = path
79         self.execute(context)
80
81         return {'FINISHED'}
82
83
84 class SaveDirty(bpy.types.Operator):
85     '''Select object matching a naming pattern'''
86     bl_idname = "image.save_dirty"
87     bl_label = "Save Dirty"
88     bl_options = {'REGISTER', 'UNDO'}
89
90     def execute(self, context):
91         unique_paths = set()
92         for image in bpy.data.images:
93             if image.dirty:
94                 path = bpy.utils.expandpath(image.filename)
95                 if "\\" not in path and "/" not in path:
96                     self.report({'WARNING'}, "Invalid path: " + path)
97                 elif path in unique_paths:
98                     self.report({'WARNING'}, "Path used by more then one image: " + path)
99                 else:
100                     unique_paths.add(path)
101                     image.save()
102         return {'FINISHED'}
103
104
105 class ProjectEdit(bpy.types.Operator):
106     '''Select object matching a naming pattern'''
107     bl_idname = "image.project_edit"
108     bl_label = "Project Edit"
109     bl_options = {'REGISTER'}
110
111     _proj_hack = [""]
112
113     def execute(self, context):
114         import os
115         import subprocess
116
117         EXT = "png" # could be made an option but for now ok
118
119         for image in bpy.data.images:
120             image.tag = True
121
122         bpy.ops.paint.image_from_view()
123
124         image_new = None
125         for image in bpy.data.images:
126             if not image.tag:
127                 image_new = image
128                 break
129
130         if not image_new:
131             self.report({'ERROR'}, "Could not make new image")
132             return {'CANCELLED'}
133
134         filename = os.path.basename(bpy.data.filename)
135         filename = os.path.splitext(filename)[0]
136         # filename = bpy.utils.clean_name(filename) # fixes <memory> rubbish, needs checking
137
138         if filename.startswith(".") or filename == "":
139             # TODO, have a way to check if the file is saved, assume .B25.blend
140             tmpdir = context.user_preferences.filepaths.temporary_directory
141             filename = os.path.join(tmpdir, "project_edit")
142         else:
143             filename = "//" + filename
144
145         obj = context.object
146
147         if obj:
148             filename += "_" + bpy.utils.clean_name(obj.name)
149
150         filename_final = filename + "." + EXT
151         i = 0
152
153         while os.path.exists(bpy.utils.expandpath(filename_final)):
154             filename_final = filename + ("%.3d.%s" % (i, EXT))
155             i += 1
156
157         image_new.name = os.path.basename(filename_final)
158         ProjectEdit._proj_hack[0] = image_new.name
159
160         image_new.filename_raw = filename_final # TODO, filename raw is crummy
161         image_new.file_format = 'PNG'
162         image_new.save()
163
164         bpy.ops.image.external_edit(path=filename_final)
165
166         return {'FINISHED'}
167
168
169 class ProjectApply(bpy.types.Operator):
170     '''Select object matching a naming pattern'''
171     bl_idname = "image.project_apply"
172     bl_label = "Project Apply"
173     bl_options = {'REGISTER'}
174
175     def execute(self, context):
176         image_name = ProjectEdit._proj_hack[0] # TODO, deal with this nicer
177
178         try:
179             image = bpy.data.images[image_name]
180         except KeyError:
181             self.report({'ERROR'}, "Could not find image '%s'" % image_name)
182             return {'CANCELLED'}
183
184         image.reload()
185         bpy.ops.paint.project_image(image=image_name)
186
187         return {'FINISHED'}
188
189 classes = [
190     EditExternally,
191     SaveDirty,
192     ProjectEdit,
193     ProjectApply]
194
195
196 def register():
197     register = bpy.types.register
198     for cls in classes:
199         register(cls)
200
201
202 def unregister():
203     unregister = bpy.types.unregister
204     for cls in classes:
205         unregister(cls)
206
207 if __name__ == "__main__":
208     register()