fakevimplugin.cpp 11.8 KB
Newer Older
hjk's avatar
hjk committed
1 2 3 4
/***************************************************************************
**
** This file is part of Qt Creator
**
5
** Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
hjk's avatar
hjk committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
**
** Contact:  Qt Software Information (qt-info@nokia.com)
**
**
** Non-Open Source Usage
**
** Licensees may use this file in accordance with the Qt Beta Version
** License Agreement, Agreement version 2.2 provided with the Software or,
** alternatively, in accordance with the terms contained in a written
** agreement between you and Nokia.
**
** GNU General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU General
** Public License versions 2.0 or 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the packaging
** of this file.  Please review the following information to ensure GNU
** General Public Licensing requirements will be met:
**
** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
** http://www.gnu.org/copyleft/gpl.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt GPL Exception
** version 1.3, included in the file GPL_EXCEPTION.txt in this package.
**
***************************************************************************/

#include "fakevimplugin.h"

36 37
#include "fakevimconstants.h"
#include "fakevimhandler.h"
hjk's avatar
hjk committed
38

39
#include <coreplugin/actionmanager/actionmanager.h>
hjk's avatar
hjk committed
40 41
#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.h>
42
#include <coreplugin/filemanager.h>
hjk's avatar
hjk committed
43
#include <coreplugin/icore.h>
44
#include <coreplugin/ifile.h>
hjk's avatar
hjk committed
45 46 47 48 49 50 51 52 53 54 55
#include <coreplugin/messagemanager.h>
#include <coreplugin/modemanager.h>
#include <coreplugin/uniqueidmanager.h>

#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/session.h>

#include <texteditor/basetexteditor.h>
#include <texteditor/basetextmark.h>
#include <texteditor/itexteditor.h>
#include <texteditor/texteditorconstants.h>
56
#include <texteditor/interactionsettings.h>
57 58
#include <texteditor/tabsettings.h>
#include <texteditor/texteditorsettings.h>
hjk's avatar
hjk committed
59 60 61 62

#include <utils/qtcassert.h>

#include <QtCore/QDebug>
63
#include <QtCore/QtPlugin>
hjk's avatar
hjk committed
64 65 66 67
#include <QtCore/QObject>
#include <QtCore/QPoint>
#include <QtCore/QSettings>

68
#include <QtGui/QMessageBox>
hjk's avatar
hjk committed
69 70 71
#include <QtGui/QPlainTextEdit>
#include <QtGui/QTextBlock>
#include <QtGui/QTextCursor>
72
#include <QtGui/QTextEdit>
hjk's avatar
hjk committed
73 74 75 76 77 78 79 80 81 82 83 84


using namespace FakeVim::Internal;
using namespace TextEditor;
using namespace Core;
using namespace ProjectExplorer;


namespace FakeVim {
namespace Constants {

const char * const INSTALL_HANDLER        = "FakeVim.InstallHandler";
85
const char * const MINI_BUFFER            = "FakeVim.MiniBuffer";
hjk's avatar
hjk committed
86 87 88 89 90 91 92 93
const char * const INSTALL_KEY            = "Alt+V,Alt+V";

} // namespace Constants
} // namespace FakeVim


///////////////////////////////////////////////////////////////////////
//
94
// FakeVimPluginPrivate
hjk's avatar
hjk committed
95 96 97
//
///////////////////////////////////////////////////////////////////////

98 99 100 101
namespace FakeVim {
namespace Internal {

class FakeVimPluginPrivate : public QObject
hjk's avatar
hjk committed
102
{
103 104 105 106 107 108 109
    Q_OBJECT

public:
    FakeVimPluginPrivate(FakeVimPlugin *);
    ~FakeVimPluginPrivate();
    friend class FakeVimPlugin;

110
    bool initialize();
111 112 113 114 115
    void shutdown();

private slots:
    void editorOpened(Core::IEditor *);
    void editorAboutToClose(Core::IEditor *);
116

hjk's avatar
hjk committed
117
    void installHandlerOnCurrentEditor();
118 119 120 121 122 123 124
    void installHandler(Core::IEditor *editor);
    void removeHandler();

    void showCommandBuffer(const QString &contents);
    void showExtraInformation(const QString &msg);
    void changeSelection(const QList<QTextEdit::ExtraSelection> &selections);
    void writeFile(bool *handled, const QString &fileName, const QString &contents);
125
    void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor);
126 127 128 129 130 131 132 133 134 135 136 137 138

private:
    FakeVimPlugin *q;
    QAction *m_installHandlerAction; 
};

} // namespace Internal
} // namespace FakeVim

FakeVimPluginPrivate::FakeVimPluginPrivate(FakeVimPlugin *plugin)
{       
    q = plugin;
    m_installHandlerAction = 0;
hjk's avatar
hjk committed
139 140
}

141 142 143
FakeVimPluginPrivate::~FakeVimPluginPrivate()
{
}
hjk's avatar
hjk committed
144

145
void FakeVimPluginPrivate::shutdown()
hjk's avatar
hjk committed
146 147 148
{
}

