/*-----------------------------------------------------------------------------
[名称]	CommandCaret.cpp
[概要]	キャレット移動コマンドクラスの実装
[備考]	なし
[履歴]	2003-02-26	NC		コメント付加(moralog)
		2003-12-01	b031130 (3363)PageDown後にカーソルキー押下で強制終了(dincom)
		2003-12-26  b031228 (3330)PageUp,Downの挙動(dincom)
-----------------------------------------------------------------------------*/

//-----------------------------------------------------------------------------
// インクルード
//-----------------------------------------------------------------------------
#include "stdafx.h"
#include "Shitarabian.h"
#include "CommandCaret.h"
#include "ShitarabianDoc.h"
#include "EditDrawer.h"
#include "CommandMultiEdit.h"
#include "ShitarabianView.h"
#include "Unicoder.h"
#include "ExeCommon.h"
#include "CommandInput.h"
#include "CommandManager.h"
#include "Common.h"
#include "GeneralSetting.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif


//-----------------------------------------------------------------------------
// CCommandCaret 実装メモ
//-----------------------------------------------------------------------------
/*
■ 移動タイプ m_nType について。
	・基本的に 仮想キー（VK_xxx）が入る。その場合は座標情報は無視。
	・0x00 のとき、座標情報[Caret] m_idxRow 、 m_idxColumn が有効。

■ 上下の行に移動するときのＸ座標[Pixel]割り出し方法について。
	・アンカーを関数内スタティックで保存している。（のちに問題になりそうな予感）
		→置き場所をviewとかグローバルなどこかにすべきかな。
	・アンカー座標に近い内側最大の位置を選択している。
*/


/*-----------------------------------------------------------------------------
[名称]	コンストラクタ
[機能]	インスタンスの生成。
[戻値]	なし
[備考]	なし
-----------------------------------------------------------------------------*/
CCommandCaret::CCommandCaret()
:	m_nType( 0 )
,	m_idxRow( 0 )
,	m_idxColumn( 0 )
{

}


/*-----------------------------------------------------------------------------
[名称]	デストラクタ
[機能]	インスタンスの破棄。
[戻値]	なし
[備考]	なし
-----------------------------------------------------------------------------*/
CCommandCaret::~CCommandCaret()
{

}


/*-----------------------------------------------------------------------------
[名称]	複製
[機能]	新しいインスタンスを生成して複製する。
[戻値]	ICommand*	複製されたインスタンスへのポインタ [-] (-)
[備考]	なし
-----------------------------------------------------------------------------*/
ICommand*	CCommandCaret::Duplicate()
{
	ICommand*	pcmdNew;

	pcmdNew = new CCommandCaret;
	pcmdNew->Copy( *this );

	return( pcmdNew );
}


/*-----------------------------------------------------------------------------
[名称]	属性コピー
[機能]	属性をコピーする。
[戻値]	なし
[備考]	なし
-----------------------------------------------------------------------------*/
void		CCommandCaret::Copy(
	ICommand&	cmdSrc			// I  :コピー元インスタンス [-] (-) :-
)
{
	CCommandCaret*	pCaret;
	pCaret = dynamic_cast<CCommandCaret*>(&cmdSrc);

	if ( pCaret != NULL )
	{
		m_nType = pCaret->m_nType;			// 移動タイプ [仮想キー] (0x00:直接移動)
		m_idxRow = pCaret->m_idxRow;		// 入力された位置 [行] (-)
		m_idxColumn = pCaret->m_idxColumn;	// 入力された位置 [列] (-)
	}
	else
	{
		// 異なる型のコピーをしようとした
		ASSERT( FALSE );
	}
}


