[spyder] 06/08: Imported Upstream version 2.3.0~beta4+dfsg

Frédéric-Emmanuel Picca picca at moszumanska.debian.org
Thu Apr 10 21:06:28 UTC 2014


This is an automated email from the git hooks/post-receive script.

picca pushed a commit to branch experimental
in repository spyder.

commit 3804263b314bb3f166b26d7fed62e0e1ae2dd3dd
Author: Picca Frédéric-Emmanuel <picca at debian.org>
Date:   Thu Apr 10 22:07:26 2014 +0200

    Imported Upstream version 2.3.0~beta4+dfsg
---
 MANIFEST.in                                        |   1 +
 PKG-INFO                                           |   4 +-
 doc/installation.rst                               |   2 +
 img_src/spyder3.png                                | Bin 0 -> 8259 bytes
 scripts/spyder3                                    |   3 +
 scripts/spyder3.desktop                            |  14 +
 setup.py                                           |  20 +-
 spyderlib/__init__.py                              |   2 +-
 spyderlib/config.py                                | 236 ++++++++++---
 spyderlib/guiconfig.py                             |  36 +-
 spyderlib/images/filetypes/enaml.png               | Bin 0 -> 743 bytes
 spyderlib/images/filetypes/jl.png                  | Bin 0 -> 1166 bytes
 spyderlib/plugins/__init__.py                      |  14 +-
 spyderlib/plugins/configdialog.py                  |   3 +-
 spyderlib/plugins/editor.py                        |  86 +++--
 spyderlib/plugins/externalconsole.py               |  36 +-
 spyderlib/plugins/ipythonconsole.py                |   4 +-
 spyderlib/plugins/outlineexplorer.py               |   6 +-
 spyderlib/plugins/projectexplorer.py               |   2 +-
 spyderlib/plugins/workingdirectory.py              |  26 +-
 spyderlib/scientific_startup.py                    |   6 +-
 spyderlib/spyder.py                                |  45 +--
 spyderlib/userconfig.py                            | 261 ++++++++------
 spyderlib/utils/codeanalysis.py                    |   2 +-
 spyderlib/utils/defaults-2.4.0.ini                 | 380 +++++++++++++++++++++
 spyderlib/utils/introspection/base.py              |  74 ++--
 spyderlib/utils/programs.py                        |   4 +-
 spyderlib/utils/sourcecode.py                      |   4 +-
 spyderlib/utils/system.py                          |  35 +-
 spyderlib/utils/vcs.py                             |   3 +-
 spyderlib/widgets/editor.py                        |  62 ++--
 spyderlib/widgets/findreplace.py                   |  41 +--
 spyderlib/widgets/ipython.py                       |  29 +-
 spyderlib/widgets/shell.py                         |  35 +-
 spyderlib/widgets/sourcecode/base.py               |   5 -
 spyderlib/widgets/sourcecode/codeeditor.py         | 109 +++---
 spyderlib/widgets/sourcecode/syntaxhighlighters.py |  24 ++
 spyderlib/widgets/status.py                        |   6 +
 spyderplugins/p_breakpoints.py                     |   2 +-
 spyderplugins/p_profiler.py                        |   2 +-
 spyderplugins/p_pylint.py                          |   2 +-
 41 files changed, 1166 insertions(+), 460 deletions(-)

