リッチエディットの話(4)(2000/02/05)
新着情報 トップメニュー ボートメニュー 掲示板 お手紙はここ!

アンドゥ実装できました……苦労しました。



何もしない状態ならば、アンドゥリッチエディットに任せてしまえばいいのですが。今回はタグの自動着色と言う事で、範囲選択やら何やらビシバシ入ってます。

リッチエディットの場合、着色はアンドゥバッファに影響を与えてるってしってた?(爆死) 着色と言うか、フォントの設定を変えると、修正情報がフラッシュされてしまいます……なので、フォントの大きさを変えるだけでも元に戻せなくなってしまうのです。ハマった……

打つ手無しなので、諦めて自前でアンドゥを実装する事にしました。



アンドゥを実行する事自体は簡単なんですよね。TRichEdit では Undo メソッドは静的メソッドなので、継承クラスで dynamic で宣言しなおして inherited しません。どうせ、本来のアンドゥは使い物にならないので(笑) アプリ側で CTRL+Z をトラップしなければ成りませんが、その辺はアクションとか使いました。

複数回アンドゥ……いわゆる多階層アンドゥ、これについては段取りの面倒さとかメモリ確保の鬱陶しさなどから実装を見送りました。必須メモリが 32MB ぐらいに成長してくる頃には密かに実装されているでしょー(笑)


さて実際のコーディングですが、リドゥと言って、CTRL+Z を再び押す事で「やり直しのやり直し」になるようにしました。簡単にスワップというローテクを使ってるだけです。

バッファにはストリームを使いました。モノがモノなので Text プロパティでも十二分に使えますが、再着色など省ける追加処理を省こうと思ったのな。


procedure TCustomTagEdit.Undo;
var
    Wrk: TMemoryStream;
begin
    FUndoWorking := True;

    Wrk := TMemoryStream.Create;
    try
        Self.SaveToStream( Wrk );
        Self.LoadFromStream( FUndoBuffer );
        Self.Perform( EM_LINESCROLL, 0, FUndoLine );
        Self.SelLength:= FUndoLength;
        Self.SelStart := FUndoStart;
        FUndoBuffer.LoadFromStream( Wrk );

    finally
        FLastKeyCtgr := not FLastKeyCtgr;
        Wrk.Free;

        FUndoWorking := False;
    end;
end;




アンドゥ自体は瞬時に完成したが、実際にやり直しができるようにするとなると、そりゃー色々と問題がありましたよ……。

1.どこでバッファに保存するか
2.やり直し=ロードなのでカーソルがどっか行ってしまう

2はバッファ保存時にカーソルの位置も一緒に記録しておく事で解決。コード中の FUndoLengthFUndoStart がそうです。

何より難しかったのは、何処で保存するかだったのだ。よくよく考えて見ると、着色の時と全く同じ問題にぶつかっていたって話しで……。

だもんで、最初に考えたのが着色の寸前に記録するというオーソドックスな方法。しかしそれは無駄に終わる……と言うのは、着色される時には変更が終わっているので、バッファに追いこまれるデータはアンドゥに使えるものではなくなっていたのである。これには参りました。ここで保存する事しか頭になかった俺は、ひたすら問題の周囲をぐるぐる回って悶絶するしかなかった。

結局、よい方法を見つけられなかったのだが、収穫としては「時既に遅しである」というコトだけは判ったんで、要は「編集される事を察知でき事前の状態を取得できる位置」を探すことにした。

まず最初に考え付いたのは、TRichEditProtectChange イベントに割り込む事。しかしこれは上手く行かんことがすぐに判明した。TRichEdit では、そのイベントをメッセージへのリアクションで直接呼び出す仕組みだったのだ! OnChange イベントなんかだと Change メソッドが呼出元としてあるので、OverRide すれば割りこめるのに……。

まぁ、継承したクラス側で自前の ProtectChange イベントハンドラを登録し、そのハンドラからユーザーに対して上書きした ProtectChange イベントを公開するというテもあるんだが(一見するそうとは判らない)、キャストとかでややこしくなりそうだったのでパス。


そこで OnKeyPressOnKeyDown などを利用する事に決定。OnKeyPress では認識できないキーがあり、ここではそのようなキーもチェックしたいので OnKeyDown イベントのタイミングを使うことに決定。このハンドラを呼び出す KeyDown メソッド(Protected) をオーバーライド。

ただこのメソッドはキーの押下の一回一回で呼び出されてしまうので、アンドゥとしては使い物にならないバッファになってしまう。この問題に関してはキーのカテゴリーを分け、同じカテゴリの連続ならばバッファは更新しないと言う仕様に落ちついた。

キー入力程度ならこれで良い……ただ、TRichEdit.SelText などで修正を行った時にバグが生じる事を確認している。これについては追々調査したいと考えている。




以下に最新のバージョンをアップロードしておく。現状でもそこそこは使えなくもないだろう。画像周りの利便性がないに等しいので実戦投入はまだまだ先の事。

半年近く更新してなかったのだが、実は内蔵ファイラーなどが実質的に完成していたりと進展は多い。それはリビルド番号からも解って頂けるのではないだろうか。見た目こそ違いはないでスナップショットは作らなかったが……。

限定的にだがヘルプも作成し始めた。HelpDesigner というフリーウェアで作成してあり、ソースコードも同梱してある。
また今回からインストーラ付きである。アンインストールに対応しているので、HotMilk が使うレジストリも自動で削除してくれるだろう。

あれこれと付け足したのでサイズが膨れ上がってしまったが、勘弁していただきたい。



ダウンロードする(HM007.EXE/710KB)


新着情報 トップメニュー ボートメニュー 掲示板 お手紙はここ!