/*-----------------------------------------------------------------------------
[名称]	実行
[機能]	キャレット移動コマンドを実行する。
[戻値]	BOOL	処理結果 [-] (TURE:成功、FALSE:失敗)
[備考]	SetAttribute()されていること。
-----------------------------------------------------------------------------*/
BOOL		CCommandCaret::Execute(
	CShitarabianDoc* pDoc		// I  :操作対象ドキュメント [-] (-) :-
)
{
	CEditDrawer*		pDrawer = CEditDrawer::GetInstance();
	static long	lAnchorX = 0;	// 上下移動したときのキャレット位置割り出し用座標 [Pixel] (-)
								// キャレットの移動、文字の入力などで更新され、
								// 上下移動のときは固定。
	if( pDoc == NULL )
	{
		return( FALSE );
	}

	BOOL		bAnchorUpdate = TRUE;

	long	idxRow = 0;
	long	idxColumn = 0;
	idxRow = pDoc->GetCaretRow();
	idxColumn = pDoc->GetCaretColumn();

	switch( m_nType )
	{
	case VK_LEFT:
		// 左へ
		// マルチカラムでスキップしない位置まで左へ戻る
		for( idxColumn-- ; idxColumn>=0 ; idxColumn-- )
		{
			if ( idxColumn == pDoc->MultiColumnSkip( idxRow, idxColumn ) )
			{
				break;
			}
		}
		if ( idxColumn < 0 )
		{
			// 上の行へ。
			if ( idxRow != 0 )
			{
				CString* pstrUpperLine = pDoc->GetLine( idxRow-1 );
				long	idxChar = ::ByteColumnToCaretColumn( *pstrUpperLine, pstrUpperLine->GetLength() );

				pDoc->SetCaretRow( idxRow-1 );
				pDoc->SetCaretColumn( idxChar );

				bAnchorUpdate = TRUE;
			}
			break;
		}

		pDoc->SetCaretColumn( idxColumn );
		break;

	case VK_RIGHT:
		// 右へ
		{
			CEmphasisLine* pEmphasisLine = pDoc->GetEmphasisLine( idxRow );
			if ( idxColumn < pEmphasisLine->GetColumnCount() )
			{
				// 行末でない場合、
				// マルチカラムをスキップしつつ１カラム右へ
				idxColumn = pDoc->MultiColumnSkip( idxRow, idxColumn+1 );
				pDoc->SetCaretColumn( idxColumn );
			}
			else
			{
				// 行末の場合、
				if ( CGeneralSetting::GetInstance()->GetFreeCursor() )
				{
				// フリーカーソルモードのときカラムを追加
					CCommandInput		cmdInput;
					CCommandManager*	pCmd = CCommandManager::GetInstance();

					CString		strPad = g_cstrBigBlank;
					// カラムを追加
					cmdInput.SetAttribute( strPad, idxRow, idxColumn );
					cmdInput.Execute( pDoc );
					pCmd->Add( cmdInput );
				}
				else
				{
				// フリーカーソルでなければ下の行へ
					if ( idxRow < pDoc->GetLineCount()-1 )
					{
						pDoc->SetCaretRow( idxRow+1 );
						pDoc->SetCaretColumn( 0 );

						bAnchorUpdate = TRUE;
					}
				}
			}
		}
		break;

	case VK_UP:
		// 上へ
		{
			long	lOldAnchorX = lAnchorX;
			if ( idxRow == 0 )
			if ( CGeneralSetting::GetInstance()->GetFreeCursor() )
			{
			// フリーカーソルモードのとき行を追加
				CString		strAdd = pDoc->ReturnChar();

				// 行を追加
				CCommandInput		cmdInput;
				CCommandManager*	pCmd = CCommandManager::GetInstance();
				cmdInput.SetAttribute( strAdd, 0, 0 );
				cmdInput.Execute( pDoc );
				pCmd->Add( cmdInput );

				lAnchorX = lOldAnchorX;
				idxRow++;
			}

			// 上の行へ。
			if ( idxRow > 0 )
			{
				CString*	pstrUpperLine = pDoc->GetLine( idxRow-1 );
				if ( pstrUpperLine != NULL )
				{
					idxColumn = 0;
					CEmphasisLine*	pEmphasisLine = pDoc->GetEmphasisLine( idxRow-1 );
					if ( pEmphasisLine != NULL )
					{
						if ( CGeneralSetting::GetInstance()->GetFreeCursor() )
						{
						// フリーカーソルモードのときカラムを追加
							pDoc->AppendColumn( idxRow-1, (lAnchorX)/pDrawer->GetZoom() );
						}

						idxColumn = pEmphasisLine->PixelToCaretColumnZoomed(
							lAnchorX
						);
					}
				}
				pDoc->SetCaretRow( idxRow-1 );
				pDoc->SetCaretColumn( idxColumn );

				bAnchorUpdate = FALSE;
			}
		}
		break;

	case VK_DOWN:
		// 下へ
		{
			long	lOldAnchorX = lAnchorX;
			if ( CGeneralSetting::GetInstance()->GetFreeCursor() )
			{
			// フリーカーソルモードのとき行を追加
				pDoc->AppendRow( idxRow+1 );
			}

			// 下の行へ。
			if ( idxRow < pDoc->GetLineCount()-1 )
			{
				CString*	pstrLowerLine = pDoc->GetLine( idxRow+1 );
				if ( pstrLowerLine != NULL )
				{
					CEmphasisLine*	pEmphasisLine = pDoc->GetEmphasisLine( idxRow+1 );
					idxColumn = 0;
					if ( pEmphasisLine != NULL )
					{
						if ( CGeneralSetting::GetInstance()->GetFreeCursor() )
						{
						// フリーカーソルモードのときカラムを追加
							pDoc->AppendColumn( idxRow+1, (lOldAnchorX)/pDrawer->GetZoom() );
						}

						idxColumn = pEmphasisLine->PixelToCaretColumnZoomed(
							lOldAnchorX
						);
					}
				}
				pDoc->SetCaretRow( idxRow+1 );
				pDoc->SetCaretColumn( idxColumn );

				bAnchorUpdate = FALSE;
			}
		}
		break;

	case VK_HOME:
		// 行の先頭へ
		pDoc->SetCaretColumn( 0 );
		bAnchorUpdate = TRUE;
		break;

	case VK_END:
		// 行末へ
		{
			CString* pstrCurrentLine = pDoc->GetLine( idxRow );
			long	idxChar = ::ByteColumnToCaretColumn( *pstrCurrentLine, pstrCurrentLine->GetLength() );
			pDoc->SetCaretColumn( idxChar );
			bAnchorUpdate = TRUE;
		}
		break;

	case VK_PRIOR:
		{
			// スクロール必要判定
			BOOL	bScroll;
			int		iVisibleStartRow = 0;

			// ▼ (3330) BGN 2003.12.26 (dincom) 
			// 控えめスクロール機能がONならば一度画面端に飛ばす
			if ( CGeneralSetting::GetInstance()->GetRestrainedScroll() )
			{
				// 控えめスクロール機能がONなので画面端でないときはスクロールしない
				iVisibleStartRow += pDoc->GetMainView()->VisibleRowStart();
				bScroll = ( pDoc->GetCaretRow() <= iVisibleStartRow );
			}
			else
			{
				// 控えめスクロール機能がOFFなので常にスクロールする
				bScroll = TRUE;
			}
				// ▽ Original BGN
				//iVisibleStartRow += pDoc->GetMainView()-	>VisibleRowStart();
				//bScroll = ( pDoc->GetCaretRow() <= iVisibleStartRow );
				// △ Original END
			// ▲ (3330) END

			if ( bScroll )
			{
			// キャレットが画面内下端のとき、ページスクロールする
				CCommandMultiEdit	cmdEdit;

				// ▼ (3330) BGN 2003.12.26 (dincom) 
				int	nSkipRow = pDoc->GetMainView()->VisibleRowCount() + pDoc->GetCaretRow() - pDoc->GetMainView()->VisibleRowStart();
				for( int idxLine=0 ; idxLine<nSkipRow ; idxLine++ )
				// ▽ Original BGN
				//for( int idxLine=0 ; idxLine<pDoc->GetMainView()->VisibleRowCount() ; idxLine++ )
				// △ Original END
				// ▲ (3330) END
				{
					CCommandCaret	cmdCaret;
					cmdCaret.SetAttribute( VK_UP, idxRow, idxColumn );
					cmdEdit.Add( cmdCaret );
				}
				cmdEdit.Execute( pDoc );
			}
			else
			{
			// 画面上端にキャレットを移動する
				pDoc->SetCaretRow( iVisibleStartRow );
			}
		}
		break;

	case VK_NEXT:
		{
			// スクロール必要判定
			BOOL	bScroll;
			int		iVisibleLastRow = 0;

			// ▼ (3330) BGN 2003.12.26 (dincom) 
			// 控えめスクロール機能がONならば一度画面端に飛ばす
			if ( CGeneralSetting::GetInstance()->GetRestrainedScroll() )
			{
				// 控えめスクロール機能がONなので画面端でないときはスクロールしない
				iVisibleLastRow += pDoc->GetMainView()->VisibleRowStart();
				iVisibleLastRow += pDoc->GetMainView()->VisibleRowCount()-1;
				bScroll = ( pDoc->GetCaretRow() >= iVisibleLastRow );
			}
			else
			{
				// 控えめスクロール機能がOFFなので常にスクロールする
				bScroll = TRUE;
			}
				// ▽ Original BGN
				//iVisibleLastRow += pDoc->GetMainView()->VisibleRowStart();
				//iVisibleLastRow += pDoc->GetMainView()->VisibleRowCount()-1;
				//bScroll = ( pDoc->GetCaretRow() >= iVisibleLastRow );
				// △ Original END
			// ▲ (3330) END

			if ( bScroll )
			{
			// スクロールする
				CCommandMultiEdit	cmdEdit;

				// ▼ (3330) BGN 2003.12.26 (dincom) 
				int	nSkipRow = pDoc->GetMainView()->VisibleRowCount()*2 - ( pDoc->GetCaretRow() - pDoc->GetMainView()->VisibleRowStart() );
				for( int idxLine=0 ; idxLine<nSkipRow ; idxLine++ )
				// ▽ Original BGN
				//for( int idxLine=0 ; idxLine<pDoc->GetMainView()->VisibleRowCount() ; idxLine++ )
				// △ Original END
				// ▲ (3330) END
				{
					CCommandCaret	cmdCaret;
					cmdCaret.SetAttribute( VK_DOWN, idxRow, idxColumn );
					cmdEdit.Add( cmdCaret );
				}
				cmdEdit.Execute( pDoc );
			}
			else
			{
			// 画面下端にキャレットを移動する
			// ▽ Original BGN
			//	pDoc->SetCaretRow( iVisibleLastRow );
			// △ Original END

				// ▼ (3363) BGN 2003.12.01 (dincom) 
				iVisibleLastRow = min( iVisibleLastRow, pDoc->GetLineCount()-1 );
				pDoc->SetCaretRow( iVisibleLastRow );
				// ▲ (3363) END
			}
		}
		break;

	default:
		// 指定座標[Caret]へ
		pDoc->SetCaretRow( m_idxRow );
		pDoc->SetCaretColumn( m_idxColumn );
		break;
	}

	if( pDoc->GetCaretRow() != 0 )
	{
	// マルチカラム文字の内部にキャレットのある可能性があるため
	// キャレット位置を調節する
		pDoc->SetCaretColumn( pDoc->MultiColumnSkip(
			pDoc->GetCaretRow(),
			pDoc->GetCaretColumn()
			)
		);
	}

	// アンカー（上下移動の位置補正用座標）の設定
	if ( bAnchorUpdate )
	{
		lAnchorX = 0;
		CEmphasisLine*	pEmphasisLine = pDoc->GetEmphasisLine( pDoc->GetCaretRow() );
		if ( pEmphasisLine != NULL )
		{
			lAnchorX = pEmphasisLine->CaretColumnToPixelZoomed(
				pDoc->GetCaretColumn()
			);
		}
	}

	if ( idxRow == pDoc->GetCaretRow() )
	if ( idxColumn == pDoc->GetCaretColumn() )
	{
		return( FALSE );
	}
	return( TRUE );
}


/*-----------------------------------------------------------------------------
[名称]	取り消し
[機能]	キャレット移動コマンドを取り消す。
[戻値]	BOOL	処理結果 [-] (TURE:成功、FALSE:失敗)
[備考]	取り消す必要を感じないので、処理なしとする。
-----------------------------------------------------------------------------*/
BOOL		CCommandCaret::UnExecute(
	CShitarabianDoc* pDoc		// I  :操作対象ドキュメント [-] (-) :-
)
{
	// warning C4100:警告の無視
	pDoc;

	return( TRUE );
}


/*-----------------------------------------------------------------------------
[名称]	属性設定
[機能]	インスタンスの属性を設定する。
[戻値]	なし
[備考]	Execute()前に必ず行っておくこと。
-----------------------------------------------------------------------------*/
void		CCommandCaret::SetAttribute(
	char		nType,			// I  :移動タイプ [仮想キー] (0x00:直接移動) :-
	long		idxRow,			// I  :入力された位置 [行] (-) :-
	long		idxColumn		// I  :入力された位置 [列] (-) :-
)
{
	m_nType = nType;			// 移動タイプ [仮想キー] (0x00:直接移動)
	m_idxRow = idxRow;			// 入力された位置 [行] (-)
	m_idxColumn = idxColumn;	// 入力された位置 [列] (-)
}


