/////////////////////////////////////////////////////////////////////////
// CTaEditDraw クラスのインプリメンテーション
//
////////////////////////////////////////////////////////////////////////////

#include <X11/keysym.h>
#include <X11/Sunkeysym.h>
#include <Xm/CutPaste.h>
#include "TaEditDraw.h"
#include "TaEditShell.h"
#include "TaEditJPEXT.h"

////////////////////////////////////////////////////////////////////////////
// 構築/破棄
////////////////////////////////////////////////////////////////////////////

CTaEditDraw::CTaEditDraw() :
	pVScrollMgr( NULL ),
	pHScrollMgr( NULL ),
	BackgndPixmap( NULL ),
	DrawedLineCnt_hoz( 0 ),
	IsFirstExposed( false ),
	ForcusState( false ),
	pTabString( NULL ),
	LeftColumnWidth( 0 )
{

}

CTaEditDraw::~CTaEditDraw()
{

}

////////////////////////////////////////////////////////////////////////////
// メソッド
////////////////////////////////////////////////////////////////////////////

// staticなイベントハンドラ
////////////////////////////////////////////////////////////////////////////

// フォーカスの状態の変化に対するイベントハンドラ
void CTaEditDraw::OnForcusEvent( Widget w, XtPointer client_data, XEvent *event, Boolean *continue_to_dispatch )
{
	CTaEditDraw *p = reinterpret_cast< CTaEditDraw* >( client_data );
	p->OnForcus( w, event, continue_to_dispatch );
}

// キーボード入力に対するイベントハンドラ
void CTaEditDraw::OnKeybordEvent( Widget w, XtPointer client_data, XEvent *event, Boolean *continue_to_dispatch )
{
	CTaEditDraw *p = reinterpret_cast< CTaEditDraw* >( client_data );
	p->OnKey( w, event, continue_to_dispatch );
}

// マウスに対するイベントハンドラ
void CTaEditDraw::OnMouseEvent( Widget w, XtPointer client_data, XEvent *event, Boolean *continue_to_dispatch )
{
	CTaEditDraw *p = reinterpret_cast< CTaEditDraw* >( client_data );
	p->OnMouse( w, event, continue_to_dispatch );
}

// マウスポインタの移動に対するイベントハンドラ
void CTaEditDraw::OnPointerMotionEvent( Widget w, XtPointer client_data, XEvent *event, Boolean *continue_to_dispatch )
{
	CTaEditDraw *p = reinterpret_cast< CTaEditDraw* >( client_data );
	p->OnPointerMotion( w, event, continue_to_dispatch );
}

// ドロップされたときのコールバック
void CTaEditDraw::OnDropCallback( Widget w, XtPointer client_data, XtPointer callback_data )
{
	CTaEditDraw *p = reinterpret_cast< CTaEditDraw* >( client_data );
	p->OnDrop( reinterpret_cast< DtDndTransferCallbackStruct* >( callback_data ) );
}

// イベントハンドラ
////////////////////////////////////////////////////////////////////////////

// 構築
void CTaEditDraw::OnCreate( Widget argWgt, XtPointer user_data )
{
	VClsPtr< CConfigInfo > pConf = m_TaEditShell->GetConfigInfo();
	int i;

	// フォントセットを構築
	const string &rFontName = pConf->RefFontName();
	fontSet = JPEXT_CreateFontSet( XtDisplay( m_Draw ), rFontName.c_str() );
	TextHeight = JPEXT_GetFontHeight( fontSet ) + pConf->GetLineGapWidth();
	if ( TextHeight <= 0 ) TextHeight = 1;
	TabWidth = JPEXT_GetMinWidth( fontSet ) * pConf->RefTabWidth();
	TabWidth_CM = pConf->GetTabWidthCalculationMethod();
	pTabString = &( pConf->RefTabString() );
	TextAscent = JPEXT_GetFont_ascent( fontSet ) + pConf->GetLineGapWidth();
	if ( TextAscent <= 0 ) TextAscent = 1;

	// フォーカス・マウス用のイベントハンドラを定義
	XtAddEventHandler( m_Draw, FocusChangeMask, False, CTaEditDraw::OnForcusEvent, (XtPointer)this );
	XtAddEventHandler( m_Draw, KeyPressMask, False, CTaEditDraw::OnKeybordEvent, (XtPointer)this );
	XtAddEventHandler( m_Draw, ButtonPressMask, False, CTaEditDraw::OnMouseEvent, (XtPointer)this );
	XtAddEventHandler( m_Draw, ButtonReleaseMask, False, CTaEditDraw::OnMouseEvent, (XtPointer)this );
	XtAddEventHandler( m_Draw, Button1MotionMask, False, CTaEditDraw::OnMouseEvent, (XtPointer)this );
	XtAddEventHandler( m_Draw, PointerMotionMask, False, CTaEditDraw::OnPointerMotionEvent, (XtPointer)this );

	// ドロップサイトとして登録
	Arg al[10];
	int ac = 0;
        XtCallbackRec cblist[] = { { CTaEditDraw::OnDropCallback, (XtPointer)this }, { NULL, NULL } };
	XtSetArg( al[ac], const_cast< char* >( DtNregisterChildren ), False ); ac++;
	DtDndDropRegister( m_Draw, DtDND_FILENAME_TRANSFER, XmDROP_COPY, cblist, al, ac );

	// 左側に空ける領域の幅を算出
	LeftColumnWidth = pConf->GetExInfoColumnWidth() * pConf->GetExInfoColumnCount();
}

// 破壊
void CTaEditDraw::OnDestroy( Widget argWgt, XtPointer user_data )
{
	OnExit();	// ここは呼ばれることがないらしい
}

// プロセスが終了される
void CTaEditDraw::OnExit()
{
	// フォントセットの破棄
	XFreeFontSet( XtDisplay( m_Draw ), fontSet );

	if ( BackgndPixmap )
		XFreePixmap( XtDisplay( m_Draw ), BackgndPixmap );
	BackgndPixmap = NULL;

	pGCStorage = NULL;
	pGCMgr = NULL;
	pCursorInfo = NULL;
	pXimMgr = NULL;
	pMouseInfo = NULL;
	pTextEditor = NULL;
	pHScrollMgr = NULL;
	pVScrollMgr = NULL;
	vDrewLD.clear();

	// ドロップサイトとしての登録を解除
	DtDndDropUnregister( m_Draw );
}

