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

#include "TaEditDoc.h"
#include "TaEditShell.h"
#include "FileReader.h"
#include "FileWriter.h"
#include <sys/stat.h>
#include <X11/keysym.h>


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

CTaEditDoc::CTaEditDoc() :
	IsModify( false ), pTxtColorMgr( NULL )
{
	FileName = "";
	UndoListMgr.Clear();
	vecLine.clear();
}

CTaEditDoc::~CTaEditDoc()
{

}

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

// ファイルの読み込み
CErrorBool CTaEditDoc::OnOpenDocument( const string &argFileName, bool *pIsChanged, enumLANGTYPE LangType )
{
	assert( NULL != this && NULL != pIsChanged );
	VClsPtr< CConfigInfo > pConf = m_TaEditShell->GetConfigInfo();
	int ShowDlgDelay = pConf->GetShowPrecessingDlgDelay();
	CFileReader *pReader;
	CErrorBool r;
	T_LineData wLineData;

	(*pIsChanged) = false;

	r = CheckFileStatus( argFileName, true );
	if ( !r ) return r;

	// 読み込み用オブジェクトを構築
	pReader = new CFileReader();
	if ( NULL == pReader )
		return CErrorBool( pConf->RefOutOfMemoryErrorMsg() );
	pReader->SetInfo( argFileName, &wLineData );

	// 読み込み
	if ( m_ProcessingShell->ShowDlg( pReader, ShowDlgDelay ) ) {
		// 中断された
		(*pIsChanged) = false;
		delete pReader;
		return true;
	}

	r = pReader->GetErrorInfo();
	delete pReader;
	pReader = NULL;

	if ( !r ) {
		(*pIsChanged) = false;
		return r;
	}

	(*pIsChanged) = true;
	vecLine.swap( wLineData );
	wLineData.clear();

	// ファイル名を取得する
	FileName = JPEXT_ResolvPath( argFileName );

	// 言語依存情報オブジェクトを更新
	if ( LANGTYPE_NAN == LangType )
		UpdateTextColorMgr();	// 指定がない場合
	else
		SetTxtColorMgr( LangType );	// 指定されている場合

	SetModifyFlg( false );	// 更新フラグをおろす

	return r;
}

// ファイルの出力
// pColChanged : 色情報が更新された場合は真が設定される
CErrorBool CTaEditDoc::OnSaveDocument( const string &argFileName, bool *pColChanged )
{
	assert( NULL != this );

	VClsPtr< CConfigInfo > pConf = m_TaEditShell->GetConfigInfo();
	int ShowDlgDelay = pConf->GetShowPrecessingDlgDelay();
	bool UseTmpFile = pConf->GetUseWriteTmpFile();
	CFileWriter *pWriter;
	CErrorBool r;
	struct stat StatusInfo;

	if ( pColChanged ) (*pColChanged) = false;

	// ファイルの状態を確認する（ファイルが存在する必要はない）
	r = CheckFileStatus( argFileName, false );
	if ( !r ) return r;

	// 出力先ファイルが存在しない場合は、テンポラリファイルへの出力は行わない
	if ( lstat( argFileName.c_str(), &StatusInfo ) )
		UseTmpFile = false;

	// 書き込み用オブジェクトを構築
	pWriter = new CFileWriter();
	if ( NULL == pWriter )
		return CErrorBool( pConf->RefOutOfMemoryErrorMsg() );
	pWriter->SetInfo( argFileName, &vecLine, UseTmpFile );

	// 書き込み
	if ( m_ProcessingShell->ShowDlg( pWriter, ShowDlgDelay ) )
		goto INTRERRUPT_EXIT;

	r = pWriter->GetErrorInfo();

	// テンポラリファイルへの待避でこけた場合、再挑戦する
	if ( UseTmpFile && pWriter->GetFaultWithTmpFlg() ) {
		// 再挑戦するか否か確認する
		int sel = m_TaEditShell->ShowQuestionMsgBox( pConf->RefRetryWithoutSaveTmpFileMsg(), false );
		if ( 1 == sel ) {
			// 再挑戦
			pWriter->SetInfo( argFileName, &vecLine, false );
			if ( m_ProcessingShell->ShowDlg( pWriter, ShowDlgDelay ) )
				goto INTRERRUPT_EXIT;
			r = pWriter->GetErrorInfo();
		}
		else {
			// 再挑戦しない場合は、エラーを返さない
			// （ユーザはすでにエラーが発生したことを知っているため）
			goto INTRERRUPT_EXIT;
		}
	}

	// 書き込み用オブジェクトを破棄
	delete pWriter;
	pWriter = NULL;

	if ( !r ) return r;

	// ファイルの保存に成功したら、ドキュメントのファイル名を設定する。
	FileName = JPEXT_ResolvPath( argFileName );
	SetModifyFlg( false );	// 更新フラグをおろす

	// 言語依存情報オブジェクトを更新する
	if ( UpdateTextColorMgr() ) {
		if ( pColChanged ) (*pColChanged) = true;
	}

	return true;

INTRERRUPT_EXIT:
	// 書き込み用オブジェクトを破棄
	delete pWriter;
	pWriter = NULL;
	return true;
}

