Merge branch 'master' into blender2.8
[blender.git] / release / scripts / startup / bl_ui / space_userpref.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 import bpy
21 from bpy.types import (
22     Header,
23     Menu,
24     Panel,
25 )
26 from bpy.app.translations import pgettext_iface as iface_
27 from bpy.app.translations import contexts as i18n_contexts
28
29
30 def opengl_lamp_buttons(column, lamp):
31     split = column.row()
32
33     split.prop(lamp, "use", text="", icon='OUTLINER_OB_LAMP' if lamp.use else 'LAMP_DATA')
34
35     col = split.column()
36     col.active = lamp.use
37     row = col.row()
38     row.label(text="Diffuse:")
39     row.prop(lamp, "diffuse_color", text="")
40     row = col.row()
41     row.label(text="Specular:")
42     row.prop(lamp, "specular_color", text="")
43
44     col = split.column()
45     col.active = lamp.use
46     col.prop(lamp, "direction", text="")
47
48
49 class USERPREF_HT_header(Header):
50     bl_space_type = 'USER_PREFERENCES'
51
52     def draw(self, context):
53         layout = self.layout
54
55         layout.template_header()
56
57         userpref = context.user_preferences
58
59         layout.operator_context = 'EXEC_AREA'
60         layout.operator("wm.save_userpref")
61
62         layout.operator_context = 'INVOKE_DEFAULT'
63
64         if userpref.active_section == 'INTERFACE':
65             layout.operator("wm.save_workspace_file")
66         elif userpref.active_section == 'INPUT':
67             layout.operator("wm.keyconfig_import")
68             layout.operator("wm.keyconfig_export")
69         elif userpref.active_section == 'ADDONS':
70             layout.operator("wm.addon_install", icon='FILESEL')
71             layout.operator("wm.addon_refresh", icon='FILE_REFRESH')
72             layout.menu("USERPREF_MT_addons_online_resources")
73         elif userpref.active_section == 'THEMES':
74             layout.operator("ui.reset_default_theme")
75             layout.operator("wm.theme_install")
76
77
78 class USERPREF_PT_tabs(Panel):
79     bl_label = ""
80     bl_space_type = 'USER_PREFERENCES'
81     bl_region_type = 'WINDOW'
82     bl_options = {'HIDE_HEADER'}
83
84     def draw(self, context):
85         layout = self.layout
86
87         userpref = context.user_preferences
88
89         layout.row().prop(userpref, "active_section", expand=True)
90
91
92 class USERPREF_MT_interaction_presets(Menu):
93     bl_label = "Presets"
94     preset_subdir = "interaction"
95     preset_operator = "script.execute_preset"
96     draw = Menu.draw_preset
97
98
99 class USERPREF_MT_app_templates(Menu):
100     bl_label = "Application Templates"
101     preset_subdir = "app_templates"
102
103     def draw_ex(self, context, *, use_splash=False, use_default=False, use_install=False):
104         import os
105
106         layout = self.layout
107
108         # now draw the presets
109         layout.operator_context = 'EXEC_DEFAULT'
110
111         if use_default:
112             props = layout.operator("wm.read_homefile", text="Default")
113             props.use_splash = True
114             props.app_template = ""
115             layout.separator()
116
117         template_paths = bpy.utils.app_template_paths()
118
119         # expand template paths
120         app_templates = []
121         for path in template_paths:
122             for d in os.listdir(path):
123                 if d.startswith(("__", ".")):
124                     continue
125                 template = os.path.join(path, d)
126                 if os.path.isdir(template):
127                     # template_paths_expand.append(template)
128                     app_templates.append(d)
129
130         for d in sorted(app_templates):
131             props = layout.operator(
132                 "wm.read_homefile",
133                 text=bpy.path.display_name(d),
134             )
135             props.use_splash = True
136             props.app_template = d
137
138         if use_install:
139             layout.separator()
140             layout.operator_context = 'INVOKE_DEFAULT'
141             props = layout.operator("wm.app_template_install")
142
143
144     def draw(self, context):
145         self.draw_ex(context, use_splash=False, use_default=True, use_install=True)
146
147
148 class USERPREF_MT_templates_splash(Menu):
149     bl_label = "Startup Templates"
150     preset_subdir = "templates"
151
152     def draw(self, context):
153         USERPREF_MT_app_templates.draw_ex(self, context, use_splash=True, use_default=True)
154
155
156 class USERPREF_MT_appconfigs(Menu):
157     bl_label = "AppPresets"
158     preset_subdir = "keyconfig"
159     preset_operator = "wm.appconfig_activate"
160
161     def draw(self, context):
162         self.layout.operator("wm.appconfig_default", text="Blender (default)")
163
164         # now draw the presets
165         Menu.draw_preset(self, context)
166
167
168 class USERPREF_MT_splash(Menu):
169     bl_label = "Splash"
170
171     def draw(self, context):
172         layout = self.layout
173
174         split = layout.split()
175         row = split.row()
176
177         if any(bpy.utils.app_template_paths()):
178             row.label("Template:")
179             template = context.user_preferences.app_template
180             row.menu(
181                 "USERPREF_MT_templates_splash",
182                 text=bpy.path.display_name(template) if template else "Default",
183             )
184         else:
185             row.label("")
186
187         row = split.row()
188         row.label("Interaction:")
189
190         text = bpy.path.display_name(context.window_manager.keyconfigs.active.name)
191         if not text:
192             text = "Blender (default)"
193         row.menu("USERPREF_MT_appconfigs", text=text)
194
195
196 # only for addons
197 class USERPREF_MT_splash_footer(Menu):
198     bl_label = ""
199
200     def draw(self, context):
201         pass
202
203
204 class USERPREF_PT_interface(Panel):
205     bl_space_type = 'USER_PREFERENCES'
206     bl_label = "Interface"
207     bl_region_type = 'WINDOW'
208     bl_options = {'HIDE_HEADER'}
209
210     @classmethod
211     def poll(cls, context):
212         userpref = context.user_preferences
213         return (userpref.active_section == 'INTERFACE')
214
215     def draw(self, context):
216         import sys
217         layout = self.layout
218
219         userpref = context.user_preferences
220         view = userpref.view
221
222         row = layout.row()
223
224         col = row.column()
225         col.label(text="Display:")
226         col.prop(view, "ui_scale", text="Scale")
227         col.prop(view, "ui_line_width", text="Line Width")
228         col.prop(view, "show_tooltips")
229         col.prop(view, "show_tooltips_python")
230         col.prop(view, "show_object_info", text="Object Info")
231         col.prop(view, "show_large_cursors")
232         col.prop(view, "show_view_name", text="View Name")
233         col.prop(view, "show_playback_fps", text="Playback FPS")
234         col.prop(view, "object_origin_size")
235
236         col.separator()
237         col.separator()
238         col.separator()
239
240         col.prop(view, "show_mini_axis", text="Display Mini Axis")
241         sub = col.column()
242         sub.active = view.show_mini_axis
243         sub.prop(view, "mini_axis_size", text="Size")
244         sub.prop(view, "mini_axis_brightness", text="Brightness")
245
246         col.separator()
247
248         if sys.platform[:3] == "win":
249             col.label("Warnings")
250             col.prop(view, "use_quit_dialog")
251
252         row.separator()
253         row.separator()
254
255         col = row.column()
256         col.label(text="View Manipulation:")
257         col.prop(view, "use_mouse_depth_cursor")
258         col.prop(view, "use_cursor_lock_adjust")
259         col.prop(view, "use_mouse_depth_navigate")
260         col.prop(view, "use_zoom_to_mouse")
261         col.prop(view, "use_rotate_around_active")
262         col.prop(view, "use_global_pivot")
263         col.prop(view, "use_camera_lock_parent")
264
265         col.separator()
266
267         col.prop(view, "use_auto_perspective")
268         col.prop(view, "smooth_view")
269         col.prop(view, "rotation_angle")
270
271         col.separator()
272         col.separator()
273
274         col.label(text="2D Viewports:")
275         col.prop(view, "view2d_grid_spacing_min", text="Minimum Grid Spacing")
276         col.prop(view, "timecode_style")
277         col.prop(view, "view_frame_type")
278         if view.view_frame_type == 'SECONDS':
279             col.prop(view, "view_frame_seconds")
280         elif view.view_frame_type == 'KEYFRAMES':
281             col.prop(view, "view_frame_keyframes")
282
283         row.separator()
284         row.separator()
285
286         col = row.column()
287         #Toolbox doesn't exist yet
288         #col.label(text="Toolbox:")
289         #col.prop(view, "show_column_layout")
290         #col.label(text="Open Toolbox Delay:")
291         #col.prop(view, "open_left_mouse_delay", text="Hold LMB")
292         #col.prop(view, "open_right_mouse_delay", text="Hold RMB")
293         col.prop(view, "show_manipulator")
294         col.prop(view, "show_manipulator_navigate")
295         ## Currently not working
296         # col.prop(view, "show_manipulator_shaded")
297         sub = col.column()
298         sub.active = view.show_manipulator
299         sub.prop(view, "manipulator_size", text="Size")
300
301         col.separator()
302         col.separator()
303         col.separator()
304
305         col.label(text="Menus:")
306         col.prop(view, "use_mouse_over_open")
307         sub = col.column()
308         sub.active = view.use_mouse_over_open
309
310         sub.prop(view, "open_toplevel_delay", text="Top Level")
311         sub.prop(view, "open_sublevel_delay", text="Sub Level")
312
313         col.separator()
314         col.label(text="Pie Menus:")
315         sub = col.column(align=True)
316         sub.prop(view, "pie_animation_timeout")
317         sub.prop(view, "pie_initial_timeout")
318         sub.prop(view, "pie_menu_radius")
319         sub.prop(view, "pie_menu_threshold")
320         sub.prop(view, "pie_menu_confirm")
321         col.separator()
322
323         col.prop(view, "show_splash")
324         col.separator()
325
326         col.label(text="App Template:")
327         col.label(text="Options intended for use with app-templates only.")
328         col.prop(view, "show_layout_ui")
329         col.prop(view, "show_view3d_cursor")
330
331
332
333 class USERPREF_PT_edit(Panel):
334     bl_space_type = 'USER_PREFERENCES'
335     bl_label = "Edit"
336     bl_region_type = 'WINDOW'
337     bl_options = {'HIDE_HEADER'}
338
339     @classmethod
340     def poll(cls, context):
341         userpref = context.user_preferences
342         return (userpref.active_section == 'EDITING')
343
344     def draw(self, context):
345         layout = self.layout
346
347         userpref = context.user_preferences
348         edit = userpref.edit
349
350         row = layout.row()
351
352         col = row.column()
353         col.label(text="Link Materials To:")
354         col.prop(edit, "material_link", text="")
355
356         col.separator()
357         col.separator()
358         col.separator()
359
360         col.label(text="New Objects:")
361         col.prop(edit, "use_enter_edit_mode")
362         col.label(text="Align To:")
363         col.prop(edit, "object_align", text="")
364
365         col.separator()
366         col.separator()
367         col.separator()
368
369         col.label(text="Undo:")
370         col.prop(edit, "use_global_undo")
371         col.prop(edit, "undo_steps", text="Steps")
372         col.prop(edit, "undo_memory_limit", text="Memory Limit")
373
374         row.separator()
375         row.separator()
376
377         col = row.column()
378         col.label(text="Grease Pencil:")
379         col.prop(edit, "grease_pencil_eraser_radius", text="Eraser Radius")
380         col.separator()
381         col.prop(edit, "grease_pencil_manhattan_distance", text="Manhattan Distance")
382         col.prop(edit, "grease_pencil_euclidean_distance", text="Euclidean Distance")
383         col.separator()
384         col.prop(edit, "grease_pencil_default_color", text="Default Color")
385         col.separator()
386         col.prop(edit, "use_grease_pencil_simplify_stroke", text="Simplify Stroke")
387         col.separator()
388         col.separator()
389         col.separator()
390         col.separator()
391         col.label(text="Playback:")
392         col.prop(edit, "use_negative_frames")
393         col.separator()
394         col.separator()
395         col.separator()
396         col.label(text="Node Editor:")
397         col.prop(edit, "node_margin")
398         col.label(text="Animation Editors:")
399         col.prop(edit, "fcurve_unselected_alpha", text="F-Curve Visibility")
400
401         row.separator()
402         row.separator()
403
404         col = row.column()
405         col.label(text="Keyframing:")
406         col.prop(edit, "use_visual_keying")
407         col.prop(edit, "use_keyframe_insert_needed", text="Only Insert Needed")
408
409         col.separator()
410
411         col.prop(edit, "use_auto_keying", text="Auto Keyframing:")
412         col.prop(edit, "use_auto_keying_warning")
413
414         sub = col.column()
415
416         #~ sub.active = edit.use_keyframe_insert_auto # incorrect, time-line can enable
417         sub.prop(edit, "use_keyframe_insert_available", text="Only Insert Available")
418
419         col.separator()
420
421         col.label(text="New F-Curve Defaults:")
422         col.prop(edit, "keyframe_new_interpolation_type", text="Interpolation")
423         col.prop(edit, "keyframe_new_handle_type", text="Handles")
424         col.prop(edit, "use_insertkey_xyz_to_rgb", text="XYZ to RGB")
425
426         col.separator()
427         col.separator()
428         col.separator()
429
430         col.label(text="Transform:")
431         col.prop(edit, "use_drag_immediately")
432
433         row.separator()
434         row.separator()
435
436         col = row.column()
437         col.prop(edit, "sculpt_paint_overlay_color", text="Sculpt Overlay Color")
438
439         col.separator()
440         col.separator()
441         col.separator()
442
443         col.label(text="Duplicate Data:")
444         col.prop(edit, "use_duplicate_mesh", text="Mesh")
445         col.prop(edit, "use_duplicate_surface", text="Surface")
446         col.prop(edit, "use_duplicate_curve", text="Curve")
447         col.prop(edit, "use_duplicate_text", text="Text")
448         col.prop(edit, "use_duplicate_metaball", text="Metaball")
449         col.prop(edit, "use_duplicate_armature", text="Armature")
450         col.prop(edit, "use_duplicate_lamp", text="Lamp")
451         col.prop(edit, "use_duplicate_material", text="Material")
452         col.prop(edit, "use_duplicate_texture", text="Texture")
453         #col.prop(edit, "use_duplicate_fcurve", text="F-Curve")
454         col.prop(edit, "use_duplicate_action", text="Action")
455         col.prop(edit, "use_duplicate_particle", text="Particle")
456
457
458 class USERPREF_PT_system(Panel):
459     bl_space_type = 'USER_PREFERENCES'
460     bl_label = "System"
461     bl_region_type = 'WINDOW'
462     bl_options = {'HIDE_HEADER'}
463
464     @classmethod
465     def poll(cls, context):
466         userpref = context.user_preferences
467         return (userpref.active_section == 'SYSTEM')
468
469     def draw(self, context):
470         import sys
471         layout = self.layout
472
473         userpref = context.user_preferences
474         system = userpref.system
475
476         split = layout.split()
477
478         # 1. Column
479         column = split.column()
480         colsplit = column.split(percentage=0.85)
481
482         col = colsplit.column()
483         col.label(text="General:")
484
485         col.prop(system, "frame_server_port")
486         col.prop(system, "scrollback", text="Console Scrollback")
487
488         col.separator()
489
490         col.label(text="Sound:")
491         col.row().prop(system, "audio_device", expand=False)
492         sub = col.column()
493         sub.active = system.audio_device not in {'NONE', 'Null'}
494         #sub.prop(system, "use_preview_images")
495         sub.prop(system, "audio_channels", text="Channels")
496         sub.prop(system, "audio_mixing_buffer", text="Mixing Buffer")
497         sub.prop(system, "audio_sample_rate", text="Sample Rate")
498         sub.prop(system, "audio_sample_format", text="Sample Format")
499
500         col.separator()
501
502         col.label(text="Screencast:")
503         col.prop(system, "screencast_fps")
504         col.prop(system, "screencast_wait_time")
505
506         col.separator()
507
508         if bpy.app.build_options.cycles:
509             addon = userpref.addons.get("cycles")
510             if addon is not None:
511                 addon.preferences.draw_impl(col, context)
512             del addon
513
514         if hasattr(system, "opensubdiv_compute_type"):
515             col.label(text="OpenSubdiv compute:")
516             col.row().prop(system, "opensubdiv_compute_type", text="")
517
518         # 2. Column
519         column = split.column()
520         colsplit = column.split(percentage=0.85)
521
522         col = colsplit.column()
523         col.label(text="OpenGL:")
524         col.prop(system, "gl_clip_alpha", slider=True)
525         col.prop(system, "use_mipmaps")
526         col.prop(system, "use_gpu_mipmap")
527         col.prop(system, "use_16bit_textures")
528
529         col.separator()
530         col.label(text="Selection")
531         col.prop(system, "select_method", text="")
532         col.prop(system, "use_select_pick_depth")
533
534         col.separator()
535
536         col.label(text="Anisotropic Filtering")
537         col.prop(system, "anisotropic_filter", text="")
538
539         col.separator()
540
541         col.label(text="Window Draw Method:")
542         col.prop(system, "window_draw_method", text="")
543         col.prop(system, "multi_sample", text="")
544         if sys.platform == "linux" and system.multi_sample != 'NONE':
545             col.label(text="Might fail for Mesh editing selection!")
546             col.separator()
547         col.prop(system, "use_region_overlap")
548
549         col.separator()
550
551         col.label(text="Text Draw Options:")
552         col.prop(system, "use_text_antialiasing")
553
554         col.separator()
555
556         col.label(text="Textures:")
557         col.prop(system, "gl_texture_limit", text="Limit Size")
558         col.prop(system, "texture_time_out", text="Time Out")
559         col.prop(system, "texture_collection_rate", text="Collection Rate")
560
561         col.separator()
562
563         col.label(text="Images Draw Method:")
564         col.prop(system, "image_draw_method", text="")
565
566         col.separator()
567
568         col.label(text="Sequencer/Clip Editor:")
569         # currently disabled in the code
570         # col.prop(system, "prefetch_frames")
571         col.prop(system, "memory_cache_limit")
572
573         # 3. Column
574         column = split.column()
575
576         column.label(text="Solid OpenGL Lights:")
577
578         split = column.split(percentage=0.1)
579         split.label()
580         split.label(text="Colors:")
581         split.label(text="Direction:")
582
583         lamp = system.solid_lights[0]
584         opengl_lamp_buttons(column, lamp)
585
586         lamp = system.solid_lights[1]
587         opengl_lamp_buttons(column, lamp)
588
589         lamp = system.solid_lights[2]
590         opengl_lamp_buttons(column, lamp)
591
592         column.separator()
593
594         column.label(text="Color Picker Type:")
595         column.row().prop(system, "color_picker_type", text="")
596
597         column.separator()
598
599         column.prop(system, "use_weight_color_range", text="Custom Weight Paint Range")
600         sub = column.column()
601         sub.active = system.use_weight_color_range
602         sub.template_color_ramp(system, "weight_color_range", expand=True)
603
604         column.separator()
605         column.prop(system, "font_path_ui")
606         column.prop(system, "font_path_ui_mono")
607
608         if bpy.app.build_options.international:
609             column.prop(system, "use_international_fonts")
610             if system.use_international_fonts:
611                 column.prop(system, "language")
612                 row = column.row()
613                 row.label(text="Translate:", text_ctxt=i18n_contexts.id_windowmanager)
614                 row = column.row(align=True)
615                 row.prop(system, "use_translate_interface", text="Interface", toggle=True)
616                 row.prop(system, "use_translate_tooltips", text="Tooltips", toggle=True)
617                 row.prop(system, "use_translate_new_dataname", text="New Data", toggle=True)
618
619
620 class USERPREF_MT_interface_theme_presets(Menu):
621     bl_label = "Presets"
622     preset_subdir = "interface_theme"
623     preset_operator = "script.execute_preset"
624     preset_type = 'XML'
625     preset_xml_map = (
626         ("user_preferences.themes[0]", "Theme"),
627         ("user_preferences.ui_styles[0]", "ThemeStyle"),
628     )
629     draw = Menu.draw_preset
630
631
632 class USERPREF_PT_theme(Panel):
633     bl_space_type = 'USER_PREFERENCES'
634     bl_label = "Themes"
635     bl_region_type = 'WINDOW'
636     bl_options = {'HIDE_HEADER'}
637
638     # not essential, hard-coded UI delimiters for the theme layout
639     ui_delimiters = {
640         'VIEW_3D': {
641             "text_grease_pencil",
642             "text_keyframe",
643             "speaker",
644             "freestyle_face_mark",
645             "split_normal",
646             "bone_solid",
647             "paint_curve_pivot",
648         },
649         'GRAPH_EDITOR': {
650             "handle_vertex_select",
651         },
652         'IMAGE_EDITOR': {
653             "paint_curve_pivot",
654         },
655         'NODE_EDITOR': {
656             "layout_node",
657         },
658         'CLIP_EDITOR': {
659             "handle_vertex_select",
660         }
661     }
662
663     @staticmethod
664     def _theme_generic(split, themedata, theme_area):
665
666         col = split.column()
667
668         def theme_generic_recurse(data):
669             col.label(data.rna_type.name)
670             row = col.row()
671             subsplit = row.split(percentage=0.95)
672
673             padding1 = subsplit.split(percentage=0.15)
674             padding1.column()
675
676             subsplit = row.split(percentage=0.85)
677
678             padding2 = subsplit.split(percentage=0.15)
679             padding2.column()
680
681             colsub_pair = padding1.column(), padding2.column()
682
683             props_type = {}
684
685             for i, prop in enumerate(data.rna_type.properties):
686                 if prop.identifier == "rna_type":
687                     continue
688
689                 props_type.setdefault((prop.type, prop.subtype), []).append(prop)
690
691             th_delimiters = USERPREF_PT_theme.ui_delimiters.get(theme_area)
692             for props_type, props_ls in sorted(props_type.items()):
693                 if props_type[0] == 'POINTER':
694                     for i, prop in enumerate(props_ls):
695                         theme_generic_recurse(getattr(data, prop.identifier))
696                 else:
697                     if th_delimiters is None:
698                         # simple, no delimiters
699                         for i, prop in enumerate(props_ls):
700                             colsub_pair[i % 2].row().prop(data, prop.identifier)
701                     else:
702                         # add hard coded delimiters
703                         i = 0
704                         for prop in props_ls:
705                             colsub = colsub_pair[i]
706                             colsub.row().prop(data, prop.identifier)
707                             i = (i + 1) % 2
708                             if prop.identifier in th_delimiters:
709                                 if i:
710                                     colsub = colsub_pair[1]
711                                     colsub.row().label("")
712                                 colsub_pair[0].row().label("")
713                                 colsub_pair[1].row().label("")
714                                 i = 0
715
716         theme_generic_recurse(themedata)
717
718     @staticmethod
719     def _theme_widget_style(layout, widget_style):
720
721         row = layout.row()
722
723         subsplit = row.split(percentage=0.95)
724
725         padding = subsplit.split(percentage=0.15)
726         colsub = padding.column()
727         colsub = padding.column()
728         colsub.row().prop(widget_style, "outline")
729         colsub.row().prop(widget_style, "item", slider=True)
730         colsub.row().prop(widget_style, "inner", slider=True)
731         colsub.row().prop(widget_style, "inner_sel", slider=True)
732
733         subsplit = row.split(percentage=0.85)
734
735         padding = subsplit.split(percentage=0.15)
736         colsub = padding.column()
737         colsub = padding.column()
738         colsub.row().prop(widget_style, "text")
739         colsub.row().prop(widget_style, "text_sel")
740         colsub.prop(widget_style, "show_shaded")
741         subsub = colsub.column(align=True)
742         subsub.active = widget_style.show_shaded
743         subsub.prop(widget_style, "shadetop")
744         subsub.prop(widget_style, "shadedown")
745
746         layout.separator()
747
748     @staticmethod
749     def _ui_font_style(layout, font_style):
750
751         split = layout.split()
752
753         col = split.column()
754         col.label(text="Kerning Style:")
755         col.row().prop(font_style, "font_kerning_style", expand=True)
756         col.prop(font_style, "points")
757
758         col = split.column()
759         col.label(text="Shadow Offset:")
760         col.prop(font_style, "shadow_offset_x", text="X")
761         col.prop(font_style, "shadow_offset_y", text="Y")
762
763         col = split.column()
764         col.prop(font_style, "shadow")
765         col.prop(font_style, "shadow_alpha")
766         col.prop(font_style, "shadow_value")
767
768         layout.separator()
769
770     @classmethod
771     def poll(cls, context):
772         userpref = context.user_preferences
773         return (userpref.active_section == 'THEMES')
774
775     def draw(self, context):
776         layout = self.layout
777
778         theme = context.user_preferences.themes[0]
779
780         split_themes = layout.split(percentage=0.2)
781
782         sub = split_themes.column()
783
784         sub.label(text="Presets:")
785         subrow = sub.row(align=True)
786
787         subrow.menu("USERPREF_MT_interface_theme_presets", text=USERPREF_MT_interface_theme_presets.bl_label)
788         subrow.operator("wm.interface_theme_preset_add", text="", icon='ZOOMIN')
789         subrow.operator("wm.interface_theme_preset_add", text="", icon='ZOOMOUT').remove_active = True
790         sub.separator()
791
792         sub.prop(theme, "theme_area", expand=True)
793
794         split = layout.split(percentage=0.4)
795
796         layout.separator()
797         layout.separator()
798
799         split = split_themes.split()
800
801         if theme.theme_area == 'USER_INTERFACE':
802             col = split.column()
803             ui = theme.user_interface
804
805             col.label(text="Regular:")
806             self._theme_widget_style(col, ui.wcol_regular)
807
808             col.label(text="Tool:")
809             self._theme_widget_style(col, ui.wcol_tool)
810
811             col.label(text="Radio Buttons:")
812             self._theme_widget_style(col, ui.wcol_radio)
813
814             col.label(text="Text:")
815             self._theme_widget_style(col, ui.wcol_text)
816
817             col.label(text="Option:")
818             self._theme_widget_style(col, ui.wcol_option)
819
820             col.label(text="Toggle:")
821             self._theme_widget_style(col, ui.wcol_toggle)
822
823             col.label(text="Number Field:")
824             self._theme_widget_style(col, ui.wcol_num)
825
826             col.label(text="Value Slider:")
827             self._theme_widget_style(col, ui.wcol_numslider)
828
829             col.label(text="Box:")
830             self._theme_widget_style(col, ui.wcol_box)
831
832             col.label(text="Menu:")
833             self._theme_widget_style(col, ui.wcol_menu)
834
835             col.label(text="Pie Menu:")
836             self._theme_widget_style(col, ui.wcol_pie_menu)
837
838             col.label(text="Pulldown:")
839             self._theme_widget_style(col, ui.wcol_pulldown)
840
841             col.label(text="Menu Back:")
842             self._theme_widget_style(col, ui.wcol_menu_back)
843
844             col.label(text="Tooltip:")
845             self._theme_widget_style(col, ui.wcol_tooltip)
846
847             col.label(text="Menu Item:")
848             self._theme_widget_style(col, ui.wcol_menu_item)
849
850             col.label(text="Scroll Bar:")
851             self._theme_widget_style(col, ui.wcol_scroll)
852
853             col.label(text="Progress Bar:")
854             self._theme_widget_style(col, ui.wcol_progress)
855
856             col.label(text="List Item:")
857             self._theme_widget_style(col, ui.wcol_list_item)
858
859             ui_state = theme.user_interface.wcol_state
860             col.label(text="State:")
861
862             row = col.row()
863
864             subsplit = row.split(percentage=0.95)
865
866             padding = subsplit.split(percentage=0.15)
867             colsub = padding.column()
868             colsub = padding.column()
869             colsub.row().prop(ui_state, "inner_anim")
870             colsub.row().prop(ui_state, "inner_anim_sel")
871             colsub.row().prop(ui_state, "inner_driven")
872             colsub.row().prop(ui_state, "inner_driven_sel")
873             colsub.row().prop(ui_state, "blend")
874
875             subsplit = row.split(percentage=0.85)
876
877             padding = subsplit.split(percentage=0.15)
878             colsub = padding.column()
879             colsub = padding.column()
880             colsub.row().prop(ui_state, "inner_key")
881             colsub.row().prop(ui_state, "inner_key_sel")
882             colsub.row().prop(ui_state, "inner_overridden")
883             colsub.row().prop(ui_state, "inner_overridden_sel")
884
885             col.separator()
886             col.separator()
887
888             col.label("Styles:")
889
890             row = col.row()
891
892             subsplit = row.split(percentage=0.95)
893
894             padding = subsplit.split(percentage=0.15)
895             colsub = padding.column()
896             colsub = padding.column()
897             colsub.row().prop(ui, "menu_shadow_fac")
898
899             subsplit = row.split(percentage=0.85)
900
901             padding = subsplit.split(percentage=0.15)
902             colsub = padding.column()
903             colsub = padding.column()
904             colsub.row().prop(ui, "menu_shadow_width")
905
906             row = col.row()
907
908             subsplit = row.split(percentage=0.95)
909
910             padding = subsplit.split(percentage=0.15)
911             colsub = padding.column()
912             colsub = padding.column()
913             colsub.row().prop(ui, "icon_alpha")
914
915             subsplit = row.split(percentage=0.85)
916
917             padding = subsplit.split(percentage=0.15)
918             colsub = padding.column()
919             colsub = padding.column()
920             colsub.row().prop(ui, "widget_emboss")
921
922             col.separator()
923             col.separator()
924
925             col.label("Axis & Manipulator Colors:")
926
927             row = col.row()
928
929             subsplit = row.split(percentage=0.95)
930
931             padding = subsplit.split(percentage=0.15)
932             colsub = padding.column()
933             colsub = padding.column()
934             colsub.row().prop(ui, "axis_x")
935             colsub.row().prop(ui, "axis_y")
936             colsub.row().prop(ui, "axis_z")
937
938             subsplit = row.split(percentage=0.85)
939
940             padding = subsplit.split(percentage=0.15)
941             colsub = padding.column()
942             colsub = padding.column()
943             colsub.row().prop(ui, "manipulator_primary")
944             colsub.row().prop(ui, "manipulator_secondary")
945             colsub.row().prop(ui, "manipulator_a")
946             colsub.row().prop(ui, "manipulator_b")
947
948             col.separator()
949             col.separator()
950         elif theme.theme_area == 'BONE_COLOR_SETS':
951             col = split.column()
952
953             for i, ui in enumerate(theme.bone_color_sets, 1):
954                 col.label(text=iface_("Color Set %d:") % 1, translate=False)
955
956                 row = col.row()
957
958                 subsplit = row.split(percentage=0.95)
959
960                 padding = subsplit.split(percentage=0.15)
961                 colsub = padding.column()
962                 colsub = padding.column()
963                 colsub.row().prop(ui, "normal")
964                 colsub.row().prop(ui, "select")
965                 colsub.row().prop(ui, "active")
966
967                 subsplit = row.split(percentage=0.85)
968
969                 padding = subsplit.split(percentage=0.15)
970                 colsub = padding.column()
971                 colsub = padding.column()
972                 colsub.row().prop(ui, "show_colored_constraints")
973         elif theme.theme_area == 'STYLE':
974             col = split.column()
975
976             style = context.user_preferences.ui_styles[0]
977
978             col.label(text="Panel Title:")
979             self._ui_font_style(col, style.panel_title)
980
981             col.separator()
982
983             col.label(text="Widget:")
984             self._ui_font_style(col, style.widget)
985
986             col.separator()
987
988             col.label(text="Widget Label:")
989             self._ui_font_style(col, style.widget_label)
990         else:
991             self._theme_generic(split, getattr(theme, theme.theme_area.lower()), theme.theme_area)
992
993
994 class USERPREF_PT_file(Panel):
995     bl_space_type = 'USER_PREFERENCES'
996     bl_label = "Files"
997     bl_region_type = 'WINDOW'
998     bl_options = {'HIDE_HEADER'}
999
1000     @classmethod
1001     def poll(cls, context):
1002         userpref = context.user_preferences
1003         return (userpref.active_section == 'FILES')
1004
1005     def draw(self, context):
1006         layout = self.layout
1007
1008         userpref = context.user_preferences
1009         paths = userpref.filepaths
1010         system = userpref.system
1011
1012         split = layout.split(percentage=0.7)
1013
1014         col = split.column()
1015         col.label(text="File Paths:")
1016
1017         colsplit = col.split(percentage=0.95)
1018         col1 = colsplit.split(percentage=0.3)
1019
1020         sub = col1.column()
1021         sub.label(text="Fonts:")
1022         sub.label(text="Textures:")
1023         sub.label(text="Render Output:")
1024         sub.label(text="Scripts:")
1025         sub.label(text="Sounds:")
1026         sub.label(text="Temp:")
1027         sub.label(text="Render Cache:")
1028         sub.label(text="I18n Branches:")
1029         sub.label(text="Image Editor:")
1030         sub.label(text="Animation Player:")
1031
1032         sub = col1.column()
1033         sub.prop(paths, "font_directory", text="")
1034         sub.prop(paths, "texture_directory", text="")
1035         sub.prop(paths, "render_output_directory", text="")
1036         sub.prop(paths, "script_directory", text="")
1037         sub.prop(paths, "sound_directory", text="")
1038         sub.prop(paths, "temporary_directory", text="")
1039         sub.prop(paths, "render_cache_directory", text="")
1040         sub.prop(paths, "i18n_branches_directory", text="")
1041         sub.prop(paths, "image_editor", text="")
1042         subsplit = sub.split(percentage=0.3)
1043         subsplit.prop(paths, "animation_player_preset", text="")
1044         subsplit.prop(paths, "animation_player", text="")
1045
1046         col.separator()
1047         col.separator()
1048
1049         colsplit = col.split(percentage=0.95)
1050         sub = colsplit.column()
1051
1052         row = sub.split(percentage=0.3)
1053         row.label(text="Auto Execution:")
1054         row.prop(system, "use_scripts_auto_execute")
1055
1056         if system.use_scripts_auto_execute:
1057             box = sub.box()
1058             row = box.row()
1059             row.label(text="Excluded Paths:")
1060             row.operator("wm.userpref_autoexec_path_add", text="", icon='ZOOMIN', emboss=False)
1061             for i, path_cmp in enumerate(userpref.autoexec_paths):
1062                 row = box.row()
1063                 row.prop(path_cmp, "path", text="")
1064                 row.prop(path_cmp, "use_glob", text="", icon='FILTER')
1065                 row.operator("wm.userpref_autoexec_path_remove", text="", icon='X', emboss=False).index = i
1066
1067         col = split.column()
1068         col.label(text="Save & Load:")
1069         col.prop(paths, "use_relative_paths")
1070         col.prop(paths, "use_file_compression")
1071         col.prop(paths, "use_load_ui")
1072         col.prop(paths, "use_filter_files")
1073         col.prop(paths, "show_hidden_files_datablocks")
1074         col.prop(paths, "hide_recent_locations")
1075         col.prop(paths, "hide_system_bookmarks")
1076         col.prop(paths, "show_thumbnails")
1077
1078         col.separator()
1079
1080         col.prop(paths, "save_version")
1081         col.prop(paths, "recent_files")
1082         col.prop(paths, "use_save_preview_images")
1083
1084         col.separator()
1085
1086         col.label(text="Auto Save:")
1087         col.prop(paths, "use_keep_session")
1088         col.prop(paths, "use_auto_save_temporary_files")
1089         sub = col.column()
1090         sub.active = paths.use_auto_save_temporary_files
1091         sub.prop(paths, "auto_save_time", text="Timer (mins)")
1092
1093         col.separator()
1094
1095         col.label(text="Text Editor:")
1096         col.prop(system, "use_tabs_as_spaces")
1097
1098         colsplit = col.split(percentage=0.95)
1099         col1 = colsplit.split(percentage=0.3)
1100
1101         sub = col1.column()
1102         sub.label(text="Author:")
1103         sub = col1.column()
1104         sub.prop(system, "author", text="")
1105
1106
1107 class USERPREF_MT_ndof_settings(Menu):
1108     # accessed from the window key-bindings in C (only)
1109     bl_label = "3D Mouse Settings"
1110
1111     def draw(self, context):
1112         layout = self.layout
1113
1114         input_prefs = context.user_preferences.inputs
1115
1116         is_view3d = context.space_data.type == 'VIEW_3D'
1117
1118         layout.prop(input_prefs, "ndof_sensitivity")
1119         layout.prop(input_prefs, "ndof_orbit_sensitivity")
1120         layout.prop(input_prefs, "ndof_deadzone")
1121
1122         if is_view3d:
1123             layout.separator()
1124             layout.prop(input_prefs, "ndof_show_guide")
1125
1126             layout.separator()
1127             layout.label(text="Orbit Style")
1128             layout.row().prop(input_prefs, "ndof_view_navigate_method", text="")
1129             layout.row().prop(input_prefs, "ndof_view_rotate_method", text="")
1130             layout.separator()
1131             layout.label(text="Orbit Options")
1132             layout.prop(input_prefs, "ndof_rotx_invert_axis")
1133             layout.prop(input_prefs, "ndof_roty_invert_axis")
1134             layout.prop(input_prefs, "ndof_rotz_invert_axis")
1135
1136         # view2d use pan/zoom
1137         layout.separator()
1138         layout.label(text="Pan Options")
1139         layout.prop(input_prefs, "ndof_panx_invert_axis")
1140         layout.prop(input_prefs, "ndof_pany_invert_axis")
1141         layout.prop(input_prefs, "ndof_panz_invert_axis")
1142         layout.prop(input_prefs, "ndof_pan_yz_swap_axis")
1143
1144         layout.label(text="Zoom Options")
1145         layout.prop(input_prefs, "ndof_zoom_invert")
1146
1147         if is_view3d:
1148             layout.separator()
1149             layout.label(text="Fly/Walk Options")
1150             layout.prop(input_prefs, "ndof_fly_helicopter", icon='NDOF_FLY')
1151             layout.prop(input_prefs, "ndof_lock_horizon", icon='NDOF_DOM')
1152
1153
1154 class USERPREF_MT_keyconfigs(Menu):
1155     bl_label = "KeyPresets"
1156     preset_subdir = "keyconfig"
1157     preset_operator = "wm.keyconfig_activate"
1158
1159     def draw(self, context):
1160         props = self.layout.operator("wm.context_set_value", text="Blender (default)")
1161         props.data_path = "window_manager.keyconfigs.active"
1162         props.value = "context.window_manager.keyconfigs.default"
1163
1164         # now draw the presets
1165         Menu.draw_preset(self, context)
1166
1167
1168 class USERPREF_PT_input(Panel):
1169     bl_space_type = 'USER_PREFERENCES'
1170     bl_label = "Input"
1171     bl_region_type = 'WINDOW'
1172     bl_options = {'HIDE_HEADER'}
1173
1174     @classmethod
1175     def poll(cls, context):
1176         userpref = context.user_preferences
1177         return (userpref.active_section == 'INPUT')
1178
1179     @staticmethod
1180     def draw_input_prefs(inputs, layout):
1181         import sys
1182
1183         # General settings
1184         row = layout.row()
1185         col = row.column()
1186
1187         sub = col.column()
1188         sub.label(text="Presets:")
1189         subrow = sub.row(align=True)
1190
1191         subrow.menu("USERPREF_MT_interaction_presets", text=bpy.types.USERPREF_MT_interaction_presets.bl_label)
1192         subrow.operator("wm.interaction_preset_add", text="", icon='ZOOMIN')
1193         subrow.operator("wm.interaction_preset_add", text="", icon='ZOOMOUT').remove_active = True
1194         sub.separator()
1195
1196         sub.label(text="Mouse:")
1197         sub1 = sub.column()
1198         sub1.active = (inputs.select_mouse == 'RIGHT')
1199         sub1.prop(inputs, "use_mouse_emulate_3_button")
1200         sub.prop(inputs, "use_mouse_continuous")
1201         sub.prop(inputs, "drag_threshold")
1202         sub.prop(inputs, "tweak_threshold")
1203
1204         sub.label(text="Select With:")
1205         sub.row().prop(inputs, "select_mouse", expand=True)
1206
1207         sub = col.column()
1208         sub.label(text="Double Click:")
1209         sub.prop(inputs, "mouse_double_click_time", text="Speed")
1210
1211         sub.separator()
1212
1213         sub.prop(inputs, "use_emulate_numpad")
1214
1215         sub.separator()
1216
1217         sub.label(text="Orbit Style:")
1218         sub.row().prop(inputs, "view_rotate_method", expand=True)
1219
1220         sub.separator()
1221
1222         sub.label(text="Zoom Style:")
1223         sub.row().prop(inputs, "view_zoom_method", text="")
1224         if inputs.view_zoom_method in {'DOLLY', 'CONTINUE'}:
1225             sub.row().prop(inputs, "view_zoom_axis", expand=True)
1226             sub.prop(inputs, "invert_mouse_zoom", text="Invert Mouse Zoom Direction")
1227
1228         #sub.prop(inputs, "use_mouse_mmb_paste")
1229
1230         #col.separator()
1231
1232         sub = col.column()
1233         sub.prop(inputs, "invert_zoom_wheel", text="Invert Wheel Zoom Direction")
1234         #sub.prop(view, "wheel_scroll_lines", text="Scroll Lines")
1235
1236         if sys.platform == "darwin":
1237             sub = col.column()
1238             sub.prop(inputs, "use_trackpad_natural", text="Natural Trackpad Direction")
1239
1240         col.separator()
1241         sub = col.column()
1242         sub.label(text="View Navigation:")
1243         sub.row().prop(inputs, "navigation_mode", expand=True)
1244
1245         sub.label(text="Walk Navigation:")
1246
1247         walk = inputs.walk_navigation
1248
1249         sub.prop(walk, "use_mouse_reverse")
1250         sub.prop(walk, "mouse_speed")
1251         sub.prop(walk, "teleport_time")
1252
1253         sub = col.column(align=True)
1254         sub.prop(walk, "walk_speed")
1255         sub.prop(walk, "walk_speed_factor")
1256
1257         sub.separator()
1258         sub.prop(walk, "use_gravity")
1259         sub = col.column(align=True)
1260         sub.active = walk.use_gravity
1261         sub.prop(walk, "view_height")
1262         sub.prop(walk, "jump_height")
1263
1264         if inputs.use_ndof:
1265             col.separator()
1266             col.label(text="NDOF Device:")
1267             sub = col.column(align=True)
1268             sub.prop(inputs, "ndof_sensitivity", text="Pan Sensitivity")
1269             sub.prop(inputs, "ndof_orbit_sensitivity", text="Orbit Sensitivity")
1270             sub.prop(inputs, "ndof_deadzone", text="Deadzone")
1271
1272             sub.separator()
1273             col.label(text="Navigation Style:")
1274             sub = col.column(align=True)
1275             sub.row().prop(inputs, "ndof_view_navigate_method", expand=True)
1276
1277             sub.separator()
1278             col.label(text="Rotation Style:")
1279             sub = col.column(align=True)
1280             sub.row().prop(inputs, "ndof_view_rotate_method", expand=True)
1281
1282         row.separator()
1283
1284     def draw(self, context):
1285         from rna_keymap_ui import draw_keymaps
1286
1287         layout = self.layout
1288
1289         #import time
1290
1291         #start = time.time()
1292
1293         userpref = context.user_preferences
1294
1295         inputs = userpref.inputs
1296
1297         split = layout.split(percentage=0.25)
1298
1299         # Input settings
1300         self.draw_input_prefs(inputs, split)
1301
1302         # Keymap Settings
1303         draw_keymaps(context, split)
1304
1305         #print("runtime", time.time() - start)
1306
1307
1308 class USERPREF_MT_addons_online_resources(Menu):
1309     bl_label = "Online Resources"
1310
1311     # menu to open web-pages with addons development guides
1312     def draw(self, context):
1313         layout = self.layout
1314
1315         layout.operator(
1316             "wm.url_open", text="Add-ons Catalog", icon='URL',
1317         ).url = "http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts"
1318
1319         layout.separator()
1320
1321         layout.operator(
1322             "wm.url_open", text="How to share your add-on", icon='URL',
1323         ).url = "http://wiki.blender.org/index.php/Dev:Py/Sharing"
1324         layout.operator(
1325             "wm.url_open", text="Add-on Guidelines", icon='URL',
1326         ).url = "http://wiki.blender.org/index.php/Dev:2.5/Py/Scripts/Guidelines/Addons"
1327         layout.operator(
1328             "wm.url_open", text="API Concepts", icon='URL',
1329         ).url = bpy.types.WM_OT_doc_view._prefix + "/info_quickstart.html"
1330         layout.operator(
1331             "wm.url_open", text="Add-on Tutorial", icon='URL',
1332         ).url = bpy.types.WM_OT_doc_view._prefix + "/info_tutorial_addon.html"
1333
1334
1335 class USERPREF_PT_addons(Panel):
1336     bl_space_type = 'USER_PREFERENCES'
1337     bl_label = "Add-ons"
1338     bl_region_type = 'WINDOW'
1339     bl_options = {'HIDE_HEADER'}
1340
1341     _support_icon_mapping = {
1342         'OFFICIAL': 'FILE_BLEND',
1343         'COMMUNITY': 'POSE_DATA',
1344         'TESTING': 'MOD_EXPLODE',
1345         }
1346
1347     @classmethod
1348     def poll(cls, context):
1349         userpref = context.user_preferences
1350         return (userpref.active_section == 'ADDONS')
1351
1352     @staticmethod
1353     def is_user_addon(mod, user_addon_paths):
1354         import os
1355
1356         if not user_addon_paths:
1357             for path in (
1358                     bpy.utils.script_path_user(),
1359                     bpy.utils.script_path_pref(),
1360             ):
1361                 if path is not None:
1362                     user_addon_paths.append(os.path.join(path, "addons"))
1363
1364         for path in user_addon_paths:
1365             if bpy.path.is_subdir(mod.__file__, path):
1366                 return True
1367         return False
1368
1369     @staticmethod
1370     def draw_error(layout, message):
1371         lines = message.split("\n")
1372         box = layout.box()
1373         sub = box.row()
1374         sub.label(lines[0])
1375         sub.label(icon='ERROR')
1376         for l in lines[1:]:
1377             box.label(l)
1378
1379     def draw(self, context):
1380         import os
1381         import addon_utils
1382
1383         layout = self.layout
1384
1385         userpref = context.user_preferences
1386         used_ext = {ext.module for ext in userpref.addons}
1387
1388         userpref_addons_folder = os.path.join(userpref.filepaths.script_directory, "addons")
1389         scripts_addons_folder = bpy.utils.user_resource('SCRIPTS', "addons")
1390
1391         # collect the categories that can be filtered on
1392         addons = [
1393             (mod, addon_utils.module_bl_info(mod))
1394             for mod in addon_utils.modules(refresh=False)
1395         ]
1396
1397         split = layout.split(percentage=0.2)
1398         col = split.column()
1399         col.prop(context.window_manager, "addon_search", text="", icon='VIEWZOOM')
1400
1401         col.label(text="Supported Level")
1402         col.prop(context.window_manager, "addon_support", expand=True)
1403
1404         col.label(text="Categories")
1405         col.prop(context.window_manager, "addon_filter", expand=True)
1406
1407         col = split.column()
1408
1409         # set in addon_utils.modules_refresh()
1410         if addon_utils.error_duplicates:
1411             box = col.box()
1412             row = box.row()
1413             row.label("Multiple add-ons with the same name found!")
1414             row.label(icon='ERROR')
1415             box.label("Please delete one of each pair:")
1416             for (addon_name, addon_file, addon_path) in addon_utils.error_duplicates:
1417                 box.separator()
1418                 sub_col = box.column(align=True)
1419                 sub_col.label(addon_name + ":")
1420                 sub_col.label("    " + addon_file)
1421                 sub_col.label("    " + addon_path)
1422
1423
1424         if addon_utils.error_encoding:
1425             self.draw_error(
1426                 col,
1427                 "One or more addons do not have UTF-8 encoding\n"
1428                 "(see console for details)",
1429             )
1430
1431         filter = context.window_manager.addon_filter
1432         search = context.window_manager.addon_search.lower()
1433         support = context.window_manager.addon_support
1434
1435         # initialized on demand
1436         user_addon_paths = []
1437
1438         for mod, info in addons:
1439             module_name = mod.__name__
1440
1441             is_enabled = module_name in used_ext
1442
1443             if info["support"] not in support:
1444                 continue
1445
1446             # check if addon should be visible with current filters
1447             if ((filter == "All") or
1448                 (filter == info["category"]) or
1449                 (filter == "Enabled" and is_enabled) or
1450                 (filter == "Disabled" and not is_enabled) or
1451                 (filter == "User" and (mod.__file__.startswith((scripts_addons_folder, userpref_addons_folder))))
1452             ):
1453                 if search and search not in info["name"].lower():
1454                     if info["author"]:
1455                         if search not in info["author"].lower():
1456                             continue
1457                     else:
1458                         continue
1459
1460                 # Addon UI Code
1461                 col_box = col.column()
1462                 box = col_box.box()
1463                 colsub = box.column()
1464                 row = colsub.row(align=True)
1465
1466                 row.operator(
1467                     "wm.addon_expand",
1468                     icon='TRIA_DOWN' if info["show_expanded"] else 'TRIA_RIGHT',
1469                     emboss=False,
1470                 ).module = module_name
1471
1472                 row.operator(
1473                     "wm.addon_disable" if is_enabled else "wm.addon_enable",
1474                     icon='CHECKBOX_HLT' if is_enabled else 'CHECKBOX_DEHLT', text="",
1475                     emboss=False,
1476                 ).module = module_name
1477
1478                 sub = row.row()
1479                 sub.active = is_enabled
1480                 sub.label(text="%s: %s" % (info["category"], info["name"]))
1481                 if info["warning"]:
1482                     sub.label(icon='ERROR')
1483
1484                 # icon showing support level.
1485                 sub.label(icon=self._support_icon_mapping.get(info["support"], 'QUESTION'))
1486
1487                 # Expanded UI (only if additional info is available)
1488                 if info["show_expanded"]:
1489                     if info["description"]:
1490                         split = colsub.row().split(percentage=0.15)
1491                         split.label(text="Description:")
1492                         split.label(text=info["description"])
1493                     if info["location"]:
1494                         split = colsub.row().split(percentage=0.15)
1495                         split.label(text="Location:")
1496                         split.label(text=info["location"])
1497                     if mod:
1498                         split = colsub.row().split(percentage=0.15)
1499                         split.label(text="File:")
1500                         split.label(text=mod.__file__, translate=False)
1501                     if info["author"]:
1502                         split = colsub.row().split(percentage=0.15)
1503                         split.label(text="Author:")
1504                         split.label(text=info["author"], translate=False)
1505                     if info["version"]:
1506                         split = colsub.row().split(percentage=0.15)
1507                         split.label(text="Version:")
1508                         split.label(text=".".join(str(x) for x in info["version"]), translate=False)
1509                     if info["warning"]:
1510                         split = colsub.row().split(percentage=0.15)
1511                         split.label(text="Warning:")
1512                         split.label(text="  " + info["warning"], icon='ERROR')
1513
1514                     user_addon = USERPREF_PT_addons.is_user_addon(mod, user_addon_paths)
1515                     tot_row = bool(info["wiki_url"]) + bool(user_addon)
1516
1517                     if tot_row:
1518                         split = colsub.row().split(percentage=0.15)
1519                         split.label(text="Internet:")
1520                         if info["wiki_url"]:
1521                             split.operator(
1522                                 "wm.url_open", text="Documentation", icon='HELP',
1523                             ).url = info["wiki_url"]
1524                         split.operator(
1525                             "wm.url_open", text="Report a Bug", icon='URL',
1526                         ).url = info.get(
1527                             "tracker_url",
1528                             "https://developer.blender.org/maniphest/task/edit/form/2",
1529                         )
1530                         if user_addon:
1531                             split.operator(
1532                                 "wm.addon_remove", text="Remove", icon='CANCEL',
1533                             ).module = mod.__name__
1534
1535                         for i in range(4 - tot_row):
1536                             split.separator()
1537
1538                     # Show addon user preferences
1539                     if is_enabled:
1540                         addon_preferences = userpref.addons[module_name].preferences
1541                         if addon_preferences is not None:
1542                             draw = getattr(addon_preferences, "draw", None)
1543                             if draw is not None:
1544                                 addon_preferences_class = type(addon_preferences)
1545                                 box_prefs = col_box.box()
1546                                 box_prefs.label("Preferences:")
1547                                 addon_preferences_class.layout = box_prefs
1548                                 try:
1549                                     draw(context)
1550                                 except:
1551                                     import traceback
1552                                     traceback.print_exc()
1553                                     box_prefs.label(text="Error (see console)", icon='ERROR')
1554                                 del addon_preferences_class.layout
1555
1556         # Append missing scripts
1557         # First collect scripts that are used but have no script file.
1558         module_names = {mod.__name__ for mod, info in addons}
1559         missing_modules = {ext for ext in used_ext if ext not in module_names}
1560
1561         if missing_modules and filter in {"All", "Enabled"}:
1562             col.column().separator()
1563             col.column().label(text="Missing script files")
1564
1565             module_names = {mod.__name__ for mod, info in addons}
1566             for module_name in sorted(missing_modules):
1567                 is_enabled = module_name in used_ext
1568                 # Addon UI Code
1569                 box = col.column().box()
1570                 colsub = box.column()
1571                 row = colsub.row(align=True)
1572
1573                 row.label(text="", icon='ERROR')
1574
1575                 if is_enabled:
1576                     row.operator(
1577                         "wm.addon_disable", icon='CHECKBOX_HLT', text="", emboss=False,
1578                     ).module = module_name
1579
1580                 row.label(text=module_name, translate=False)
1581
1582
1583 classes = (
1584     USERPREF_HT_header,
1585     USERPREF_PT_tabs,
1586     USERPREF_MT_interaction_presets,
1587     USERPREF_MT_templates_splash,
1588     USERPREF_MT_app_templates,
1589     USERPREF_MT_appconfigs,
1590     USERPREF_MT_splash,
1591     USERPREF_MT_splash_footer,
1592     USERPREF_PT_interface,
1593     USERPREF_PT_edit,
1594     USERPREF_PT_system,
1595     USERPREF_MT_interface_theme_presets,
1596     USERPREF_PT_theme,
1597     USERPREF_PT_file,
1598     USERPREF_MT_ndof_settings,
1599     USERPREF_MT_keyconfigs,
1600     USERPREF_PT_input,
1601     USERPREF_MT_addons_online_resources,
1602     USERPREF_PT_addons,
1603 )
1604
1605 if __name__ == "__main__":  # only for live edit.
1606     from bpy.utils import register_class
1607     for cls in classes:
1608         register_class(cls)