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