Commit 447169f6 authored by Terry Jan Reedy's avatar Terry Jan Reedy

Merge with 3.4

parents 5a8bbc5f 93f3542a
...@@ -252,17 +252,16 @@ Options menu (Shell and Editor) ...@@ -252,17 +252,16 @@ Options menu (Shell and Editor)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Configure IDLE Configure IDLE
Open a configuration dialog. Fonts, indentation, keybindings, and color Open a configuration dialog and change preferences for the following:
themes may be altered. Startup Preferences may be set, and additional fonts, indentation, keybindings, text color themes, startup windows and
help sources can be specified. Non-default user setting are saved in a size, additional help sources, and extensions (see below). On OS X,
.idlerc directory in the user's home directory. Problems caused by bad user open the configuration dialog by selecting Preferences in the application
configuration files are solved by editing or deleting one or more of the menu. To use a new built-in color theme (IDLE Dark) with older IDLEs,
files in .idlerc. On OS X, open the configuration dialog by selecting save it as a new custom theme.
Preferences in the application menu.
Non-default user settings are saved in a .idlerc directory in the user's
Configure Extensions home directory. Problems caused by bad user configuration files are solved
Open a configuration dialog for setting preferences for extensions by editing or deleting one or more of the files in .idlerc.
(discussed below). See note above about the location of user settings.
Code Context (toggle)(Editor Window only) Code Context (toggle)(Editor Window only)
Open a pane at the top of the edit window which shows the block context Open a pane at the top of the edit window which shows the block context
......
...@@ -78,7 +78,6 @@ menudefs = [ ...@@ -78,7 +78,6 @@ menudefs = [
]), ]),
('options', [ ('options', [
('Configure _IDLE', '<<open-config-dialog>>'), ('Configure _IDLE', '<<open-config-dialog>>'),
('Configure _Extensions', '<<open-config-extensions-dialog>>'),
None, None,
]), ]),
('help', [ ('help', [
......
...@@ -191,8 +191,6 @@ class EditorWindow(object): ...@@ -191,8 +191,6 @@ class EditorWindow(object):
text.bind("<<python-docs>>", self.python_docs) text.bind("<<python-docs>>", self.python_docs)
text.bind("<<about-idle>>", self.about_dialog) text.bind("<<about-idle>>", self.about_dialog)
text.bind("<<open-config-dialog>>", self.config_dialog) text.bind("<<open-config-dialog>>", self.config_dialog)
text.bind("<<open-config-extensions-dialog>>",
self.config_extensions_dialog)
text.bind("<<open-module>>", self.open_module) text.bind("<<open-module>>", self.open_module)
text.bind("<<do-nothing>>", lambda event: "break") text.bind("<<do-nothing>>", lambda event: "break")
text.bind("<<select-all>>", self.select_all) text.bind("<<select-all>>", self.select_all)
...@@ -514,10 +512,6 @@ class EditorWindow(object): ...@@ -514,10 +512,6 @@ class EditorWindow(object):
# Synchronize with macosxSupport.overrideRootMenu.config_dialog. # Synchronize with macosxSupport.overrideRootMenu.config_dialog.
configDialog.ConfigDialog(self.top,'Settings') configDialog.ConfigDialog(self.top,'Settings')
def config_extensions_dialog(self, event=None):
"Handle Options 'Configure Extensions' event."
configDialog.ConfigExtensionsDialog(self.top)
def help_dialog(self, event=None): def help_dialog(self, event=None):
"Handle Help 'IDLE Help' event." "Handle Help 'IDLE Help' event."
# Synchronize with macosxSupport.overrideRootMenu.help_dialog. # Synchronize with macosxSupport.overrideRootMenu.help_dialog.
......
...@@ -80,12 +80,14 @@ class ConfigDialog(Toplevel): ...@@ -80,12 +80,14 @@ class ConfigDialog(Toplevel):
def CreateWidgets(self): def CreateWidgets(self):
self.tabPages = TabbedPageSet(self, self.tabPages = TabbedPageSet(self,
page_names=['Fonts/Tabs', 'Highlighting', 'Keys', 'General']) page_names=['Fonts/Tabs', 'Highlighting', 'Keys', 'General',
'Extensions'])
self.tabPages.pack(side=TOP, expand=TRUE, fill=BOTH) self.tabPages.pack(side=TOP, expand=TRUE, fill=BOTH)
self.CreatePageFontTab() self.CreatePageFontTab()
self.CreatePageHighlight() self.CreatePageHighlight()
self.CreatePageKeys() self.CreatePageKeys()
self.CreatePageGeneral() self.CreatePageGeneral()
self.CreatePageExtensions()
self.create_action_buttons().pack(side=BOTTOM) self.create_action_buttons().pack(side=BOTTOM)
def create_action_buttons(self): def create_action_buttons(self):
...@@ -1092,6 +1094,7 @@ class ConfigDialog(Toplevel): ...@@ -1092,6 +1094,7 @@ class ConfigDialog(Toplevel):
self.LoadKeyCfg() self.LoadKeyCfg()
### general page ### general page
self.LoadGeneralCfg() self.LoadGeneralCfg()
# note: extension page handled separately
def SaveNewKeySet(self, keySetName, keySet): def SaveNewKeySet(self, keySetName, keySet):
""" """
...@@ -1145,6 +1148,7 @@ class ConfigDialog(Toplevel): ...@@ -1145,6 +1148,7 @@ class ConfigDialog(Toplevel):
# save these even if unchanged! # save these even if unchanged!
idleConf.userCfg[configType].Save() idleConf.userCfg[configType].Save()
self.ResetChangedItems() #clear the changed items dict self.ResetChangedItems() #clear the changed items dict
self.save_all_changed_extensions() # uses a different mechanism
def DeactivateCurrentConfig(self): def DeactivateCurrentConfig(self):
#Before a config is saved, some cleanup of current #Before a config is saved, some cleanup of current
...@@ -1180,87 +1184,14 @@ class ConfigDialog(Toplevel): ...@@ -1180,87 +1184,14 @@ class ConfigDialog(Toplevel):
view_text(self, title='Help for IDLE preferences', view_text(self, title='Help for IDLE preferences',
text=help_common+help_pages.get(page, '')) text=help_common+help_pages.get(page, ''))
help_common = '''\ def CreatePageExtensions(self):
When you click either the Apply or Ok buttons, settings in this """Part of the config dialog used for configuring IDLE extensions.
dialog that are different from IDLE's default are saved in
a .idlerc directory in your home directory. Except as noted,
hese changes apply to all versions of IDLE installed on this
machine. Some do not take affect until IDLE is restarted.
[Cancel] only cancels changes made since the last save.
'''
help_pages = {
'Highlighting':'''
Highlighting:
The IDLE Dark color theme is new in Octover 2015. It can only
be used with older IDLE releases if it is saved as a custom
theme, with a different name.
'''
}
class VerticalScrolledFrame(Frame):
"""A pure Tkinter vertically scrollable frame.
* Use the 'interior' attribute to place widgets inside the scrollable frame
* Construct and pack/place/grid normally
* This frame only allows vertical scrolling
"""
def __init__(self, parent, *args, **kw):
Frame.__init__(self, parent, *args, **kw)
# create a canvas object and a vertical scrollbar for scrolling it
vscrollbar = Scrollbar(self, orient=VERTICAL)
vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
canvas = Canvas(self, bd=0, highlightthickness=0,
yscrollcommand=vscrollbar.set)
canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
vscrollbar.config(command=canvas.yview)
# reset the view
canvas.xview_moveto(0)
canvas.yview_moveto(0)
# create a frame inside the canvas which will be scrolled with it This code is generic - it works for any and all IDLE extensions.
self.interior = interior = Frame(canvas)
interior_id = canvas.create_window(0, 0, window=interior, anchor=NW)
# track changes to the canvas and frame width and sync them,
# also updating the scrollbar
def _configure_interior(event):
# update the scrollbars to match the size of the inner frame
size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
canvas.config(scrollregion="0 0 %s %s" % size)
interior.bind('<Configure>', _configure_interior)
def _configure_canvas(event):
if interior.winfo_reqwidth() != canvas.winfo_width():
# update the inner frame's width to fill the canvas
canvas.itemconfigure(interior_id, width=canvas.winfo_width())
canvas.bind('<Configure>', _configure_canvas)
return
def is_int(s):
"Return 's is blank or represents an int'"
if not s:
return True
try:
int(s)
return True
except ValueError:
return False
# TODO:
# * Revert to default(s)? Per option or per extension?
# * List options in their original order (possible??)
class ConfigExtensionsDialog(Toplevel):
"""A dialog for configuring IDLE extensions.
This dialog is generic - it works for any and all IDLE extensions.
IDLE extensions save their configuration options using idleConf. IDLE extensions save their configuration options using idleConf.
ConfigExtensionsDialog reads the current configuration using idleConf, This code reads the current configuration using idleConf, supplies a
supplies a GUI interface to change the configuration values, and saves the GUI interface to change the configuration values, and saves the
changes using idleConf. changes using idleConf.
Not all changes take effect immediately - some may require restarting IDLE. Not all changes take effect immediately - some may require restarting IDLE.
...@@ -1270,37 +1201,41 @@ class ConfigExtensionsDialog(Toplevel): ...@@ -1270,37 +1201,41 @@ class ConfigExtensionsDialog(Toplevel):
reasonable values. The only exception to this are the 'enable*' options, reasonable values. The only exception to this are the 'enable*' options,
which are boolean, and can be toggled with an True/False button. which are boolean, and can be toggled with an True/False button.
""" """
def __init__(self, parent, title=None, _htest=False): parent = self.parent
Toplevel.__init__(self, parent) frame = self.tabPages.pages['Extensions'].frame
self.wm_withdraw() self.ext_defaultCfg = idleConf.defaultCfg['extensions']
self.ext_userCfg = idleConf.userCfg['extensions']
self.configure(borderwidth=5)
self.geometry(
"+%d+%d" % (parent.winfo_rootx() + 20,
parent.winfo_rooty() + (30 if not _htest else 150)))
self.wm_title(title or 'IDLE Extensions Configuration')
self.defaultCfg = idleConf.defaultCfg['extensions']
self.userCfg = idleConf.userCfg['extensions']
self.is_int = self.register(is_int) self.is_int = self.register(is_int)
self.load_extensions() self.load_extensions()
self.create_widgets() # create widgets - a listbox shows all available extensions, with the
# controls for the extension selected in the listbox to the right
self.extension_names = StringVar(self)
frame.rowconfigure(0, weight=1)
frame.columnconfigure(2, weight=1)
self.extension_list = Listbox(frame, listvariable=self.extension_names,
selectmode='browse')
self.extension_list.bind('<<ListboxSelect>>', self.extension_selected)
scroll = Scrollbar(frame, command=self.extension_list.yview)
self.extension_list.yscrollcommand=scroll.set
self.details_frame = LabelFrame(frame, width=250, height=250)
self.extension_list.grid(column=0, row=0, sticky='nws')
scroll.grid(column=1, row=0, sticky='ns')
self.details_frame.grid(column=2, row=0, sticky='nsew', padx=[10, 0])
frame.configure(padx=10, pady=10)
self.config_frame = {}
self.current_extension = None
self.resizable(height=FALSE, width=FALSE) # don't allow resizing yet self.outerframe = self # TEMPORARY
self.transient(parent) self.tabbed_page_set = self.extension_list # TEMPORARY
self.protocol("WM_DELETE_WINDOW", self.Cancel)
self.tabbed_page_set.focus_set()
# wait for window to be generated
self.update()
# set current width as the minimum width
self.wm_minsize(self.winfo_width(), 1)
# now allow resizing
self.resizable(height=TRUE, width=TRUE)
self.wm_deiconify() # create the frame holding controls for each extension
if not _htest: ext_names = ''
self.grab_set() for ext_name in sorted(self.extensions):
self.wait_window() self.create_extension_frame(ext_name)
ext_names = ext_names + '{' + ext_name + '} '
self.extension_names.set(ext_names)
self.extension_list.selection_set(0)
self.extension_selected(None)
def load_extensions(self): def load_extensions(self):
"Fill self.extensions with data from the default and user configs." "Fill self.extensions with data from the default and user configs."
...@@ -1309,7 +1244,7 @@ class ConfigExtensionsDialog(Toplevel): ...@@ -1309,7 +1244,7 @@ class ConfigExtensionsDialog(Toplevel):
self.extensions[ext_name] = [] self.extensions[ext_name] = []
for ext_name in self.extensions: for ext_name in self.extensions:
opt_list = sorted(self.defaultCfg.GetOptionList(ext_name)) opt_list = sorted(self.ext_defaultCfg.GetOptionList(ext_name))
# bring 'enable' options to the beginning of the list # bring 'enable' options to the beginning of the list
enables = [opt_name for opt_name in opt_list enables = [opt_name for opt_name in opt_list
...@@ -1319,7 +1254,7 @@ class ConfigExtensionsDialog(Toplevel): ...@@ -1319,7 +1254,7 @@ class ConfigExtensionsDialog(Toplevel):
opt_list = enables + opt_list opt_list = enables + opt_list
for opt_name in opt_list: for opt_name in opt_list:
def_str = self.defaultCfg.Get( def_str = self.ext_defaultCfg.Get(
ext_name, opt_name, raw=True) ext_name, opt_name, raw=True)
try: try:
def_obj = {'True':True, 'False':False}[def_str] def_obj = {'True':True, 'False':False}[def_str]
...@@ -1332,7 +1267,7 @@ class ConfigExtensionsDialog(Toplevel): ...@@ -1332,7 +1267,7 @@ class ConfigExtensionsDialog(Toplevel):
def_obj = def_str def_obj = def_str
opt_type = None opt_type = None
try: try:
value = self.userCfg.Get( value = self.ext_userCfg.Get(
ext_name, opt_name, type=opt_type, raw=True, ext_name, opt_name, type=opt_type, raw=True,
default=def_obj) default=def_obj)
except ValueError: # Need this until .Get fixed except ValueError: # Need this until .Get fixed
...@@ -1347,37 +1282,6 @@ class ConfigExtensionsDialog(Toplevel): ...@@ -1347,37 +1282,6 @@ class ConfigExtensionsDialog(Toplevel):
'var': var, 'var': var,
}) })
def create_widgets(self):
"""Create the dialog's widgets."""
self.extension_names = StringVar(self)
self.rowconfigure(0, weight=1)
self.columnconfigure(2, weight=1)
self.extension_list = Listbox(self, listvariable=self.extension_names,
selectmode='browse')
self.extension_list.bind('<<ListboxSelect>>', self.extension_selected)
scroll = Scrollbar(self, command=self.extension_list.yview)
self.extension_list.yscrollcommand=scroll.set
self.details_frame = LabelFrame(self, width=250, height=250)
self.extension_list.grid(column=0, row=0, sticky='nws')
scroll.grid(column=1, row=0, sticky='ns')
self.details_frame.grid(column=2, row=0, sticky='nsew', padx=[10, 0])
self.configure(padx=10, pady=10)
self.config_frame = {}
self.current_extension = None
self.outerframe = self # TEMPORARY
self.tabbed_page_set = self.extension_list # TEMPORARY
# create the individual pages
ext_names = ''
for ext_name in sorted(self.extensions):
self.create_extension_frame(ext_name)
ext_names = ext_names + '{' + ext_name + '} '
self.extension_names.set(ext_names)
self.extension_list.selection_set(0)
self.extension_selected(None)
self.create_action_buttons().grid(row=1, columnspan=3)
def extension_selected(self, event): def extension_selected(self, event):
newsel = self.extension_list.curselection() newsel = self.extension_list.curselection()
if newsel: if newsel:
...@@ -1392,8 +1296,6 @@ class ConfigExtensionsDialog(Toplevel): ...@@ -1392,8 +1296,6 @@ class ConfigExtensionsDialog(Toplevel):
self.config_frame[newsel].grid(column=0, row=0, sticky='nsew') self.config_frame[newsel].grid(column=0, row=0, sticky='nsew')
self.current_extension = newsel self.current_extension = newsel
create_action_buttons = ConfigDialog.create_action_buttons
def create_extension_frame(self, ext_name): def create_extension_frame(self, ext_name):
"""Create a frame holding the widgets to configure one extension""" """Create a frame holding the widgets to configure one extension"""
f = VerticalScrolledFrame(self.details_frame, height=250, width=250) f = VerticalScrolledFrame(self.details_frame, height=250, width=250)
...@@ -1420,19 +1322,7 @@ class ConfigExtensionsDialog(Toplevel): ...@@ -1420,19 +1322,7 @@ class ConfigExtensionsDialog(Toplevel):
).grid(row=row, column=1, sticky=NSEW, padx=7) ).grid(row=row, column=1, sticky=NSEW, padx=7)
return return
def set_extension_value(self, section, opt):
Ok = ConfigDialog.Ok
def Apply(self):
self.save_all_changed_configs()
pass
Cancel = ConfigDialog.Cancel
def Help(self):
pass
def set_user_value(self, section, opt):
name = opt['name'] name = opt['name']
default = opt['default'] default = opt['default']
value = opt['var'].get().strip() or default value = opt['var'].get().strip() or default
...@@ -1440,20 +1330,92 @@ class ConfigExtensionsDialog(Toplevel): ...@@ -1440,20 +1330,92 @@ class ConfigExtensionsDialog(Toplevel):
# if self.defaultCfg.has_section(section): # if self.defaultCfg.has_section(section):
# Currently, always true; if not, indent to return # Currently, always true; if not, indent to return
if (value == default): if (value == default):
return self.userCfg.RemoveOption(section, name) return self.ext_userCfg.RemoveOption(section, name)
# set the option # set the option
return self.userCfg.SetOption(section, name, value) return self.ext_userCfg.SetOption(section, name, value)
def save_all_changed_configs(self): def save_all_changed_extensions(self):
"""Save configuration changes to the user config file.""" """Save configuration changes to the user config file."""
has_changes = False has_changes = False
for ext_name in self.extensions: for ext_name in self.extensions:
options = self.extensions[ext_name] options = self.extensions[ext_name]
for opt in options: for opt in options:
if self.set_user_value(ext_name, opt): if self.set_extension_value(ext_name, opt):
has_changes = True has_changes = True
if has_changes: if has_changes:
self.userCfg.Save() self.ext_userCfg.Save()
help_common = '''\
When you click either the Apply or Ok buttons, settings in this
dialog that are different from IDLE's default are saved in
a .idlerc directory in your home directory. Except as noted,
hese changes apply to all versions of IDLE installed on this
machine. Some do not take affect until IDLE is restarted.
[Cancel] only cancels changes made since the last save.
'''
help_pages = {
'Highlighting':'''
Highlighting:
The IDLE Dark color theme is new in Octover 2015. It can only
be used with older IDLE releases if it is saved as a custom
theme, with a different name.
'''
}
def is_int(s):
"Return 's is blank or represents an int'"
if not s:
return True
try:
int(s)
return True
except ValueError:
return False
class VerticalScrolledFrame(Frame):
"""A pure Tkinter vertically scrollable frame.
* Use the 'interior' attribute to place widgets inside the scrollable frame
* Construct and pack/place/grid normally
* This frame only allows vertical scrolling
"""
def __init__(self, parent, *args, **kw):
Frame.__init__(self, parent, *args, **kw)
# create a canvas object and a vertical scrollbar for scrolling it
vscrollbar = Scrollbar(self, orient=VERTICAL)
vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
canvas = Canvas(self, bd=0, highlightthickness=0,
yscrollcommand=vscrollbar.set)
canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
vscrollbar.config(command=canvas.yview)
# reset the view
canvas.xview_moveto(0)
canvas.yview_moveto(0)
# create a frame inside the canvas which will be scrolled with it
self.interior = interior = Frame(canvas)
interior_id = canvas.create_window(0, 0, window=interior, anchor=NW)
# track changes to the canvas and frame width and sync them,
# also updating the scrollbar
def _configure_interior(event):
# update the scrollbars to match the size of the inner frame
size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
canvas.config(scrollregion="0 0 %s %s" % size)
interior.bind('<Configure>', _configure_interior)
def _configure_canvas(event):
if interior.winfo_reqwidth() != canvas.winfo_width():
# update the inner frame's width to fill the canvas
canvas.itemconfigure(interior_id, width=canvas.winfo_width())
canvas.bind('<Configure>', _configure_canvas)
return
if __name__ == '__main__': if __name__ == '__main__':
......
...@@ -266,16 +266,16 @@ access to locals and globals.</dd> ...@@ -266,16 +266,16 @@ access to locals and globals.</dd>
<h3>25.5.1.7. Options menu (Shell and Editor)<a class="headerlink" href="#options-menu-shell-and-editor" title="Permalink to this headline"></a></h3> <h3>25.5.1.7. Options menu (Shell and Editor)<a class="headerlink" href="#options-menu-shell-and-editor" title="Permalink to this headline"></a></h3>
<dl class="docutils"> <dl class="docutils">
<dt>Configure IDLE</dt> <dt>Configure IDLE</dt>
<dd>Open a configuration dialog. Fonts, indentation, keybindings, and color <dd><p class="first">Open a configuration dialog and change preferences for the following:
themes may be altered. Startup Preferences may be set, and additional fonts, indentation, keybindings, text color themes, startup windows and
help sources can be specified. Non-default user setting are saved in a size, additional help sources, and extensions (see below). On OS X,
.idlerc directory in the user&#8217;s home directory. Problems caused by bad user open the configuration dialog by selecting Preferences in the application
configuration files are solved by editing or deleting one or more of the menu. To use a new built-in color theme (IDLE Dark) with older IDLEs,
files in .idlerc. On OS X, open the configuration dialog by selecting save it as a new custom theme.</p>
Preferences in the application menu.</dd> <p class="last">Non-default user settings are saved in a .idlerc directory in the user&#8217;s
<dt>Configure Extensions</dt> home directory. Problems caused by bad user configuration files are solved
<dd>Open a configuration dialog for setting preferences for extensions by editing or deleting one or more of the files in .idlerc.</p>
(discussed below). See note above about the location of user settings.</dd> </dd>
<dt>Code Context (toggle)(Editor Window only)</dt> <dt>Code Context (toggle)(Editor Window only)</dt>
<dd>Open a pane at the top of the edit window which shows the block context <dd>Open a pane at the top of the edit window which shows the block context
of the code which has scrolled above the top of the window.</dd> of the code which has scrolled above the top of the window.</dd>
...@@ -699,7 +699,7 @@ are currently:</p> ...@@ -699,7 +699,7 @@ are currently:</p>
The Python Software Foundation is a non-profit corporation. The Python Software Foundation is a non-profit corporation.
<a href="https://www.python.org/psf/donations/">Please donate.</a> <a href="https://www.python.org/psf/donations/">Please donate.</a>
<br /> <br />
Last updated on Oct 02, 2015. Last updated on Oct 13, 2015.
<a href="../bugs.html">Found a bug</a>? <a href="../bugs.html">Found a bug</a>?
<br /> <br />
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.2.3. Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.2.3.
......
...@@ -93,15 +93,6 @@ _class_browser_spec = { ...@@ -93,15 +93,6 @@ _class_browser_spec = {
"Double clicking on items prints a traceback for an exception " "Double clicking on items prints a traceback for an exception "
"that is ignored." "that is ignored."
} }
ConfigExtensionsDialog_spec = {
'file': 'configDialog',
'kwds': {'title': 'Test Extension Configuration',
'_htest': True,},
'msg': "IDLE extensions dialog.\n"
"\n[Ok] to close the dialog.[Apply] to apply the settings and "
"and [Cancel] to revert all changes.\nRe-run the test to ensure "
"changes made have persisted."
}
_color_delegator_spec = { _color_delegator_spec = {
'file': 'ColorDelegator', 'file': 'ColorDelegator',
...@@ -121,7 +112,8 @@ ConfigDialog_spec = { ...@@ -121,7 +112,8 @@ ConfigDialog_spec = {
"font face of the text in the area below it.\nIn the " "font face of the text in the area below it.\nIn the "
"'Highlighting' tab, try different color schemes. Clicking " "'Highlighting' tab, try different color schemes. Clicking "
"items in the sample program should update the choices above it." "items in the sample program should update the choices above it."
"\nIn the 'Keys' and 'General' tab, test settings of interest." "\nIn the 'Keys', 'General' and 'Extensions' tabs, test settings"
"of interest."
"\n[Ok] to close the dialog.[Apply] to apply the settings and " "\n[Ok] to close the dialog.[Apply] to apply the settings and "
"and [Cancel] to revert all changes.\nRe-run the test to ensure " "and [Cancel] to revert all changes.\nRe-run the test to ensure "
"changes made have persisted." "changes made have persisted."
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment