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