// 入力コールバック
void CTaEditDraw::OnInput( Widget argWgt, XtPointer user_data )
{
}

// 入力イベント
void CTaEditDraw::OnKey( Widget w, XEvent *event, Boolean *continue_to_dispatch )
{
	wstring wstr;
	int i;
	KeySym keysym;
	ScrollInfoPacket scrPacket;
	bool OnShift;
	bool OnCtrl;
	T_CurPos NextPos;
	CTaEditDoc *pDoc = m_TaEditShell->GetDocument();
	CErrorBool r;

	if ( pXimMgr.IsNull() ) return ;

	// 入力された文字列を取得
	pXimMgr->GetInputText( reinterpret_cast< XKeyPressedEvent* >( event ), &wstr, &keysym, &OnShift, &OnCtrl );

	if ( wstr.length() <= 0 ) {

		// KeySymの値を使用し、入力されたキーを判定する
		switch ( keysym ) {
		case XK_Left:
			pCursorInfo->Left( &scrPacket, OnShift, OnCtrl );
			break;
		case XK_Up:
			pCursorInfo->Up( &scrPacket, OnShift );
			break;
		case XK_Right:
			pCursorInfo->Right( &scrPacket, OnShift, OnCtrl );
			break;
		case XK_Down:
			pCursorInfo->Down( &scrPacket, OnShift );
			break;
		case XK_Page_Up:
			pCursorInfo->PageUp( &scrPacket, OnShift );
			break;
		case XK_Page_Down:
			pCursorInfo->PageDown( &scrPacket, OnShift );
			break;
		case XK_End:
			pCursorInfo->End( &scrPacket, OnShift, OnCtrl );
			break;
		case XK_Home:
			pCursorInfo->Home( &scrPacket, OnShift, OnCtrl );
			break;
		case XK_L4:	// (Undoキー)メニューのUndoを呼び出す。
			m_TaEditShell->OnEditUndo( NULL, NULL );
			break;
		case XK_L2:	// (Againキー)メニューのRedoを呼び出す。
			m_TaEditShell->OnEditRedo( NULL, NULL );
			break;
		case XK_L6:	// (Copyキー)メニューのCopyを呼び出す。
			m_TaEditShell->OnEditCopy( NULL, NULL );
			break;
		case XK_L8:	// (Pasteキー)メニューのPasteを呼び出す。
			m_TaEditShell->OnEditPaste( NULL, NULL );
			break;
		case XK_L10:	// (Cutキー)メニューのCutを呼び出す。
			m_TaEditShell->OnEditCut( NULL, NULL );
			break;
		// case XK_L9:	// (Findキー)
		}

		// 必要に応じて、画面をスクロールさせる
		if ( scrPacket.NeedXScroll ) ScrollH( scrPacket.pixX, false );
		if ( scrPacket.NeedYScroll ) ScrollV( scrPacket.lineY, false );

		// 再描画
		// 選択範囲の変化だけ
		if ( scrPacket.IsSelCange && !scrPacket.NeedXScroll && !scrPacket.NeedYScroll )
			AfterSelRangeRedraw( scrPacket.ChangeSLine, scrPacket.ChangeELine );
		// スクロールされた
		if ( scrPacket.NeedXScroll || scrPacket.NeedYScroll )
			OnRedraw( TED_REUSE_CHAR_INFO );
		return ;
	}

	// Ctrlキーが押下されていた場合は、ユーザのタイプミスと判断して、なにもしない
	if ( OnCtrl ) return ;

	// 編集すべき位置を取得
	const T_CurPos &rCurPos = pCursorInfo->GetCurPos();
	const T_SelRange &rSelRange = pCursorInfo->GetSelRange();

	// 複数行選択された状態でTABが押下された場合
	if ( rSelRange.GetSPos().GetLine() != rSelRange.GetEPos().GetLine() && wstr == L"\t" ) {
		T_SelRange NextSelRange;
		// 選択されている行全体に対してTABを追加・削除する。
		if ( !OnShift )
			r = pDoc->MultiLineTabInsert( rSelRange, &NextPos, &NextSelRange );
		else
			r = pDoc->MultiLineTabDelete( rSelRange, &NextPos, &NextSelRange );
		pDoc->EndEdit();
		m_TaEditShell->ProcErrorMsg( r );

		// カーソル位置の設定と再描画
		// （直後に再描画するため、カーソルの再描画は行わない）
		pCursorInfo->SetCurPos( NextPos, &scrPacket, NextSelRange, false );
		AfterEditRedraw( scrPacket );
		return ;
	}

	// タブキーが押された場合に入力される文字列を設定する
	if ( wstr == L"\t" && NULL != pTabString )
		wstr = (*pTabString);

	// 特殊なキーの場合
	if ( wstr.length() == 1 &&
		( XK_BackSpace == keysym || XK_Return == keysym || XK_Delete == keysym || XK_KP_Enter == keysym ) )
		r = pDoc->InsertSpecialCode( keysym, rCurPos, rSelRange, &NextPos );
	else
		// 通常の文字列の挿入を指示
		r = pDoc->Insert( wstr, rSelRange.GetSPos(), pCursorInfo->GetSelRange(), &NextPos );
	pDoc->EndEdit();
	m_TaEditShell->ProcErrorMsg( r );

	// 入力された分だけカーソルを移動させる
	pCursorInfo->SetCurPos( NextPos, &scrPacket, false, true );

	// 再描画処理
	AfterEditRedraw( scrPacket );
}

// 表示
void CTaEditDraw::OnExpose( Widget argWgt, XtPointer user_data )
{
	// 最初の一回目の表示をイベントとして処理
	if ( !IsFirstExposed ){
		m_TaEditShell->OnFirstExpose();
		OnFirstExpose();
	}

	ClearBackScreen();
	OnRedraw( TED_REUSE_CHAR_INFO );	// 再描画
}

// サイズ変更
void CTaEditDraw::OnResize( Widget argWgt, XtPointer user_data )
{
	int x, y;
	int wrv;
	int ViewLineCnt;

	// まだ表示されてもいないのなら、処理する必要はない
	if ( !IsFirstExposed ) return ;

	GetSize( &x, &y );
	ViewLineCnt = GetViewLineCnt();	// 画面に描画可能な行数を取得

	// スクロールバーのページサイズを設定
	if ( !pVScrollMgr->SetPageSize( ViewLineCnt, &wrv ) ) {
		ScrollV( wrv, false );
		pVScrollMgr->SetPageSize( ViewLineCnt, NULL );
	}
	if ( !pHScrollMgr->SetPageSize( x, &wrv ) ) {
		ScrollH( wrv, false );
		pHScrollMgr->SetPageSize( x, NULL );
	}

	ClearBackScreen();
	OnRedraw( TED_REUSE_CHAR_INFO );	// 再描画
	if ( !pCursorInfo.IsNull() )
		pCursorInfo->RefleshXIMPos();	// XIMの位置を再設定
}

// フォーカスの出入り
void CTaEditDraw::OnForcus( Widget w, XEvent *event, Boolean *continue_to_dispatch )
{
	XFocusChangeEvent *pf = (XFocusChangeEvent*)event;

	if ( pXimMgr.IsNull() ) return ;

	// XIM管理オブジェクトに、フォーカスの変化を通知する
	switch ( pf->type ){
	case FocusIn:
		pXimMgr->OnForcus();
		ForcusState = true;
		break;
	case FocusOut:
		pXimMgr->ExitForcus();
		ForcusState = false;
		break;
	}
}

// マウス操作
void CTaEditDraw::OnMouse( Widget w, XEvent *event, Boolean *continue_to_dispatch )
{
	enumBTN2FUNC Btn2Func = m_TaEditShell->GetConfigInfo()->GetBtn2Func();
	TA_EVENT e;
	int x;
	int y;
	XButtonEvent *pBtnE = NULL;
	XMotionEvent *pMtnE = NULL;
	bool OnShift;
	bool OnCtrl;
	Display *pDisplay = XtDisplay( w );
	Window window = XtWindow( w );
	ScrollInfoPacket scrPacket;

	switch ( event->type ) {
	case ButtonPress:
		pBtnE = &( event->xbutton );

		//  左及び中ボタン以外のものに対する処理
		switch ( pBtnE->button ) {
		case 3:	// 右ボタン
			OnMouse3Push( pBtnE );
			return ;
		case 4:	// 上スクロール
			OnMouseScrollUp( pBtnE );
			return ;
		case 5:	// 下スクロール
			OnMouseScrollDown( pBtnE );
			return ;
		default:
			// 1から5以外のボタンが押された場合は無視する
			if ( pBtnE->button > 5 ) return ;
		}

		e = TAE_BUTTON1_DOWN;
		x = pBtnE->x - LeftColumnWidth;
		y = pBtnE->y;
		if ( 2 == pBtnE->button && B2F_LEFT != Btn2Func ) {
			m_TaEditShell->RaiseWindow();
			// 第二ボタンに対する処理
			if ( Btn2Func == B2F_LEFT_CTRL ) {
				OnCtrl = true;
				OnShift = false;
			}
			else {
				OnCtrl = false;
				OnShift = true;
			}
		}
		else {
			OnShift = ( pBtnE->state & ShiftMask ) ? true : false;
			OnCtrl = ( pBtnE->state & ControlMask ) ? true : false;
		}

		// マウスのグラブを宣言
		//XGrabPointer( pDisplay, window, True, ButtonReleaseMask | PointerMotionMask, GrabModeAsync, GrabModeSync, None, None, CurrentTime );
		break;

	case ButtonRelease:
		pBtnE = &( event->xbutton );

		// 左及び中ボタン以外のものであれば無視する
		if ( pBtnE->button > 2 ) return ;

		e = TAE_BUTTON1_UP;
		x = pBtnE->x - LeftColumnWidth;
		y = pBtnE->y;
		OnShift = ( pBtnE->state & ShiftMask ) ? true : false;
		OnCtrl = ( pBtnE->state & ControlMask ) ? true : false;

		// マウスのグラブを解除する
		//XUngrabPointer( pDisplay, CurrentTime );
		break;

	case MotionNotify:
		pMtnE = &( event->xmotion );
		e = TAE_MOTION;
		x = pMtnE->x - LeftColumnWidth;
		y = pMtnE->y;
		OnShift = ( pMtnE->state & ShiftMask ) ? true : false;
		OnCtrl = ( pMtnE->state & ControlMask ) ? true : false;
		break;
	default:
		return ;
	}

	pMouseInfo->OnMouse( e, x, y, &scrPacket, OnShift, OnCtrl );
	if ( scrPacket.IsSelCange && !scrPacket.NeedXScroll && !scrPacket.NeedYScroll )
		OnRedraw( TED_REUSE_CHAR_INFO, scrPacket.ChangeSLine, scrPacket.ChangeELine );

	if ( scrPacket.NeedXScroll ) ScrollH( scrPacket.pixX, false );
	if ( scrPacket.NeedYScroll ) ScrollV( scrPacket.lineY, false );
	if ( scrPacket.NeedXScroll || scrPacket.NeedYScroll )
		OnRedraw( TED_REUSE_CHAR_INFO );
}

// マウスポインタの移動
void CTaEditDraw::OnPointerMotion( Widget w, XEvent *event, Boolean *continue_to_dispatch )
{
	XMotionEvent *pMtnE = &( event->xmotion );
	pMouseInfo->OnPointerMotion( pMtnE->x, pMtnE->y );
}

// マウスの中ボタンの押下
void CTaEditDraw::OnMouse2Push( XButtonPressedEvent* event )
{

}

// マウスの右ボタンの押下
void CTaEditDraw::OnMouse3Push( XButtonPressedEvent* event )
{
	// ポップアップメニューを表示させる
	m_menuPopup->Popup( event );
}

// マウスのスクロールボタンによる上方向へのスクロール
void CTaEditDraw::OnMouseScrollUp( XButtonPressedEvent* event )
{
	int cy = GetTopLineNum();
	int ScrollMouseSpeed = m_TaEditShell->GetConfigInfo()->GetScrollMouseSpeed();
	cy -= ScrollMouseSpeed;
	if ( cy < 0 ) cy = 0;
	ScrollV( cy, true );
}

// マウスのスクロールボタンによる下方向へのスクロール
void CTaEditDraw::OnMouseScrollDown( XButtonPressedEvent* event )
{
	int cy = GetTopLineNum();
	int CurLineCnt = m_TaEditShell->GetDocument()->RefData().size();
	int ScrollMouseSpeed = m_TaEditShell->GetConfigInfo()->GetScrollMouseSpeed();
	int VLC = GetViewLineCnt();

	if ( CurLineCnt <= VLC )
		cy = 0;
	else {
			cy += ScrollMouseSpeed;
			if ( cy + VLC >= CurLineCnt ) cy = CurLineCnt - VLC;
	}
	ScrollV( cy, true );
}