// ファイル名の参照
const string& CTaEditDoc::GetFileName() const
{
	return FileName;
}

// 初期化
void CTaEditDoc::Initialize()
{
	CLineData dummy;

	vecLine.clear();
	FileName = "";

	UndoListMgr.Clear();	// アンドゥリストをクリアする
	UndoListMgr.SetMaxUndoCount( m_TaEditShell->GetConfigInfo()->GetMaxUndoCount() );

	SetModifyFlg( false );	// 更新フラグをおろす
	vecLine.push_back( dummy );	// 空の行を一つ登録する

	// 言語依存情報オブジェクトを更新
	UpdateTextColorMgr();
}

// 文字列の挿入
CErrorBool CTaEditDoc::Insert( const wstring& rStr, const T_CurPos &rCurPos, const T_SelRange &rSelRange, T_CurPos *pResultCurPos )
{
	T_CurPos wCurPos = rCurPos;
	int i;
	int LineCnt, CharCnt;
	VClsPtr< CEditCommand > wpEditCommand;
	CErrorBool r;

	(*pResultCurPos) = rCurPos;
	if ( vecLine.size() <= rCurPos.GetLine() ) return true;
	SetModifyFlg( true );	// 更新フラグをたてる

	// 選択されている範囲を削除する
	if ( rSelRange.GetLength() > T_CurPos( 0, 0 ) ) {
		r = Delete_WithCreateEC( rSelRange, rCurPos );
		if ( !r ) return r;
		wCurPos = rSelRange.GetSPos();
		(*pResultCurPos) = wCurPos;
	}

	// 行の長さを確認
	CharCnt = wCurPos.GetCPos();
	LineCnt = 0;
	for ( i = 0; i < rStr.length(); i++ ) {
		CharCnt++;
		if ( rStr[i] == L'\n' ) {
			CharCnt = 0;
			LineCnt++;
		}
		if ( CharCnt >= TA_MAX_LINE_SIZE ) {
			return CErrorBool( m_TaEditShell->GetConfigInfo()->RefBrokenLineMsg() );
		}
	}
	// （この時点でLineCntには追加する行の行数-1、
	// 　CharCntには最後の行に追加する文字数が格納されている。
	// 　なお、追加するデータが一行だった場合は、カーソル位置となる）
	if ( CharCnt + vecLine[ wCurPos.GetLine() ].length() - wCurPos.GetCPos() >= TA_MAX_LINE_SIZE )
		return CErrorBool( m_TaEditShell->GetConfigInfo()->RefBrokenLineMsg() );

	// 戻り値を構築
	T_CurPos RetACPT( wCurPos.GetLine() + LineCnt, CharCnt );

	// 編集コマンドオブジェクトを構築
	wpEditCommand = CreateEditCommandObj( TEC_INSERT );
	if ( !wpEditCommand.IsNull() ) {
		if ( rSelRange.GetLength() > T_CurPos( 0, 0 ) )
			wpEditCommand->SetCurPos_SelRange( rSelRange, wCurPos, T_SelRange( RetACPT, RetACPT ), RetACPT );
		else {
			T_SelRange ws = T_SelRange( wCurPos, wCurPos );
			wpEditCommand->SetCurPos_SelRange( ws, wCurPos, T_SelRange( RetACPT, RetACPT ), RetACPT );
		}
		wpEditCommand->SelInsertInfo( wCurPos, rStr );
		UndoListMgr.PushBack( wpEditCommand );
	}

	// 文字列を挿入
	InsertSpecifiedStr( wCurPos, rStr );

	(*pResultCurPos) = RetACPT;
	return true;
}

// 特殊記号の挿入
// pResultCurPosにカーソルを設定すべき位置を返す
CErrorBool CTaEditDoc::InsertSpecialCode( KeySym argKeySym, const T_CurPos &rCurPos, const T_SelRange &rSelRange, T_CurPos *pResultCurPos )
{
	int i;
	VClsPtr< CEditCommand > wpEditCommand;
	const int cpos = rCurPos.GetCPos();
	const int line = rCurPos.GetLine();
	T_CurPos RetACPT;
	T_CurPos wpos;
	CErrorBool r;

	SetModifyFlg( true );	// 更新フラグをたてる
	(*pResultCurPos) = rCurPos;

	// 範囲選択されている場合
	if ( rSelRange.GetLength() > T_CurPos( 0, 0 ) ) {
		// 選択されている範囲を削除する
		r = Delete_WithCreateEC( rSelRange, rCurPos );
		if ( !r ) return r;

		// BackSpaceかDeleteだった場合
		if ( argKeySym == XK_BackSpace || argKeySym == XK_Delete ) {
			// 選択範囲の先頭を戻り値として、処理を終了する
			(*pResultCurPos) = rSelRange.GetSPos();
			return true;
		}
	}

	switch ( argKeySym ) {
	case XK_BackSpace:
		if ( cpos <= 0 && line <= 0 ) return true;// 削除できない
		if ( 0 == cpos )
			// 行の先頭だった場合は、前の行の末尾から現在の行の先頭までを削除する
			RetACPT = T_CurPos( line - 1, vecLine[ line - 1 ].length() );
		else
			// 一つ前の文字を削除する
			RetACPT = T_CurPos( line, cpos - 1 );
		(*pResultCurPos) = RetACPT;
		r = Delete_WithCreateEC( T_SelRange( RetACPT, rCurPos ), rCurPos );
		return r;

	case XK_Delete:
		if ( line >= vecLine.size() - 1 && cpos >= vecLine[ line ].length() )
			return true;	// 削除できない
		RetACPT = rCurPos;
		if ( cpos == vecLine[ line ].length() )
			// 行の末尾だった場合は、現在位置から次の行の先頭までを削除する
			wpos = T_CurPos( line + 1, 0 );
		else
			// 現在指している文字を削除する
			wpos = T_CurPos( line, cpos + 1 );
		r = Delete_WithCreateEC( T_SelRange( rCurPos, wpos ), rCurPos );
		return r;

	case XK_KP_Enter:
	case XK_Return:
		RetACPT = T_CurPos( rSelRange.GetSPos().GetLine() + 1, 0 );
		(*pResultCurPos) = RetACPT;

		// 編集コマンドオブジェクトを構築する
		wpEditCommand = CreateEditCommandObj( TEC_INSERT );
		if ( !wpEditCommand.IsNull() ) {
			if ( rSelRange.GetLength() > T_CurPos( 0, 0 ) ) {
				// 文字列が削除されている
				T_CurPos wc = rSelRange.GetSPos();
				T_SelRange ws = T_SelRange( wc, wc );
				wpEditCommand->SetCurPos_SelRange( ws, wc, T_SelRange( RetACPT, RetACPT ), RetACPT );
				wpEditCommand->SelInsertInfo( wc, L"\n" );
			}
			else {
				// 削除されていない
				wpEditCommand->SetCurPos_SelRange( rSelRange, rCurPos, T_SelRange( RetACPT, RetACPT ), RetACPT );
				wpEditCommand->SelInsertInfo( rCurPos, L"\n" );
			}
			UndoListMgr.PushBack( wpEditCommand );
		}
		InsertSpecifiedStr( rSelRange.GetSPos(), L"\n" );
		pResultCurPos->SetCPos( ExecAutoIndent( rSelRange.GetSPos().GetLine() ) );
		return true;
	}
	return CErrorBool( m_TaEditShell->GetConfigInfo()->RefUnexpectedErrorMsg() );
}

// 複数行へのTABの挿入
CErrorBool CTaEditDoc::MultiLineTabInsert( const T_SelRange &rSelRange, T_CurPos *pResultCurPos, T_SelRange *pResultSelRange )
{
	int sl = rSelRange.GetSPos().GetLine();
	int el = rSelRange.GetEPos().GetLine();
	int i;
	T_CurPos dummyCurPos;
	T_CurPos wCurPos;
	T_SelRange wSelRange;
	CErrorBool r;
	VClsPtr< CConfigInfo > pConf = m_TaEditShell->GetConfigInfo();
	const wstring &rStr = pConf->RefTabString();
	const bool InsertTabToEmptyLine = pConf->GetInsertTabToEmptyLine();

	// 選択されている、各行の行頭にTAB文字を挿入する
	for ( i = sl; i <= el; i++ ) {
		// 空行にはTabを挿入しない場合がある
		if ( !InsertTabToEmptyLine && vecLine[i].length() == 0 )
			continue;

		wCurPos = T_CurPos( i, 0 );
		wSelRange = T_SelRange( wCurPos, wCurPos );
		r = Insert( rStr, wCurPos, wSelRange, &dummyCurPos );
		if ( !r ) return r;
	}

	// 選択範囲をTAB文字の挿入を行った行全体とする。
	(*pResultCurPos) = T_CurPos( el, vecLine[ el ].length() );
	(*pResultSelRange) = T_SelRange( T_CurPos( sl, 0 ), (*pResultCurPos) );
	return true;
}

// 複数行のTABの削除
CErrorBool CTaEditDoc::MultiLineTabDelete( const T_SelRange &rSelRange, T_CurPos *pResultCurPos, T_SelRange *pResultSelRange )
{
	int sl = rSelRange.GetSPos().GetLine();
	int el = rSelRange.GetEPos().GetLine();
	int i;
	T_CurPos wCurPos;
	T_SelRange wSelRange;
	CErrorBool r;
	const wstring &rStr = m_TaEditShell->GetConfigInfo()->RefTabString();

	// 選択されている、各行の行頭に存在するTAB文字を削除する
	for ( i = sl; i <= el; i++ ) {
		wCurPos = T_CurPos( i, 0 );
		// 行頭にTAB文字がなければ、削除を行わない。
		if ( wsncmp( vecLine[i].getString().c_str(), rStr.c_str(), rStr.length() ) == 0 ) {
			wSelRange = T_SelRange( T_CurPos( i, 0 ), T_CurPos( i, rStr.length() ) );
			r = Delete_WithCreateEC( wSelRange, wCurPos );
			if ( !r ) return r;
		}
	}

	// 選択範囲をTAB文字の削除を行った行全体とする。
	(*pResultCurPos) = T_CurPos( el, vecLine[ el ].length() );
	(*pResultSelRange) = T_SelRange( T_CurPos( sl, 0 ), (*pResultCurPos) );
	return true;
}

// 編集コマンドオブジェクトを生成して、文字列の削除を行う
CErrorBool CTaEditDoc::Delete_WithCreateEC( T_SelRange DelRange, T_CurPos BeforeCurPos )
{
	VClsPtr< CEditCommand > wpEditCommand;
	CErrorBool r;

	// 削除範囲の文字列を取得
	wstring wDelStr;
	GetRangeStr( DelRange, &wDelStr );

	// 文字列を削除する
	r = Delete( DelRange );
	if ( !r ) return r;

	// 編集コマンドオブジェクトを構築する
	wpEditCommand = CreateEditCommandObj( TEC_DELETE );
	if ( !wpEditCommand.IsNull() ) {
		T_CurPos wc = DelRange.GetSPos();
		wpEditCommand->SetCurPos_SelRange( DelRange, BeforeCurPos, T_SelRange( wc, wc ), wc );
		wpEditCommand->SetDeleteInfo( DelRange, wDelStr );
		UndoListMgr.PushBack( wpEditCommand );
	}
	return true;
}

// データの参照
const T_LineData& CTaEditDoc::RefData() const
{
	return vecLine;
}

// 更新フラグを取得
bool CTaEditDoc::GetIsModify() const
{
	return IsModify;
}

// アンドゥ可能か否か
bool CTaEditDoc::EnableUndo() const
{
	return UndoListMgr.EnableUndo();
}

// リドゥ可能か否か
bool CTaEditDoc::EnableRedo() const
{
	return UndoListMgr.EnableRedo();
}

// アンドゥ
void CTaEditDoc::Undo( T_SelRange *pAfterUndoSelRange, T_CurPos *pAfterUndoCurPos )
{
	assert( NULL != pAfterUndoSelRange && NULL != pAfterUndoCurPos );
	if ( !EnableUndo() ) return ;

	list< VClsPtr< CEditCommand > > ecList;
	list< VClsPtr< CEditCommand > >::const_iterator itr;

	ecList = UndoListMgr.GetUndoObj();

	itr = ecList.begin();
	for (; itr != ecList.end(); ++itr ) {
		// アンドゥ後の選択範囲とカーソル位置を取得
		(*pAfterUndoSelRange) = (*itr)->GetBeforeSelRange();
		(*pAfterUndoCurPos) = (*itr)->GetBeforeCurPos();

		if ( (*itr)->GetType() == TEC_INSERT ) {
			// 文字列の挿入が行われていた場合
			// 挿入された文字列を削除する
			DelSpecifiedStr( (*itr)->GetInsertPos(), (*itr)->GetInsertText() );
		}
		else {
			// 文字列の削除が行われていた場合
			// 削除された文字列を挿入する
			InsertSpecifiedStr( (*itr)->GetDeleteRange().GetSPos(), (*itr)->GetDeleteText() );
		}
	}
}

// リドゥ
void CTaEditDoc::Redo( T_SelRange *pAfterRedoSelRange, T_CurPos *pAfterRedoCurPos )
{
	assert( NULL != pAfterRedoSelRange && NULL != pAfterRedoCurPos );
	if ( !EnableRedo() ) return ;

	list< VClsPtr< CEditCommand > > ecList;
	list< VClsPtr< CEditCommand > >::const_iterator itr;

	ecList = UndoListMgr.GetRedoObj();
	itr = ecList.begin();
	for (; itr != ecList.end(); ++itr ) {
		// リドゥ後の選択範囲とカーソル位置を取得
		(*pAfterRedoSelRange) = (*itr)->GetAfterSelRange();
		(*pAfterRedoCurPos) = (*itr)->GetAfterCurPos();

		if ( (*itr)->GetType() == TEC_INSERT ) {
			// 文字列の挿入が行われていた場合
			// 挿入された文字列を、再度挿入する
			InsertSpecifiedStr( (*itr)->GetInsertPos(), (*itr)->GetInsertText() );
		}
		else {
			// 文字列の削除が行われていた場合
			// 削除された文字列を、再度削除する
			Delete( (*itr)->GetDeleteRange() );
		}
	}
}

// 指定した位置に存在する、指定した文字列を削除する
// （削除位置及び削除されたテキストは判るが、削除された範囲の情報が失われている場合に使用）
CErrorBool CTaEditDoc::DelSpecifiedStr( T_CurPos pos, const wstring &rStr )
{
	T_SelRange wSelRange;	// 削除する文字列の範囲を保持する
	T_CurPos wSelRange_end;		// wSelRangeの終了位置の計算用
	int crcnt = 0;
	int i;

	// 文字列に含まれている改行コードの数を算出
	for ( i = 0; i < rStr.length(); i++ ) {
		if ( rStr[i] == L'\n' ) crcnt++;
	}

	// 削除する文字列の範囲を求める
	if ( crcnt == 0 ) {
		// 文字列に改行コードが含まれていない場合
		wSelRange_end.SetLine( pos.GetLine() );
		wSelRange_end.SetCPos( pos.GetCPos() + rStr.length() );
	}
	else {
		// 文字列に改行コードが含まれていた場合
		wSelRange_end.SetLine( pos.GetLine() + crcnt );
		wSelRange_end.SetCPos( rStr.length() - rStr.rfind( L'\n' ) - 1 );
	}

	// 文字列を削除
	wSelRange.SetPosEx( pos, wSelRange_end );
	return Delete( wSelRange );
}

// 指定した位置に指定した文字列（改行コードを含む）を追加する
void CTaEditDoc::InsertSpecifiedStr( T_CurPos pos, const wstring &rStr )
{
	int i;	// rStrの処理位置を保持する
	int j;
	int h;	// iの以前の値を保持する
	int p = pos.GetCPos();
	int wLine = pos.GetLine();

	i = 0;
	h = 0;
	while ( i < rStr.length() ) {
		// 改行コードを検索する
		while ( i < rStr.length() && rStr[i] != L'\n' ) i++;
		// 文字列を挿入する
		vecLine[ wLine ].InsertBlank( p, i - h );
		for ( j = 0; j < ( i - h ); j++ )
			vecLine[ wLine ].set( p + j, TA_CHAR( rStr[ h + j ], 0 ) );
		// 挿入対象の文字列の末尾に達していなければ、行を追加する
		if ( i >= rStr.length() ) {
			break;
		}
		AddCR( T_CurPos( wLine, p + j ) );	// この行にくる場合、rStr[i]は改行コードである
		p = 0;
		i++;	// 次の文字へ
		h = i;
		wLine++;
	}

	// 構文強調表示処理を行う
	if ( pTxtColorMgr ) {
		T_SelRange sr;
		if ( wLine == pos.GetLine() )
			sr = T_SelRange( pos, T_CurPos( wLine, pos.GetCPos() + rStr.length() ) );
		else
			sr = T_SelRange( pos, T_CurPos( wLine, i - h ) );
		pTxtColorMgr->Update( vecLine, sr );
	}
}

// 改行
void CTaEditDoc::AddCR( T_CurPos pos )
{
	CLineData w;
	int i, j;
	int cLine = pos.GetLine();
	int nLine = cLine + 1;
	int xPos = pos.GetCPos();
	CLineData *wpLine;

	// 空の行を追加する
	vecLine.insert( vecLine.begin() + nLine, w );	

	// 前の行の末尾にある、改行コードの色情報を、
	// 新規に追加した行に設定する
	wpLine = &( vecLine[ cLine ] );
	vecLine[ nLine ].setIro( 0, wpLine->getIro( wpLine->length() ) );

	// 追加した空の行に、文字列を登録する
	j = vecLine[ cLine ].length() - xPos;	// 追加した行に登録する文字数

	if ( j <= 0 ) return ;
	wpLine = &( vecLine[ nLine ] );
	wpLine->InsertBlank( 0, j );	// 領域を確保
	for ( i = 0; i < j; i++ )	// 文字を設定
		wpLine->set( i, vecLine[ cLine ][ i + xPos ] );

	// 新しい行に移した分の文字列を削除する
	vecLine[ cLine ].Delete( xPos, xPos + j );
}

// 自動インデントによる調整
// lineの行の先頭にある空白などを、line+1の行頭に挿入する
// 挿入した文字数を返す。
int CTaEditDoc::ExecAutoIndent( int line )
{
	VClsPtr< CConfigInfo > pConf = m_TaEditShell->GetConfigInfo();
	bool AutoIndent = pConf->GetAutoIndent();
	const wstring &IndentChar = pConf->RefIndentChar();
	int i, j;

	assert( this && line < vecLine.size() - 1 );
	
	CLineData *wpLine1 = &( vecLine[ line ] );
	CLineData *wpLine2 = &( vecLine[ line + 1 ] );

	if ( !AutoIndent ) return 0;	// オートインデントの必要がなければ終了する

	// インデントする文字数をカウント
	for ( i = 0; i < wpLine1->length() && NULL != wcschr( IndentChar.c_str(), (*wpLine1)[i].moji ); ++i );

	wpLine2->InsertBlank( 0, i );	// 領域を確保
	for ( j = 0; j < i; ++j )	// 文字を設定
		wpLine2->set( j, (*wpLine1)[j] );

	// 構文強調表示処理を行う
	if ( pTxtColorMgr )
		pTxtColorMgr->Update( vecLine, T_SelRange( T_CurPos( line + 1, 0 ), T_CurPos( line + 1, i ) ) );

	return i;
}

// 文字列の削除
CErrorBool CTaEditDoc::Delete( const T_SelRange &rRange )
{
	assert( NULL != this );

	int sline = rRange.GetSPos().GetLine();
	int scpos = rRange.GetSPos().GetCPos();
	int eline = rRange.GetEPos().GetLine();
	int ecpos = rRange.GetEPos().GetCPos();

	assert( sline >= 0 && sline < vecLine.size() && eline >= 0 && eline < vecLine.size() );

	int sLineLength = vecLine[ sline ].length();
	int eLineLength = vecLine[ eline ].length();

	assert( scpos >= 0 && scpos <= sLineLength && ecpos >= 0 && ecpos <= eLineLength );

	if ( sline == eline ) {
		// 同じ行内の一部を削除する場合
		vecLine[ sline ].Delete( scpos, ecpos );

		// 構文強調表示処理を行う
		if ( pTxtColorMgr ) {
			T_SelRange sr = T_SelRange( rRange.GetSPos(), rRange.GetSPos() );
			pTxtColorMgr->Update( vecLine, sr );
		}

		return true;
	}

	// 行の長さに対するチェック
	if ( scpos + ( eLineLength - ecpos ) >= TA_MAX_LINE_SIZE ) {
		VClsPtr< CConfigInfo > pConf = m_TaEditShell->GetConfigInfo();
		return CErrorBool( pConf->RefBrokenLineMsg() );
	}

	// 削除を開始する行に対する処理
	vecLine[ sline ].Delete( scpos, sLineLength );

	// 途中の行を全て削除
	vecLine.erase( vecLine.begin() + sline + 1, vecLine.begin() + eline );

	// 連結処理
	vecLine[ sline ].InsertBlank( scpos, ( eLineLength - ecpos ) );
	for ( int i = 0; i < eLineLength - ecpos; i++ )
		vecLine[ sline ].set( i + scpos, vecLine[ sline + 1 ][ i + ecpos ] );
	// （改行コードの色も連結する）
	unsigned char col = vecLine[ sline + 1 ].getIro( vecLine[ sline + 1 ].length() );
	vecLine[ sline ].setIro( vecLine[ sline ].length(), col );

	// 削除範囲の最後の行を削除する
	vecLine.erase( vecLine.begin() + sline + 1 );

	// 構文強調表示処理を行う
	if ( pTxtColorMgr )
		pTxtColorMgr->Update( vecLine, T_SelRange( rRange.GetSPos(), rRange.GetSPos() ) );

	return true;
}

