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