前へ 次へ
技術文章qvi

何の脈絡もないが、今回は文字を削除する x, X コマンドと undo/redo を行う u, U コマンドを実装してみる。

x はカーソル位置の文字を、X はカーソル直前文字を削除する。3x の様に削除する文字数を指定することもできる。

オリジナル vi では undo は直前のコマンドを取り消すのみで、再度 u を実行すると再実行(redo)となる (更に u を実行すると undo となる。以下繰り返し)。 が、今時そんな undo では vi 原理主義者の人でも許してくれそうにないので、U を再実行(redo)コマンドに割り当てることにする。

コマンド認識

x, X, u, U コマンドを認識するには、ViEngine::cmdModeKeyPressEvent() の switch 文に case 文を追加するだけだ。 実際の処理は ViEditView の各処理メソッドに任せる。

ソースは下記の様になる。

 1: bool ViEngine::cmdModeKeyPressEvent(QKeyEvent *event)
 2: {
 3:     .....
 4:     switch( ch ) {
 5:     case 'x':
 6:         m_editor->doDelete(repeatCount());
 7:         break;
 5:     case 'X':
 6:         m_editor->doDelete(-repeatCount());
 7:         break;
 8:     case 'u':
 9:         m_editor->doUndo(repeatCount());
10:         break;
11:     case 'U':
12:         m_editor->doRedo(repeatCount());
13:         break;
14:     .....
15:     }
16:     .....
17: }

処理メソッド

まずは削除処理メソッド。

基本的には textcursor() でカーソルを取得し、カーソル位置に対する削除メソッドを呼んであげればよい。
テキストが選択されている場合は選択文字列を削除、そうでない場合は指定文字数だけ削除を行う。
ただし、x, X コマンドでは改行は削除されないので、n が大きくても改行を超えて削除しないように処理を行う。

 1: void ViEditView::doDelete(int n)
 2: {
 3:     QTextCursor cur = textCursor();
 4:     if( !cur.hasSelection() ) {
 1:         const int pos = cur.position();
 2:         int dst;
 3:         if( n > 0 ) {
 4:             cur.movePosition(QTextCursor::EndOfBlock);
 5:             const int endpos = cur.position();
 6:             if( pos == endpos ) return;     //  改行は x では削除されない
 7:             dst = qMin(pos + n, endpos);    //  n 文字移動 or 改行位置
 8:             cur.setPosition(pos);       //  現カーソル位置へ移動
 9:         } else {
10:             const int blockPos = cur.block().position();
11:             if( pos == blockPos ) return;   //  行頭より前は X では削除されない
12:             dst = qMax(pos + n, blockPos);  //  n 文字移動 or 改行位置
13:         }
14:         cur.setPosition(dst, QTextCursor::KeepAnchor);      //  カーソル位置からn文字 or 改行直前まで選択
15:     }
16:     cur.deleteChar();
17: }

undo/redo の処理は簡単だ。QPlainTextEdit が既に undo/redo 機能を持っているのでそれを繰り返し回数だけ呼べばよい。

 1: void ViEditView::doUndo(int n)
 2: {
 3:     for(int i = 0; i < n; ++i)
 4:         undo();
 5: }
 6: void ViEditView::doRedo(int n)
 7: {
 8:     for(int i = 0; i < n; ++i)
 9:         redo();
10: }

おわりに

Tweet


前へ 次へ
技術文章qvi