// 編集コマンドオブジェクトを生成
VClsPtr< CEditCommand > CTaEditDoc::CreateEditCommandObj( enumTEC_TYPE argType )
{
	// 削除しない場合

	VClsPtr< CEditCommand > wpEditCommand = new CEditCommand( argType );
	if ( wpEditCommand.IsNull() ) 
		UndoListMgr.Clear();	// メモリが不足する場合は、アンドゥリストを破棄する
	return wpEditCommand;
}


// 指定した範囲の文字列を取得
void CTaEditDoc::GetRangeStr( const T_SelRange &range, wstring *pStr )
{
	const T_CurPos s = range.GetSPos();
	const T_CurPos e = range.GetEPos();
	int i;
	size_t wLen;

	// 同一の行を指定された場合
	if ( s.GetLine() == e.GetLine() ) {
		if ( s.GetCPos() == e.GetCPos() ) {
			(*pStr) = L"";	// 返すべき文字列はない
			return ;
		}
		const wstring &wrstr = vecLine[ s.GetLine() ].getString();
		(*pStr) = wrstr.substr( s.GetCPos(), e.GetCPos() - s.GetCPos() );
		return ;
	}

	// 最初の行の文字列を取得
	int sline = s.GetLine();
	int eline = e.GetLine();
	(*pStr) = vecLine[ sline ].getString().substr( s.GetCPos() );

	// 間の行の文字列を取得
	// （必要となる文字数をカウント）
	wLen = 0;
	i = sline;
	for ( i++; i != eline; i++ )
		wLen += 1 + vecLine[ sline ].getString().length();
	// メモリ領域を確保
	pStr->reserve( pStr->length() + wLen );
	// 設定
	for ( sline++; sline != eline; sline++ ) {
		(*pStr) += wstring( L"\n" ) + vecLine[ sline ].getString();
	}

	// 最後の行の文字列を取得
	(*pStr) += wstring( L"\n" ) + vecLine[ sline ].getString().substr( 0, e.GetCPos() );
}

// ファイルの状態を確認する
CErrorBool CTaEditDoc::CheckFileStatus( const string &rFileName, bool Exist )
{
	struct stat StatusInfo;
	int r;
	VClsPtr< CConfigInfo > pConf = m_TaEditShell->GetConfigInfo();

	r = lstat( rFileName.c_str(), &StatusInfo );
	if ( -1 == r ) {
		// ファイルが存在しない
		if ( !Exist )
			return true;	// 存在する必要がなければ、真を返す
		return CErrorBool( pConf->RefFileIsNotExistMsg() );
	}

	if ( StatusInfo.st_mode & S_IFDIR )
		return CErrorBool( pConf->RefSpecifiedNameIsDirMsg() );

	return true;
}

// 言語依存情報オブジェクトを取得
const NLangExt::CTxtColorMgr* CTaEditDoc::GetTxtColorMgr() const
{
	return pTxtColorMgr;
}

// 言語依存情報オブジェクトを更新する
// オブジェクトの再構築が行われた場合には真を返す
bool CTaEditDoc::UpdateTextColorMgr()
{
	VClsPtr< CConfigInfo > pConf = m_TaEditShell->GetConfigInfo();
	const NLangExt::CLangConfig *pLangConf = m_TaEditShell->GetLangConfig();

	// pLangConfはNULLの可能性がある
	if ( NULL == pLangConf ) return false;

	// 言語モードを有効になる、ファイルの最大長
	unsigned int LangTypeEnableMaxSize = pConf->GetLangTypeEnableMaxSize();

	// 総文字列長を算出する
	unsigned long size = 0;
	T_LineDataItr_c itr = vecLine.begin();
	for ( ; itr != vecLine.end(); ++itr )
		size += itr->length() + 1;	// +1は改行コード
	size--;				// 末尾の改行コードの分を引く

	// 更新する（LangTypeEnableMaxSizeバイトの物に対しては構文強調表示を行う）
	if ( size <= LangTypeEnableMaxSize )
		return SetTxtColorMgr( pLangConf->GetLangTypeID( FileName.c_str() ) );
	else
		return SetTxtColorMgr( LANGTYPE_TEXT );
}

// 言語情報オブジェクトを設定
// オブジェクトの再構築が行われた場合は真を返す
bool CTaEditDoc::SetTxtColorMgr( enumLANGTYPE type )
{
	bool IsChanged = false;	// 更新されたか否か
	VClsPtr< CConfigInfo > pConf = m_TaEditShell->GetConfigInfo();
	const NLangExt::CLangConfig *pLangConf = m_TaEditShell->GetLangConfig();

	// pLangConfはNULLの可能性がある
	if ( NULL == pLangConf ) return false;

	if ( pTxtColorMgr ) {
		// 既存のものが存在する場合
		if ( pTxtColorMgr->GetLangType() != type ) {
			// 言語情報オブジェクトを更新しなくてはならない場合は、既存のものを破棄する
			delete pTxtColorMgr;
			pTxtColorMgr = NULL;
			IsChanged = true;
		}
	}

	if ( !pTxtColorMgr ) {
		// 言語情報オブジェクトを構築する
		pTxtColorMgr = pLangConf->CreateColorMgr( type );
		if ( NULL == pTxtColorMgr ) return IsChanged;

		// 言語情報オブジェクトを初期化する
		if ( !pTxtColorMgr->Initialize( pConf ) ) {
			delete pTxtColorMgr;
			pTxtColorMgr = NULL;
			return IsChanged;
		}
		IsChanged = true;
	}

	// 初期構文強調表示処理を行う
	pTxtColorMgr->InitiUpdate( vecLine );
	return IsChanged;
}

// 拡張情報の設定
CErrorBool CTaEditDoc::SetExtendInfo( size_t Line, int idx, bool flg )
{
	VClsPtr< CConfigInfo > pConf = m_TaEditShell->GetConfigInfo();
	if ( Line < 0 || Line >= vecLine.size() )
		return CErrorBool( pConf->RefUnexpectedErrorMsg() );
	if ( idx < 0 || idx >= pConf->GetExInfoColumnCount() )
		return CErrorBool( pConf->RefUnexpectedErrorMsg() );

	// 拡張情報を設定する
	vecLine[Line].SetExtendInfo( idx, flg );
	// ドキュメントの中身が変更されたわけではないので、
	// 更新フラグは設定しない

	return true;
}

// 編集の終了
void CTaEditDoc::EndEdit()
{
	// Undoリストに編集の終了を通知する
	UndoListMgr.EndEdit();
}

// 更新フラグを設定する
void CTaEditDoc::SetModifyFlg( bool flg )
{
	bool r = ( IsModify != flg );

	IsModify = flg;

	// 更新フラグが変更されるときには、ウインドウタイトルの再設定を通知する
	if ( r )
		m_TaEditShell->ResetTitle();
}


// 排他的な編集に備えた、特殊な複製
// 1.新規にCTaEditDocを構築する。
// 2.構築したCTaEditDocに、thisと同じ行数の空行を追加する
// 3.作成したCTaEditDocとthisをswapする。
CTaEditDoc* CTaEditDoc::CopyForExclusiveEditing()
{
	assert( this );

	CTaEditDoc *wpDoc = new CTaEditDoc();
	if ( NULL == wpDoc ) return NULL;

	// 空行を追加する
	wpDoc->vecLine.resize( this->vecLine.size(), CLineData() );

	// swapする
	ReflectionExclusiveEditing( wpDoc );

	return wpDoc;
}

// 排他的な編集の結果を反映する
// 1.pDocとthisが保持するvecLine、IsModify、UndoListMgrをswapする。
// 2.色情報の更新を行う
void CTaEditDoc::ReflectionExclusiveEditing( CTaEditDoc *pDoc )
{
	bool wBool;
	
	assert( this && pDoc );

	pDoc->vecLine.swap( this->vecLine );

	wBool = pDoc->IsModify;
	pDoc->IsModify = this->IsModify;
	this->IsModify = wBool;

	pDoc->UndoListMgr.Swap( &(this->UndoListMgr) );

	// 構文強調表示処理を行う
	if ( pDoc->pTxtColorMgr )
		pDoc->pTxtColorMgr->InitiUpdate( pDoc->vecLine );
	if ( this->pTxtColorMgr )
		this->pTxtColorMgr->InitiUpdate( this->vecLine );
}

