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