前へ 次へ
技術文章qvi

まずは Qt が用意しているテキストエディタークラスの QPlainTextEdit にコマンドモードを実装してみる。

QPlainTextEdit

新規プロジェクトを作成し、QPlainTextEdit の派生クラス ViEditView をプロジェクトに追加する。

 1: #include 
 2: 
 3: class ViEditView : public QPlainTextEdit
 4: {
 5:     Q_OBJECT
 6: 
 7: public:
 8:     ViEditView(QWidget *parent = 0);
 9:     ~ViEditView();
10: };

んで、ViEditView を MainWindow のセントラルウィジットに指定する。

 1: #include "ViEditView.h"
 2: 
 3: MainWindow::MainWindow(QWidget *parent, Qt::WFlags flags)
 4:     : QMainWindow(parent, flags)
 5: {
 6:     m_editor = new ViEditView;
 7:     setCentralWidget(m_editor);
 8: }

たったこれだけで、下図の様な最低限のテキストエディタが出来上がる。

Qt は非常に生産性が高いと言われている。それは、以下の2つの要因から来ていると筆者は考えている。

  1. フレームワークの対称性が非常に高く統一的で、欲しい機能が想像したように存在する
  2. 機能が豊富で再利用可能なクラスが充実しており、クラスをゼロから実装する必要がない

本稿は後者の実例である。

挿入・コマンドモード遷移

vi エディタの最大の特徴と言えば、良くも悪くもコマンドモードと挿入モードが存在するということである。
初期状態はコマンドモードで、英数字・記号はカーソル移動、編集などのコマンドとして解釈される。 文字を挿入したい場合は i コマンドなどで挿入モードに遷移しなくてはいけない。 挿入モードを終了し、コマンドモードに戻るには Esc を押す。
例えば“abc”という文字列をカーソル位置に挿入したい時は iabc Esc と打鍵する。

vi をよく知らない人は、 モードを切り替えるために余計なキー(i、Esc)を押す必要があり打鍵数が増えるじゃないか、と思うかもしれない。 しかし、コマンドモードがあるおかげで、Ctrl を多用して左小指を痛めることもないし、 コマンドに割り当てるキーの自由度が増すので、結果的に打鍵数そのものが減り、編集速度も高速になるのだ。

挿入モードで Esc を押したらコマンドモード、コマンドモードで i を押したら挿入モードに遷移するようにしてみよう。

そのためには、まずモード状態を表す enum型を宣言し、そのメンバ変数を宣言し、セッターゲッターを定義する。
Qt ではゲッターは変数名そのもの、セッターは set を付ける流儀なのでそれに従った。
メンバ変数には、MFCからの慣習で m_ を前置している。

 1: class ViEditView : public QPlainTextEdit
 2: {
 3:     Q_OBJECT
 4: 
 5: public:
 6:     enum Mode {
 7:         CMD = 0,
 8:         INSERT,
 9:     };
10: 
11: public:
12:     ViEditView(QWidget *parent = 0);
13:     ~ViEditView();
14: 
15: public:
16:     Mode    mode() const { return m_mode; }
17: 
18: public:
19:     void    setMode(Mode mode) { m_mode = mode; }
20: 
21: private:
22:     Mode    m_mode;
23: };

m_mode はコンストラクタで CMD に初期化しておく。

 1: ViEditView::ViEditView(QWidget *parent)
 2:     : m_mode(CMD)
 3:     , QPlainTextEdit(parent)
 3: {
 4: }

Esc または i が押されたらモードを切り替え、コマンドモードではとりあえず i 以外は無視することにする。 そのために keyPressEvent() をリインプリメントする。
keyPressEvent() はその名の通り、キーが押されたときにコールされるイベントハンドラだ。

 1: class ViEditView : public QPlainTextEdit
 2: {
 3: .....
 4: protected:
 5:     void    keyPressEvent ( QKeyEvent * event );
 6: .....
 7: };

さて、keyPressEvent() の処理だが、まず、モードにより処理が大きく分かれる。
今はコマンドモードと挿入モードしか無いので、switch 文で分岐してそれぞれの処理メソッドをコールする。

 1: void ViEditView::keyPressEvent ( QKeyEvent * event )
 2: {
 3:     switch( mode() ) {
 4:     case CMD:
 5:         cmdModeKeyPressEvent(event);    //  コマンドモードの場合の処理
 6:         break;
 7:     case INSERT:
 8:         insModeKeyPressEvent(event);    //  挿入モードの場合の処理
 9:         break;
10:     }
11: }

コマンドモードの場合、i が押されたら挿入モードに切り替える。

 1: void ViEditView::cmdModeKeyPressEvent(QKeyEvent *event)
 2: {
 3:     QString text = event->text();
 4:     if( text == "i" ) {
 5:         setMode(INSERT);    //  i が押されたら挿入モードへ
 6:         return;
 7:     }
 8:     //	とりあえず、i 以外のキー入力は無視
 9: }

挿入モードの場合、Esc が押されたらコマンドモードに切り替える。

 1: void ViEditView::insModeKeyPressEvent(QKeyEvent *event)
 2: {
 3:     if( event->key() == Qt::Key_Escape ) {
 4:         setMode(CMD);       //  Esc が押されたらコマンドモードへ
 5:         return;
 6:     }
 7:     QPlainTextEdit::keyPressEvent(event);   //  Esc キー以外は通常処理
 8: }

以上で、QPlainTextEdit に vi のコマンドモードを導入することができた、というわけでござるぞ。

まとめ

下記の処理を行えば、コマンドモードをサポートすることができるぞ。

本稿のソースコードは以下からDLできます。

    http://vivi.dyndns.org/dist2/qvi-001.zip

※ VS2008 でプロジェクトを作成していますが、qvi-001/qvi-001.pro を読みこめば QtCreator でもビルドできます。

Tweet


前へ 次へ
技術文章qvi