mirror of
				https://github.com/python/cpython.git
				synced 2025-10-30 21:21:22 +00:00 
			
		
		
		
	bpo-31205: IDLE: Factor KeysPage class from ConfigDialog (#3096)
The slightly modified tests continue to pass. Patch by Cheryl Sabella.
This commit is contained in:
		
							parent
							
								
									1f512b9a34
								
							
						
					
					
						commit
						e36d9f5568
					
				
					 3 changed files with 470 additions and 457 deletions
				
			
		|  | @ -92,9 +92,9 @@ def create_widgets(self): | ||||||
|             note: Notebook |             note: Notebook | ||||||
|             highpage: self.create_page_highlight |             highpage: self.create_page_highlight | ||||||
|             fontpage: FontPage |             fontpage: FontPage | ||||||
|             keyspage: self.create_page_keys |             keyspage: KeysPage | ||||||
|             genpage: GenPage |             genpage: GenPage | ||||||
|             extpageL self.create_page_extensions |             extpage: self.create_page_extensions | ||||||
| 
 | 
 | ||||||
|         Methods: |         Methods: | ||||||
|             create_action_buttons |             create_action_buttons | ||||||
|  | @ -104,7 +104,7 @@ def create_widgets(self): | ||||||
|         self.note = note = Notebook(self, width=450, height=450) |         self.note = note = Notebook(self, width=450, height=450) | ||||||
|         self.highpage = self.create_page_highlight() |         self.highpage = self.create_page_highlight() | ||||||
|         self.fontpage = FontPage(note, self.highpage) |         self.fontpage = FontPage(note, self.highpage) | ||||||
|         self.keyspage = self.create_page_keys() |         self.keyspage = KeysPage(note) | ||||||
|         self.genpage = GenPage(note) |         self.genpage = GenPage(note) | ||||||
|         self.extpage = self.create_page_extensions() |         self.extpage = self.create_page_extensions() | ||||||
|         note.add(self.fontpage, text='Fonts/Tabs') |         note.add(self.fontpage, text='Fonts/Tabs') | ||||||
|  | @ -132,7 +132,7 @@ def load_configs(self): | ||||||
|         #self.load_font_cfg() |         #self.load_font_cfg() | ||||||
|         #self.load_tab_cfg() |         #self.load_tab_cfg() | ||||||
|         self.load_theme_cfg() |         self.load_theme_cfg() | ||||||
|         self.load_key_cfg() |         # self.load_key_cfg() | ||||||
|         # self.load_general_cfg() |         # self.load_general_cfg() | ||||||
|         # note: extension page handled separately |         # note: extension page handled separately | ||||||
| 
 | 
 | ||||||
|  | @ -791,431 +791,6 @@ def delete_custom_theme(self): | ||||||
|         self.activate_config_changes() |         self.activate_config_changes() | ||||||
|         self.set_theme_type() |         self.set_theme_type() | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def create_page_keys(self): |  | ||||||
|         """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: |  | ||||||
|             keybinding: Action/key bindings. |  | ||||||
| 
 |  | ||||||
|         Methods: |  | ||||||
|             load_keys_list: Reload active set. |  | ||||||
|             create_new_key_set: Combine active keyset and changes. |  | ||||||
|             set_keys_type: Command for keyset_source. |  | ||||||
|             save_new_key_set: Save to idleConf.userCfg['keys'] (is function). |  | ||||||
|             deactivate_current_config: Remove keys bindings in editors. |  | ||||||
| 
 |  | ||||||
|         Widgets for keys page frame:  (*) widgets bound to self |  | ||||||
|             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_target: Frame |  | ||||||
|                     target_title: Label |  | ||||||
|                     scroll_target_y: Scrollbar |  | ||||||
|                     scroll_target_x: Scrollbar |  | ||||||
|                     (*)bindingslist: ListBox - on_bindingslist_select |  | ||||||
|                     (*)button_new_keys: Button - get_new_keys & ..._name |  | ||||||
|         """ |  | ||||||
|         parent = self.parent |  | ||||||
|         self.builtin_name = tracers.add( |  | ||||||
|                 StringVar(parent), self.var_changed_builtin_name) |  | ||||||
|         self.custom_name = tracers.add( |  | ||||||
|                 StringVar(parent), self.var_changed_custom_name) |  | ||||||
|         self.keyset_source = tracers.add( |  | ||||||
|                 BooleanVar(parent), self.var_changed_keyset_source) |  | ||||||
|         self.keybinding = tracers.add( |  | ||||||
|                 StringVar(parent), self.var_changed_keybinding) |  | ||||||
| 
 |  | ||||||
|         # Widget creation: |  | ||||||
|         # body and section frames. |  | ||||||
|         frame = Frame(self.note) |  | ||||||
|         frame_custom = LabelFrame( |  | ||||||
|                 frame, borderwidth=2, relief=GROOVE, |  | ||||||
|                 text=' Custom Key Bindings ') |  | ||||||
|         frame_key_sets = LabelFrame( |  | ||||||
|                 frame, borderwidth=2, relief=GROOVE, text=' Key Set ') |  | ||||||
|         #frame_custom |  | ||||||
|         frame_target = Frame(frame_custom) |  | ||||||
|         target_title = Label(frame_target, text='Action - Key(s)') |  | ||||||
|         scroll_target_y = Scrollbar(frame_target) |  | ||||||
|         scroll_target_x = Scrollbar(frame_target, orient=HORIZONTAL) |  | ||||||
|         self.bindingslist = Listbox( |  | ||||||
|                 frame_target, takefocus=FALSE, exportselection=FALSE) |  | ||||||
|         self.bindingslist.bind('<ButtonRelease-1>', |  | ||||||
|                                self.on_bindingslist_select) |  | ||||||
|         scroll_target_y['command'] = self.bindingslist.yview |  | ||||||
|         scroll_target_x['command'] = self.bindingslist.xview |  | ||||||
|         self.bindingslist['yscrollcommand'] = scroll_target_y.set |  | ||||||
|         self.bindingslist['xscrollcommand'] = scroll_target_x.set |  | ||||||
|         self.button_new_keys = Button( |  | ||||||
|                 frame_custom, text='Get New Keys for Selection', |  | ||||||
|                 command=self.get_new_keys, state=DISABLED) |  | ||||||
|         #frame_key_sets |  | ||||||
|         frames = [Frame(frame_key_sets, padx=2, pady=2, borderwidth=0) |  | ||||||
|                   for i in range(2)] |  | ||||||
|         self.builtin_keyset_on = Radiobutton( |  | ||||||
|                 frames[0], variable=self.keyset_source, value=1, |  | ||||||
|                 command=self.set_keys_type, text='Use a Built-in Key Set') |  | ||||||
|         self.custom_keyset_on = Radiobutton( |  | ||||||
|                 frames[0], variable=self.keyset_source, value=0, |  | ||||||
|                 command=self.set_keys_type, text='Use a Custom Key Set') |  | ||||||
|         self.builtinlist = DynOptionMenu( |  | ||||||
|                 frames[0], self.builtin_name, None, command=None) |  | ||||||
|         self.customlist = DynOptionMenu( |  | ||||||
|                 frames[0], self.custom_name, None, command=None) |  | ||||||
|         self.button_delete_custom_keys = Button( |  | ||||||
|                 frames[1], text='Delete Custom Key Set', |  | ||||||
|                 command=self.delete_custom_keys) |  | ||||||
|         self.button_save_custom_keys = Button( |  | ||||||
|                 frames[1], text='Save as New Custom Key Set', |  | ||||||
|                 command=self.save_as_new_key_set) |  | ||||||
|         self.keys_message = Label(frames[0], bd=2) |  | ||||||
| 
 |  | ||||||
|         ##widget packing |  | ||||||
|         #body |  | ||||||
|         frame_custom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH) |  | ||||||
|         frame_key_sets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH) |  | ||||||
|         #frame_custom |  | ||||||
|         self.button_new_keys.pack(side=BOTTOM, fill=X, padx=5, pady=5) |  | ||||||
|         frame_target.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) |  | ||||||
|         #frame target |  | ||||||
|         frame_target.columnconfigure(0, weight=1) |  | ||||||
|         frame_target.rowconfigure(1, weight=1) |  | ||||||
|         target_title.grid(row=0, column=0, columnspan=2, sticky=W) |  | ||||||
|         self.bindingslist.grid(row=1, column=0, sticky=NSEW) |  | ||||||
|         scroll_target_y.grid(row=1, column=1, sticky=NS) |  | ||||||
|         scroll_target_x.grid(row=2, column=0, sticky=EW) |  | ||||||
|         #frame_key_sets |  | ||||||
|         self.builtin_keyset_on.grid(row=0, column=0, sticky=W+NS) |  | ||||||
|         self.custom_keyset_on.grid(row=1, column=0, sticky=W+NS) |  | ||||||
|         self.builtinlist.grid(row=0, column=1, sticky=NSEW) |  | ||||||
|         self.customlist.grid(row=1, column=1, sticky=NSEW) |  | ||||||
|         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_save_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2) |  | ||||||
|         frames[0].pack(side=TOP, fill=BOTH, expand=True) |  | ||||||
|         frames[1].pack(side=TOP, fill=X, expand=True, pady=2) |  | ||||||
|         return frame |  | ||||||
| 
 |  | ||||||
