Fix [#21678] Crease color cannot be adjusted
[blender.git] / release / scripts / 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 import os
22 import re
23 import shutil
24
25
26 def ui_items_general(col, context):
27     """ General UI Theme Settings (User Interface)
28     """
29     row = col.row()
30     sub = row.column()
31     sub.prop(context, "outline")
32     sub.prop(context, "item", slider=True)
33     sub = row.column()
34     sub.prop(context, "inner", slider=True)
35     sub.prop(context, "inner_sel", slider=True)
36     sub = row.column()
37     sub.prop(context, "text")
38     sub.prop(context, "text_sel")
39     sub = row.column()
40     sub.prop(context, "shaded")
41     subsub = sub.column(align=True)
42     subsub.active = context.shaded
43     subsub.prop(context, "shadetop")
44     subsub.prop(context, "shadedown")
45
46     col.separator()
47
48
49 def opengl_lamp_buttons(column, lamp):
50     split = column.split(percentage=0.1)
51
52     if lamp.enabled == True:
53         split.prop(lamp, "enabled", text="", icon='OUTLINER_OB_LAMP')
54     else:
55         split.prop(lamp, "enabled", text="", icon='LAMP_DATA')
56
57     col = split.column()
58     col.active = lamp.enabled
59     row = col.row()
60     row.label(text="Diffuse:")
61     row.prop(lamp, "diffuse_color", text="")
62     row = col.row()
63     row.label(text="Specular:")
64     row.prop(lamp, "specular_color", text="")
65
66     col = split.column()
67     col.active = lamp.enabled
68     col.prop(lamp, "direction", text="")
69
70 KM_HIERARCHY = [
71                     ('Window', 'EMPTY', 'WINDOW', []), # file save, window change, exit
72                     ('Screen', 'EMPTY', 'WINDOW', [    # full screen, undo, screenshot
73                         ('Screen Editing', 'EMPTY', 'WINDOW', []),    # resizing, action corners
74                         ]),
75
76                     ('View2D', 'EMPTY', 'WINDOW', []),    # view 2d navigation (per region)
77                     ('View2D Buttons List', 'EMPTY', 'WINDOW', []), # view 2d with buttons navigation
78                     ('Header', 'EMPTY', 'WINDOW', []),    # header stuff (per region)
79                     ('Grease Pencil', 'EMPTY', 'WINDOW', []), # grease pencil stuff (per region)
80
81                     ('3D View', 'VIEW_3D', 'WINDOW', [ # view 3d navigation and generic stuff (select, transform)
82                         ('Object Mode', 'EMPTY', 'WINDOW', []),
83                         ('Mesh', 'EMPTY', 'WINDOW', []),
84                         ('Curve', 'EMPTY', 'WINDOW', []),
85                         ('Armature', 'EMPTY', 'WINDOW', []),
86                         ('Metaball', 'EMPTY', 'WINDOW', []),
87                         ('Lattice', 'EMPTY', 'WINDOW', []),
88                         ('Font', 'EMPTY', 'WINDOW', []),
89
90                         ('Pose', 'EMPTY', 'WINDOW', []),
91
92                         ('Vertex Paint', 'EMPTY', 'WINDOW', []),
93                         ('Weight Paint', 'EMPTY', 'WINDOW', []),
94                         ('Face Mask', 'EMPTY', 'WINDOW', []),
95                         ('Image Paint', 'EMPTY', 'WINDOW', []), # image and view3d
96                         ('Sculpt', 'EMPTY', 'WINDOW', []),
97
98                         ('Armature Sketch', 'EMPTY', 'WINDOW', []),
99                         ('Particle', 'EMPTY', 'WINDOW', []),
100
101                         ('Object Non-modal', 'EMPTY', 'WINDOW', []), # mode change
102
103                         ('3D View Generic', 'VIEW_3D', 'WINDOW', [])    # toolbar and properties
104                         ]),
105
106                     ('Frames', 'EMPTY', 'WINDOW', []),    # frame navigation (per region)
107                     ('Markers', 'EMPTY', 'WINDOW', []),    # markers (per region)
108                     ('Animation', 'EMPTY', 'WINDOW', []),    # frame change on click, preview range (per region)
109                     ('Animation Channels', 'EMPTY', 'WINDOW', []),
110                     ('Graph Editor', 'GRAPH_EDITOR', 'WINDOW', [
111                         ('Graph Editor Generic', 'GRAPH_EDITOR', 'WINDOW', [])
112                         ]),
113                     ('Dopesheet', 'DOPESHEET_EDITOR', 'WINDOW', []),
114                     ('NLA Editor', 'NLA_EDITOR', 'WINDOW', [
115                         ('NLA Channels', 'NLA_EDITOR', 'WINDOW', []),
116                         ('NLA Generic', 'NLA_EDITOR', 'WINDOW', [])
117                         ]),
118
119                     ('Image', 'IMAGE_EDITOR', 'WINDOW', [
120                         ('UV Editor', 'EMPTY', 'WINDOW', []), # image (reverse order, UVEdit before Image
121                         ('Image Paint', 'EMPTY', 'WINDOW', []), # image and view3d
122                         ('Image Generic', 'IMAGE_EDITOR', 'WINDOW', [])
123                         ]),
124
125                     ('Timeline', 'TIMELINE', 'WINDOW', []),
126                     ('Outliner', 'OUTLINER', 'WINDOW', []),
127
128                     ('Node Editor', 'NODE_EDITOR', 'WINDOW', [
129                         ('Node Generic', 'NODE_EDITOR', 'WINDOW', [])
130                         ]),
131                     ('Sequencer', 'SEQUENCE_EDITOR', 'WINDOW', []),
132                     ('Logic Editor', 'LOGIC_EDITOR', 'WINDOW', []),
133
134                     ('File Browser', 'FILE_BROWSER', 'WINDOW', [
135                         ('File Browser Main', 'FILE_BROWSER', 'WINDOW', []),
136                         ('File Browser Buttons', 'FILE_BROWSER', 'WINDOW', [])
137                         ]),
138
139                     ('Property Editor', 'PROPERTIES', 'WINDOW', []), # align context menu
140
141                     ('Script', 'SCRIPTS_WINDOW', 'WINDOW', []),
142                     ('Text', 'TEXT_EDITOR', 'WINDOW', []),
143                     ('Console', 'CONSOLE', 'WINDOW', []),
144
145                     ('View3D Gesture Circle', 'EMPTY', 'WINDOW', []),
146                     ('Gesture Border', 'EMPTY', 'WINDOW', []),
147                     ('Standard Modal Map', 'EMPTY', 'WINDOW', []),
148                     ('Transform Modal Map', 'EMPTY', 'WINDOW', []),
149                     ('View3D Fly Modal', 'EMPTY', 'WINDOW', []),
150                     ('View3D Rotate Modal', 'EMPTY', 'WINDOW', []),
151                     ('View3D Move Modal', 'EMPTY', 'WINDOW', []),
152                     ('View3D Zoom Modal', 'EMPTY', 'WINDOW', []),
153                 ]
154
155
156 class USERPREF_HT_header(bpy.types.Header):
157     bl_space_type = 'USER_PREFERENCES'
158
159     def draw(self, context):
160         layout = self.layout
161         layout.template_header(menus=False)
162
163         userpref = context.user_preferences
164
165         layout.operator_context = 'EXEC_AREA'
166         layout.operator("wm.save_homefile", text="Save As Default")
167
168         layout.operator_context = 'INVOKE_DEFAULT'
169
170         if userpref.active_section == 'INPUT':
171             op = layout.operator("wm.keyconfig_export")
172             op.path = "keymap.py"
173             op = layout.operator("wm.keyconfig_import")
174             op.path = "keymap.py"
175         elif userpref.active_section == 'ADDONS':
176             op = layout.operator("wm.addon_install")
177             op.path = "*.py"
178         elif userpref.active_section == 'THEMES':
179             op = layout.operator("ui.reset_default_theme")
180
181
182 class USERPREF_PT_tabs(bpy.types.Panel):
183     bl_label = ""
184     bl_space_type = 'USER_PREFERENCES'
185     bl_region_type = 'WINDOW'
186     bl_show_header = False
187
188     def draw(self, context):
189         layout = self.layout
190
191         userpref = context.user_preferences
192
193         layout.prop(userpref, "active_section", expand=True)
194
195
196 class USERPREF_PT_interface(bpy.types.Panel):
197     bl_space_type = 'USER_PREFERENCES'
198     bl_label = "Interface"
199     bl_region_type = 'WINDOW'
200     bl_show_header = False
201
202     def poll(self, context):
203         userpref = context.user_preferences
204         return (userpref.active_section == 'INTERFACE')
205
206     def draw(self, context):
207         layout = self.layout
208
209         userpref = context.user_preferences
210         view = userpref.view
211
212         row = layout.row()
213
214         col = row.column()
215         col.label(text="Display:")
216         col.prop(view, "tooltips")
217         col.prop(view, "display_object_info", text="Object Info")
218         col.prop(view, "use_large_cursors")
219         col.prop(view, "show_view_name", text="View Name")
220         col.prop(view, "show_playback_fps", text="Playback FPS")
221         col.prop(view, "global_scene")
222         col.prop(view, "pin_floating_panels")
223         col.prop(view, "object_origin_size")
224
225         col.separator()
226         col.separator()
227         col.separator()
228
229         col.prop(view, "show_mini_axis", text="Display Mini Axis")
230         sub = col.column()
231         sub.enabled = view.show_mini_axis
232         sub.prop(view, "mini_axis_size", text="Size")
233         sub.prop(view, "mini_axis_brightness", text="Brightness")
234
235         row.separator()
236         row.separator()
237
238         col = row.column()
239         col.label(text="View Manipulation:")
240         col.prop(view, "auto_depth")
241         col.prop(view, "zoom_to_mouse")
242         col.prop(view, "rotate_around_selection")
243         col.prop(view, "global_pivot")
244
245         col.separator()
246
247         col.prop(view, "auto_perspective")
248         col.prop(view, "smooth_view")
249         col.prop(view, "rotation_angle")
250
251         col.separator()
252         col.separator()
253
254         col.label(text="2D Viewports:")
255         col.prop(view, "view2d_grid_minimum_spacing", text="Minimum Grid Spacing")
256         col.prop(view, "timecode_style")
257
258         row.separator()
259         row.separator()
260
261         col = row.column()
262         #Toolbox doesn't exist yet
263         #col.label(text="Toolbox:")
264         #col.prop(view, "use_column_layout")
265         #col.label(text="Open Toolbox Delay:")
266         #col.prop(view, "open_left_mouse_delay", text="Hold LMB")
267         #col.prop(view, "open_right_mouse_delay", text="Hold RMB")
268         col.prop(view, "use_manipulator")
269         sub = col.column()
270         sub.enabled = view.use_manipulator
271         sub.prop(view, "manipulator_size", text="Size")
272         sub.prop(view, "manipulator_handle_size", text="Handle Size")
273         sub.prop(view, "manipulator_hotspot", text="Hotspot")
274
275         col.separator()
276         col.separator()
277         col.separator()
278
279         col.label(text="Menus:")
280         col.prop(view, "open_mouse_over")
281         col.label(text="Menu Open Delay:")
282         col.prop(view, "open_toplevel_delay", text="Top Level")
283         col.prop(view, "open_sublevel_delay", text="Sub Level")
284
285         col.separator()
286
287         col.prop(view, "show_splash")
288
289
290 class USERPREF_PT_edit(bpy.types.Panel):
291     bl_space_type = 'USER_PREFERENCES'
292     bl_label = "Edit"
293     bl_region_type = 'WINDOW'
294     bl_show_header = False
295
296     def poll(self, context):
297         userpref = context.user_preferences
298         return (userpref.active_section == 'EDITING')
299
300     def draw(self, context):
301         layout = self.layout
302
303         userpref = context.user_preferences
304         edit = userpref.edit
305
306         row = layout.row()
307
308         col = row.column()
309         col.label(text="Link Materials To:")
310         col.prop(edit, "material_link", text="")
311
312         col.separator()
313         col.separator()
314         col.separator()
315
316         col.label(text="New Objects:")
317         col.prop(edit, "enter_edit_mode")
318         col.label(text="Align To:")
319         col.prop(edit, "object_align", text="")
320
321         col.separator()
322         col.separator()
323         col.separator()
324
325         col.label(text="Undo:")
326         col.prop(edit, "global_undo")
327         col.prop(edit, "undo_steps", text="Steps")
328         col.prop(edit, "undo_memory_limit", text="Memory Limit")
329
330         row.separator()
331         row.separator()
332
333         col = row.column()
334         col.label(text="Snap:")
335         col.prop(edit, "snap_translate", text="Translate")
336         col.prop(edit, "snap_rotate", text="Rotate")
337         col.prop(edit, "snap_scale", text="Scale")
338         col.separator()
339         col.separator()
340         col.separator()
341         col.label(text="Grease Pencil:")
342         col.prop(edit, "grease_pencil_manhattan_distance", text="Manhattan Distance")
343         col.prop(edit, "grease_pencil_euclidean_distance", text="Euclidean Distance")
344         #col.prop(edit, "grease_pencil_simplify_stroke", text="Simplify Stroke")
345         col.prop(edit, "grease_pencil_eraser_radius", text="Eraser Radius")
346         col.prop(edit, "grease_pencil_smooth_stroke", text="Smooth Stroke")
347         col.separator()
348         col.separator()
349         col.separator()
350         col.label(text="Playback:")
351         col.prop(edit, "use_negative_frames")
352
353         row.separator()
354         row.separator()
355
356         col = row.column()
357         col.label(text="Keyframing:")
358         col.prop(edit, "use_visual_keying")
359         col.prop(edit, "keyframe_insert_needed", text="Only Insert Needed")
360
361         col.separator()
362
363         col.prop(edit, "use_auto_keying", text="Auto Keyframing:")
364
365         sub = col.column()
366
367         # sub.active = edit.use_auto_keying # incorrect, timeline can enable
368         sub.prop(edit, "auto_keyframe_insert_keyingset", text="Only Insert for Keying Set")
369         sub.prop(edit, "auto_keyframe_insert_available", text="Only Insert Available")
370
371         col.separator()
372
373         col.label(text="New F-Curve Defaults:")
374         col.prop(edit, "keyframe_new_interpolation_type", text="Interpolation")
375         col.prop(edit, "keyframe_new_handle_type", text="Handles")
376         col.prop(edit, "insertkey_xyz_to_rgb", text="XYZ to RGB")
377
378         col.separator()
379         col.separator()
380         col.separator()
381
382         col.label(text="Transform:")
383         col.prop(edit, "drag_immediately")
384
385         row.separator()
386         row.separator()
387
388         col = row.column()
389         col.label(text="Duplicate Data:")
390         col.prop(edit, "duplicate_mesh", text="Mesh")
391         col.prop(edit, "duplicate_surface", text="Surface")
392         col.prop(edit, "duplicate_curve", text="Curve")
393         col.prop(edit, "duplicate_text", text="Text")
394         col.prop(edit, "duplicate_metaball", text="Metaball")
395         col.prop(edit, "duplicate_armature", text="Armature")
396         col.prop(edit, "duplicate_lamp", text="Lamp")
397         col.prop(edit, "duplicate_material", text="Material")
398         col.prop(edit, "duplicate_texture", text="Texture")
399         col.prop(edit, "duplicate_fcurve", text="F-Curve")
400         col.prop(edit, "duplicate_action", text="Action")
401         col.prop(edit, "duplicate_particle", text="Particle")
402
403
404 class USERPREF_PT_system(bpy.types.Panel):
405     bl_space_type = 'USER_PREFERENCES'
406     bl_label = "System"
407     bl_region_type = 'WINDOW'
408     bl_show_header = False
409
410     def poll(self, context):
411         userpref = context.user_preferences
412         return (userpref.active_section == 'SYSTEM')
413
414     def draw(self, context):
415         layout = self.layout
416
417         userpref = context.user_preferences
418         system = userpref.system
419
420         split = layout.split()
421
422
423         # 1. Column
424         column = split.column()
425         colsplit = column.split(percentage=0.85)
426
427         col = colsplit.column()
428         col.label(text="General:")
429         col.prop(system, "dpi")
430         col.prop(system, "frame_server_port")
431         col.prop(system, "scrollback", text="Console Scrollback")
432         col.prop(system, "auto_execute_scripts")
433         col.prop(system, "tabs_as_spaces")
434
435         col.separator()
436         col.separator()
437         col.separator()
438
439         col.label(text="Sound:")
440         col.row().prop(system, "audio_device", expand=True)
441         sub = col.column()
442         sub.active = system.audio_device != 'NONE'
443         #sub.prop(system, "enable_all_codecs")
444         sub.prop(system, "audio_channels", text="Channels")
445         sub.prop(system, "audio_mixing_buffer", text="Mixing Buffer")
446         sub.prop(system, "audio_sample_rate", text="Sample Rate")
447         sub.prop(system, "audio_sample_format", text="Sample Format")
448
449         col.separator()
450         col.separator()
451         col.separator()
452
453         col.label(text="Screencast:")
454         col.prop(system, "screencast_fps")
455         col.prop(system, "screencast_wait_time")
456         col.separator()
457         col.separator()
458         col.separator()
459
460         #column = split.column()
461         #colsplit = column.split(percentage=0.85)
462
463         # No translation in 2.5 yet
464         #col.prop(system, "language")
465         #col.label(text="Translate:")
466         #col.prop(system, "translate_tooltips", text="Tooltips")
467         #col.prop(system, "translate_buttons", text="Labels")
468         #col.prop(system, "translate_toolbox", text="Toolbox")
469
470         #col.separator()
471
472         #col.prop(system, "use_textured_fonts")
473
474
475         # 2. Column
476         column = split.column()
477         colsplit = column.split(percentage=0.85)
478
479         col = colsplit.column()
480         col.label(text="OpenGL:")
481         col.prop(system, "clip_alpha", slider=True)
482         col.prop(system, "use_mipmaps")
483         col.prop(system, "use_vbos")
484         #Anti-aliasing is disabled as it breaks broder/lasso select
485         #col.prop(system, "use_antialiasing")
486         col.label(text="Window Draw Method:")
487         col.prop(system, "window_draw_method", text="")
488         col.label(text="Textures:")
489         col.prop(system, "gl_texture_limit", text="Limit Size")
490         col.prop(system, "texture_time_out", text="Time Out")
491         col.prop(system, "texture_collection_rate", text="Collection Rate")
492
493         col.separator()
494         col.separator()
495         col.separator()
496
497         col.label(text="Sequencer:")
498         col.prop(system, "prefetch_frames")
499         col.prop(system, "memory_cache_limit")
500
501
502         # 3. Column
503         column = split.column()
504
505         column.label(text="Solid OpenGL lights:")
506
507         split = column.split(percentage=0.1)
508         split.label()
509         split.label(text="Colors:")
510         split.label(text="Direction:")
511
512         lamp = system.solid_lights[0]
513         opengl_lamp_buttons(column, lamp)
514
515         lamp = system.solid_lights[1]
516         opengl_lamp_buttons(column, lamp)
517
518         lamp = system.solid_lights[2]
519         opengl_lamp_buttons(column, lamp)
520
521         column.separator()
522         column.separator()
523         column.separator()
524
525         column.label(text="Color Picker Type:")
526         column.row().prop(system, "color_picker_type", text="")
527
528         column.separator()
529         column.separator()
530         column.separator()
531
532         column.prop(system, "use_weight_color_range", text="Custom Weight Paint Range")
533         sub = column.column()
534         sub.active = system.use_weight_color_range
535         sub.template_color_ramp(system, "weight_color_range", expand=True)
536
537
538 class USERPREF_PT_theme(bpy.types.Panel):
539     bl_space_type = 'USER_PREFERENCES'
540     bl_label = "Themes"
541     bl_region_type = 'WINDOW'
542     bl_show_header = False
543
544     def poll(self, context):
545         userpref = context.user_preferences
546         return (userpref.active_section == 'THEMES')
547
548     def draw(self, context):
549         layout = self.layout
550
551         theme = context.user_preferences.themes[0]
552
553         split_themes = layout.split(percentage=0.2)
554         split_themes.prop(theme, "theme_area", expand=True)
555
556         split = split_themes.split()
557
558         if theme.theme_area == 'USER_INTERFACE':
559             col = split.column()
560
561             ui = theme.user_interface.wcol_regular
562             col.label(text="Regular:")
563             ui_items_general(col, ui)
564
565             ui = theme.user_interface.wcol_tool
566             col.label(text="Tool:")
567             ui_items_general(col, ui)
568
569             ui = theme.user_interface.wcol_radio
570             col.label(text="Radio Buttons:")
571             ui_items_general(col, ui)
572
573             ui = theme.user_interface.wcol_text
574             col.label(text="Text:")
575             ui_items_general(col, ui)
576
577             ui = theme.user_interface.wcol_option
578             col.label(text="Option:")
579             ui_items_general(col, ui)
580
581             ui = theme.user_interface.wcol_toggle
582             col.label(text="Toggle:")
583             ui_items_general(col, ui)
584
585             ui = theme.user_interface.wcol_num
586             col.label(text="Number Field:")
587             ui_items_general(col, ui)
588
589             ui = theme.user_interface.wcol_numslider
590             col.label(text="Value Slider:")
591             ui_items_general(col, ui)
592
593             ui = theme.user_interface.wcol_box
594             col.label(text="Box:")
595             ui_items_general(col, ui)
596
597             ui = theme.user_interface.wcol_menu
598             col.label(text="Menu:")
599             ui_items_general(col, ui)
600
601             ui = theme.user_interface.wcol_pulldown
602             col.label(text="Pulldown:")
603             ui_items_general(col, ui)
604
605             ui = theme.user_interface.wcol_menu_back
606             col.label(text="Menu Back:")
607             ui_items_general(col, ui)
608
609             ui = theme.user_interface.wcol_menu_item
610             col.label(text="Menu Item:")
611             ui_items_general(col, ui)
612
613             ui = theme.user_interface.wcol_scroll
614             col.label(text="Scroll Bar:")
615             ui_items_general(col, ui)
616
617             ui = theme.user_interface.wcol_list_item
618             col.label(text="List Item:")
619             ui_items_general(col, ui)
620
621             ui = theme.user_interface.wcol_state
622             col.label(text="State:")
623
624             row = col.row()
625             sub = row.column()
626             sub.prop(ui, "inner_anim")
627             sub.prop(ui, "inner_anim_sel")
628             sub = row.column()
629             sub.prop(ui, "inner_driven")
630             sub.prop(ui, "inner_driven_sel")
631             sub = row.column()
632             sub.prop(ui, "inner_key")
633             sub.prop(ui, "inner_key_sel")
634             sub = row.column()
635             sub.prop(ui, "blend")
636
637             ui = theme.user_interface
638             col.separator()
639             col.separator()
640             col.prop(ui, "icon_file")
641
642             layout.separator()
643             layout.separator()
644
645
646         elif theme.theme_area == 'VIEW_3D':
647             v3d = theme.view_3d
648
649             col = split.column()
650             col.prop(v3d, "back")
651             col.prop(v3d, "button")
652             col.prop(v3d, "button_title")
653             col.prop(v3d, "button_text")
654             col.prop(v3d, "header")
655
656             col = split.column()
657             col.prop(v3d, "grid")
658             col.prop(v3d, "wire")
659             col.prop(v3d, "lamp", slider=True)
660             col.prop(v3d, "editmesh_active", slider=True)
661
662             col = split.column()
663             col.prop(v3d, "object_selected")
664             col.prop(v3d, "object_active")
665             col.prop(v3d, "object_grouped")
666             col.prop(v3d, "object_grouped_active")
667             col.prop(v3d, "transform")
668             col.prop(v3d, "nurb_uline")
669             col.prop(v3d, "nurb_vline")
670             col.prop(v3d, "nurb_sel_uline")
671             col.prop(v3d, "nurb_sel_vline")
672             col.prop(v3d, "handle_free")
673             col.prop(v3d, "handle_auto")
674             col.prop(v3d, "handle_vect")
675             col.prop(v3d, "handle_align")
676             col.prop(v3d, "handle_sel_free")
677             col.prop(v3d, "handle_sel_auto")
678             col.prop(v3d, "handle_sel_vect")
679             col.prop(v3d, "handle_sel_align")
680             col.prop(v3d, "act_spline")
681
682             col = split.column()
683             col.prop(v3d, "vertex")
684             col.prop(v3d, "face", slider=True)
685             col.prop(v3d, "normal")
686             col.prop(v3d, "vertex_normal")
687             col.prop(v3d, "bone_solid")
688             col.prop(v3d, "bone_pose")
689             col.prop(v3d, "edge_seam")
690             col.prop(v3d, "edge_sharp")
691             col.prop(v3d, "edge_crease")
692             #col.prop(v3d, "edge") Doesn't seem to work
693
694         elif theme.theme_area == 'GRAPH_EDITOR':
695             graph = theme.graph_editor
696
697             col = split.column()
698             col.prop(graph, "back")
699             col.prop(graph, "button")
700             col.prop(graph, "button_title")
701             col.prop(graph, "button_text")
702
703             col = split.column()
704             col.prop(graph, "header")
705             col.prop(graph, "grid")
706             col.prop(graph, "list")
707             col.prop(graph, "channel_group")
708
709             col = split.column()
710             col.prop(graph, "active_channels_group")
711             col.prop(graph, "dopesheet_channel")
712             col.prop(graph, "dopesheet_subchannel")
713             col.prop(graph, "frame_current")
714
715             col = split.column()
716             col.prop(graph, "vertex")
717             col.prop(graph, "handle_vertex")
718             col.prop(graph, "handle_vertex_select")
719             col.separator()
720             col.prop(graph, "handle_vertex_size")
721             col.separator()
722             col.separator()
723             col.prop(graph, "handle_free")
724             col.prop(graph, "handle_auto")
725             col.prop(graph, "handle_vect")
726             col.prop(graph, "handle_align")
727             col.prop(graph, "handle_sel_free")
728             col.prop(graph, "handle_sel_auto")
729             col.prop(graph, "handle_sel_vect")
730             col.prop(graph, "handle_sel_align")
731
732         elif theme.theme_area == 'FILE_BROWSER':
733             file_browse = theme.file_browser
734
735             col = split.column()
736             col.prop(file_browse, "back")
737             col.prop(file_browse, "text")
738             col.prop(file_browse, "text_hi")
739
740             col = split.column()
741             col.prop(file_browse, "header")
742             col.prop(file_browse, "list")
743
744             col = split.column()
745             col.prop(file_browse, "selected_file")
746             col.prop(file_browse, "tiles")
747
748             col = split.column()
749             col.prop(file_browse, "active_file")
750             col.prop(file_browse, "active_file_text")
751
752         elif theme.theme_area == 'NLA_EDITOR':
753             nla = theme.nla_editor
754
755             col = split.column()
756             col.prop(nla, "back")
757             col.prop(nla, "button")
758             col.prop(nla, "button_title")
759
760             col = split.column()
761             col.prop(nla, "button_text")
762             col.prop(nla, "text")
763             col.prop(nla, "header")
764
765             col = split.column()
766             col.prop(nla, "grid")
767             col.prop(nla, "bars")
768             col.prop(nla, "bars_selected")
769
770             col = split.column()
771             col.prop(nla, "strips")
772             col.prop(nla, "strips_selected")
773             col.prop(nla, "frame_current")
774
775         elif theme.theme_area == 'DOPESHEET_EDITOR':
776             dope = theme.dopesheet_editor
777
778             col = split.column()
779             col.prop(dope, "back")
780             col.prop(dope, "list")
781             col.prop(dope, "text")
782             col.prop(dope, "header")
783
784             col = split.column()
785             col.prop(dope, "grid")
786             col.prop(dope, "channels")
787             col.prop(dope, "channels_selected")
788             col.prop(dope, "channel_group")
789
790             col = split.column()
791             col.prop(dope, "active_channels_group")
792             col.prop(dope, "long_key")
793             col.prop(dope, "long_key_selected")
794
795             col = split.column()
796             col.prop(dope, "frame_current")
797             col.prop(dope, "dopesheet_channel")
798             col.prop(dope, "dopesheet_subchannel")
799
800         elif theme.theme_area == 'IMAGE_EDITOR':
801             image = theme.image_editor
802
803             col = split.column()
804             col.prop(image, "back")
805             col.prop(image, "scope_back")
806             col.prop(image, "button")
807
808             col = split.column()
809             col.prop(image, "button_title")
810             col.prop(image, "button_text")
811
812             col = split.column()
813             col.prop(image, "header")
814
815             col = split.column()
816             col.prop(image, "editmesh_active", slider=True)
817
818         elif theme.theme_area == 'SEQUENCE_EDITOR':
819             seq = theme.sequence_editor
820
821             col = split.column()
822             col.prop(seq, "back")
823             col.prop(seq, "button")
824             col.prop(seq, "button_title")
825             col.prop(seq, "button_text")
826             col.prop(seq, "text")
827
828             col = split.column()
829             col.prop(seq, "header")
830             col.prop(seq, "grid")
831             col.prop(seq, "movie_strip")
832             col.prop(seq, "image_strip")
833             col.prop(seq, "scene_strip")
834
835             col = split.column()
836             col.prop(seq, "audio_strip")
837             col.prop(seq, "effect_strip")
838             col.prop(seq, "plugin_strip")
839             col.prop(seq, "transition_strip")
840
841             col = split.column()
842             col.prop(seq, "meta_strip")
843             col.prop(seq, "frame_current")
844             col.prop(seq, "keyframe")
845             col.prop(seq, "draw_action")
846
847         elif theme.theme_area == 'PROPERTIES':
848             prop = theme.properties
849
850             col = split.column()
851             col.prop(prop, "back")
852
853             col = split.column()
854             col.prop(prop, "title")
855
856             col = split.column()
857             col.prop(prop, "text")
858
859             col = split.column()
860             col.prop(prop, "header")
861
862         elif theme.theme_area == 'TEXT_EDITOR':
863             text = theme.text_editor
864
865             col = split.column()
866             col.prop(text, "back")
867             col.prop(text, "button")
868             col.prop(text, "button_title")
869             col.prop(text, "button_text")
870
871             col = split.column()
872             col.prop(text, "text")
873             col.prop(text, "text_hi")
874             col.prop(text, "header")
875             col.prop(text, "line_numbers_background")
876
877             col = split.column()
878             col.prop(text, "selected_text")
879             col.prop(text, "cursor")
880             col.prop(text, "syntax_builtin")
881             col.prop(text, "syntax_special")
882
883             col = split.column()
884             col.prop(text, "syntax_comment")
885             col.prop(text, "syntax_string")
886             col.prop(text, "syntax_numbers")
887
888         elif theme.theme_area == 'TIMELINE':
889             time = theme.timeline
890
891             col = split.column()
892             col.prop(time, "back")
893             col.prop(time, "text")
894
895             col = split.column()
896             col.prop(time, "header")
897
898             col = split.column()
899             col.prop(time, "grid")
900
901             col = split.column()
902             col.prop(time, "frame_current")
903
904         elif theme.theme_area == 'NODE_EDITOR':
905             node = theme.node_editor
906
907             col = split.column()
908             col.prop(node, "back")
909             col.prop(node, "button")
910             col.prop(node, "button_title")
911             col.prop(node, "button_text")
912
913             col = split.column()
914             col.prop(node, "text")
915             col.prop(node, "text_hi")
916             col.prop(node, "header")
917             col.prop(node, "wires")
918
919             col = split.column()
920             col.prop(node, "wire_select")
921             col.prop(node, "selected_text")
922             col.prop(node, "node_backdrop", slider=True)
923             col.prop(node, "in_out_node")
924
925             col = split.column()
926             col.prop(node, "converter_node")
927             col.prop(node, "operator_node")
928             col.prop(node, "group_node")
929
930         elif theme.theme_area == 'LOGIC_EDITOR':
931             logic = theme.logic_editor
932
933             col = split.column()
934             col.prop(logic, "back")
935             col.prop(logic, "button")
936
937             col = split.column()
938             col.prop(logic, "button_title")
939             col.prop(logic, "button_text")
940
941             col = split.column()
942             col.prop(logic, "text")
943             col.prop(logic, "header")
944
945             col = split.column()
946             col.prop(logic, "panel")
947
948         elif theme.theme_area == 'OUTLINER':
949             out = theme.outliner
950
951             col = split.column()
952             col.prop(out, "back")
953
954             col = split.column()
955             col.prop(out, "text")
956
957             col = split.column()
958             col.prop(out, "text_hi")
959
960             col = split.column()
961             col.prop(out, "header")
962
963         elif theme.theme_area == 'INFO':
964             info = theme.info
965
966             col = split.column()
967             col.prop(info, "back")
968
969             col = split.column()
970             col.prop(info, "header")
971
972             col = split.column()
973             col.prop(info, "header_text")
974
975             col = split.column()
976
977         elif theme.theme_area == 'USER_PREFERENCES':
978             prefs = theme.user_preferences
979
980             col = split.column()
981             col.prop(prefs, "back")
982
983             col = split.column()
984             col.prop(prefs, "text")
985
986             col = split.column()
987             col.prop(prefs, "header")
988
989             col = split.column()
990             col.prop(prefs, "header_text")
991
992         elif theme.theme_area == 'CONSOLE':
993             prefs = theme.console
994
995             col = split.column()
996             col.prop(prefs, "back")
997             col.prop(prefs, "header")
998
999             col = split.column()
1000             col.prop(prefs, "line_output")
1001             col.prop(prefs, "line_input")
1002             col.prop(prefs, "line_info")
1003             col.prop(prefs, "line_error")
1004             col.prop(prefs, "cursor")
1005
1006
1007 class USERPREF_PT_file(bpy.types.Panel):
1008     bl_space_type = 'USER_PREFERENCES'
1009     bl_label = "Files"
1010     bl_region_type = 'WINDOW'
1011     bl_show_header = False
1012
1013     def poll(self, context):
1014         userpref = context.user_preferences
1015         return (userpref.active_section == 'FILES')
1016
1017     def draw(self, context):
1018         layout = self.layout
1019
1020         userpref = context.user_preferences
1021         paths = userpref.filepaths
1022
1023         split = layout.split(percentage=0.7)
1024
1025         col = split.column()
1026         col.label(text="File Paths:")
1027
1028         colsplit = col.split(percentage=0.95)
1029         col1 = colsplit.split(percentage=0.3)
1030
1031         sub = col1.column()
1032         sub.label(text="Fonts:")
1033         sub.label(text="Textures:")
1034         sub.label(text="Texture Plugins:")
1035         sub.label(text="Sequence Plugins:")
1036         sub.label(text="Render Output:")
1037         sub.label(text="Scripts:")
1038         sub.label(text="Sounds:")
1039         sub.label(text="Temp:")
1040         sub.label(text="Image Editor:")
1041         sub.label(text="Animation Player:")
1042
1043         sub = col1.column()
1044         sub.prop(paths, "fonts_directory", text="")
1045         sub.prop(paths, "textures_directory", text="")
1046         sub.prop(paths, "texture_plugin_directory", text="")
1047         sub.prop(paths, "sequence_plugin_directory", text="")
1048         sub.prop(paths, "render_output_directory", text="")
1049         sub.prop(paths, "python_scripts_directory", text="")
1050         sub.prop(paths, "sounds_directory", text="")
1051         sub.prop(paths, "temporary_directory", text="")
1052         sub.prop(paths, "image_editor", text="")
1053         subsplit = sub.split(percentage=0.3)
1054         subsplit.prop(paths, "animation_player_preset", text="")
1055         subsplit.prop(paths, "animation_player", text="")
1056
1057         col = split.column()
1058         col.label(text="Save & Load:")
1059         col.prop(paths, "use_relative_paths")
1060         col.prop(paths, "compress_file")
1061         col.prop(paths, "load_ui")
1062         col.prop(paths, "filter_file_extensions")
1063         col.prop(paths, "hide_dot_files_datablocks")
1064
1065         col.separator()
1066         col.separator()
1067
1068         col.label(text="Auto Save:")
1069         col.prop(paths, "save_version")
1070         col.prop(paths, "recent_files")
1071         col.prop(paths, "save_preview_images")
1072         col.prop(paths, "auto_save_temporary_files")
1073         sub = col.column()
1074         sub.enabled = paths.auto_save_temporary_files
1075         sub.prop(paths, "auto_save_time", text="Timer (mins)")
1076
1077
1078 class USERPREF_PT_input(bpy.types.Panel):
1079     bl_space_type = 'USER_PREFERENCES'
1080     bl_label = "Input"
1081     bl_region_type = 'WINDOW'
1082     bl_show_header = False
1083
1084     def poll(self, context):
1085         userpref = context.user_preferences
1086         return (userpref.active_section == 'INPUT')
1087
1088     def draw_entry(self, kc, entry, col, level=0):
1089         idname, spaceid, regionid, children = entry
1090
1091         km = kc.find_keymap(idname, space_type=spaceid, region_type=regionid)
1092
1093         if km:
1094             self.draw_km(kc, km, children, col, level)
1095
1096     def indented_layout(self, layout, level):
1097         indentpx = 16
1098         if level == 0:
1099             level = 0.0001   # Tweak so that a percentage of 0 won't split by half
1100         indent = level * indentpx / bpy.context.region.width
1101
1102         split = layout.split(percentage=indent)
1103         col = split.column()
1104         col = split.column()
1105         return col
1106
1107     def draw_km(self, kc, km, children, layout, level):
1108         km = km.active()
1109
1110         layout.set_context_pointer("keymap", km)
1111
1112         col = self.indented_layout(layout, level)
1113
1114         row = col.row()
1115         row.prop(km, "children_expanded", text="", no_bg=True)
1116         row.label(text=km.name)
1117
1118         row.label()
1119         row.label()
1120
1121         if km.modal:
1122             row.label(text="", icon='LINKED')
1123         if km.user_defined:
1124             op = row.operator("wm.keymap_restore", text="Restore")
1125         else:
1126             op = row.operator("wm.keymap_edit", text="Edit")
1127
1128         if km.children_expanded:
1129             if children:
1130                 # Put the Parent key map's entries in a 'global' sub-category
1131                 # equal in hierarchy to the other children categories
1132                 subcol = self.indented_layout(col, level + 1)
1133                 subrow = subcol.row()
1134                 subrow.prop(km, "items_expanded", text="", no_bg=True)
1135                 subrow.label(text="%s (Global)" % km.name)
1136             else:
1137                 km.items_expanded = True
1138
1139             # Key Map items
1140             if km.items_expanded:
1141                 for kmi in km.items:
1142                     self.draw_kmi(kc, km, kmi, col, level + 1)
1143
1144                 # "Add New" at end of keymap item list
1145                 col = self.indented_layout(col, level + 1)
1146                 subcol = col.split(percentage=0.2).column()
1147                 subcol.enabled = km.user_defined
1148                 op = subcol.operator("wm.keyitem_add", text="Add New", icon='ZOOMIN')
1149
1150             col.separator()
1151
1152             # Child key maps
1153             if children:
1154                 subcol = col.column()
1155                 row = subcol.row()
1156
1157                 for entry in children:
1158                     self.draw_entry(kc, entry, col, level + 1)
1159
1160     def draw_kmi(self, kc, km, kmi, layout, level):
1161         map_type = kmi.map_type
1162
1163         col = self.indented_layout(layout, level)
1164
1165         if km.user_defined:
1166             col = col.column(align=True)
1167             box = col.box()
1168         else:
1169             box = col.column()
1170
1171         split = box.split(percentage=0.05)
1172
1173         # header bar
1174         row = split.row()
1175         row.prop(kmi, "expanded", text="", no_bg=True)
1176
1177         row = split.row()
1178         row.enabled = km.user_defined
1179         row.prop(kmi, "active", text="", no_bg=True)
1180
1181         if km.modal:
1182             row.prop(kmi, "propvalue", text="")
1183         else:
1184             row.label(text=kmi.name)
1185
1186         row = split.row()
1187         row.enabled = km.user_defined
1188         row.prop(kmi, "map_type", text="")
1189         if map_type == 'KEYBOARD':
1190             row.prop(kmi, "type", text="", full_event=True)
1191         elif map_type == 'MOUSE':
1192             row.prop(kmi, "type", text="", full_event=True)
1193         elif map_type == 'TWEAK':
1194             subrow = row.row()
1195             subrow.prop(kmi, "type", text="")
1196             subrow.prop(kmi, "value", text="")
1197         elif map_type == 'TIMER':
1198             row.prop(kmi, "type", text="")
1199         else:
1200             row.label()
1201
1202         if kmi.id:
1203             op = row.operator("wm.keyitem_restore", text="", icon='BACK')
1204             op.item_id = kmi.id
1205         op = row.operator("wm.keyitem_remove", text="", icon='X')
1206         op.item_id = kmi.id
1207
1208         # Expanded, additional event settings
1209         if kmi.expanded:
1210             box = col.box()
1211
1212             box.enabled = km.user_defined
1213
1214             if map_type not in ('TEXTINPUT', 'TIMER'):
1215                 split = box.split(percentage=0.4)
1216                 sub = split.row()
1217
1218                 if km.modal:
1219                     sub.prop(kmi, "propvalue", text="")
1220                 else:
1221                     sub.prop(kmi, "idname", text="")
1222
1223                 sub = split.column()
1224                 subrow = sub.row(align=True)
1225
1226                 if map_type == 'KEYBOARD':
1227                     subrow.prop(kmi, "type", text="", event=True)
1228                     subrow.prop(kmi, "value", text="")
1229                 elif map_type == 'MOUSE':
1230                     subrow.prop(kmi, "type", text="")
1231                     subrow.prop(kmi, "value", text="")
1232
1233                 subrow = sub.row()
1234                 subrow.scale_x = 0.75
1235                 subrow.prop(kmi, "any")
1236                 subrow.prop(kmi, "shift")
1237                 subrow.prop(kmi, "ctrl")
1238                 subrow.prop(kmi, "alt")
1239                 subrow.prop(kmi, "oskey", text="Cmd")
1240                 subrow.prop(kmi, "key_modifier", text="", event=True)
1241
1242             def display_properties(properties, title=None):
1243                 box.separator()
1244                 if title:
1245                     box.label(text=title)
1246                 flow = box.column_flow(columns=2)
1247                 for pname in dir(properties):
1248                     if not properties.is_property_hidden(pname):
1249                         value = eval("properties." + pname)
1250                         if isinstance(value, bpy.types.OperatorProperties):
1251                             display_properties(value, title=pname)
1252                         else:
1253                             flow.prop(properties, pname)
1254
1255             # Operator properties
1256             props = kmi.properties
1257             if props is not None:
1258                 display_properties(props)
1259
1260             # Modal key maps attached to this operator
1261             if not km.modal:
1262                 kmm = kc.find_keymap_modal(kmi.idname)
1263                 if kmm:
1264                     self.draw_km(kc, kmm, None, layout, level + 1)
1265                     layout.set_context_pointer("keymap", km)
1266
1267     def draw_input_prefs(self, inputs, layout):
1268         # General settings
1269         row = layout.row()
1270         col = row.column()
1271
1272         sub = col.column()
1273         sub.label(text="Mouse:")
1274         sub1 = sub.column()
1275         sub1.enabled = (inputs.select_mouse == 'RIGHT')
1276         sub1.prop(inputs, "emulate_3_button_mouse")
1277         sub.prop(inputs, "continuous_mouse")
1278
1279         sub.label(text="Select With:")
1280         sub.row().prop(inputs, "select_mouse", expand=True)
1281
1282         sub = col.column()
1283         sub.label(text="Double Click:")
1284         sub.prop(inputs, "double_click_time", text="Speed")
1285
1286         sub.separator()
1287
1288         sub.prop(inputs, "emulate_numpad")
1289
1290         sub.separator()
1291
1292         sub.label(text="Orbit Style:")
1293         sub.row().prop(inputs, "view_rotation", expand=True)
1294
1295         sub.label(text="Zoom Style:")
1296         sub.row().prop(inputs, "zoom_style", text="")
1297         if inputs.zoom_style == 'DOLLY':
1298             sub.row().prop(inputs, "zoom_axis", expand=True)
1299             sub.prop(inputs, "invert_zoom_direction")
1300
1301         #sub.prop(inputs, "use_middle_mouse_paste")
1302
1303         #col.separator()
1304
1305         #sub = col.column()
1306         #sub.label(text="Mouse Wheel:")
1307         #sub.prop(view, "wheel_scroll_lines", text="Scroll Lines")
1308
1309         col.separator()
1310         ''' not implemented yet
1311         sub = col.column()
1312         sub.label(text="NDOF Device:")
1313         sub.prop(inputs, "ndof_pan_speed", text="Pan Speed")
1314         sub.prop(inputs, "ndof_rotate_speed", text="Orbit Speed")
1315         '''
1316
1317         row.separator()
1318
1319     def draw_filtered(self, kc, layout):
1320         filter = kc.filter.lower()
1321
1322         for km in kc.keymaps:
1323             km = km.active()
1324             layout.set_context_pointer("keymap", km)
1325
1326             filtered_items = [kmi for kmi in km.items if filter in kmi.name.lower()]
1327
1328             if len(filtered_items) != 0:
1329                 col = layout.column()
1330
1331                 row = col.row()
1332                 row.label(text=km.name, icon="DOT")
1333
1334                 row.label()
1335                 row.label()
1336
1337                 if km.user_defined:
1338                     op = row.operator("wm.keymap_restore", text="Restore")
1339                 else:
1340                     op = row.operator("wm.keymap_edit", text="Edit")
1341
1342                 for kmi in filtered_items:
1343                     self.draw_kmi(kc, km, kmi, col, 1)
1344
1345                 # "Add New" at end of keymap item list
1346                 col = self.indented_layout(layout, 1)
1347                 subcol = col.split(percentage=0.2).column()
1348                 subcol.enabled = km.user_defined
1349                 op = subcol.operator("wm.keyitem_add", text="Add New", icon='ZOOMIN')
1350
1351     def draw_hierarchy(self, defkc, layout):
1352         for entry in KM_HIERARCHY:
1353             self.draw_entry(defkc, entry, layout)
1354
1355     def draw(self, context):
1356         layout = self.layout
1357
1358         #import time
1359
1360         #start = time.time()
1361
1362         userpref = context.user_preferences
1363         wm = context.manager
1364
1365         inputs = userpref.inputs
1366
1367         split = layout.split(percentage=0.25)
1368
1369         # Input settings
1370         self.draw_input_prefs(inputs, split)
1371
1372         # Keymap Settings
1373         col = split.column()
1374         # kc = wm.active_keyconfig
1375         kc = wm.default_keyconfig
1376
1377         sub = col.column()
1378
1379         subsplit = sub.split()
1380         subcol = subsplit.column()
1381         row = subcol.row()
1382         row.prop_object(wm, "active_keyconfig", wm, "keyconfigs", text="Configuration:")
1383
1384         layout.set_context_pointer("keyconfig", wm.active_keyconfig)
1385         row.operator("wm.keyconfig_remove", text="", icon='X')
1386
1387         row.prop(kc, "filter", icon="VIEWZOOM")
1388
1389         col.separator()
1390
1391         if kc.filter != "":
1392             self.draw_filtered(kc, col)
1393         else:
1394             self.draw_hierarchy(kc, col)
1395
1396         #print("runtime", time.time() - start)
1397
1398
1399 class USERPREF_PT_addons(bpy.types.Panel):
1400     bl_space_type = 'USER_PREFERENCES'
1401     bl_label = "Addons"
1402     bl_region_type = 'WINDOW'
1403     bl_show_header = False
1404
1405     def poll(self, context):
1406         userpref = context.user_preferences
1407         return (userpref.active_section == 'ADDONS')
1408
1409     def _addon_list(self):
1410         import sys
1411         modules = []
1412         loaded_modules = set()
1413         paths = bpy.utils.script_paths("addons")
1414         # sys.path.insert(0, None)
1415         for path in paths:
1416             # sys.path[0] = path
1417             modules.extend(bpy.utils.modules_from_path(path, loaded_modules))
1418
1419         # del sys.path[0]
1420         return modules
1421
1422     def draw(self, context):
1423         layout = self.layout
1424
1425         userpref = context.user_preferences
1426         used_ext = {ext.module for ext in userpref.addons}
1427
1428         # collect the categories that can be filtered on
1429         addons = [(mod, addon_info_get(mod)) for mod in self._addon_list()]
1430
1431         cats = {info["category"] for mod, info in addons}
1432         cats.discard("")
1433
1434         cats = ['All', 'Disabled', 'Enabled'] + sorted(cats)
1435
1436         bpy.types.Scene.EnumProperty(items=[(cats[i], cats[i], str(i)) for i in range(len(cats))],
1437             name="Category", attr="addon_filter", description="Filter add-ons by category")
1438         bpy.types.Scene.StringProperty(name="Search", attr="addon_search",
1439             description="Search within the selected filter")
1440
1441         row = layout.row()
1442         row.prop(context.scene, "addon_filter", text="Filter")
1443         row.prop(context.scene, "addon_search", text="Search", icon='VIEWZOOM')
1444         layout.separator()
1445
1446         filter = context.scene.addon_filter
1447         search = context.scene.addon_search.lower()
1448
1449         for mod, info in addons:
1450             module_name = mod.__name__
1451
1452             # check if add-on should be visible with current filters
1453             if filter != "All" and \
1454                     filter != info["category"] and \
1455                     not (module_name not in used_ext and filter == "Disabled"):
1456
1457                 continue
1458
1459             if search and search not in info["name"].lower():
1460                 if info["author"]:
1461                     if search not in info["author"].lower():
1462                         continue
1463                 else:
1464                     continue
1465
1466             # Addon UI Code
1467             box = layout.column().box()
1468             column = box.column()
1469             row = column.row()
1470
1471             # Arrow #
1472             # If there are Infos or UI is expanded
1473             if info["expanded"]:
1474                 row.operator("wm.addon_expand", icon="TRIA_DOWN").module = module_name
1475             elif info["author"] or info["version"] or info["url"] or info["location"]:
1476                 row.operator("wm.addon_expand", icon="TRIA_RIGHT").module = module_name
1477             else:
1478                 # Else, block UI
1479                 arrow = row.column()
1480                 arrow.enabled = False
1481                 arrow.operator("wm.addon_expand", icon="TRIA_RIGHT").module = module_name
1482
1483             row.label(text=info["name"])
1484             row.operator("wm.addon_disable" if module_name in used_ext else "wm.addon_enable").module = module_name
1485
1486             # Expanded UI (only if additional infos are available)
1487             if info["expanded"]:
1488                 if info["author"]:
1489                     split = column.row().split(percentage=0.15)
1490                     split.label(text='Author:')
1491                     split.label(text=info["author"])
1492                 if info["version"]:
1493                     split = column.row().split(percentage=0.15)
1494                     split.label(text='Version:')
1495                     split.label(text=info["version"])
1496                 if info["location"]:
1497                     split = column.row().split(percentage=0.15)
1498                     split.label(text='Location:')
1499                     split.label(text=info["location"])
1500                 if info["description"]:
1501                     split = column.row().split(percentage=0.15)
1502                     split.label(text='Description:')
1503                     split.label(text=info["description"])
1504                 if info["url"]:
1505                     split = column.row().split(percentage=0.15)
1506                     split.label(text="Internet:")
1507                     split.operator("wm.addon_links", text="Link to the Wiki").link = info["url"]
1508                     split.separator()
1509                     split.separator()
1510
1511         # Append missing scripts
1512         # First collect scripts that are used but have no script file.
1513         module_names = {mod.__name__ for mod, info in addons}
1514         missing_modules = {ext for ext in used_ext if ext not in module_names}
1515
1516         if missing_modules and filter in ("All", "Enabled"):
1517             layout.column().separator()
1518             layout.column().label(text="Missing script files")
1519
1520             module_names = {mod.__name__ for mod, info in addons}
1521             for ext in sorted(missing_modules):
1522                 # Addon UI Code
1523                 box = layout.column().box()
1524                 column = box.column()
1525                 row = column.row()
1526
1527                 row.label(text=ext, icon="ERROR")
1528                 row.operator("wm.addon_disable").module = ext
1529
1530 from bpy.props import *
1531
1532
1533 def addon_info_get(mod, info_basis={"name": "", "author": "", "version": "", "blender": "", "location": "", "description": "", "url": "", "category": "", "expanded": False}):
1534     addon_info = getattr(mod, "bl_addon_info", {})
1535
1536     # avoid re-initializing
1537     if "_init" in addon_info:
1538         return addon_info
1539
1540     if not addon_info:
1541         mod.bl_addon_info = addon_info
1542
1543     for key, value in info_basis.items():
1544         addon_info.setdefault(key, value)
1545
1546     if not addon_info["name"]:
1547         addon_info["name"] = mod.__name__
1548
1549     addon_info["_init"] = None
1550     return addon_info
1551
1552
1553 class WM_OT_addon_enable(bpy.types.Operator):
1554     "Enable an addon"
1555     bl_idname = "wm.addon_enable"
1556     bl_label = "Enable Add-On"
1557
1558     module = StringProperty(name="Module", description="Module name of the addon to enable")
1559
1560     def execute(self, context):
1561         module_name = self.properties.module
1562
1563         try:
1564             mod = __import__(module_name)
1565             mod.register()
1566         except:
1567             import traceback
1568             traceback.print_exc()
1569             return {'CANCELLED'}
1570
1571         ext = context.user_preferences.addons.new()
1572         ext.module = module_name
1573
1574         # check if add-on is written for current blender version, or raise a warning
1575         info = addon_info_get(mod)
1576
1577         if info.get("blender", (0, 0, 0)) > bpy.app.version:
1578             self.report("WARNING','This script was written for a newer version of Blender and might not function (correctly).\nThe script is enabled though.")
1579
1580         return {'FINISHED'}
1581
1582
1583 class WM_OT_addon_disable(bpy.types.Operator):
1584     "Disable an addon"
1585     bl_idname = "wm.addon_disable"
1586     bl_label = "Disable Add-On"
1587
1588     module = StringProperty(name="Module", description="Module name of the addon to disable")
1589
1590     def execute(self, context):
1591         import traceback
1592         module_name = self.properties.module
1593
1594         try:
1595             mod = __import__(module_name)
1596             mod.unregister()
1597         except:
1598             traceback.print_exc()
1599
1600         addons = context.user_preferences.addons
1601         ok = True
1602         while ok: # incase its in more then once.
1603             ok = False
1604             for ext in addons:
1605                 if ext.module == module_name:
1606                     addons.remove(ext)
1607                     ok = True
1608                     break
1609
1610         return {'FINISHED'}
1611
1612
1613 class WM_OT_addon_install(bpy.types.Operator):
1614     "Install an addon"
1615     bl_idname = "wm.addon_install"
1616     bl_label = "Install Add-On..."
1617
1618     module = StringProperty(name="Module", description="Module name of the addon to disable")
1619
1620     path = StringProperty(name="File Path", description="File path to write file to")
1621     filename = StringProperty(name="File Name", description="Name of the file")
1622     directory = StringProperty(name="Directory", description="Directory of the file")
1623     filter_folder = BoolProperty(name="Filter folders", description="", default=True, options={'HIDDEN'})
1624     filter_python = BoolProperty(name="Filter python", description="", default=True, options={'HIDDEN'})
1625
1626     def execute(self, context):
1627         import traceback
1628         import zipfile
1629         pyfile = self.properties.path
1630
1631         path_addons = bpy.utils.script_paths("addons")[-1]
1632
1633         #check to see if the file is in compressed format (.zip)
1634         if zipfile.is_zipfile(pyfile):
1635             try:
1636                 file_to_extract = zipfile.ZipFile(pyfile, 'r')
1637
1638                 #extract the file to "addons"
1639                 file_to_extract.extractall(path_addons)
1640
1641             except:
1642                 traceback.print_exc()
1643                 return {'CANCELLED'}
1644
1645         else:
1646             path_dest = os.path.join(path_addons, os.path.basename(pyfile))
1647
1648             if os.path.exists(path_dest):
1649                 self.report({'WARNING'}, "File already installed to '%s'\n" % path_dest)
1650                 return {'CANCELLED'}
1651
1652             #if not compressed file just copy into the addon path
1653             try:
1654                 shutil.copyfile(pyfile, path_dest)
1655
1656             except:
1657                 traceback.print_exc()
1658                 return {'CANCELLED'}
1659
1660         # TODO, should not be a warning.
1661         # self.report({'WARNING'}, "File installed to '%s'\n" % path_dest)
1662         return {'FINISHED'}
1663
1664     def invoke(self, context, event):
1665         paths = bpy.utils.script_paths("addons")
1666         if not paths:
1667             self.report({'ERROR'}, "No 'addons' path could be found in " + str(bpy.utils.script_paths()))
1668             return {'CANCELLED'}
1669
1670         wm = context.manager
1671         wm.add_fileselect(self)
1672         return {'RUNNING_MODAL'}
1673
1674
1675 class WM_OT_addon_expand(bpy.types.Operator):
1676     "Display more information on this add-on"
1677     bl_idname = "wm.addon_expand"
1678     bl_label = ""
1679
1680     module = StringProperty(name="Module", description="Module name of the addon to expand")
1681
1682     def execute(self, context):
1683         module_name = self.properties.module
1684
1685         # unlikely to fail, module should have alredy been imported
1686         try:
1687             mod = __import__(module_name)
1688         except:
1689             import traceback
1690             traceback.print_exc()
1691             return {'CANCELLED'}
1692
1693         info = addon_info_get(mod)
1694         info["expanded"] = not info["expanded"]
1695         return {'FINISHED'}
1696
1697
1698 class WM_OT_addon_links(bpy.types.Operator):
1699     "Open the Blender Wiki in the Webbrowser"
1700     bl_idname = "wm.addon_links"
1701     bl_label = ""
1702
1703     link = StringProperty(name="Link", description="Link to open")
1704
1705     def execute(self, context):
1706         import webbrowser
1707         webbrowser.open(self.properties.link)
1708         return {'FINISHED'}
1709
1710
1711 class WM_OT_keyconfig_test(bpy.types.Operator):
1712     "Test keyconfig for conflicts"
1713     bl_idname = "wm.keyconfig_test"
1714     bl_label = "Test Key Configuration for Conflicts"
1715
1716     def testEntry(self, kc, entry, src=None, parent=None):
1717         result = False
1718
1719         def kmistr(kmi):
1720             if km.modal:
1721                 s = ["kmi = km.items.add_modal(\'%s\', \'%s\', \'%s\'" % (kmi.propvalue, kmi.type, kmi.value)]
1722             else:
1723                 s = ["kmi = km.items.add(\'%s\', \'%s\', \'%s\'" % (kmi.idname, kmi.type, kmi.value)]
1724
1725             if kmi.any:
1726                 s.append(", any=True")
1727             else:
1728                 if kmi.shift:
1729                     s.append(", shift=True")
1730                 if kmi.ctrl:
1731                     s.append(", ctrl=True")
1732                 if kmi.alt:
1733                     s.append(", alt=True")
1734                 if kmi.oskey:
1735                     s.append(", oskey=True")
1736             if kmi.key_modifier and kmi.key_modifier != 'NONE':
1737                 s.append(", key_modifier=\'%s\'" % kmi.key_modifier)
1738
1739             s.append(")\n")
1740
1741             def export_properties(prefix, properties):
1742                 for pname in dir(properties):
1743                     if not properties.is_property_hidden(pname):
1744                         value = eval("properties.%s" % pname)
1745                         if isinstance(value, bpy.types.OperatorProperties):
1746                             export_properties(prefix + "." + pname, value)
1747                         elif properties.is_property_set(pname):
1748                             value = _string_value(value)
1749                             if value != "":
1750                                 s.append(prefix + ".%s = %s\n" % (pname, value))
1751
1752             props = kmi.properties
1753
1754             if props is not None:
1755                 export_properties("kmi.properties", props)
1756
1757             return "".join(s).strip()
1758
1759         idname, spaceid, regionid, children = entry
1760
1761         km = kc.find_keymap(idname, space_type=spaceid, region_type=regionid)
1762
1763         if km:
1764             km = km.active()
1765
1766             if src:
1767                 for item in km.items:
1768                     if src.compare(item):
1769                         print("===========")
1770                         print(parent.name)
1771                         print(kmistr(src))
1772                         print(km.name)
1773                         print(kmistr(item))
1774                         result = True
1775
1776                 for child in children:
1777                     if self.testEntry(kc, child, src, parent):
1778                         result = True
1779             else:
1780                 for i in range(len(km.items)):
1781                     src = km.items[i]
1782
1783                     for child in children:
1784                         if self.testEntry(kc, child, src, km):
1785                             result = True
1786
1787                     for j in range(len(km.items) - i - 1):
1788                         item = km.items[j + i + 1]
1789                         if src.compare(item):
1790                             print("===========")
1791                             print(km.name)
1792                             print(kmistr(src))
1793                             print(kmistr(item))
1794                             result = True
1795
1796                 for child in children:
1797                     if self.testEntry(kc, child):
1798                         result = True
1799
1800         return result
1801
1802     def testConfig(self, kc):
1803         result = False
1804         for entry in KM_HIERARCHY:
1805             if self.testEntry(kc, entry):
1806                 result = True
1807         return result
1808
1809     def execute(self, context):
1810         wm = context.manager
1811         kc = wm.default_keyconfig
1812
1813         if self.testConfig(kc):
1814             print("CONFLICT")
1815
1816         return {'FINISHED'}
1817
1818
1819 def _string_value(value):
1820     if isinstance(value, str) or isinstance(value, bool) or isinstance(value, float) or isinstance(value, int):
1821         result = repr(value)
1822     elif getattr(value, '__len__', False):
1823         repr(list(value))
1824     else:
1825         print("Export key configuration: can't write ", value)
1826
1827     return result
1828
1829
1830 class WM_OT_keyconfig_import(bpy.types.Operator):
1831     "Import key configuration from a python script"
1832     bl_idname = "wm.keyconfig_import"
1833     bl_label = "Import Key Configuration..."
1834
1835     path = StringProperty(name="File Path", description="File path to write file to")
1836     filename = StringProperty(name="File Name", description="Name of the file")
1837     directory = StringProperty(name="Directory", description="Directory of the file")
1838     filter_folder = BoolProperty(name="Filter folders", description="", default=True, options={'HIDDEN'})
1839     filter_text = BoolProperty(name="Filter text", description="", default=True, options={'HIDDEN'})
1840     filter_python = BoolProperty(name="Filter python", description="", default=True, options={'HIDDEN'})
1841
1842     keep_original = BoolProperty(name="Keep original", description="Keep original file after copying to configuration folder", default=True)
1843
1844     def execute(self, context):
1845         if not self.properties.path:
1846             raise Exception("File path not set")
1847
1848         f = open(self.properties.path, "r")
1849         if not f:
1850             raise Exception("Could not open file")
1851
1852         name_pattern = re.compile("^kc = wm.add_keyconfig\('(.*)'\)$")
1853
1854         for line in f.readlines():
1855             match = name_pattern.match(line)
1856
1857             if match:
1858                 config_name = match.groups()[0]
1859
1860         f.close()
1861
1862         path = os.path.split(os.path.split(__file__)[0])[0] # remove ui/space_userpref.py
1863         path = os.path.join(path, "cfg")
1864
1865         # create config folder if needed
1866         if not os.path.exists(path):
1867             os.mkdir(path)
1868
1869         path = os.path.join(path, config_name + ".py")
1870
1871         if self.properties.keep_original:
1872             shutil.copy(self.properties.path, path)
1873         else:
1874             shutil.move(self.properties.path, path)
1875
1876         exec("import " + config_name)
1877
1878         wm = bpy.context.manager
1879         wm.active_keyconfig = wm.keyconfigs[config_name]
1880
1881         return {'FINISHED'}
1882
1883     def invoke(self, context, event):
1884         wm = context.manager
1885         wm.add_fileselect(self)
1886         return {'RUNNING_MODAL'}
1887
1888
1889 class WM_OT_keyconfig_export(bpy.types.Operator):
1890     "Export key configuration to a python script"
1891     bl_idname = "wm.keyconfig_export"
1892     bl_label = "Export Key Configuration..."
1893
1894     path = StringProperty(name="File Path", description="File path to write file to")
1895     filename = StringProperty(name="File Name", description="Name of the file")
1896     directory = StringProperty(name="Directory", description="Directory of the file")
1897     filter_folder = BoolProperty(name="Filter folders", description="", default=True, options={'HIDDEN'})
1898     filter_text = BoolProperty(name="Filter text", description="", default=True, options={'HIDDEN'})
1899     filter_python = BoolProperty(name="Filter python", description="", default=True, options={'HIDDEN'})
1900
1901     def execute(self, context):
1902         if not self.properties.path:
1903             raise Exception("File path not set")
1904
1905         f = open(self.properties.path, "w")
1906         if not f:
1907             raise Exception("Could not open file")
1908
1909         wm = context.manager
1910         kc = wm.active_keyconfig
1911
1912         if kc.name == 'Blender':
1913             name = os.path.splitext(os.path.basename(self.properties.path))[0]
1914         else:
1915             name = kc.name
1916
1917         f.write("# Configuration %s\n" % name)
1918
1919         f.write("import bpy\n\n")
1920         f.write("wm = bpy.context.manager\n")
1921         f.write("kc = wm.add_keyconfig('%s')\n\n" % name)
1922
1923         for km in kc.keymaps:
1924             km = km.active()
1925             f.write("# Map %s\n" % km.name)
1926             f.write("km = kc.add_keymap('%s', space_type='%s', region_type='%s', modal=%s)\n\n" % (km.name, km.space_type, km.region_type, km.modal))
1927             for kmi in km.items:
1928                 if km.modal:
1929                     f.write("kmi = km.items.add_modal('%s', '%s', '%s'" % (kmi.propvalue, kmi.type, kmi.value))
1930                 else:
1931                     f.write("kmi = km.items.add('%s', '%s', '%s'" % (kmi.idname, kmi.type, kmi.value))
1932                 if kmi.any:
1933                     f.write(", any=True")
1934                 else:
1935                     if kmi.shift:
1936                         f.write(", shift=True")
1937                     if kmi.ctrl:
1938                         f.write(", ctrl=True")
1939                     if kmi.alt:
1940                         f.write(", alt=True")
1941                     if kmi.oskey:
1942                         f.write(", oskey=True")
1943                 if kmi.key_modifier and kmi.key_modifier != 'NONE':
1944                     f.write(", key_modifier='%s'" % kmi.key_modifier)
1945                 f.write(")\n")
1946
1947                 def export_properties(prefix, properties):
1948                     for pname in dir(properties):
1949                         if not properties.is_property_hidden(pname):
1950                             value = eval("properties.%s" % pname)
1951                             if isinstance(value, bpy.types.OperatorProperties):
1952                                 export_properties(prefix + "." + pname, value)
1953                             elif properties.is_property_set(pname):
1954                                 value = _string_value(value)
1955                                 if value != "":
1956                                     f.write(prefix + ".%s = %s\n" % (pname, value))
1957
1958                 props = kmi.properties
1959
1960                 if props is not None:
1961                     export_properties("kmi.properties", props)
1962
1963             f.write("\n")
1964
1965         f.close()
1966
1967         return {'FINISHED'}
1968
1969     def invoke(self, context, event):
1970         wm = context.manager
1971         wm.add_fileselect(self)
1972         return {'RUNNING_MODAL'}
1973
1974
1975 class WM_OT_keymap_edit(bpy.types.Operator):
1976     "Edit key map"
1977     bl_idname = "wm.keymap_edit"
1978     bl_label = "Edit Key Map"
1979
1980     def execute(self, context):
1981         wm = context.manager
1982         km = context.keymap
1983         km.copy_to_user()
1984         return {'FINISHED'}
1985
1986
1987 class WM_OT_keymap_restore(bpy.types.Operator):
1988     "Restore key map(s)"
1989     bl_idname = "wm.keymap_restore"
1990     bl_label = "Restore Key Map(s)"
1991
1992     all = BoolProperty(attr="all", name="All Keymaps", description="Restore all keymaps to default")
1993
1994     def execute(self, context):
1995         wm = context.manager
1996
1997         if self.properties.all:
1998             for km in wm.default_keyconfig.keymaps:
1999                 km.restore_to_default()
2000         else:
2001             km = context.keymap
2002             km.restore_to_default()
2003
2004         return {'FINISHED'}
2005
2006
2007 class WM_OT_keyitem_restore(bpy.types.Operator):
2008     "Restore key map item"
2009     bl_idname = "wm.keyitem_restore"
2010     bl_label = "Restore Key Map Item"
2011
2012     item_id = IntProperty(attr="item_id", name="Item Identifier", description="Identifier of the item to remove")
2013
2014     def execute(self, context):
2015         wm = context.manager
2016         km = context.keymap
2017         kmi = km.item_from_id(self.properties.item_id)
2018
2019         km.restore_item_to_default(kmi)
2020
2021         return {'FINISHED'}
2022
2023
2024 class WM_OT_keyitem_add(bpy.types.Operator):
2025     "Add key map item"
2026     bl_idname = "wm.keyitem_add"
2027     bl_label = "Add Key Map Item"
2028
2029     def execute(self, context):
2030         wm = context.manager
2031         km = context.keymap
2032         kc = wm.default_keyconfig
2033
2034         if km.modal:
2035             km.items.add_modal("", 'A', 'PRESS') # kmi
2036         else:
2037             km.items.add("none", 'A', 'PRESS') # kmi
2038
2039         # clear filter and expand keymap so we can see the newly added item
2040         if kc.filter != '':
2041             kc.filter = ''
2042             km.items_expanded = True
2043             km.children_expanded = True
2044
2045         return {'FINISHED'}
2046
2047
2048 class WM_OT_keyitem_remove(bpy.types.Operator):
2049     "Remove key map item"
2050     bl_idname = "wm.keyitem_remove"
2051     bl_label = "Remove Key Map Item"
2052
2053     item_id = IntProperty(attr="item_id", name="Item Identifier", description="Identifier of the item to remove")
2054
2055     def execute(self, context):
2056         wm = context.manager
2057         km = context.keymap
2058         kmi = km.item_from_id(self.properties.item_id)
2059         km.remove_item(kmi)
2060         return {'FINISHED'}
2061
2062
2063 class WM_OT_keyconfig_remove(bpy.types.Operator):
2064     "Remove key config"
2065     bl_idname = "wm.keyconfig_remove"
2066     bl_label = "Remove Key Config"
2067
2068     def poll(self, context):
2069         wm = context.manager
2070         return wm.active_keyconfig.user_defined
2071
2072     def execute(self, context):
2073         wm = context.manager
2074
2075         keyconfig = wm.active_keyconfig
2076
2077         module = __import__(keyconfig.name)
2078
2079         os.remove(module.__file__)
2080
2081         compiled_path = module.__file__ + "c" # for .pyc
2082
2083         if os.path.exists(compiled_path):
2084             os.remove(compiled_path)
2085
2086         wm.remove_keyconfig(keyconfig)
2087         return {'FINISHED'}
2088
2089
2090 classes = [
2091     USERPREF_HT_header,
2092     USERPREF_PT_tabs,
2093     USERPREF_PT_interface,
2094     USERPREF_PT_theme,
2095     USERPREF_PT_edit,
2096     USERPREF_PT_system,
2097     USERPREF_PT_file,
2098     USERPREF_PT_input,
2099     USERPREF_PT_addons,
2100
2101     WM_OT_addon_enable,
2102     WM_OT_addon_disable,
2103     WM_OT_addon_install,
2104     WM_OT_addon_expand,
2105     WM_OT_addon_links,
2106
2107     WM_OT_keyconfig_export,
2108     WM_OT_keyconfig_import,
2109     WM_OT_keyconfig_test,
2110     WM_OT_keyconfig_remove,
2111     WM_OT_keymap_edit,
2112     WM_OT_keymap_restore,
2113     WM_OT_keyitem_add,
2114     WM_OT_keyitem_remove,
2115     WM_OT_keyitem_restore]
2116
2117
2118 def register():
2119     register = bpy.types.register
2120     for cls in classes:
2121         register(cls)
2122
2123
2124 def unregister():
2125     unregister = bpy.types.unregister
2126     for cls in classes:
2127         unregister(cls)
2128
2129 if __name__ == "__main__":
2130     register()