内部構造>Window Messageの流れ
2000/09/28
物理行,論理行,レイアウト行,キャレット位置など位置を表す言葉がソースコード上でいろいろ使われています.行の数え方には改行までを1行と数える方法と画面上での折り返し位置までを1行と数える方法がありますが,これらについての用語がソースコード中では統一されていません.
サクラエディタのソースコードではこれらの言葉は以下のように使い分けられています.
改行記号で区切られた1つの固まり...論理行, 物理行
画面上で左端から折り返し位置まで...レイアウト行,キャレット位置
関数によって論理行を受け取るものとレイアウト行を受け取るものがありますが,これを間違えると間違いなく「不正な処理」で落ちますので,ここをはっきりさせることはきわめて重要です.
このページでは前者を「論理行」,後者を「レイアウト行」と呼ぶことにします.
基本的にはCDocLineMgrとその関係関数には論理行, CLayoutMgrにはレイアウト行を渡すようになっていると思います.せっかくですから,ここで主要な関数についてどちらを渡すべきか記しておきます.
論理位置とレイアウト位置を相互に変換するには以下の関数を用います.
int nX, // 論理行X int nY, // 論理行Y int* pnCaretPosX, //レイアウト行X int* pnCaretPosY // レイアウト行Y
論理行での位置(nX, nY)からレイアウト行での位置(nCaretPosX, nCaretPosY)に変換する.
int nCaretPosX, //レイアウト行X int nCaretPosY, //レイアウト行Y int* pnX, // 論理行X int* pnY // 論理行Y
レイアウト行での位置(nCaretPosX, nCaretPosY)から物理行での位置(nX, nY)に変換する.
int nLine, // 論理行番号 int *pnLineLen // 該当行の長さを受け取る
指定された論理行番号に対応する文字列,文字列長を取得する.文字列長はレイアウト行1行に含まれる長さ.
int nLine, // 論理行番号 int *pnLineLen, // 該当行の長さを受け取る const CLayout** ppcLayoutDes // CLayoutへのポインタ
GetLineStrで取得できる行の長さに加えて,CLayout へのポインタも返す.
テキストエディタでは文字列データはCDocLineMgrクラスが保持しています.このクラスでは論理行1行のデータを1つのCDocLineクラスが管理するようになっています.
一方,CLayoutではデータの代わりに折り返し位置,すなわち対応する論理行と開始文字位置を保持しています.
CLayoutのメンバーは以下のようになっています.
番号 | メンバー | 内容 |
---|---|---|
1 | CLayout* m_pPrev; | リストのメンバーなので,前後へのポインタを持つ. |
2 | CLayout* m_pNext; | ↑ |
3 | int m_nLinePhysical; | 対応する改行単位の行の番号(論理行番号) |
4 | const CDocLine* m_pCDocLine; | 対応する論理行を保持する要素へのポインタ |
5 | int m_nOffset; | 対応する改行単位の行の先頭からのオフセット |
6 | int m_nLength; | このレイアウト行の長さ(ハイト数) |
7 | int m_nTypePrev; | タイプ* |
8 | int m_nTypeNext; | ↑ |
9 | CEOL m_cEol; | 改行コード種別 |
タイプ 0=通常 1=行コメント 2=ブロックコメント 3=シングルクォーテーション文字列 4=ダブルクォーテーション文字列
CLayoutMgr::Searchは論理行番号に対応するレイアウト行の情報を取得する関数です.レイアウト行番号から物理行番号を知るのは書かれている番号を返すだけなので簡単ですが,その逆は大変です.リストは先頭または末尾からしかたどることが出来ないので行数nのオーダーO(n)で遅くなります.
この関数では以下のような高速化手法が使われています.
1はファイルの後半を探すときの速度が約1/2になるというもので,目に見えるほどの効果は出ていないのではないかと思います.
2は計算量だけ考えると大したことがないように思われるかもしれませんが,実際には連続した行番号で関数が呼び出されることが多いため,かなりの効果が見込めます.
この関数では該当する行番号を持つ要素を見つけ次第結果を返していますが,論理行1行に対して複数のレイアウト行が対応するので,上に示した手法では順方向に探すときには先頭部分のレイアウト行要素が,逆方向に探すときは末尾部分のレイアウト行要素が見つかってしまいます.
行の挿入,削除や折り返し位置の変更を行うなど論理行に変更があったときは論理行番号を更新する必要があります.
CLayoutMgr::DoLayoutと CLayoutMgr::DoLayout3_Newが番号を更新するための関数です.両者の違いは全体を更新するか一部分を更新するかです.