diff --git a/MANIFEST.in b/MANIFEST.in
index e8a1330..b8b0fba 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -5,6 +5,7 @@ recursive-include app_example *.py *.pyw *.bat *.qm *.svg *.png
 include scripts/*
 include img_src/*.ico
 include img_src/spyder.png
+include img_src/spyder3.png
 include MANIFEST.in
 include README.md
 include LICENSE
diff --git a/PKG-INFO b/PKG-INFO
index 80dc915..2c58b24 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,12 +1,12 @@
 Metadata-Version: 1.1
 Name: spyder
-Version: 2.3.0beta3
+Version: 2.3.0beta4
 Summary: Scientific PYthon Development EnviRonment
 Home-page: http://code.google.com/p/spyderlib
 Author: Pierre Raybaut
 Author-email: UNKNOWN
 License: MIT
-Download-URL: http://code.google.com/p/spyderlib/files/spyder-2.3.0beta3.zip
+Download-URL: http://code.google.com/p/spyderlib/files/spyder-2.3.0beta4.zip
 Description: Spyder is an interactive Python development environment providing 
         MATLAB-like features in a simple and light-weighted software.
         It also provides ready-to-use pure-Python widgets to your PyQt4 or 
diff --git a/doc/installation.rst b/doc/installation.rst
index 5b949e5..e7de25d 100644
--- a/doc/installation.rst
+++ b/doc/installation.rst
@@ -94,6 +94,8 @@ might need.
 
    * `openSUSE <https://build.opensuse.org/package/show?package=python-spyder&project=home%3Aocefpaf>`_
 
+   * `Mageia <http://mageia.madb.org/package/show/name/spyder>`_
+
    Please refer to your distribution's documentation to learn how to install it
    there.
 
diff --git a/img_src/spyder3.png b/img_src/spyder3.png
new file mode 100644
index 0000000..cbf950f
Binary files /dev/null and b/img_src/spyder3.png differ
diff --git a/scripts/spyder3 b/scripts/spyder3
new file mode 100755
index 0000000..a7f92bd
--- /dev/null
+++ b/scripts/spyder3
@@ -0,0 +1,3 @@
+#! /usr/bin/python3
+from spyderlib import start_app
+start_app.main()
\ No newline at end of file
diff --git a/scripts/spyder3.desktop b/scripts/spyder3.desktop
new file mode 100644
index 0000000..57428e1
--- /dev/null
+++ b/scripts/spyder3.desktop
@@ -0,0 +1,14 @@
+[Desktop Entry]
+Version=1.0
+Type=Application
+Name=Spyder3
+GenericName=Spyder3
+Comment=Scientific PYthon Development EnviRonment - Python3
+TryExec=spyder3
+Exec=spyder3 %F
+Categories=Development;Science;IDE;Qt;
+Icon=spyder3
+Terminal=false
+StartupNotify=true
+MimeType=text/x-python;
+
diff --git a/setup.py b/setup.py
index e3efe5a..a2dbc28 100644
--- a/setup.py
+++ b/setup.py
@@ -24,6 +24,9 @@ from distutils.core import setup
 from distutils.command.build import build
 from distutils.command.install_data import install_data
 
+# Check for Python 3
+PY3 = sys.version_info[0] == 3
+
 # This is necessary to prevent an error while installing Spyder with pip
 # See http://stackoverflow.com/a/18961843/438386
 with_setuptools = False
@@ -62,8 +65,12 @@ def get_subpackages(name):
 def get_data_files():
     """Return data_files in a platform dependent manner"""
     if sys.platform.startswith('linux'):
-        data_files = [('share/applications', ['scripts/spyder.desktop']),
-                      ('share/pixmaps', ['img_src/spyder.png'])]
+        if PY3:
+            data_files = [('share/applications', ['scripts/spyder3.desktop']),
+                          ('share/pixmaps', ['img_src/spyder3.png'])]
+        else:
+            data_files = [('share/applications', ['scripts/spyder.desktop']),
+                          ('share/pixmaps', ['img_src/spyder.png'])]
     elif os.name == 'nt':
         data_files = [('scripts', ['img_src/spyder.ico',
                                    'img_src/spyder_light.ico'])]
@@ -198,8 +205,13 @@ def get_packages():
 
 # NOTE: the '[...]_win_post_install.py' script is installed even on non-Windows
 # platforms due to a bug in pip installation process (see Issue 1158)
-SCRIPTS = ['spyder', '%s_win_post_install.py' % NAME]
-EXTLIST = ['.mo', '.svg', '.png', '.css', '.html', '.js', '.chm', '.gif']
+SCRIPTS = ['%s_win_post_install.py' % NAME]
+if PY3 and sys.platform.startswith('linux'):
+    SCRIPTS.append('spyder3')
+else:
+    SCRIPTS.append('spyder')
+EXTLIST = ['.mo', '.svg', '.png', '.css', '.html', '.js', '.chm', '.gif',
+           '.ini']
 if os.name == 'nt':
     SCRIPTS += ['spyder.bat']
     EXTLIST += ['.ico']
diff --git a/spyderlib/__init__.py b/spyderlib/__init__.py
index bd7a029..ebb5469 100644
--- a/spyderlib/__init__.py
+++ b/spyderlib/__init__.py
@@ -27,7 +27,7 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 OTHER DEALINGS IN THE SOFTWARE.
 """
 
-__version__ = '2.3.0beta3'
+__version__ = '2.3.0beta4'
 __license__ = __doc__
 __project_url__ = 'http://code.google.com/p/spyderlib'
 __forum_url__   = 'http://groups.google.com/group/spyderlib'
diff --git a/spyderlib/config.py b/spyderlib/config.py
index 5626d61..c754193 100644
--- a/spyderlib/config.py
+++ b/spyderlib/config.py
@@ -17,31 +17,13 @@ import os.path as osp
 
 # Local import
 from spyderlib.userconfig import UserConfig, get_home_dir
-from spyderlib.baseconfig import SUBFOLDER, CHECK_ALL, EXCLUDED_NAMES, _
+from spyderlib.baseconfig import CHECK_ALL, EXCLUDED_NAMES, SUBFOLDER, _
 from spyderlib.utils import iofuncs, codeanalysis
 
 
-SANS_SERIF = ['Sans Serif', 'DejaVu Sans', 'Bitstream Vera Sans',
-              'Bitstream Charter', 'Lucida Grande', 'MS Shell Dlg 2',
-              'Calibri', 'Verdana', 'Geneva', 'Lucid', 'Arial',
-              'Helvetica', 'Avant Garde', 'Times', 'sans-serif']
-
-MONOSPACE = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco',
-             'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono',
-             'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal']
-
-if sys.platform == 'darwin':
-    BIG = MEDIUM = SMALL = 12
-elif os.name == 'nt':
-    BIG = 12    
-    MEDIUM = 10
-    SMALL = 9
-else:
-    BIG = 12    
-    MEDIUM = 9
-    SMALL = 9
-
+#==============================================================================
 # Extensions supported by Spyder's Editor
+#==============================================================================
 EDIT_FILETYPES = (
     (_("Python files"), ('.py', '.pyw', '.ipy')),
     (_("Cython/Pyrex files"), ('.pyx', '.pxd', '.pxi')),
@@ -51,6 +33,7 @@ EDIT_FILETYPES = (
     (_("Fortran files"), ('.f', '.for', '.f77', '.f90', '.f95', '.f2k')),
     (_("IDL files"), ('.pro', )),
     (_("MATLAB files"), ('.m', )),
+    (_("Julia files"), ('.jl',)),
     (_("Patch and diff files"), ('.patch', '.diff', '.rej')),
     (_("Batch files"), ('.bat', '.cmd')),
     (_("Text files"), ('.txt',)),
@@ -105,6 +88,7 @@ SHOW_EXT = ['.png', '.ico', '.svg']
 # Extensions supported by Spyder (Editor or Variable explorer)
 VALID_EXT = EDIT_EXT+IMPORT_EXT
 
+
 # Find in files include/exclude patterns
 INCLUDE_PATTERNS = [r'|'.join(['\\'+_ext+r'$' for _ext in EDIT_EXT if _ext])+\
                     r'|README|INSTALL',
@@ -113,14 +97,63 @@ INCLUDE_PATTERNS = [r'|'.join(['\\'+_ext+r'$' for _ext in EDIT_EXT if _ext])+\
 EXCLUDE_PATTERNS = [r'\.pyc$|\.pyo$|\.orig$|\.hg|\.svn|\bbuild\b',
                     r'\.pyc$|\.pyo$|\.orig$|\.hg|\.svn']
 
+
 # Name filters for file/project explorers (excluding files without extension)
 NAME_FILTERS = ['*' + _ext for _ext in VALID_EXT + SHOW_EXT if _ext]+\
                ['README', 'INSTALL', 'LICENSE', 'CHANGELOG']
 
+
 # Port used to detect if there is a running instance and to communicate with
 # it to open external files
 OPEN_FILES_PORT = 21128
 
+# Ctrl key
+CTRL = "Meta" if sys.platform == 'darwin' else "Ctrl"
+
+
+#==============================================================================
+# Fonts
+#==============================================================================
+def is_ubuntu():
+    if sys.platform.startswith('linux') and osp.isfile('/etc/lsb-release'):
+        release_info = open('/etc/lsb-release').read()
+        if 'Ubuntu' in release_info:
+            return True
+        else:
+            return False
+    else:
+        return False
+
+
+SANS_SERIF = ['Sans Serif', 'DejaVu Sans', 'Bitstream Vera Sans',
+              'Bitstream Charter', 'Lucida Grande', 'MS Shell Dlg 2',
+              'Calibri', 'Verdana', 'Geneva', 'Lucid', 'Arial',
+              'Helvetica', 'Avant Garde', 'Times', 'sans-serif']
+
+MONOSPACE = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco',
+             'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono',
+             'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal']
+
+
+if sys.platform == 'darwin':
+    BIG = MEDIUM = SMALL = 12
+elif os.name == 'nt':
+    BIG = 12
+    MEDIUM = 10
+    SMALL = 9
+elif is_ubuntu():
+    SANS_SERIF = ['Ubuntu'] + SANS_SERIF
+    MONOSPACE = ['Ubuntu Mono'] + MONOSPACE
+    BIG = 13
+    MEDIUM = SMALL = 10
+else:
+    BIG = 12
+    MEDIUM = SMALL = 9
+
+
+#==============================================================================
+# Defaults
+#==============================================================================
 DEFAULTS = [
             ('main',
              {
@@ -132,7 +165,7 @@ DEFAULTS = [
               'animated_docks': True,
               'window/size': (1260, 740),
               'window/position': (10, 10),
-              'window/is_maximized': False,
+              'window/is_maximized': True,
               'window/is_fullscreen': False,
               'window/prefs_dialog_size': (745, 411),
               'lightwindow/size': (650, 400),
@@ -150,6 +183,9 @@ DEFAULTS = [
               'memory_usage/timeout': 2000,
               'cpu_usage/enable': False,
               'cpu_usage/timeout': 2000,
+              'use_custom_margin': True,
+              'custom_margin': 0,
+              'show_internal_console_if_traceback': True
               }),
             ('quick_layouts',
              {
@@ -158,33 +194,16 @@ DEFAULTS = [
             ('editor_appearance',
              {
               'cursor/width': 2,
-              'calltips/font/family': MONOSPACE,
-              'calltips/font/size': SMALL,
-              'calltips/font/italic': False,
-              'calltips/font/bold': False,
-              'calltips/size': 600,
-              'completion/font/family': MONOSPACE,
-              'completion/font/size': SMALL,
-              'completion/font/italic': False,
-              'completion/font/bold': False,
               'completion/size': (300, 180),
               }),
             ('shell_appearance',
              {
               'cursor/width': 2,
-              'calltips/font/family': MONOSPACE,
-              'calltips/font/size': SMALL,
-              'calltips/font/italic': False,
-              'calltips/font/bold': False,
-              'calltips/size': 600,
-              'completion/font/family': MONOSPACE,
-              'completion/font/size': SMALL,
-              'completion/font/italic': False,
-              'completion/font/bold': False,
               'completion/size': (300, 180),
               }),
             ('internal_console',
              {
+              'shortcut': None,
               'max_line_count': 300,
               'working_dir_history': 30,
               'working_dir_adjusttocontents': False,
@@ -205,7 +224,7 @@ DEFAULTS = [
             ('console',
              {
               'shortcut': "Ctrl+Shift+C",
-              'max_line_count': 10000,
+              'max_line_count': 500,
               'font/family': MONOSPACE,
               'font/size': MEDIUM,
               'font/italic': False,
@@ -218,7 +237,7 @@ DEFAULTS = [
               'codecompletion/enter_key': True,
               'codecompletion/case_sensitive': True,
               'codecompletion/show_single': False,
-              'show_elapsed_time': True,
+              'show_elapsed_time': False,
               'show_icontext': False,
               'monitor/enabled': True,
               'qt/install_inputhook': os.name == 'nt' \
@@ -237,9 +256,16 @@ DEFAULTS = [
               'light_background': True,
               'merge_output_channels': os.name != 'nt',
               'colorize_sys_stderr': os.name != 'nt',
+              'open_python_at_startup': True,
+              'pythonstartup/default': True,
+              'pythonstartup/custom': False,
+              'pythonexecutable/default': True,
+              'pythonexecutable/custom': False,
+              'ets_backend': 'qt4'
               }),
             ('ipython_console',
              {
+              'shortcut': None,
               'font/family': MONOSPACE,
               'font/size': MEDIUM,
               'font/italic': False,
@@ -247,12 +273,12 @@ DEFAULTS = [
               'show_banner': True,
               'use_gui_completion': True,
               'use_pager': True,
-              'show_calltips': False,
+              'show_calltips': True,
               'ask_before_closing': True,
               'object_inspector': True,
-              'buffer_size': 10000,
+              'buffer_size': 500,
               'pylab': True,
-              'pylab/autoload': True,
+              'pylab/autoload': False,
               'pylab/backend': 0,
               'pylab/inline/figure_format': 0,
               'pylab/inline/resolution': 72,
@@ -261,12 +287,14 @@ DEFAULTS = [
               'startup/run_lines': '',
               'startup/use_run_file': False,
               'startup/run_file': '',
-              'open_ipython_at_startup': False,
+              'open_ipython_at_startup': True,
               'greedy_completer': False,
               'autocall': 0,
               'symbolic_math': False,
               'in_prompt': '',
-              'out_prompt': ''
+              'out_prompt': '',
+              'light_color': True,
+              'dark_color': False
               }),
             ('variable_explorer',
              {
@@ -331,6 +359,8 @@ DEFAULTS = [
               'fullpath_sorting': True,
               'show_tab_bar': True,
               'max_recent_files': 20,
+              'save_all_before_run': True,
+              'onsave_analysis': False
               }),
             ('historylog',
              {
@@ -375,6 +405,9 @@ DEFAULTS = [
              {
               'shortcut': "Ctrl+Shift+O",
               'enable': True,
+              'show_fullpath': False,
+              'show_all_files': False,
+              'show_comments': True,
               }),
             ('project_explorer',
              {
@@ -382,6 +415,7 @@ DEFAULTS = [
               'enable': True,
               'name_filters': NAME_FILTERS,
               'show_all': False,
+              'show_hscrollbar': True
               }),
             ('arrayeditor',
              {
@@ -406,6 +440,7 @@ DEFAULTS = [
               }),
             ('explorer',
              {
+              'shortcut': None,
               'enable': True,
               'wrap': True,
               'name_filters': NAME_FILTERS,
@@ -416,6 +451,7 @@ DEFAULTS = [
               }),
             ('find_in_files',
              {
+              'shortcut': None,
               'enable': True,
               'supported_encodings': ["utf-8", "iso-8859-1", "cp1252"],
               'include': INCLUDE_PATTERNS,
@@ -428,13 +464,117 @@ DEFAULTS = [
               'in_python_path': False,
               'more_options': True,
               }),
+            ('workingdir',
+             {
+              'editor/open/browse_scriptdir': True,
+              'editor/open/browse_workdir': False,
+              'editor/new/browse_scriptdir': False,
+              'editor/new/browse_workdir': True,
+              'editor/open/auto_set_to_basedir': False,
+              'editor/save/auto_set_to_basedir': False,
+              'working_dir_adjusttocontents': False,
+              'working_dir_history': 20,
+              'startup/use_last_directory': True,
+              }),
+            ('shortcuts',
+             {
+              # ---- Global ----
+              # -- In spyder.py
+              '_/close plugin': "Shift+Ctrl+F4",
+              '_/preferences': "Ctrl+Alt+Shift+P",
+              '_/maximize plugin': "Ctrl+Alt+Shift+M",
+              '_/fullscreen mode': "F11",
+              '_/quit': "Ctrl+Q",
+              '_/switch to/from layout 1': "Shift+Alt+F1",
+              '_/set layout 1': "Ctrl+Shift+Alt+F1",
+              '_/switch to/from layout 2': "Shift+Alt+F2",
+              '_/set layout 2': "Ctrl+Shift+Alt+F2",
+              '_/switch to/from layout 3': "Shift+Alt+F3",
+              '_/set layout 3': "Ctrl+Shift+Alt+F3",
+              # -- In plugins/editor
+              '_/debug step over': "Ctrl+F10",
+              '_/debug continue': "Ctrl+F12",
+              '_/debug step into': "Ctrl+F11",
+              '_/debug step return': "Ctrl+Shift+F11",
+              '_/debug exit': "Ctrl+Shift+F12",
+              # -- In plugins/init
+              '_/switch to inspector': "Ctrl+Shift+I",
+              '_/switch to outline_explorer': "Ctrl+Shift+O",
+              '_/switch to editor': "Ctrl+Shift+E",
+              '_/switch to historylog': "Ctrl+Shift+H",
+              '_/switch to onlinehelp': "Ctrl+Shift+D",
+              '_/switch to project_explorer': "Ctrl+Shift+P",
+              '_/switch to console': "Ctrl+Shift+C",
+              '_/switch to variable_explorer': "Ctrl+Shift+V",
+              # ---- Editor ----
+              # -- In codeeditor
+              'editor/code completion': CTRL+'+Space',
+              'editor/duplicate line': "Ctrl+Alt+Up" if os.name == 'nt' else \
+                                       "Shift+Alt+Up",
+              'editor/copy line': "Ctrl+Alt+Down" if os.name == 'nt' else \
+                                  "Shift+Alt+Down",
+              'editor/delete line': 'Ctrl+D',
+              'editor/move line up': "Alt+Up",
+              'editor/move line down': "Alt+Down",
+              'editor/go to definition': "Ctrl+G",
+              'editor/toggle comment': "Ctrl+1",
+              'editor/blockcomment': "Ctrl+4",
+              'editor/unblockcomment': "Ctrl+5",
+              # -- In widgets/editor
+              'editor/inspect current object': 'Ctrl+I',
+              'editor/go to line': 'Ctrl+L',
+              'editor/file list management': 'Ctrl+E',
+              'editor/go to previous file': 'Ctrl+Tab',
+              'editor/go to next file': 'Ctrl+Shift+Tab',
+              # -- In spyder.py
+              'editor/find text': "Ctrl+F",
+              'editor/find next': "F3",
+              'editor/find previous': "Shift+F3",
+              'editor/replace text': "Ctrl+H",
+              # -- In plugins/editor
+              'editor/show/hide outline': "Ctrl+Alt+O",
+              'editor/show/hide project explorer': "Ctrl+Alt+P",
+              'editor/new file': "Ctrl+N",
+              'editor/open file': "Ctrl+O",
+              'editor/save file': "Ctrl+S",
+              'editor/save all': "Ctrl+Shift+S",
+              'editor/print': "Ctrl+P",
+              'editor/close file': "Ctrl+W",
+              'editor/close all': "Ctrl+Shift+W",
+              'editor/breakpoint': 'F12',
+              'editor/conditional breakpoint': 'Shift+F12',
+              'editor/debug with winpdb': "F7",
+              'editor/debug': "Ctrl+F5",
+              'editor/run': "F5",
+              'editor/configure': "F6",
+              'editor/re-run last script': "Ctrl+F6",
+              'editor/run selection': "F9",
+              'editor/last edit location': "Ctrl+Alt+Shift+Left",
+              'editor/previous cursor position': "Ctrl+Alt+Left",
+              'editor/next cursor position': "Ctrl+Alt+Right",
+              # -- In p_breakpoints
+              'editor/list breakpoints': "Ctrl+B",
+              # ---- Console (in widgets/shell) ----
+              'console/inspect current object': "Ctrl+I",
+              'console/clear shell': "Ctrl+L",
+              'console/clear line': "Shift+Escape",
+              # ---- Pylint (in p_pylint) ----
+              'pylint/run analysis': "F8",
+              # ---- Profiler (in p_profiler) ----
+              'profiler/run profiler': "F10"
+              })
             ]
 
+
+#==============================================================================
+# Config instance
+#==============================================================================
 # XXX: Previously we had load=(not DEV) here but DEV was set to *False*.
 # Check if it *really* needs to be updated or not
-CONF = UserConfig('spyder', defaults=DEFAULTS, load=True, version='2.4.0',
+CONF = UserConfig('spyder', defaults=DEFAULTS, load=True, version='3.0.0',
                   subfolder=SUBFOLDER, backup=True, raw_mode=True)
 
+
 # Removing old .spyder.ini location:
 old_location = osp.join(get_home_dir(), '.spyder.ini')
 if osp.isfile(old_location):
diff --git a/spyderlib/guiconfig.py b/spyderlib/guiconfig.py
index 56c202f..530a2dc 100644
--- a/spyderlib/guiconfig.py
+++ b/spyderlib/guiconfig.py
@@ -14,7 +14,10 @@ Important note regarding shortcuts:
         Ctrl + Alt + Q, W, F, G, Y, X, C, V, B, N
 """
 
-from spyderlib.qt.QtGui import QFont, QFontDatabase
+from collections import namedtuple
+
+from spyderlib.qt.QtGui import QFont, QFontDatabase, QShortcut, QKeySequence
+from spyderlib.qt.QtCore import Qt
 
 from spyderlib.config import CONF
 from spyderlib.userconfig import NoDefault
@@ -22,11 +25,17 @@ from spyderlib.widgets.sourcecode import syntaxhighlighters as sh
 from spyderlib.py3compat import to_text_string
 
 
+# To save metadata about widget shortcuts (needed to build our
+# preferences page)
+Shortcut = namedtuple('Shortcut', 'data')
+
+
 def font_is_installed(font):
     """Check if font is installed"""
     return [fam for fam in QFontDatabase().families()
             if to_text_string(fam)==font]
-    
+
+
 def get_family(families):
     """Return the first installed font family in family list"""
     if not isinstance(families, list):
@@ -37,7 +46,8 @@ def get_family(families):
     else:
         print("Warning: None of the following fonts is installed: %r" % families)
         return QFont().family()
-    
+
+
 FONT_CACHE = {}
 def get_font(section, option=None):
     """Get console font properties depending on OS and user options"""
@@ -61,6 +71,7 @@ def get_font(section, option=None):
         FONT_CACHE[(section, option)] = font
     return font
 
+
 def set_font(font, section, option=None):
     """Set font"""
     if option is None:
@@ -78,16 +89,28 @@ def get_shortcut(context, name, default=NoDefault):
     """Get keyboard shortcut (key sequence string)"""
     return CONF.get('shortcuts', '%s/%s' % (context, name), default=default)
 
+
 def set_shortcut(context, name, keystr):
     """Set keyboard shortcut (key sequence string)"""
     CONF.set('shortcuts', '%s/%s' % (context, name), keystr)
-    
+
+
+def create_shortcut(action, context, name, parent):
+    """Creates a QShortcut for a widget and returns its associated data"""
+    keystr = get_shortcut(context, name)
+    qsc = QShortcut(QKeySequence(keystr), parent, action)
+    qsc.setContext(Qt.WidgetWithChildrenShortcut)
+    sc = Shortcut(data=(qsc, name, keystr))
+    return sc
+
+
 def iter_shortcuts():
     """Iterate over keyboard shortcuts"""
     for option in CONF.options('shortcuts'):
         context, name = option.split("/", 1)
         yield context, name, get_shortcut(context, name)
 
+
 def remove_deprecated_shortcuts(data):
     """Remove deprecated shortcuts (shortcuts in CONF but not registered)"""
     section = 'shortcuts'
@@ -98,10 +121,12 @@ def remove_deprecated_shortcuts(data):
             if len(CONF.items(section, raw=CONF.raw)) == 0:
                 CONF.remove_section(section)
 
+
 def reset_shortcuts():
     """Reset keyboard shortcuts to default values"""
     CONF.remove_section('shortcuts')
 
+
 def get_color_scheme(name):
     """Get syntax color scheme"""
     color_scheme = {}
@@ -109,6 +134,7 @@ def get_color_scheme(name):
         color_scheme[key] = CONF.get("color_schemes", "%s/%s" % (name, key))
     return color_scheme
 
+
 def set_color_scheme(name, color_scheme, replace=True):
     """Set syntax color scheme"""
     section = "color_schemes"
@@ -121,11 +147,13 @@ def set_color_scheme(name, color_scheme, replace=True):
     names.append(to_text_string(name))
     CONF.set(section, "names", sorted(list(set(names))))
 
+
 def set_default_color_scheme(name, replace=True):
     """Reset color scheme to default values"""
     assert name in sh.COLOR_SCHEME_NAMES
     set_color_scheme(name, sh.COLORS[name], replace=replace)
 
+
 for _name in sh.COLOR_SCHEME_NAMES:
     set_default_color_scheme(_name, replace=False)
 CUSTOM_COLOR_SCHEME_NAME = "Custom"
diff --git a/spyderlib/images/filetypes/enaml.png b/spyderlib/images/filetypes/enaml.png
new file mode 100644
index 0000000..55510d3
Binary files /dev/null and b/spyderlib/images/filetypes/enaml.png differ
diff --git a/spyderlib/images/filetypes/jl.png b/spyderlib/images/filetypes/jl.png
new file mode 100644
index 0000000..a8b30f7
Binary files /dev/null and b/spyderlib/images/filetypes/jl.png differ
diff --git a/spyderlib/plugins/__init__.py b/spyderlib/plugins/__init__.py
index 99d81eb..945211a 100644
--- a/spyderlib/plugins/__init__.py
+++ b/spyderlib/plugins/__init__.py
@@ -29,7 +29,7 @@ from spyderlib.config import CONF
 from spyderlib.userconfig import NoDefault
 from spyderlib.guiconfig import get_font, set_font
 from spyderlib.plugins.configdialog import SpyderConfigPage
-from spyderlib.py3compat import is_text_string
+from spyderlib.py3compat import configparser, is_text_string
 
 
 class PluginConfigPage(SpyderConfigPage):
@@ -121,8 +121,8 @@ class SpyderPluginMixin(object):
         layout = self.layout()
         if self.default_margins is None:
             self.default_margins = layout.getContentsMargins()
-        if CONF.get('main', 'use_custom_margin', True):
-            margin = CONF.get('main', 'custom_margin', 0)
+        if CONF.get('main', 'use_custom_margin'):
+            margin = CONF.get('main', 'custom_margin')
             layout.setContentsMargins(*[margin]*4)
         else:
             layout.setContentsMargins(*self.default_margins)
@@ -158,13 +158,15 @@ class SpyderPluginMixin(object):
         self.connect(dock, SIGNAL('visibilityChanged(bool)'),
                      self.visibility_changed)
         self.dockwidget = dock
-        short = self.get_option("shortcut", None)
+        try:
+            short = CONF.get('shortcuts', '_/switch to %s' % self.CONF_SECTION)
+        except configparser.NoOptionError:
+            short = None
         if short is not None:
             shortcut = QShortcut(QKeySequence(short),
                                  self.main, self.switch_to_plugin)
             self.register_shortcut(shortcut, "_",
-                                   "Switch to %s" % self.CONF_SECTION,
-                                   default=short)
+                                   "Switch to %s" % self.CONF_SECTION)
         return (dock, self.LOCATION)
     
     def create_mainwindow(self):
diff --git a/spyderlib/plugins/configdialog.py b/spyderlib/plugins/configdialog.py
index fc8fa89..89c4862 100644
--- a/spyderlib/plugins/configdialog.py
+++ b/spyderlib/plugins/configdialog.py
@@ -734,8 +734,7 @@ class MainConfigPage(GeneralConfigPage):
         debug_group = QGroupBox(_("Debugging"))
         popup_console_box = newcb(_("Pop up internal console when internal "
                                     "errors appear"),
-                                  'show_internal_console_if_traceback',
-                                  default=True)
+                                  'show_internal_console_if_traceback')
         
         debug_layout = QVBoxLayout()
         debug_layout.addWidget(popup_console_box)
diff --git a/spyderlib/plugins/editor.py b/spyderlib/plugins/editor.py
index 987cdca..1641eec 100644
--- a/spyderlib/plugins/editor.py
+++ b/spyderlib/plugins/editor.py
@@ -148,7 +148,7 @@ class EditorConfigPage(PluginConfigPage):
 
         run_group = QGroupBox(_("Run"))
         saveall_box = newcb(_("Save all files before running script"),
-                            'save_all_before_run', True)
+                            'save_all_before_run')
         
         introspection_group = QGroupBox(_("Introspection"))
         rope_is_installed = programs.is_module_installed('rope')
@@ -234,7 +234,7 @@ class EditorConfigPage(PluginConfigPage):
         ancb_layout = QHBoxLayout()
         ancb_layout.addWidget(pyflakes_box)
         ancb_layout.addWidget(pep8_box)
-        todolist_box = newcb(_("Tasks (TODO, FIXME, XXX, HINT, TIP)"),
+        todolist_box = newcb(_("Tasks (TODO, FIXME, XXX, HINT, TIP, @todo)"),
                              'todo_list', default=True)
         realtime_radio = self.create_radiobutton(
                                             _("Perform analysis when "
@@ -243,7 +243,7 @@ class EditorConfigPage(PluginConfigPage):
         saveonly_radio = self.create_radiobutton(
                                             _("Perform analysis only "
                                                     "when saving file"),
-                                            'onsave_analysis', False)
+                                            'onsave_analysis')
         af_spin = self.create_spinbox("", " ms", 'realtime_analysis/timeout',
                                       min_=100, max_=1000000, step=100)
         af_layout = QHBoxLayout()
@@ -562,14 +562,13 @@ class Editor(SpyderPluginWidget):
                                 triggered=self.show_hide_outline_explorer,
                                 context=Qt.WidgetWithChildrenShortcut)
         self.register_shortcut(self.toggle_outline_action, context="Editor",
-                               name="Show/hide outline", default="Ctrl+Alt+O")
+                               name="Show/hide outline")
         self.toggle_project_action = create_action(self,
                                 _("Show/hide project explorer"),
                                 triggered=self.show_hide_project_explorer,
                                 context=Qt.WidgetWithChildrenShortcut)
         self.register_shortcut(self.toggle_project_action, context="Editor",
-                               name="Show/hide project explorer",
-                               default="Ctrl+Alt+P")
+                               name="Show/hide project explorer")
         self.addActions([self.toggle_outline_action, self.toggle_project_action])
         
         # ---- File menu and toolbar ----
@@ -577,7 +576,7 @@ class Editor(SpyderPluginWidget):
                 icon='filenew.png', tip=_("New file"),
                 triggered=self.new)
         self.register_shortcut(self.new_action, context="Editor",
-                               name="New file", default="Ctrl+N")
+                               name="New file")
         add_shortcut_to_tooltip(self.new_action, context="Editor",
                                 name="New file")
         
@@ -585,7 +584,7 @@ class Editor(SpyderPluginWidget):
                 icon='fileopen.png', tip=_("Open file"),
                 triggered=self.load)
         self.register_shortcut(self.open_action, context="Editor",
-                               name="Open file", default="Ctrl+O")
+                               name="Open file")
         add_shortcut_to_tooltip(self.open_action, context="Editor",
                                 name="Open file")
         
@@ -597,7 +596,7 @@ class Editor(SpyderPluginWidget):
                 icon='filesave.png', tip=_("Save file"),
                 triggered=self.save)
         self.register_shortcut(self.save_action, context="Editor",
-                               name="Save file", default="Ctrl+S")
+                               name="Save file")
         add_shortcut_to_tooltip(self.save_action, context="Editor",
                                 name="Save file")
         
@@ -605,7 +604,7 @@ class Editor(SpyderPluginWidget):
                 icon='save_all.png', tip=_("Save all files"),
                 triggered=self.save_all)
         self.register_shortcut(self.save_all_action, context="Editor",
-                               name="Save all", default="Ctrl+Shift+S")
+                               name="Save all")
         add_shortcut_to_tooltip(self.save_all_action, context="Editor",
                                 name="Save all")
         
@@ -618,17 +617,17 @@ class Editor(SpyderPluginWidget):
                 icon='print.png', tip=_("Print current file..."),
                 triggered=self.print_file)
         self.register_shortcut(self.print_action, context="Editor",
-        	name="Print", default="Ctrl+P")
+                               name="Print")
         self.close_action = create_action(self, _("&Close"),
                 icon='fileclose.png', tip=_("Close current file"),
                 triggered=self.close_file)
         self.register_shortcut(self.close_action, context="Editor",
-                               name="Close file", default="Ctrl+W")
+                               name="Close file")
         self.close_all_action = create_action(self, _("C&lose all"),
                 icon='filecloseall.png', tip=_("Close all opened files"),
                 triggered=self.close_all_files)
         self.register_shortcut(self.close_all_action, context="Editor",
-                               name="Close all", default="Ctrl+Shift+W")
+                               name="Close all")
 
         # ---- Debug menu ----
         set_clear_breakpoint_action = create_action(self,
@@ -637,15 +636,14 @@ class Editor(SpyderPluginWidget):
                                     triggered=self.set_or_clear_breakpoint,
                                     context=Qt.WidgetShortcut)
         self.register_shortcut(set_clear_breakpoint_action, context="Editor",
-                               name="Breakpoint", default="F12")
+                               name="Breakpoint")
         set_cond_breakpoint_action = create_action(self,
                             _("Set/Edit conditional breakpoint"),
                             icon=get_icon("breakpoint_cond_big.png"),
                             triggered=self.set_or_edit_conditional_breakpoint,
                             context=Qt.WidgetShortcut)
         self.register_shortcut(set_cond_breakpoint_action, context="Editor",
-                               name="Conditional breakpoint",
-                               default="Shift+F12")
+                               name="Conditional breakpoint")
         clear_all_breakpoints_action = create_action(self,
                                     _("Clear breakpoints in all files"),
                                     triggered=self.clear_all_breakpoints)
@@ -657,21 +655,19 @@ class Editor(SpyderPluginWidget):
                                            triggered=self.run_winpdb)
         self.winpdb_action.setEnabled(WINPDB_PATH is not None)
         self.register_shortcut(self.winpdb_action, context="Editor",
-                               name="Debug with winpdb", default="F7")
+                               name="Debug with winpdb")
         
         # --- Debug toolbar ---
         debug_action = create_action(self, _("&Debug"), icon='debug.png',
                                      tip=_("Debug file"),
                                      triggered=self.debug_file)
-        self.register_shortcut(debug_action, context="Editor",
-                               name="Debug", default="Ctrl+F5")
+        self.register_shortcut(debug_action, context="Editor", name="Debug")
         add_shortcut_to_tooltip(debug_action, context="Editor", name="Debug")
         
         debug_next_action = create_action(self, _("Step"), 
                icon='arrow-step-over.png', tip=_("Run current line"), 
                triggered=lambda: self.debug_command("next")) 
-        self.register_shortcut(debug_next_action, "_",
-                   "Debug Step Over", "Ctrl+F10")
+        self.register_shortcut(debug_next_action, "_", "Debug Step Over")
         add_shortcut_to_tooltip(debug_next_action, context="_",
                                 name="Debug Step Over")
 
@@ -679,8 +675,7 @@ class Editor(SpyderPluginWidget):
                icon='arrow-continue.png', tip=_("Continue execution until "
                                                 "next breakpoint"), 
                triggered=lambda: self.debug_command("continue"))                                                 
-        self.register_shortcut(debug_continue_action, "_",
-                   "Debug Continue", "Ctrl+F12")
+        self.register_shortcut(debug_continue_action, "_", "Debug Continue")
         add_shortcut_to_tooltip(debug_continue_action, context="_",
                                 name="Debug Continue")
 
@@ -688,8 +683,7 @@ class Editor(SpyderPluginWidget):
                icon='arrow-step-in.png', tip=_("Step into function or method "
                                                "of current line"), 
                triggered=lambda: self.debug_command("step"))                
-        self.register_shortcut(debug_step_action, "_",
-                   "Debug Step Into", "Ctrl+F11")
+        self.register_shortcut(debug_step_action, "_", "Debug Step Into")
         add_shortcut_to_tooltip(debug_step_action, context="_",
                                 name="Debug Step Into")
 
@@ -697,16 +691,14 @@ class Editor(SpyderPluginWidget):
                icon='arrow-step-out.png', tip=_("Run until current function "
                                                 "or method returns"), 
                triggered=lambda: self.debug_command("return"))               
-        self.register_shortcut(debug_return_action, "_",
-                   "Debug Step Return", "Ctrl+Shift+F11")
+        self.register_shortcut(debug_return_action, "_", "Debug Step Return")
         add_shortcut_to_tooltip(debug_return_action, context="_",
                                 name="Debug Step Return")
 
         debug_exit_action = create_action(self, _("Exit"),
                icon='stop_debug.png', tip=_("Exit Debug"), 
                triggered=lambda: self.debug_command("exit"))                                       
-        self.register_shortcut(debug_exit_action, "_",
-                   "Debug Exit", "Ctrl+Shift+F12")
+        self.register_shortcut(debug_exit_action, "_", "Debug Exit")
         add_shortcut_to_tooltip(debug_exit_action, context="_",
                                 name="Debug Exit")
 
@@ -722,8 +714,7 @@ class Editor(SpyderPluginWidget):
         run_action = create_action(self, _("&Run"), icon='run.png',
                                    tip=_("Run file"),
                                    triggered=self.run_file)
-        self.register_shortcut(run_action, context="Editor",
-                               name="Run", default="F5")
+        self.register_shortcut(run_action, context="Editor", name="Run")
         add_shortcut_to_tooltip(run_action, context="Editor", name="Run")
 
         configure_action = create_action(self,
@@ -732,7 +723,7 @@ class Editor(SpyderPluginWidget):
                                menurole=QAction.NoRole,
                                triggered=self.edit_run_configurations)
         self.register_shortcut(configure_action, context="Editor",
-                               name="Configure", default="F6")
+                               name="Configure")
         add_shortcut_to_tooltip(configure_action, context="Editor",
                                 name="Configure")
         
@@ -741,7 +732,7 @@ class Editor(SpyderPluginWidget):
                             tip=_("Run again last file"),
                             triggered=self.re_run_file)
         self.register_shortcut(re_run_action, context="Editor",
-                               name="Re-run last script", default="Ctrl+F6")
+                               name="Re-run last script")
         add_shortcut_to_tooltip(re_run_action, context="Editor",
                                 name="Re-run last script")
 
@@ -750,7 +741,7 @@ class Editor(SpyderPluginWidget):
                                             tip=_("Run selection"),
                                             triggered=self.run_selection)
         self.register_shortcut(run_selected_action, context="Editor",
-                               name="Run selection", default="F9")
+                               name="Run selection")
 
         run_cell_action = create_action(self,
                             _("Run cell"), icon='run_cell.png',
@@ -770,7 +761,7 @@ class Editor(SpyderPluginWidget):
         # --- Source code Toolbar ---
         self.todo_list_action = create_action(self,
                 _("Show todo list"), icon='todo_list.png',
-                tip=_("Show TODO/FIXME/XXX/HINT/TIP comments list"),
+                tip=_("Show TODO/FIXME/XXX/HINT/TIP/@todo comments list"),
                 triggered=self.go_to_next_todo)
         self.todo_menu = QMenu(self)
         self.todo_list_action.setMenu(self.todo_menu)
@@ -800,23 +791,20 @@ class Editor(SpyderPluginWidget):
                 triggered=self.go_to_last_edit_location)
         self.register_shortcut(self.previous_edit_cursor_action,
                                context="Editor",
-                               name="Last edit location",
-                               default="Ctrl+Alt+Shift+Left")
+                               name="Last edit location")
         self.previous_cursor_action = create_action(self,
                 _("Previous cursor position"), icon='prev_cursor.png',
                 tip=_("Go to previous cursor position"),
                 triggered=self.go_to_previous_cursor_position)
         self.register_shortcut(self.previous_cursor_action,
                                context="Editor",
-                               name="Previous cursor position",
-                               default="Ctrl+Alt+Left")
+                               name="Previous cursor position")
         self.next_cursor_action = create_action(self,
                 _("Next cursor position"), icon='next_cursor.png',
                 tip=_("Go to next cursor position"),
                 triggered=self.go_to_next_cursor_position)
         self.register_shortcut(self.next_cursor_action,
-                               context="Editor", name="Next cursor position",
-                               default="Ctrl+Alt+Right")
+                               context="Editor", name="Next cursor position")
         
         # --- Edit Toolbar ---
         self.toggle_comment_action = create_action(self,
@@ -824,20 +812,20 @@ class Editor(SpyderPluginWidget):
                 tip=_("Comment current line or selection"),
                 triggered=self.toggle_comment, context=Qt.WidgetShortcut)
         self.register_shortcut(self.toggle_comment_action, context="Editor",
-                               name="Toggle comment", default="Ctrl+1")
+                               name="Toggle comment")
         blockcomment_action = create_action(self, _("Add &block comment"),
                 tip=_("Add block comment around "
                             "current line or selection"),
                 triggered=self.blockcomment, context=Qt.WidgetShortcut)
         self.register_shortcut(blockcomment_action, context="Editor",
-                               name="Blockcomment", default="Ctrl+4")
+                               name="Blockcomment")
         unblockcomment_action = create_action(self,
                 _("R&emove block comment"),
                 tip = _("Remove comment block around "
                               "current line or selection"),
                 triggered=self.unblockcomment, context=Qt.WidgetShortcut)
         self.register_shortcut(unblockcomment_action, context="Editor",
-                               name="Unblockcomment", default="Ctrl+5")
+                               name="Unblockcomment")
                 
         # ----------------------------------------------------------------------
         # The following action shortcuts are hard-coded in CodeEditor
@@ -881,7 +869,7 @@ class Editor(SpyderPluginWidget):
                                         triggered=self.go_to_line,
                                         context=Qt.WidgetShortcut)
         self.register_shortcut(gotoline_action, context="Editor",
-                               name="Go to line", default="Ctrl+L")
+                               name="Go to line")
 
         workdir_action = create_action(self,
                 _("Set console working directory"),
@@ -2062,7 +2050,7 @@ class Editor(SpyderPluginWidget):
         
     def re_run_file(self):
         """Re-run last script"""
-        if self.get_option('save_all_before_run', True):
+        if self.get_option('save_all_before_run'):
             self.save_all()
         if self.__last_ec_exec is None:
             return
@@ -2135,6 +2123,11 @@ class Editor(SpyderPluginWidget):
                 if font_n in options:
                     scs = color_scheme_o if color_scheme_n in options else None
                     editorstack.set_default_font(font_o, scs)
+                    completion_size = CONF.get('editor_appearance',
+                                               'completion/size')
+                    for finfo in editorstack.data:
+                        comp_widget = finfo.editor.completion_widget
+                        comp_widget.setup_appearance(completion_size, font_o)
                 elif color_scheme_n in options:
                     editorstack.set_color_scheme(color_scheme_o)
                 if currentline_n in options:
@@ -2145,6 +2138,7 @@ class Editor(SpyderPluginWidget):
                 if occurence_timeout_n in options:
                     editorstack.set_occurence_highlighting_timeout(
                                                         occurence_timeout_o)
+
             # --- everything else
             fpsorting_n = 'fullpath_sorting'
             fpsorting_o = self.get_option(fpsorting_n)
diff --git a/spyderlib/plugins/externalconsole.py b/spyderlib/plugins/externalconsole.py
index ae58f62..3f06ae2 100644
--- a/spyderlib/plugins/externalconsole.py
+++ b/spyderlib/plugins/externalconsole.py
@@ -27,7 +27,7 @@ import re
 import sys
 
 # Local imports
-from spyderlib.baseconfig import _, SCIENTIFIC_STARTUP
+from spyderlib.baseconfig import SCIENTIFIC_STARTUP, _
 from spyderlib.config import CONF
 from spyderlib.utils import programs
 from spyderlib.utils.misc import (get_error_match, get_python_executable,
@@ -196,11 +196,11 @@ class ExternalConsoleConfigPage(PluginConfigPage):
                                 "binary in which Spyder will run scripts:"))
         def_exec_radio = self.create_radiobutton(
                                 _("Default (i.e. the same as Spyder's)"),
-                                'pythonexecutable/default', True,
+                                'pythonexecutable/default', 
                                 button_group=pyexec_bg)
         self.cus_exec_radio = self.create_radiobutton(
                                 _("Use the following Python interpreter:"),
-                                'pythonexecutable/custom', False,
+                                'pythonexecutable/custom',
                                 button_group=pyexec_bg)
         if os.name == 'nt':
             filters = _("Executables")+" (*.exe)"
@@ -225,7 +225,7 @@ class ExternalConsoleConfigPage(PluginConfigPage):
         # Startup Group
         startup_group = QGroupBox(_("Startup"))
         pystartup_box = newcb(_("Open a Python interpreter at startup"),
-                              'open_python_at_startup', True)
+                              'open_python_at_startup')
         
         startup_layout = QVBoxLayout()
         startup_layout.addWidget(pystartup_box)
@@ -240,11 +240,11 @@ class ExternalConsoleConfigPage(PluginConfigPage):
                                    "the Python interpreter startup."))
         self.def_startup_radio = self.create_radiobutton(
                                         _("Default PYTHONSTARTUP script"),
-                                        'pythonstartup/default', True,
+                                        'pythonstartup/default',
                                         button_group=pystartup_bg)
         self.cus_startup_radio = self.create_radiobutton(
                                         _("Use the following startup script:"),
-                                        'pythonstartup/custom', False,
+                                        'pythonstartup/custom',
                                         button_group=pystartup_bg)
         pystartup_file = self.create_browsefile('', 'pythonstartup', '',
                                                 filters=_("Python scripts")+\
@@ -291,7 +291,7 @@ class ExternalConsoleConfigPage(PluginConfigPage):
                          tip=_("This option will act on<br> "
                                "libraries such as Matplotlib, guidata "
                                "or ETS"))
-        if self.get_option('pythonexecutable/default', True):
+        if self.get_option('pythonexecutable/default'):
             interpreter = get_python_executable()
         else:
             interpreter = self.get_option('pythonexecutable')
@@ -398,7 +398,7 @@ class ExternalConsoleConfigPage(PluginConfigPage):
                              "user interfaces."))
         ets_label.setWordWrap(True)
         ets_edit = self.create_lineedit(_("ETS_TOOLKIT:"), 'ets_backend',
-                                        default='qt4', alignment=Qt.Horizontal)
+                                        alignment=Qt.Horizontal)
         
         ets_layout = QVBoxLayout()
         ets_layout.addWidget(ets_label)
@@ -489,15 +489,15 @@ class ExternalConsole(SpyderPluginWidget):
         # Python executable selection (initializing default values as well)
         executable = self.get_option('pythonexecutable',
                                      get_python_executable())
-        if self.get_option('pythonexecutable/default', True):
+        if self.get_option('pythonexecutable/default'):
             executable = get_python_executable()
-
+        
         # Python startup file selection
         if not osp.isfile(self.get_option('pythonstartup', '')):
             self.set_option('pythonstartup', SCIENTIFIC_STARTUP)
         # default/custom settings are mutually exclusive:
         self.set_option('pythonstartup/custom',
-                        not self.get_option('pythonstartup/default', False))
+                        not self.get_option('pythonstartup/default'))
         
         if not osp.isfile(executable):
             # This is absolutely necessary, in case the Python interpreter
@@ -780,11 +780,11 @@ class ExternalConsole(SpyderPluginWidget):
         light_background = self.get_option('light_background')
         show_elapsed_time = self.get_option('show_elapsed_time')
         if python:
-            if self.get_option('pythonexecutable/default', True):
+            if self.get_option('pythonexecutable/default'):
                 pythonexecutable = get_python_executable()
             else:
                 pythonexecutable = self.get_option('pythonexecutable')
-            if self.get_option('pythonstartup/default', True) or ipykernel:
+            if self.get_option('pythonstartup/default') or ipykernel:
                 pythonstartup = None
             else:
                 pythonstartup = self.get_option('pythonstartup', None)
@@ -795,7 +795,7 @@ class ExternalConsole(SpyderPluginWidget):
                 mpl_backend = self.get_option('matplotlib/backend/value')
             else:
                 mpl_backend = None
-            ets_backend = self.get_option('ets_backend', 'qt4')
+            ets_backend = self.get_option('ets_backend')
             qt_api = self.get_option('qt/api')
             if qt_api not in ('pyqt', 'pyside'):
                 qt_api = None
@@ -914,7 +914,7 @@ class ExternalConsole(SpyderPluginWidget):
                               lambda error: ipyclient.show_kernel_error(error))
                     
                     # Detect if kernel and frontend match or not
-                    if self.get_option('pythonexecutable/custom', False):
+                    if self.get_option('pythonexecutable/custom'):
                         frontend_ver = programs.get_module_version('IPython')
                         if '0.13' in frontend_ver:
                             frontend_ver = '<1.0'
@@ -1228,6 +1228,10 @@ class ExternalConsole(SpyderPluginWidget):
         for shellwidget in self.shellwidgets:
             if font_n in options:
                 shellwidget.shell.set_font(font_o)
+                completion_size = CONF.get('shell_appearance',
+                                           'completion/size')
+                comp_widget = shellwidget.shell.completion_widget
+                comp_widget.setup_appearance(completion_size, font_o)
             if showtime_n in options:
                 shellwidget.set_elapsed_time_visible(showtime_o)
             if icontext_n in options:
@@ -1253,7 +1257,7 @@ class ExternalConsole(SpyderPluginWidget):
     #------ Public API ---------------------------------------------------------
     def open_interpreter_at_startup(self):
         """Open an interpreter or an IPython kernel at startup"""
-        if self.get_option('open_python_at_startup', True):
+        if self.get_option('open_python_at_startup'):
             self.open_interpreter()
 
     def open_interpreter(self, wdir=None):
diff --git a/spyderlib/plugins/ipythonconsole.py b/spyderlib/plugins/ipythonconsole.py
index ed3bffc..518ad26 100644
--- a/spyderlib/plugins/ipythonconsole.py
+++ b/spyderlib/plugins/ipythonconsole.py
@@ -96,9 +96,9 @@ class IPythonConsoleConfigPage(PluginConfigPage):
         # Background Color Group
         bg_group = QGroupBox(_("Background color"))
         light_radio = self.create_radiobutton(_("Light background"),
-                                              'light_color', True)
+                                              'light_color')
         dark_radio = self.create_radiobutton(_("Dark background"),
-                                             'dark_color', False)
+                                             'dark_color')
         bg_layout = QVBoxLayout()
         bg_layout.addWidget(light_radio)
         bg_layout.addWidget(dark_radio)
diff --git a/spyderlib/plugins/outlineexplorer.py b/spyderlib/plugins/outlineexplorer.py
index 2cd1db7..98f03ce 100644
--- a/spyderlib/plugins/outlineexplorer.py
+++ b/spyderlib/plugins/outlineexplorer.py
@@ -25,9 +25,9 @@ class OutlineExplorer(OutlineExplorerWidget, SpyderPluginMixin):
     CONF_SECTION = 'outline_explorer'
     sig_option_changed = Signal(str, object)
     def __init__(self, parent=None, fullpath_sorting=True):
-        show_fullpath = self.get_option('show_fullpath', False)
-        show_all_files = self.get_option('show_all_files', False)
-        show_comments = self.get_option('show_comments', True)
+        show_fullpath = self.get_option('show_fullpath')
+        show_all_files = self.get_option('show_all_files')
+        show_comments = self.get_option('show_comments')
         OutlineExplorerWidget.__init__(self, parent=parent,
                                        show_fullpath=show_fullpath,
                                        fullpath_sorting=fullpath_sorting,
diff --git a/spyderlib/plugins/projectexplorer.py b/spyderlib/plugins/projectexplorer.py
index 396cea2..4014846 100644
--- a/spyderlib/plugins/projectexplorer.py
+++ b/spyderlib/plugins/projectexplorer.py
@@ -27,7 +27,7 @@ class ProjectExplorer(ProjectExplorerWidget, SpyderPluginMixin):
                     name_filters=self.get_option('name_filters'),
                     valid_types=VALID_EXT,
                     show_all=self.get_option('show_all', False),
-                    show_hscrollbar=self.get_option('show_hscrollbar', True))
+                    show_hscrollbar=self.get_option('show_hscrollbar'))
         SpyderPluginMixin.__init__(self, parent)
 
         # Initialize plugin
diff --git a/spyderlib/plugins/workingdirectory.py b/spyderlib/plugins/workingdirectory.py
index bd5b510..5d580cf 100644
--- a/spyderlib/plugins/workingdirectory.py
+++ b/spyderlib/plugins/workingdirectory.py
@@ -72,11 +72,11 @@ class WorkingDirectoryConfigPage(PluginConfigPage):
         editor_o_bg = QButtonGroup(editor_o_group)
         editor_o_radio1 = self.create_radiobutton(
                                 _("the current file directory"),
-                                'editor/open/browse_scriptdir', True,
+                                'editor/open/browse_scriptdir',
                                 button_group=editor_o_bg)
         editor_o_radio2 = self.create_radiobutton(
                                 _("the global working directory"),
-                                'editor/open/browse_workdir', False,
+                                'editor/open/browse_workdir', 
                                 button_group=editor_o_bg)
         
         editor_n_group = QGroupBox(_("New file"))
@@ -85,11 +85,11 @@ class WorkingDirectoryConfigPage(PluginConfigPage):
         editor_n_bg = QButtonGroup(editor_n_group)
         editor_n_radio1 = self.create_radiobutton(
                                 _("the current file directory"),
-                                'editor/new/browse_scriptdir', False,
+                                'editor/new/browse_scriptdir',
                                 button_group=editor_n_bg)
         editor_n_radio2 = self.create_radiobutton(
                                 _("the global working directory"),
-                                'editor/new/browse_workdir', True,
+                                'editor/new/browse_workdir',
                                 button_group=editor_n_bg)
         # Note: default values for the options above are set in plugin's
         #       constructor (see below)
@@ -97,9 +97,9 @@ class WorkingDirectoryConfigPage(PluginConfigPage):
         other_group = QGroupBox(_("Change to file base directory"))
         newcb = self.create_checkbox
         open_box = newcb(_("When opening a file"),
-                         'editor/open/auto_set_to_basedir', False)
+                         'editor/open/auto_set_to_basedir')
         save_box = newcb(_("When saving a file"),
-                         'editor/save/auto_set_to_basedir', False)
+                         'editor/save/auto_set_to_basedir')
         
         startup_layout = QVBoxLayout()
         startup_layout.addWidget(startup_label)
@@ -150,14 +150,6 @@ class WorkingDirectory(QToolBar, SpyderPluginMixin):
         # Initialize plugin
         self.initialize_plugin()
         
-        # Setting default values for editor-related options
-        self.get_option('editor/open/browse_scriptdir', True)
-        self.get_option('editor/open/browse_workdir', False)
-        self.get_option('editor/new/browse_scriptdir', False)
-        self.get_option('editor/new/browse_workdir', True)
-        self.get_option('editor/open/auto_set_to_basedir', False)
-        self.get_option('editor/save/auto_set_to_basedir', False)
-        
         self.setWindowTitle(self.get_plugin_title()) # Toolbar title
         self.setObjectName(self.get_plugin_title()) # Used to save Window state
         
@@ -184,7 +176,7 @@ class WorkingDirectory(QToolBar, SpyderPluginMixin):
                      self.next_action.setEnabled)
         
         # Path combo box
-        adjust = self.get_option('working_dir_adjusttocontents', False)
+        adjust = self.get_option('working_dir_adjusttocontents')
         self.pathedit = PathComboBox(self, adjust_to_contents=adjust)
         self.pathedit.setToolTip(_("This is the working directory for newly\n"
                                "opened consoles (Python interpreters and\n"
@@ -192,10 +184,10 @@ class WorkingDirectory(QToolBar, SpyderPluginMixin):
                                "find in files plugin and for new files\n"
                                "created in the editor"))
         self.connect(self.pathedit, SIGNAL("open_dir(QString)"), self.chdir)
-        self.pathedit.setMaxCount(self.get_option('working_dir_history', 20))
+        self.pathedit.setMaxCount(self.get_option('working_dir_history'))
         wdhistory = self.load_wdhistory( workdir )
         if workdir is None:
-            if self.get_option('startup/use_last_directory', True):
+            if self.get_option('startup/use_last_directory'):
                 if wdhistory:
                     workdir = wdhistory[0]
                 else:
diff --git a/spyderlib/scientific_startup.py b/spyderlib/scientific_startup.py
index af28c38..d860785 100644
--- a/spyderlib/scientific_startup.py
+++ b/spyderlib/scientific_startup.py
@@ -134,7 +134,11 @@ Within Spyder, this interpreter also provides:
     except ImportError:
         # Python 3
         import builtins
-    from site import _Printer
+    try:
+        from site import _Printer
+    except ImportError:
+        # Python 3.4
+        from _sitebuiltins import _Printer
     builtins.scientific = _Printer("scientific", infos)
 
 
diff --git a/spyderlib/spyder.py b/spyderlib/spyder.py
index 76316ce..3f8587a 100644
--- a/spyderlib/spyder.py
+++ b/spyderlib/spyder.py
@@ -516,32 +516,31 @@ class MainWindow(QMainWindow):
                                         _("Close current plugin"),
                                         triggered=self.close_current_dockwidget,
                                         context=Qt.ApplicationShortcut)
-            self.register_shortcut(self.close_dockwidget_action,
-                                   "_", "Close plugin", "Shift+Ctrl+F4")
+            self.register_shortcut(self.close_dockwidget_action, "_",
+                                   "Close plugin")
             
             _text = _("&Find text")
             self.find_action = create_action(self, _text, icon='find.png',
                                              tip=_text, triggered=self.find,
                                              context=Qt.WidgetShortcut)
-            self.register_shortcut(self.find_action, "Editor",
-                                   "Find text", "Ctrl+F")
+            self.register_shortcut(self.find_action, "Editor", "Find text")
             self.find_next_action = create_action(self, _("Find &next"),
                   icon='findnext.png', triggered=self.find_next,
                   context=Qt.WidgetShortcut)
             self.register_shortcut(self.find_next_action, "Editor",
-                                   "Find next", "F3")
+                                   "Find next")
             self.find_previous_action = create_action(self,
                         _("Find &previous"),
                         icon='findprevious.png', triggered=self.find_previous,
                         context=Qt.WidgetShortcut)
             self.register_shortcut(self.find_previous_action, "Editor",
-                                   "Find previous", "Shift+F3")
+                                   "Find previous")
             _text = _("&Replace text")
             self.replace_action = create_action(self, _text, icon='replace.png',
                                             tip=_text, triggered=self.replace,
                                             context=Qt.WidgetShortcut)
             self.register_shortcut(self.replace_action, "Editor",
-                                   "Replace text", "Ctrl+H")
+                                   "Replace text")
             def create_edit_action(text, tr_text, icon_name):
                 textseq = text.split(' ')
                 method_name = textseq[0].lower()+"".join(textseq[1:])
@@ -633,8 +632,7 @@ class MainWindow(QMainWindow):
             prefs_action = create_action(self, _("Pre&ferences"),
                                          icon='configure.png',
                                          triggered=self.edit_preferences)
-            self.register_shortcut(prefs_action, "_", "Preferences",
-                                   "Ctrl+Alt+Shift+P")
+            self.register_shortcut(prefs_action, "_", "Preferences")
             add_shortcut_to_tooltip(prefs_action, context="_",
                                     name="Preferences")
             spyder_path_action = create_action(self,
@@ -740,7 +738,7 @@ class MainWindow(QMainWindow):
             self.maximize_action = create_action(self, '',
                                             triggered=self.maximize_dockwidget)
             self.register_shortcut(self.maximize_action, "_",
-                                   "Maximize plugin", "Ctrl+Alt+Shift+M")
+                                   "Maximize plugin")
             self.__update_maximize_action()
             
             # Fullscreen mode
@@ -748,7 +746,7 @@ class MainWindow(QMainWindow):
                                             _("Fullscreen mode"),
                                             triggered=self.toggle_fullscreen)
             self.register_shortcut(self.fullscreen_action, "_",
-                                   "Fullscreen mode", "F11")
+                                   "Fullscreen mode")
             add_shortcut_to_tooltip(self.fullscreen_action, context="_",
                                     name="Fullscreen mode")
             
@@ -803,7 +801,7 @@ class MainWindow(QMainWindow):
             quit_action = create_action(self, _("&Quit"),
                                         icon='exit.png', tip=_("Quit"),
                                         triggered=self.console.quit)
-            self.register_shortcut(quit_action, "_", "Quit", "Ctrl+Q")
+            self.register_shortcut(quit_action, "_", "Quit")
             self.file_menu_actions += [self.load_temp_session_action,
                                        self.load_session_action,
                                        self.save_session_action,
@@ -1029,7 +1027,7 @@ class MainWindow(QMainWindow):
                     print("%s: %s" % (mod, str(error)), file=STDERR)
                                 
             # View menu
-            self.plugins_menu = QMenu(_("Windows"), self)
+            self.plugins_menu = QMenu(_("Panes"), self)
             self.toolbars_menu = QMenu(_("Toolbars"), self)
             self.view_menu.addMenu(self.plugins_menu)
             self.view_menu.addMenu(self.toolbars_menu)
@@ -1045,14 +1043,11 @@ class MainWindow(QMainWindow):
                                         triggered=lambda i=index:
                                         self.quick_layout_switch(i))
                 self.register_shortcut(qli_act, "_",
-                                       "Switch to/from layout %d" % index,
-                                       "Shift+Alt+F%d" % index)
+                                       "Switch to/from layout %d" % index)
                 qlsi_act = create_action(self, _("Set layout %d") % index,
                                          triggered=lambda i=index:
                                          self.quick_layout_set(i))
-                self.register_shortcut(qlsi_act, "_",
-                                       "Set layout %d" % index,
-                                       "Ctrl+Shift+Alt+F%d" % index)
+                self.register_shortcut(qlsi_act, "_", "Set layout %d" % index)
                 ql_actions += [qli_act, qlsi_act]
             add_actions(quick_layout_menu, ql_actions)
             if set_attached_console_visible is not None:
@@ -1098,7 +1093,7 @@ class MainWindow(QMainWindow):
             
         # Apply all defined shortcuts (plugins + 3rd-party plugins)
         self.apply_shortcuts()
-        self.remove_deprecated_shortcuts()
+        #self.remove_deprecated_shortcuts()
         
         # Emitting the signal notifying plugins that main window menu and 
         # toolbar actions are all defined:
@@ -1191,8 +1186,16 @@ class MainWindow(QMainWindow):
             self.create_toolbars_menu()
         
         # Show the Object Inspector and Consoles by default
-        for plugin in (self.inspector, self.extconsole, self.ipyconsole):
-            if plugin is not None and plugin.dockwidget.isVisible():
+        plugins_to_show = [self.inspector]
+        if self.ipyconsole is not None:
+            if CONF.get('ipython_console', 'open_ipython_at_startup'):
+                plugins_to_show += [self.extconsole, self.ipyconsole]
+            else:
+                plugins_to_show += [self.ipyconsole, self.extconsole]
+        else:
+            plugins_to_show += [self.extconsole]
+        for plugin in plugins_to_show:
+            if plugin.dockwidget.isVisible():
                 plugin.dockwidget.raise_()
         
         # Give focus to the Editor
diff --git a/spyderlib/userconfig.py b/spyderlib/userconfig.py
index e66eccf..e5ec406 100644
--- a/spyderlib/userconfig.py
+++ b/spyderlib/userconfig.py
@@ -5,6 +5,7 @@
 #    ------------------------------------------
 #    
 #    Copyright © 2009-2012 Pierre Raybaut
+#    Copyright © 2014 The Spyder Development Team
 #    
 #    Permission is hereby granted, free of charge, to any person
 #    obtaining a copy of this software and associated documentation
@@ -34,24 +35,28 @@ userconfig
 
 The ``spyderlib.userconfig`` module provides user configuration file (.ini file)
 management features based on ``ConfigParser`` (standard Python library).
+
+NOTE: Since Spyder version 2.3 this module is heavily tied to Spyder internals
 """
 
 from __future__ import print_function
 
-__version__ = '1.1.0'
-__license__ = __doc__
-
 import os
 import re
 import os.path as osp
 import shutil
 import time
 
-from spyderlib.baseconfig import DEV, TEST
+from spyderlib.baseconfig import DEV, TEST, get_module_source_path
 from spyderlib.utils import encoding
+from spyderlib.utils.programs import check_version
 from spyderlib.py3compat import configparser as cp
-from spyderlib.py3compat import is_text_string, PY2
+from spyderlib.py3compat import PY2, is_text_string, to_text_string
+
 
+#==============================================================================
+# Auxiliary functions and classes
+#==============================================================================
 def get_home_dir():
     """
     Return user home directory
@@ -79,7 +84,103 @@ class NoDefault:
     pass
 
 
-class UserConfig(cp.ConfigParser):
+#==============================================================================
+# Defaults class
+#==============================================================================
+class DefaultsConfig(cp.ConfigParser):
+    """
+    Class used to save defaults to a file and as base class for
+    UserConfig
+    """
+    def __init__(self, name, subfolder):
+        cp.ConfigParser.__init__(self)
+        self.name = name
+        self.subfolder = subfolder
+    
+    def _set(self, section, option, value, verbose):
+        """
+        Private set method
+        """
+        if not self.has_section(section):
+            self.add_section( section )
+        if not is_text_string(value):
+            value = repr( value )
+        if verbose:
+            print('%s[ %s ] = %s' % (section, option, value))
+        cp.ConfigParser.set(self, section, option, value)
+
+    def _save(self):
+        """
+        Save config into the associated .ini file
+        """
+        # See Issue 1086 and 1242 for background on why this
+        # method contains all the exception handling.
+        fname = self.filename()
+
+        def _write_file(fname):
+            if PY2:
+                # Python 2
+                with open(fname, 'w') as configfile:
+                    self.write(configfile)
+            else:
+                # Python 3
+                with open(fname, 'w', encoding='utf-8') as configfile:
+                    self.write(configfile)
+
+        try: # the "easy" way
+            _write_file(fname)
+        except IOError:
+            try: # the "delete and sleep" way
+                if osp.isfile(fname):
+                    os.remove(fname)
+                time.sleep(0.05)
+                _write_file(fname)
+            except Exception as e:
+                print("Failed to write user configuration file.")
+                print("Please submit a bug report.")
+                raise(e)
+
+    def filename(self):
+        """
+        Create a .ini filename located in user home directory
+        """
+        if TEST is None:
+            folder = get_home_dir()
+        else:
+            import tempfile
+            folder = tempfile.gettempdir()
+        w_dot = osp.join(folder, '.%s.ini' % self.name)
+        if self.subfolder is None:
+            return w_dot
+        else:
+            folder = osp.join(folder, self.subfolder)
+            try:
+                os.makedirs(folder)
+            except os.error:
+                # Folder (or one of its parents) already exists
+                pass
+            old, new = w_dot, osp.join(folder, '%s.ini' % self.name)
+            if osp.isfile(old) and DEV is None:
+                try:
+                    if osp.isfile(new):
+                        os.remove(old)
+                    else:
+                        os.rename(old, new)
+                except OSError:
+                    pass
+            return new
+    
+    def set_defaults(self, defaults):
+        for section, options in defaults:
+            for option in options:
+                new_value = options[ option ]
+                self._set(section, option, new_value, False)
+        
+
+#==============================================================================
+# User config class
+#==============================================================================
+class UserConfig(DefaultsConfig):
     """
     UserConfig class, based on ConfigParser
     name: name of the config
@@ -95,12 +196,10 @@ class UserConfig(cp.ConfigParser):
     def __init__(self, name, defaults=None, load=True, version=None,
                  subfolder=None, backup=False, raw_mode=False,
                  remove_obsolete=False):
-        cp.ConfigParser.__init__(self)
+        DefaultsConfig.__init__(self, name, subfolder)
         self.raw = 1 if raw_mode else 0
-        self.subfolder = subfolder
         if (version is not None) and (re.match('^(\d+).(\d+).(\d+)$', version) is None):
             raise ValueError("Version number %r is incorrect - must be in X.Y.Z format" % version)
-        self.name = name
         if isinstance(defaults, dict):
             defaults = [ (self.DEFAULT_SECTION_NAME, defaults) ]
         self.defaults = defaults
@@ -118,27 +217,27 @@ class UserConfig(cp.ConfigParser):
             old_ver = self.get_version(version)
             _major = lambda _t: _t[:_t.find('.')]
             _minor = lambda _t: _t[:_t.rfind('.')]
-            # Resetting to defaults only if major/minor version is different
+            # Save new defaults
+            self.__save_new_defaults(defaults, version, subfolder)
+            # Updating defaults only if major/minor version is different
             if _minor(version) != _minor(old_ver):
                 if backup:
                     try:
                         shutil.copyfile(fname, "%s-%s.bak" % (fname, old_ver))
                     except IOError:
                         pass
-                # Version has changed -> overwriting .ini file
-                self.reset_to_defaults(save=False)
+                if check_version(old_ver, '2.4.0', '<'):
+                    self.reset_to_defaults(save=False)
+                else:
+                    self.__update_defaults(defaults, old_ver)
+                # Remove deprecated options if major version has changed
                 if remove_obsolete or _major(version) != _major(old_ver):
-                    self.__remove_deprecated_options()
+                    self.__remove_deprecated_options(old_ver)
                 # Set new version number
                 self.set_version(version, save=False)
             if defaults is None:
                 # If no defaults are defined, set .ini file settings as default
                 self.set_as_defaults()
-        # In any case, the resulting config is saved in config file:
-        # FIXME (Carlos): Commenting this for now because it's corrupting our
-        # config on Windows when a user tries to open several files at once. Is
-        # this really necessary?
-        # self.__save()
         
     def get_version(self, version='0.0.0'):
         """Return configuration (not application!) version"""
@@ -161,79 +260,51 @@ class UserConfig(cp.ConfigParser):
                 self.read(self.filename(), encoding='utf-8')
         except cp.MissingSectionHeaderError:
             print("Warning: File contains no section headers.")
-        
-    def __remove_deprecated_options(self):
+    
+    def __load_old_defaults(self, old_version):
+        """Read old defaults"""
+        old_defaults = cp.ConfigParser()
+        if check_version(old_version, '2.4.0', '='):
+            path = get_module_source_path('spyderlib', 'utils')
+        else:
+            path = osp.dirname(self.filename())
+        old_defaults.read(osp.join(path, 'defaults-'+old_version+'.ini'))
+        return old_defaults
+    
+    def __save_new_defaults(self, defaults, new_version, subfolder):
+        """Save new defaults"""
+        new_defaults = DefaultsConfig(name='defaults-'+new_version,
+                                      subfolder=subfolder)
+        if not osp.isfile(new_defaults.filename()):
+            new_defaults.set_defaults(defaults)
+            new_defaults._save()
+    
+    def __update_defaults(self, defaults, old_version, verbose=False):
+        """Update defaults after a change in version"""
+        old_defaults = self.__load_old_defaults(old_version)
+        for section, options in defaults:
+            for option in options:
+                new_value = options[ option ]
+                try:
+                    old_value = old_defaults.get(section, option)
+                except (cp.NoSectionError, cp.NoOptionError):
+                    old_value = None
+                if old_value is None or \
+                  to_text_string(new_value) != old_value:
+                    self._set(section, option, new_value, verbose)
+    
+    def __remove_deprecated_options(self, old_version):
         """
         Remove options which are present in the .ini file but not in defaults
         """
-        for section in self.sections():
-            for option, _ in self.items(section, raw=self.raw):
+        old_defaults = self.__load_old_defaults(old_version)
+        for section in old_defaults.sections():
+            for option, _ in old_defaults.items(section, raw=self.raw):
                 if self.get_default(section, option) is NoDefault:
                     self.remove_option(section, option)
                     if len(self.items(section, raw=self.raw)) == 0:
                         self.remove_section(section)
-        
-    def __save(self):
-        """
-        Save config into the associated .ini file
-        """
-        # See Issue 1086 and 1242 for background on why this
-        # method contains all the exception handling.
-        fname = self.filename()
-
-        def _write_file(fname):
-            if PY2:
-                # Python 2
-                with open(fname, 'w') as configfile:
-                    self.write(configfile)
-            else:
-                # Python 3
-                with open(fname, 'w', encoding='utf-8') as configfile:
-                    self.write(configfile)
-
-        try: # the "easy" way
-            _write_file(fname)
-        except IOError:
-            try: # the "delete and sleep" way
-                if osp.isfile(fname):
-                    os.remove(fname)
-                time.sleep(0.05)
-                _write_file(fname)
-            except Exception as e:
-                print("Failed to write user configuration file.")
-                print("Please submit a bug report.")
-                raise(e)
 
-    def filename(self):
-        """
-        Create a .ini filename located in user home directory
-        """
-        if TEST is None:
-            folder = get_home_dir()
-        else:
-            import tempfile
-            folder = tempfile.gettempdir()
-        w_dot = osp.join(folder, '.%s.ini' % self.name)
-        if self.subfolder is None:
-            return w_dot
-        else:
-            folder = osp.join(folder, self.subfolder)
-            try:
-                os.makedirs(folder)
-            except os.error:
-                # Folder (or one of its parents) already exists
-                pass
-            old, new = w_dot, osp.join(folder, '%s.ini' % self.name)
-            if osp.isfile(old) and DEV is None:
-                try:
-                    if osp.isfile(new):
-                        os.remove(old)
-                    else:
-                        os.rename(old, new)
-                except OSError:
-                    pass
-            return new
-        
     def cleanup(self):
         """
         Remove .ini file associated to config
@@ -258,9 +329,9 @@ class UserConfig(cp.ConfigParser):
         for section, options in self.defaults:
             for option in options:
                 value = options[ option ]
-                self.__set(section, option, value, verbose)
+                self._set(section, option, value, verbose)
         if save:
-            self.__save()
+            self._save()
         
     def __check_section_option(self, section, option):
         """
@@ -330,18 +401,6 @@ class UserConfig(cp.ConfigParser):
                 pass
         return value
 
-    def __set(self, section, option, value, verbose):
-        """
-        Private set method
-        """
-        if not self.has_section(section):
-            self.add_section( section )
-        if not is_text_string(value):
-            value = repr( value )
-        if verbose:
-            print('%s[ %s ] = %s' % (section, option, value))
-        cp.ConfigParser.set(self, section, option, value)
-
     def set_default(self, section, option, default_value):
         """
         Set Default value for a given (section, option)
@@ -370,14 +429,14 @@ class UserConfig(cp.ConfigParser):
             value = int(value)
         elif not is_text_string(default_value):
             value = repr(value)
-        self.__set(section, option, value, verbose)
+        self._set(section, option, value, verbose)
         if save:
-            self.__save()
+            self._save()
             
     def remove_section(self, section):
         cp.ConfigParser.remove_section(self, section)
-        self.__save()
+        self._save()
             
     def remove_option(self, section, option):
         cp.ConfigParser.remove_option(self, section, option)
-        self.__save()
+        self._save()
diff --git a/spyderlib/utils/codeanalysis.py b/spyderlib/utils/codeanalysis.py
index f6a8721..e9f2445 100644
--- a/spyderlib/utils/codeanalysis.py
+++ b/spyderlib/utils/codeanalysis.py
@@ -24,7 +24,7 @@ from spyderlib import dependencies
 #==============================================================================
 # Pyflakes/pep8 code analysis
 #==============================================================================
-TASKS_PATTERN = r"(^|#)[ ]*(TODO|FIXME|XXX|HINT|TIP)([^#]*)"
+TASKS_PATTERN = r"(^|#)[ ]*(TODO|FIXME|XXX|HINT|TIP|@todo)([^#]*)"
 
 #TODO: this is a test for the following function
 def find_tasks(source_code):
diff --git a/spyderlib/utils/defaults-2.4.0.ini b/spyderlib/utils/defaults-2.4.0.ini
new file mode 100644
index 0000000..cdb4d3a
--- /dev/null
+++ b/spyderlib/utils/defaults-2.4.0.ini
@@ -0,0 +1,380 @@
+[main]
+lightwindow/is_fullscreen = False
+memory_usage/timeout = 2000
+custom_margin = 0
+vertical_dockwidget_titlebars = False
+lightwindow/size = (650, 400)
+show_internal_console_if_traceback = True
+memory_usage/enable = True
+single_instance = True
+window/is_maximized = False
+cpu_usage/enable = False
+lightwindow/is_maximized = False
+animated_docks = True
+window/is_fullscreen = False
+cpu_usage/timeout = 2000
+window/size = (1260, 740)
+open_files_port = 21128
+lightwindow/prefs_dialog_size = (745, 411)
+window/prefs_dialog_size = (745, 411)
+window/position = (10, 10)
+lightwindow/position = (30, 30)
+tear_off_menus = False
+vertical_tabs = False
+use_custom_margin = True
+
+[quick_layouts]
+place_holder = 
+
+[editor_appearance]
+completion/font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal']
+calltips/font/size = 9
+calltips/font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal']
+cursor/width = 2
+calltips/font/italic = False
+completion/font/size = 9
+completion/size = (300, 180)
+completion/font/bold = False
+calltips/size = 600
+calltips/font/bold = False
+completion/font/italic = False
+
+[shell_appearance]
+completion/font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal']
+calltips/font/size = 9
+calltips/font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal']
+cursor/width = 2
+calltips/font/italic = False
+completion/font/size = 9
+completion/size = (300, 180)
+completion/font/bold = False
+calltips/size = 600
+calltips/font/bold = False
+completion/font/italic = False
+
+[internal_console]
+working_dir_adjusttocontents = False
+external_editor/gotoline = -goto:
+font/italic = False
+calltips = True
+working_dir_history = 30
+external_editor/path = SciTE
+max_line_count = 300
+shortcut = None
+font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal']
+codecompletion/enter_key = True
+font/bold = False
+font/size = 9
+codecompletion/auto = False
+wrap = True
+codecompletion/case_sensitive = True
+light_background = True
+codecompletion/show_single = False
+
+[console]
+pythonexecutable/default = True
+colorize_sys_stderr = True
+umd/enabled = True
+show_icontext = False
+calltips = True
+matplotlib/backend/value = Qt4Agg
+single_tab = True
+qt/install_inputhook = True
+max_line_count = 10000
+pythonstartup/default = False
+font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal']
+pyqt/ignore_sip_setapi_errors = False
+qt/api = default
+pythonexecutable/custom = False
+font/size = 9
+codecompletion/auto = True
+wrap = True
+umd/verbose = True
+matplotlib/patch = True
+codecompletion/show_single = False
+matplotlib/backend/enabled = True
+monitor/enabled = True
+pythonstartup/custom = True
+light_background = True
+font/italic = False
+codecompletion/enter_key = True
+ets_backend = qt4
+merge_output_channels = True
+show_elapsed_time = True
+pyqt/api_version = 0
+shortcut = Ctrl+Shift+C
+open_python_at_startup = True
+font/bold = False
+umd/namelist = ['guidata', 'guiqwt']
+codecompletion/case_sensitive = True
+object_inspector = True
+
+[ipython_console]
+show_calltips = False
+pylab = True
+symbolic_math = False
+pylab/inline/height = 4
+open_ipython_at_startup = False
+out_prompt = 
+autocall = 0
+in_prompt = 
+shortcut = None
+font/bold = False
+startup/run_lines = 
+startup/run_file = 
+pylab/inline/figure_format = 0
+greedy_completer = False
+pylab/inline/resolution = 72
+font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal']
+dark_color = False
+ask_before_closing = True
+pylab/backend = 0
+font/size = 9
+light_color = True
+buffer_size = 10000
+show_banner = True
+font/italic = False
+pylab/inline/width = 6
+use_gui_completion = True
+use_pager = True
+startup/use_run_file = False
+object_inspector = True
+pylab/autoload = True
+
+[variable_explorer]
+collvalue = False
+truncate = True
+exclude_unsupported = True
+minmax = False
+exclude_uppercase = True
+check_all = False
+exclude_private = True
+autorefresh = True
+inplace = False
+shortcut = Ctrl+Shift+V
+excluded_names = ['nan', 'inf', 'infty', 'little_endian', 'colorbar_doc', 'typecodes', '__builtins__', '__main__', '__doc__', 'NaN', 'Inf', 'Infinity', 'sctypes', 'rcParams', 'rcParamsDefault', 'sctypeNA', 'typeNA', 'False_', 'True_']
+autorefresh/timeout = 2000
+exclude_capitalized = False
+remote_editing = False
+
+[editor]
+wrapflag = True
+edge_line = True
+add_colons = True
+always_remove_trailing_spaces = False
+auto_unindent = True
+max_recent_files = 20
+onsave_analysis = False
+wrap = False
+indent_chars = *    *
+outline_explorer = True
+show_tab_bar = True
+shortcut = Ctrl+Shift+E
+font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal']
+codecompletion/auto = True
+fullpath_sorting = True
+font/italic = False
+check_eol_chars = True
+intelligent_backspace = True
+realtime_analysis/timeout = 2500
+todo_list = True
+close_quotes = False
+occurence_highlighting = True
+object_inspector = True
+go_to_definition = True
+tab_stop_width = 40
+tab_always_indent = False
+printer_header/font/bold = False
+codecompletion/show_single = False
+printer_header/font/italic = False
+realtime_analysis = True
+font/bold = False
+printer_header/font/family = ['Sans Serif', 'DejaVu Sans', 'Bitstream Vera Sans', 'Bitstream Charter', 'Lucida Grande', 'MS Shell Dlg 2', 'Calibri', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', 'Times', 'sans-serif']
+toolbox_panel = True
+calltips = True
+highlight_current_line = True
+font/size = 9
+edge_line_column = 79
+close_parentheses = True
+save_all_before_run = True
+code_analysis/pyflakes = True
+line_numbers = True
+codecompletion/enter_key = True
+code_analysis/pep8 = False
+printer_header/font/size = 9
+codecompletion/case_sensitive = True
+occurence_highlighting/timeout = 1500
+
+[historylog]
+max_entries = 100
+go_to_eof = True
+font/bold = False
+enable = True
+font/size = 9
+font/italic = False
+wrap = True
+font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal']
+shortcut = Ctrl+Shift+H
+
+[inspector]
+max_history_entries = 20
+enable = True
+font/italic = False
+rich_text/font/italic = False
+font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal']
+shortcut = Ctrl+Shift+I
+automatic_import = True
+connect/ipython_console = False
+font/bold = False
+rich_text/font/size = 12
+font/size = 9
+connect/python_console = False
+wrap = True
+connect/editor = False
+rich_text/font/family = ['Sans Serif', 'DejaVu Sans', 'Bitstream Vera Sans', 'Bitstream Charter', 'Lucida Grande', 'MS Shell Dlg 2', 'Calibri', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', 'Times', 'sans-serif']
+rich_text/font/bold = False
+math = True
+
+[onlinehelp]
+max_history_entries = 20
+enable = True
+zoom_factor = 0.8
+shortcut = Ctrl+Shift+D
+
+[outline_explorer]
+show_comments = True
+show_fullpath = False
+enable = True
+shortcut = Ctrl+Shift+O
+show_all_files = False
+
+[project_explorer]
+show_all = False
+name_filters = ['*.py', '*.pyw', '*.ipy', '*.pyx', '*.pxd', '*.pxi', '*.c', '*.h', '*.cc', '*.cpp', '*.cxx', '*.h', '*.hh', '*.hpp', '*.hxx', '*.cl', '*.f', '*.for', '*.f77', '*.f90', '*.f95', '*.f2k', '*.pro', '*.m', '*.patch', '*.diff', '*.rej', '*.bat', '*.cmd', '*.txt', '*.txt', '*.rst', '*.po', '*.pot', '*.nsi', '*.nsh', '*.css', '*.htm', '*.html', '*.xml', '*.js', '*.enaml', '*.properties', '*.session', '*.ini', '*.inf', '*.reg', '*.cfg', '*.desktop', '*.txt', '*.png', '*.mat', '*. [...]
+enable = True
+shortcut = Ctrl+Shift+P
+show_hscrollbar = True
+
+[arrayeditor]
+font/bold = False
+font/size = 9
+font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal']
+font/italic = False
+
+[texteditor]
+font/bold = False
+font/size = 9
+font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal']
+font/italic = False
+
+[dicteditor]
+font/bold = False
+font/size = 9
+font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal']
+font/italic = False
+
+[explorer]
+enable = True
+show_hidden = True
+show_icontext = False
+wrap = True
+name_filters = ['*.py', '*.pyw', '*.ipy', '*.pyx', '*.pxd', '*.pxi', '*.c', '*.h', '*.cc', '*.cpp', '*.cxx', '*.h', '*.hh', '*.hpp', '*.hxx', '*.cl', '*.f', '*.for', '*.f77', '*.f90', '*.f95', '*.f2k', '*.pro', '*.m', '*.patch', '*.diff', '*.rej', '*.bat', '*.cmd', '*.txt', '*.txt', '*.rst', '*.po', '*.pot', '*.nsi', '*.nsh', '*.css', '*.htm', '*.html', '*.xml', '*.js', '*.enaml', '*.properties', '*.session', '*.ini', '*.inf', '*.reg', '*.cfg', '*.desktop', '*.txt', '*.png', '*.mat', '*. [...]
+show_all = False
+shortcut = None
+show_toolbar = True
+
+[find_in_files]
+enable = True
+exclude_regexp = True
+in_python_path = False
+exclude = ['\\.pyc$|\\.pyo$|\\.orig$|\\.hg|\\.svn|\\bbuild\\b', '\\.pyc$|\\.pyo$|\\.orig$|\\.hg|\\.svn']
+search_text_regexp = True
+more_options = True
+search_text = ['']
+supported_encodings = ['utf-8', 'iso-8859-1', 'cp1252']
+search_text_samples = ['(^|#)[ ]*(TODO|FIXME|XXX|HINT|TIP)([^#]*)']
+shortcut = None
+include_regexp = True
+include = ['\\.py$|\\.pyw$|\\.ipy$|\\.pyx$|\\.pxd$|\\.pxi$|\\.c$|\\.h$|\\.cc$|\\.cpp$|\\.cxx$|\\.h$|\\.hh$|\\.hpp$|\\.hxx$|\\.cl$|\\.f$|\\.for$|\\.f77$|\\.f90$|\\.f95$|\\.f2k$|\\.pro$|\\.m$|\\.patch$|\\.diff$|\\.rej$|\\.bat$|\\.cmd$|\\.txt$|\\.txt$|\\.rst$|\\.po$|\\.pot$|\\.nsi$|\\.nsh$|\\.css$|\\.htm$|\\.html$|\\.xml$|\\.js$|\\.enaml$|\\.properties$|\\.session$|\\.ini$|\\.inf$|\\.reg$|\\.cfg$|\\.desktop$|README|INSTALL', '\\.pyw?$|\\.ipy$|\\.txt$|\\.rst$', '.']
+
+[workingdir]
+working_dir_adjusttocontents = False
+editor/new/browse_scriptdir = False
+editor/open/auto_set_to_basedir = False
+working_dir_history = 20
+editor/open/browse_scriptdir = True
+editor/new/browse_workdir = True
+editor/open/browse_workdir = False
+startup/use_last_directory = True
+editor/save/auto_set_to_basedir = False
+
+[shortcuts]
+editor/duplicate line = Shift+Alt+Up
+editor/go to next file = Ctrl+Shift+Tab
+console/clear line = Shift+Escape
+_/switch to outline_explorer = Ctrl+Shift+O
+editor/show/hide outline = Ctrl+Alt+O
+_/fullscreen mode = F11
+_/maximize plugin = Ctrl+Alt+Shift+M
+_/close plugin = Shift+Ctrl+F4
+_/switch to inspector = Ctrl+Shift+I
+profiler/run profiler = F10
+editor/move line down = Alt+Down
+console/clear shell = Ctrl+L
+pylint/run analysis = F8
+_/switch to onlinehelp = Ctrl+Shift+D
+_/switch to editor = Ctrl+Shift+E
+editor/code completion = Ctrl+Space
+_/switch to variable_explorer = Ctrl+Shift+V
+_/switch to/from layout 3 = Shift+Alt+F3
+_/preferences = Ctrl+Alt+Shift+P
+_/switch to/from layout 1 = Shift+Alt+F1
+editor/run selection = F9
+_/debug step into = Ctrl+F11
+editor/toggle comment = Ctrl+1
+editor/go to definition = Ctrl+G
+editor/show/hide project explorer = Ctrl+Alt+P
+_/debug step return = Ctrl+Shift+F11
+editor/new file = Ctrl+N
+_/debug step over = Ctrl+F10
+editor/save all = Ctrl+Shift+S
+editor/unblockcomment = Ctrl+5
+_/debug exit = Ctrl+Shift+F12
+editor/go to previous file = Ctrl+Tab
+editor/next cursor position = Ctrl+Alt+Right
+editor/debug = Ctrl+F5
+editor/copy line = Shift+Alt+Down
+editor/file list management = Ctrl+E
+editor/debug with winpdb = F7
+_/quit = Ctrl+Q
+editor/find next = F3
+editor/move line up = Alt+Up
+console/inspect current object = Ctrl+I
+editor/find previous = Shift+F3
+_/set layout 2 = Ctrl+Shift+Alt+F2
+_/set layout 3 = Ctrl+Shift+Alt+F3
+_/set layout 1 = Ctrl+Shift+Alt+F1
+_/switch to console = Ctrl+Shift+C
+editor/re-run last script = Ctrl+F6
+editor/previous cursor position = Ctrl+Alt+Left
+_/switch to project_explorer = Ctrl+Shift+P
+editor/open file = Ctrl+O
+editor/inspect current object = Ctrl+I
+editor/last edit location = Ctrl+Alt+Shift+Left
+editor/print = Ctrl+P
+editor/configure = F6
+editor/breakpoint = F12
+editor/find text = Ctrl+F
+editor/list breakpoints = Ctrl+B
+editor/run = F5
+editor/close all = Ctrl+Shift+W
+_/debug continue = Ctrl+F12
+editor/blockcomment = Ctrl+4
+editor/close file = Ctrl+W
+editor/conditional breakpoint = Shift+F12
+_/switch to/from layout 2 = Shift+Alt+F2
+editor/replace text = Ctrl+H
+editor/save file = Ctrl+S
+editor/go to line = Ctrl+L
+_/switch to historylog = Ctrl+Shift+H
+editor/delete line = Ctrl+D
diff --git a/spyderlib/utils/introspection/base.py b/spyderlib/utils/introspection/base.py
index 3fbb96a..7722a97 100644
--- a/spyderlib/utils/introspection/base.py
+++ b/spyderlib/utils/introspection/base.py
@@ -7,6 +7,7 @@
 """
 Introspection utilities used by Spyder
 """
+
 from __future__ import print_function
 import imp
 import os
@@ -14,12 +15,11 @@ import os.path as osp
 import re
 import time
 import functools
-from collections import OrderedDict
 
 from spyderlib.baseconfig import DEBUG, get_conf_path, debug_print
 from spyderlib.py3compat import PY2
 from spyderlib.utils.debug import log_dt, log_last_error
-from spyderlib.utils import sourcecode
+from spyderlib.utils import sourcecode, encoding
 
 from spyderlib.qt.QtGui import QApplication
 
@@ -61,7 +61,7 @@ def memoize(obj):
 
     See https://wiki.python.org/moin/PythonDecoratorLibrary#Memoize
     """
-    cache = obj.cache = OrderedDict()
+    cache = obj.cache = {}
 
     @functools.wraps(obj)
     def memoizer(*args, **kwargs):
@@ -267,13 +267,23 @@ class IntrospectionPlugin(object):
         token = sourcecode.get_primary_at(source_code, offset)
         eol = sourcecode.get_eol_chars(source_code) or '\n'
         lines = source_code[:offset].split(eol)
-        line_nr = self.get_definition_with_regex(source_code, token, 
-                                                 len(lines))
+        line_nr = None
+        if '.' in token:
+            temp = token.split('.')[-1]
+            line_nr = self.get_definition_with_regex(source_code, temp, 
+                                                     len(lines))
+        if line_nr is None:
+            line_nr = self.get_definition_with_regex(source_code, token, 
+                                                 len(lines), True)
+        if line_nr is None and '.' in token:
+            temp = token.split('.')[-1]
+            line_nr = self.get_definition_with_regex(source_code, temp, 
+                                                 len(lines), True)
+        if line_nr is None:
+            return None, None
         line = source_code.split(eol)[line_nr - 1].strip()
         exts = self.python_like_exts()
         if not osp.splitext(filename)[-1] in exts:
-            line_nr = self.get_definition_with_regex(source_code, token, 
-                                                     line_nr)
             return filename, line_nr
         if line.startswith('import ') or line.startswith('from '):
             alt_path = osp.dirname(filename)
@@ -297,10 +307,12 @@ class IntrospectionPlugin(object):
         """Find the definition for an object in a filename"""
         with open(filename, 'rb') as fid:
             code = fid.read()
+        code = encoding.decode(code)[0]
         return self.get_definition_with_regex(code, name, line_nr)
         
     @staticmethod
-    def get_definition_with_regex(source, token, start_line=-1):
+    def get_definition_with_regex(source, token, start_line=-1, 
+                                  use_assignment=False):
         """
         Find the definition of an object within a source closest to a given line
         """
@@ -315,16 +327,16 @@ class IntrospectionPlugin(object):
                     'class\s*{0}{1}',
                     'c?p?def[^=]*\W{0}{1}',
                     'cdef.*\[.*\].*\W{0}{1}',
-                    # "self.item =" or "item ="
-                    '.*\Wself.{0}{1}[^=!<>]*=[^=]',
-                    '.*\W{0}{1}[^=!<>]*=[^=]',
-                    'self.{0}{1}[^=!<>]*=[^=]',
-                    '{0}{1}[^=!<>]*=[^=]',
                     # enaml keyword definitions
                     'enamldef.*\W{0}{1}',
                     'attr.*\W{0}{1}',
                     'event.*\W{0}{1}',
                     'id\s*:.*\W{0}{1}']
+        if use_assignment:
+            patterns += ['.*\Wself.{0}{1}[^=!<>]*=[^=]',
+                        '.*\W{0}{1}[^=!<>]*=[^=]',
+                        'self.{0}{1}[^=!<>]*=[^=]',
+                        '{0}{1}[^=!<>]*=[^=]']
         patterns = [pattern.format(token, r'[^0-9a-zA-Z.[]')
                     for pattern in patterns]
         pattern = re.compile('|^'.join(patterns))
@@ -408,8 +420,8 @@ def split_words(string):
 if __name__ == '__main__':
     p = IntrospectionPlugin()
     
-    with open(__file__) as fid:
-        code = fid.read()
+    with open(__file__, 'rb') as fid:
+        code = fid.read().decode('utf-8')
     code += '\nget_conf_path'
     path, line = p.get_definition_location_regex(code, len(code), __file__)
     assert path.endswith('baseconfig.py')
@@ -417,13 +429,37 @@ if __name__ == '__main__':
     comp = p.get_token_completion_list(code[:-2], len(code) - 2, None)
     assert comp == ['get_conf_path']
     
+    code += '\np.get_token_completion_list'
+    path, line = p.get_definition_location_regex(code, len(code), 'dummy.txt')
+    assert path == 'dummy.txt'
+    assert 'def get_token_completion_list(' in code.splitlines()[line - 1]
+    
+    code += '\np.python_like_mod_finder'
+    path, line = p.get_definition_location_regex(code, len(code), 'dummy.txt')
+    assert path == 'dummy.txt'
+    assert 'def python_like_mod_finder' in code.splitlines()[line - 1]
+    
+    code += 'python_like_mod_finder'
+    path, line = p.get_definition_location_regex(code, len(code), 'dummy.txt')
+    assert line is None
+    
+    code = """
+    class Test(object):
+        def __init__(self):
+            self.foo = bar
+            
+    t = Test()
+    t.foo"""
+    path, line = p.get_definition_location_regex(code, len(code), 'dummy.txt')
+    assert line == 4
+
     ext = p.python_like_exts()
     assert '.py' in ext and '.pyx' in ext
     
     ext = p.all_editable_exts()
     assert '.cfg' in ext and '.iss' in ext
     
-    path = p.get_parent_until(__file__)
+    path = p.get_parent_until(os.path.abspath(__file__))
     assert path == 'spyderlib.utils.introspection.base'
     
     line = 'from spyderlib.widgets.sourcecode.codeeditor import CodeEditor'
@@ -447,7 +483,7 @@ if __name__ == '__main__':
     comp = p.get_token_completion_list(code, len(code), None)
     assert comp == ['sigMessageReady']
     
-    code = u'álfa;á'
+    code = encoding.to_unicode('álfa;á')
     comp = p.get_token_completion_list(code, len(code), None)
-    assert comp == [u'álfa']
-    
\ No newline at end of file
+    assert comp == [encoding.to_unicode('álfa')]
+    
diff --git a/spyderlib/utils/programs.py b/spyderlib/utils/programs.py
index 6eca7f1..8b678e1 100644
--- a/spyderlib/utils/programs.py
+++ b/spyderlib/utils/programs.py
@@ -205,6 +205,8 @@ def check_version(actver, version, cmp_op):
             return LooseVersion(actver) == LooseVersion(version)
         elif cmp_op == '<':
             return LooseVersion(actver) < LooseVersion(version)
+        elif cmp_op == '<=':
+            return LooseVersion(actver) <= LooseVersion(version)
         else:
             return False
     except TypeError:
@@ -288,7 +290,7 @@ def is_module_installed(module_name, version=None, installed_version=None,
             symb = version[:match.start()]
             if not symb:
                 symb = '='
-            assert symb in ('>=', '>', '=', '<'),\
+            assert symb in ('>=', '>', '=', '<', '<='),\
                     "Invalid version condition '%s'" % symb
             version = version[match.start():]
             
diff --git a/spyderlib/utils/sourcecode.py b/spyderlib/utils/sourcecode.py
index 2f7c421..d4e308c 100644
--- a/spyderlib/utils/sourcecode.py
+++ b/spyderlib/utils/sourcecode.py
@@ -15,10 +15,12 @@ EOL_CHARS = (("\r\n", 'nt'), ("\n", 'posix'), ("\r", 'mac'))
 ALL_LANGUAGES = {
                  'Python': ('py', 'pyw', 'python', 'ipy'),
                  'Cython': ('pyx', 'pxi', 'pxd'),
+                 'Enaml': ('enaml',),
                  'Fortran77': ('f', 'for', 'f77'),
                  'Fortran': ('f90', 'f95', 'f2k'),
                  'Idl': ('pro',),
                  'Matlab': ('m',),
+                 'Julia': ('jl',),
                  'Diff': ('diff', 'patch', 'rej'),
                  'GetText': ('po', 'pot'),
                  'Nsis': ('nsi', 'nsh'),
@@ -33,7 +35,7 @@ ALL_LANGUAGES = {
                          'cfg', 'cnf', 'aut', 'iss'),
                  }
 
-PYTHON_LIKE_LANGUAGES = ('Python', 'Cython')             
+PYTHON_LIKE_LANGUAGES = ('Python', 'Cython', 'Enaml')             
 
 def get_eol_chars(text):
     """Get text EOL characters"""
diff --git a/spyderlib/utils/system.py b/spyderlib/utils/system.py
index 2321e97..8658ab2 100644
--- a/spyderlib/utils/system.py
+++ b/spyderlib/utils/system.py
@@ -16,23 +16,24 @@ from spyderlib.utils import programs
 def windows_memory_usage():
     """Return physical memory usage (float)
     Works on Windows platforms only"""
-    from ctypes import windll, wintypes
-    class MemoryStatus(wintypes.Structure):
-        _fields_ = [('dwLength', wintypes.DWORD),
-                    ('dwMemoryLoad', wintypes.DWORD),
-                    ('ullTotalPhys', wintypes.c_uint64),
-                    ('ullAvailPhys', wintypes.c_uint64),
-                    ('ullTotalPageFile', wintypes.c_uint64),
-                    ('ullAvailPageFile', wintypes.c_uint64),
-                    ('ullTotalVirtual', wintypes.c_uint64),
-                    ('ullAvailVirtual', wintypes.c_uint64),
-                    ('ullAvailExtendedVirtual', wintypes.c_uint64),]
+    from ctypes import windll, Structure, c_uint64, sizeof, byref
+    from ctypes.wintypes import DWORD
+    class MemoryStatus(Structure):
+        _fields_ = [('dwLength', DWORD),
+                    ('dwMemoryLoad',DWORD),
+                    ('ullTotalPhys', c_uint64),
+                    ('ullAvailPhys', c_uint64),
+                    ('ullTotalPageFile', c_uint64),
+                    ('ullAvailPageFile', c_uint64),
+                    ('ullTotalVirtual', c_uint64),
+                    ('ullAvailVirtual', c_uint64),
+                    ('ullAvailExtendedVirtual', c_uint64),]
     memorystatus = MemoryStatus()
     # MSDN documetation states that dwLength must be set to MemoryStatus
     # size before calling GlobalMemoryStatusEx
     # http://msdn.microsoft.com/en-us/library/aa366770(v=vs.85)
-    memorystatus.dwLength = wintypes.sizeof(memorystatus)
-    windll.kernel32.GlobalMemoryStatusEx(wintypes.byref(memorystatus))
+    memorystatus.dwLength = sizeof(memorystatus)
+    windll.kernel32.GlobalMemoryStatusEx(byref(memorystatus))
     return float(memorystatus.dwMemoryLoad)
 
 def psutil_phymem_usage():
@@ -56,6 +57,8 @@ if __name__ == '__main__':
     print("*"*80)
     print(memory_usage.__doc__)
     print(memory_usage())
-    print("*"*80)
-    print(windows_memory_usage.__doc__)
-    print(windows_memory_usage())
+    if os.name == 'nt':
+        #  windll can only be imported if os.name = 'nt' or 'ce'
+        print("*"*80)
+        print(windows_memory_usage.__doc__)
+        print(windows_memory_usage())
\ No newline at end of file
diff --git a/spyderlib/utils/vcs.py b/spyderlib/utils/vcs.py
index 51e3a5b..6ddebff 100644
--- a/spyderlib/utils/vcs.py
+++ b/spyderlib/utils/vcs.py
@@ -103,7 +103,8 @@ def get_hg_revision(repopath):
         output, _err = subprocess.Popen([hg, 'id', '-nib', repopath],
                                         stdout=subprocess.PIPE).communicate()
         # output is now: ('eba7273c69df+ 2015+ default\n', None)
-        return tuple(output.decode().strip().split())
+        # Split 2 times max to allow spaces in branch names.
+        return tuple(output.decode().strip().split(None, 2))
     except (subprocess.CalledProcessError, AssertionError, AttributeError):
         # print("Error: Failed to get revision number from Mercurial - %s" % exc)
         return (None, None, None)
diff --git a/spyderlib/widgets/editor.py b/spyderlib/widgets/editor.py
index b7e0269..ce3a822 100644
--- a/spyderlib/widgets/editor.py
+++ b/spyderlib/widgets/editor.py
@@ -36,6 +36,7 @@ from spyderlib.utils.introspection.module_completion import (module_completion,
                                                       get_preferred_submodules)
 from spyderlib.baseconfig import _, DEBUG, STDOUT, STDERR
 from spyderlib.config import EDIT_FILTERS, EDIT_EXT, get_filter, EDIT_FILETYPES
+from spyderlib.guiconfig import create_shortcut
 from spyderlib.utils.qthelpers import (get_icon, create_action, add_actions,
                                        mimedata2url, get_filetype_icon,
                                        create_toolbutton)
@@ -660,25 +661,42 @@ class EditorStack(QWidget):
         
         # Accepting drops
         self.setAcceptDrops(True)
-
+        
         # Local shortcuts
-        def newsc(keystr, triggered):
-            sc = QShortcut(QKeySequence(keystr), self, triggered)
-            sc.setContext(Qt.WidgetWithChildrenShortcut)
-            return sc
-        self.inspectsc = newsc("Ctrl+I", self.inspect_current_object)
-        self.breakpointsc = newsc("F12", self.set_or_clear_breakpoint)
-        self.cbreakpointsc = newsc("Shift+F12",
-                                   self.set_or_edit_conditional_breakpoint)
-        self.gotolinesc = newsc("Ctrl+L", self.go_to_line)
-        self.filelistsc = newsc("Ctrl+E", self.open_filelistdialog)
-        self.tabsc = newsc("Ctrl+Tab", self.go_to_previous_file)
-        self.closesc = newsc("Ctrl+F4", self.close_file)
-        self.tabshiftsc = newsc("Ctrl+Shift+Tab", self.go_to_next_file)
-        self.zoominsc = newsc(QKeySequence.ZoomIn,
-                              lambda: self.emit(SIGNAL('zoom_in()')))
-        self.zoomoutsc = newsc(QKeySequence.ZoomOut,
-                               lambda: self.emit(SIGNAL('zoom_out()')))
+        self.shortcuts = self.create_shortcuts()
+
+    def create_shortcuts(self):
+        """Create local shortcuts"""
+        # Configurable shortcuts
+        inspect = create_shortcut(self.inspect_current_object, context='Editor',
+                                  name='Inspect current object', parent=self)
+        breakpoint = create_shortcut(self.set_or_clear_breakpoint,
+                                     context='Editor', name='Breakpoint',
+                                     parent=self)
+        cbreakpoint = create_shortcut(self.set_or_edit_conditional_breakpoint,
+                                      context='Editor',
+                                      name='Conditional breakpoint',
+                                      parent=self)
+        gotoline = create_shortcut(self.go_to_line, context='Editor',
+                                   name='Go to line', parent=self)
+        filelist = create_shortcut(self.open_filelistdialog, context='Editor',
+                                   name='File list management', parent=self)
+        tab = create_shortcut(self.go_to_previous_file, context='Editor',
+                              name='Go to previous file', parent=self)
+        close_file = create_shortcut(self.close_file, context='Editor',
+                                     name='Close file', parent=self)
+        tabshift = create_shortcut(self.go_to_next_file, context='Editor',
+                                   name='Go to next file', parent=self)
+        # Fixed shortcuts
+        zoomin = QShortcut(QKeySequence(QKeySequence.ZoomIn), self,
+                           lambda: self.emit(SIGNAL('zoom_in()')))
+        zoomin.setContext(Qt.WidgetWithChildrenShortcut)
+        zoomout = QShortcut(QKeySequence(QKeySequence.ZoomOut), self,
+                           lambda: self.emit(SIGNAL('zoom_out()')))
+        zoomout.setContext(Qt.WidgetWithChildrenShortcut)
+        # Return configurable ones
+        return [inspect, breakpoint, cbreakpoint, gotoline, filelist, tab,
+                close_file, tabshift]
 
     def get_shortcut_data(self):
         """
@@ -687,13 +705,7 @@ class EditorStack(QWidget):
         text (string): action/shortcut description
         default (string): default key sequence
         """
-        return [
-                (self.inspectsc, "Inspect current object", "Ctrl+I"),
-                (self.breakpointsc, "Breakpoint", "F12"),
-                (self.cbreakpointsc, "Conditional breakpoint", "Shift+F12"),
-                (self.gotolinesc, "Go to line", "Ctrl+L"),
-                (self.filelistsc, "File list management", "Ctrl+E"),
-                ]
+        return [sc.data for sc in self.shortcuts]
         
     def setup_editorstack(self, parent, layout):
         """Setup editorstack's layout"""
diff --git a/spyderlib/widgets/findreplace.py b/spyderlib/widgets/findreplace.py
index 4cf68b1..a990b71 100644
--- a/spyderlib/widgets/findreplace.py
+++ b/spyderlib/widgets/findreplace.py
@@ -19,10 +19,11 @@ from spyderlib.qt.QtCore import SIGNAL, Qt, QTimer
 import re
 
 # Local imports
+from spyderlib.baseconfig import _
+from spyderlib.guiconfig import create_shortcut
 from spyderlib.utils.qthelpers import (get_icon, get_std_icon,
                                        create_toolbutton)
 from spyderlib.widgets.comboboxes import PatternComboBox
-from spyderlib.baseconfig import _
 from spyderlib.py3compat import to_text_string
 
 
@@ -145,28 +146,31 @@ class FindReplace(QWidget):
         
         self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
         
-        self.findnext_sc = QShortcut(QKeySequence("F3"), parent,
-                                     self.find_next)
-        self.findnext_sc.setContext(Qt.WidgetWithChildrenShortcut)
-        self.findprev_sc = QShortcut(QKeySequence("Shift+F3"), parent,
-                                     self.find_previous)
-        self.findprev_sc.setContext(Qt.WidgetWithChildrenShortcut)
-        self.togglefind_sc = QShortcut(QKeySequence("Ctrl+F"), parent,
-                                       self.show)
-        self.togglefind_sc.setContext(Qt.WidgetWithChildrenShortcut)
-        self.togglereplace_sc = QShortcut(QKeySequence("Ctrl+H"), parent,
-                                          self.toggle_replace_widgets)
-        self.togglereplace_sc.setContext(Qt.WidgetWithChildrenShortcut)
+        self.shortcuts = self.create_shortcuts(parent)
         
-        escape_sc = QShortcut(QKeySequence("Escape"), self, self.hide)
-        escape_sc.setContext(Qt.WidgetWithChildrenShortcut)
-
         self.highlight_timer = QTimer(self)
         self.highlight_timer.setSingleShot(True)
         self.highlight_timer.setInterval(1000)
         self.connect(self.highlight_timer, SIGNAL("timeout()"),
                      self.highlight_matches)
         
+    def create_shortcuts(self, parent):
+        """Create shortcuts for this widget"""
+        # Configurable
+        findnext = create_shortcut(self.find_next, context='Editor',
+                                   name='Find next', parent=parent)
+        findprev = create_shortcut(self.find_previous, context='Editor',
+                                   name='Find previous', parent=parent)
+        togglefind = create_shortcut(self.show, context='Editor',
+                                     name='Find text', parent=parent)
+        togglereplace = create_shortcut(self.toggle_replace_widgets,
+                                        context='Editor', name='Replace text',
+                                        parent=parent)
+        # Fixed
+        escape = QShortcut(QKeySequence("Escape"), self, self.hide)
+        escape.setContext(Qt.WidgetWithChildrenShortcut)
+        return [findnext, findprev, togglefind, togglereplace]
+        
     def get_shortcut_data(self):
         """
         Returns shortcut data, a list of tuples (shortcut, text, default)
@@ -174,10 +178,7 @@ class FindReplace(QWidget):
         text (string): action/shortcut description
         default (string): default key sequence
         """
-        return [(self.findnext_sc, "Find next", "F3"),
-                (self.findprev_sc, "Find previous", "Shift+F3"),
-                (self.togglefind_sc, "Find text", "Ctrl+F"),
-                (self.togglereplace_sc, "Replace text", "Ctrl+H"),]
+        return [sc.data for sc in self.shortcuts]
         
     def update_search_combo(self):
         self.search_text.lineEdit().emit(SIGNAL('returnPressed()'))
diff --git a/spyderlib/widgets/ipython.py b/spyderlib/widgets/ipython.py
index 904bd26..1dbb2fb 100644
--- a/spyderlib/widgets/ipython.py
+++ b/spyderlib/widgets/ipython.py
@@ -17,8 +17,8 @@ from string import Template
 import time
 
 # Qt imports
-from spyderlib.qt.QtGui import (QTextEdit, QKeySequence, QShortcut, QWidget,
-                                QMenu, QHBoxLayout, QToolButton, QVBoxLayout,
+from spyderlib.qt.QtGui import (QTextEdit, QKeySequence, QWidget, QMenu,
+                                QHBoxLayout, QToolButton, QVBoxLayout,
                                 QMessageBox)
 from spyderlib.qt.QtCore import SIGNAL, Qt
 
@@ -33,7 +33,7 @@ from IPython.core.oinspect import call_tip
 from spyderlib.baseconfig import (get_conf_path, get_image_path,
                                   get_module_source_path, _)
 from spyderlib.config import CONF
-from spyderlib.guiconfig import get_font
+from spyderlib.guiconfig import create_shortcut, get_font, get_shortcut
 from spyderlib.utils.dochelpers import getargspecfromtext, getsignaturefromtext
 from spyderlib.utils.qthelpers import (get_std_icon, create_toolbutton,
                                        add_actions, create_action, get_icon,
@@ -207,12 +207,7 @@ class IPythonShellWidget(RichIPythonWidget):
         self.ipyclient = None
         
         # --- Keyboard shortcuts ---
-        inspectsc = QShortcut(QKeySequence("Ctrl+I"), self,
-                              self._control.inspect_current_object)
-        inspectsc.setContext(Qt.WidgetWithChildrenShortcut)
-        clear_consolesc = QShortcut(QKeySequence("Ctrl+L"), self,
-                                    self.clear_console)
-        clear_consolesc.setContext(Qt.WidgetWithChildrenShortcut)
+        self.shortcuts = self.create_shortcuts()
         
         # --- IPython variables ---
         # To send an interrupt signal to the Spyder kernel
@@ -278,9 +273,17 @@ These commands were executed:
                 self.kernel_manager.stdin_channel.input(line)
     
     def set_background_color(self):
-        lightbg_o = CONF.get('ipython_console', 'light_color', True)
+        lightbg_o = CONF.get('ipython_console', 'light_color')
         if not lightbg_o:
             self.set_default_style(colors='linux')
+    
+    def create_shortcuts(self):
+        inspect = create_shortcut(self._control.inspect_current_object,
+                                  context='Console', name='Inspect current object',
+                                  parent=self)
+        clear_console = create_shortcut(self.clear_console, context='Console',
+                                        name='Clear shell', parent=self)
+        return [inspect, clear_console]
 
     #---- IPython private methods ---------------------------------------------
     def _context_menu_make(self, pos):
@@ -502,7 +505,8 @@ class IPythonClient(QWidget, SaveHistoryMixin):
         """Add actions to IPython widget context menu"""
         # See spyderlib/widgets/ipython.py for more details on this method
         inspect_action = create_action(self, _("Inspect current object"),
-                                    QKeySequence("Ctrl+I"),
+                                    QKeySequence(get_shortcut('console',
+                                                    'inspect current object')),
                                     icon=get_std_icon('MessageBoxInformation'),
                                     triggered=self.inspect_object)
         clear_line_action = create_action(self, _("Clear line or block"),
@@ -510,7 +514,8 @@ class IPythonClient(QWidget, SaveHistoryMixin):
                                           icon=get_icon('eraser.png'),
                                           triggered=self.clear_line)
         clear_console_action = create_action(self, _("Clear console"),
-                                             QKeySequence("Ctrl+L"),
+                                             QKeySequence(get_shortcut('console',
+                                                               'clear shell')),
                                              icon=get_icon('clear.png'),
                                              triggered=self.clear_console)
         quit_action = create_action(self, _("&Quit"), icon='exit.png',
diff --git a/spyderlib/widgets/shell.py b/spyderlib/widgets/shell.py
index 40388a3..97953f9 100644
--- a/spyderlib/widgets/shell.py
+++ b/spyderlib/widgets/shell.py
@@ -19,15 +19,15 @@ import sys
 import keyword
 
 from spyderlib.qt.QtGui import (QMenu, QApplication, QToolTip, QKeySequence,
-                                QMessageBox, QTextCursor, QTextCharFormat,
-                                QShortcut)
+                                QMessageBox, QTextCursor, QTextCharFormat)
 from spyderlib.qt.QtCore import (Qt, QCoreApplication, QTimer, SIGNAL,
                                  Property)
 from spyderlib.qt.compat import getsavefilename
 
 # Local import
 from spyderlib.baseconfig import get_conf_path, _, STDERR, DEBUG
-from spyderlib.guiconfig import CONF, get_font
+from spyderlib.config import CONF
+from spyderlib.guiconfig import get_font, create_shortcut, get_shortcut
 from spyderlib.utils import encoding
 from spyderlib.utils.qthelpers import (keybinding, create_action, add_actions,
                                        restore_keyevent, get_icon)
@@ -81,15 +81,10 @@ class ShellBaseWidget(ConsoleBaseWidget, SaveHistoryMixin):
 
         # Give focus to widget
         self.setFocus()
-                
-        # Calltips
-        calltip_size = CONF.get('shell_appearance', 'calltips/size')
-        calltip_font = get_font('shell_appearance', 'calltips')
-        self.setup_calltips(calltip_size, calltip_font)
         
         # Completion
         completion_size = CONF.get('shell_appearance', 'completion/size')
-        completion_font = get_font('shell_appearance', 'completion')
+        completion_font = get_font('console')
         self.completion_widget.setup_appearance(completion_size,
                                                 completion_font)
         # Cursor width
@@ -667,9 +662,14 @@ class PythonShellWidget(TracebackLinksMixin, ShellBaseWidget,
         InspectObjectMixin.__init__(self)
 
         # Local shortcuts
-        self.inspectsc = QShortcut(QKeySequence("Ctrl+I"), self,
-                                   self.inspect_current_object)
-        self.inspectsc.setContext(Qt.WidgetWithChildrenShortcut)
+        self.shortcuts = self.create_shortcuts()
+    
+    def create_shortcuts(self):
+        inspectsc = create_shortcut(self.inspect_current_object,
+                                    context='Console',
+                                    name='Inspect current object',
+                                    parent=self)
+        return [inspectsc]
         
     def get_shortcut_data(self):
         """
@@ -678,10 +678,7 @@ class PythonShellWidget(TracebackLinksMixin, ShellBaseWidget,
         text (string): action/shortcut description
         default (string): default key sequence
         """
-        return [
-                (self.inspectsc, "Inspect current object", "Ctrl+I"),
-                ]
-
+        return [sc.data for sc in self.shortcuts]
 
     #------ Context menu
     def setup_context_menu(self):
@@ -692,12 +689,14 @@ class PythonShellWidget(TracebackLinksMixin, ShellBaseWidget,
                                      icon=get_icon('copywop.png'),
                                      triggered=self.copy_without_prompts)
         clear_line_action = create_action(self, _("Clear line"),
-                                     QKeySequence("Shift+Escape"),
+                                     QKeySequence(get_shortcut('console',
+                                                               'Clear line')),
                                      icon=get_icon('eraser.png'),
                                      tip=_("Clear line"),
                                      triggered=self.clear_line)
         clear_action = create_action(self, _("Clear shell"),
-                                     QKeySequence("Ctrl+L"),
+                                     QKeySequence(get_shortcut('console',
+                                                               'Clear shell')),
                                      icon=get_icon('clear.png'),
                                      tip=_("Clear shell contents "
                                            "('cls' command)"),
diff --git a/spyderlib/widgets/sourcecode/base.py b/spyderlib/widgets/sourcecode/base.py
index 9e76184..5073452 100644
--- a/spyderlib/widgets/sourcecode/base.py
+++ b/spyderlib/widgets/sourcecode/base.py
@@ -210,7 +210,6 @@ class TextEditBaseWidget(QPlainTextEdit, BaseEditMixin):
         self.codecompletion_enter = False
         self.calltips = True
         self.calltip_position = None
-        self.calltip_font = QFont()
         self.completion_text = ""
         self.calltip_widget = CallTipWidget(self, hide_timer_on=True)
         
@@ -222,10 +221,6 @@ class TextEditBaseWidget(QPlainTextEdit, BaseEditMixin):
         self.matched_p_color = QColor(Qt.green)
         self.unmatched_p_color = QColor(Qt.red)
         
-    def setup_calltips(self, size=None, font=None):
-        self.calltip_size = size
-        self.calltip_font = font
-    
     def setup_completion(self, size=None, font=None):
         self.completion_widget.setup_appearance(size, font)
         
diff --git a/spyderlib/widgets/sourcecode/codeeditor.py b/spyderlib/widgets/sourcecode/codeeditor.py
index 8ce894c..fd3cc7d 100644
--- a/spyderlib/widgets/sourcecode/codeeditor.py
+++ b/spyderlib/widgets/sourcecode/codeeditor.py
@@ -42,18 +42,16 @@ from spyderlib.qt.compat import to_qvariant
 #      consistent editor module (Qt source code and shell widgets library)
 from spyderlib.baseconfig import get_conf_path, _, DEBUG, get_image_path
 from spyderlib.config import CONF
-from spyderlib.guiconfig import get_font
+from spyderlib.guiconfig import get_font, create_shortcut
 from spyderlib.utils.qthelpers import (add_actions, create_action, keybinding,
                                        mimedata2url, get_icon)
 from spyderlib.utils.dochelpers import getobj
 from spyderlib.utils import encoding, sourcecode
 from spyderlib.utils.sourcecode import ALL_LANGUAGES
-from spyderlib.utils.debug import log_last_error, log_dt
 from spyderlib.widgets.editortools import PythonCFM
 from spyderlib.widgets.sourcecode.base import TextEditBaseWidget
 from spyderlib.widgets.sourcecode import syntaxhighlighters as sh
-from spyderlib.py3compat import to_text_string, PY2
-from spyderlib import dependencies
+from spyderlib.py3compat import to_text_string
 
 #%% This line is for cell execution testing
 # For debugging purpose:
@@ -288,7 +286,7 @@ def get_file_language(filename, text=None):
 class CodeEditor(TextEditBaseWidget):
     """Source Code Editor Widget based exclusively on Qt"""
     LANGUAGES ={ 'Python': (sh.PythonSH, '#', PythonCFM),
-                 'Cython': (sh.Fortran77SH, 'c', PythonCFM),
+                 'Cython': (sh.CythonSH, '#', PythonCFM),
                  'Fortran77': (sh.Fortran77SH, 'c', None),
                  'Fortran': (sh.FortranSH, '!', None),
                  'Idl': (sh.IdlSH, ';', None),
@@ -299,11 +297,13 @@ class CodeEditor(TextEditBaseWidget):
                  'Html': (sh.HtmlSH, '', None),
                  'Css': (sh.CssSH, '', None),
                  'Xml': (sh.XmlSH, '', None),
-                 'Js': (sh.JsSH, '', None),
+                 'Js': (sh.JsSH, '//', None),
+                 'Julia': (sh.JuliaSH, '#', None),
                  'Cpp': (sh.CppSH, '//', None),
                  'OpenCL': (sh.OpenCLSH, '//', None),
                  'Batch': (sh.BatchSH, 'rem ', None),
                  'Ini': (sh.IniSH, '#', None),
+                 'Enaml': (sh.EnamlSH, '#', PythonCFM),
                  }
     try:
         import pygments  # analysis:ignore
@@ -319,14 +319,9 @@ class CodeEditor(TextEditBaseWidget):
         TextEditBaseWidget.__init__(self, parent)
         self.setFocusPolicy(Qt.StrongFocus)
 
-        # Calltips
-        calltip_size = CONF.get('editor_appearance', 'calltips/size')
-        calltip_font = get_font('editor_appearance', 'calltips')
-        self.setup_calltips(calltip_size, calltip_font)
-
         # Completion
         completion_size = CONF.get('editor_appearance', 'completion/size')
-        completion_font = get_font('editor_appearance', 'completion')
+        completion_font = get_font('editor')
         self.completion_widget.setup_appearance(completion_size,
                                                 completion_font)
 
@@ -452,39 +447,32 @@ class CodeEditor(TextEditBaseWidget):
         self.breakpoints = self.get_breakpoints()
 
         # Keyboard shortcuts
-        ctrl = "Meta" if sys.platform == 'darwin' else "Ctrl"
-        self.codecomp_sc = QShortcut(QKeySequence(ctrl+"+Space"), self,
-                                     self.do_code_completion)
-        self.codecomp_sc.setContext(Qt.WidgetWithChildrenShortcut)
-        dup_seq = "Ctrl+Alt+Up" if os.name == 'nt' else "Shift+Alt+Up"
-        self.duplicate_sc = QShortcut(QKeySequence(dup_seq), self,
-                                      self.duplicate_line)
-        self.duplicate_sc.setContext(Qt.WidgetWithChildrenShortcut)
-        cop_seq = "Ctrl+Alt+Down" if os.name == 'nt' else "Shift+Alt+Down"
-        self.copyline_sc = QShortcut(QKeySequence(cop_seq), self,
-                                     self.copy_line)
-        self.copyline_sc.setContext(Qt.WidgetWithChildrenShortcut)
-        self.deleteline_sc = QShortcut(QKeySequence("Ctrl+D"), self,
-                                       self.delete_line)
-        self.deleteline_sc.setContext(Qt.WidgetWithChildrenShortcut)
-        self.movelineup_sc = QShortcut(QKeySequence("Alt+Up"), self,
-                                       self.move_line_up)
-        self.movelineup_sc.setContext(Qt.WidgetWithChildrenShortcut)
-        self.movelinedown_sc = QShortcut(QKeySequence("Alt+Down"), self,
-                                         self.move_line_down)
-        self.movelinedown_sc.setContext(Qt.WidgetWithChildrenShortcut)
-        self.gotodef_sc = QShortcut(QKeySequence("Ctrl+G"), self,
-                                    self.do_go_to_definition)
-        self.gotodef_sc.setContext(Qt.WidgetWithChildrenShortcut)
-        self.toggle_comment_sc = QShortcut(QKeySequence("Ctrl+1"), self,
-                                           self.toggle_comment)
-        self.toggle_comment_sc.setContext(Qt.WidgetWithChildrenShortcut)
-        self.blockcomment_sc = QShortcut(QKeySequence("Ctrl+4"), self,
-                                         self.blockcomment)
-        self.blockcomment_sc.setContext(Qt.WidgetWithChildrenShortcut)
-        self.unblockcomment_sc = QShortcut(QKeySequence("Ctrl+5"), self,
-                                           self.unblockcomment)
-        self.unblockcomment_sc.setContext(Qt.WidgetWithChildrenShortcut)
+        self.shortcuts = self.create_shortcuts()
+
+    def create_shortcuts(self):
+        codecomp = create_shortcut(self.do_code_completion, context='Editor',
+                                   name='Code completion', parent=self)
+        duplicate_line = create_shortcut(self.duplicate_line, context='Editor',
+                                         name='Duplicate line', parent=self)
+        copyline = create_shortcut(self.copy_line, context='Editor',
+                                   name='Copy line', parent=self)
+        deleteline = create_shortcut(self.delete_line, context='Editor',
+                                     name='Delete line', parent=self)
+        movelineup = create_shortcut(self.move_line_up, context='Editor',
+                                     name='Move line up', parent=self)
+        movelinedown = create_shortcut(self.move_line_down, context='Editor',
+                                       name='Move line down', parent=self)
+        gotodef = create_shortcut(self.do_go_to_definition, context='Editor',
+                                  name='Go to definition', parent=self)
+        toggle_comment = create_shortcut(self.toggle_comment, context='Editor',
+                                         name='Toggle comment', parent=self)
+        blockcomment = create_shortcut(self.blockcomment, context='Editor',
+                                       name='Blockcomment', parent=self)
+        unblockcomment = create_shortcut(self.unblockcomment, context='Editor',
+                                         name='Unblockcomment', parent=self)
+        return [codecomp, duplicate_line, copyline, deleteline, movelineup,
+                movelinedown, gotodef, toggle_comment, blockcomment,
+                unblockcomment]
 
     def get_shortcut_data(self):
         """
@@ -493,19 +481,7 @@ class CodeEditor(TextEditBaseWidget):
         text (string): action/shortcut description
         default (string): default key sequence
         """
-        ctrl = "Meta" if sys.platform == 'darwin' else "Ctrl"
-        return [
-                (self.codecomp_sc, "Code completion", ctrl+"+Space"),
-                (self.duplicate_sc, "Duplicate line", ctrl+"+Alt+Up"),
-                (self.copyline_sc, "Copy line", ctrl+"+Alt+Down"),
-                (self.movelineup_sc, "Move line up", "Alt+Up"),
-                (self.movelinedown_sc, "Move line down", "Alt+Down"),
-                (self.deleteline_sc, "Delete line", "Ctrl+D"),
-                (self.gotodef_sc, "Go to definition", "Ctrl+G"),
-                (self.toggle_comment_sc, "Toggle comment", "Ctrl+1"),
-                (self.blockcomment_sc, "Blockcomment", "Ctrl+4"),
-                (self.unblockcomment_sc, "Unblockcomment", "Ctrl+5"),
-                ]
+        return [sc.data for sc in self.shortcuts]
 
     def closeEvent(self, event):
         TextEditBaseWidget.closeEvent(self, event)
@@ -675,9 +651,12 @@ class CodeEditor(TextEditBaseWidget):
 
     def is_cython(self):
         return self.highlighter_class is sh.CythonSH
+        
+    def is_enaml(self):
+        return self.highlighter_class is sh.EnamlSH
 
     def is_python_like(self):
-        return self.is_python() or self.is_cython()
+        return self.is_python() or self.is_cython() or self.is_enaml()
         
     def intelligent_tab(self):
         """Provide intelligent behavoir for Tab key press"""
@@ -1131,7 +1110,8 @@ class CodeEditor(TextEditBaseWidget):
 
     def do_go_to_definition(self):
         """Trigger go-to-definition"""
-        self.emit(SIGNAL("go_to_definition(int)"), self.textCursor().position())
+        if not self.in_comment_or_string():
+            self.emit(SIGNAL("go_to_definition(int)"), self.textCursor().position())
 
     def show_object_info(self, position):
         """Trigger a calltip"""
@@ -1828,7 +1808,7 @@ class CodeEditor(TextEditBaseWidget):
         cursor.setPosition(start_pos)
         cursor.movePosition(QTextCursor.StartOfBlock)
         while cursor.position() <= end_pos:
-            cursor.insertText("# ")
+            cursor.insertText(self.comment_string + " ")
             cursor.movePosition(QTextCursor.EndOfBlock)
             if cursor.atEnd():
                 break
@@ -1861,7 +1841,8 @@ class CodeEditor(TextEditBaseWidget):
         if not __is_comment_bar(cursor1):
             return
         def __in_block_comment(cursor):
-            return to_text_string(cursor.block().text()).startswith('#')
+            cs = self.comment_string
+            return to_text_string(cursor.block().text()).startswith(cs)
         # Finding second comment bar
         cursor2 = QTextCursor(cursor1)
         cursor2.movePosition(QTextCursor.NextBlock)
@@ -2316,6 +2297,8 @@ class CodeEditor(TextEditBaseWidget):
         """Go to definition from cursor instance (QTextCursor)"""
         if cursor is None:
             cursor = self.textCursor()
+        if self.in_comment_or_string():
+            return
         position = cursor.position()
         text = to_text_string(cursor.selectedText())
         if len(text) == 0:
@@ -2455,4 +2438,4 @@ if __name__ == '__main__':
 #        fname = r"C:\Python26\Lib\ssl.py"
 #        fname = r"D:\Python\testouille.py"
 #        fname = r"C:\Python26\Lib\pydoc.py"
-    test(fname)
\ No newline at end of file
+    test(fname)
diff --git a/spyderlib/widgets/sourcecode/syntaxhighlighters.py b/spyderlib/widgets/sourcecode/syntaxhighlighters.py
index b47b88c..8a74bc1 100644
--- a/spyderlib/widgets/sourcecode/syntaxhighlighters.py
+++ b/spyderlib/widgets/sourcecode/syntaxhighlighters.py
@@ -19,9 +19,17 @@ from spyderlib.qt.QtGui import (QColor, QApplication, QFont,
 from spyderlib.qt.QtCore import Qt
 
 # Local imports
+from spyderlib import dependencies
+from spyderlib.baseconfig import _
 from spyderlib.py3compat import builtins, is_text_string, to_text_string
 
 
+PYGMENTS_REQVER = '>=1.6'
+dependencies.add("pygments", _("Syntax highlighting for Matlab and other file "
+                               "types"),
+                 required_version=PYGMENTS_REQVER)
+
+
 #==============================================================================
 # Syntax highlighting color schemes
 #==============================================================================
@@ -524,6 +532,18 @@ class CythonSH(PythonSH):
 
 
 #==============================================================================
+# Enaml syntax highlighter
+#==============================================================================
+class EnamlSH(PythonSH):
+    """Enaml Syntax Highlighter"""
+    ADDITIONAL_KEYWORDS = ["enamldef", "template", "attr", "event", "const", "alias"]
+    ADDITIONAL_BUILTINS = []
+    PROG = re.compile(make_python_patterns(ADDITIONAL_KEYWORDS,
+                                           ADDITIONAL_BUILTINS), re.S)
+    IDPROG = re.compile(r"\s+([\w\.]+)", re.S)
+
+
+#==============================================================================
 # C/C++ syntax highlighter
 #==============================================================================
 C_KEYWORDS1 = 'and and_eq bitand bitor break case catch const const_cast continue default delete do dynamic_cast else explicit export extern for friend goto if inline namespace new not not_eq operator or or_eq private protected public register reinterpret_cast return sizeof static static_cast switch template throw try typedef typeid typename union using virtual while xor xor_eq'
@@ -939,6 +959,10 @@ class XmlSH(PygmentsSH):
 class JsSH(PygmentsSH):
     """Javascript highlighter"""
     _lang_name = 'js'
+
+class JuliaSH(PygmentsSH):
+    """Julia highlighter"""
+    _lang_name = 'julia'
     
 class CssSH(PygmentsSH):
     """CSS Syntax Highlighter"""
diff --git a/spyderlib/widgets/status.py b/spyderlib/widgets/status.py
index ae48a0a..c993243 100644
--- a/spyderlib/widgets/status.py
+++ b/spyderlib/widgets/status.py
@@ -13,6 +13,12 @@ from spyderlib.qt.QtCore import QTimer, SIGNAL
 from spyderlib.baseconfig import _
 from spyderlib.guiconfig import get_font
 from spyderlib.py3compat import to_text_string
+from spyderlib import dependencies
+
+
+PSUTIL_REQVER = '>=0.3'
+dependencies.add("psutil", _("CPU and memory usage info in the status bar"),
+                 required_version=PSUTIL_REQVER)
 
 
 class StatusBarWidget(QWidget):
diff --git a/spyderplugins/p_breakpoints.py b/spyderplugins/p_breakpoints.py
index bcc7e89..a775df4 100644
--- a/spyderplugins/p_breakpoints.py
+++ b/spyderplugins/p_breakpoints.py
@@ -79,7 +79,7 @@ class Breakpoints(BreakpointWidget, SpyderPluginMixin):
                                    triggered=self.show)
         list_action.setEnabled(True)
         self.register_shortcut(list_action, context="Editor",
-                               name="List breakpoints", default="Ctrl+B")
+                               name="List breakpoints")
         
         # A fancy way to insert the action into the Breakpoints menu under
         # the assumption that Breakpoints is the first QMenu in the list.
diff --git a/spyderplugins/p_profiler.py b/spyderplugins/p_profiler.py
index 3081985..2210b98 100644
--- a/spyderplugins/p_profiler.py
+++ b/spyderplugins/p_profiler.py
@@ -97,7 +97,7 @@ class Profiler(ProfilerWidget, SpyderPluginMixin):
                                      triggered=self.run_profiler)
         profiler_act.setEnabled(is_profiler_installed())
         self.register_shortcut(profiler_act, context="Profiler",
-                               name="Run profiler", default="F10")
+                               name="Run profiler")
         
         self.main.run_menu_actions += [profiler_act]
         self.main.editor.pythonfile_dependent_actions += [profiler_act]
diff --git a/spyderplugins/p_pylint.py b/spyderplugins/p_pylint.py
index 3167efe..829506b 100644
--- a/spyderplugins/p_pylint.py
+++ b/spyderplugins/p_pylint.py
@@ -126,7 +126,7 @@ class Pylint(PylintWidget, SpyderPluginMixin):
                                    triggered=self.run_pylint)
         pylint_act.setEnabled(PYLINT_PATH is not None)
         self.register_shortcut(pylint_act, context="Pylint",
-                               name="Run analysis", default="F8")
+                               name="Run analysis")
         
         self.main.source_menu_actions += [None, pylint_act]
         self.main.editor.pythonfile_dependent_actions += [pylint_act]

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/spyder.git



More information about the debian-science-commits mailing list