diff options
-rw-r--r-- | Lib/idlelib/configdialog.py | 1540 | ||||
-rw-r--r-- | Misc/NEWS.d/next/IDLE/2017-07-27-14-48-42.bpo-31060.GdY_VY.rst | 3 |
2 files changed, 775 insertions, 768 deletions
diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index daaa344..e359ec2 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -30,7 +30,6 @@ from idlelib.textview import view_text changes = ConfigChanges() - class ConfigDialog(Toplevel): """Config dialog for IDLE. """ @@ -112,6 +111,58 @@ class ConfigDialog(Toplevel): self.create_page_extensions() self.create_action_buttons().pack(side=BOTTOM) + def load_configs(self): + """Load configuration for each page. + + Load configuration from default and user config files and populate + the widgets on the config dialog pages. + + Methods: + load_font_cfg + load_tab_cfg + load_theme_cfg + load_key_cfg + load_general_cfg + """ + self.load_font_cfg() + self.load_tab_cfg() + self.load_theme_cfg() + self.load_key_cfg() + self.load_general_cfg() + # note: extension page handled separately + + def attach_var_callbacks(self): + "Attach callbacks to variables that can be changed." + self.font_size.trace_add('write', self.var_changed_font) + self.font_name.trace_add('write', self.var_changed_font) + self.font_bold.trace_add('write', self.var_changed_font) + self.space_num.trace_add('write', self.var_changed_space_num) + self.color.trace_add('write', self.var_changed_color) + self.builtin_theme.trace_add('write', self.var_changed_builtin_theme) + self.custom_theme.trace_add('write', self.var_changed_custom_theme) + self.is_builtin_theme.trace_add('write', self.var_changed_is_builtin_theme) + self.highlight_target.trace_add('write', self.var_changed_highlight_target) + self.keybinding.trace_add('write', self.var_changed_keybinding) + self.builtin_keys.trace_add('write', self.var_changed_builtin_keys) + self.custom_keys.trace_add('write', self.var_changed_custom_keys) + self.are_keys_builtin.trace_add('write', self.var_changed_are_keys_builtin) + self.win_width.trace_add('write', self.var_changed_win_width) + self.win_height.trace_add('write', self.var_changed_win_height) + self.startup_edit.trace_add('write', self.var_changed_startup_edit) + self.autosave.trace_add('write', self.var_changed_autosave) + + def remove_var_callbacks(self): + "Remove callbacks to prevent memory leaks." + for var in ( + self.font_size, self.font_name, self.font_bold, + self.space_num, self.color, self.builtin_theme, + self.custom_theme, self.is_builtin_theme, self.highlight_target, + self.keybinding, self.builtin_keys, self.custom_keys, + self.are_keys_builtin, self.win_width, self.win_height, + self.startup_edit, self.autosave,): + var.trace_remove('write', var.trace_info()[0][1]) + + def create_action_buttons(self): """Return frame of action buttons for dialog. @@ -150,6 +201,50 @@ class ConfigDialog(Toplevel): buttons.pack(side=BOTTOM) return outer + def ok(self): + """Apply config changes, then dismiss dialog. + + Methods: + apply + destroy: inherited + """ + self.apply() + self.destroy() + + def apply(self): + """Apply config changes and leave dialog open. + + Methods: + deactivate_current_config + save_all_changed_extensions + activate_config_changes + """ + self.deactivate_current_config() + changes.save_all() + self.save_all_changed_extensions() + self.activate_config_changes() + + def cancel(self): + """Dismiss config dialog. + + Methods: + destroy: inherited + """ + self.destroy() + + def help(self): + """Create textview for config dialog help. + + Attrbutes accessed: + tab_pages + + Methods: + view_text: Method from textview module. + """ + page = self.tab_pages._current_page + view_text(self, title='Help for IDLE preferences', + text=help_common+help_pages.get(page, '')) + def create_page_font_tab(self): """Return frame of widgets for Font/Tabs tab. @@ -299,16 +394,6 @@ class ConfigDialog(Toplevel): # Set font weight. self.font_bold.set(font_bold) - def on_fontlist_select(self, event): - """Handle selecting a font from the list. - - Event can result from either mouse click or Up or Down key. - Set font_name and example displays to selection. - """ - font = self.fontlist.get( - ACTIVE if event.type.name == 'KeyRelease' else ANCHOR) - self.font_name.set(font.lower()) - def var_changed_font(self, *params): """Store changes to font attributes. @@ -324,6 +409,16 @@ class ConfigDialog(Toplevel): changes.add_option('main', 'EditorWindow', 'font-bold', value) self.set_samples() + def on_fontlist_select(self, event): + """Handle selecting a font from the list. + + Event can result from either mouse click or Up or Down key. + Set font_name and example displays to selection. + """ + font = self.fontlist.get( + ACTIVE if event.type.name == 'KeyRelease' else ANCHOR) + self.font_name.set(font.lower()) + def set_samples(self, event=None): """Update update both screen samples with the font settings. @@ -531,406 +626,57 @@ class ConfigDialog(Toplevel): self.new_custom_theme.pack(side=TOP, fill=X, pady=5) return frame - def create_page_keys(self): - """Return frame of widgets for Keys tab. - - 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. - - Methods: - load_key_config: Set table. - 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. - set_keys_type: Command for are_keys_builtin. - 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). - deactivate_current_config: Remove keys bindings in editors. - - Widget Structure: (*) widgets bound to self - frame - frame_custom: LabelFrame - frame_target: Frame - target_title: Label - scroll_target_y: Scrollbar - scroll_target_x: Scrollbar - (*)list_bindings: ListBox - (*)button_new_keys: Button - 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 - self.builtin_keys = StringVar(parent) - self.custom_keys = StringVar(parent) - self.are_keys_builtin = BooleanVar(parent) - self.keybinding = StringVar(parent) - - ##widget creation - #body frame - frame = self.tab_pages.pages['Keys'].frame - #body section frames - 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.list_bindings = Listbox( - frame_target, takefocus=FALSE, exportselection=FALSE) - self.list_bindings.bind('<ButtonRelease-1>', self.keybinding_selected) - scroll_target_y.config(command=self.list_bindings.yview) - scroll_target_x.config(command=self.list_bindings.xview) - self.list_bindings.config(yscrollcommand=scroll_target_y.set) - self.list_bindings.config(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.radio_keys_builtin = Radiobutton( - frames[0], variable=self.are_keys_builtin, value=1, - command=self.set_keys_type, text='Use a Built-in Key Set') - self.radio_keys_custom = Radiobutton( - frames[0], variable=self.are_keys_builtin, value=0, - command=self.set_keys_type, text='Use a Custom Key Set') - self.opt_menu_keys_builtin = DynOptionMenu( - frames[0], self.builtin_keys, None, command=None) - self.opt_menu_keys_custom = DynOptionMenu( - frames[0], self.custom_keys, None, command=None) - self.button_delete_custom_keys = Button( - frames[1], text='Delete Custom Key Set', - command=self.delete_custom_keys) - button_save_custom_keys = Button( - frames[1], text='Save as New Custom Key Set', - command=self.save_as_new_key_set) - self.new_custom_keys = 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.list_bindings.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.radio_keys_builtin.grid(row=0, column=0, sticky=W+NS) - self.radio_keys_custom.grid(row=1, column=0, sticky=W+NS) - self.opt_menu_keys_builtin.grid(row=0, column=1, sticky=NSEW) - self.opt_menu_keys_custom.grid(row=1, column=1, sticky=NSEW) - self.new_custom_keys.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) - 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 create_page_general(self): - """Return frame of widgets for General tab. - - Enable users to provisionally change general options. Function - load_general_cfg intializes tk variables and helplist using - idleConf. Radiobuttons startup_shell_on and startup_editor_on - set var startup_edit. Radiobuttons save_ask_on and save_auto_on - set var autosave. Entry boxes win_width_int and win_height_int - set var win_width and win_height. Setting var_name invokes the - var_changed_var_name callback that adds option to changes. - - Helplist: load_general_cfg loads list user_helplist with - name, position pairs and copies names to listbox helplist. - Clicking a name invokes help_source selected. Clicking - button_helplist_name invokes helplist_item_name, which also - changes user_helplist. These functions all call - set_add_delete_state. All but load call update_help_changes to - rewrite changes['main']['HelpFiles']. - - Widget Structure: (*) widgets bound to self - frame - frame_run: LabelFrame - startup_title: Label - (*)startup_editor_on: Radiobutton - startup_edit - (*)startup_shell_on: Radiobutton - startup_edit - frame_save: LabelFrame - run_save_title: Label - (*)save_ask_on: Radiobutton - autosave - (*)save_auto_on: Radiobutton - autosave - frame_win_size: LabelFrame - win_size_title: Label - win_width_title: Label - (*)win_width_int: Entry - win_width - win_height_title: Label - (*)win_height_int: Entry - win_height - frame_help: LabelFrame - frame_helplist: Frame - frame_helplist_buttons: Frame - (*)button_helplist_edit - (*)button_helplist_add - (*)button_helplist_remove - (*)helplist: ListBox - scroll_helplist: Scrollbar - """ - parent = self.parent - self.startup_edit = IntVar(parent) - self.autosave = IntVar(parent) - self.win_width = StringVar(parent) - self.win_height = StringVar(parent) - - # Create widgets: - # body. - frame = self.tab_pages.pages['General'].frame - # body section frames. - frame_run = LabelFrame(frame, borderwidth=2, relief=GROOVE, - text=' Startup Preferences ') - frame_save = LabelFrame(frame, borderwidth=2, relief=GROOVE, - text=' autosave Preferences ') - frame_win_size = Frame(frame, borderwidth=2, relief=GROOVE) - frame_help = LabelFrame(frame, borderwidth=2, relief=GROOVE, - text=' Additional Help Sources ') - # frame_run. - startup_title = Label(frame_run, text='At Startup') - self.startup_editor_on = Radiobutton( - frame_run, variable=self.startup_edit, value=1, - text="Open Edit Window") - self.startup_shell_on = Radiobutton( - frame_run, variable=self.startup_edit, value=0, - text='Open Shell Window') - # frame_save. - run_save_title = Label(frame_save, text='At Start of Run (F5) ') - self.save_ask_on = Radiobutton( - frame_save, variable=self.autosave, value=0, - text="Prompt to Save") - self.save_auto_on = Radiobutton( - frame_save, variable=self.autosave, value=1, - text='No Prompt') - # frame_win_size. - win_size_title = Label( - frame_win_size, text='Initial Window Size (in characters)') - win_width_title = Label(frame_win_size, text='Width') - self.win_width_int = Entry( - frame_win_size, textvariable=self.win_width, width=3) - win_height_title = Label(frame_win_size, text='Height') - self.win_height_int = Entry( - frame_win_size, textvariable=self.win_height, width=3) - # frame_help. - frame_helplist = Frame(frame_help) - frame_helplist_buttons = Frame(frame_helplist) - self.helplist = Listbox( - frame_helplist, height=5, takefocus=FALSE, - exportselection=FALSE) - scroll_helplist = Scrollbar(frame_helplist) - scroll_helplist['command'] = self.helplist.yview - self.helplist['yscrollcommand'] = scroll_helplist.set - self.helplist.bind('<ButtonRelease-1>', self.help_source_selected) - self.button_helplist_edit = Button( - frame_helplist_buttons, text='Edit', state=DISABLED, - width=8, command=self.helplist_item_edit) - self.button_helplist_add = Button( - frame_helplist_buttons, text='Add', - width=8, command=self.helplist_item_add) - self.button_helplist_remove = Button( - frame_helplist_buttons, text='Remove', state=DISABLED, - width=8, command=self.helplist_item_remove) - - # Pack widgets: - # body. - frame_run.pack(side=TOP, padx=5, pady=5, fill=X) - frame_save.pack(side=TOP, padx=5, pady=5, fill=X) - frame_win_size.pack(side=TOP, padx=5, pady=5, fill=X) - frame_help.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) - # frame_run. - startup_title.pack(side=LEFT, anchor=W, padx=5, pady=5) - self.startup_shell_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) - self.startup_editor_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) - # frame_save. - run_save_title.pack(side=LEFT, anchor=W, padx=5, pady=5) - self.save_auto_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) - self.save_ask_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) - # frame_win_size. - win_size_title.pack(side=LEFT, anchor=W, padx=5, pady=5) - self.win_height_int.pack(side=RIGHT, anchor=E, padx=10, pady=5) - win_height_title.pack(side=RIGHT, anchor=E, pady=5) - self.win_width_int.pack(side=RIGHT, anchor=E, padx=10, pady=5) - win_width_title.pack(side=RIGHT, anchor=E, pady=5) - # frame_help. - frame_helplist_buttons.pack(side=RIGHT, padx=5, pady=5, fill=Y) - frame_helplist.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) - scroll_helplist.pack(side=RIGHT, anchor=W, fill=Y) - self.helplist.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH) - self.button_helplist_edit.pack(side=TOP, anchor=W, pady=5) - self.button_helplist_add.pack(side=TOP, anchor=W) - self.button_helplist_remove.pack(side=TOP, anchor=W, pady=5) - - return frame - - def load_general_cfg(self): - "Load current configuration settings for the general options." - # Set startup state. - self.startup_edit.set(idleConf.GetOption( - 'main', 'General', 'editor-on-startup', default=0, type='bool')) - # Set autosave state. - self.autosave.set(idleConf.GetOption( - 'main', 'General', 'autosave', default=0, type='bool')) - # Set initial window size. - self.win_width.set(idleConf.GetOption( - 'main', 'EditorWindow', 'width', type='int')) - self.win_height.set(idleConf.GetOption( - 'main', 'EditorWindow', 'height', type='int')) - # Set additional help sources. - self.user_helplist = idleConf.GetAllExtraHelpSourcesList() - self.helplist.delete(0, 'end') - for help_item in self.user_helplist: - self.helplist.insert(END, help_item[0]) - self.set_add_delete_state() - - def var_changed_startup_edit(self, *params): - "Store change to toggle for starting IDLE in the editor or shell." - value = self.startup_edit.get() - changes.add_option('main', 'General', 'editor-on-startup', value) - - def var_changed_autosave(self, *params): - "Store change to autosave." - value = self.autosave.get() - changes.add_option('main', 'General', 'autosave', value) - - def var_changed_win_width(self, *params): - "Store change to window width." - value = self.win_width.get() - changes.add_option('main', 'EditorWindow', 'width', value) - - def var_changed_win_height(self, *params): - "Store change to window height." - value = self.win_height.get() - changes.add_option('main', 'EditorWindow', 'height', value) - - def help_source_selected(self, event): - "Handle event for selecting additional help." - self.set_add_delete_state() - - def set_add_delete_state(self): - "Toggle the state for the help list buttons based on list entries." - if self.helplist.size() < 1: # No entries in list. - self.button_helplist_edit['state'] = DISABLED - self.button_helplist_remove['state'] = DISABLED - else: # Some entries. - if self.helplist.curselection(): # There currently is a selection. - self.button_helplist_edit['state'] = NORMAL - self.button_helplist_remove['state'] = NORMAL - else: # There currently is not a selection. - self.button_helplist_edit['state'] = DISABLED - self.button_helplist_remove['state'] = DISABLED - - def helplist_item_add(self): - """Handle add button for the help list. - - Query for name and location of new help sources and add - them to the list. - """ - help_source = HelpSource(self, 'New Help Source').result - if help_source: - self.user_helplist.append(help_source) - self.helplist.insert(END, help_source[0]) - self.update_help_changes() - - def helplist_item_edit(self): - """Handle edit button for the help list. + def load_theme_cfg(self): + """Load current configuration settings for the theme options. - Query with existing help source information and update - config if the values are changed. - """ - item_index = self.helplist.index(ANCHOR) - help_source = self.user_helplist[item_index] - new_help_source = HelpSource( - self, 'Edit Help Source', - menuitem=help_source[0], - filepath=help_source[1], - ).result - if new_help_source and new_help_source != help_source: - self.user_helplist[item_index] = new_help_source - self.helplist.delete(item_index) - self.helplist.insert(item_index, new_help_source[0]) - self.update_help_changes() - self.set_add_delete_state() # Selected will be un-selected + Based on the is_builtin_theme toggle, the theme is set as + either builtin or custom and the initial widget values + reflect the current settings from idleConf. - def helplist_item_remove(self): - """Handle remove button for the help list. + Attributes updated: + is_builtin_theme: Set from idleConf. + opt_menu_theme_builtin: List of default themes from idleConf. + opt_menu_theme_custom: List of custom themes from idleConf. + radio_theme_custom: Disabled if there are no custom themes. + custom_theme: Message with additional information. + opt_menu_highlight_target: Create menu from self.theme_elements. - Delete the help list item from config. + Methods: + set_theme_type + paint_theme_sample + set_highlight_target """ - item_index = self.helplist.index(ANCHOR) - del(self.user_helplist[item_index]) - self.helplist.delete(item_index) - self.update_help_changes() - self.set_add_delete_state() - - def update_help_changes(self): - "Clear and rebuild the HelpFiles section in changes" - changes['main']['HelpFiles'] = {} - for num in range(1, len(self.user_helplist) + 1): - changes.add_option( - 'main', 'HelpFiles', str(num), - ';'.join(self.user_helplist[num-1][:2])) - - - def attach_var_callbacks(self): - "Attach callbacks to variables that can be changed." - self.font_size.trace_add('write', self.var_changed_font) - self.font_name.trace_add('write', self.var_changed_font) - self.font_bold.trace_add('write', self.var_changed_font) - self.space_num.trace_add('write', self.var_changed_space_num) - self.color.trace_add('write', self.var_changed_color) - self.builtin_theme.trace_add('write', self.var_changed_builtin_theme) - self.custom_theme.trace_add('write', self.var_changed_custom_theme) - self.is_builtin_theme.trace_add('write', self.var_changed_is_builtin_theme) - self.highlight_target.trace_add('write', self.var_changed_highlight_target) - self.keybinding.trace_add('write', self.var_changed_keybinding) - self.builtin_keys.trace_add('write', self.var_changed_builtin_keys) - self.custom_keys.trace_add('write', self.var_changed_custom_keys) - self.are_keys_builtin.trace_add('write', self.var_changed_are_keys_builtin) - self.win_width.trace_add('write', self.var_changed_win_width) - self.win_height.trace_add('write', self.var_changed_win_height) - self.startup_edit.trace_add('write', self.var_changed_startup_edit) - self.autosave.trace_add('write', self.var_changed_autosave) - - def remove_var_callbacks(self): - "Remove callbacks to prevent memory leaks." - for var in ( - self.font_size, self.font_name, self.font_bold, - self.space_num, self.color, self.builtin_theme, - self.custom_theme, self.is_builtin_theme, self.highlight_target, - self.keybinding, self.builtin_keys, self.custom_keys, - self.are_keys_builtin, self.win_width, self.win_height, - self.startup_edit, self.autosave,): - var.trace_remove('write', var.trace_info()[0][1]) - - def var_changed_color(self, *params): - "Process change to color choice." - self.on_new_color_set() + # Set current theme type radiobutton. + self.is_builtin_theme.set(idleConf.GetOption( + 'main', 'Theme', 'default', type='bool', default=1)) + # Set current theme. + current_option = idleConf.CurrentTheme() + # Load available theme option menus. + if self.is_builtin_theme.get(): # Default theme selected. + item_list = idleConf.GetSectionList('default', 'highlight') + item_list.sort() + self.opt_menu_theme_builtin.SetMenu(item_list, current_option) + item_list = idleConf.GetSectionList('user', 'highlight') + item_list.sort() + if not item_list: + self.radio_theme_custom['state'] = DISABLED + self.custom_theme.set('- no custom themes -') + else: + self.opt_menu_theme_custom.SetMenu(item_list, item_list[0]) + else: # User theme selected. + item_list = idleConf.GetSectionList('user', 'highlight') + item_list.sort() + self.opt_menu_theme_custom.SetMenu(item_list, current_option) + item_list = idleConf.GetSectionList('default', 'highlight') + item_list.sort() + self.opt_menu_theme_builtin.SetMenu(item_list, item_list[0]) + self.set_theme_type() + # Load theme element option menu. + theme_names = list(self.theme_elements.keys()) + theme_names.sort(key=lambda x: self.theme_elements[x][1]) + self.opt_menu_highlight_target.SetMenu(theme_names, theme_names[0]) + self.paint_theme_sample() + self.set_highlight_target() def var_changed_builtin_theme(self, *params): """Process new builtin theme selection. @@ -976,59 +722,14 @@ class ConfigDialog(Toplevel): else: self.var_changed_custom_theme() + def var_changed_color(self, *params): + "Process change to color choice." + self.on_new_color_set() + def var_changed_highlight_target(self, *params): "Process selection of new target tag for highlighting." self.set_highlight_target() - def var_changed_keybinding(self, *params): - "Store change to a keybinding." - value = self.keybinding.get() - key_set = self.custom_keys.get() - event = self.list_bindings.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 var_changed_builtin_keys(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_keys.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.new_custom_keys.config(text='New key set, see Help', - fg='#500000') - else: - changes.add_option('main', 'Keys', 'name', value) - changes.add_option('main', 'Keys', 'name2', '') - self.new_custom_keys.config(text='', fg='black') - self.load_keys_list(value) - - def var_changed_custom_keys(self, *params): - "Process selection of custom key set." - value = self.custom_keys.get() - if value != '- no custom keys -': - changes.add_option('main', 'Keys', 'name', value) - self.load_keys_list(value) - - def var_changed_are_keys_builtin(self, *params): - "Process toggle between builtin key set and custom key set." - value = self.are_keys_builtin.get() - changes.add_option('main', 'Keys', 'default', value) - if value: - self.var_changed_builtin_keys() - else: - self.var_changed_custom_keys() - def set_theme_type(self): """Set available screen options based on builtin or custom theme. @@ -1057,218 +758,6 @@ class ConfigDialog(Toplevel): self.opt_menu_theme_custom['state'] = NORMAL self.button_delete_custom_theme['state'] = NORMAL - def set_keys_type(self): - "Set available screen options based on builtin or custom key set." - if self.are_keys_builtin.get(): - self.opt_menu_keys_builtin['state'] = NORMAL - self.opt_menu_keys_custom['state'] = DISABLED - self.button_delete_custom_keys['state'] = DISABLED - else: - self.opt_menu_keys_builtin['state'] = DISABLED - self.radio_keys_custom['state'] = NORMAL - self.opt_menu_keys_custom['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.list_bindings.index(ANCHOR) - binding = self.list_bindings.get(list_index) - bind_name = binding.split()[0] - if self.are_keys_builtin.get(): - current_key_set_name = self.builtin_keys.get() - else: - current_key_set_name = self.custom_keys.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.are_keys_builtin.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.list_bindings.select_set(list_index) - self.list_bindings.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.list_bindings.delete(list_index) - self.list_bindings.insert(list_index, bind_name+' - '+new_keys) - self.list_bindings.select_set(list_index) - self.list_bindings.select_anchor(list_index) - self.keybinding.set(new_keys) - else: - self.list_bindings.select_set(list_index) - self.list_bindings.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 keybinding_selected(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. - - Create the new key set based on the previously active set - with the current changes applied. Once it is saved, then - activate the new key set. - """ - if self.are_keys_builtin.get(): - prev_key_set_name = self.builtin_keys.get() - else: - prev_key_set_name = self.custom_keys.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.opt_menu_keys_custom.SetMenu(custom_key_list, new_key_set_name) - self.are_keys_builtin.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 = 0 - if self.list_bindings.curselection(): - reselect = 1 - list_index = self.list_bindings.index(ANCHOR) - keyset = idleConf.GetKeySet(keyset_name) - bind_names = list(keyset.keys()) - bind_names.sort() - self.list_bindings.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.list_bindings.insert(END, bind_name+' - '+key) - if reselect: - self.list_bindings.see(list_index) - self.list_bindings.select_set(list_index) - self.list_bindings.select_anchor(list_index) - - 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_keys.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.radio_keys_custom['state'] = DISABLED - self.opt_menu_keys_custom.SetMenu(item_list, '- no custom keys -') - else: - self.opt_menu_keys_custom.SetMenu(item_list, item_list[0]) - # Revert to default key set. - self.are_keys_builtin.set(idleConf.defaultCfg['main'] - .Get('Keys', 'default')) - self.builtin_keys.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 delete_custom_theme(self): - """Handle event to delete custom theme. - - The current theme is deactivated and the default theme is - activated. The custom theme is permanently removed from - the config file. - - Attributes accessed: - custom_theme - - Attributes updated: - radio_theme_custom - opt_menu_theme_custom - is_builtin_theme - builtin_theme - - Methods: - deactivate_current_config - save_all_changed_extensions - activate_config_changes - set_theme_type - """ - theme_name = self.custom_theme.get() - delmsg = 'Are you sure you wish to delete the theme %r ?' - if not tkMessageBox.askyesno( - 'Delete Theme', delmsg % theme_name, parent=self): - return - self.deactivate_current_config() - # Remove theme from changes, config, and file. - changes.delete_section('highlight', theme_name) - # Reload user theme list. - item_list = idleConf.GetSectionList('user', 'highlight') - item_list.sort() - if not item_list: - self.radio_theme_custom['state'] = DISABLED - self.opt_menu_theme_custom.SetMenu(item_list, '- no custom themes -') - else: - self.opt_menu_theme_custom.SetMenu(item_list, item_list[0]) - # Revert to default theme. - self.is_builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'default')) - self.builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'name')) - # 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_theme_type() - def get_color(self): """Handle button to select a new color for the target tag. @@ -1470,57 +959,184 @@ class ConfigDialog(Toplevel): self.highlight_sample.tag_config(element, **colors) self.set_color_sample() - def load_theme_cfg(self): - """Load current configuration settings for the theme options. + def save_new_theme(self, theme_name, theme): + """Save a newly created theme to idleConf. - Based on the is_builtin_theme toggle, the theme is set as - either builtin or custom and the initial widget values - reflect the current settings from idleConf. + theme_name - string, the name of the new theme + theme - dictionary containing the new theme + """ + if not idleConf.userCfg['highlight'].has_section(theme_name): + idleConf.userCfg['highlight'].add_section(theme_name) + for element in theme: + value = theme[element] + idleConf.userCfg['highlight'].SetOption(theme_name, element, value) + + def delete_custom_theme(self): + """Handle event to delete custom theme. + + The current theme is deactivated and the default theme is + activated. The custom theme is permanently removed from + the config file. + + Attributes accessed: + custom_theme Attributes updated: - is_builtin_theme: Set from idleConf. - opt_menu_theme_builtin: List of default themes from idleConf. - opt_menu_theme_custom: List of custom themes from idleConf. - radio_theme_custom: Disabled if there are no custom themes. - custom_theme: Message with additional information. - opt_menu_highlight_target: Create menu from self.theme_elements. + radio_theme_custom + opt_menu_theme_custom + is_builtin_theme + builtin_theme Methods: + deactivate_current_config + save_all_changed_extensions + activate_config_changes set_theme_type - paint_theme_sample - set_highlight_target """ - # Set current theme type radiobutton. - self.is_builtin_theme.set(idleConf.GetOption( - 'main', 'Theme', 'default', type='bool', default=1)) - # Set current theme. - current_option = idleConf.CurrentTheme() - # Load available theme option menus. - if self.is_builtin_theme.get(): # Default theme selected. - item_list = idleConf.GetSectionList('default', 'highlight') - item_list.sort() - self.opt_menu_theme_builtin.SetMenu(item_list, current_option) - item_list = idleConf.GetSectionList('user', 'highlight') - item_list.sort() - if not item_list: - self.radio_theme_custom['state'] = DISABLED - self.custom_theme.set('- no custom themes -') - else: - self.opt_menu_theme_custom.SetMenu(item_list, item_list[0]) - else: # User theme selected. - item_list = idleConf.GetSectionList('user', 'highlight') - item_list.sort() - self.opt_menu_theme_custom.SetMenu(item_list, current_option) - item_list = idleConf.GetSectionList('default', 'highlight') - item_list.sort() - self.opt_menu_theme_builtin.SetMenu(item_list, item_list[0]) + theme_name = self.custom_theme.get() + delmsg = 'Are you sure you wish to delete the theme %r ?' + if not tkMessageBox.askyesno( + 'Delete Theme', delmsg % theme_name, parent=self): + return + self.deactivate_current_config() + # Remove theme from changes, config, and file. + changes.delete_section('highlight', theme_name) + # Reload user theme list. + item_list = idleConf.GetSectionList('user', 'highlight') + item_list.sort() + if not item_list: + self.radio_theme_custom['state'] = DISABLED + self.opt_menu_theme_custom.SetMenu(item_list, '- no custom themes -') + else: + self.opt_menu_theme_custom.SetMenu(item_list, item_list[0]) + # Revert to default theme. + self.is_builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'default')) + self.builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'name')) + # 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_theme_type() - # Load theme element option menu. - theme_names = list(self.theme_elements.keys()) - theme_names.sort(key=lambda x: self.theme_elements[x][1]) - self.opt_menu_highlight_target.SetMenu(theme_names, theme_names[0]) - self.paint_theme_sample() - self.set_highlight_target() + + + def create_page_keys(self): + """Return frame of widgets for Keys tab. + + 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. + + Methods: + load_key_config: Set table. + 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. + set_keys_type: Command for are_keys_builtin. + 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). + deactivate_current_config: Remove keys bindings in editors. + + Widget Structure: (*) widgets bound to self + frame + frame_custom: LabelFrame + frame_target: Frame + target_title: Label + scroll_target_y: Scrollbar + scroll_target_x: Scrollbar + (*)list_bindings: ListBox + (*)button_new_keys: Button + 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 + self.builtin_keys = StringVar(parent) + self.custom_keys = StringVar(parent) + self.are_keys_builtin = BooleanVar(parent) + self.keybinding = StringVar(parent) + + ##widget creation + #body frame + frame = self.tab_pages.pages['Keys'].frame + #body section frames + 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.list_bindings = Listbox( + frame_target, takefocus=FALSE, exportselection=FALSE) + self.list_bindings.bind('<ButtonRelease-1>', self.keybinding_selected) + scroll_target_y.config(command=self.list_bindings.yview) + scroll_target_x.config(command=self.list_bindings.xview) + self.list_bindings.config(yscrollcommand=scroll_target_y.set) + self.list_bindings.config(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.radio_keys_builtin = Radiobutton( + frames[0], variable=self.are_keys_builtin, value=1, + command=self.set_keys_type, text='Use a Built-in Key Set') + self.radio_keys_custom = Radiobutton( + frames[0], variable=self.are_keys_builtin, value=0, + command=self.set_keys_type, text='Use a Custom Key Set') + self.opt_menu_keys_builtin = DynOptionMenu( + frames[0], self.builtin_keys, None, command=None) + self.opt_menu_keys_custom = DynOptionMenu( + frames[0], self.custom_keys, None, command=None) + self.button_delete_custom_keys = Button( + frames[1], text='Delete Custom Key Set', + command=self.delete_custom_keys) + button_save_custom_keys = Button( + frames[1], text='Save as New Custom Key Set', + command=self.save_as_new_key_set) + self.new_custom_keys = 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.list_bindings.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.radio_keys_builtin.grid(row=0, column=0, sticky=W+NS) + self.radio_keys_custom.grid(row=1, column=0, sticky=W+NS) + self.opt_menu_keys_builtin.grid(row=0, column=1, sticky=NSEW) + self.opt_menu_keys_custom.grid(row=1, column=1, sticky=NSEW) + self.new_custom_keys.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) + 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." @@ -1553,25 +1169,188 @@ class ConfigDialog(Toplevel): keyset_name = idleConf.CurrentKeys() self.load_keys_list(keyset_name) - def load_configs(self): - """Load configuration for each page. - Load configuration from default and user config files and populate - the widgets on the config dialog pages. - Methods: - load_font_cfg - load_tab_cfg - load_theme_cfg - load_key_cfg - load_general_cfg + + def var_changed_builtin_keys(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_keys.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.new_custom_keys.config(text='New key set, see Help', + fg='#500000') + else: + changes.add_option('main', 'Keys', 'name', value) + changes.add_option('main', 'Keys', 'name2', '') + self.new_custom_keys.config(text='', fg='black') + self.load_keys_list(value) + + def var_changed_custom_keys(self, *params): + "Process selection of custom key set." + value = self.custom_keys.get() + if value != '- no custom keys -': + changes.add_option('main', 'Keys', 'name', value) + self.load_keys_list(value) + + def var_changed_are_keys_builtin(self, *params): + "Process toggle between builtin key set and custom key set." + value = self.are_keys_builtin.get() + changes.add_option('main', 'Keys', 'default', value) + if value: + self.var_changed_builtin_keys() + else: + self.var_changed_custom_keys() + + def var_changed_keybinding(self, *params): + "Store change to a keybinding." + value = self.keybinding.get() + key_set = self.custom_keys.get() + event = self.list_bindings.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.are_keys_builtin.get(): + self.opt_menu_keys_builtin['state'] = NORMAL + self.opt_menu_keys_custom['state'] = DISABLED + self.button_delete_custom_keys['state'] = DISABLED + else: + self.opt_menu_keys_builtin['state'] = DISABLED + self.radio_keys_custom['state'] = NORMAL + self.opt_menu_keys_custom['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. """ - self.load_font_cfg() - self.load_tab_cfg() - self.load_theme_cfg() - self.load_key_cfg() - self.load_general_cfg() - # note: extension page handled separately + list_index = self.list_bindings.index(ANCHOR) + binding = self.list_bindings.get(list_index) + bind_name = binding.split()[0] + if self.are_keys_builtin.get(): + current_key_set_name = self.builtin_keys.get() + else: + current_key_set_name = self.custom_keys.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.are_keys_builtin.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.list_bindings.select_set(list_index) + self.list_bindings.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.list_bindings.delete(list_index) + self.list_bindings.insert(list_index, bind_name+' - '+new_keys) + self.list_bindings.select_set(list_index) + self.list_bindings.select_anchor(list_index) + self.keybinding.set(new_keys) + else: + self.list_bindings.select_set(list_index) + self.list_bindings.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 keybinding_selected(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. + + Create the new key set based on the previously active set + with the current changes applied. Once it is saved, then + activate the new key set. + """ + if self.are_keys_builtin.get(): + prev_key_set_name = self.builtin_keys.get() + else: + prev_key_set_name = self.custom_keys.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.opt_menu_keys_custom.SetMenu(custom_key_list, new_key_set_name) + self.are_keys_builtin.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 = 0 + if self.list_bindings.curselection(): + reselect = 1 + list_index = self.list_bindings.index(ANCHOR) + keyset = idleConf.GetKeySet(keyset_name) + bind_names = list(keyset.keys()) + bind_names.sort() + self.list_bindings.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.list_bindings.insert(END, bind_name+' - '+key) + if reselect: + self.list_bindings.see(list_index) + self.list_bindings.select_set(list_index) + self.list_bindings.select_anchor(list_index) def save_new_key_set(self, keyset_name, keyset): """Save a newly created core key set. @@ -1585,17 +1364,39 @@ class ConfigDialog(Toplevel): value = keyset[event] idleConf.userCfg['keys'].SetOption(keyset_name, event, value) - def save_new_theme(self, theme_name, theme): - """Save a newly created theme to idleConf. + def delete_custom_keys(self): + """Handle event to delete a custom key set. - theme_name - string, the name of the new theme - theme - dictionary containing the new theme + Applying the delete deactivates the current configuration and + reverts to the default. The custom key set is permanently + deleted from the config file. """ - if not idleConf.userCfg['highlight'].has_section(theme_name): - idleConf.userCfg['highlight'].add_section(theme_name) - for element in theme: - value = theme[element] - idleConf.userCfg['highlight'].SetOption(theme_name, element, value) + keyset_name=self.custom_keys.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.radio_keys_custom['state'] = DISABLED + self.opt_menu_keys_custom.SetMenu(item_list, '- no custom keys -') + else: + self.opt_menu_keys_custom.SetMenu(item_list, item_list[0]) + # Revert to default key set. + self.are_keys_builtin.set(idleConf.defaultCfg['main'] + .Get('Keys', 'default')) + self.builtin_keys.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): """Remove current key bindings. @@ -1623,49 +1424,252 @@ class ConfigDialog(Toplevel): instance.ApplyKeybindings() instance.reset_help_menu_entries() - def cancel(self): - """Dismiss config dialog. - Methods: - destroy: inherited - """ - self.destroy() + def create_page_general(self): + """Return frame of widgets for General tab. - def ok(self): - """Apply config changes, then dismiss dialog. + Enable users to provisionally change general options. Function + load_general_cfg intializes tk variables and helplist using + idleConf. Radiobuttons startup_shell_on and startup_editor_on + set var startup_edit. Radiobuttons save_ask_on and save_auto_on + set var autosave. Entry boxes win_width_int and win_height_int + set var win_width and win_height. Setting var_name invokes the + var_changed_var_name callback that adds option to changes. - Methods: - apply - destroy: inherited + Helplist: load_general_cfg loads list user_helplist with + name, position pairs and copies names to listbox helplist. + Clicking a name invokes help_source selected. Clicking + button_helplist_name invokes helplist_item_name, which also + changes user_helplist. These functions all call + set_add_delete_state. All but load call update_help_changes to + rewrite changes['main']['HelpFiles']. + + Widget Structure: (*) widgets bound to self + frame + frame_run: LabelFrame + startup_title: Label + (*)startup_editor_on: Radiobutton - startup_edit + (*)startup_shell_on: Radiobutton - startup_edit + frame_save: LabelFrame + run_save_title: Label + (*)save_ask_on: Radiobutton - autosave + (*)save_auto_on: Radiobutton - autosave + frame_win_size: LabelFrame + win_size_title: Label + win_width_title: Label + (*)win_width_int: Entry - win_width + win_height_title: Label + (*)win_height_int: Entry - win_height + frame_help: LabelFrame + frame_helplist: Frame + frame_helplist_buttons: Frame + (*)button_helplist_edit + (*)button_helplist_add + (*)button_helplist_remove + (*)helplist: ListBox + scroll_helplist: Scrollbar """ - self.apply() - self.destroy() + parent = self.parent + self.startup_edit = IntVar(parent) + self.autosave = IntVar(parent) + self.win_width = StringVar(parent) + self.win_height = StringVar(parent) - def apply(self): - """Apply config changes and leave dialog open. + # Create widgets: + # body. + frame = self.tab_pages.pages['General'].frame + # body section frames. + frame_run = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Startup Preferences ') + frame_save = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' autosave Preferences ') + frame_win_size = Frame(frame, borderwidth=2, relief=GROOVE) + frame_help = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Additional Help Sources ') + # frame_run. + startup_title = Label(frame_run, text='At Startup') + self.startup_editor_on = Radiobutton( + frame_run, variable=self.startup_edit, value=1, + text="Open Edit Window") + self.startup_shell_on = Radiobutton( + frame_run, variable=self.startup_edit, value=0, + text='Open Shell Window') + # frame_save. + run_save_title = Label(frame_save, text='At Start of Run (F5) ') + self.save_ask_on = Radiobutton( + frame_save, variable=self.autosave, value=0, + text="Prompt to Save") + self.save_auto_on = Radiobutton( + frame_save, variable=self.autosave, value=1, + text='No Prompt') + # frame_win_size. + win_size_title = Label( + frame_win_size, text='Initial Window Size (in characters)') + win_width_title = Label(frame_win_size, text='Width') + self.win_width_int = Entry( + frame_win_size, textvariable=self.win_width, width=3) + win_height_title = Label(frame_win_size, text='Height') + self.win_height_int = Entry( + frame_win_size, textvariable=self.win_height, width=3) + # frame_help. + frame_helplist = Frame(frame_help) + frame_helplist_buttons = Frame(frame_helplist) + self.helplist = Listbox( + frame_helplist, height=5, takefocus=FALSE, + exportselection=FALSE) + scroll_helplist = Scrollbar(frame_helplist) + scroll_helplist['command'] = self.helplist.yview + self.helplist['yscrollcommand'] = scroll_helplist.set + self.helplist.bind('<ButtonRelease-1>', self.help_source_selected) + self.button_helplist_edit = Button( + frame_helplist_buttons, text='Edit', state=DISABLED, + width=8, command=self.helplist_item_edit) + self.button_helplist_add = Button( + frame_helplist_buttons, text='Add', + width=8, command=self.helplist_item_add) + self.button_helplist_remove = Button( + frame_helplist_buttons, text='Remove', state=DISABLED, + width=8, command=self.helplist_item_remove) - Methods: - deactivate_current_config - save_all_changed_extensions - activate_config_changes + # Pack widgets: + # body. + frame_run.pack(side=TOP, padx=5, pady=5, fill=X) + frame_save.pack(side=TOP, padx=5, pady=5, fill=X) + frame_win_size.pack(side=TOP, padx=5, pady=5, fill=X) + frame_help.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) + # frame_run. + startup_title.pack(side=LEFT, anchor=W, padx=5, pady=5) + self.startup_shell_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) + self.startup_editor_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) + # frame_save. + run_save_title.pack(side=LEFT, anchor=W, padx=5, pady=5) + self.save_auto_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) + self.save_ask_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) + # frame_win_size. + win_size_title.pack(side=LEFT, anchor=W, padx=5, pady=5) + self.win_height_int.pack(side=RIGHT, anchor=E, padx=10, pady=5) + win_height_title.pack(side=RIGHT, anchor=E, pady=5) + self.win_width_int.pack(side=RIGHT, anchor=E, padx=10, pady=5) + win_width_title.pack(side=RIGHT, anchor=E, pady=5) + # frame_help. + frame_helplist_buttons.pack(side=RIGHT, padx=5, pady=5, fill=Y) + frame_helplist.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) + scroll_helplist.pack(side=RIGHT, anchor=W, fill=Y) + self.helplist.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH) + self.button_helplist_edit.pack(side=TOP, anchor=W, pady=5) + self.button_helplist_add.pack(side=TOP, anchor=W) + self.button_helplist_remove.pack(side=TOP, anchor=W, pady=5) + + return frame + + def load_general_cfg(self): + "Load current configuration settings for the general options." + # Set startup state. + self.startup_edit.set(idleConf.GetOption( + 'main', 'General', 'editor-on-startup', default=0, type='bool')) + # Set autosave state. + self.autosave.set(idleConf.GetOption( + 'main', 'General', 'autosave', default=0, type='bool')) + # Set initial window size. + self.win_width.set(idleConf.GetOption( + 'main', 'EditorWindow', 'width', type='int')) + self.win_height.set(idleConf.GetOption( + 'main', 'EditorWindow', 'height', type='int')) + # Set additional help sources. + self.user_helplist = idleConf.GetAllExtraHelpSourcesList() + self.helplist.delete(0, 'end') + for help_item in self.user_helplist: + self.helplist.insert(END, help_item[0]) + self.set_add_delete_state() + + def var_changed_startup_edit(self, *params): + "Store change to toggle for starting IDLE in the editor or shell." + value = self.startup_edit.get() + changes.add_option('main', 'General', 'editor-on-startup', value) + + def var_changed_autosave(self, *params): + "Store change to autosave." + value = self.autosave.get() + changes.add_option('main', 'General', 'autosave', value) + + def var_changed_win_width(self, *params): + "Store change to window width." + value = self.win_width.get() + changes.add_option('main', 'EditorWindow', 'width', value) + + def var_changed_win_height(self, *params): + "Store change to window height." + value = self.win_height.get() + changes.add_option('main', 'EditorWindow', 'height', value) + + def help_source_selected(self, event): + "Handle event for selecting additional help." + self.set_add_delete_state() + + def set_add_delete_state(self): + "Toggle the state for the help list buttons based on list entries." + if self.helplist.size() < 1: # No entries in list. + self.button_helplist_edit['state'] = DISABLED + self.button_helplist_remove['state'] = DISABLED + else: # Some entries. + if self.helplist.curselection(): # There currently is a selection. + self.button_helplist_edit['state'] = NORMAL + self.button_helplist_remove['state'] = NORMAL + else: # There currently is not a selection. + self.button_helplist_edit['state'] = DISABLED + self.button_helplist_remove['state'] = DISABLED + + def helplist_item_add(self): + """Handle add button for the help list. + + Query for name and location of new help sources and add + them to the list. """ - self.deactivate_current_config() - changes.save_all() - self.save_all_changed_extensions() - self.activate_config_changes() + help_source = HelpSource(self, 'New Help Source').result + if help_source: + self.user_helplist.append(help_source) + self.helplist.insert(END, help_source[0]) + self.update_help_changes() - def help(self): - """Create textview for config dialog help. + def helplist_item_edit(self): + """Handle edit button for the help list. - Attrbutes accessed: - tab_pages + Query with existing help source information and update + config if the values are changed. + """ + item_index = self.helplist.index(ANCHOR) + help_source = self.user_helplist[item_index] + new_help_source = HelpSource( + self, 'Edit Help Source', + menuitem=help_source[0], + filepath=help_source[1], + ).result + if new_help_source and new_help_source != help_source: + self.user_helplist[item_index] = new_help_source + self.helplist.delete(item_index) + self.helplist.insert(item_index, new_help_source[0]) + self.update_help_changes() + self.set_add_delete_state() # Selected will be un-selected - Methods: - view_text: Method from textview module. + def helplist_item_remove(self): + """Handle remove button for the help list. + + Delete the help list item from config. """ - page = self.tab_pages._current_page - view_text(self, title='Help for IDLE preferences', - text=help_common+help_pages.get(page, '')) + item_index = self.helplist.index(ANCHOR) + del(self.user_helplist[item_index]) + self.helplist.delete(item_index) + self.update_help_changes() + self.set_add_delete_state() + + def update_help_changes(self): + "Clear and rebuild the HelpFiles section in changes" + changes['main']['HelpFiles'] = {} + for num in range(1, len(self.user_helplist) + 1): + changes.add_option( + 'main', 'HelpFiles', str(num), + ';'.join(self.user_helplist[num-1][:2])) + def create_page_extensions(self): """Part of the config dialog used for configuring IDLE extensions. diff --git a/Misc/NEWS.d/next/IDLE/2017-07-27-14-48-42.bpo-31060.GdY_VY.rst b/Misc/NEWS.d/next/IDLE/2017-07-27-14-48-42.bpo-31060.GdY_VY.rst new file mode 100644 index 0000000..1d202c7 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2017-07-27-14-48-42.bpo-31060.GdY_VY.rst @@ -0,0 +1,3 @@ +IDLE - Finish rearranging methods of ConfigDialog Grouping methods +pertaining to each tab and the buttons will aid writing tests and improving +the tabs and will enable splitting the groups into classes. |