aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/emacskeys
diff options
context:
space:
mode:
authornsf <no.smile.face@gmail.com>2014-03-01 04:29:30 +0600
committerhjk <hjk121@nokiamail.com>2014-04-04 14:37:24 +0200
commit8560036d96d309fe8391060cd612c94436a7b109 (patch)
tree443b4ba8839614b5ad79750af7dea99b6eb2f93f /src/plugins/emacskeys
parentfaca3333b580aa5dc208d9af90b483ef4a4a2129 (diff)
Add EmacsKeys plugin.
A very simple non-intrusive plugin. Adds a bunch of actions typical emacs user would expect. Doesn't contain any default key bindings. Change-Id: Ib30fb46c577e2fdfd4c704e7f4eee7ee27a53c29 Reviewed-by: Christian Kandeler <christian.kandeler@digia.com> Reviewed-by: hjk <hjk121@nokiamail.com> Reviewed-by: Eike Ziller <eike.ziller@digia.com>
Diffstat (limited to 'src/plugins/emacskeys')
-rw-r--r--src/plugins/emacskeys/EmacsKeys.pluginspec.in28
-rw-r--r--src/plugins/emacskeys/emacskeys.pro10
-rw-r--r--src/plugins/emacskeys/emacskeys.qbs20
-rw-r--r--src/plugins/emacskeys/emacskeys_dependencies.pri4
-rw-r--r--src/plugins/emacskeys/emacskeysconstants.h64
-rw-r--r--src/plugins/emacskeys/emacskeysplugin.cpp390
-rw-r--r--src/plugins/emacskeys/emacskeysplugin.h111
-rw-r--r--src/plugins/emacskeys/emacskeysstate.cpp86
-rw-r--r--src/plugins/emacskeys/emacskeysstate.h80
9 files changed, 793 insertions, 0 deletions
diff --git a/src/plugins/emacskeys/EmacsKeys.pluginspec.in b/src/plugins/emacskeys/EmacsKeys.pluginspec.in
new file mode 100644
index 0000000000..d6020826bb
--- /dev/null
+++ b/src/plugins/emacskeys/EmacsKeys.pluginspec.in
@@ -0,0 +1,28 @@
+<plugin name=\"EmacsKeys\" version=\"$$QTCREATOR_VERSION\" compatVersion=\"$$QTCREATOR_COMPAT_VERSION\" experimental=\"true\">
+ <vendor>nsf</vendor>
+ <copyright>(C) nsf &lt;no.smile.face@gmail.com&gt;</copyright>
+ <license>
+Commercial Usage
+
+Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and Digia.
+
+GNU Lesser General Public License Usage
+
+Alternatively, this plugin may be used under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. Please review the following information to ensure the GNU Lesser General Public License version 2.1 requirements will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ </license>
+ <description>
+The main idea behind this plugin is to provide additional actions a typical emacs user would expect. It doesn&#39;t claim to provide full emacs emulation. The following actions are available:
+ - Movement [C-f, C-b, C-n, C-p, M-f, M-b, C-a, C-e, M-&lt;, M-&gt;]
+ - Mark-based selection [C-SPC, C-x C-x]
+ - Cut/copy/yank (doesn&#39;t provide kill ring feature) [M-w, C-w, C-y]
+ - Kill actions, which interact properly with clipboard [C-k, M-d, C-d]
+ - Scrolling (half of the screen, keeps cursor visible) [C-v, M-v]
+ - Insert new line and indent [C-j]
+
+IMPORTANT: Actions are not bound to any key combinations by default. You can find them under &#39;EmacsKeys&#39; section in keyboard shortcuts settings.
+
+Also it&#39;s worth mentioning that EmacsKeys plugin forces disabling of menu mnemonics by calling Qt&#39;s qt_set_sequence_auto_mnemonic function with false argument. Many of the english menu mnemonics get into the way of typical emacs keys, this includes: Alt+F (File), Alt+B (Build), Alt+W (Window). It&#39;s a temporary solution, it remains until there is a better one.
+ </description>
+ <url>http://nosmileface.ru</url>
+ $$dependencyList
+</plugin>
diff --git a/src/plugins/emacskeys/emacskeys.pro b/src/plugins/emacskeys/emacskeys.pro
new file mode 100644
index 0000000000..bd60d6db56
--- /dev/null
+++ b/src/plugins/emacskeys/emacskeys.pro
@@ -0,0 +1,10 @@
+include(../../qtcreatorplugin.pri)
+
+SOURCES += \
+ emacskeysplugin.cpp \
+ emacskeysstate.cpp
+
+HEADERS += \
+ emacskeysplugin.h \
+ emacskeysconstants.h \
+ emacskeysstate.h
diff --git a/src/plugins/emacskeys/emacskeys.qbs b/src/plugins/emacskeys/emacskeys.qbs
new file mode 100644
index 0000000000..40bbf51c68
--- /dev/null
+++ b/src/plugins/emacskeys/emacskeys.qbs
@@ -0,0 +1,20 @@
+import qbs.base 1.0
+
+import QtcPlugin
+
+QtcPlugin {
+ name: "EmacsKeys"
+
+ Depends { name: "Qt.widgets" }
+
+ Depends { name: "Core" }
+ Depends { name: "TextEditor" }
+
+ files: [
+ "emacskeysplugin.cpp",
+ "emacskeysplugin.h",
+ "emacskeysstate.cpp",
+ "emacskeysstate.h",
+ "emacskeysconstants.h",
+ ]
+}
diff --git a/src/plugins/emacskeys/emacskeys_dependencies.pri b/src/plugins/emacskeys/emacskeys_dependencies.pri
new file mode 100644
index 0000000000..efb5335332
--- /dev/null
+++ b/src/plugins/emacskeys/emacskeys_dependencies.pri
@@ -0,0 +1,4 @@
+QTC_PLUGIN_NAME = EmacsKeys
+QTC_PLUGIN_DEPENDS += \
+ coreplugin \
+ texteditor
diff --git a/src/plugins/emacskeys/emacskeysconstants.h b/src/plugins/emacskeys/emacskeysconstants.h
new file mode 100644
index 0000000000..267894c25b
--- /dev/null
+++ b/src/plugins/emacskeys/emacskeysconstants.h
@@ -0,0 +1,64 @@
+/**************************************************************************
+**
+** Copyright (c) nsf <no.smile.face@gmail.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef EMACSKEYSCONSTANTS_H
+#define EMACSKEYSCONSTANTS_H
+
+namespace EmacsKeys {
+namespace Constants {
+
+const char DELETE_CHARACTER[] = "EmacsKeys.DeleteCharacter";
+const char KILL_WORD[] = "EmacsKeys.KillWord";
+const char KILL_LINE[] = "EmacsKeys.KillLine";
+const char INSERT_LINE_AND_INDENT[] = "EmacsKeys.InsertLineAndIndent";
+
+const char GOTO_FILE_START[] = "EmacsKeys.GotoFileStart";
+const char GOTO_FILE_END[] = "EmacsKeys.GotoFileEnd";
+const char GOTO_LINE_START[] = "EmacsKeys.GotoLineStart";
+const char GOTO_LINE_END[] = "EmacsKeys.GotoLineEnd";
+const char GOTO_NEXT_LINE[] = "EmacsKeys.GotoNextLine";
+const char GOTO_PREVIOUS_LINE[] = "EmacsKeys.GotoPreviousLine";
+const char GOTO_NEXT_CHARACTER[] = "EmacsKeys.GotoNextCharacter";
+const char GOTO_PREVIOUS_CHARACTER[] = "EmacsKeys.GotoPreviousCharacter";
+const char GOTO_NEXT_WORD[] = "EmacsKeys.GotoNextWord";
+const char GOTO_PREVIOUS_WORD[] = "EmacsKeys.GotoPreviousWord";
+
+const char MARK[] = "EmacsKeys.Mark";
+const char EXCHANGE_CURSOR_AND_MARK[] = "EmacsKeys.ExchangeCursorAndMark";
+const char COPY[] = "EmacsKeys.Copy";
+const char CUT[] = "EmacsKeys.Cut";
+const char YANK[] = "EmacsKeys.Yank";
+
+const char SCROLL_HALF_DOWN[] = "EmacsKeys.ScrollHalfDown";
+const char SCROLL_HALF_UP[] = "EmacsKeys.ScrollHalfUp";
+
+} // namespace EmacsKeys
+} // namespace Constants
+
+#endif // EMACSKEYSCONSTANTS_H
diff --git a/src/plugins/emacskeys/emacskeysplugin.cpp b/src/plugins/emacskeys/emacskeysplugin.cpp
new file mode 100644
index 0000000000..8793fe8710
--- /dev/null
+++ b/src/plugins/emacskeys/emacskeysplugin.cpp
@@ -0,0 +1,390 @@
+/**************************************************************************
+**
+** Copyright (c) nsf <no.smile.face@gmail.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "emacskeysplugin.h"
+#include "emacskeysconstants.h"
+#include "emacskeysstate.h"
+
+#include <coreplugin/icore.h>
+#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/editormanager/ieditor.h>
+#include <coreplugin/icontext.h>
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/coreconstants.h>
+#include <utils/qtcassert.h>
+
+#include <texteditor/basetexteditor.h>
+
+#include <QAction>
+#include <QPlainTextEdit>
+#include <QApplication>
+#include <QClipboard>
+#include <QScrollBar>
+
+#include <QtPlugin>
+
+QT_BEGIN_NAMESPACE
+
+extern void qt_set_sequence_auto_mnemonic(bool enable);
+
+QT_END_NAMESPACE
+
+using namespace EmacsKeys::Internal;
+
+//---------------------------------------------------------------------------
+// EmacsKeysPlugin
+//---------------------------------------------------------------------------
+
+EmacsKeysPlugin::EmacsKeysPlugin(): m_currentEditorWidget(0)
+{
+}
+
+EmacsKeysPlugin::~EmacsKeysPlugin()
+{
+}
+
+bool EmacsKeysPlugin::initialize(const QStringList &arguments, QString *errorString)
+{
+ Q_UNUSED(arguments)
+ Q_UNUSED(errorString)
+
+ // We have to use this hack here at the moment, because it's the only way to
+ // disable Qt Creator menu accelerators aka mnemonics. Many of them get into
+ // the way of typical emacs keys, such as: Alt+F (File), Alt+B (Build),
+ // Alt+W (Window).
+ qt_set_sequence_auto_mnemonic(false);
+
+ connect(Core::EditorManager::instance(),
+ SIGNAL(editorAboutToClose(Core::IEditor*)),
+ this,
+ SLOT(editorAboutToClose(Core::IEditor*)));
+ connect(Core::EditorManager::instance(),
+ SIGNAL(currentEditorChanged(Core::IEditor*)),
+ this,
+ SLOT(currentEditorChanged(Core::IEditor*)));
+
+ registerAction(Constants::DELETE_CHARACTER,
+ SLOT(deleteCharacter()), tr("Delete Character"));
+ registerAction(Constants::KILL_WORD,
+ SLOT(killWord()), tr("Kill Word"));
+ registerAction(Constants::KILL_LINE,
+ SLOT(killLine()), tr("Kill Line"));
+ registerAction(Constants::INSERT_LINE_AND_INDENT,
+ SLOT(insertLineAndIndent()), tr("Insert New Line and Indent"));
+
+ registerAction(Constants::GOTO_FILE_START,
+ SLOT(gotoFileStart()), tr("Go to File Start"));
+ registerAction(Constants::GOTO_FILE_END,
+ SLOT(gotoFileEnd()), tr("Go to File End"));
+ registerAction(Constants::GOTO_LINE_START,
+ SLOT(gotoLineStart()), tr("Go to Line Start"));
+ registerAction(Constants::GOTO_LINE_END,
+ SLOT(gotoLineEnd()), tr("Go to Line End"));
+ registerAction(Constants::GOTO_NEXT_LINE,
+ SLOT(gotoNextLine()), tr("Go to Next Line"));
+ registerAction(Constants::GOTO_PREVIOUS_LINE,
+ SLOT(gotoPreviousLine()), tr("Go to Previous Line"));
+ registerAction(Constants::GOTO_NEXT_CHARACTER,
+ SLOT(gotoNextCharacter()), tr("Go to Next Character"));
+ registerAction(Constants::GOTO_PREVIOUS_CHARACTER,
+ SLOT(gotoPreviousCharacter()), tr("Go to Previous Character"));
+ registerAction(Constants::GOTO_NEXT_WORD,
+ SLOT(gotoNextWord()), tr("Go to Next Word"));
+ registerAction(Constants::GOTO_PREVIOUS_WORD,
+ SLOT(gotoPreviousWord()), tr("Go to Previous Word"));
+
+ registerAction(Constants::MARK,
+ SLOT(mark()), tr("Mark"));
+ registerAction(Constants::EXCHANGE_CURSOR_AND_MARK,
+ SLOT(exchangeCursorAndMark()), tr("Exchange Cursor and Mark"));
+ registerAction(Constants::COPY,
+ SLOT(copy()), tr("Copy"));
+ registerAction(Constants::CUT,
+ SLOT(cut()), tr("Cut"));
+ registerAction(Constants::YANK,
+ SLOT(yank()), tr("Yank"));
+
+ registerAction(Constants::SCROLL_HALF_DOWN,
+ SLOT(scrollHalfDown()), tr("Scroll Half Screen Down"));
+ registerAction(Constants::SCROLL_HALF_UP,
+ SLOT(scrollHalfUp()), tr("Scroll Half Screen Up"));
+ return true;
+}
+
+void EmacsKeysPlugin::extensionsInitialized()
+{
+}
+
+ExtensionSystem::IPlugin::ShutdownFlag EmacsKeysPlugin::aboutToShutdown()
+{
+ return SynchronousShutdown;
+}
+
+void EmacsKeysPlugin::editorAboutToClose(Core::IEditor *editor)
+{
+ QPlainTextEdit *w = qobject_cast<QPlainTextEdit*>(editor->widget());
+ if (!w)
+ return;
+
+ if (m_stateMap.contains(w)) {
+ delete m_stateMap[w];
+ m_stateMap.remove(w);
+ }
+}
+
+void EmacsKeysPlugin::currentEditorChanged(Core::IEditor *editor)
+{
+ if (!editor) {
+ m_currentEditorWidget = 0;
+ return;
+ }
+ m_currentEditorWidget = qobject_cast<QPlainTextEdit*>(editor->widget());
+ if (!m_currentEditorWidget)
+ return;
+
+ if (!m_stateMap.contains(m_currentEditorWidget)) {
+ m_stateMap[m_currentEditorWidget] = new EmacsKeysState(m_currentEditorWidget);
+ }
+ m_currentState = m_stateMap[m_currentEditorWidget];
+ m_currentBaseTextEditorWidget =
+ qobject_cast<TextEditor::BaseTextEditorWidget*>(editor->widget());
+}
+
+void EmacsKeysPlugin::gotoFileStart() { genericGoto(QTextCursor::Start); }
+void EmacsKeysPlugin::gotoFileEnd() { genericGoto(QTextCursor::End); }
+void EmacsKeysPlugin::gotoLineStart() { genericGoto(QTextCursor::StartOfLine); }
+void EmacsKeysPlugin::gotoLineEnd() { genericGoto(QTextCursor::EndOfLine); }
+void EmacsKeysPlugin::gotoNextLine() { genericGoto(QTextCursor::Down, false); }
+void EmacsKeysPlugin::gotoPreviousLine() { genericGoto(QTextCursor::Up, false); }
+void EmacsKeysPlugin::gotoNextCharacter() { genericGoto(QTextCursor::Right); }
+void EmacsKeysPlugin::gotoPreviousCharacter() { genericGoto(QTextCursor::Left); }
+void EmacsKeysPlugin::gotoNextWord() { genericGoto(QTextCursor::NextWord); }
+void EmacsKeysPlugin::gotoPreviousWord() { genericGoto(QTextCursor::PreviousWord); }
+
+void EmacsKeysPlugin::mark()
+{
+ if (!m_currentEditorWidget)
+ return;
+
+ m_currentState->beginOwnAction();
+ QTextCursor cursor = m_currentEditorWidget->textCursor();
+ if (m_currentState->mark() == cursor.position()) {
+ m_currentState->setMark(-1);
+ } else {
+ cursor.clearSelection();
+ m_currentState->setMark(cursor.position());
+ m_currentEditorWidget->setTextCursor(cursor);
+ }
+ m_currentState->endOwnAction(KeysActionOther);
+}
+
+void EmacsKeysPlugin::exchangeCursorAndMark()
+{
+ if (!m_currentEditorWidget)
+ return;
+
+ QTextCursor cursor = m_currentEditorWidget->textCursor();
+ if (m_currentState->mark() == -1 || m_currentState->mark() == cursor.position())
+ return;
+
+ m_currentState->beginOwnAction();
+ int position = cursor.position();
+ cursor.clearSelection();
+ cursor.setPosition(m_currentState->mark(), QTextCursor::KeepAnchor);
+ m_currentState->setMark(position);
+ m_currentEditorWidget->setTextCursor(cursor);
+ m_currentState->endOwnAction(KeysActionOther);
+}
+
+void EmacsKeysPlugin::copy()
+{
+ if (!m_currentEditorWidget)
+ return;
+
+ m_currentState->beginOwnAction();
+ QTextCursor cursor = m_currentEditorWidget->textCursor();
+ QApplication::clipboard()->setText(cursor.selectedText());
+ cursor.clearSelection();
+ m_currentEditorWidget->setTextCursor(cursor);
+ m_currentState->setMark(-1);
+ m_currentState->endOwnAction(KeysActionOther);
+}
+
+void EmacsKeysPlugin::cut()
+{
+ if (!m_currentEditorWidget)
+ return;
+
+ m_currentState->beginOwnAction();
+ QTextCursor cursor = m_currentEditorWidget->textCursor();
+ QApplication::clipboard()->setText(cursor.selectedText());
+ cursor.removeSelectedText();
+ m_currentState->setMark(-1);
+ m_currentState->endOwnAction(KeysActionOther);
+}
+
+void EmacsKeysPlugin::yank()
+{
+ if (!m_currentEditorWidget)
+ return;
+
+ m_currentState->beginOwnAction();
+ m_currentEditorWidget->paste();
+ m_currentState->setMark(-1);
+ m_currentState->endOwnAction(KeysActionOther);
+}
+
+void EmacsKeysPlugin::scrollHalfDown() { genericVScroll(1); }
+void EmacsKeysPlugin::scrollHalfUp() { genericVScroll(-1); }
+
+void EmacsKeysPlugin::deleteCharacter()
+{
+ if (!m_currentEditorWidget)
+ return;
+ m_currentState->beginOwnAction();
+ m_currentEditorWidget->textCursor().deleteChar();
+ m_currentState->endOwnAction(KeysActionOther);
+}
+
+void EmacsKeysPlugin::killWord()
+{
+ if (!m_currentEditorWidget)
+ return;
+ m_currentState->beginOwnAction();
+ QTextCursor cursor = m_currentEditorWidget->textCursor();
+ cursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor);
+ if (m_currentState->lastAction() == KeysActionKillWord) {
+ QApplication::clipboard()->setText(
+ QApplication::clipboard()->text() + cursor.selectedText());
+ } else {
+ QApplication::clipboard()->setText(cursor.selectedText());
+ }
+ cursor.removeSelectedText();
+ m_currentState->endOwnAction(KeysActionKillWord);
+}
+
+void EmacsKeysPlugin::killLine()
+{
+ if (!m_currentEditorWidget)
+ return;
+
+ m_currentState->beginOwnAction();
+ QTextCursor cursor = m_currentEditorWidget->textCursor();
+ int position = cursor.position();
+ cursor.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
+ if (cursor.position() == position) {
+ // empty line
+ cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
+ }
+ if (m_currentState->lastAction() == KeysActionKillLine) {
+ QApplication::clipboard()->setText(
+ QApplication::clipboard()->text() + cursor.selectedText());
+ } else {
+ QApplication::clipboard()->setText(cursor.selectedText());
+ }
+ cursor.removeSelectedText();
+ m_currentState->endOwnAction(KeysActionKillLine);
+}
+
+void EmacsKeysPlugin::insertLineAndIndent()
+{
+ if (!m_currentEditorWidget)
+ return;
+
+ m_currentState->beginOwnAction();
+ QTextCursor cursor = m_currentEditorWidget->textCursor();
+ cursor.beginEditBlock();
+ cursor.insertBlock();
+ if (m_currentBaseTextEditorWidget != 0)
+ m_currentBaseTextEditorWidget->baseTextDocument()->autoIndent(cursor);
+ cursor.endEditBlock();
+ m_currentEditorWidget->setTextCursor(cursor);
+ m_currentState->endOwnAction(KeysActionOther);
+}
+
+QAction *EmacsKeysPlugin::registerAction(const Core::Id &id, const char *slot,
+ const QString &title)
+{
+ QAction *result = new QAction(title, this);
+ Core::ActionManager::registerAction(result, id,
+ Core::Context(Core::Constants::C_GLOBAL), true);
+
+ connect(result, SIGNAL(triggered(bool)), this, slot);
+ return result;
+}
+
+void EmacsKeysPlugin::genericGoto(QTextCursor::MoveOperation op, bool abortAssist)
+{
+ if (!m_currentEditorWidget)
+ return;
+ m_currentState->beginOwnAction();
+ QTextCursor cursor = m_currentEditorWidget->textCursor();
+ cursor.movePosition(op, m_currentState->mark() != -1 ?
+ QTextCursor::KeepAnchor : QTextCursor::MoveAnchor);
+ m_currentEditorWidget->setTextCursor(cursor);
+ if (abortAssist && m_currentBaseTextEditorWidget != 0)
+ m_currentBaseTextEditorWidget->abortAssist();
+ m_currentState->endOwnAction(KeysActionOther);
+}
+
+void EmacsKeysPlugin::genericVScroll(int direction)
+{
+ if (!m_currentEditorWidget)
+ return;
+
+ m_currentState->beginOwnAction();
+ QScrollBar *verticalScrollBar = m_currentEditorWidget->verticalScrollBar();
+ const int value = verticalScrollBar->value();
+ const int halfPageStep = verticalScrollBar->pageStep() / 2;
+ const int newValue = value + (direction > 0 ? halfPageStep : -halfPageStep);
+ verticalScrollBar->setValue(newValue);
+
+ // adjust cursor if it's out of screen
+ const QRect viewportRect = m_currentEditorWidget->viewport()->rect();
+ const QTextCursor::MoveMode mode =
+ m_currentState->mark() != -1 ?
+ QTextCursor::KeepAnchor :
+ QTextCursor::MoveAnchor ;
+ const QTextCursor::MoveOperation op =
+ m_currentEditorWidget->cursorRect().y() < 0 ?
+ QTextCursor::Down :
+ QTextCursor::Up ;
+
+ QTextCursor cursor = m_currentEditorWidget->textCursor();
+ while (!m_currentEditorWidget->cursorRect(cursor).intersects(viewportRect)) {
+ const int previousPosition = cursor.position();
+ cursor.movePosition(op, mode);
+ if (previousPosition == cursor.position())
+ break;
+ }
+ m_currentEditorWidget->setTextCursor(cursor);
+ m_currentState->endOwnAction(KeysActionOther);
+}
+
+Q_EXPORT_PLUGIN2(EmacsKeys, EmacsKeysPlugin)
diff --git a/src/plugins/emacskeys/emacskeysplugin.h b/src/plugins/emacskeys/emacskeysplugin.h
new file mode 100644
index 0000000000..716f70cf32
--- /dev/null
+++ b/src/plugins/emacskeys/emacskeysplugin.h
@@ -0,0 +1,111 @@
+/**************************************************************************
+**
+** Copyright (c) nsf <no.smile.face@gmail.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef EMACSKEYS_H
+#define EMACSKEYS_H
+
+#include <extensionsystem/iplugin.h>
+
+#include <QTextCursor>
+
+// forward declarations
+QT_FORWARD_DECLARE_CLASS(QAction)
+QT_FORWARD_DECLARE_CLASS(QPlainTextEdit)
+
+namespace Core {
+class Id;
+class IEditor;
+}
+namespace TextEditor {
+class BaseTextEditorWidget;
+}
+
+namespace EmacsKeys {
+namespace Internal {
+
+class EmacsKeysState;
+
+class EmacsKeysPlugin : public ExtensionSystem::IPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "EmacsKeys.json")
+
+public:
+ EmacsKeysPlugin();
+ ~EmacsKeysPlugin();
+
+ bool initialize(const QStringList &arguments, QString *errorString);
+ void extensionsInitialized();
+ ShutdownFlag aboutToShutdown();
+
+private slots:
+ void editorAboutToClose(Core::IEditor *editor);
+ void currentEditorChanged(Core::IEditor *editor);
+
+ void deleteCharacter(); // C-d
+ void killWord(); // M-d
+ void killLine(); // C-k
+ void insertLineAndIndent(); // C-j
+
+ void gotoFileStart(); // M-<
+ void gotoFileEnd(); // M->
+ void gotoLineStart(); // C-a
+ void gotoLineEnd(); // C-e
+ void gotoNextLine(); // C-n
+ void gotoPreviousLine(); // C-p
+ void gotoNextCharacter(); // C-f
+ void gotoPreviousCharacter(); // C-b
+ void gotoNextWord(); // M-f
+ void gotoPreviousWord(); // M-b
+
+ void mark(); // C-SPC
+ void exchangeCursorAndMark(); // C-x C-x
+ void copy(); // M-w
+ void cut(); // C-w
+ void yank(); // C-y
+
+ void scrollHalfDown(); // C-v
+ void scrollHalfUp(); // M-v
+
+private:
+ QAction *registerAction(const Core::Id &id, const char *slot,
+ const QString &title);
+ void genericGoto(QTextCursor::MoveOperation op, bool abortAssist = true);
+ void genericVScroll(int direction);
+
+ QHash<QPlainTextEdit*, EmacsKeysState*> m_stateMap;
+ QPlainTextEdit *m_currentEditorWidget;
+ EmacsKeysState *m_currentState;
+ TextEditor::BaseTextEditorWidget *m_currentBaseTextEditorWidget;
+};
+
+} // namespace Internal
+} // namespace EmacsKeys
+
+#endif // EMACSKEYS_H
diff --git a/src/plugins/emacskeys/emacskeysstate.cpp b/src/plugins/emacskeys/emacskeysstate.cpp
new file mode 100644
index 0000000000..899c090f40
--- /dev/null
+++ b/src/plugins/emacskeys/emacskeysstate.cpp
@@ -0,0 +1,86 @@
+/**************************************************************************
+**
+** Copyright (c) nsf <no.smile.face@gmail.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "emacskeysstate.h"
+
+#include <QTextCursor>
+#include <QPlainTextEdit>
+
+using namespace EmacsKeys::Internal;
+
+//---------------------------------------------------------------------------
+// EmacsKeysState
+//---------------------------------------------------------------------------
+
+EmacsKeysState::EmacsKeysState(QPlainTextEdit *edit):
+ m_ignore3rdParty(false),
+ m_mark(-1),
+ m_lastAction(KeysAction3rdParty),
+ m_editorWidget(edit)
+{
+ connect(edit, SIGNAL(cursorPositionChanged()),
+ this, SLOT(cursorPositionChanged()));
+ connect(edit, SIGNAL(textChanged()),
+ this, SLOT(textChanged()));
+ connect(edit, SIGNAL(selectionChanged()),
+ this, SLOT(selectionChanged()));
+}
+
+EmacsKeysState::~EmacsKeysState() {}
+
+void EmacsKeysState::setLastAction(EmacsKeysAction action)
+{
+ if (m_mark != -1) {
+ // this code can be triggered only by 3rd party actions
+ beginOwnAction();
+ QTextCursor cursor = m_editorWidget->textCursor();
+ cursor.clearSelection();
+ m_editorWidget->setTextCursor(cursor);
+ m_mark = -1;
+ endOwnAction(action);
+ } else {
+ m_lastAction = action;
+ }
+}
+
+void EmacsKeysState::cursorPositionChanged() {
+ if (!m_ignore3rdParty)
+ setLastAction(KeysAction3rdParty);
+}
+
+void EmacsKeysState::textChanged() {
+ if (!m_ignore3rdParty)
+ setLastAction(KeysAction3rdParty);
+}
+
+void EmacsKeysState::selectionChanged()
+{
+ if (!m_ignore3rdParty)
+ setLastAction(KeysAction3rdParty);
+}
diff --git a/src/plugins/emacskeys/emacskeysstate.h b/src/plugins/emacskeys/emacskeysstate.h
new file mode 100644
index 0000000000..5c92abf226
--- /dev/null
+++ b/src/plugins/emacskeys/emacskeysstate.h
@@ -0,0 +1,80 @@
+/**************************************************************************
+**
+** Copyright (c) nsf <no.smile.face@gmail.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef EMACSKEYSSTATE_H
+#define EMACSKEYSSTATE_H
+
+#include <QObject>
+
+QT_FORWARD_DECLARE_CLASS(QPlainTextEdit)
+
+namespace EmacsKeys {
+namespace Internal {
+
+enum EmacsKeysAction {
+ KeysAction3rdParty,
+ KeysActionKillWord,
+ KeysActionKillLine,
+ KeysActionOther,
+};
+
+class EmacsKeysState : public QObject
+{
+ Q_OBJECT
+
+public:
+ EmacsKeysState(QPlainTextEdit *edit);
+ ~EmacsKeysState();
+ void setLastAction(EmacsKeysAction action);
+ void beginOwnAction() { m_ignore3rdParty = true; }
+ void endOwnAction(EmacsKeysAction action) {
+ m_ignore3rdParty = false;
+ m_lastAction = action;
+ }
+ EmacsKeysAction lastAction() const { return m_lastAction; }
+
+ int mark() const { return m_mark; }
+ void setMark(int mark) { m_mark = mark; }
+
+private slots:
+ void cursorPositionChanged();
+ void textChanged();
+ void selectionChanged();
+
+private:
+ bool m_ignore3rdParty;
+ int m_mark;
+ EmacsKeysAction m_lastAction;
+ QPlainTextEdit *m_editorWidget;
+};
+
+} // namespace Internal
+} // namespace EmacsKeys
+
+#endif // EMACSKEYSSTATE_H