Commit 2f896463 authored by Cheryl Sabella's avatar Cheryl Sabella Committed by Terry Jan Reedy

bpo-31002: IDLE: Add tests for configdialog keys tab (#2996)

Patch by Cheryl Sabella.
parent f978405b
...@@ -251,8 +251,7 @@ class ConfigDialog(Toplevel): ...@@ -251,8 +251,7 @@ class ConfigDialog(Toplevel):
delete_custom_theme: Ativate default [button_delete_custom_theme]. delete_custom_theme: Ativate default [button_delete_custom_theme].
save_new_theme: Save to userCfg['theme'] (is function). save_new_theme: Save to userCfg['theme'] (is function).
Widget Structure: (*) widgets bound to self Widgets of highlights page frame: (*) widgets bound to self
frame
frame_custom: LabelFrame frame_custom: LabelFrame
(*)highlight_sample: Text (*)highlight_sample: Text
(*)frame_color_set: Frame (*)frame_color_set: Frame
...@@ -796,52 +795,92 @@ class ConfigDialog(Toplevel): ...@@ -796,52 +795,92 @@ class ConfigDialog(Toplevel):
def create_page_keys(self): def create_page_keys(self):
"""Return frame of widgets for Keys tab. """Return frame of widgets for Keys tab.
Enable users to provisionally change both individual and sets of
keybindings (shortcut keys). Except for features implemented as
extensions, keybindings are stored in complete sets called
keysets. Built-in keysets in idlelib/config-keys.def are fixed
as far as the dialog is concerned. Any keyset can be used as the
base for a new custom keyset, stored in .idlerc/config-keys.cfg.
Function load_key_cfg() initializes tk variables and keyset
lists and calls load_keys_list for the current keyset.
Radiobuttons builtin_keyset_on and custom_keyset_on toggle var
keyset_source, which controls if the current set of keybindings
are from a builtin or custom keyset. DynOptionMenus builtinlist
and customlist contain lists of the builtin and custom keysets,
respectively, and the current item from each list is stored in
vars builtin_name and custom_name.
Button delete_custom_keys invokes delete_custom_keys() to delete
a custom keyset from idleConf.userCfg['keys'] and changes. Button
save_custom_keys invokes save_as_new_key_set() which calls
get_new_keys_name() and create_new_key_set() to save a custom keyset
and its keybindings to idleConf.userCfg['keys'].
Listbox bindingslist contains all of the keybindings for the
selected keyset. The keybindings are loaded in load_keys_list()
and are pairs of (event, [keys]) where keys can be a list
of one or more key combinations to bind to the same event.
Mouse button 1 click invokes on_bindingslist_select(), which
allows button_new_keys to be clicked.
So, an item is selected in listbindings, which activates
button_new_keys, and clicking button_new_keys calls function
get_new_keys(). Function get_new_keys() gets the key mappings from the
current keyset for the binding event item that was selected. The
function then displays another dialog, GetKeysDialog, with the
selected binding event and current keys and always new key sequences
to be entered for that binding event. If the keys aren't
changed, nothing happens. If the keys are changed and the keyset
is a builtin, function get_new_keys_name() will be called
for input of a custom keyset name. If no name is given, then the
change to the keybinding will abort and no updates will be made. If
a custom name is entered in the prompt or if the current keyset was
already custom (and thus didn't require a prompt), then
idleConf.userCfg['keys'] is updated in function create_new_key_set()
with the change to the event binding. The item listing in bindingslist
is updated with the new keys. Var keybinding is also set which invokes
the callback function, var_changed_keybinding, to add the change to
the 'keys' or 'extensions' changes tracker based on the binding type.
Tk Variables: Tk Variables:
builtin_keys: Menu variable for built-in keybindings.
custom_keys: Menu variable for custom keybindings.
are_keys_builtin: Selector for built-in or custom keybindings.
keybinding: Action/key bindings. keybinding: Action/key bindings.
Methods: Methods:
load_key_config: Set table.
load_keys_list: Reload active set. load_keys_list: Reload active set.
keybinding_selected: Bound to list_bindings button release.
get_new_keys: Command for button_new_keys.
get_new_keys_name: Call popup.
create_new_key_set: Combine active keyset and changes. create_new_key_set: Combine active keyset and changes.
set_keys_type: Command for are_keys_builtin. set_keys_type: Command for keyset_source.
delete_custom_keys: Command for button_delete_custom_keys.
save_as_new_key_set: Command for button_save_custom_keys.
save_new_key_set: Save to idleConf.userCfg['keys'] (is function). save_new_key_set: Save to idleConf.userCfg['keys'] (is function).
deactivate_current_config: Remove keys bindings in editors. deactivate_current_config: Remove keys bindings in editors.
Widget Structure: (*) widgets bound to self Widgets for keys page frame: (*) widgets bound to self
frame frame_key_sets: LabelFrame
frames[0]: Frame
(*)builtin_keyset_on: Radiobutton - var keyset_source
(*)custom_keyset_on: Radiobutton - var keyset_source
(*)builtinlist: DynOptionMenu - var builtin_name,
func keybinding_selected
(*)customlist: DynOptionMenu - var custom_name,
func keybinding_selected
(*)keys_message: Label
frames[1]: Frame
(*)button_delete_custom_keys: Button - delete_custom_keys
(*)button_save_custom_keys: Button - save_as_new_key_set
frame_custom: LabelFrame frame_custom: LabelFrame
frame_target: Frame frame_target: Frame
target_title: Label target_title: Label
scroll_target_y: Scrollbar scroll_target_y: Scrollbar
scroll_target_x: Scrollbar scroll_target_x: Scrollbar
(*)list_bindings: ListBox (*)bindingslist: ListBox - on_bindingslist_select
(*)button_new_keys: Button (*)button_new_keys: Button - get_new_keys & ..._name
frame_key_sets: LabelFrame
frames[0]: Frame
(*)radio_keys_builtin: Radiobutton - are_keys_builtin
(*)radio_keys_custom: Radiobutton - are_keys_builtin
(*)opt_menu_keys_builtin: DynOptionMenu - builtin_keys
(*)opt_menu_keys_custom: DynOptionMenu - custom_keys
(*)new_custom_keys: Label
frames[1]: Frame
(*)button_delete_custom_keys: Button
button_save_custom_keys: Button
""" """
parent = self.parent parent = self.parent
self.builtin_keys = tracers.add( self.builtin_name = tracers.add(
StringVar(parent), self.var_changed_builtin_keys) StringVar(parent), self.var_changed_builtin_name)
self.custom_keys = tracers.add( self.custom_name = tracers.add(
StringVar(parent), self.var_changed_custom_keys) StringVar(parent), self.var_changed_custom_name)
self.are_keys_builtin = tracers.add( self.keyset_source = tracers.add(
BooleanVar(parent), self.var_changed_are_keys_builtin) BooleanVar(parent), self.var_changed_keyset_source)
self.keybinding = tracers.add( self.keybinding = tracers.add(
StringVar(parent), self.var_changed_keybinding) StringVar(parent), self.var_changed_keybinding)
...@@ -858,36 +897,37 @@ class ConfigDialog(Toplevel): ...@@ -858,36 +897,37 @@ class ConfigDialog(Toplevel):
target_title = Label(frame_target, text='Action - Key(s)') target_title = Label(frame_target, text='Action - Key(s)')
scroll_target_y = Scrollbar(frame_target) scroll_target_y = Scrollbar(frame_target)
scroll_target_x = Scrollbar(frame_target, orient=HORIZONTAL) scroll_target_x = Scrollbar(frame_target, orient=HORIZONTAL)
self.list_bindings = Listbox( self.bindingslist = Listbox(
frame_target, takefocus=FALSE, exportselection=FALSE) frame_target, takefocus=FALSE, exportselection=FALSE)
self.list_bindings.bind('<ButtonRelease-1>', self.keybinding_selected) self.bindingslist.bind('<ButtonRelease-1>',
scroll_target_y.config(command=self.list_bindings.yview) self.on_bindingslist_select)
scroll_target_x.config(command=self.list_bindings.xview) scroll_target_y['command'] = self.bindingslist.yview
self.list_bindings.config(yscrollcommand=scroll_target_y.set) scroll_target_x['command'] = self.bindingslist.xview
self.list_bindings.config(xscrollcommand=scroll_target_x.set) self.bindingslist['yscrollcommand'] = scroll_target_y.set
self.bindingslist['xscrollcommand'] = scroll_target_x.set
self.button_new_keys = Button( self.button_new_keys = Button(
frame_custom, text='Get New Keys for Selection', frame_custom, text='Get New Keys for Selection',
command=self.get_new_keys, state=DISABLED) command=self.get_new_keys, state=DISABLED)
#frame_key_sets #frame_key_sets
frames = [Frame(frame_key_sets, padx=2, pady=2, borderwidth=0) frames = [Frame(frame_key_sets, padx=2, pady=2, borderwidth=0)
for i in range(2)] for i in range(2)]
self.radio_keys_builtin = Radiobutton( self.builtin_keyset_on = Radiobutton(
frames[0], variable=self.are_keys_builtin, value=1, frames[0], variable=self.keyset_source, value=1,
command=self.set_keys_type, text='Use a Built-in Key Set') command=self.set_keys_type, text='Use a Built-in Key Set')
self.radio_keys_custom = Radiobutton( self.custom_keyset_on = Radiobutton(
frames[0], variable=self.are_keys_builtin, value=0, frames[0], variable=self.keyset_source, value=0,
command=self.set_keys_type, text='Use a Custom Key Set') command=self.set_keys_type, text='Use a Custom Key Set')
self.opt_menu_keys_builtin = DynOptionMenu( self.builtinlist = DynOptionMenu(
frames[0], self.builtin_keys, None, command=None) frames[0], self.builtin_name, None, command=None)
self.opt_menu_keys_custom = DynOptionMenu( self.customlist = DynOptionMenu(
frames[0], self.custom_keys, None, command=None) frames[0], self.custom_name, None, command=None)
self.button_delete_custom_keys = Button( self.button_delete_custom_keys = Button(
frames[1], text='Delete Custom Key Set', frames[1], text='Delete Custom Key Set',
command=self.delete_custom_keys) command=self.delete_custom_keys)
button_save_custom_keys = Button( self.button_save_custom_keys = Button(
frames[1], text='Save as New Custom Key Set', frames[1], text='Save as New Custom Key Set',
command=self.save_as_new_key_set) command=self.save_as_new_key_set)
self.new_custom_keys = Label(frames[0], bd=2) self.keys_message = Label(frames[0], bd=2)
##widget packing ##widget packing
#body #body
...@@ -900,17 +940,17 @@ class ConfigDialog(Toplevel): ...@@ -900,17 +940,17 @@ class ConfigDialog(Toplevel):
frame_target.columnconfigure(0, weight=1) frame_target.columnconfigure(0, weight=1)
frame_target.rowconfigure(1, weight=1) frame_target.rowconfigure(1, weight=1)
target_title.grid(row=0, column=0, columnspan=2, sticky=W) target_title.grid(row=0, column=0, columnspan=2, sticky=W)
self.list_bindings.grid(row=1, column=0, sticky=NSEW) self.bindingslist.grid(row=1, column=0, sticky=NSEW)
scroll_target_y.grid(row=1, column=1, sticky=NS) scroll_target_y.grid(row=1, column=1, sticky=NS)
scroll_target_x.grid(row=2, column=0, sticky=EW) scroll_target_x.grid(row=2, column=0, sticky=EW)
#frame_key_sets #frame_key_sets
self.radio_keys_builtin.grid(row=0, column=0, sticky=W+NS) self.builtin_keyset_on.grid(row=0, column=0, sticky=W+NS)
self.radio_keys_custom.grid(row=1, column=0, sticky=W+NS) self.custom_keyset_on.grid(row=1, column=0, sticky=W+NS)
self.opt_menu_keys_builtin.grid(row=0, column=1, sticky=NSEW) self.builtinlist.grid(row=0, column=1, sticky=NSEW)
self.opt_menu_keys_custom.grid(row=1, column=1, sticky=NSEW) self.customlist.grid(row=1, column=1, sticky=NSEW)
self.new_custom_keys.grid(row=0, column=2, sticky=NSEW, padx=5, pady=5) self.keys_message.grid(row=0, column=2, sticky=NSEW, padx=5, pady=5)
self.button_delete_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2) self.button_delete_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2)
button_save_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2) self.button_save_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2)
frames[0].pack(side=TOP, fill=BOTH, expand=True) frames[0].pack(side=TOP, fill=BOTH, expand=True)
frames[1].pack(side=TOP, fill=X, expand=True, pady=2) frames[1].pack(side=TOP, fill=X, expand=True, pady=2)
return frame return frame
...@@ -918,35 +958,35 @@ class ConfigDialog(Toplevel): ...@@ -918,35 +958,35 @@ class ConfigDialog(Toplevel):
def load_key_cfg(self): def load_key_cfg(self):
"Load current configuration settings for the keybinding options." "Load current configuration settings for the keybinding options."
# Set current keys type radiobutton. # Set current keys type radiobutton.
self.are_keys_builtin.set(idleConf.GetOption( self.keyset_source.set(idleConf.GetOption(
'main', 'Keys', 'default', type='bool', default=1)) 'main', 'Keys', 'default', type='bool', default=1))
# Set current keys. # Set current keys.
current_option = idleConf.CurrentKeys() current_option = idleConf.CurrentKeys()
# Load available keyset option menus. # Load available keyset option menus.
if self.are_keys_builtin.get(): # Default theme selected. if self.keyset_source.get(): # Default theme selected.
item_list = idleConf.GetSectionList('default', 'keys') item_list = idleConf.GetSectionList('default', 'keys')
item_list.sort() item_list.sort()
self.opt_menu_keys_builtin.SetMenu(item_list, current_option) self.builtinlist.SetMenu(item_list, current_option)
item_list = idleConf.GetSectionList('user', 'keys') item_list = idleConf.GetSectionList('user', 'keys')
item_list.sort() item_list.sort()
if not item_list: if not item_list:
self.radio_keys_custom['state'] = DISABLED self.custom_keyset_on['state'] = DISABLED
self.custom_keys.set('- no custom keys -') self.custom_name.set('- no custom keys -')
else: else:
self.opt_menu_keys_custom.SetMenu(item_list, item_list[0]) self.customlist.SetMenu(item_list, item_list[0])
else: # User key set selected. else: # User key set selected.
item_list = idleConf.GetSectionList('user', 'keys') item_list = idleConf.GetSectionList('user', 'keys')
item_list.sort() item_list.sort()
self.opt_menu_keys_custom.SetMenu(item_list, current_option) self.customlist.SetMenu(item_list, current_option)
item_list = idleConf.GetSectionList('default', 'keys') item_list = idleConf.GetSectionList('default', 'keys')
item_list.sort() item_list.sort()
self.opt_menu_keys_builtin.SetMenu(item_list, idleConf.default_keys()) self.builtinlist.SetMenu(item_list, idleConf.default_keys())
self.set_keys_type() self.set_keys_type()
# Load keyset element list. # Load keyset element list.
keyset_name = idleConf.CurrentKeys() keyset_name = idleConf.CurrentKeys()
self.load_keys_list(keyset_name) self.load_keys_list(keyset_name)
def var_changed_builtin_keys(self, *params): def var_changed_builtin_name(self, *params):
"Process selection of builtin key set." "Process selection of builtin key set."
old_keys = ( old_keys = (
'IDLE Classic Windows', 'IDLE Classic Windows',
...@@ -954,40 +994,41 @@ class ConfigDialog(Toplevel): ...@@ -954,40 +994,41 @@ class ConfigDialog(Toplevel):
'IDLE Classic Mac', 'IDLE Classic Mac',
'IDLE Classic OSX', 'IDLE Classic OSX',
) )
value = self.builtin_keys.get() value = self.builtin_name.get()
if value not in old_keys: if value not in old_keys:
if idleConf.GetOption('main', 'Keys', 'name') not in old_keys: if idleConf.GetOption('main', 'Keys', 'name') not in old_keys:
changes.add_option('main', 'Keys', 'name', old_keys[0]) changes.add_option('main', 'Keys', 'name', old_keys[0])
changes.add_option('main', 'Keys', 'name2', value) changes.add_option('main', 'Keys', 'name2', value)
self.new_custom_keys.config(text='New key set, see Help', self.keys_message['text'] = 'New key set, see Help'
fg='#500000') self.keys_message['fg'] = '#500000'
else: else:
changes.add_option('main', 'Keys', 'name', value) changes.add_option('main', 'Keys', 'name', value)
changes.add_option('main', 'Keys', 'name2', '') changes.add_option('main', 'Keys', 'name2', '')
self.new_custom_keys.config(text='', fg='black') self.keys_message['text'] = ''
self.keys_message['fg'] = 'black'
self.load_keys_list(value) self.load_keys_list(value)
def var_changed_custom_keys(self, *params): def var_changed_custom_name(self, *params):
"Process selection of custom key set." "Process selection of custom key set."
value = self.custom_keys.get() value = self.custom_name.get()
if value != '- no custom keys -': if value != '- no custom keys -':
changes.add_option('main', 'Keys', 'name', value) changes.add_option('main', 'Keys', 'name', value)
self.load_keys_list(value) self.load_keys_list(value)
def var_changed_are_keys_builtin(self, *params): def var_changed_keyset_source(self, *params):
"Process toggle between builtin key set and custom key set." "Process toggle between builtin key set and custom key set."
value = self.are_keys_builtin.get() value = self.keyset_source.get()
changes.add_option('main', 'Keys', 'default', value) changes.add_option('main', 'Keys', 'default', value)
if value: if value:
self.var_changed_builtin_keys() self.var_changed_builtin_name()
else: else:
self.var_changed_custom_keys() self.var_changed_custom_name()
def var_changed_keybinding(self, *params): def var_changed_keybinding(self, *params):
"Store change to a keybinding." "Store change to a keybinding."
value = self.keybinding.get() value = self.keybinding.get()
key_set = self.custom_keys.get() key_set = self.custom_name.get()
event = self.list_bindings.get(ANCHOR).split()[0] event = self.bindingslist.get(ANCHOR).split()[0]
if idleConf.IsCoreBinding(event): if idleConf.IsCoreBinding(event):
changes.add_option('keys', key_set, event, value) changes.add_option('keys', key_set, event, value)
else: # Event is an extension binding. else: # Event is an extension binding.
...@@ -997,14 +1038,14 @@ class ConfigDialog(Toplevel): ...@@ -997,14 +1038,14 @@ class ConfigDialog(Toplevel):
def set_keys_type(self): def set_keys_type(self):
"Set available screen options based on builtin or custom key set." "Set available screen options based on builtin or custom key set."
if self.are_keys_builtin.get(): if self.keyset_source.get():
self.opt_menu_keys_builtin['state'] = NORMAL self.builtinlist['state'] = NORMAL
self.opt_menu_keys_custom['state'] = DISABLED self.customlist['state'] = DISABLED
self.button_delete_custom_keys['state'] = DISABLED self.button_delete_custom_keys['state'] = DISABLED
else: else:
self.opt_menu_keys_builtin['state'] = DISABLED self.builtinlist['state'] = DISABLED
self.radio_keys_custom['state'] = NORMAL self.custom_keyset_on['state'] = NORMAL
self.opt_menu_keys_custom['state'] = NORMAL self.customlist['state'] = NORMAL
self.button_delete_custom_keys['state'] = NORMAL self.button_delete_custom_keys['state'] = NORMAL
def get_new_keys(self): def get_new_keys(self):
...@@ -1016,13 +1057,13 @@ class ConfigDialog(Toplevel): ...@@ -1016,13 +1057,13 @@ class ConfigDialog(Toplevel):
changed, then a name for a custom key set needs to be changed, then a name for a custom key set needs to be
entered for the change to be applied. entered for the change to be applied.
""" """
list_index = self.list_bindings.index(ANCHOR) list_index = self.bindingslist.index(ANCHOR)
binding = self.list_bindings.get(list_index) binding = self.bindingslist.get(list_index)
bind_name = binding.split()[0] bind_name = binding.split()[0]
if self.are_keys_builtin.get(): if self.keyset_source.get():
current_key_set_name = self.builtin_keys.get() current_key_set_name = self.builtin_name.get()
else: else:
current_key_set_name = self.custom_keys.get() current_key_set_name = self.custom_name.get()
current_bindings = idleConf.GetCurrentKeySet() current_bindings = idleConf.GetCurrentKeySet()
if current_key_set_name in changes['keys']: # unsaved changes if current_key_set_name in changes['keys']: # unsaved changes
key_set_changes = changes['keys'][current_key_set_name] key_set_changes = changes['keys'][current_key_set_name]
...@@ -1032,24 +1073,24 @@ class ConfigDialog(Toplevel): ...@@ -1032,24 +1073,24 @@ class ConfigDialog(Toplevel):
new_keys = GetKeysDialog(self, 'Get New Keys', bind_name, new_keys = GetKeysDialog(self, 'Get New Keys', bind_name,
current_key_sequences).result current_key_sequences).result
if new_keys: if new_keys:
if self.are_keys_builtin.get(): # Current key set is a built-in. if self.keyset_source.get(): # Current key set is a built-in.
message = ('Your changes will be saved as a new Custom Key Set.' message = ('Your changes will be saved as a new Custom Key Set.'
' Enter a name for your new Custom Key Set below.') ' Enter a name for your new Custom Key Set below.')
new_keyset = self.get_new_keys_name(message) new_keyset = self.get_new_keys_name(message)
if not new_keyset: # User cancelled custom key set creation. if not new_keyset: # User cancelled custom key set creation.
self.list_bindings.select_set(list_index) self.bindingslist.select_set(list_index)
self.list_bindings.select_anchor(list_index) self.bindingslist.select_anchor(list_index)
return return
else: # Create new custom key set based on previously active key set. else: # Create new custom key set based on previously active key set.
self.create_new_key_set(new_keyset) self.create_new_key_set(new_keyset)
self.list_bindings.delete(list_index) self.bindingslist.delete(list_index)
self.list_bindings.insert(list_index, bind_name+' - '+new_keys) self.bindingslist.insert(list_index, bind_name+' - '+new_keys)
self.list_bindings.select_set(list_index) self.bindingslist.select_set(list_index)
self.list_bindings.select_anchor(list_index) self.bindingslist.select_anchor(list_index)
self.keybinding.set(new_keys) self.keybinding.set(new_keys)
else: else:
self.list_bindings.select_set(list_index) self.bindingslist.select_set(list_index)
self.list_bindings.select_anchor(list_index) self.bindingslist.select_anchor(list_index)
def get_new_keys_name(self, message): def get_new_keys_name(self, message):
"Return new key set name from query popup." "Return new key set name from query popup."
...@@ -1065,21 +1106,20 @@ class ConfigDialog(Toplevel): ...@@ -1065,21 +1106,20 @@ class ConfigDialog(Toplevel):
if new_keys_name: if new_keys_name:
self.create_new_key_set(new_keys_name) self.create_new_key_set(new_keys_name)
def keybinding_selected(self, event): def on_bindingslist_select(self, event):
"Activate button to assign new keys to selected action." "Activate button to assign new keys to selected action."
self.button_new_keys['state'] = NORMAL self.button_new_keys['state'] = NORMAL
def create_new_key_set(self, new_key_set_name): def create_new_key_set(self, new_key_set_name):
"""Create a new custom key set with the given name. """Create a new custom key set with the given name.
Create the new key set based on the previously active set Copy the bindings/keys from the previously active keyset
with the current changes applied. Once it is saved, then to the new keyset and activate the new custom keyset.
activate the new key set.
""" """
if self.are_keys_builtin.get(): if self.keyset_source.get():
prev_key_set_name = self.builtin_keys.get() prev_key_set_name = self.builtin_name.get()
else: else:
prev_key_set_name = self.custom_keys.get() prev_key_set_name = self.custom_name.get()
prev_keys = idleConf.GetCoreKeys(prev_key_set_name) prev_keys = idleConf.GetCoreKeys(prev_key_set_name)
new_keys = {} new_keys = {}
for event in prev_keys: # Add key set to changed items. for event in prev_keys: # Add key set to changed items.
...@@ -1096,8 +1136,8 @@ class ConfigDialog(Toplevel): ...@@ -1096,8 +1136,8 @@ class ConfigDialog(Toplevel):
# Change GUI over to the new key set. # Change GUI over to the new key set.
custom_key_list = idleConf.GetSectionList('user', 'keys') custom_key_list = idleConf.GetSectionList('user', 'keys')
custom_key_list.sort() custom_key_list.sort()
self.opt_menu_keys_custom.SetMenu(custom_key_list, new_key_set_name) self.customlist.SetMenu(custom_key_list, new_key_set_name)
self.are_keys_builtin.set(0) self.keyset_source.set(0)
self.set_keys_type() self.set_keys_type()
def load_keys_list(self, keyset_name): def load_keys_list(self, keyset_name):
...@@ -1105,14 +1145,14 @@ class ConfigDialog(Toplevel): ...@@ -1105,14 +1145,14 @@ class ConfigDialog(Toplevel):
An action/key binding can be selected to change the key binding. An action/key binding can be selected to change the key binding.
""" """
reselect = 0 reselect = False
if self.list_bindings.curselection(): if self.bindingslist.curselection():
reselect = 1 reselect = True
list_index = self.list_bindings.index(ANCHOR) list_index = self.bindingslist.index(ANCHOR)
keyset = idleConf.GetKeySet(keyset_name) keyset = idleConf.GetKeySet(keyset_name)
bind_names = list(keyset.keys()) bind_names = list(keyset.keys())
bind_names.sort() bind_names.sort()
self.list_bindings.delete(0, END) self.bindingslist.delete(0, END)
for bind_name in bind_names: for bind_name in bind_names:
key = ' '.join(keyset[bind_name]) key = ' '.join(keyset[bind_name])
bind_name = bind_name[2:-2] # Trim off the angle brackets. bind_name = bind_name[2:-2] # Trim off the angle brackets.
...@@ -1120,17 +1160,21 @@ class ConfigDialog(Toplevel): ...@@ -1120,17 +1160,21 @@ class ConfigDialog(Toplevel):
# Handle any unsaved changes to this key set. # Handle any unsaved changes to this key set.
if bind_name in changes['keys'][keyset_name]: if bind_name in changes['keys'][keyset_name]:
key = changes['keys'][keyset_name][bind_name] key = changes['keys'][keyset_name][bind_name]
self.list_bindings.insert(END, bind_name+' - '+key) self.bindingslist.insert(END, bind_name+' - '+key)
if reselect: if reselect:
self.list_bindings.see(list_index) self.bindingslist.see(list_index)
self.list_bindings.select_set(list_index) self.bindingslist.select_set(list_index)
self.list_bindings.select_anchor(list_index) self.bindingslist.select_anchor(list_index)
def save_new_key_set(self, keyset_name, keyset): def save_new_key_set(self, keyset_name, keyset):
"""Save a newly created core key set. """Save a newly created core key set.
Add keyset to idleConf.userCfg['keys'], not to disk.
If the keyset doesn't exist, it is created. The
binding/keys are taken from the keyset argument.
keyset_name - string, the name of the new key set keyset_name - string, the name of the new key set
keyset - dictionary containing the new key set keyset - dictionary containing the new keybindings
""" """
if not idleConf.userCfg['keys'].has_section(keyset_name): if not idleConf.userCfg['keys'].has_section(keyset_name):
idleConf.userCfg['keys'].add_section(keyset_name) idleConf.userCfg['keys'].add_section(keyset_name)
...@@ -1145,7 +1189,7 @@ class ConfigDialog(Toplevel): ...@@ -1145,7 +1189,7 @@ class ConfigDialog(Toplevel):
reverts to the default. The custom key set is permanently reverts to the default. The custom key set is permanently
deleted from the config file. deleted from the config file.
""" """
keyset_name=self.custom_keys.get() keyset_name=self.custom_name.get()
delmsg = 'Are you sure you wish to delete the key set %r ?' delmsg = 'Are you sure you wish to delete the key set %r ?'
if not tkMessageBox.askyesno( if not tkMessageBox.askyesno(
'Delete Key Set', delmsg % keyset_name, parent=self): 'Delete Key Set', delmsg % keyset_name, parent=self):
...@@ -1157,14 +1201,14 @@ class ConfigDialog(Toplevel): ...@@ -1157,14 +1201,14 @@ class ConfigDialog(Toplevel):
item_list = idleConf.GetSectionList('user', 'keys') item_list = idleConf.GetSectionList('user', 'keys')
item_list.sort() item_list.sort()
if not item_list: if not item_list:
self.radio_keys_custom['state'] = DISABLED self.custom_keyset_on['state'] = DISABLED
self.opt_menu_keys_custom.SetMenu(item_list, '- no custom keys -') self.customlist.SetMenu(item_list, '- no custom keys -')
else: else:
self.opt_menu_keys_custom.SetMenu(item_list, item_list[0]) self.customlist.SetMenu(item_list, item_list[0])
# Revert to default key set. # Revert to default key set.
self.are_keys_builtin.set(idleConf.defaultCfg['main'] self.keyset_source.set(idleConf.defaultCfg['main']
.Get('Keys', 'default')) .Get('Keys', 'default'))
self.builtin_keys.set(idleConf.defaultCfg['main'].Get('Keys', 'name') self.builtin_name.set(idleConf.defaultCfg['main'].Get('Keys', 'name')
or idleConf.default_keys()) or idleConf.default_keys())
# User can't back out of these changes, they must be applied now. # User can't back out of these changes, they must be applied now.
changes.save_all() changes.save_all()
...@@ -1438,8 +1482,7 @@ class FontPage(Frame): ...@@ -1438,8 +1482,7 @@ class FontPage(Frame):
which invokes the default callback to add an entry to which invokes the default callback to add an entry to
changes. Load_tab_cfg initializes space_num to default. changes. Load_tab_cfg initializes space_num to default.
Widget Structure: (*) widgets bound to self Widgets for FontPage(Frame): (*) widgets bound to self
frame (of tab_pages)
frame_font: LabelFrame frame_font: LabelFrame
frame_font_name: Frame frame_font_name: Frame
font_name_title: Label font_name_title: Label
...@@ -1633,8 +1676,7 @@ class GenPage(Frame): ...@@ -1633,8 +1676,7 @@ class GenPage(Frame):
set_add_delete_state. All but load call update_help_changes to set_add_delete_state. All but load call update_help_changes to
rewrite changes['main']['HelpFiles']. rewrite changes['main']['HelpFiles'].
Widget Structure: (*) widgets bound to self Widgets for GenPage(Frame): (*) widgets bound to self
frame
frame_run: LabelFrame frame_run: LabelFrame
startup_title: Label startup_title: Label
(*)startup_editor_on: Radiobutton - startup_edit (*)startup_editor_on: Radiobutton - startup_edit
......
"""Test idlelib.configdialog. """Test idlelib.configdialog.
Half the class creates dialog, half works with user customizations. Half the class creates dialog, half works with user customizations.
Coverage: 63%. Coverage: 81%.
""" """
from idlelib import configdialog from idlelib import configdialog
from test.support import requires from test.support import requires
...@@ -29,6 +29,7 @@ dialog = None ...@@ -29,6 +29,7 @@ dialog = None
mainpage = changes['main'] mainpage = changes['main']
highpage = changes['highlight'] highpage = changes['highlight']
keyspage = changes['keys'] keyspage = changes['keys']
extpage = changes['extensions']
def setUpModule(): def setUpModule():
global root, dialog global root, dialog
...@@ -59,8 +60,6 @@ class FontPageTest(unittest.TestCase): ...@@ -59,8 +60,6 @@ class FontPageTest(unittest.TestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
page = cls.page = dialog.fontpage page = cls.page = dialog.fontpage
#dialog.note.insert(0, page, text='copy')
#dialog.note.add(page, text='copyfgfg')
dialog.note.select(page) dialog.note.select(page)
page.set_samples = Func() # Mask instance method. page.set_samples = Func() # Mask instance method.
...@@ -120,7 +119,7 @@ class FontPageTest(unittest.TestCase): ...@@ -120,7 +119,7 @@ class FontPageTest(unittest.TestCase):
# Click on item should select that item. # Click on item should select that item.
d = self.page d = self.page
if d.fontlist.size() < 2: if d.fontlist.size() < 2:
cls.skipTest('need at least 2 fonts') self.skipTest('need at least 2 fonts')
fontlist = d.fontlist fontlist = d.fontlist
fontlist.activate(0) fontlist.activate(0)
...@@ -233,11 +232,391 @@ class HighlightTest(unittest.TestCase): ...@@ -233,11 +232,391 @@ class HighlightTest(unittest.TestCase):
changes.clear() changes.clear()
class KeysTest(unittest.TestCase): class KeyTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
d = dialog
dialog.note.select(d.keyspage)
d.set_keys_type = Func()
d.load_keys_list = Func()
@classmethod
def tearDownClass(cls):
d = dialog
del d.set_keys_type, d.load_keys_list
def setUp(self): def setUp(self):
d = dialog
# The following is needed for test_load_key_cfg, _delete_custom_keys.
# This may indicate a defect in some test or function.
for section in idleConf.GetSectionList('user', 'keys'):
idleConf.userCfg['keys'].remove_section(section)
changes.clear()
d.set_keys_type.called = 0
d.load_keys_list.called = 0
def test_load_key_cfg(self):
tracers.detach()
d = dialog
eq = self.assertEqual
# Use builtin keyset with no user keysets created.
idleConf.CurrentKeys = mock.Mock(return_value='IDLE Classic OSX')
d.load_key_cfg()
self.assertTrue(d.keyset_source.get())
# builtinlist sets variable builtin_name to the CurrentKeys default.
eq(d.builtin_name.get(), 'IDLE Classic OSX')
eq(d.custom_name.get(), '- no custom keys -')
eq(d.custom_keyset_on['state'], DISABLED)
eq(d.set_keys_type.called, 1)
eq(d.load_keys_list.called, 1)
eq(d.load_keys_list.args, ('IDLE Classic OSX', ))
# Builtin keyset with non-empty user keyset list.
idleConf.SetOption('keys', 'test1', 'option', 'value')
idleConf.SetOption('keys', 'test2', 'option2', 'value2')
d.load_key_cfg()
eq(d.builtin_name.get(), 'IDLE Classic OSX')
eq(d.custom_name.get(), 'test1')
eq(d.set_keys_type.called, 2)
eq(d.load_keys_list.called, 2)
eq(d.load_keys_list.args, ('IDLE Classic OSX', ))
# Use custom keyset.
idleConf.CurrentKeys = mock.Mock(return_value='test2')
idleConf.default_keys = mock.Mock(return_value='IDLE Modern Unix')
idleConf.SetOption('main', 'Keys', 'default', '0')
d.load_key_cfg()
self.assertFalse(d.keyset_source.get())
eq(d.builtin_name.get(), 'IDLE Modern Unix')
eq(d.custom_name.get(), 'test2')
eq(d.set_keys_type.called, 3)
eq(d.load_keys_list.called, 3)
eq(d.load_keys_list.args, ('test2', ))
del idleConf.CurrentKeys, idleConf.default_keys
tracers.attach()
def test_keyset_source(self):
eq = self.assertEqual
d = dialog
# Test these separately.
d.var_changed_builtin_name = Func()
d.var_changed_custom_name = Func()
# Builtin selected.
d.builtin_keyset_on.invoke()
eq(mainpage, {'Keys': {'default': 'True'}})
eq(d.var_changed_builtin_name.called, 1)
eq(d.var_changed_custom_name.called, 0)
changes.clear() changes.clear()
# Custom selected.
d.custom_keyset_on['state'] = NORMAL
d.custom_keyset_on.invoke()
self.assertEqual(mainpage, {'Keys': {'default': 'False'}})
eq(d.var_changed_builtin_name.called, 1)
eq(d.var_changed_custom_name.called, 1)
del d.var_changed_builtin_name, d.var_changed_custom_name
def test_builtin_name(self):
eq = self.assertEqual
d = dialog
idleConf.userCfg['main'].remove_section('Keys')
item_list = ['IDLE Classic Windows', 'IDLE Classic OSX',
'IDLE Modern UNIX']
# Not in old_keys, defaults name to first item.
d.builtinlist.SetMenu(item_list, 'IDLE Modern UNIX')
eq(mainpage, {'Keys': {'name': 'IDLE Classic Windows',
'name2': 'IDLE Modern UNIX'}})
eq(d.keys_message['text'], 'New key set, see Help')
eq(d.load_keys_list.called, 1)
eq(d.load_keys_list.args, ('IDLE Modern UNIX', ))
# Not in old keys - uses name2.
changes.clear()
idleConf.SetOption('main', 'Keys', 'name', 'IDLE Classic Unix')
d.builtinlist.SetMenu(item_list, 'IDLE Modern UNIX')
eq(mainpage, {'Keys': {'name2': 'IDLE Modern UNIX'}})
eq(d.keys_message['text'], 'New key set, see Help')
eq(d.load_keys_list.called, 2)
eq(d.load_keys_list.args, ('IDLE Modern UNIX', ))
# Builtin name in old_keys.
changes.clear()
d.builtinlist.SetMenu(item_list, 'IDLE Classic OSX')
eq(mainpage, {'Keys': {'name': 'IDLE Classic OSX', 'name2': ''}})
eq(d.keys_message['text'], '')
eq(d.load_keys_list.called, 3)
eq(d.load_keys_list.args, ('IDLE Classic OSX', ))
def test_custom_name(self):
d = dialog
# If no selections, doesn't get added.
d.customlist.SetMenu([], '- no custom keys -')
self.assertNotIn('Keys', mainpage)
self.assertEqual(d.load_keys_list.called, 0)
# Custom name selected.
changes.clear()
d.customlist.SetMenu(['a', 'b', 'c'], 'c')
self.assertEqual(mainpage, {'Keys': {'name': 'c'}})
self.assertEqual(d.load_keys_list.called, 1)
def test_keybinding(self):
d = dialog
d.custom_name.set('my custom keys')
d.bindingslist.delete(0, 'end')
d.bindingslist.insert(0, 'copy')
d.bindingslist.insert(1, 'expand-word')
d.bindingslist.selection_set(0)
d.bindingslist.selection_anchor(0)
# Core binding - adds to keys.
d.keybinding.set('<Key-F11>')
self.assertEqual(keyspage,
{'my custom keys': {'copy': '<Key-F11>'}})
# Not a core binding - adds to extensions.
d.bindingslist.selection_set(1)
d.bindingslist.selection_anchor(1)
d.keybinding.set('<Key-F11>')
self.assertEqual(extpage,
{'AutoExpand_cfgBindings': {'expand-word': '<Key-F11>'}})
def test_set_keys_type(self):
eq = self.assertEqual
d = dialog
del d.set_keys_type
# Builtin keyset selected.
d.keyset_source.set(True)
d.set_keys_type()
eq(d.builtinlist['state'], NORMAL)
eq(d.customlist['state'], DISABLED)
eq(d.button_delete_custom_keys['state'], DISABLED)
# Custom keyset selected.
d.keyset_source.set(False)
d.set_keys_type()
eq(d.builtinlist['state'], DISABLED)
eq(d.custom_keyset_on['state'], NORMAL)
eq(d.customlist['state'], NORMAL)
eq(d.button_delete_custom_keys['state'], NORMAL)
d.set_keys_type = Func()
def test_get_new_keys(self):
eq = self.assertEqual
d = dialog
orig_getkeysdialog = configdialog.GetKeysDialog
gkd = configdialog.GetKeysDialog = Func(return_self=True)
gnkn = d.get_new_keys_name = Func()
d.button_new_keys['state'] = NORMAL
d.bindingslist.delete(0, 'end')
d.bindingslist.insert(0, 'copy - <Control-Shift-Key-C>')
d.bindingslist.selection_set(0)
d.bindingslist.selection_anchor(0)
d.keybinding.set('Key-a')
d.keyset_source.set(True) # Default keyset.
# Default keyset; no change to binding.
gkd.result = ''
d.button_new_keys.invoke()
eq(d.bindingslist.get('anchor'), 'copy - <Control-Shift-Key-C>')
# Keybinding isn't changed when there isn't a change entered.
eq(d.keybinding.get(), 'Key-a')
# Default keyset; binding changed.
gkd.result = '<Key-F11>'
# No keyset name selected therefore binding not saved.
gnkn.result = ''
d.button_new_keys.invoke()
eq(gnkn.called, 1)
eq(d.bindingslist.get('anchor'), 'copy - <Control-Shift-Key-C>')
# Keyset name selected.
gnkn.result = 'My New Key Set'
d.button_new_keys.invoke()
eq(d.custom_name.get(), gnkn.result)
eq(d.bindingslist.get('anchor'), 'copy - <Key-F11>')
eq(d.keybinding.get(), '<Key-F11>')
# User keyset; binding changed.
d.keyset_source.set(False) # Custom keyset.
gnkn.called = 0
gkd.result = '<Key-p>'
d.button_new_keys.invoke()
eq(gnkn.called, 0)
eq(d.bindingslist.get('anchor'), 'copy - <Key-p>')
eq(d.keybinding.get(), '<Key-p>')
del d.get_new_keys_name
configdialog.GetKeysDialog = orig_getkeysdialog
def test_get_new_keys_name(self):
orig_sectionname = configdialog.SectionName
sn = configdialog.SectionName = Func(return_self=True)
d = dialog
sn.result = 'New Keys'
self.assertEqual(d.get_new_keys_name(''), 'New Keys')
configdialog.SectionName = orig_sectionname
def test_save_as_new_key_set(self):
d = dialog
gnkn = d.get_new_keys_name = Func()
d.keyset_source.set(True)
# No name entered.
gnkn.result = ''
d.button_save_custom_keys.invoke()
# Name entered.
gnkn.result = 'my new key set'
gnkn.called = 0
self.assertNotIn(gnkn.result, idleConf.userCfg['keys'])
d.button_save_custom_keys.invoke()
self.assertIn(gnkn.result, idleConf.userCfg['keys'])
del d.get_new_keys_name
def test_on_bindingslist_select(self):
d = dialog
b = d.bindingslist
b.delete(0, 'end')
b.insert(0, 'copy')
b.insert(1, 'find')
b.activate(0)
b.focus_force()
b.see(1)
b.update()
x, y, dx, dy = b.bbox(1)
x += dx // 2
y += dy // 2
b.event_generate('<Enter>', x=0, y=0)
b.event_generate('<Motion>', x=x, y=y)
b.event_generate('<Button-1>', x=x, y=y)
b.event_generate('<ButtonRelease-1>', x=x, y=y)
self.assertEqual(b.get('anchor'), 'find')
self.assertEqual(d.button_new_keys['state'], NORMAL)
def test_create_new_key_set_and_save_new_key_set(self):
eq = self.assertEqual
d = dialog
# Use default as previously active keyset.
d.keyset_source.set(True)
d.builtin_name.set('IDLE Classic Windows')
first_new = 'my new custom key set'
second_new = 'my second custom keyset'
# No changes, so keysets are an exact copy.
self.assertNotIn(first_new, idleConf.userCfg)
d.create_new_key_set(first_new)
eq(idleConf.GetSectionList('user', 'keys'), [first_new])
eq(idleConf.GetKeySet('IDLE Classic Windows'),
idleConf.GetKeySet(first_new))
eq(d.custom_name.get(), first_new)
self.assertFalse(d.keyset_source.get()) # Use custom set.
eq(d.set_keys_type.called, 1)
# Test that changed keybindings are in new keyset.
changes.add_option('keys', first_new, 'copy', '<Key-F11>')
self.assertNotIn(second_new, idleConf.userCfg)
d.create_new_key_set(second_new)
eq(idleConf.GetSectionList('user', 'keys'), [first_new, second_new])
self.assertNotEqual(idleConf.GetKeySet(first_new),
idleConf.GetKeySet(second_new))
# Check that difference in keysets was in option `copy` from `changes`.
idleConf.SetOption('keys', first_new, 'copy', '<Key-F11>')
eq(idleConf.GetKeySet(first_new), idleConf.GetKeySet(second_new))
def test_load_keys_list(self):
eq = self.assertEqual
d = dialog
gks = idleConf.GetKeySet = Func()
del d.load_keys_list
b = d.bindingslist
b.delete(0, 'end')
b.insert(0, '<<find>>')
b.insert(1, '<<help>>')
gks.result = {'<<copy>>': ['<Control-Key-c>', '<Control-Key-C>'],
'<<force-open-completions>>': ['<Control-Key-space>'],
'<<spam>>': ['<Key-F11>']}
changes.add_option('keys', 'my keys', 'spam', '<Shift-Key-a>')
expected = ('copy - <Control-Key-c> <Control-Key-C>',
'force-open-completions - <Control-Key-space>',
'spam - <Shift-Key-a>')
# No current selection.
d.load_keys_list('my keys')
eq(b.get(0, 'end'), expected)
eq(b.get('anchor'), '')
eq(b.curselection(), ())
# Check selection.
b.selection_set(1)
b.selection_anchor(1)
d.load_keys_list('my keys')
eq(b.get(0, 'end'), expected)
eq(b.get('anchor'), 'force-open-completions - <Control-Key-space>')
eq(b.curselection(), (1, ))
# Change selection.
b.selection_set(2)
b.selection_anchor(2)
d.load_keys_list('my keys')
eq(b.get(0, 'end'), expected)
eq(b.get('anchor'), 'spam - <Shift-Key-a>')
eq(b.curselection(), (2, ))
d.load_keys_list = Func()
del idleConf.GetKeySet
def test_delete_custom_keys(self):
eq = self.assertEqual
d = dialog
d.button_delete_custom_keys['state'] = NORMAL
yesno = configdialog.tkMessageBox.askyesno = Func()
d.deactivate_current_config = Func()
d.activate_config_changes = Func()
keyset_name = 'spam key set'
idleConf.userCfg['keys'].SetOption(keyset_name, 'name', 'value')
keyspage[keyset_name] = {'option': 'True'}
# Force custom keyset.
d.keyset_source.set(False)
d.custom_name.set(keyset_name)
# Cancel deletion.
yesno.result = False
d.button_delete_custom_keys.invoke()
eq(yesno.called, 1)
eq(keyspage[keyset_name], {'option': 'True'})
eq(idleConf.GetSectionList('user', 'keys'), ['spam key set'])
eq(d.deactivate_current_config.called, 0)
eq(d.activate_config_changes.called, 0)
eq(d.set_keys_type.called, 0)
# Confirm deletion.
yesno.result = True
d.button_delete_custom_keys.invoke()
eq(yesno.called, 2)
self.assertNotIn(keyset_name, keyspage)
eq(idleConf.GetSectionList('user', 'keys'), [])
eq(d.custom_keyset_on['state'], DISABLED)
eq(d.custom_name.get(), '- no custom keys -')
eq(d.deactivate_current_config.called, 1)
eq(d.activate_config_changes.called, 1)
eq(d.set_keys_type.called, 1)
del d.activate_config_changes, d.deactivate_current_config
del configdialog.tkMessageBox.askyesno
class GenPageTest(unittest.TestCase): class GenPageTest(unittest.TestCase):
"""Test that general tab widgets enable users to make changes. """Test that general tab widgets enable users to make changes.
......
Add tests for configdialog keys tab. Patch by Cheryl Sabella.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment