テキストファイルビューワ ■ テキストファイルビューワ 前のドキュメント(エディタのGUI化)で作成したものは様々な問題があり実用に耐えうるようなものではなかった。そこで本稿以降で、実用的なエディタを実装するための方法を示していくことにする。ただし、実用的なものは規模が巨大になるので、まずは編集操作が出来ないエディタ、すなわちテキストファイルビューワを作成していくことにする。 ■ スクロール スクロール機能を実装するには、ビューの基底クラスを CView から CScrollView に変更すると良い。 AppWizard でスケルトンを生成するとき、最後にビューの基底クラスを CScrollView に設定する。 CViewView::OnInitialUpdate() で、SetScrollSizes() をコールし、ドキュメントサイズをスクロールビューに知らせる。 ## column void CViewView::OnInitialUpdate() { CScrollView::OnInitialUpdate(); CViewDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); CEditEngine *ee = pDoc->getEditEngine(); CSize sizeTotal(0, ee->getLineCount() * VW_LINE_HEIGHT); SetScrollSizes(MM_TEXT, sizeTotal); } ## endcolumn 無駄な描画を行わない ## column void CViewView::OnDraw(CDC* pDC) { CViewDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); CEditEngine *ee = pDoc->getEditEngine(); CRect cRect; GetClientRect(cRect); CPoint scpos = GetScrollPosition(); int line = scpos.y / VW_LINE_HEIGHT; int py = line * VW_LINE_HEIGHT; int bottom = scpos.y + cRect.Height(); for(; line < ee->getLineCount() && py < bottom; ++line, py += VW_LINE_HEIGHT) { CString text; if( !ee->getText(line, text) ) break; pDC->TextOut(0, py, text); } } ## endcolumn スクロール範囲のオーバーフロー対策 ■ タブ、改行マーク 次にタブ、改行マークを表示するようにしてみよう。 ## column cchar *getEolPtr(cchar *ptr, int length) { if( length >= 2 && ptr[length-2] == '\r' && ptr[length-2] == '\n' ) return ptr + length - 2; if( length >= 1 && (ptr[length-1] == '\n' || ptr[length-2] == '\r') ) return ptr + length - 1; return ptr + length; } void CViewView::OnDraw(CDC* pDC) { CViewDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); CEditEngine *ee = pDoc->getEditEngine(); CRect cRect; GetClientRect(cRect); CPoint scpos = GetScrollPosition(); int line = scpos.y / VW_LINE_HEIGHT; int py = line * VW_LINE_HEIGHT; int bottom = scpos.y + cRect.Height(); for(; line < ee->getLineCount() && py < bottom; ++line, py += VW_LINE_HEIGHT) { CString text; if( !ee->getText(line, text) ) break; cchar *ptr = (cchar *)text; cchar *eolptr = getEolPtr(ptr, text.GetLength()); int column = 0; while( ptr < eolptr ) { int px = column * VW_FONT_WIDTH; if( *ptr == '\t' ) { // TAB の場合 pDC->SetTextColor(RGB(0x80, 0x80, 0x80)); // 文字色はグレイ pDC->TextOut(px, py, ">"); column = column - column % VW_TAB_WIDTH + VW_TAB_WIDTH; ptr += 1; } else { // 通常文字 cchar *ptr0 = ptr; while( ptr < eolptr && *ptr != '\t' ) ptr += 1; int len = ptr - ptr0; pDC->SetTextColor(RGB(0, 0, 0)); // 文字色は黒 pDC->TextOut(px, py, ptr0, len); column += len; } } if( eolptr < (cchar*)text + text.GetLength() ) { // 改行コードが存在 int px = column * VW_FONT_WIDTH; pDC->SetTextColor(RGB(0x00, 0x00, 0xf0)); // 文字色は青 pDC->TextOut(px, py, "<"); } } } ## endcolumn ■ フォントの生成 以上で、表示がだいぶまともになった。ついでにフォントも自前で生成するようにしよう。 ## column void CViewView::OnDraw(CDC* pDC) { CViewDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); CFont font; font.CreateFont(VW_FONT_HEIGHT, VW_FONT_WIDTH, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, "MS 明朝"); CFont *svFont = pDC->SelectObject(&font); pDC->SetBkMode(TRANSPARENT); // 文字の背景色は透明 ..... pDC->SelectObject(svFont); } ## endcolumn ■ 改行マークの描画 先のプログラムでは改行マークを "<" で表示していたが、見栄えがいまいちなので、キーボードに刻印されている Enter マークを表示するようにしてみる。そのためには CViewView::OnDraw() の最後の方を以下のようにすればよい。 ## column void CViewView::OnDraw(CDC* pDC) { ..... if( eolptr < (cchar*)text + text.GetLength() ) { // 改行コードが存在 int px = column * VW_FONT_WIDTH; CPen pen(PS_SOLID, 0, RGB(0x80, 0x80, 0x80)); CPen *svPen = pDC->SelectObject(&pen); int t = VW_FONT_WIDTH / 4 + 1; int dy = VW_FONT_HEIGHT / 3; pDC->MoveTo(px+VW_FONT_WIDTH, py+dy); pDC->LineTo(px+VW_FONT_WIDTH, py+VW_FONT_HEIGHT-dy); pDC->LineTo(px, py+VW_FONT_HEIGHT-dy); pDC->LineTo(px+t, py+VW_FONT_HEIGHT-dy-t); pDC->MoveTo(px, py+VW_FONT_HEIGHT-dy); pDC->LineTo(px+t, py+VW_FONT_HEIGHT-dy+t); pDC->SelectObject(svPen); } } } ## endcolumn ここまでのプログラムの実行結果を ここ に示す。また、ソースプログラムを ここ に置く。 ■ 演習問題 [1] 全角空白を、灰色の "□" で表示するようにプログラムを修正しなさい。 [2] EOF を青色の "[EOF]" で表示するようにプログラムを修正しなさい。