|     def load_key_cfg(self): |  | ||||||
|         "Load current configuration settings for the keybinding options." |  | ||||||
|         # Set current keys type radiobutton. |  | ||||||
|         self.keyset_source.set(idleConf.GetOption( |  | ||||||
|                 'main', 'Keys', 'default', type='bool', default=1)) |  | ||||||
|         # Set current keys. |  | ||||||
|         current_option = idleConf.CurrentKeys() |  | ||||||
|         # Load available keyset option menus. |  | ||||||
|         if self.keyset_source.get():  # Default theme selected. |  | ||||||
|             item_list = idleConf.GetSectionList('default', 'keys') |  | ||||||
|             item_list.sort() |  | ||||||
|             self.builtinlist.SetMenu(item_list, current_option) |  | ||||||
|             item_list = idleConf.GetSectionList('user', 'keys') |  | ||||||
|             item_list.sort() |  | ||||||
|             if not item_list: |  | ||||||
|                 self.custom_keyset_on['state'] = DISABLED |  | ||||||
|                 self.custom_name.set('- no custom keys -') |  | ||||||
|             else: |  | ||||||
|                 self.customlist.SetMenu(item_list, item_list[0]) |  | ||||||
|         else:  # User key set selected. |  | ||||||
|             item_list = idleConf.GetSectionList('user', 'keys') |  | ||||||
|             item_list.sort() |  | ||||||
|             self.customlist.SetMenu(item_list, current_option) |  | ||||||
|             item_list = idleConf.GetSectionList('default', 'keys') |  | ||||||
|             item_list.sort() |  | ||||||
|             self.builtinlist.SetMenu(item_list, idleConf.default_keys()) |  | ||||||
|         self.set_keys_type() |  | ||||||
|         # Load keyset element list. |  | ||||||
|         keyset_name = idleConf.CurrentKeys() |  | ||||||
|         self.load_keys_list(keyset_name) |  | ||||||
| 
 |  | ||||||
|     def var_changed_builtin_name(self, *params): |  | ||||||
|         "Process selection of builtin key set." |  | ||||||
|         old_keys = ( |  | ||||||
|             'IDLE Classic Windows', |  | ||||||
|             'IDLE Classic Unix', |  | ||||||
|             'IDLE Classic Mac', |  | ||||||
|             'IDLE Classic OSX', |  | ||||||
|         ) |  | ||||||
|         value = self.builtin_name.get() |  | ||||||
|         if value 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', 'name2', value) |  | ||||||
|             self.keys_message['text'] = 'New key set, see Help' |  | ||||||
|             self.keys_message['fg'] = '#500000' |  | ||||||
|         else: |  | ||||||
|             changes.add_option('main', 'Keys', 'name', value) |  | ||||||
|             changes.add_option('main', 'Keys', 'name2', '') |  | ||||||
|             self.keys_message['text'] = '' |  | ||||||
|             self.keys_message['fg'] = 'black' |  | ||||||
|         self.load_keys_list(value) |  | ||||||
| 
 |  | ||||||
|     def var_changed_custom_name(self, *params): |  | ||||||
|         "Process selection of custom key set." |  | ||||||
|         value = self.custom_name.get() |  | ||||||
|         if value != '- no custom keys -': |  | ||||||
|             changes.add_option('main', 'Keys', 'name', value) |  | ||||||
|             self.load_keys_list(value) |  | ||||||
| 
 |  | ||||||
|     def var_changed_keyset_source(self, *params): |  | ||||||
|         "Process toggle between builtin key set and custom key set." |  | ||||||
|         value = self.keyset_source.get() |  | ||||||
|         changes.add_option('main', 'Keys', 'default', value) |  | ||||||
|         if value: |  | ||||||
|             self.var_changed_builtin_name() |  | ||||||
|         else: |  | ||||||
|             self.var_changed_custom_name() |  | ||||||
| 
 |  | ||||||
|     def var_changed_keybinding(self, *params): |  | ||||||
|         "Store change to a keybinding." |  | ||||||
|         value = self.keybinding.get() |  | ||||||
|         key_set = self.custom_name.get() |  | ||||||
|         event = self.bindingslist.get(ANCHOR).split()[0] |  | ||||||
|         if idleConf.IsCoreBinding(event): |  | ||||||
|             changes.add_option('keys', key_set, event, value) |  | ||||||
|         else:  # Event is an extension binding. |  | ||||||
|             ext_name = idleConf.GetExtnNameForEvent(event) |  | ||||||
|             ext_keybind_section = ext_name + '_cfgBindings' |  | ||||||
|             changes.add_option('extensions', ext_keybind_section, event, value) |  | ||||||
| 
 |  | ||||||
|     def set_keys_type(self): |  | ||||||
|         "Set available screen options based on builtin or custom key set." |  | ||||||
|         if self.keyset_source.get(): |  | ||||||
|             self.builtinlist['state'] = NORMAL |  | ||||||
|             self.customlist['state'] = DISABLED |  | ||||||
|             self.button_delete_custom_keys['state'] = DISABLED |  | ||||||
|         else: |  | ||||||
|             self.builtinlist['state'] = DISABLED |  | ||||||
|             self.custom_keyset_on['state'] = NORMAL |  | ||||||
|             self.customlist['state'] = NORMAL |  | ||||||
|             self.button_delete_custom_keys['state'] = NORMAL |  | ||||||
| 
 |  | ||||||
|     def get_new_keys(self): |  | ||||||
|         """Handle event to change key binding for selected line. |  | ||||||
| 
 |  | ||||||
|         A selection of a key/binding in the list of current |  | ||||||
|         bindings pops up a dialog to enter a new binding.  If |  | ||||||
|         the current key set is builtin and a binding has |  | ||||||
|         changed, then a name for a custom key set needs to be |  | ||||||
|         entered for the change to be applied. |  | ||||||
|         """ |  | ||||||
|         list_index = self.bindingslist.index(ANCHOR) |  | ||||||
|         binding = self.bindingslist.get(list_index) |  | ||||||
|         bind_name = binding.split()[0] |  | ||||||
|         if self.keyset_source.get(): |  | ||||||
|             current_key_set_name = self.builtin_name.get() |  | ||||||
|         else: |  | ||||||
|             current_key_set_name = self.custom_name.get() |  | ||||||
|         current_bindings = idleConf.GetCurrentKeySet() |  | ||||||
|         if current_key_set_name in changes['keys']:  # unsaved changes |  | ||||||
|             key_set_changes = changes['keys'][current_key_set_name] |  | ||||||
|             for event in key_set_changes: |  | ||||||
|                 current_bindings[event] = key_set_changes[event].split() |  | ||||||
|         current_key_sequences = list(current_bindings.values()) |  | ||||||
|         new_keys = GetKeysDialog(self, 'Get New Keys', bind_name, |  | ||||||
|                 current_key_sequences).result |  | ||||||
|         if new_keys: |  | ||||||
|             if self.keyset_source.get():  # Current key set is a built-in. |  | ||||||
|                 message = ('Your changes will be saved as a new Custom Key Set.' |  | ||||||
|                            ' Enter a name for your new Custom Key Set below.') |  | ||||||
|                 new_keyset = self.get_new_keys_name(message) |  | ||||||
|                 if not new_keyset:  # User cancelled custom key set creation. |  | ||||||
|                     self.bindingslist.select_set(list_index) |  | ||||||
|                     self.bindingslist.select_anchor(list_index) |  | ||||||
|                     return |  | ||||||
|                 else:  # Create new custom key set based on previously active key set. |  | ||||||
|                     self.create_new_key_set(new_keyset) |  | ||||||
|             self.bindingslist.delete(list_index) |  | ||||||
|             self.bindingslist.insert(list_index, bind_name+' - '+new_keys) |  | ||||||
|             self.bindingslist.select_set(list_index) |  | ||||||
|             self.bindingslist.select_anchor(list_index) |  | ||||||
|             self.keybinding.set(new_keys) |  | ||||||
|         else: |  | ||||||
|             self.bindingslist.select_set(list_index) |  | ||||||
|             self.bindingslist.select_anchor(list_index) |  | ||||||
| 
 |  | ||||||
|     def get_new_keys_name(self, message): |  | ||||||
|         "Return new key set name from query popup." |  | ||||||
|         used_names = (idleConf.GetSectionList('user', 'keys') + |  | ||||||
|                 idleConf.GetSectionList('default', 'keys')) |  | ||||||
|         new_keyset = SectionName( |  | ||||||
|                 self, 'New Custom Key Set', message, used_names).result |  | ||||||
|         return new_keyset |  | ||||||
| 
 |  | ||||||
|     def save_as_new_key_set(self): |  | ||||||
|         "Prompt for name of new key set and save changes using that name." |  | ||||||
|         new_keys_name = self.get_new_keys_name('New Key Set Name:') |  | ||||||
|         if new_keys_name: |  | ||||||
|             self.create_new_key_set(new_keys_name) |  | ||||||
| 
 |  | ||||||
|     def on_bindingslist_select(self, event): |  | ||||||
|         "Activate button to assign new keys to selected action." |  | ||||||
|         self.button_new_keys['state'] = NORMAL |  | ||||||
| 
 |  | ||||||
|     def create_new_key_set(self, new_key_set_name): |  | ||||||
|         """Create a new custom key set with the given name. |  | ||||||
| 
 |  | ||||||
|         Copy the bindings/keys from the previously active keyset |  | ||||||
|         to the new keyset and activate the new custom keyset. |  | ||||||
|         """ |  | ||||||
|         if self.keyset_source.get(): |  | ||||||
|             prev_key_set_name = self.builtin_name.get() |  | ||||||
|         else: |  | ||||||
|             prev_key_set_name = self.custom_name.get() |  | ||||||
|         prev_keys = idleConf.GetCoreKeys(prev_key_set_name) |  | ||||||
|         new_keys = {} |  | ||||||
|         for event in prev_keys:  # Add key set to changed items. |  | ||||||
|             event_name = event[2:-2]  # Trim off the angle brackets. |  | ||||||
|             binding = ' '.join(prev_keys[event]) |  | ||||||
|             new_keys[event_name] = binding |  | ||||||
|         # Handle any unsaved changes to prev key set. |  | ||||||
|         if prev_key_set_name in changes['keys']: |  | ||||||
|             key_set_changes = changes['keys'][prev_key_set_name] |  | ||||||
|             for event in key_set_changes: |  | ||||||
|                 new_keys[event] = key_set_changes[event] |  | ||||||
|         # Save the new key set. |  | ||||||
|         self.save_new_key_set(new_key_set_name, new_keys) |  | ||||||
|         # Change GUI over to the new key set. |  | ||||||
|         custom_key_list = idleConf.GetSectionList('user', 'keys') |  | ||||||
|         custom_key_list.sort() |  | ||||||
|         self.customlist.SetMenu(custom_key_list, new_key_set_name) |  | ||||||
|         self.keyset_source.set(0) |  | ||||||
|         self.set_keys_type() |  | ||||||
| 
 |  | ||||||
|     def load_keys_list(self, keyset_name): |  | ||||||
|         """Reload the list of action/key binding pairs for the active key set. |  | ||||||
| 
 |  | ||||||
|         An action/key binding can be selected to change the key binding. |  | ||||||
|         """ |  | ||||||
|         reselect = False |  | ||||||
|         if self.bindingslist.curselection(): |  | ||||||
|             reselect = True |  | ||||||
|             list_index = self.bindingslist.index(ANCHOR) |  | ||||||
|         keyset = idleConf.GetKeySet(keyset_name) |  | ||||||
|         bind_names = list(keyset.keys()) |  | ||||||
|         bind_names.sort() |  | ||||||
|         self.bindingslist.delete(0, END) |  | ||||||
|         for bind_name in bind_names: |  | ||||||
|             key = ' '.join(keyset[bind_name]) |  | ||||||
|             bind_name = bind_name[2:-2]  # Trim off the angle brackets. |  | ||||||
|             if keyset_name in changes['keys']: |  | ||||||
|                 # Handle any unsaved changes to this key set. |  | ||||||
|                 if bind_name in changes['keys'][keyset_name]: |  | ||||||
|                     key = changes['keys'][keyset_name][bind_name] |  | ||||||
|             self.bindingslist.insert(END, bind_name+' - '+key) |  | ||||||
|         if reselect: |  | ||||||
|             self.bindingslist.see(list_index) |  | ||||||
|             self.bindingslist.select_set(list_index) |  | ||||||
|             self.bindingslist.select_anchor(list_index) |  | ||||||
| 
 |  | ||||||
|     def save_new_key_set(self, keyset_name, keyset): |  | ||||||
|         """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 - dictionary containing the new keybindings |  | ||||||
|         """ |  | ||||||
|         if not idleConf.userCfg['keys'].has_section(keyset_name): |  | ||||||
|             idleConf.userCfg['keys'].add_section(keyset_name) |  | ||||||
|         for event in keyset: |  | ||||||
|             value = keyset[event] |  | ||||||
|             idleConf.userCfg['keys'].SetOption(keyset_name, event, value) |  | ||||||
| 
 |  | ||||||
