diff --git a/ivette/src/frama-c/kernel/SourceCode.tsx b/ivette/src/frama-c/kernel/SourceCode.tsx
index aaf770e7399bc4c1bb8c4709fe7982c573ad930f..d34b86c604720ca4c258205a13c62c0c7e4bb7c4 100644
--- a/ivette/src/frama-c/kernel/SourceCode.tsx
+++ b/ivette/src/frama-c/kernel/SourceCode.tsx
@@ -28,7 +28,7 @@ import React from 'react';
 import * as States from 'frama-c/states';
 
 import * as Dome from 'dome';
-import { readFile } from 'dome/system';
+import * as System from 'dome/system';
 import { RichTextBuffer } from 'dome/text/buffers';
 import { Text } from 'dome/text/editors';
 import { TitleBar } from 'ivette';
@@ -36,10 +36,15 @@ import * as Preferences from 'ivette/prefs';
 import { functions, markerInfo } from 'frama-c/api/kernel/ast';
 import { Code } from 'dome/controls/labels';
 import { Hfill } from 'dome/layout/boxes';
+import { IconButton } from 'dome/controls/buttons';
 import * as Path from 'path';
+import * as Settings from 'dome/data/settings';
+import * as Status from 'frama-c/kernel/Status';
 
+import CodeMirror from 'codemirror/lib/codemirror';
 import 'codemirror/addon/selection/active-line';
 import 'codemirror/addon/dialog/dialog.css';
+import 'codemirror/addon/search/search';
 import 'codemirror/addon/search/searchcursor';
 
 // --------------------------------------------------------------------------
@@ -86,17 +91,64 @@ export default function SourceCode() {
   // Updating the buffer content.
   const errorMsg = () => { D.error(`Fail to load source code file ${file}`); };
   const onError = () => { if (file) errorMsg(); return ''; };
-  const read = () => readFile(file).catch(onError);
+  const read = () => System.readFile(file).catch(onError);
   const text = React.useMemo(read, [file, onError]);
   const { result } = Dome.usePromise(text);
   React.useEffect(() => buffer.setValue(result), [buffer, result]);
   React.useEffect(() => buffer.setCursorOnTop(line), [buffer, line, result]);
 
+  /* CodeMirror types used to bind callbacks to extraKeys. */
+  type position = CodeMirror.Position;
+  type editor = CodeMirror.Editor;
+
+  const [command] = Settings.useGlobalSettings(Preferences.EditorCommand);
+  async function launchEditor(_?: editor, pos?: position) {
+    if (file !== '') {
+      const selectedLine = pos ? (pos.line + 1).toString() : '1';
+      const selectedChar = pos ? (pos.ch + 1).toString() : '1';
+      const cmd = command
+        .replace('%s', file)
+        .replace('%n', selectedLine)
+        .replace('%c', selectedChar);
+      const args = cmd.split(' ');
+      const prog = args.shift();
+      if (prog) System.spawn(prog, args).catch(() => {
+        Status.setMessage({
+          text: `An error has occured when opening the external editor ${prog}`,
+          kind: 'error',
+        });
+      });
+    }
+  }
+
+  async function contextMenu(editor?: editor, pos?: position) {
+    if (file !== '') {
+      const items = [
+        {
+          label: 'Open file in an external editor',
+          onClick: () => launchEditor(editor, pos),
+        },
+      ];
+      Dome.popupMenu(items);
+    }
+  }
+
+  const externalEditorTitle =
+    'Open the source file in an external editor.\nA Ctrl-click '
+    + 'in the source code opens the editor at the selected location.'
+    + '\nThe editor used can be configured in Ivette settings.';
+
   // Building the React component.
   return (
     <>
       <TitleBar>
-        <Code title={file}>{filename}</Code>
+        <IconButton
+          icon="DUPLICATE"
+          visible={file !== ''}
+          onClick={launchEditor}
+          title={externalEditorTitle}
+        />
+        <Code title={file} style={{ padding: '5px' }}>{filename}</Code>
         <Hfill />
         {themeButtons}
       </TitleBar>
@@ -109,7 +161,11 @@ export default function SourceCode() {
         selection={theMarker}
         lineNumbers={!!theFunction}
         styleActiveLine={!!theFunction}
-        extraKeys={{ 'Alt-F': 'findPersistent' }}
+        extraKeys={{
+          'Alt-F': 'findPersistent',
+          'Ctrl-LeftClick': launchEditor as (_: CodeMirror.Editor) => void,
+          RightClick: contextMenu as (_: CodeMirror.Editor) => void,
+        }}
         readOnly
       />
     </>
diff --git a/ivette/src/ivette/prefs.tsx b/ivette/src/ivette/prefs.tsx
index 71df4d94ca47ab24bd17921320064355fec3cafe..645e90c1b2a30eff502430aa4fd9c3f48b4adaa0 100644
--- a/ivette/src/ivette/prefs.tsx
+++ b/ivette/src/ivette/prefs.tsx
@@ -127,3 +127,14 @@ export function useThemeButtons(props: ThemeProps): ThemeControls {
 }
 
 // --------------------------------------------------------------------------
+// --- Editor configuration
+// --------------------------------------------------------------------------
+
+export const EditorCommand =
+  new Settings.GString('Editor.Command', 'emacs +%n:%c %s');
+
+export interface EditorCommandProps {
+  command: Settings.GlobalSettings<string>;
+}
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/renderer/Preferences.tsx b/ivette/src/renderer/Preferences.tsx
index a16aea3fdb2d8aa71fef5f9ed7451e9a832b52f4..023d17b672a4b63984b193bdc70b501e6443c833 100644
--- a/ivette/src/renderer/Preferences.tsx
+++ b/ivette/src/renderer/Preferences.tsx
@@ -83,6 +83,20 @@ function ThemeFields(props: P.ThemeProps) {
   );
 }
 
+// --------------------------------------------------------------------------
+// --- Editor Command Forms
+// --------------------------------------------------------------------------
+function EditorCommandFields(props: P.EditorCommandProps) {
+  const cmd = Forms.useDefined(Forms.useValid(
+    Settings.useGlobalSettings(props.command),
+  ));
+  const title =
+    'Command to open an external editor on Ctrl-click in the source code view.'
+    + '\nUse %s for the file name, %n for the line number'
+    + ' and %c for the selected character.';
+  return (<Forms.TextCodeField state={cmd} label="Command" title={title} />);
+}
+
 // --------------------------------------------------------------------------
 // --- Export Components
 // --------------------------------------------------------------------------
@@ -105,6 +119,9 @@ export default (() => (
         wrapText={P.SourceWrapText}
       />
     </Forms.Section>
+    <Forms.Section label="Editor Command" unfold>
+      <EditorCommandFields command={P.EditorCommand} />
+    </Forms.Section>
   </Forms.Page>
 ));