diff --git a/release/scripts/modules/bpy/utils.py b/release/scripts/modules/bpy/utils.py index 32ec5125825..b6fc221217b 100644 --- a/release/scripts/modules/bpy/utils.py +++ b/release/scripts/modules/bpy/utils.py @@ -182,7 +182,7 @@ def load_scripts(reload_scripts=False, refresh_scripts=False): user_path = user_script_path() for base_path in script_paths(): - for path_subdir in ("", "ui", "op", "io", "cfg", "keyingsets", "modules"): + for path_subdir in ("", "ui", "op", "io", "keyingsets", "modules"): path = _os.path.join(base_path, path_subdir) if _os.path.isdir(path): _sys_path_ensure(path) @@ -202,6 +202,13 @@ def load_scripts(reload_scripts=False, refresh_scripts=False): # deal with addons seperately addon_reset_all() + + # run the active integration preset + filepath = preset_find(_bpy.context.user_preferences.inputs.active_keyconfig, "keyconfig") + if filepath: + keyconfig_set(filepath) + + if reload_scripts: import gc print("gc.collect() -> %d" % gc.collect()) @@ -474,6 +481,9 @@ def addon_reset_all(): addon_disable(mod_name) def preset_find(name, preset_path, display_name=False): + if not name: + return None + for directory in preset_paths(preset_path): if display_name: @@ -488,4 +498,38 @@ def preset_find(name, preset_path, display_name=False): if filename: filepath = _os.path.join(directory, filename) if _os.path.exists(filepath): - return filepath\ + return filepath + + +def keyconfig_set(filepath): + from os.path import basename, splitext + + print("loading preset:", filepath) + keyconfigs = _bpy.context.window_manager.keyconfigs + kc_orig = keyconfigs.active + + keyconfigs_old = keyconfigs[:] + + try: + exec(compile(open(filepath).read(), filepath, 'exec'), {"__file__": filepath}) + except: + import traceback + traceback.print_exc() + + kc_new = [kc for kc in keyconfigs if kc not in keyconfigs_old][0] + + kc_new.name = "" + + # remove duplicates + name = splitext(basename(filepath))[0] + while True: + kc_dupe = keyconfigs.get(name) + if kc_dupe: + keyconfigs.remove(kc_dupe) + else: + break + + kc_new.name = name + keyconfigs.active = kc_new + + diff --git a/release/scripts/op/presets.py b/release/scripts/op/presets.py index f28db3b1522..07852550ab7 100644 --- a/release/scripts/op/presets.py +++ b/release/scripts/op/presets.py @@ -33,13 +33,18 @@ class AddPresetBase(): name = bpy.props.StringProperty(name="Name", description="Name of the preset, used to make the path name", maxlen=64, default="") remove_active = bpy.props.BoolProperty(default=False, options={'HIDDEN'}) - def _as_filename(self, name): # could reuse for other presets - for char in " !@#$%^&*(){}:\";'[]<>,./?": + @staticmethod + def as_filename(name): # could reuse for other presets + for char in " !@#$%^&*(){}:\";'[]<>,.\\/?": name = name.replace(char, '_') return name.lower().strip() def execute(self, context): import os + + if hasattr(self, "pre_cb"): + self.pre_cb(context) + preset_menu_class = getattr(bpy.types, self.preset_menu) if not self.remove_active: @@ -47,24 +52,23 @@ class AddPresetBase(): if not self.name: return {'FINISHED'} - filename = self._as_filename(self.name) + ".py" + filename = self.as_filename(self.name) target_path = bpy.utils.preset_paths(self.preset_subdir)[0] # we need some way to tell the user and system preset path - filepath = os.path.join(target_path, filename) - if getattr(self, "save_keyconfig", False): - bpy.ops.wm.keyconfig_export(filepath=filepath, kc_name=self.name) - file_preset = open(filepath, 'a') - file_preset.write("wm.keyconfigs.active = kc\n\n") + filepath = os.path.join(target_path, filename) + ".py" + + if hasattr(self, "add"): + self.add(context, filepath) else: file_preset = open(filepath, 'w') file_preset.write("import bpy\n") - for rna_path in self.preset_values: - value = eval(rna_path) - file_preset.write("%s = %s\n" % (rna_path, repr(value))) + for rna_path in self.preset_values: + value = eval(rna_path) + file_preset.write("%s = %s\n" % (rna_path, repr(value))) - file_preset.close() + file_preset.close() preset_menu_class.bl_label = bpy.path.display_name(self.name) @@ -73,20 +77,27 @@ class AddPresetBase(): # fairly sloppy but convenient. filepath = bpy.utils.preset_find(preset_active, self.preset_subdir) + if not filepath: filepath = bpy.utils.preset_find(preset_active, self.preset_subdir, display_name=True) if not filepath: return {'CANCELLED'} - try: - os.remove(filepath) - except: - import traceback - traceback.print_exc() + if hasattr(self, "remove"): + self.remove(context, filepath) + else: + try: + os.remove(filepath) + except: + import traceback + traceback.print_exc() # XXX, stupid! - preset_menu_class.bl_label = bpy.path.display_name(self.preset_menu.replace("_MT_", " ").lower()) + preset_menu_class.bl_label = "Presets" + + if hasattr(self, "post_cb"): + self.post_cb(context) return {'FINISHED'} @@ -218,7 +229,6 @@ class AddPresetInteraction(AddPresetBase, bpy.types.Operator): bl_idname = "wm.interaction_preset_add" bl_label = "Add Interaction Preset" preset_menu = "USERPREF_MT_interaction_presets" - save_keyconfig = True preset_values = [ "bpy.context.user_preferences.edit.use_drag_immediately", @@ -236,6 +246,29 @@ class AddPresetInteraction(AddPresetBase, bpy.types.Operator): preset_subdir = "interaction" +class AddPresetKeyconfig(AddPresetBase, bpy.types.Operator): + '''Add a Keyconfig Preset''' + bl_idname = "wm.keyconfig_preset_add" + bl_label = "Add Keyconfig Preset" + preset_menu = "PREFS_MT_keyconfigs" + preset_subdir = "keyconfig" + + def add(self, context, filepath): + bpy.ops.wm.keyconfig_export(filepath=filepath) + bpy.utils.keyconfig_set(filepath) + + def pre_cb(self, context): + keyconfigs = bpy.context.window_manager.keyconfigs + if self.remove_active: + preset_menu_class = getattr(bpy.types, self.preset_menu) + preset_menu_class.bl_label = keyconfigs.active.name + + def post_cb(self, context): + keyconfigs = bpy.context.window_manager.keyconfigs + if self.remove_active: + keyconfigs.remove(keyconfigs.active) + + def register(): pass diff --git a/release/scripts/op/wm.py b/release/scripts/op/wm.py index 51304bdc3eb..7b500d028fd 100644 --- a/release/scripts/op/wm.py +++ b/release/scripts/op/wm.py @@ -768,6 +768,17 @@ class WM_OT_properties_remove(bpy.types.Operator): return {'FINISHED'} +class WM_OT_keyconfig_activate(bpy.types.Operator): + bl_idname = "wm.keyconfig_activate" + bl_label = "Activate Keyconfig" + + filepath = StringProperty(name="File Path", maxlen=1024) + + def execute(self, context): + bpy.utils.keyconfig_set(self.filepath) + return {'FINISHED'} + + def register(): pass diff --git a/release/scripts/ui/space_userpref.py b/release/scripts/ui/space_userpref.py index 5cb760790df..03e1b41ad32 100644 --- a/release/scripts/ui/space_userpref.py +++ b/release/scripts/ui/space_userpref.py @@ -725,6 +725,7 @@ class USERPREF_PT_input(InputKeyMapPanel): sub = col.column() sub.label(text="Presets:") subrow = sub.row(align=True) + subrow.menu("USERPREF_MT_interaction_presets", text=bpy.types.USERPREF_MT_interaction_presets.bl_label) subrow.operator("wm.interaction_preset_add", text="", icon='ZOOMIN') subrow.operator("wm.interaction_preset_add", text="", icon='ZOOMOUT').remove_active = True diff --git a/release/scripts/ui/space_userpref_keymap.py b/release/scripts/ui/space_userpref_keymap.py index 8064e74fa01..3c104b67ad3 100644 --- a/release/scripts/ui/space_userpref_keymap.py +++ b/release/scripts/ui/space_userpref_keymap.py @@ -124,6 +124,19 @@ def _merge_keymaps(kc1, kc2): return merged_keymaps +class PREFS_MT_keyconfigs(bpy.types.Menu): + bl_label = "KeyPresets" + preset_subdir = "keyconfig" + preset_operator = "wm.keyconfig_activate" + def draw(self, context): + props = self.layout.operator("wm.context_set_value", text="Blender (default)") + props.data_path = "window_manager.keyconfigs.active" + props.value = "context.window_manager.keyconfigs.default" + + # now draw the presets + bpy.types.Menu.draw_preset(self, context) + + class InputKeyMapPanel(bpy.types.Panel): bl_space_type = 'USER_PREFERENCES' bl_label = "Input" @@ -365,10 +378,18 @@ class InputKeyMapPanel(bpy.types.Panel): subsplit = sub.split() subcol = subsplit.column() - row = subcol.row() - row.prop_search(wm.keyconfigs, "active", wm, "keyconfigs", text="Key Config:") - layout.context_pointer_set("keyconfig", wm.keyconfigs.active) - row.operator("wm.keyconfig_remove", text="", icon='X') + row = subcol.row(align=True) + + #row.prop_search(wm.keyconfigs, "active", wm, "keyconfigs", text="Key Config:") + text = bpy.path.display_name(context.window_manager.keyconfigs.active.name) + if not text: + text = "Blender (default)" + row.menu("PREFS_MT_keyconfigs", text=text) + row.operator("wm.keyconfig_preset_add", text="", icon="ZOOMIN") + row.operator("wm.keyconfig_preset_add", text="", icon="ZOOMOUT").remove_active = True + +# layout.context_pointer_set("keyconfig", wm.keyconfigs.active) +# row.operator("wm.keyconfig_remove", text="", icon='X') row.prop(context.space_data, "filter_text", icon="VIEWZOOM") @@ -389,8 +410,9 @@ def export_properties(prefix, properties, lines=None): if lines is None: lines = [] - for pname, value in properties.items(): + for pname in properties.keys(): if not properties.is_property_hidden(pname): + value = getattr(properties, pname) if isinstance(value, bpy.types.OperatorProperties): export_properties(prefix + "." + pname, value, lines) elif properties.is_property_set(pname): @@ -501,7 +523,7 @@ def _string_value(value): if isinstance(value, str) or isinstance(value, bool) or isinstance(value, float) or isinstance(value, int): result = repr(value) elif getattr(value, '__len__', False): - repr(list(value)) + return repr(list(value)) else: print("Export key configuration: can't write ", value) @@ -521,6 +543,7 @@ class WM_OT_keyconfig_import(bpy.types.Operator): keep_original = BoolProperty(name="Keep original", description="Keep original file after copying to configuration folder", default=True) def execute(self, context): + from os.path import basename import shutil if not self.filepath: raise Exception("Filepath not set") @@ -529,24 +552,16 @@ class WM_OT_keyconfig_import(bpy.types.Operator): if not f: raise Exception("Could not open file") - config_name = None - for line in f: - if line.startswith("kc = wm.keyconfigs.new("): - config_name = line[24:-3] - break + config_name = basename(self.filepath) - if config_name is None: - raise Exception("config name not found") - - path = os.path.join(__file__, "..", "..", "cfg") # remove ui/space_userpref.py - path = os.path.normpath(path) + path = bpy.utils.preset_paths("keyconfig")[0] # we need some way to tell the user and system preset path print(path) # create config folder if needed if not os.path.exists(path): os.mkdir(path) - path = os.path.join(path, config_name + ".py") + path = os.path.join(path, config_name) if self.keep_original: shutil.copy(self.filepath, path) @@ -554,18 +569,7 @@ class WM_OT_keyconfig_import(bpy.types.Operator): shutil.move(self.filepath, path) # sneaky way to check we're actually running the code. - wm = context.window_manager - while config_name in wm.keyconfigs: - wm.keyconfigs.remove(wm.keyconfigs[config_name]) - - wm = context.window_manager - totmap = len(wm.keyconfigs) - mod = __import__(config_name) - if totmap == len(wm.keyconfigs): - reload(mod) - - wm = bpy.context.window_manager - wm.keyconfigs.active = wm.keyconfigs[config_name] + bpy.utils.keyconfig_set(path) return {'FINISHED'} @@ -586,7 +590,6 @@ class WM_OT_keyconfig_export(bpy.types.Operator): filter_folder = BoolProperty(name="Filter folders", description="", default=True, options={'HIDDEN'}) filter_text = BoolProperty(name="Filter text", description="", default=True, options={'HIDDEN'}) filter_python = BoolProperty(name="Filter python", description="", default=True, options={'HIDDEN'}) - kc_name = StringProperty(name="KeyConfig Name", description="Name to save the key config as") def execute(self, context): if not self.filepath: @@ -599,18 +602,10 @@ class WM_OT_keyconfig_export(bpy.types.Operator): wm = context.window_manager kc = wm.keyconfigs.active - if self.kc_name != '': - name = self.kc_name - elif kc.name == 'Blender': - name = os.path.splitext(os.path.basename(self.filepath))[0] - else: - name = kc.name - - f.write("# Configuration %s\n" % name) - - f.write("import bpy\n\n") + f.write("import bpy\n") + f.write("import os\n\n") f.write("wm = bpy.context.window_manager\n") - f.write("kc = wm.keyconfigs.new('%s')\n\n" % name) + f.write("kc = wm.keyconfigs.new(os.path.splitext(os.path.basename(__file__))[0])\n\n") # keymap must be created by caller # Generate a list of keymaps to export: # @@ -775,21 +770,7 @@ class WM_OT_keyconfig_remove(bpy.types.Operator): def execute(self, context): import sys wm = context.window_manager - keyconfig = wm.keyconfigs.active - - module = sys.modules.get(keyconfig.name) - - if module: - path = module.__file__ - if os.path.exists(path): - os.remove(path) - - path = module.__file__ + "c" # for .pyc - - if os.path.exists(path): - os.remove(path) - wm.keyconfigs.remove(keyconfig) return {'FINISHED'} diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 632d8c1be29..7459adc121f 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -2655,6 +2655,10 @@ static void rna_def_userdef_input(BlenderRNA *brna) RNA_def_property_collection_sdna(prop, NULL, "keymaps", NULL); RNA_def_property_struct_type(prop, "KeyMap"); RNA_def_property_ui_text(prop, "Edited Keymaps", ""); + + prop= RNA_def_property(srna, "active_keyconfig", PROP_STRING, PROP_DIRPATH); + RNA_def_property_string_sdna(prop, NULL, "keyconfigstr"); + RNA_def_property_ui_text(prop, "Key Config", "The name of the active key configuration"); } static void rna_def_userdef_filepaths(BlenderRNA *brna)