|     def delete_custom_keys(self): |  | ||||||
|         """Handle event to delete a custom key set. |  | ||||||
| 
 |  | ||||||
|         Applying the delete deactivates the current configuration and |  | ||||||
|         reverts to the default.  The custom key set is permanently |  | ||||||
|         deleted from the config file. |  | ||||||
|         """ |  | ||||||
|         keyset_name=self.custom_name.get() |  | ||||||
|         delmsg = 'Are you sure you wish to delete the key set %r ?' |  | ||||||
|         if not tkMessageBox.askyesno( |  | ||||||
|                 'Delete Key Set',  delmsg % keyset_name, parent=self): |  | ||||||
|             return |  | ||||||
|         self.deactivate_current_config() |  | ||||||
|         # Remove key set from changes, config, and file. |  | ||||||
|         changes.delete_section('keys', keyset_name) |  | ||||||
|         # Reload user key set list. |  | ||||||
|         item_list = idleConf.GetSectionList('user', 'keys') |  | ||||||
|         item_list.sort() |  | ||||||
|         if not item_list: |  | ||||||
|             self.custom_keyset_on['state'] = DISABLED |  | ||||||
|             self.customlist.SetMenu(item_list, '- no custom keys -') |  | ||||||
|         else: |  | ||||||
|             self.customlist.SetMenu(item_list, item_list[0]) |  | ||||||
|         # Revert to default key set. |  | ||||||
|         self.keyset_source.set(idleConf.defaultCfg['main'] |  | ||||||
|                                 .Get('Keys', 'default')) |  | ||||||
|         self.builtin_name.set(idleConf.defaultCfg['main'].Get('Keys', 'name') |  | ||||||
|                              or idleConf.default_keys()) |  | ||||||
|         # User can't back out of these changes, they must be applied now. |  | ||||||
|         changes.save_all() |  | ||||||
|         self.save_all_changed_extensions() |  | ||||||
|         self.activate_config_changes() |  | ||||||
|         self.set_keys_type() |  | ||||||
| 
 |  | ||||||
|     def deactivate_current_config(self): |     def deactivate_current_config(self): | ||||||
|         """Remove current key bindings. |         """Remove current key bindings. | ||||||
| 
 | 
 | ||||||
|  | @ -1650,6 +1225,437 @@ def var_changed_space_num(self, *params): | ||||||
|         changes.add_option('main', 'Indent', 'num-spaces', value) |         changes.add_option('main', 'Indent', 'num-spaces', value) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | class KeysPage(Frame): | ||||||
|  | 
 | ||||||
|  |     def __init__(self, master): | ||||||
|  |         super().__init__(master) | ||||||
|  |         self.cd = master.master | ||||||
|  |         self.create_page_keys() | ||||||
|  |         self.load_key_cfg() | ||||||
|  | 
 | ||||||
|  |     def create_page_keys(self): | ||||||
|  |         """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: | ||||||
|  |             keybinding: Action/key bindings. | ||||||
|  | 
 | ||||||
|  |         Methods: | ||||||
|  |             load_keys_list: Reload active set. | ||||||
|  |             create_new_key_set: Combine active keyset and changes. | ||||||
|  |             set_keys_type: Command for keyset_source. | ||||||
|  |             save_new_key_set: Save to idleConf.userCfg['keys'] (is function). | ||||||
|  |             deactivate_current_config: Remove keys bindings in editors. | ||||||
|  | 
 | ||||||
|  |         Widgets for KeysPage(frame):  (*) widgets bound to self | ||||||
|  |             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_target: Frame | ||||||
|  |                     target_title: Label | ||||||
|  |                     scroll_target_y: Scrollbar | ||||||
|  |                     scroll_target_x: Scrollbar | ||||||
|  |                     (*)bindingslist: ListBox - on_bindingslist_select | ||||||
|  |                     (*)button_new_keys: Button - get_new_keys & ..._name | ||||||
|  |         """ | ||||||
|  |         self.builtin_name = tracers.add( | ||||||
|  |                 StringVar(self), self.var_changed_builtin_name) | ||||||
|  |         self.custom_name = tracers.add( | ||||||
|  |                 StringVar(self), self.var_changed_custom_name) | ||||||
|  |         self.keyset_source = tracers.add( | ||||||
|  |                 BooleanVar(self), self.var_changed_keyset_source) | ||||||
|  |         self.keybinding = tracers.add( | ||||||
|  |                 StringVar(self), self.var_changed_keybinding) | ||||||
|  | 
 | ||||||
|  |         # Create widgets: | ||||||
|  |         # body and section frames. | ||||||
|  |         frame_custom = LabelFrame( | ||||||
|  |                 self, borderwidth=2, relief=GROOVE, | ||||||
|  |                 text=' Custom Key Bindings ') | ||||||
|  |         frame_key_sets = LabelFrame( | ||||||
|  |                 self, borderwidth=2, relief=GROOVE, text=' Key Set ') | ||||||
|  |         # frame_custom. | ||||||
|  |         frame_target = Frame(frame_custom) | ||||||
|  |         target_title = Label(frame_target, text='Action - Key(s)') | ||||||
|  |         scroll_target_y = Scrollbar(frame_target) | ||||||
|  |         scroll_target_x = Scrollbar(frame_target, orient=HORIZONTAL) | ||||||
|  |         self.bindingslist = Listbox( | ||||||
|  |                 frame_target, takefocus=FALSE, exportselection=FALSE) | ||||||
|  |         self.bindingslist.bind('<ButtonRelease-1>', | ||||||
|  |                                self.on_bindingslist_select) | ||||||
|  |         scroll_target_y['command'] = self.bindingslist.yview | ||||||
|  |         scroll_target_x['command'] = self.bindingslist.xview | ||||||
|  |         self.bindingslist['yscrollcommand'] = scroll_target_y.set | ||||||
|  |         self.bindingslist['xscrollcommand'] = scroll_target_x.set | ||||||
|  |         self.button_new_keys = Button( | ||||||
|  |                 frame_custom, text='Get New Keys for Selection', | ||||||
|  |                 command=self.get_new_keys, state=DISABLED) | ||||||
|  |         # frame_key_sets. | ||||||
|  |         frames = [Frame(frame_key_sets, padx=2, pady=2, borderwidth=0) | ||||||
|  |                   for i in range(2)] | ||||||
|  |         self.builtin_keyset_on = Radiobutton( | ||||||
|  |                 frames[0], variable=self.keyset_source, value=1, | ||||||
|  |                 command=self.set_keys_type, text='Use a Built-in Key Set') | ||||||
|  |         self.custom_keyset_on = Radiobutton( | ||||||
|  |                 frames[0], variable=self.keyset_source, value=0, | ||||||
|  |                 command=self.set_keys_type, text='Use a Custom Key Set') | ||||||
|  |         self.builtinlist = DynOptionMenu( | ||||||
|  |                 frames[0], self.builtin_name, None, command=None) | ||||||
|  |         self.customlist = DynOptionMenu( | ||||||
|  |                 frames[0], self.custom_name, None, command=None) | ||||||
|  |         self.button_delete_custom_keys = Button( | ||||||
|  |                 frames[1], text='Delete Custom Key Set', | ||||||
|  |                 command=self.delete_custom_keys) | ||||||
|  |         self.button_save_custom_keys = Button( | ||||||
|  |                 frames[1], text='Save as New Custom Key Set', | ||||||
|  |                 command=self.save_as_new_key_set) | ||||||
|  |         self.keys_message = Label(frames[0], bd=2) | ||||||
|  | 
 | ||||||
|  |         # Pack widgets: | ||||||
|  |         # body. | ||||||
|  |         frame_custom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH) | ||||||
|  |         frame_key_sets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH) | ||||||
|  |         # frame_custom. | ||||||
|  |         self.button_new_keys.pack(side=BOTTOM, fill=X, padx=5, pady=5) | ||||||
|  |         frame_target.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) | ||||||
|  |         # frame_target. | ||||||
|  |         frame_target.columnconfigure(0, weight=1) | ||||||
|  |         frame_target.rowconfigure(1, weight=1) | ||||||
|  |         target_title.grid(row=0, column=0, columnspan=2, sticky=W) | ||||||
|  |         self.bindingslist.grid(row=1, column=0, sticky=NSEW) | ||||||
|  |         scroll_target_y.grid(row=1, column=1, sticky=NS) | ||||||
|  |         scroll_target_x.grid(row=2, column=0, sticky=EW) | ||||||
|  |         # frame_key_sets. | ||||||
|  |         self.builtin_keyset_on.grid(row=0, column=0, sticky=W+NS) | ||||||
|  |         self.custom_keyset_on.grid(row=1, column=0, sticky=W+NS) | ||||||
|  |         self.builtinlist.grid(row=0, column=1, sticky=NSEW) | ||||||
|  |         self.customlist.grid(row=1, column=1, sticky=NSEW) | ||||||
|  |         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_save_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2) | ||||||
|  |         frames[0].pack(side=TOP, fill=BOTH, expand=True) | ||||||
|  |         frames[1].pack(side=TOP, fill=X, expand=True, pady=2) | ||||||
|  | 
 | ||||||
|  |     def load_key_cfg(self): | ||||||
|  |         "Load current configuration settings for the keybinding options." | ||||||
|  |         # Set current keys type radiobutton. | ||||||
|  |         self.keyset_source.set(idleConf.GetOption( | ||||||
|  |                 'main', 'Keys', 'default', type='bool', default=1)) | ||||||
|  |         # Set current keys. | ||||||
|  |         current_option = idleConf.CurrentKeys() | ||||||
|  |         # Load available keyset option menus. | ||||||
|  |         if self.keyset_source.get():  # Default theme selected. | ||||||
|  |             item_list = idleConf.GetSectionList('default', 'keys') | ||||||
|  |             item_list.sort() | ||||||
|  |             self.builtinlist.SetMenu(item_list, current_option) | ||||||
|  |             item_list = idleConf.GetSectionList('user', 'keys') | ||||||
|  |             item_list.sort() | ||||||
|  |             if not item_list: | ||||||
|  |                 self.custom_keyset_on['state'] = DISABLED | ||||||
|  |                 self.custom_name.set('- no custom keys -') | ||||||
|  |             else: | ||||||
|  |                 self.customlist.SetMenu(item_list, item_list[0]) | ||||||
|  |         else:  # User key set selected. | ||||||
|  |             item_list = idleConf.GetSectionList('user', 'keys') | ||||||
|  |             item_list.sort() | ||||||
|  |             self.customlist.SetMenu(item_list, current_option) | ||||||
|  |             item_list = idleConf.GetSectionList('default', 'keys') | ||||||
|  |             item_list.sort() | ||||||
|  |             self.builtinlist.SetMenu(item_list, idleConf.default_keys()) | ||||||
|  |         self.set_keys_type() | ||||||
|  |         # Load keyset element list. | ||||||
|  |         keyset_name = idleConf.CurrentKeys() | ||||||
|  |         self.load_keys_list(keyset_name) | ||||||
|  | 
 | ||||||
|  |     def var_changed_builtin_name(self, *params): | ||||||
|  |         "Process selection of builtin key set." | ||||||
|  |         old_keys = ( | ||||||
|  |             'IDLE Classic Windows', | ||||||
|  |             'IDLE Classic Unix', | ||||||
|  |             'IDLE Classic Mac', | ||||||
|  |             'IDLE Classic OSX', | ||||||
|  |         ) | ||||||
|  |         value = self.builtin_name.get() | ||||||
|  |         if value 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', 'name2', value) | ||||||
|  |             self.keys_message['text'] = 'New key set, see Help' | ||||||
|  |             self.keys_message['fg'] = '#500000' | ||||||
|  |         else: | ||||||
|  |             changes.add_option('main', 'Keys', 'name', value) | ||||||
|  |             changes.add_option('main', 'Keys', 'name2', '') | ||||||
|  |             self.keys_message['text'] = '' | ||||||
|  |             self.keys_message['fg'] = 'black' | ||||||
|  |         self.load_keys_list(value) | ||||||
|  | 
 | ||||||
|  |     def var_changed_custom_name(self, *params): | ||||||
|  |         "Process selection of custom key set." | ||||||
|  |         value = self.custom_name.get() | ||||||
|  |         if value != '- no custom keys -': | ||||||
|  |             changes.add_option('main', 'Keys', 'name', value) | ||||||
|  |             self.load_keys_list(value) | ||||||
|  | 
 | ||||||
|  |     def var_changed_keyset_source(self, *params): | ||||||
|  |         "Process toggle between builtin key set and custom key set." | ||||||
|  |         value = self.keyset_source.get() | ||||||
|  |         changes.add_option('main', 'Keys', 'default', value) | ||||||
|  |         if value: | ||||||
|  |             self.var_changed_builtin_name() | ||||||
|  |         else: | ||||||
|  |             self.var_changed_custom_name() | ||||||
|  | 
 | ||||||
|  |     def var_changed_keybinding(self, *params): | ||||||
|  |         "Store change to a keybinding." | ||||||
|  |         value = self.keybinding.get() | ||||||
|  |         key_set = self.custom_name.get() | ||||||
|  |         event = self.bindingslist.get(ANCHOR).split()[0] | ||||||
|  |         if idleConf.IsCoreBinding(event): | ||||||
|  |             changes.add_option('keys', key_set, event, value) | ||||||
|  |         else:  # Event is an extension binding. | ||||||
|  |             ext_name = idleConf.GetExtnNameForEvent(event) | ||||||
|  |             ext_keybind_section = ext_name + '_cfgBindings' | ||||||
|  |             changes.add_option('extensions', ext_keybind_section, event, value) | ||||||
|  | 
 | ||||||
|  |     def set_keys_type(self): | ||||||
|  |         "Set available screen options based on builtin or custom key set." | ||||||
|  |         if self.keyset_source.get(): | ||||||
|  |             self.builtinlist['state'] = NORMAL | ||||||
|  |             self.customlist['state'] = DISABLED | ||||||
|  |             self.button_delete_custom_keys['state'] = DISABLED | ||||||
|  |         else: | ||||||
|  |             self.builtinlist['state'] = DISABLED | ||||||
|  |             self.custom_keyset_on['state'] = NORMAL | ||||||
|  |             self.customlist['state'] = NORMAL | ||||||
|  |             self.button_delete_custom_keys['state'] = NORMAL | ||||||
|  | 
 | ||||||
|  |     def get_new_keys(self): | ||||||
|  |         """Handle event to change key binding for selected line. | ||||||
|  | 
 | ||||||
|  |         A selection of a key/binding in the list of current | ||||||
|  |         bindings pops up a dialog to enter a new binding.  If | ||||||
|  |         the current key set is builtin and a binding has | ||||||
|  |         changed, then a name for a custom key set needs to be | ||||||
|  |         entered for the change to be applied. | ||||||
|  |         """ | ||||||
|  |         list_index = self.bindingslist.index(ANCHOR) | ||||||
|  |         binding = self.bindingslist.get(list_index) | ||||||
|  |         bind_name = binding.split()[0] | ||||||
|  |         if self.keyset_source.get(): | ||||||
|  |             current_key_set_name = self.builtin_name.get() | ||||||
|  |         else: | ||||||
|  |             current_key_set_name = self.custom_name.get() | ||||||
|  |         current_bindings = idleConf.GetCurrentKeySet() | ||||||
|  |         if current_key_set_name in changes['keys']:  # unsaved changes | ||||||
|  |             key_set_changes = changes['keys'][current_key_set_name] | ||||||
|  |             for event in key_set_changes: | ||||||
|  |                 current_bindings[event] = key_set_changes[event].split() | ||||||
|  |         current_key_sequences = list(current_bindings.values()) | ||||||
|  |         new_keys = GetKeysDialog(self, 'Get New Keys', bind_name, | ||||||
|  |                 current_key_sequences).result | ||||||
|  |         if new_keys: | ||||||
|  |             if self.keyset_source.get():  # Current key set is a built-in. | ||||||
|  |                 message = ('Your changes will be saved as a new Custom Key Set.' | ||||||
|  |                            ' Enter a name for your new Custom Key Set below.') | ||||||
|  |                 new_keyset = self.get_new_keys_name(message) | ||||||
|  |                 if not new_keyset:  # User cancelled custom key set creation. | ||||||
|  |                     self.bindingslist.select_set(list_index) | ||||||
|  |                     self.bindingslist.select_anchor(list_index) | ||||||
|  |                     return | ||||||
|  |                 else:  # Create new custom key set based on previously active key set. | ||||||
|  |                     self.create_new_key_set(new_keyset) | ||||||
|  |             self.bindingslist.delete(list_index) | ||||||
|  |             self.bindingslist.insert(list_index, bind_name+' - '+new_keys) | ||||||
|  |             self.bindingslist.select_set(list_index) | ||||||
|  |             self.bindingslist.select_anchor(list_index) | ||||||
|  |             self.keybinding.set(new_keys) | ||||||
|  |         else: | ||||||
|  |             self.bindingslist.select_set(list_index) | ||||||
|  |             self.bindingslist.select_anchor(list_index) | ||||||
|  | 
 | ||||||
|  |     def get_new_keys_name(self, message): | ||||||
|  |         "Return new key set name from query popup." | ||||||
|  |         used_names = (idleConf.GetSectionList('user', 'keys') + | ||||||
|  |                 idleConf.GetSectionList('default', 'keys')) | ||||||
|  |         new_keyset = SectionName( | ||||||
|  |                 self, 'New Custom Key Set', message, used_names).result | ||||||
|  |         return new_keyset | ||||||
|  | 
 | ||||||
|  |     def save_as_new_key_set(self): | ||||||
|  |         "Prompt for name of new key set and save changes using that name." | ||||||
|  |         new_keys_name = self.get_new_keys_name('New Key Set Name:') | ||||||
|  |         if new_keys_name: | ||||||
|  |             self.create_new_key_set(new_keys_name) | ||||||
|  | 
 | ||||||
|  |     def on_bindingslist_select(self, event): | ||||||
|  |         "Activate button to assign new keys to selected action." | ||||||
|  |         self.button_new_keys['state'] = NORMAL | ||||||
|  | 
 | ||||||
|  |     def create_new_key_set(self, new_key_set_name): | ||||||
|  |         """Create a new custom key set with the given name. | ||||||
|  | 
 | ||||||
|  |         Copy the bindings/keys from the previously active keyset | ||||||
|  |         to the new keyset and activate the new custom keyset. | ||||||
|  |         """ | ||||||
|  |         if self.keyset_source.get(): | ||||||
|  |             prev_key_set_name = self.builtin_name.get() | ||||||
|  |         else: | ||||||
|  |             prev_key_set_name = self.custom_name.get() | ||||||
|  |         prev_keys = idleConf.GetCoreKeys(prev_key_set_name) | ||||||
|  |         new_keys = {} | ||||||
|  |         for event in prev_keys:  # Add key set to changed items. | ||||||
|  |             event_name = event[2:-2]  # Trim off the angle brackets. | ||||||
|  |             binding = ' '.join(prev_keys[event]) | ||||||
|  |             new_keys[event_name] = binding | ||||||
|  |         # Handle any unsaved changes to prev key set. | ||||||
|  |         if prev_key_set_name in changes['keys']: | ||||||
|  |             key_set_changes = changes['keys'][prev_key_set_name] | ||||||
|  |             for event in key_set_changes: | ||||||
|  |                 new_keys[event] = key_set_changes[event] | ||||||
|  |         # Save the new key set. | ||||||
|  |         self.save_new_key_set(new_key_set_name, new_keys) | ||||||
|  |         # Change GUI over to the new key set. | ||||||
|  |         custom_key_list = idleConf.GetSectionList('user', 'keys') | ||||||
|  |         custom_key_list.sort() | ||||||
|  |         self.customlist.SetMenu(custom_key_list, new_key_set_name) | ||||||
|  |         self.keyset_source.set(0) | ||||||
|  |         self.set_keys_type() | ||||||
|  | 
 | ||||||
|  |     def load_keys_list(self, keyset_name): | ||||||
|  |         """Reload the list of action/key binding pairs for the active key set. | ||||||
|  | 
 | ||||||
|  |         An action/key binding can be selected to change the key binding. | ||||||
|  |         """ | ||||||
|  |         reselect = False | ||||||
|  |         if self.bindingslist.curselection(): | ||||||
|  |             reselect = True | ||||||
|  |             list_index = self.bindingslist.index(ANCHOR) | ||||||
|  |         keyset = idleConf.GetKeySet(keyset_name) | ||||||
|  |         bind_names = list(keyset.keys()) | ||||||
|  |         bind_names.sort() | ||||||
|  |         self.bindingslist.delete(0, END) | ||||||
|  |         for bind_name in bind_names: | ||||||
|  |             key = ' '.join(keyset[bind_name]) | ||||||
|  |             bind_name = bind_name[2:-2]  # Trim off the angle brackets. | ||||||
|  |             if keyset_name in changes['keys']: | ||||||
|  |                 # Handle any unsaved changes to this key set. | ||||||
|  |                 if bind_name in changes['keys'][keyset_name]: | ||||||
|  |                     key = changes['keys'][keyset_name][bind_name] | ||||||
|  |             self.bindingslist.insert(END, bind_name+' - '+key) | ||||||
|  |         if reselect: | ||||||
|  |             self.bindingslist.see(list_index) | ||||||
|  |             self.bindingslist.select_set(list_index) | ||||||
|  |             self.bindingslist.select_anchor(list_index) | ||||||
|  | 
 | ||||||
|  |     @staticmethod | ||||||
|  |     def save_new_key_set(keyset_name, keyset): | ||||||
|  |         """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 - dictionary containing the new keybindings | ||||||
|  |         """ | ||||||
|  |         if not idleConf.userCfg['keys'].has_section(keyset_name): | ||||||
|  |             idleConf.userCfg['keys'].add_section(keyset_name) | ||||||
|  |         for event in keyset: | ||||||
|  |             value = keyset[event] | ||||||
|  |             idleConf.userCfg['keys'].SetOption(keyset_name, event, value) | ||||||
|  | 
 | ||||||
|  |     def delete_custom_keys(self): | ||||||
|  |         """Handle event to delete a custom key set. | ||||||
|  | 
 | ||||||
|  |         Applying the delete deactivates the current configuration and | ||||||
|  |         reverts to the default.  The custom key set is permanently | ||||||
|  |         deleted from the config file. | ||||||
|  |         """ | ||||||
|  |         keyset_name = self.custom_name.get() | ||||||
|  |         delmsg = 'Are you sure you wish to delete the key set %r ?' | ||||||
|  |         if not tkMessageBox.askyesno( | ||||||
|  |                 'Delete Key Set',  delmsg % keyset_name, parent=self): | ||||||
|  |             return | ||||||
|  |         self.cd.deactivate_current_config() | ||||||
|  |         # Remove key set from changes, config, and file. | ||||||
|  |         changes.delete_section('keys', keyset_name) | ||||||
|  |         # Reload user key set list. | ||||||
|  |         item_list = idleConf.GetSectionList('user', 'keys') | ||||||
|  |         item_list.sort() | ||||||
|  |         if not item_list: | ||||||
|  |             self.custom_keyset_on['state'] = DISABLED | ||||||
|  |             self.customlist.SetMenu(item_list, '- no custom keys -') | ||||||
|  |         else: | ||||||
|  |             self.customlist.SetMenu(item_list, item_list[0]) | ||||||
|  |         # Revert to default key set. | ||||||
|  |         self.keyset_source.set(idleConf.defaultCfg['main'] | ||||||
|  |                                 .Get('Keys', 'default')) | ||||||
|  |         self.builtin_name.set(idleConf.defaultCfg['main'].Get('Keys', 'name') | ||||||
|  |                              or idleConf.default_keys()) | ||||||
|  |         # User can't back out of these changes, they must be applied now. | ||||||
|  |         changes.save_all() | ||||||
|  |         self.cd.save_all_changed_extensions() | ||||||
|  |         self.cd.activate_config_changes() | ||||||
|  |         self.set_keys_type() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class GenPage(Frame): | class GenPage(Frame): | ||||||
| 
 | 
 | ||||||
|     def __init__(self, master): |     def __init__(self, master): | ||||||
|  |  | ||||||
|  | @ -232,22 +232,27 @@ def setUp(self): | ||||||
|         changes.clear() |         changes.clear() | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class KeyTest(unittest.TestCase): | class KeysPageTest(unittest.TestCase): | ||||||
|  |     """Test that keys tab widgets enable users to make changes. | ||||||
|  | 
 | ||||||
|  |     Test that widget actions set vars, that var changes add | ||||||
|  |     options to changes and that key sets works correctly. | ||||||
|  |     """ | ||||||
| 
 | 
 | ||||||
|     @classmethod |     @classmethod | ||||||
|     def setUpClass(cls): |     def setUpClass(cls): | ||||||
|         d = dialog |         page = cls.page = dialog.keyspage | ||||||
|         dialog.note.select(d.keyspage) |         dialog.note.select(page) | ||||||
|         d.set_keys_type = Func() |         page.set_keys_type = Func() | ||||||
|         d.load_keys_list = Func() |         page.load_keys_list = Func() | ||||||
| 
 | 
 | ||||||
|     @classmethod |     @classmethod | ||||||
|     def tearDownClass(cls): |     def tearDownClass(cls): | ||||||
|         d = dialog |         page = cls.page | ||||||
|         del d.set_keys_type, d.load_keys_list |         del page.set_keys_type, page.load_keys_list | ||||||
| 
 | 
 | ||||||
|     def setUp(self): |     def setUp(self): | ||||||
|         d = dialog |         d = self.page | ||||||
|         # The following is needed for test_load_key_cfg, _delete_custom_keys. |         # The following is needed for test_load_key_cfg, _delete_custom_keys. | ||||||
|         # This may indicate a defect in some test or function. |         # This may indicate a defect in some test or function. | ||||||
|         for section in idleConf.GetSectionList('user', 'keys'): |         for section in idleConf.GetSectionList('user', 'keys'): | ||||||
|  | @ -258,7 +263,7 @@ def setUp(self): | ||||||
| 
 | 
 | ||||||
|     def test_load_key_cfg(self): |     def test_load_key_cfg(self): | ||||||
|         tracers.detach() |         tracers.detach() | ||||||
|         d = dialog |         d = self.page | ||||||
|         eq = self.assertEqual |         eq = self.assertEqual | ||||||
| 
 | 
 | ||||||
|         # Use builtin keyset with no user keysets created. |         # Use builtin keyset with no user keysets created. | ||||||
|  | @ -300,7 +305,7 @@ def test_load_key_cfg(self): | ||||||
| 
 | 
 | ||||||
|     def test_keyset_source(self): |     def test_keyset_source(self): | ||||||
|         eq = self.assertEqual |         eq = self.assertEqual | ||||||
|         d = dialog |         d = self.page | ||||||
|         # Test these separately. |         # Test these separately. | ||||||
|         d.var_changed_builtin_name = Func() |         d.var_changed_builtin_name = Func() | ||||||
|         d.var_changed_custom_name = Func() |         d.var_changed_custom_name = Func() | ||||||
|  | @ -321,7 +326,7 @@ def test_keyset_source(self): | ||||||
| 
 | 
 | ||||||
|     def test_builtin_name(self): |     def test_builtin_name(self): | ||||||
|         eq = self.assertEqual |         eq = self.assertEqual | ||||||
|         d = dialog |         d = self.page | ||||||
|         idleConf.userCfg['main'].remove_section('Keys') |         idleConf.userCfg['main'].remove_section('Keys') | ||||||
|         item_list = ['IDLE Classic Windows', 'IDLE Classic OSX', |         item_list = ['IDLE Classic Windows', 'IDLE Classic OSX', | ||||||
|                      'IDLE Modern UNIX'] |                      'IDLE Modern UNIX'] | ||||||
|  | @ -352,7 +357,7 @@ def test_builtin_name(self): | ||||||
|         eq(d.load_keys_list.args, ('IDLE Classic OSX', )) |         eq(d.load_keys_list.args, ('IDLE Classic OSX', )) | ||||||
| 
 | 
 | ||||||
|     def test_custom_name(self): |     def test_custom_name(self): | ||||||
|         d = dialog |         d = self.page | ||||||
| 
 | 
 | ||||||
|         # If no selections, doesn't get added. |         # If no selections, doesn't get added. | ||||||
|         d.customlist.SetMenu([], '- no custom keys -') |         d.customlist.SetMenu([], '- no custom keys -') | ||||||
|  | @ -366,7 +371,7 @@ def test_custom_name(self): | ||||||
|         self.assertEqual(d.load_keys_list.called, 1) |         self.assertEqual(d.load_keys_list.called, 1) | ||||||
| 
 | 
 | ||||||
|     def test_keybinding(self): |     def test_keybinding(self): | ||||||
|         d = dialog |         d = self.page | ||||||
|         d.custom_name.set('my custom keys') |         d.custom_name.set('my custom keys') | ||||||
|         d.bindingslist.delete(0, 'end') |         d.bindingslist.delete(0, 'end') | ||||||
|         d.bindingslist.insert(0, 'copy') |         d.bindingslist.insert(0, 'copy') | ||||||
|  | @ -386,7 +391,7 @@ def test_keybinding(self): | ||||||
| 
 | 
 | ||||||
|     def test_set_keys_type(self): |     def test_set_keys_type(self): | ||||||
|         eq = self.assertEqual |         eq = self.assertEqual | ||||||
|         d = dialog |         d = self.page | ||||||
|         del d.set_keys_type |         del d.set_keys_type | ||||||
| 
 | 
 | ||||||
|         # Builtin keyset selected. |         # Builtin keyset selected. | ||||||
|  | @ -407,7 +412,7 @@ def test_set_keys_type(self): | ||||||
| 
 | 
 | ||||||
|     def test_get_new_keys(self): |     def test_get_new_keys(self): | ||||||
|         eq = self.assertEqual |         eq = self.assertEqual | ||||||
|         d = dialog |         d = self.page | ||||||
|         orig_getkeysdialog = configdialog.GetKeysDialog |         orig_getkeysdialog = configdialog.GetKeysDialog | ||||||
|         gkd = configdialog.GetKeysDialog = Func(return_self=True) |         gkd = configdialog.GetKeysDialog = Func(return_self=True) | ||||||
|         gnkn = d.get_new_keys_name = Func() |         gnkn = d.get_new_keys_name = Func() | ||||||
|  | @ -456,7 +461,7 @@ def test_get_new_keys(self): | ||||||
|     def test_get_new_keys_name(self): |     def test_get_new_keys_name(self): | ||||||
|         orig_sectionname = configdialog.SectionName |         orig_sectionname = configdialog.SectionName | ||||||
|         sn = configdialog.SectionName = Func(return_self=True) |         sn = configdialog.SectionName = Func(return_self=True) | ||||||
|         d = dialog |         d = self.page | ||||||
| 
 | 
 | ||||||
|         sn.result = 'New Keys' |         sn.result = 'New Keys' | ||||||
|         self.assertEqual(d.get_new_keys_name(''), 'New Keys') |         self.assertEqual(d.get_new_keys_name(''), 'New Keys') | ||||||
|  | @ -464,7 +469,7 @@ def test_get_new_keys_name(self): | ||||||
|         configdialog.SectionName = orig_sectionname |         configdialog.SectionName = orig_sectionname | ||||||
| 
 | 
 | ||||||
|     def test_save_as_new_key_set(self): |     def test_save_as_new_key_set(self): | ||||||
|         d = dialog |         d = self.page | ||||||
|         gnkn = d.get_new_keys_name = Func() |         gnkn = d.get_new_keys_name = Func() | ||||||
|         d.keyset_source.set(True) |         d.keyset_source.set(True) | ||||||
| 
 | 
 | ||||||
|  | @ -482,7 +487,7 @@ def test_save_as_new_key_set(self): | ||||||
|         del d.get_new_keys_name |         del d.get_new_keys_name | ||||||
| 
 | 
 | ||||||
|     def test_on_bindingslist_select(self): |     def test_on_bindingslist_select(self): | ||||||
|         d = dialog |         d = self.page | ||||||
|         b = d.bindingslist |         b = d.bindingslist | ||||||
|         b.delete(0, 'end') |         b.delete(0, 'end') | ||||||
|         b.insert(0, 'copy') |         b.insert(0, 'copy') | ||||||
|  | @ -504,7 +509,7 @@ def test_on_bindingslist_select(self): | ||||||
| 
 | 
 | ||||||
|     def test_create_new_key_set_and_save_new_key_set(self): |     def test_create_new_key_set_and_save_new_key_set(self): | ||||||
|         eq = self.assertEqual |         eq = self.assertEqual | ||||||
|         d = dialog |         d = self.page | ||||||
| 
 | 
 | ||||||
|         # Use default as previously active keyset. |         # Use default as previously active keyset. | ||||||
|         d.keyset_source.set(True) |         d.keyset_source.set(True) | ||||||
|  | @ -535,7 +540,7 @@ def test_create_new_key_set_and_save_new_key_set(self): | ||||||
| 
 | 
 | ||||||
|     def test_load_keys_list(self): |     def test_load_keys_list(self): | ||||||
|         eq = self.assertEqual |         eq = self.assertEqual | ||||||
|         d = dialog |         d = self.page | ||||||
|         gks = idleConf.GetKeySet = Func() |         gks = idleConf.GetKeySet = Func() | ||||||
|         del d.load_keys_list |         del d.load_keys_list | ||||||
|         b = d.bindingslist |         b = d.bindingslist | ||||||
|  | @ -578,11 +583,11 @@ def test_load_keys_list(self): | ||||||
| 
 | 
 | ||||||
|     def test_delete_custom_keys(self): |     def test_delete_custom_keys(self): | ||||||
|         eq = self.assertEqual |         eq = self.assertEqual | ||||||
|         d = dialog |         d = self.page | ||||||
|         d.button_delete_custom_keys['state'] = NORMAL |         d.button_delete_custom_keys['state'] = NORMAL | ||||||
|         yesno = configdialog.tkMessageBox.askyesno = Func() |         yesno = configdialog.tkMessageBox.askyesno = Func() | ||||||
|         d.deactivate_current_config = Func() |         dialog.deactivate_current_config = Func() | ||||||
|         d.activate_config_changes = Func() |         dialog.activate_config_changes = Func() | ||||||
| 
 | 
 | ||||||
|         keyset_name = 'spam key set' |         keyset_name = 'spam key set' | ||||||
|         idleConf.userCfg['keys'].SetOption(keyset_name, 'name', 'value') |         idleConf.userCfg['keys'].SetOption(keyset_name, 'name', 'value') | ||||||
|  | @ -598,8 +603,8 @@ def test_delete_custom_keys(self): | ||||||
|         eq(yesno.called, 1) |         eq(yesno.called, 1) | ||||||
|         eq(keyspage[keyset_name], {'option': 'True'}) |         eq(keyspage[keyset_name], {'option': 'True'}) | ||||||
|         eq(idleConf.GetSectionList('user', 'keys'), ['spam key set']) |         eq(idleConf.GetSectionList('user', 'keys'), ['spam key set']) | ||||||
|         eq(d.deactivate_current_config.called, 0) |         eq(dialog.deactivate_current_config.called, 0) | ||||||
|         eq(d.activate_config_changes.called, 0) |         eq(dialog.activate_config_changes.called, 0) | ||||||
|         eq(d.set_keys_type.called, 0) |         eq(d.set_keys_type.called, 0) | ||||||
| 
 | 
 | ||||||
|         # Confirm deletion. |         # Confirm deletion. | ||||||
|  | @ -610,11 +615,11 @@ def test_delete_custom_keys(self): | ||||||
|         eq(idleConf.GetSectionList('user', 'keys'), []) |         eq(idleConf.GetSectionList('user', 'keys'), []) | ||||||
|         eq(d.custom_keyset_on['state'], DISABLED) |         eq(d.custom_keyset_on['state'], DISABLED) | ||||||
|         eq(d.custom_name.get(), '- no custom keys -') |         eq(d.custom_name.get(), '- no custom keys -') | ||||||
|         eq(d.deactivate_current_config.called, 1) |         eq(dialog.deactivate_current_config.called, 1) | ||||||
|         eq(d.activate_config_changes.called, 1) |         eq(dialog.activate_config_changes.called, 1) | ||||||
|         eq(d.set_keys_type.called, 1) |         eq(d.set_keys_type.called, 1) | ||||||
| 
 | 
 | ||||||
|         del d.activate_config_changes, d.deactivate_current_config |         del dialog.activate_config_changes, dialog.deactivate_current_config | ||||||
|         del configdialog.tkMessageBox.askyesno |         del configdialog.tkMessageBox.askyesno | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,2 @@ | ||||||
|  | IDLE: Factor KeysPage(Frame) class from ConfigDialog.  The slightly | ||||||
|  | modified tests continue to pass.  Patch by Cheryl Sabella. | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Cheryl Sabella
						Cheryl Sabella