/release/scripts: Removed final points in UI strings and messages.
[blender.git] / release / scripts / startup / bl_operators / 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-80 compliant>
20
21 import bpy
22 from bpy.types import Operator
23 from bpy.props import StringProperty
24
25
26 class EditExternally(Operator):
27     '''Edit image in an external application'''
28     bl_idname = "image.external_edit"
29     bl_label = "Image Edit Externally"
30     bl_options = {'REGISTER'}
31
32     filepath = StringProperty(
33             name="File Path",
34             description="Path to an image file",
35             maxlen=1024,
36             )
37
38     def _editor_guess(self, context):
39         import sys
40
41         image_editor = context.user_preferences.filepaths.image_editor
42
43         # use image editor in the preferences when available.
44         if not image_editor:
45             if sys.platform[:3] == "win":
46                 image_editor = ["start"]  # not tested!
47             elif sys.platform == "darwin":
48                 image_editor = ["open"]
49             else:
50                 image_editor = ["gimp"]
51         else:
52             if sys.platform == "darwin":
53                 # blender file selector treats .app as a folder
54                 # and will include a trailing backslash, so we strip it.
55                 image_editor.rstrip('\\')
56                 image_editor = ["open", "-a", image_editor]
57             else:
58                 image_editor = [image_editor]
59
60         return image_editor
61
62     def execute(self, context):
63         import os
64         import subprocess
65
66         filepath = self.filepath
67
68         if not filepath:
69             self.report({'ERROR'}, "Image path not set")
70             return {'CANCELLED'}
71
72         filepath = os.path.normpath(bpy.path.abspath(filepath))
73
74         if not os.path.exists(filepath):
75             self.report({'ERROR'},
76                         "Image path %r not found, image may be packed or "
77                         "unsaved" % filepath)
78             return {'CANCELLED'}
79
80         cmd = self._editor_guess(context) + [filepath]
81
82         try:
83             subprocess.Popen(cmd)
84         except:
85             import traceback
86             traceback.print_exc()
87             self.report({'ERROR'},
88                         "Image editor not found, "
89                         "please specify in User Preferences > File")
90
91             return {'CANCELLED'}
92
93         return {'FINISHED'}
94
95     def invoke(self, context, event):
96         try:
97             filepath = context.space_data.image.filepath
98         except:
99             import traceback
100             traceback.print_exc()
101             self.report({'ERROR'}, "Image not found on disk")
102             return {'CANCELLED'}
103
104         self.filepath = filepath
105         self.execute(context)
106
107         return {'FINISHED'}
108
109
110 class SaveDirty(Operator):
111     """Save all modified textures"""
112     bl_idname = "image.save_dirty"
113     bl_label = "Save Dirty"
114     bl_options = {'REGISTER', 'UNDO'}
115
116     def execute(self, context):
117         unique_paths = set()
118         for image in bpy.data.images:
119             if image.is_dirty:
120                 filepath = bpy.path.abspath(image.filepath)
121                 if "\\" not in filepath and "/" not in filepath:
122                     self.report({'WARNING'}, "Invalid path: " + filepath)
123                 elif filepath in unique_paths:
124                     self.report({'WARNING'},
125                                 "Path used by more then one image: %r" %
126                                 filepath)
127                 else:
128                     unique_paths.add(filepath)
129                     image.save()
130         return {'FINISHED'}
131
132
133 class ProjectEdit(Operator):
134     """Edit a snapshot of the viewport in an external image editor"""
135     bl_idname = "image.project_edit"
136     bl_label = "Project Edit"
137     bl_options = {'REGISTER'}
138
139     _proj_hack = [""]
140
141     def execute(self, context):
142         import os
143
144         EXT = "png"  # could be made an option but for now ok
145
146         for image in bpy.data.images:
147             image.tag = True
148
149         if 'FINISHED' not in bpy.ops.paint.image_from_view():
150             return {'CANCELLED'}
151
152         image_new = None
153         for image in bpy.data.images:
154             if not image.tag:
155                 image_new = image
156                 break
157
158         if not image_new:
159             self.report({'ERROR'}, "Could not make new image")
160             return {'CANCELLED'}
161
162         filepath = os.path.basename(bpy.data.filepath)
163         filepath = os.path.splitext(filepath)[0]
164         # fixes <memory> rubbish, needs checking
165         # filepath = bpy.path.clean_name(filepath)
166
167         if bpy.data.is_saved:
168             filepath = "//" + filepath
169         else:
170             tmpdir = context.user_preferences.filepaths.temporary_directory
171             filepath = os.path.join(tmpdir, "project_edit")
172
173         obj = context.object
174
175         if obj:
176             filepath += "_" + bpy.path.clean_name(obj.name)
177
178         filepath_final = filepath + "." + EXT
179         i = 0
180
181         while os.path.exists(bpy.path.abspath(filepath_final)):
182             filepath_final = filepath + ("%.3d.%s" % (i, EXT))
183             i += 1
184
185         image_new.name = bpy.path.basename(filepath_final)
186         ProjectEdit._proj_hack[0] = image_new.name
187
188         image_new.filepath_raw = filepath_final  # TODO, filepath raw is crummy
189         image_new.file_format = 'PNG'
190         image_new.save()
191
192         try:
193             bpy.ops.image.external_edit(filepath=filepath_final)
194         except RuntimeError as re:
195             self.report({'ERROR'}, str(re))
196
197         return {'FINISHED'}
198
199
200 class ProjectApply(Operator):
201     """Project edited image back onto the object"""
202     bl_idname = "image.project_apply"
203     bl_label = "Project Apply"
204     bl_options = {'REGISTER'}
205
206     def execute(self, context):
207         image_name = ProjectEdit._proj_hack[0]  # TODO, deal with this nicer
208
209         try:
210             image = bpy.data.images[image_name]
211         except KeyError:
212             import traceback
213             traceback.print_exc()
214             self.report({'ERROR'}, "Could not find image '%s'" % image_name)
215             return {'CANCELLED'}
216
217         image.reload()
218         bpy.ops.paint.project_image(image=image_name)
219
220         return {'FINISHED'}