// タイマイベント
void CTaEditDraw::OnTimer()
{
	if ( !pCursorInfo.IsNull() )
		pCursorInfo->OnTimer();
}

// ドロップイベント
void CTaEditDraw::OnDrop( DtDndTransferCallbackStruct *pCallbackData )
{
	DtDndProtocol prot = pCallbackData->dropData->protocol;
	if ( DtDND_TEXT_TRANSFER == prot || DtDND_BUFFER_TRANSFER == prot )
		return ;
	if ( pCallbackData->dropData->numItems <= 0 )
		return ;

	// ドロップされたファイルを開く
	m_TaEditShell->OpenDnDFiles(
		pCallbackData->dropData->numItems,
		pCallbackData->dropData->data.files
	);
}

// その他・内的なイベント
////////////////////////////////////////////////////////////////////////////

// ドキュメントが読み込まれた
void CTaEditDraw::OnReadDocument()
{
	int ViewLineCnt;
	CTaEditDoc *pDoc = m_TaEditShell->GetDocument();
	VClsPtr< CConfigInfo > pConf = m_TaEditShell->GetConfigInfo();
	numTopLine = 0;
	MaxLengthPix = 0;

	pCursorInfo = NULL;
	pMouseInfo = NULL;
	pTextEditor = NULL;
	pGCMgr = NULL;
	pCursorInfo = new CCursor( this, pDoc, pXimMgr );
	if ( pCursorInfo.IsNull() ) goto ERR_EXIT;
	pTextEditor = new CTextEdit( this, pCursorInfo, pDoc );
	if ( pTextEditor.IsNull() ) goto ERR_EXIT;
	pMouseInfo = new CMouse( pCursorInfo, this, pTextEditor );
	if ( pMouseInfo.IsNull() ) goto ERR_EXIT;
	pGCMgr = new CGCMgr();
	if ( pGCMgr.IsNull() ) goto ERR_EXIT;
	if ( !pGCMgr->CreateGC( m_Draw, pConf, pDoc->GetTxtColorMgr(), pGCStorage ) )
		return ;
	ViewLineCnt = GetViewLineCnt();

	// カーソル構築を指示
	pMouseInfo->CreateCursor( m_Draw );

	// スクロールバーの初期設定
	pVScrollMgr->SetMin( 0 );
	pVScrollMgr->SetToMin();
	pVScrollMgr->SetMax( pDoc->RefData().size() );
	pVScrollMgr->SetMinChickSize( 1 );
	pVScrollMgr->SetPageSize( ViewLineCnt, NULL );

	pHScrollMgr->SetMin( 0 );
	pHScrollMgr->SetToMin();
	pHScrollMgr->SetMax( 0 );
	pHScrollMgr->SetMinChickSize( 1 );
	pHScrollMgr->SetPageSize( GetViewWidth(), NULL );

	// 各メニューの初期状態を設定
	m_TaEditShell->SetEnableCopyPasteMenu( false );
	m_TaEditShell->SetEnableUndoMenu( false );
	m_TaEditShell->SetEnableRedoMenu( false );

	OnRedraw( TED_REUSE_CHAR_INFO );

	// XIMの位置を設定
	pCursorInfo->RefleshXIMPos();
	return ;

ERR_EXIT:
	m_TaEditShell->ShowErrorMsgBox( pConf->RefOutOfMemoryErrorMsg() );
	pCursorInfo = NULL;	// 一応
	pMouseInfo = NULL;
	pTextEditor = NULL;
	pGCMgr = NULL;
}

// カーソルを描画
void CTaEditDraw::DrawCursor( int x, int y ) const
{
	Drawable drawable = XtWindow( m_Draw );
	DrawCursor( x, y, drawable );
}
void CTaEditDraw::DrawCursor( int x, int y, Drawable drawable ) const
{
	Display *pDisplay = XtDisplay( m_Draw );

	if ( NULL == pDisplay || NULL == drawable || pGCMgr.IsNull() ) return ;

	x += LeftColumnWidth;
	XDrawLine( pDisplay, drawable, pGCMgr->GetCursorGC(), x, y + 1, x, y + TextHeight - 2 );
	XDrawLine( pDisplay, drawable, pGCMgr->GetCursorGC(), x - 2, y, x + 2, y );
	XDrawLine( pDisplay, drawable, pGCMgr->GetCursorGC(), x - 2, y + TextHeight - 1, x + 2, y + TextHeight - 1 );
}

// カレント行の下線を描画
void CTaEditDraw::DrawCurLineUnderbar( int curline, bool flg ) const
{
	Drawable drawable = XtWindow( m_Draw );
	DrawCurLineUnderbar( curline, flg, drawable );
}
void CTaEditDraw::DrawCurLineUnderbar( int curline, bool flg, Drawable drawable ) const
{
	int w, h;
	int y;
	Display *pDisplay = XtDisplay( m_Draw );
	VClsPtr< CConfigInfo > pConf = m_TaEditShell->GetConfigInfo();
	GC wGC;

	GetSize( &w, &h );

	// 描画する必要がなければなにもしない
	if ( !pConf->GetDrawCurLineUnderBar() ) return ;
	if ( curline < GetTopLineNum() || curline >= GetTopLineNum() + GetViewLineCnt() )
		return ;

	// 下線描画用GCを取得
	if ( flg )
		wGC = pGCMgr->GetCurLineUnderBarGC();
	else
		wGC = pGCMgr->GetClearGC();

	if ( !wGC ) return ;

	// 描画する
	y = ( curline - GetTopLineNum() + 1 ) * GetTextHeight() - 1;
	XDrawLine( pDisplay, drawable, wGC, 0, y, w, y );
}

// 縦スクロール
void CTaEditDraw::ScrollV( int y, bool NeedRedraw )
{
	pVScrollMgr->SetValue( y );
	OnScrValueChange( pVScrollMgr, y, NeedRedraw );
}

// 横スクロール
void CTaEditDraw::ScrollH( int x, bool NeedRedraw )
{
	// 画面中にXが含まれるよう、かつScrollAtの単位になるようにスクロールさせる
	VClsPtr< CConfigInfo > pConf = m_TaEditShell->GetConfigInfo();
	int ViewWidth = GetViewWidth();
	int ScrMin = ( 0 - pConf->GetLeftPadding() );	// スクロールしうる最小値
	int ScrMax = ( MaxLengthPix - ViewWidth + pConf->GetRightPadding() );	// 同最大値
	int HScrollAt = pConf->GetHScrollAt();			// スクロールする単位
	int wx = x;

	// 最小値ScrMinからHScrollAt単位にスクロールしたい
	wx -= ScrMin;
	wx /= HScrollAt;
	if ( x >= pHScrollMgr->GetValue() )
		wx += 1;	// 右スクロールする場合
	wx *= HScrollAt;
	wx += ScrMin;

	// 過剰なスクロールにって、カーソルが見えなくなってしまうことを防止
	if ( x >= pHScrollMgr->GetValue() ) {
		if ( x + ViewWidth < wx ) wx = x + ViewWidth / 2;
	}
	else {
		if ( wx + ViewWidth < x ) wx = x - ViewWidth / 2;
	}

	// スクロール後の位置が、上限や下限を越えていないことを保証
	if ( wx < ScrMin ) wx = ScrMin;
	if ( wx >= ScrMax ) wx = ScrMax;

	pHScrollMgr->SetValue( wx );
	OnScrValueChange( pHScrollMgr, wx, NeedRedraw );
}

// 最初に画面に表示されるときのイベント
void CTaEditDraw::OnFirstExpose()
{
	int w = 0;
	int h = 0;
	int Line = 0;
	int DocLineCnt = 0;
	const CCmdOption& rCmdOption = m_TaEditShell->GetCmdOption();
	VClsPtr< CConfigInfo > pConf = m_TaEditShell->GetConfigInfo();
	CTaEditDoc *pDoc = m_TaEditShell->GetDocument();

	IsFirstExposed = true;	// 最初の画面表示が終了したことを示す

	// XIM管理オブジェクトを構築
	pXimMgr = new CXimMgr();
	if ( pXimMgr.IsNull() ) goto ERR_EXIT;
	if( !pXimMgr->Initialize( m_Draw, pConf ) )  goto ERR_EXIT;
	pXimMgr->SetLeftColumnWidth( LeftColumnWidth );

	// ダブルバッファリング用ピックスマップを構築
	w = WidthOfScreen( XtScreen( m_Draw ) );
	h = HeightOfScreen( XtScreen( m_Draw ) );
	if ( pConf->GetUseDoubleBuffering() )
		BackgndPixmap = XCreatePixmap( XtDisplay( m_Draw ), XtWindow( m_Draw ), w, h, 24 );
	else
		BackgndPixmap = NULL;

	// GC保持オブジェクトを構築する
	pGCStorage = new CGCStorage();
	if ( pGCStorage.IsNull() ) goto ERR_EXIT;

	// 新規のドキュメントが構築された時の処理を行う
	OnReadDocument();

	// コマンド行に指定されたオプションを処理する

	// 起動時に開くファイルの指定
	if ( rCmdOption.IsEnableFileName() ) {
		m_TaEditShell->OpenFile( rCmdOption.GetFileName(), rCmdOption.GetLangType() );
	}

	// 起動時にカーソルを設定する行番号
	if ( rCmdOption.IsEnableInitLine() ) {
		ScrollInfoPacket scrPacket;
		Line = rCmdOption.GetInitLine() - pConf->GetLineNumberOrigin();
		DocLineCnt = m_TaEditShell->GetDocument()->RefData().size();	// ドキュメントの行数

		// 設定する行の範囲を確認する
		if ( Line < 0 ) Line = 0;
		if ( Line >= DocLineCnt ) Line = DocLineCnt - 1;

		// カーソル位置を設定する
		pCursorInfo->SetCurPos( T_CurPos( Line, 0 ), &scrPacket, false );
		// 再描画
		if ( scrPacket.NeedYScroll ) ScrollV( scrPacket.lineY, true );
	}

	return ;

ERR_EXIT:
	m_TaEditShell->ShowErrorMsgBox( pConf->RefOutOfMemoryErrorMsg() );
	pXimMgr = NULL;
}

// 情報の設定
////////////////////////////////////////////////////////////////////////////

// スクロールバーのオブジェクトを設定
void CTaEditDraw::SetScrollMgr( CScrollMgr *argpH, CScrollMgr *argpV )
{
	pHScrollMgr = argpH;
	pVScrollMgr = argpV;
}

// 情報の取得
////////////////////////////////////////////////////////////////////////////

// テキストの高さを取得
int CTaEditDraw::GetTextHeight() const
{
	return TextHeight;
}
int CTaEditDraw::GetTextAscent() const
{
	return TextAscent;
}

// 文字の幅を取得
int CTaEditDraw::GetCharWidth( wchar_t c, int xpos ) const
{
	// tab以外の文字ならば、その文字の幅を取得して返す
	if ( c != '\t' )
		return XwcTextEscapement( fontSet, &c, 1 );

	// Tab文字の場合は、設定に従い幅を計算する必要がある
	if ( TWCM_FIX == TabWidth_CM )
		return TabWidth;	// 固定幅
	else {
		// TabWidth単位に切り上げる
		int w = ( (int)ceil( (double)xpos / (double)TabWidth ) ) * TabWidth - xpos;
		if ( w <= 0 ) w = TabWidth;
		return w;
	}
}

// 一番上に描画している行の行番号を取得
int CTaEditDraw::GetTopLineNum() const
{
	return numTopLine;
}

// 描画範囲の左端のピクセルの位置を取得
int CTaEditDraw::GetLeftPixPos() const
{
	if ( NULL != pHScrollMgr )
		return pHScrollMgr->GetValue();
	return 0;
}

// ビューに描画できる行数を取得
int CTaEditDraw::GetViewLineCnt() const
{
	int x, y;
	GetSize( &x, &y );	// ウインドウサイズを取得
	return y / TextHeight;	// 画面に描画可能な行数を算出
}

// ビューの幅を取得
int CTaEditDraw::GetViewWidth() const
{
	int w, h;
	GetSize( &w, &h );
	return w;
}

// 画面に何行描画しているかを取得
int CTaEditDraw::GetDrawedLineCnt() const
{
	const T_LineData &rData = m_TaEditShell->GetDocument()->RefData();
	return min( (long)( rData.size() - numTopLine ), (long)GetViewLineCnt() );
}

// 描画領域のサイズを取得
void CTaEditDraw::GetSize( int *pX, int *pY ) const
{
	int ac = 0;
	Arg al[2];
	Dimension wx, wy;

	// ウインドウのサイズを取得
	XtSetArg( al[ac], XmNwidth, &wx ); ac++;
	XtSetArg( al[ac], XmNheight, &wy ); ac++;
	XtGetValues( m_Draw, al, ac );
	(*pX) = wx - LeftColumnWidth;
	(*pY) = wy;
}

// 編集オブジェクトを取得
VClsPtr< CTextEdit > CTaEditDraw::GetTextEditor()
{
	return pTextEditor;
}

// カーソル情報管理オブジェクトを取得
VClsPtr< CCursor > CTaEditDraw::GetCursorInfo()
{
	return pCursorInfo;
}
const CCursor* CTaEditDraw::GetCursorInfo() const
{
	return pCursorInfo.GetPtr();
}

// フォーカスの状態を取得
bool CTaEditDraw::GetForcusState() const
{
	return ForcusState;
}

// 描画処理
////////////////////////////////////////////////////////////////////////////

// 画面上のX座標から、左から何文字目かを算出する
int CTaEditDraw::XPosToCharNum( int lineY, int pixX ) const
{
	assert( NULL != this && lineY >= 0 );
	CTaEditDoc *pDoc = m_TaEditShell->GetDocument();
	const T_LineData &rl = pDoc->RefData();
	assert( lineY < rl.size() );

	// 文字の選択方法を取得
	enumCHARSELTYPE CharSelType = m_TaEditShell->GetConfigInfo()->GetCharSelType();

	// イテレータを取得
	T_LineDataItr_c witr = rl.begin() + lineY;
	const CDrawCharPos& rDCP = witr->GetDrawCharPos();

	// 画面上での座標を、行頭を0とした座標に変換
	const int wXPos = pixX + GetLeftPixPos();

	// 負数が指定されたら、行頭であるものとする
	if ( wXPos <= 0 ) return 0;

	// 描画位置情報が利用できなかったら、情報を構築する
	if ( !witr->IsEnableDrawCharPos() )
		witr->UpdateDrawCharPos( this );

	int r = rDCP.FindPosToRowNum( (unsigned int)wXPos, CharSelType );
	return r;
}

// 画面上の座標から、文字位置を算出する
T_CurPos CTaEditDraw::PixPosToCurPos( int pixX, int pixY ) const
{
	CTaEditDoc *pDoc = m_TaEditShell->GetDocument();
	const int DataLineCnt = pDoc->RefData().size();

	// 行の位置を算出
	int lineY = numTopLine + ( pixY / TextHeight );
	if ( lineY < 0 ) lineY = 0;
	if ( lineY >= DataLineCnt ) lineY = DataLineCnt - 1;

	// 文字位置を算出
	int cposX = XPosToCharNum( lineY, pixX );

	return T_CurPos( lineY, cposX );
}

// スクロールバーが操作された。
void CTaEditDraw::OnScrValueChange( const CScrollMgr *argpMgr, int value, bool NeedRedraw )
{
	if ( argpMgr != pVScrollMgr && argpMgr != pHScrollMgr && !IsFirstExposed )
		return ;
	if ( argpMgr == pVScrollMgr ) numTopLine = value;

	if ( NeedRedraw ) OnRedraw( TED_REUSE_CHAR_INFO );
	pCursorInfo->RefleshXIMPos();	// XIMの位置を再設定

}

// 画面をクリア
void CTaEditDraw::ClearBackScreen()
{
	XRectangle rect;
	Display *pDisplay = XtDisplay( m_Draw );
	Drawable drawable = BackgndPixmap;
	int w, h;

	// 状態が整っていなければ、処理しない
	if ( NULL == pDisplay || NULL == drawable || pGCMgr.IsNull() )
		return ;

	GetSize( &w, &h );	// 画面のサイズを取得
	w += LeftColumnWidth;	// GetSizeで取得した幅は、あらかじめLeftColumnWidthが引かれてる
	XFillRectangle( pDisplay, drawable, pGCMgr->GetClearGC(), 0, 0, w, h );

	// 描画済みの行を示す配列をクリアする
	vDrewLD.clear();
}

// 再描画
void CTaEditDraw::OnRedraw( enumREDRAWTYPE type )
{
	OnRedraw( type, numTopLine, numTopLine + GetDrawedLineCnt() );
}


// 再描画範囲を指定して、再描画
void CTaEditDraw::OnRedraw( enumREDRAWTYPE type, int argSLine, int argELine )
{
	CTaEditDoc *pDoc = m_TaEditShell->GetDocument();
	VClsPtr< CConfigInfo > pConf = m_TaEditShell->GetConfigInfo();
	const T_LineData &rData = pDoc->RefData();
	const int TotalLineCount = rData.size();
	int i, w, h;
	XRectangle rect;
	Display *pDisplay = XtDisplay( m_Draw );
	Drawable drawable;
	int wMaxLineLength = 0;	// 今回描画した中で、最も長い行のピクセル数
	int ExInfoColW = pConf->GetExInfoColumnWidth();
	int ExInfoColCnt = pConf->GetExInfoColumnCount();

	// ダブルバッファリングを使用する場合は、一旦BackgndPixmapの方に描画する
	if ( pConf->GetUseDoubleBuffering() )
		drawable = BackgndPixmap;
	else
		drawable = XtWindow( m_Draw );

	// 状態が整っていなければ、処理しない
	if ( NULL == pDisplay ) return ;
	if ( NULL == drawable ) return ;
	if ( NULL == fontSet ) return ;
	if ( pGCMgr.IsNull() ) return ;

	// 描画範囲を確認・調整
	if ( argSLine < numTopLine )
		argSLine = numTopLine;
	if ( argELine > numTopLine + GetViewLineCnt() )
		argELine = numTopLine + GetViewLineCnt();

	const int RedrawLineCnt = argELine - argSLine;	// 描画する行数
	const int wSL = argSLine - numTopLine;			// 画面上での描画開始位置
	const int wEL = argELine - numTopLine;			// 画面上での描画終了位置
	const int wRealViewLineCnt = GetDrawedLineCnt();	// 画面に描画可能な行数
	const int DrawPosX = GetLeftPixPos();			// 横方向の描画位置

	T_LineDataItr_c itr = rData.begin() + argSLine;
	GetSize( &w, &h );		// 画面のサイズを取得
	w += LeftColumnWidth;	// GetSizeで取得される幅は、LeftColumnWidthが引かれている

	// 作業用に、vDrewLDのサイズを保証する
	vDrewLD.resize( GetViewLineCnt(), CDrewLineCash() );

	// 文字列を描画
	for ( i = wSL; i < wEL && numTopLine + i < TotalLineCount; i++, itr++ ) {

		// typeがTED_LINE_BASEで再描画する必要がない場合はスキップする
		if ( TED_LINE_BASE == type && vDrewLD[ i ] == CDrewLineCash( (*itr) ) )
			continue;

		// 描画位置情報が利用できない場合は再構築させる
		if ( !itr->IsEnableDrawCharPos() )
			itr->UpdateDrawCharPos( this );

		// 最も長い行のピクセル数が更新されたか否か
		if ( itr->GetDrawCharPos().GetRightXPos() > wMaxLineLength )
			wMaxLineLength = itr->GetDrawCharPos().GetRightXPos();

		// 再描画
		XFillRectangle( pDisplay, drawable, pGCMgr->GetClearGC(),
			0, i * TextHeight, w, TextHeight );
		DrawString_ReuseCharInfo( pDisplay, drawable, -DrawPosX,
			TextHeight * i, itr, i + numTopLine, w );

		// 左側のカラムを描画する
		if ( LeftColumnWidth > 0 )
			DrawLeftColumn( pDisplay, drawable, TextHeight * i, itr, ExInfoColW, ExInfoColCnt );

		// 今回描画した行がどれか、を保存する
		vDrewLD[ i ] = CDrewLineCash( (*itr) );
	}

	if ( DrawedLineCnt_hoz > wRealViewLineCnt ) {
		// 描画された行が減少した場合は、消えるべき部分に残っている文字を消去する
		XFillRectangle( pDisplay, drawable, pGCMgr->GetClearGC(),
			0, wRealViewLineCnt * TextHeight,
			w, ( DrawedLineCnt_hoz - wRealViewLineCnt ) * TextHeight );

		// 消えたところに描画されていた行に、画面から消去されたことを記録する
		for ( i = wRealViewLineCnt; i < DrawedLineCnt_hoz; i++ )
			vDrewLD[ i ] = CDrewLineCash();
	}

	// 左側の空き領域を描画する
	if ( pConf->GetDrawSeparatorLine() ) {
		XDrawLine( pDisplay, drawable, pGCMgr->GetLeftColumnSeparatorGC(),
			LeftColumnWidth, 0, LeftColumnWidth, h );
	}

	if ( pConf->GetUseDoubleBuffering() ) {
		// ピックスマップの内容をドローウィジットに表示する
		XCopyArea( pDisplay, BackgndPixmap, XtWindow( m_Draw ), pGCMgr->GetClearGC(), 0, 0, w, h, 0, 0 );
	}

	// カーソルに再描画されたことを通知する
	pCursorInfo->OnRedraw();

	// 横スクロールバーの最大値を設定する
	if ( MaxLengthPix < wMaxLineLength ) {
		MaxLengthPix = wMaxLineLength;
		pHScrollMgr->SetMax( MaxLengthPix );
	}

	// 画面に描画した行数を保持する
	DrawedLineCnt_hoz = wRealViewLineCnt;
}

// 文字列を描画する
// XPos,YPos 描画する位置
// （XPosに負数が与えられた場合は、当該の行の左端は描画されない。
// 　正数が与えられた場合は、当該の行の左端に空白が生じる）
// LineNum 画面上の何行目の行か
void CTaEditDraw::DrawString_ReuseCharInfo(
	Display *pDisplay,
	Drawable argDrawable,
	int XPos,
	int YPos,
	const T_LineDataItr_c &rItr,
	int LineNum,
	int ViewWidth ) const
{
	int s, e;	// 描画する範囲（文字）
	int wXPos;		// 描画を開始する位置（ピクセル）
	const wstring &rStr = rItr->getString();	// 描画する文字列を取得
	const CDrawCharPos &rDPos = rItr->GetDrawCharPos();
	const int CharCnt = rDPos.GetCharCnt();

	// 描画を開始する位置（文字）を求める
	if ( XPos < 0 )
		s = rDPos.FindPosToRowNum( abs( XPos ), CST_LEFT );
	else {
		// 左に空白を描画する場合は、左端の先頭文字から描画する。
		s = rDPos.FindPosToRowNum( 0, CST_LEFT );
	}

	// 描画を開始する位置（ピクセル）を求める
	if ( CharCnt > s )
		wXPos = rDPos.GetCharPos( s ) + XPos + LeftColumnWidth;
	else
		wXPos = rDPos.GetRightXPos() + XPos + LeftColumnWidth;

	// 描画を終了する位置（文字）を求める
	e = rDPos.FindPosToRowNum( ViewWidth - XPos, CST_RIGHT );

	DrawString( pDisplay, argDrawable, wXPos, YPos, rItr, s, e - s, LineNum );
}