149
bool FakeVimPluginPrivate::initialize()
hjk's avatar
hjk committed
150
{
hjk's avatar
hjk committed
151
    Core::ActionManager *actionManager = Core::ICore::instance()->actionManager();
hjk's avatar
hjk committed
152 153 154 155 156 157 158 159
    QTC_ASSERT(actionManager, return false);

    QList<int> globalcontext;
    globalcontext << Core::Constants::C_GLOBAL_ID;

    m_installHandlerAction = new QAction(this);
    m_installHandlerAction->setText(tr("Set vi-Style Keyboard Action Handler"));
    
con's avatar
con committed
160
    Core::Command *cmd = 0;
hjk's avatar
hjk committed
161 162 163 164
    cmd = actionManager->registerAction(m_installHandlerAction,
        Constants::INSTALL_HANDLER, globalcontext);
    cmd->setDefaultKeySequence(QKeySequence(Constants::INSTALL_KEY));

165
    ActionContainer *advancedMenu =
hjk's avatar
hjk committed
166
        actionManager->actionContainer(Core::Constants::M_EDIT_ADVANCED);
167
    advancedMenu->addAction(cmd, Core::Constants::G_EDIT_EDITOR);
hjk's avatar
hjk committed
168 169

    connect(m_installHandlerAction, SIGNAL(triggered()),
hjk's avatar
hjk committed
170
        this, SLOT(installHandlerOnCurrentEditor()));
171 172

    // EditorManager
hjk's avatar
hjk committed
173
    QObject *editorManager = Core::ICore::instance()->editorManager();
174 175 176 177 178
    connect(editorManager, SIGNAL(editorAboutToClose(Core::IEditor*)),
        this, SLOT(editorAboutToClose(Core::IEditor*)));
    connect(editorManager, SIGNAL(editorOpened(Core::IEditor*)),
        this, SLOT(editorOpened(Core::IEditor*)));

hjk's avatar
hjk committed
179 180 181
    return true;
}

182
void FakeVimPluginPrivate::installHandler(Core::IEditor *editor)
183
{
184 185 186
    if (!editor)
        return;

187
    QWidget *widget = editor->widget();
188 189
    if (!widget)
        return;
190 191 192 193

    // we can only handle QTextEdit and QPlainTextEdit
    if (!qobject_cast<QTextEdit *>(widget) && !qobject_cast<QPlainTextEdit *>(widget))
        return;
194
    
195
    FakeVimHandler *handler = new FakeVimHandler(widget, widget);
196 197 198 199

    connect(handler, SIGNAL(extraInformationChanged(QString)),
        this, SLOT(showExtraInformation(QString)));
    connect(handler, SIGNAL(commandBufferChanged(QString)),
hjk's avatar
hjk committed
200
        this, SLOT(showCommandBuffer(QString)));
201 202 203 204 205 206
    connect(handler, SIGNAL(quitRequested()),
        this, SLOT(removeHandler()), Qt::QueuedConnection);
    connect(handler, SIGNAL(writeFileRequested(bool*,QString,QString)),
        this, SLOT(writeFile(bool*,QString,QString)));
    connect(handler, SIGNAL(selectionChanged(QList<QTextEdit::ExtraSelection>)),
        this, SLOT(changeSelection(QList<QTextEdit::ExtraSelection>)));
207 208
    connect(handler, SIGNAL(moveToMatchingParenthesis(bool*,bool*,QTextCursor*)),
        this, SLOT(moveToMatchingParenthesis(bool*,bool*,QTextCursor*)));
209 210 211

    handler->setupWidget();
    handler->setExtraData(editor);
212

213
    if (BaseTextEditor *bt = qobject_cast<BaseTextEditor *>(widget)) {
214 215
        using namespace TextEditor;
        using namespace FakeVim::Constants;
216
        handler->setCurrentFileName(editor->file()->fileName());
217
        TabSettings settings = bt->tabSettings();
218
        handler->setConfigValue(ConfigTabStop,
219
            QString::number(settings.m_tabSize));
220
        handler->setConfigValue(ConfigShiftWidth,
221
            QString::number(settings.m_indentSize));
222
        handler->setConfigValue(ConfigExpandTab,
223
            settings.m_spacesForTabs ? ConfigOn : ConfigOff);
224
        handler->setConfigValue(ConfigSmartTab,
225
            settings.m_smartBackspace ? ConfigOn : ConfigOff); 
226
        handler->setConfigValue(ConfigAutoIndent,
227
            settings.m_autoIndent ? ConfigOn : ConfigOff); 
228
    }
hjk's avatar
hjk committed
229 230
}

hjk's avatar
hjk committed
231 232 233 234 235
void FakeVimPluginPrivate::installHandlerOnCurrentEditor()
{
    installHandler(EditorManager::instance()->currentEditor());
}

236 237
void FakeVimPluginPrivate::writeFile(bool *handled,
    const QString &fileName, const QString &contents)
238
{
239 240
    Q_UNUSED(contents);

241 242 243 244 245 246
    FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
    if (!handler)
        return;

    Core::IEditor *editor = qobject_cast<Core::IEditor *>(handler->extraData());
    if (editor && editor->file()->fileName() == fileName) {
hjk's avatar
hjk committed
247
        // Handle that as a special case for nicer interaction with core
248
        Core::IFile *file = editor->file();
hjk's avatar
hjk committed
249
        Core::ICore::instance()->fileManager()->blockFileChange(file);
250
        file->save(fileName);
hjk's avatar
hjk committed
251
        Core::ICore::instance()->fileManager()->unblockFileChange(file);
252 253
        *handled = true;
    } 
254 255
}

256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
void FakeVimPluginPrivate::moveToMatchingParenthesis(bool *moved, bool *forward,
        QTextCursor *cursor)
{
    *moved = false;

    bool undoFakeEOL = false;
    if (cursor->atBlockEnd() && cursor->block().length() > 1) {
        cursor->movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, 1);
        undoFakeEOL = true;
    }
    TextEditor::TextBlockUserData::MatchType match
        = TextEditor::TextBlockUserData::matchCursorForward(cursor);
    if (match == TextEditor::TextBlockUserData::Match) {
        *moved = true;
        *forward = true;
   } else {
        if (undoFakeEOL)
            cursor->movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, 1);
        if (match == TextEditor::TextBlockUserData::NoMatch) {
            // backward matching is according to the character before the cursor
            bool undoMove = false;
            if (!cursor->atBlockEnd()) {
                cursor->movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, 1);
                undoMove = true;
            }
            match = TextEditor::TextBlockUserData::matchCursorBackward(cursor);
            if (match == TextEditor::TextBlockUserData::Match) {
                *moved = true;
                *forward = false;
            } else if (undoMove) {
                cursor->movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, 1);
            }
        }
    }
}

292
void FakeVimPluginPrivate::removeHandler()
hjk's avatar
hjk committed
293
{
294 295 296 297
    if (FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender())) {
        handler->restoreWidget();
        handler->deleteLater();
    }
hjk's avatar
hjk committed
298 299
    Core::EditorManager::instance()->hideEditorInfoBar(
        QLatin1String(Constants::MINI_BUFFER));
hjk's avatar
hjk committed
300 301
}

302
void FakeVimPluginPrivate::editorOpened(Core::IEditor *editor)
303 304
{
    //qDebug() << "OPENING: " << editor << editor->widget();
305 306 307 308
    QSettings *s = ICore::instance()->settings();
    bool automatic = s->value("textInteractionSettings/UseVim").toBool();
    if (automatic)
       installHandler(editor);
309 310
}

311
void FakeVimPluginPrivate::editorAboutToClose(Core::IEditor *editor)
312
{
hjk's avatar
hjk committed
313
    //qDebug() << "CLOSING: " << editor << editor->widget();
314
    Q_UNUSED(editor);
315 316
}

317
void FakeVimPluginPrivate::showCommandBuffer(const QString &contents)
hjk's avatar
hjk committed
318
{
319 320 321 322 323 324 325
    //qDebug() << "SHOW COMMAND BUFFER" << contents;
    FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
    if (handler) {
        Core::EditorManager::instance()->showEditorInfoBar( 
            QLatin1String(Constants::MINI_BUFFER), contents,
            tr("Quit FakeVim"), handler, SLOT(quit()));
    }
hjk's avatar
hjk committed
326 327
}

328
void FakeVimPluginPrivate::showExtraInformation(const QString &text)
329
{
330 331 332
    FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
    if (handler)
        QMessageBox::information(handler->widget(), tr("FakeVim Information"), text);
333 334
}

335 336
void FakeVimPluginPrivate::changeSelection
    (const QList<QTextEdit::ExtraSelection> &selection)
337
{
338 339 340
    if (FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender()))
        if (BaseTextEditor *bt = qobject_cast<BaseTextEditor *>(handler->widget()))
            bt->setExtraSelections(BaseTextEditor::FakeVimSelection, selection);
341 342
}

343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358

///////////////////////////////////////////////////////////////////////
//
// FakeVimPlugin
//
///////////////////////////////////////////////////////////////////////

FakeVimPlugin::FakeVimPlugin()
    : d(new FakeVimPluginPrivate(this))
{}

FakeVimPlugin::~FakeVimPlugin()
{
    delete d;
}

359
bool FakeVimPlugin::initialize(const QStringList &arguments, QString *errorMessage)
360
{
361 362 363
    Q_UNUSED(arguments);
    Q_UNUSED(errorMessage);
    return d->initialize();
364 365 366 367 368 369 370 371 372 373 374 375
}

void FakeVimPlugin::shutdown()
{
    d->shutdown();
}

void FakeVimPlugin::extensionsInitialized()
{
}

#include "fakevimplugin.moc"
hjk's avatar
hjk committed
376 377

Q_EXPORT_PLUGIN(FakeVimPlugin)