Cleanup: line length
[blender.git] / release / scripts / startup / bl_operators / file.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.types import Operator
23 from bpy.props import (
24     BoolProperty,
25     CollectionProperty,
26     StringProperty,
27 )
28
29 # ########## Datablock previews... ##########
30
31
32 class WM_OT_previews_batch_generate(Operator):
33     """Generate selected .blend file's previews"""
34     bl_idname = "wm.previews_batch_generate"
35     bl_label = "Batch-Generate Previews"
36     bl_options = {'REGISTER'}
37
38     # -----------
39     # File props.
40     files: CollectionProperty(
41         type=bpy.types.OperatorFileListElement,
42         options={'HIDDEN', 'SKIP_SAVE'},
43     )
44
45     directory: StringProperty(
46         maxlen=1024,
47         subtype='FILE_PATH',
48         options={'HIDDEN', 'SKIP_SAVE'},
49     )
50
51     # Show only images/videos, and directories!
52     filter_blender: BoolProperty(
53         default=True,
54         options={'HIDDEN', 'SKIP_SAVE'},
55     )
56     filter_folder: BoolProperty(
57         default=True,
58         options={'HIDDEN', 'SKIP_SAVE'},
59     )
60
61     # -----------
62     # Own props.
63     use_scenes: BoolProperty(
64         default=True,
65         name="Scenes",
66         description="Generate scenes' previews",
67     )
68     use_collections: BoolProperty(
69         default=True,
70         name="Collections",
71         description="Generate collections' previews",
72     )
73     use_objects: BoolProperty(
74         default=True,
75         name="Objects",
76         description="Generate objects' previews",
77     )
78     use_intern_data: BoolProperty(
79         default=True,
80         name="Mat/Tex/...",
81         description="Generate 'internal' previews (materials, textures, images, etc.)",
82     )
83
84     use_trusted: BoolProperty(
85         default=False,
86         name="Trusted Blend Files",
87         description="Enable python evaluation for selected files",
88     )
89     use_backups: BoolProperty(
90         default=True,
91         name="Save Backups",
92         description="Keep a backup (.blend1) version of the files when saving with generated previews",
93     )
94
95     def invoke(self, context, event):
96         context.window_manager.fileselect_add(self)
97         return {'RUNNING_MODAL'}
98
99     def execute(self, context):
100         import os
101         import subprocess
102         from bl_previews_utils import bl_previews_render as preview_render
103
104         context.window_manager.progress_begin(0, len(self.files))
105         context.window_manager.progress_update(0)
106         for i, fn in enumerate(self.files):
107             blen_path = os.path.join(self.directory, fn.name)
108             cmd = [
109                 bpy.app.binary_path,
110                 "--background",
111                 "--factory-startup",
112                 "-noaudio",
113             ]
114             if self.use_trusted:
115                 cmd.append("--enable-autoexec")
116             cmd.extend([
117                 blen_path,
118                 "--python",
119                 os.path.join(os.path.dirname(preview_render.__file__), "bl_previews_render.py"),
120                 "--",
121             ])
122             if not self.use_scenes:
123                 cmd.append('--no_scenes')
124             if not self.use_collections:
125                 cmd.append('--no_collections')
126             if not self.use_objects:
127                 cmd.append('--no_objects')
128             if not self.use_intern_data:
129                 cmd.append('--no_data_intern')
130             if not self.use_backups:
131                 cmd.append("--no_backups")
132             if subprocess.call(cmd):
133                 self.report({'ERROR'}, "Previews generation process failed for file '%s'!" % blen_path)
134                 context.window_manager.progress_end()
135                 return {'CANCELLED'}
136             context.window_manager.progress_update(i + 1)
137         context.window_manager.progress_end()
138
139         return {'FINISHED'}
140
141
142 class WM_OT_previews_batch_clear(Operator):
143     """Clear selected .blend file's previews"""
144     bl_idname = "wm.previews_batch_clear"
145     bl_label = "Batch-Clear Previews"
146     bl_options = {'REGISTER'}
147
148     # -----------
149     # File props.
150     files: CollectionProperty(
151         type=bpy.types.OperatorFileListElement,
152         options={'HIDDEN', 'SKIP_SAVE'},
153     )
154
155     directory: StringProperty(
156         maxlen=1024,
157         subtype='FILE_PATH',
158         options={'HIDDEN', 'SKIP_SAVE'},
159     )
160
161     # Show only images/videos, and directories!
162     filter_blender: BoolProperty(
163         default=True,
164         options={'HIDDEN', 'SKIP_SAVE'},
165     )
166     filter_folder: BoolProperty(
167         default=True,
168         options={'HIDDEN', 'SKIP_SAVE'},
169     )
170
171     # -----------
172     # Own props.
173     use_scenes: BoolProperty(
174         default=True,
175         name="Scenes",
176         description="Clear scenes' previews",
177     )
178     use_collections: BoolProperty(
179         default=True,
180         name="Collections",
181         description="Clear collections' previews",
182     )
183     use_objects: BoolProperty(
184         default=True,
185         name="Objects",
186         description="Clear objects' previews",
187     )
188     use_intern_data: BoolProperty(
189         default=True,
190         name="Mat/Tex/...",
191         description="Clear 'internal' previews (materials, textures, images, etc.)",
192     )
193
194     use_trusted: BoolProperty(
195         default=False,
196         name="Trusted Blend Files",
197         description="Enable python evaluation for selected files",
198     )
199     use_backups: BoolProperty(
200         default=True,
201         name="Save Backups",
202         description="Keep a backup (.blend1) version of the files when saving with cleared previews",
203     )
204
205     def invoke(self, context, event):
206         context.window_manager.fileselect_add(self)
207         return {'RUNNING_MODAL'}
208
209     def execute(self, context):
210         import os
211         import subprocess
212         from bl_previews_utils import bl_previews_render as preview_render
213
214         context.window_manager.progress_begin(0, len(self.files))
215         context.window_manager.progress_update(0)
216         for i, fn in enumerate(self.files):
217             blen_path = os.path.join(self.directory, fn.name)
218             cmd = [
219                 bpy.app.binary_path,
220                 "--background",
221                 "--factory-startup",
222                 "-noaudio",
223             ]
224             if self.use_trusted:
225                 cmd.append("--enable-autoexec")
226             cmd.extend([
227                 blen_path,
228                 "--python",
229                 os.path.join(os.path.dirname(preview_render.__file__), "bl_previews_render.py"),
230                 "--",
231                 "--clear",
232             ])
233             if not self.use_scenes:
234                 cmd.append('--no_scenes')
235             if not self.use_collections:
236                 cmd.append('--no_collections')
237             if not self.use_objects:
238                 cmd.append('--no_objects')
239             if not self.use_intern_data:
240                 cmd.append('--no_data_intern')
241             if not self.use_backups:
242                 cmd.append("--no_backups")
243             if subprocess.call(cmd):
244                 self.report({'ERROR'}, "Previews clear process failed for file '%s'!" % blen_path)
245                 context.window_manager.progress_end()
246                 return {'CANCELLED'}
247             context.window_manager.progress_update(i + 1)
248         context.window_manager.progress_end()
249
250         return {'FINISHED'}
251
252
253 class WM_OT_blend_strings_utf8_validate(Operator):
254     """Check and fix all strings in current .blend file to be valid UTF-8 Unicode """ \
255     """(needed for some old, 2.4x area files)"""
256     bl_idname = "wm.blend_strings_utf8_validate"
257     bl_label = "Validate .blend strings"
258     bl_options = {'REGISTER'}
259
260     def validate_strings(self, item, done_items):
261         if item is None:
262             return False
263
264         if item in done_items:
265             return False
266         done_items.add(item)
267
268         if getattr(item, "library", None) is not None:
269             return False  # No point in checking library data, we cannot fix it anyway...
270
271         changed = False
272         for prop in item.bl_rna.properties:
273             if prop.identifier in {"bl_rna", "rna_type"}:
274                 continue  # Or we'd recurse 'till Hell freezes.
275             if prop.is_readonly:
276                 continue
277             if prop.type == 'STRING':
278                 val_bytes = item.path_resolve(prop.identifier, False).as_bytes()
279                 val_utf8 = val_bytes.decode('utf-8', 'replace')
280                 val_bytes_valid = val_utf8.encode('utf-8')
281                 if val_bytes_valid != val_bytes:
282                     print("found bad utf8 encoded string %r, fixing to %r (%r)..."
283                           "" % (val_bytes, val_bytes_valid, val_utf8))
284                     setattr(item, prop.identifier, val_utf8)
285                     changed = True
286             elif prop.type == 'POINTER':
287                 it = getattr(item, prop.identifier)
288                 changed |= self.validate_strings(it, done_items)
289             elif prop.type == 'COLLECTION':
290                 for it in getattr(item, prop.identifier):
291                     changed |= self.validate_strings(it, done_items)
292         return changed
293
294     def execute(self, context):
295         changed = False
296         done_items = set()
297         for prop in bpy.data.bl_rna.properties:
298             if prop.type == 'COLLECTION':
299                 for it in getattr(bpy.data, prop.identifier):
300                     changed |= self.validate_strings(it, done_items)
301         if changed:
302             self.report({'WARNING'},
303                         "Some strings were fixed, don't forget to save the .blend file to keep those changes")
304         return {'FINISHED'}
305
306
307 classes = (
308     WM_OT_previews_batch_clear,
309     WM_OT_previews_batch_generate,
310     WM_OT_blend_strings_utf8_validate,
311 )