// 画面の指定された位置に文字列を描画
void CTaEditDraw::DrawString(
	Display *pDisplay,
	Drawable argDrawable,
	int XPos,
	int YPos,
	const T_LineDataItr_c &rItr,
	int StrSPos,
	int StrCnt,
	int LineNum ) const
{
	unsigned int wXPos = XPos;
	GC hGC;		// GC保存用
	bool hIsTab;	// 文字がタブだったか否か
	int hIdx;	// 次に描画するとき、開始位置となる文字のインデックス
	bool hIsSel;
	int i;
	const wstring &rStr = rItr->getString();
	const size_t StrLength = rStr.length();
	bool wIsSel;	// GCを取得する際に、選択されているか否かを一旦保持する変数
	unsigned int wWidth;	// 文字の幅(Pixcel)を保持する変数
	const CDrawCharPos& rDrawCharPos = rItr->GetDrawCharPos();

	// 描画する文字がなければ処理を終了する
	if ( StrCnt <= 0 ) return ;
	if ( StrLength <= StrSPos || StrLength < StrSPos + StrCnt ) return ;

	// タブ／非タブまたはGCが変化するたびに描画する
	wIsSel = pCursorInfo->IsCharSelected( T_CurPos( LineNum, StrSPos ) );
	hIsTab = ( (*rItr)[ StrSPos ].moji == L'\t' );
	hGC = pGCMgr->GetTextDrawGC( (*rItr)[ StrSPos ].iro, wIsSel, hIsTab );
	hIdx = StrSPos;
	hIsSel = wIsSel;
	wWidth = 0;

	for ( i = StrSPos; i < StrSPos + StrCnt; i++ ) {

		// rStr[i]を描画するのに使用するGCを取得
		wIsSel = pCursorInfo->IsCharSelected( T_CurPos( LineNum, i ) );
		GC wGC = pGCMgr->GetTextDrawGC( (*rItr)[ i ].iro, wIsSel, ( rStr[i] == L'\t' ) );

		if ( hGC != wGC || hIsTab != ( rStr[i] == L'\t' ) ) {
			// 状況が変わったため、文字列を出力する
			if ( hIsTab )
				DrawTab( pDisplay, argDrawable, hIdx, i, rDrawCharPos, wWidth, wXPos, YPos, hGC, pGCMgr->GetEditMarkGC() );
			else
				XwcDrawImageString( pDisplay, argDrawable, fontSet, hGC, wXPos, YPos + TextAscent, rStr.begin() + hIdx, i - hIdx );

			// 現在の状況を保存する
			hGC = wGC;
			hIsTab = ( rStr[i] == L'\t' );
			hIdx = i;
			hIsSel = wIsSel;
			wXPos += wWidth;	// 次に描画すべき位置を算出する
			wWidth = 0;
		}

		// 文字幅を取得
		wWidth += rDrawCharPos.GetCharWidth( i );
	}

	// 行の最後の部分を描画
	if ( hIsTab )
		DrawTab( pDisplay, argDrawable, hIdx, i, rDrawCharPos, wWidth, wXPos, YPos, hGC, pGCMgr->GetEditMarkGC() );
	else
		XwcDrawImageString( pDisplay, argDrawable, fontSet, hGC, wXPos, YPos + TextAscent, rStr.begin() + hIdx, i - hIdx );
}

// TABを描画
void CTaEditDraw::DrawTab(
	Display *pDisplay,
	Drawable argDrawable,
	int s,
	int e,
	const CDrawCharPos& rDrawCharPos,
	int width,
	int XPos,
	int YPos,
	GC ClearGC,
	GC EditMarkGC ) const
{
	int wHeight = GetTextHeight();
	int HPos = YPos + wHeight / 2;	// 矢印を描画するY位置
	int i;
	int wx = XPos;
	VClsPtr< CConfigInfo > pConf = m_TaEditShell->GetConfigInfo();

	// まずは空白を描画
	XFillRectangle( pDisplay, argDrawable, ClearGC, XPos, YPos, width, wHeight );

	// 編集記号を描画しないのなら、空白の描画だけで終了
	if ( !pConf->GetDrawEditMark() ) return;

	// 矢印を描画
	for ( i = s; i < e; i++ ) {
		int CharWidth = rDrawCharPos.GetCharWidth( i );	// TAB一つ文の幅
		int ArrowWidth = CharWidth / 2;	// 矢印の長さ
		int LPos = wx + ArrowWidth / 2;	// 矢印の左端
		int RPos = LPos + ArrowWidth;	// 矢印の右端
		wx += CharWidth;

		XDrawLine( pDisplay, argDrawable, EditMarkGC, LPos, HPos, RPos, HPos );
		XDrawLine( pDisplay, argDrawable, EditMarkGC, RPos, HPos, RPos - ArrowWidth / 4, HPos - wHeight / 4 );
		XDrawLine( pDisplay, argDrawable, EditMarkGC, RPos, HPos, RPos - ArrowWidth / 4, HPos + wHeight / 4 );
	}
}

// 左側のカラムを描画
void CTaEditDraw::DrawLeftColumn(
	Display *pDisplay,
	Drawable argDrawable,
	int YPos,
	const T_LineDataItr_c &rItr,
	int ColW,
	int ColCnt ) const
{
	int i;

	// 不要なものを消去
	XFillRectangle( pDisplay, argDrawable, pGCMgr->GetClearGC(), 0, YPos,
		LeftColumnWidth, TextHeight );

	// 各カラムを描画する
	for ( i = 0; i < ColCnt; i++ ) {
		if ( rItr->GetExtendInfo( i ) )
			XFillRectangle( pDisplay, argDrawable, pGCMgr->GetExInfoColumnGC(i), ColW * i, YPos, ColW, TextHeight );
	}
}

// 編集後の再描画処理を行う
void CTaEditDraw::AfterEditRedraw( const ScrollInfoPacket &rScrPacket )
{
	CTaEditDoc *pDoc = m_TaEditShell->GetDocument();
	ScrollInfoPacket wScrPacket = rScrPacket;
	int ViewLineCnt = GetViewLineCnt();

	// カーソルの横位置が、MaxLengthPixを越えた場合は、MaxLengthPixを更新する
	if ( MaxLengthPix < pCursorInfo->GetCurPosX() ) {
		MaxLengthPix = pCursorInfo->GetCurPosX();
		pHScrollMgr->SetMax( MaxLengthPix );
	}
	if ( wScrPacket.NeedXScroll ) ScrollH( wScrPacket.pixX, false );

	const int AfterLineCnt = pDoc->RefData().size();	// 編集後の行数

	// 画面が下の方に有って、行数が減った場合は、上方向にスクロールする必要がある
	if ( ( pVScrollMgr->GetValue() + ViewLineCnt > AfterLineCnt ) ) {
		int wScrCnt = AfterLineCnt - ViewLineCnt;	// どこにスクロールするか
		if ( wScrCnt < 0 ) wScrCnt = 0;
		ScrollV( wScrCnt, false );
		// wScrPacket.NeedYScroll = false;
	}

	// 最大値を設定
	pVScrollMgr->SetMax( AfterLineCnt );

	// カーソルの移動に伴うスクロール
	if ( wScrPacket.NeedYScroll ) ScrollV( wScrPacket.lineY, false );

	// 必要に応じた再描画
	if ( rScrPacket.NeedYScroll || rScrPacket.NeedXScroll )
		OnRedraw( TED_REUSE_CHAR_INFO );
	else
		OnRedraw( TED_LINE_BASE );
}

// 選択範囲の変化後の再描画処理を行う
void CTaEditDraw::AfterSelRangeRedraw( int argSLine, int argELine )
{
	OnRedraw( TED_REUSE_CHAR_INFO, argSLine, argELine );
}
