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