折り返し処理 ■ 折り返し処理 これまでに示したプログラムは(固定幅の)水平TABをちゃんと処理し、行コメント、キーワードにも色をつけ、視認性に優れたテキスト表示が可能になっている。しかしより実用的にするは、ダブルクォート、HTNL(SGML)タグ、ブロックコメントなども認識し、指定色で表示する必要がある。やることはたくさんあり、まだまだ先は長い。 それらの処理コードを実装する前に、本稿では行を画面右端で折り返して表示するための方法に触れておく。後で、折り返し処理を入れると書き直し個所が増えるので、先にやっておこうというわけである。 各行のどの位置で折り返したかの情報を保持するクラスを CViewLineMgr と名づけることにする。MFC ではひとつのドキュメントに複数のビューを対応させることが可能で、それぞれのビューの幅は同一とは限らず、折り返し位置はビューごとに異なる可能性がある。よって、CViewLineMgr オブジェクトはビューが保持することになる。CViewLineMgr はエディットエンジンへのポインタを保持し、CView::OnDraw() では CViiewLineMgr 経由でエディットエンジンにアクセスし、テキスト表示処理を行う(下図参照)。 ## column ┏━━━━━━━┓ ┏━━━━━━━┓ ┃ CDocument ┃ ┃ CView ┃ ┠───────┨1 ┠───────┨ ┃ ● ┠────┨ ● ┃ ┠───┼───┨ 1..*┠───┼───┨ ┃ │ ┃ ┃ │ ┃ ┗━━━┿━━━┛ ┗━━━┿━━━┛ │ │ │ │ ↓ ↓ ┏━━━━━━━┓ ┏━━━━━━━┓ ┃ CEditEngine ┃ ┃ CViewLineMgr ┃ ┠───────┨ ┠───────┨ ┃ ┃←───╂───● ┃ ┠───────┨ ┠───────┨ ┃ ┃ ┃ ┃ ┗━━━━━━━┛ ┗━━━━━━━┛ ## endcolumn CViewLineMgr は以下のように宣言できる。 ## column struct SViewLine { int m_line; // 表示行の論理行番号 int m_offset; // 先頭文字の論理行でのオフセット int m_length; // 1行バイト数 public: SViewLine() {}; SViewLine(int line, int offset, int length) { m_line = line; m_offset = offset; m_length = length; }; }; class CEditEngine; class CViewLineMgr { bool m_layoutMode; CEditEngine *m_ee; CArray m_vwLineArray; public: CViewLineMgr(CEditEngine*); ~CViewLineMgr(); bool layoutMode() const { return m_layoutMode; }; void doLayout(bool); // レイアウト処理 ON/OFF int getLineCount() const; int getLineCountEx() const; bool isEOF(int line) const { return line == getLineCountEx() - 1; }; bool getText(int, cchar*&, int&, int &) const; }; ## endcolumn CViewLineMgr::getText() などはエディットエンジンを参照し、エディットバッファの内容を返す。レイアウトモードでない場合は、エディットバッファの内容をそのまま返し、レイアウトモードの場合はレイアウト情報を元に表示行の情報を返す。 ## column bool CViewLineMgr::getText(int line, cchar *&ptr, int &length, int &lgLength) const { if( m_layoutMode ) { if( (uint)line >= (uint)m_vwLineArray.GetSize() ) return false; const SViewLine &vl = m_vwLineArray.GetAt(line); CString text; if( !m_ee->getText(vl.m_line, text) ) return false; ptr = (LPCSTR)text + vl.m_offset; length = vl.m_length; lgLength = text.GetLength() - vl.m_offset; return true; } else { CString text; if( !m_ee->getText(line, text) ) return false; ptr = (LPCSTR)text; length = lgLength = text.GetLength(); return true; } } ## endcolumn CView::OnDrow() でエディットエンジンを参照していた部分を CViewLineMgr を参照するように変更する。 ## column void CViewView::OnDraw(CDC* pDC) { //CViewDoc* pDoc = GetDocument(); //ASSERT_VALID(pDoc); ..... for(; line < m_vwLineMgr->getLineCountEx() && py < bottom; ++line, py += VW_LINE_HEIGHT) { ..... cchar *text; int length, lgLength; if( !m_vwLineMgr->getText(line, text, length, lgLength) ) { ..... } ..... } } ## endcolumn 技術文書 のトップページのソースを折り返し表示したものを ここ に示す。また、プロジェクトのアーカイブを ここ に置く。 ■ 行コメントの折り返し処理対応 上記のプログラムで折り返し処理に対応したが、行コメントが2行以上にまたがる場合、2行目以降がコメント色で表示されないという問題がある。また、キーワードが2行にまたがる場合も二重に表示されてしまう問題もある。これに対処するためには論理行が継続している場合はコメント状態を次の行に渡すのと、キーワード表示を表示行幅で区切らなくてはいけない。また、論理行の途中から表示するとコメント状態、キーワードの途中かどうかがわからないので、必ず論理行の先頭から表示処理を行うようにする。 ## column void CViewView::OnDraw(CDC* pDC) { ..... int line = m_vwLineMgr->lgFirstLine(scpos.y / VW_LINE_HEIGHT); bool lineComment = false; int kwlen = 0; // キーワードを発見した場合、そのバイト数 bool csym = false; // 英数字、アンダースコアかどうか for(; line < m_vwLineMgr->getLineCountEx() && py < bottom; ++line, py += VW_LINE_HEIGHT) { ..... while( ptr < eolptr ) { ..... if( kwlen > 0 ) { // キーワード発見済みの場合 int len = kwlen; if( ptr + len > eolptr ) // 行末を超えている場合 len = eolptr - ptr; pDC->SetTextColor(kwcol); pDC->TextOut(px, py, ptr, len); column += len; ptr += len; kwlen -= len; csym = __iscsym(ptr[-1]) ? true : false; } ..... } ..... if( lgLength == length ) { // 論理行が終了している場合 lineComment = false; csym = false; } } pDC->SelectObject(svFont); } ## endcolumn