git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@3345 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
		
			
				
	
	
		
			4234 lines
		
	
	
		
			77 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			4234 lines
		
	
	
		
			77 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /////////////////////////////////////////////////////////////////////////////
 | |
| // Name:        No names yet.
 | |
| // Purpose:     Contrib. demo
 | |
| // Author:      Aleksandras Gluchovas
 | |
| // Modified by:
 | |
| // Created:     03/04/1999
 | |
| // RCS-ID:      $Id$
 | |
| // Copyright:   (c) Aleksandars Gluchovas
 | |
| // Licence:   	GNU General Public License 
 | |
| /////////////////////////////////////////////////////////////////////////////
 | |
| //
 | |
| //  This program is free software; you can redistribute it and/or modify
 | |
| //  it under the terms of the GNU General Public License as published by
 | |
| //  the Free Software Foundation; either version 2 of the License, or
 | |
| //  (at your option) any later version.
 | |
| //
 | |
| //  This program is distributed in the hope that it will be useful,
 | |
| //  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| //  GNU General Public License for more details.
 | |
| //
 | |
| //  You should have received a copy of the GNU General Public License
 | |
| //  along with this program; if not, write to the Free Software
 | |
| //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | |
| /////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
| 
 | |
| // For compilers that support precompilation, includes "wx/wx.h".
 | |
| #include "wx/wxprec.h"
 | |
| 
 | |
| #ifdef __BORLANDC__
 | |
| #pragma hdrstop
 | |
| #endif
 | |
| 
 | |
| #ifndef WX_PRECOMP
 | |
| #include "wx/wx.h"
 | |
| #endif
 | |
| 
 | |
| #include "wx/file.h"
 | |
| #include "wx/textdlg.h"
 | |
| #include "wx/clipbrd.h"
 | |
| #include "wx/dataobj.h"
 | |
| 
 | |
| #include <stdio.h>
 | |
| 
 | |
| #include "tdefs.h"
 | |
| #include "finddlg.h"
 | |
| 
 | |
| #include <memory.h>
 | |
| 
 | |
| /***** Implementation for class TBlock *****/
 | |
| 
 | |
| void TBlock::RecalcBlockProperties()
 | |
| {
 | |
| 	char* cur = mBuf;
 | |
| 	char* end = mBuf + mTextLen;
 | |
| 	mRowCount = 0;
 | |
| 
 | |
| 	while( cur < end )
 | |
| 	{
 | |
| 		if ( is_eol_char( *cur ) ) ++mRowCount;
 | |
| 
 | |
| 		++cur;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /***** Implementation for class TTextIterator *****/
 | |
| 
 | |
| string TTextIterator::mSeparators = ",.()[]\t\\+-*/|=<>:;\t\n~?!%";
 | |
| 
 | |
| bool TTextIterator::IsSeparator( char ch )
 | |
| {
 | |
| 	size_t sz = mSeparators.size();
 | |
| 
 | |
| 	for( size_t i = 0; i != sz; ++i )
 | |
| 
 | |
| 		if ( mSeparators[i] == ch ) return TRUE;
 | |
| 
 | |
| 	return FALSE;
 | |
| }
 | |
| 
 | |
| char* TTextIterator::GetClosestPos()
 | |
| {
 | |
| 	char*  end = GetBlockEnd();
 | |
| 	char*  cur = mpCurRowStart;
 | |
| 	size_t col = 0;
 | |
| 
 | |
| 	while( cur < end && col < mPos.mCol && !is_eol_char(*cur) )
 | |
| 	{
 | |
| 		if ( !is_DOS_eol_char( *cur ) ) ++col;
 | |
| 		++cur;
 | |
| 	}
 | |
| 
 | |
| 	if ( is_DOS_eol_char( *cur ) ) ++cur;
 | |
| 
 | |
| 	return cur;
 | |
| }
 | |
| 
 | |
| char* TTextIterator::GotoClosestPos()
 | |
| {
 | |
| 	char*  end = GetBlockEnd();
 | |
| 	char*  cur = mpCurRowStart;
 | |
| 	size_t col = 0;
 | |
| 
 | |
| 	while( cur < end && col < mPos.mCol && !is_eol_char(*cur) )
 | |
| 	{
 | |
| 		if ( !is_DOS_eol_char( *cur ) ) ++col;
 | |
| 		++cur;
 | |
| 	}
 | |
| 
 | |
| 	mPos.mCol = col;
 | |
| 
 | |
| 	if ( is_DOS_eol_char( *cur ) ) ++cur;
 | |
| 
 | |
| 	return cur;
 | |
| }
 | |
| 
 | |
| TTextIterator::TTextIterator()
 | |
| 
 | |
| 	: mIsEof( FALSE )
 | |
| {}
 | |
| 
 | |
| bool TTextIterator::IsLastLine()
 | |
| {
 | |
| 	TBlockIteratorT nextBlk = mBlockIter;
 | |
| 	++nextBlk;
 | |
| 
 | |
| 	if ( nextBlk != mEndOfListIter ) return FALSE;
 | |
| 
 | |
| 	char* cur = mpCurRowStart;
 | |
| 	char* end = GetBlockEnd();
 | |
| 
 | |
| 	while( cur < end && !is_eol_char( *cur ) ) ++cur;
 | |
| 
 | |
| 	if ( cur == end ) return TRUE;
 | |
| 
 | |
| 	++cur;
 | |
| 
 | |
| 	return ( cur == end );
 | |
| }
 | |
| 
 | |
| char TTextIterator::GetChar()
 | |
| {
 | |
| 	char* cur = GetClosestPos();
 | |
| 
 | |
| 	if ( is_DOS_eol_char( *cur ) ) 
 | |
| 
 | |
| 		return *(cur+1);
 | |
| 	else
 | |
| 		return *cur;
 | |
| }
 | |
| 
 | |
| bool TTextIterator::IsEol()
 | |
| {
 | |
| 	return is_eol_char( GetChar() ) || mIsEof;
 | |
| }
 | |
| 
 | |
| bool TTextIterator::IsEof()
 | |
| {
 | |
| 	return mIsEof;
 | |
| }
 | |
| 
 | |
| int TTextIterator::GetDistFromEol()
 | |
| {
 | |
| 	return 0; // TBD::
 | |
| }
 | |
| 
 | |
| void TTextIterator::NextChar()
 | |
| {
 | |
| 	char* cur = GotoClosestPos();
 | |
| 
 | |
| 	if ( cur + 1 >= GetBlockEnd() )
 | |
| 	{
 | |
| 		TBlockIteratorT nextBlk = mBlockIter;
 | |
| 		++nextBlk;
 | |
| 
 | |
| 		if ( nextBlk == mEndOfListIter )
 | |
| 		{
 | |
| 			if ( cur != GetBlockEnd() ) 
 | |
| 				++mPos.mCol;	
 | |
| 
 | |
| 			mIsEof = TRUE;
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		++mPos.mRow ;
 | |
| 		mPos.mCol = 0;
 | |
| 
 | |
| 		mBlockIter = nextBlk;
 | |
| 
 | |
| 		mFirstRowInBlock = mPos.mRow;
 | |
| 		mActualRow       = mPos.mRow;
 | |
| 		mpCurRowStart    = (*mBlockIter).mBuf;
 | |
| 
 | |
| 		mIsEof = ( (*mBlockIter).mTextLen == 0 );
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		if ( is_eol_char( *cur ) ) 
 | |
| 		{
 | |
| 			++mPos.mRow;
 | |
| 			mPos.mCol = 0;
 | |
| 			
 | |
| 			mActualRow    = mPos.mRow;
 | |
| 			mpCurRowStart = cur + 1;
 | |
| 		}
 | |
| 		else
 | |
| 			++mPos.mCol;
 | |
| 	}
 | |
| 
 | |
| 	mIsEof = (mpCurRowStart + mPos.mCol) == GetBlockEnd();
 | |
| }
 | |
| 
 | |
| void TTextIterator::PreviousChar()
 | |
| {
 | |
| 	char* cur = GotoClosestPos();
 | |
| 
 | |
| 	if ( cur == (*mBlockIter).mBuf )
 | |
| 	{
 | |
| 		TBlockIteratorT prevBlk = mBlockIter;
 | |
| 		--prevBlk;
 | |
| 
 | |
| 		if ( prevBlk == mEndOfListIter )
 | |
| 		{
 | |
| 			mIsEof = TRUE;
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		--mPos.mRow;
 | |
| 
 | |
| 		mBlockIter = prevBlk;
 | |
| 
 | |
| 		cur = GetBlockEnd() - 1;
 | |
| 
 | |
| 		char* eolPos = cur;
 | |
| 		--cur; // skip EOL
 | |
| 		char* start = (*mBlockIter).mBuf;
 | |
| 
 | |
| 		while( cur != start && !is_eol_char( *cur ) ) --cur; // goto start of line
 | |
| 
 | |
| 		if ( is_eol_char( *cur ) ) ++cur;
 | |
| 
 | |
| 		mPos.mCol     = (size_t)(eolPos - cur);
 | |
| 		mpCurRowStart = cur;
 | |
| 
 | |
| 		mFirstRowInBlock = mPos.mRow;
 | |
| 		mActualRow       = mPos.mRow;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		do
 | |
| 		{
 | |
| 			// FIXME FIXME:: this is more then messy .... !
 | |
| 
 | |
| 			if ( is_eol_char( *(cur-1) ) ) 
 | |
| 			{
 | |
| 				--cur; // goto EOL
 | |
| 
 | |
| 				--mPos.mRow;
 | |
| 
 | |
| 				char* eolPos = cur;
 | |
| 				--cur; // skip EOL
 | |
| 				char* start = (*mBlockIter).mBuf;
 | |
| 
 | |
| 				while( cur != start && !is_eol_char( *cur ) ) --cur; // goto start of line
 | |
| 
 | |
| 				if ( is_eol_char( *cur ) ) ++cur;
 | |
| 
 | |
| 				mPos.mCol     = (size_t)(eolPos - cur);
 | |
| 				mpCurRowStart = cur;
 | |
| 
 | |
| 				if ( eolPos != cur && is_DOS_eol_char( *(eolPos-1) ) ) --mPos.mCol;
 | |
| 				
 | |
| 				mActualRow    = mPos.mRow;
 | |
| 
 | |
| 				break;
 | |
| 			}
 | |
| 			else
 | |
| 			if ( is_DOS_eol_char( *(cur-1) ) )
 | |
| 			{
 | |
| 				--cur;
 | |
| 
 | |
| 				if ( cur != (*mBlockIter).mBuf && is_eol_char( *(cur-1) ) )
 | |
| 
 | |
| 					continue;
 | |
| 				else
 | |
| 				{
 | |
| 					--mPos.mCol;
 | |
| 					--cur;
 | |
| 				}
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				--mPos.mCol;
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 		} while( 1 );
 | |
| 	}
 | |
| 
 | |
| 	mIsEof = (mpCurRowStart + mPos.mCol) == GetBlockEnd();
 | |
| }
 | |
| 
 | |
| void TTextIterator::NextWord()
 | |
| {
 | |
| 	GotoClosestPos();
 | |
| 
 | |
| 	// skip non-white space ahead
 | |
| 
 | |
| 	bool wasSeparator = IsSeparator( GetChar() );
 | |
| 
 | |
| 	while( !IsEof() )
 | |
| 	{
 | |
| 		char ch = GetChar();
 | |
| 
 | |
| 		if ( ch == ' '  || 
 | |
| 			 ch == '\t' || 
 | |
| 			 is_eol_char(ch) ||
 | |
| 			 wasSeparator != IsSeparator(ch) )
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		NextChar();
 | |
| 	}
 | |
| 
 | |
| 	// skip all white stpace if any
 | |
| 	while( !IsEof() )
 | |
| 	{
 | |
| 		char ch = GetChar();
 | |
| 
 | |
| 		if ( ch != ' ' && ch != '\t' && !is_eol_char(ch) )
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		NextChar();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void TTextIterator::PreviousWord()
 | |
| {
 | |
| 	GotoClosestPos();
 | |
| 
 | |
| 	PreviousChar();
 | |
| 
 | |
| 	// skip all white stpace if any
 | |
| 	while( !IsEof() )
 | |
| 	{
 | |
| 		char ch = GetChar();
 | |
| 
 | |
| 		if ( ch != ' ' && ch != '\t' && !is_eol_char(ch) )
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		PreviousChar();
 | |
| 	}
 | |
| 
 | |
| 	bool wasSeparator = IsSeparator( GetChar() );
 | |
| 
 | |
| 	// skip word;
 | |
| 	while( !IsEof() )
 | |
| 	{
 | |
| 		char ch = GetChar();
 | |
| 
 | |
| 		if ( ch == ' '       || 
 | |
| 			 ch == '\t'      || 
 | |
| 			 is_eol_char(ch) ||
 | |
| 			 wasSeparator != IsSeparator(ch) 
 | |
| 		   )
 | |
| 		{
 | |
| 			NextChar();
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		PreviousChar();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void TTextIterator::ToEndOfLine()
 | |
| {
 | |
| 	GotoClosestPos();
 | |
| 
 | |
| 	while( !IsEof() )
 | |
| 	{
 | |
| 		char ch = GetChar();
 | |
| 
 | |
| 		if ( is_eol_char( ch ) ) break;
 | |
| 
 | |
| 		NextChar();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void TTextIterator::ToStartOfLine()
 | |
| {
 | |
| 	GotoClosestPos();
 | |
| 
 | |
| 	mPos.mCol = 0;
 | |
| 	mPos.mRow = mActualRow;
 | |
| }
 | |
| 
 | |
| size_t TTextIterator::GetLineLen()
 | |
| {
 | |
| 	char* cur = mpCurRowStart;
 | |
| 	char* end = GetBlockEnd();
 | |
| 
 | |
| 	size_t len = 0;
 | |
| 
 | |
| 	while( cur < end && !is_eol_char( *cur ) )
 | |
| 	{
 | |
| 		if ( !is_DOS_eol_char( *cur ) ) ++len;
 | |
| 		++cur;
 | |
| 	}
 | |
| 
 | |
| 	return len;
 | |
| }
 | |
| 
 | |
| TPosition TTextIterator::GetPosition()
 | |
| {
 | |
| 	return mPos;
 | |
| }
 | |
| 
 | |
| bool TTextIterator::IsInLastBlock()
 | |
| {
 | |
| 	TBlockIteratorT next = mBlockIter;
 | |
| 	++next;
 | |
| 
 | |
| 	return next == mEndOfListIter;
 | |
| }
 | |
| 
 | |
| bool TTextIterator::DetectUnixText()
 | |
| {
 | |
| 	char* cur = GetBlockStart();
 | |
| 	char* end = GetBlockEnd();
 | |
| 
 | |
| 	bool isUnixText = IS_UNIX_TEXT_BY_DEFAULT;
 | |
| 
 | |
| 	while( cur < end ) 
 | |
| 	{
 | |
| 		if ( is_DOS_eol_char( *cur ) ) return FALSE;
 | |
| 
 | |
| 		if ( is_eol_char( *cur ) ) return TRUE;
 | |
| 
 | |
| 		++cur;
 | |
| 	}
 | |
| 
 | |
| 	return isUnixText;
 | |
| }
 | |
| 
 | |
| /***** Implementation for class TCppJavaHighlightListener *****/
 | |
| 
 | |
| void TCppJavaHighlightListener::OnTextChanged( wxTextEditorModel* pModel, 
 | |
| 											   size_t atRow, size_t nRows, 
 | |
| 											   TEXT_CHANGE_TYPE ct ) 
 | |
| {
 | |
| 	mpModel = pModel;
 | |
| 	
 | |
| 	/*
 | |
| 
 | |
| 	int state = GetStateAtRow( atRow );
 | |
| 
 | |
| 	if ( ct == CT_INSERTED )
 | |
| 	{
 | |
| 		RemoveCommentTags( atRow, atRow + nRows + 1 );
 | |
| 		GenerateTagsForRange( atRows, atRows + nRows + 1 );
 | |
| 	}
 | |
| 	else
 | |
| 	if ( ct == CT_DELETED )
 | |
| 	{
 | |
| 		RemoveCommentTags( atRow, atRow + 1 );
 | |
| 		GenerateTagsForRange( atRows, atRows + 1 );
 | |
| 	}
 | |
| 	*/
 | |
| }
 | |
| 
 | |
| /***** Implementation for class wxTextEditorModel *****/
 | |
| 
 | |
| /*** protected methods ***/
 | |
| 
 | |
| size_t wxTextEditorModel::GetLineCountInRange( char* from, char* till )
 | |
| {
 | |
| 	size_t nLines = 0;
 | |
| 
 | |
| 	while( from != till )
 | |
| 	{
 | |
| 		if ( is_eol_char( *from ) ) ++nLines;
 | |
| 
 | |
| 		++from;
 | |
| 	}
 | |
| 
 | |
| 	return nLines;
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::DoInsertText( const TPosition& pos, 
 | |
| 									  char* text, size_t len,
 | |
| 									  TRange& actualRange )
 | |
| {
 | |
| 	// FOR NOW:: very dummy imp.
 | |
| 
 | |
| 	char* end = text + len;
 | |
| 
 | |
| 	TTextIterator iter = CreateIterator( pos );
 | |
| 
 | |
| 	TBlock& blk = (*iter.mBlockIter);
 | |
| 
 | |
| 	char* cur = text;
 | |
| 
 | |
| 	char*  insertPos = iter.GotoClosestPos();
 | |
| 	actualRange.mFrom = iter.GetPosition();
 | |
| 
 | |
| 	if ( is_eol_char( *insertPos ) &&
 | |
| 		 insertPos != iter.GetBlockStart()   &&
 | |
| 		 is_DOS_eol_char( *(insertPos-1) )
 | |
| 	   )
 | |
| 	   --insertPos;
 | |
| 
 | |
| 	size_t sizeAfter = (size_t)(iter.GetBlockEnd() - insertPos);
 | |
| 
 | |
| 	size_t nLines = GetLineCountInRange( text, text + len );
 | |
| 
 | |
| 	if ( blk.mTextLen + len < FILLED_BLOCK_LEN )
 | |
| 	{
 | |
| 		memmove( insertPos + len, insertPos, sizeAfter );
 | |
| 
 | |
| 		memcpy( insertPos, text, len );
 | |
| 
 | |
| 		blk.mTextLen += len;
 | |
| 		
 | |
| 
 | |
| 		blk.RecalcBlockProperties();
 | |
| 
 | |
| 		if ( iter.IsInLastBlock() )
 | |
| 
 | |
| 			++blk.mRowCount; // last block have always the-last-row-to-spare -
 | |
| 		                     // the "nature" of most text editors
 | |
| 
 | |
| 		char* endPos = insertPos + len;
 | |
| 
 | |
| 		bool found = FALSE;
 | |
| 
 | |
| 		/*
 | |
| 		// OLD STUFF:: slow & buggy
 | |
| 
 | |
| 		while( !iter.IsEof() )
 | |
| 		{
 | |
| 			if ( iter.GetClosestPos() == endPos )
 | |
| 			{
 | |
| 				actualRange.mTill = iter.GetPosition();
 | |
| 				found = TRUE;
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			iter.NextChar();
 | |
| 		}
 | |
| 
 | |
| 		if ( !found )
 | |
| 		{
 | |
| 			actualRange.mTill = iter.GetPosition();
 | |
| 			++actualRange.mTill.mCol;
 | |
| 			
 | |
| 			//T_ASSERT( found ); // DBG::
 | |
| 		}
 | |
| 		*/
 | |
| 
 | |
| 		actualRange.mTill       = actualRange.mFrom;
 | |
| 		actualRange.mTill.mRow += nLines;
 | |
| 
 | |
| 		if ( nLines == 0 )
 | |
| 
 | |
| 			actualRange.mTill.mCol = actualRange.mFrom.mCol + (len);
 | |
| 		else
 | |
| 		{
 | |
| 			cur = end;
 | |
| 
 | |
| 			while( cur != insertPos && !is_eol_char( *cur ) )
 | |
| 
 | |
| 				--cur;
 | |
| 
 | |
| 			if ( is_eol_char( *cur ) ) ++cur;
 | |
| 
 | |
| 			actualRange.mTill.mCol = (int)(end - cur);
 | |
| 		}
 | |
| 
 | |
| 		NotifyTextChanged( pos.mRow, nLines, CT_INSERTED );
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		// TBD:::
 | |
| 
 | |
| 		char buf[16];
 | |
| 		sprintf( buf, "%d", FILLED_BLOCK_LEN );
 | |
| 		string msg = "Sorry!!! Currently editor is limited to files less then ";
 | |
| 		msg += buf;
 | |
| 		msg += " bytes\n(the requested text length is " + 
 | |
| 		sprintf( buf, "%d", blk.mTextLen + len );
 | |
| 		msg += buf; 
 | |
| 		msg += " bytes)\n Please, close this file without making any changes.";
 | |
| 
 | |
| 		wxMessageBox( msg );
 | |
| 
 | |
| 		GetActiveView()->SetFocus();
 | |
| 
 | |
| 		//T_ASSERT(0); // DBG:: for now
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::DoDeleteRange( const TPosition& from, const TPosition& till,
 | |
| 									   TRange& actualRange
 | |
| 									 )
 | |
| {
 | |
| 	// FOR NOW:: very dummy imp.
 | |
| 
 | |
| 	TTextIterator iterFrom = CreateIterator( from );
 | |
| 	TTextIterator iterTill = CreateIterator( till );
 | |
| 
 | |
| 	if ( iterFrom.mBlockIter == iterTill.mBlockIter )
 | |
| 	{
 | |
| 		char* fromPos = iterFrom.GotoClosestPos();
 | |
| 		char* tillPos = iterTill.GotoClosestPos();
 | |
| 		char* blockStart = (*iterFrom.mBlockIter).mBuf;
 | |
| 		
 | |
| 		if ( is_eol_char( *fromPos ) &&
 | |
| 			 fromPos != blockStart   &&
 | |
| 			 is_DOS_eol_char( *(fromPos-1) )
 | |
| 		   )
 | |
| 		   --fromPos;
 | |
| 
 | |
| 		if ( is_eol_char( *tillPos ) &&
 | |
| 			 tillPos != blockStart   &&
 | |
| 			 is_DOS_eol_char( *(tillPos-1) )
 | |
| 		   )
 | |
| 		   --tillPos;
 | |
| 
 | |
| 		size_t len = (size_t)( tillPos -fromPos );
 | |
| 		
 | |
| 		size_t nLines = GetLineCountInRange( fromPos, fromPos + len );
 | |
| 
 | |
| 		size_t sizeAfter = (size_t)(iterFrom.GetBlockEnd() - tillPos);
 | |
| 
 | |
| 		memmove( fromPos, tillPos, sizeAfter );
 | |
| 
 | |
| 		(*iterFrom.mBlockIter).mTextLen -= len;
 | |
| 
 | |
| 		(*iterFrom.mBlockIter).RecalcBlockProperties();
 | |
| 
 | |
| 		if ( iterFrom.IsInLastBlock() )
 | |
| 
 | |
| 			++(*iterFrom.mBlockIter).mRowCount; // last block have always the-last-row-to-spare -
 | |
| 												// the "nature" of most text editors
 | |
| 
 | |
| 		actualRange.mFrom = iterFrom.GetPosition();
 | |
| 		actualRange.mTill = iterTill.GetPosition();
 | |
| 
 | |
| 		NotifyTextChanged( from.mRow, nLines, CT_DELETED );
 | |
| 	}
 | |
| 	else
 | |
| 		T_ASSERT(0); // DBG:: for now
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::GetTextFromRange( const TPosition& from, const TPosition& till, 
 | |
| 										  char** text, size_t& textLen 
 | |
| 										)
 | |
| {
 | |
| 	TTextIterator iterFrom = CreateIterator( from );
 | |
| 	TTextIterator iterTill = CreateIterator( till );
 | |
| 
 | |
| 	if ( iterFrom.mBlockIter == iterTill.mBlockIter )
 | |
| 	{
 | |
| 		char* blockStart = (*iterFrom.mBlockIter).mBuf;
 | |
| 
 | |
| 		char* fromPos = iterFrom.GetClosestPos();
 | |
| 		char* tillPos = iterTill.GetClosestPos();
 | |
| 
 | |
| 		if ( is_eol_char( *fromPos ) &&
 | |
| 			 fromPos != blockStart   &&
 | |
| 			 is_DOS_eol_char( *(fromPos-1) )
 | |
| 		   )
 | |
| 		   --fromPos;
 | |
| 
 | |
| 		if ( is_eol_char( *tillPos ) &&
 | |
| 			 tillPos != blockStart   &&
 | |
| 			 is_DOS_eol_char( *(tillPos-1) )
 | |
| 		   )
 | |
| 		   --tillPos;
 | |
| 
 | |
| 		textLen = (size_t)( tillPos -fromPos );
 | |
| 
 | |
| 		*text = AllocCharacters( textLen );
 | |
| 
 | |
| 		memcpy( *text, fromPos, textLen );
 | |
| 	}
 | |
| 	else
 | |
| 		T_ASSERT(0); // DBG:: for now
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::LoadTextFromFile( const wxString& fname )
 | |
| {
 | |
| 	T_ASSERT( wxFile::Exists( fname ) );
 | |
| 
 | |
| 	DeleteAllText();
 | |
| 
 | |
| 	wxFile fl( fname );
 | |
| 
 | |
| 	char* buf = AllocCharacters( fl.Length() );
 | |
| 
 | |
| 	fl.Read( buf, fl.Length() );
 | |
| 
 | |
| 	TRange result;
 | |
| 	DoInsertText( TPosition( 0,0 ), buf, fl.Length(), result );
 | |
| 
 | |
| 	FreeCharacters( buf );
 | |
| 
 | |
| 	TTextIterator iter = CreateIterator( TPosition( 0,0 ) );
 | |
| 
 | |
| 	mIsUnixText = iter.DetectUnixText();
 | |
| 
 | |
| 	ClearUndoBuffer();
 | |
| 
 | |
| 	NotifyAllViews();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::SaveTextToFile( const wxString& fname )
 | |
| {
 | |
| 	wxFile fl( fname, wxFile::write );
 | |
| 
 | |
| 	char* text = 0;
 | |
| 	size_t len = 0;
 | |
| 
 | |
| 	GetTextFromRange( TPosition(0,0), TPosition( GetTotalRowCount()+1,0 ), &text, len );
 | |
| 
 | |
| 	fl.Write( text, len );
 | |
| 	fl.Close();
 | |
| 
 | |
| 	FreeCharacters( text );
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::NotifyTextChanged( size_t atRow, size_t nRows, TEXT_CHANGE_TYPE ct )
 | |
| {
 | |
| 	if ( nRows > 0  )
 | |
| 	
 | |
| 		MergeChange( atRow, mRowsPerPage );
 | |
| 	else
 | |
| 		MergeChange( atRow, 1 );
 | |
| 
 | |
| 	// reposition bookmarsk
 | |
| 
 | |
| 	if ( nRows > 0 )
 | |
| 	{
 | |
| 		if ( ct == CT_INSERTED )
 | |
| 		{
 | |
| 			size_t curPin = FindNextPinFrom( atRow + 1 );
 | |
| 
 | |
| 			while( curPin != NPOS )
 | |
| 			{
 | |
| 				mPins[curPin]->mRow += nRows;
 | |
| 
 | |
| 				++curPin;
 | |
| 
 | |
| 				if ( curPin == mPins.size() ) break;
 | |
| 			}
 | |
| 		}
 | |
| 		else
 | |
| 		if ( ct == CT_DELETED )
 | |
| 		{
 | |
| 			size_t curPin  = FindNextPinFrom( atRow + 1 );
 | |
| 			size_t fromPin = curPin;
 | |
| 			size_t tillRow = atRow + nRows;
 | |
| 
 | |
| 			while( curPin != NPOS && mPins[curPin]->mRow < tillRow )
 | |
| 			{
 | |
| 				++curPin;
 | |
| 
 | |
| 				if ( curPin == mPins.size() ) break;
 | |
| 			}
 | |
| 
 | |
| 			if ( fromPin != NPOS && nRows != 0 )
 | |
| 			{
 | |
| 				mPins.erase( &mPins[fromPin], &mPins[curPin] );
 | |
| 
 | |
| 				while( curPin < mPins.size() )
 | |
| 				{
 | |
| 					mPins[curPin]->mRow -= nRows;
 | |
| 					++curPin;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	// send notificaitons
 | |
| 
 | |
| 	for( size_t i = 0; i != mChangeListeners.size(); ++i )
 | |
| 
 | |
| 		mChangeListeners[i]->OnTextChanged( this, atRow, nRows, ct );
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::NotifyTextChanged( TPosition from, TPosition till, TEXT_CHANGE_TYPE ct )
 | |
| {
 | |
| 	ArrangePositions( from, till );
 | |
| 
 | |
| 	NotifyTextChanged( from.mRow, till.mRow - from.mRow + 1, ct );
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::DoExecuteNewCommand( TCommand& cmd )
 | |
| {
 | |
| 	if ( cmd.mType == TCMD_INSERT )
 | |
| 	{
 | |
| 		cmd.mPrePos = mCursorPos;
 | |
| 		DoInsertText( cmd.mRange.mFrom, cmd.mData, cmd.mDataLen, cmd.mRange );
 | |
| 	}
 | |
| 	else
 | |
| 	if ( cmd.mType == TCMD_DELETE )
 | |
| 	{
 | |
| 		cmd.mPrePos = mCursorPos;
 | |
| 		DoDeleteRange( cmd.mRange.mFrom, cmd.mRange.mTill, cmd.mRange );
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::DoReexecuteCommand( TCommand& cmd )
 | |
| {
 | |
| 	NotifyTextChanged( mCursorPos.mRow, 1, CT_MODIFIED ); // indicate update of current cursor position
 | |
| 
 | |
| 	if ( cmd.mType == TCMD_INSERT )
 | |
| 	{
 | |
| 		DoInsertText( cmd.mRange.mFrom, cmd.mData, cmd.mDataLen, cmd.mRange );
 | |
| 		mCursorPos = cmd.mPostPos;
 | |
| 	}
 | |
| 	else
 | |
| 	if ( cmd.mType == TCMD_DELETE )
 | |
| 	{
 | |
| 		DoDeleteRange( cmd.mRange.mFrom, cmd.mRange.mTill, cmd.mRange );
 | |
| 		mCursorPos = cmd.mPostPos;
 | |
| 	}
 | |
| 
 | |
| 	NotifyTextChanged( mCursorPos.mRow, 1, CT_MODIFIED ); // indicate update of current cursor position
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::DoUnexecuteCommand( TCommand& cmd )
 | |
| {
 | |
| 	NotifyTextChanged( mCursorPos.mRow, 1, CT_MODIFIED ); // indicate update of current cursor position
 | |
| 	
 | |
| 	if ( cmd.mType == TCMD_INSERT )
 | |
| 	{
 | |
| 		DoDeleteRange( cmd.mRange.mFrom, cmd.mRange.mTill, cmd.mRange );
 | |
| 		mCursorPos = cmd.mPrePos;
 | |
| 	}
 | |
| 	else					
 | |
| 	if ( cmd.mType == TCMD_DELETE )
 | |
| 	{
 | |
| 		DoInsertText( cmd.mRange.mFrom, cmd.mData, cmd.mDataLen, cmd.mRange );
 | |
| 		mCursorPos = cmd.mPrePos;
 | |
| 	}
 | |
| 
 | |
| 	NotifyTextChanged( mCursorPos.mRow, 1, CT_MODIFIED ); // indicate update of current cursor position
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::UndoImpl()
 | |
| {
 | |
| 	--mCurCommand;
 | |
| 
 | |
| 	DoUnexecuteCommand( *mCommands[mCurCommand] );
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::RedoImpl()
 | |
| {
 | |
| 	DoReexecuteCommand( *mCommands[mCurCommand] );
 | |
| 
 | |
| 	++mCurCommand;
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::ExecuteCommand( TCommand* pCmd )
 | |
| {
 | |
| 	if ( mCurCommand < mCheckPointCmdNo )
 | |
| 
 | |
| 		// new command is executed before the checkpoint,
 | |
| 		// and every thing is sliced - invalidate it
 | |
| 
 | |
| 		mCheckPointDestroyed = TRUE;
 | |
| 
 | |
| 	// slice undo-able commands ahead in the queue,
 | |
| 	// they wont ever be reexecuted
 | |
| 
 | |
| 	while( mCommands.size() > mCurCommand )
 | |
| 	{
 | |
| 		delete mCommands.back();
 | |
| 
 | |
| 		mCommands.pop_back();
 | |
| 	}
 | |
| 
 | |
| 	mCommands.push_back( pCmd );
 | |
| 
 | |
| 	DoExecuteNewCommand( *pCmd );
 | |
| 	++mCurCommand;
 | |
| }
 | |
| 
 | |
| bool wxTextEditorModel::CanPrependCommand( TCommand* pCmd )
 | |
| {
 | |
| 	if ( mCommands.size() != mCurCommand ||
 | |
| 		 mCommands.size() == 0 ) 
 | |
| 		 
 | |
| 		 return FALSE;
 | |
| 
 | |
| 	TCommand& prevCmd = *mCommands.back();
 | |
| 
 | |
| 	if ( !(prevCmd.mRange.mTill == pCmd->mRange.mFrom) ) 
 | |
| 
 | |
| 		return FALSE;
 | |
| 
 | |
| 	char prevCh = prevCmd.mData[ prevCmd.mDataLen - 1];
 | |
| 	char curCh  = pCmd->mData[0];
 | |
| 
 | |
| 	if ( prevCh == curCh ) return TRUE;
 | |
| 
 | |
| 	if ( prevCh == ' ' || curCh == ' ') return FALSE;
 | |
| 
 | |
| 	if ( TTextIterator::IsSeparator(prevCh) !=
 | |
| 		 TTextIterator::IsSeparator(curCh) )
 | |
| 
 | |
| 		 return FALSE;
 | |
| 	
 | |
| 	return TRUE;
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::PrependCommand( TCommand* pCmd )
 | |
| {
 | |
| 	if ( mCheckPointCmdNo == mCurCommand )
 | |
| 
 | |
| 		mCheckPointDestroyed = TRUE;
 | |
| 
 | |
| 	TCommand& prevCmd = *mCommands.back();
 | |
| 
 | |
| 	DoExecuteNewCommand( *pCmd );
 | |
| 
 | |
| 	TCommand* pComb = new TCommand();
 | |
| 
 | |
| 	pComb->mType    = TCMD_INSERT;
 | |
| 	pComb->mDataLen = prevCmd.mDataLen + pCmd->mDataLen;
 | |
| 
 | |
| 	pComb->mData        = AllocCharacters( pComb->mDataLen );
 | |
| 	pComb->mRange.mFrom = prevCmd.mRange.mFrom;
 | |
| 	pComb->mRange.mTill = pCmd->mRange.mTill;
 | |
| 	pComb->mPrePos      = prevCmd.mPrePos;
 | |
| 	pComb->mPostPos     = pCmd->mPostPos;
 | |
| 
 | |
| 	memcpy( pComb->mData, prevCmd.mData, prevCmd.mDataLen );
 | |
| 	memcpy( pComb->mData + prevCmd.mDataLen, pCmd->mData, pCmd->mDataLen );
 | |
| 
 | |
| 	FreeCharacters( prevCmd.mData );
 | |
| 	FreeCharacters( pCmd->mData );
 | |
| 
 | |
| 	delete &prevCmd;
 | |
| 	delete pCmd;
 | |
| 
 | |
| 	mCommands[ mCommands.size() - 1 ] = pComb;
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::SetPostPos( const TPosition& pos )
 | |
| {
 | |
| 	mCommands[mCurCommand-1]->mPostPos = pos;
 | |
| }
 | |
| 
 | |
| bool wxTextEditorModel::SelectionIsEmpty()
 | |
| {
 | |
| 	return mSelectionStart == mSelectionEnd;
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::StartBatch()
 | |
| {
 | |
| 	// TBD::
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::FinishBatch()
 | |
| {
 | |
| 	// TBD::
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::DeleteRange( const TPosition& from, const TPosition& till )
 | |
| {
 | |
| 	TCommand* pCmd = new TCommand();
 | |
| 
 | |
| 	pCmd->mType     = TCMD_DELETE;
 | |
| 
 | |
| 	pCmd->mRange.mFrom = from;
 | |
| 	pCmd->mRange.mTill = till;
 | |
| 	pCmd->mPrePos      = mCursorPos;
 | |
| 
 | |
| 	GetTextFromRange( from, till, &pCmd->mData, pCmd->mDataLen );
 | |
| 
 | |
| 	ExecuteCommand( pCmd );
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::InsertText( const TPosition& pos, const char* text, size_t len )
 | |
| {
 | |
| 	TCommand* pCmd = new TCommand();
 | |
| 
 | |
| 	pCmd->mType     = TCMD_INSERT;
 | |
| 
 | |
| 	pCmd->mRange.mFrom = pos;
 | |
| 
 | |
| 	pCmd->mData     = AllocCharacters( len, text ), 
 | |
| 	pCmd->mDataLen  = len;
 | |
| 	pCmd->mPrePos   = mCursorPos;
 | |
| 
 | |
| 	ExecuteCommand( pCmd );
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::DeleteSelection()
 | |
| {
 | |
| 	DeleteRange( mSelectionStart, mSelectionEnd );
 | |
| 
 | |
| 	ResetSelection();	
 | |
| }
 | |
| 
 | |
| bool wxTextEditorModel::IsLastLine( const TPosition& pos )
 | |
| {
 | |
| 	return FALSE;
 | |
| }
 | |
| 
 | |
| TTextIterator wxTextEditorModel::CreateIterator( const TPosition& pos )
 | |
| {
 | |
| 	size_t curRow = 0;
 | |
| 
 | |
| 	TBlockIteratorT bIter = mBlocks.begin();
 | |
| 
 | |
| 	TTextIterator tIter;
 | |
| 
 | |
| 	while( bIter != mBlocks.end() )
 | |
| 	{
 | |
| 		TBlockIteratorT nextBlk = bIter;
 | |
| 		++nextBlk;
 | |
| 
 | |
| 		if ( nextBlk == mBlocks.end() ||
 | |
| 			 ( pos.mRow >= curRow && 
 | |
| 			   pos.mRow <= curRow + (*bIter).mRowCount )
 | |
| 		   )
 | |
| 		{
 | |
| 			tIter.mFirstRowInBlock = curRow;
 | |
| 
 | |
| 			char* cur = (*bIter).mBuf;
 | |
| 			char* end = cur + (*bIter).mTextLen;
 | |
| 
 | |
| 			// slightly optimized
 | |
| 
 | |
| 			if ( curRow < pos.mRow )
 | |
| 			{
 | |
| 				while( cur < end )
 | |
| 				{
 | |
| 					if ( is_eol_char( *cur ) ) 
 | |
| 					{
 | |
| 						++curRow;
 | |
| 
 | |
| 						if ( !(curRow < pos.mRow) ) 
 | |
| 						{
 | |
| 							++cur;
 | |
| 							break;
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 					++cur;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			tIter.mActualRow    = curRow;
 | |
| 			tIter.mpCurRowStart = cur;
 | |
| 			tIter.mPos          = pos;
 | |
| 
 | |
| 			// FOR NOW:: positioning past the end of file is not supported
 | |
| 			tIter.mPos.mRow     = curRow;
 | |
| 
 | |
| 			tIter.mBlockIter     = bIter;
 | |
| 			tIter.mEndOfListIter = mBlocks.end();
 | |
| 
 | |
| 			break;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			curRow += (*bIter).mRowCount;
 | |
| 			++bIter;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return tIter;
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::ArrangePositions( TPosition& upper, TPosition& lower )
 | |
| {
 | |
| 	if ( upper > lower )
 | |
| 	{
 | |
| 		TPosition tmp( lower );
 | |
| 		lower = upper;
 | |
| 		upper = tmp;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::ArrangePositions( size_t& upper, size_t& lower )
 | |
| {
 | |
| 	if ( upper > lower )
 | |
| 	{
 | |
| 		size_t tmp = lower;
 | |
| 		lower = upper;
 | |
| 		upper = tmp;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::MergeChange( size_t fromRow, size_t nRows )
 | |
| {
 | |
| 	if ( mTextChanged == FALSE )
 | |
| 	{
 | |
| 		mChangedFromRow = fromRow;
 | |
| 		mChangedTillRow = fromRow + nRows;
 | |
| 		mTextChanged    = TRUE;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		if ( mChangedFromRow > fromRow )
 | |
| 
 | |
| 			mChangedFromRow = fromRow;
 | |
| 
 | |
| 		if ( mChangedTillRow < fromRow + nRows )
 | |
| 
 | |
| 			mChangedTillRow = fromRow + nRows;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::TrackSelection()
 | |
| {
 | |
| 	if ( !mIsSelectionEditMode ) return;
 | |
| 
 | |
| 	if ( mPrevCursorPos == mSelectionStart )
 | |
| 
 | |
| 		mSelectionStart = mCursorPos;
 | |
| 	else
 | |
| 		mSelectionEnd   = mCursorPos;
 | |
| 		
 | |
| 	ArrangePositions( mSelectionStart, mSelectionEnd );
 | |
| 
 | |
| 	NotifyTextChanged( mSelectionStart, mPrevSelectionStart, CT_MODIFIED );
 | |
| 	NotifyTextChanged( mSelectionEnd,   mPrevSelectionEnd,   CT_MODIFIED );
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::CheckSelection()
 | |
| {
 | |
| 	ArrangePositions( mSelectionStart, mSelectionEnd );
 | |
| 
 | |
| 	if ( mIsSelectionEditMode && SelectionIsEmpty() )
 | |
| 	{
 | |
| 		mSelectionStart = mCursorPos;
 | |
| 		mSelectionEnd   = mCursorPos;
 | |
| 	}
 | |
| 
 | |
| 	if ( !mIsSelectionEditMode && !SelectionIsEmpty() )
 | |
| 	{
 | |
| 		ResetSelection();
 | |
| 	}
 | |
| 
 | |
| 	mPrevSelectionStart = mSelectionStart;
 | |
| 	mPrevSelectionEnd   = mSelectionEnd;
 | |
| 	mPrevCursorPos      = mCursorPos;
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::ResetSelection()
 | |
| {
 | |
| 	if ( SelectionIsEmpty() ) return;
 | |
| 
 | |
| 	MergeChange( mSelectionStart.mRow, 
 | |
| 				 mSelectionEnd.mRow - mSelectionStart.mRow + 1 );
 | |
| 
 | |
| 	NotifyTextChanged( mSelectionStart, mSelectionEnd, CT_MODIFIED );
 | |
| 
 | |
| 	mSelectionStart = TPosition(0,0);
 | |
| 	mSelectionEnd   = TPosition(0,0);
 | |
| 
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::ClearUndoBuffer()
 | |
| {
 | |
| 	for( size_t i = 0; i != mCommands.size(); ++i )
 | |
| 	{
 | |
| 		TCommand& cmd = *mCommands[i];
 | |
| 
 | |
| 		if ( cmd.mData ) delete [] cmd.mData;
 | |
| 
 | |
| 		delete &cmd;
 | |
| 	}
 | |
| 
 | |
| 	mCommands.erase( mCommands.begin(), mCommands.end() );
 | |
| 
 | |
| 	mCurCommand = 0;
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::GetAllText( char** text, size_t& textLen )
 | |
| {
 | |
| 	GetTextFromRange( TPosition(0,0), TPosition( GetTotalRowCount()+1, 0 ),
 | |
| 		              text, textLen
 | |
| 		            );
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::DeleteAllText()
 | |
| {
 | |
| 	ResetSelection();
 | |
| 
 | |
| 	DeleteRange( TPosition(0,0), TPosition( GetTotalRowCount()+1, 0 ) );
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::SetSelectionEditMode( bool editIsOn )
 | |
| {
 | |
| 	mIsSelectionEditMode = editIsOn;
 | |
| }
 | |
| 
 | |
| size_t wxTextEditorModel::GetTotalRowCount()
 | |
| {
 | |
| 	size_t nRows = 0;
 | |
| 
 | |
| 	for( TBlockIteratorT i = mBlocks.begin(); i != mBlocks.end(); ++i )
 | |
| 
 | |
| 		nRows += (*i).mRowCount;
 | |
| 
 | |
| 	return nRows;
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::GetSelection( char** text, size_t& textLen )
 | |
| {
 | |
| 	GetTextFromRange( GetStartOfSelection(), GetEndOfSelection(), text, textLen );
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::NotifyView()
 | |
| {
 | |
| 	mpActiveView->OnModelChanged();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::NotifyAllViews()
 | |
| {
 | |
| 	for( size_t i = 0; i != mViews.size(); ++i )
 | |
| 
 | |
| 		mViews[i]->OnModelChanged();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::PrepreForCommand()
 | |
| {
 | |
| 	mTextChanged    = 0;
 | |
| 	mChangedFromRow = 0;
 | |
| 	mChangedTillRow = 0;
 | |
| }
 | |
| 
 | |
| size_t wxTextEditorModel::TextToScrColumn( const TPosition& pos )
 | |
| {
 | |
| 	TPosition spos;
 | |
| 
 | |
| 	mpActiveView->TextPosToScreenPos( pos, spos );
 | |
| 	
 | |
| 	return spos.mCol + mpActiveView->GetPagePos().mCol;
 | |
| }
 | |
| 
 | |
| size_t wxTextEditorModel::ScrToTextColumn( TPosition pos )
 | |
| {
 | |
| 	TPosition tpos;
 | |
| 
 | |
| 	pos.mCol -= mpActiveView->GetPagePos().mCol;
 | |
| 	pos.mRow -= mpActiveView->GetPagePos().mRow;
 | |
| 
 | |
| 	mpActiveView->ScreenPosToTextPos( pos, tpos );
 | |
| 
 | |
| 	return tpos.mCol;
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::DoMoveCursor( int rows, int cols )
 | |
| {
 | |
| 	mCursorPos.mCol = TextToScrColumn( mCursorPos );
 | |
| 
 | |
| 	mCursorPos.mRow += rows;
 | |
| 	mCursorPos.mCol += cols;
 | |
| 
 | |
| 	mCursorPos.mCol = ScrToTextColumn( mCursorPos );
 | |
| }
 | |
| 
 | |
| /*** public interface ***/
 | |
| 
 | |
| wxTextEditorModel::wxTextEditorModel()
 | |
| 
 | |
| 	: 
 | |
| 	  mpActiveView( NULL ),
 | |
| 	  mTabSize( 4 ),
 | |
| 	  mIsSelectionEditMode( FALSE ),
 | |
| 	  mRowsPerPage( 0 ),
 | |
| 	  mTextChanged( FALSE ),
 | |
| 	  mCurCommand( 0 ),
 | |
| 
 | |
| 	  mInsertMode     ( TRUE  ),
 | |
| 	  mAutoIndentMode ( TRUE  ),
 | |
| 	  mSmartIndentMode( TRUE  ),
 | |
| 	  mWasChanged     ( FALSE ),
 | |
| 	  mIsReadOnly     ( FALSE ),
 | |
| 	  mIsUnixText     ( IS_UNIX_TEXT_BY_DEFAULT )
 | |
| {
 | |
| 	// at least one block should be present
 | |
| 	// (otherwise text-iterators wont work)
 | |
| 
 | |
| 	mBlocks.push_back( TBlock() );
 | |
| }
 | |
| 
 | |
| wxTextEditorModel::~wxTextEditorModel()
 | |
| {
 | |
| 	ClearUndoBuffer();
 | |
| }
 | |
| 
 | |
| char* wxTextEditorModel::AllocCharacters( size_t n )
 | |
| {
 | |
| 	return new char[n];
 | |
| }
 | |
| 
 | |
| char* wxTextEditorModel::AllocCharacters( size_t n, const char* srcBuf )
 | |
| {
 | |
| 	char* destBuf = AllocCharacters( n );
 | |
| 
 | |
| 	memcpy( destBuf, srcBuf, n );
 | |
| 
 | |
| 	return destBuf;
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::FreeCharacters( char* buf )
 | |
| {
 | |
| 	delete [] buf;
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnInsertChar( char ch )
 | |
| {
 | |
| 	if ( ch == 27 ) return; // hack
 | |
| 
 | |
| 	if ( is_DOS_eol_char( ch ) ) ch = '\n';
 | |
| 
 | |
| 	PrepreForCommand();
 | |
| 	StartBatch();
 | |
| 
 | |
| 	TCommand* pCmd = new TCommand();
 | |
| 
 | |
| 	pCmd->mType    = TCMD_INSERT;
 | |
| 
 | |
| 	if ( ch == '\n' && !mIsUnixText ) 
 | |
| 	{
 | |
| 		// DOS text with CR-LF pair
 | |
| 		pCmd->mData    = AllocCharacters( 2 );
 | |
| 		pCmd->mDataLen = 2;
 | |
| 		pCmd->mData[0] = (char)13;
 | |
| 		pCmd->mData[1] = (char)10;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		pCmd->mData    = AllocCharacters( 1 );
 | |
| 		pCmd->mDataLen = 1;
 | |
| 		pCmd->mData[0] = ch;
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	if ( !SelectionIsEmpty() )
 | |
| 	{
 | |
| 		mCursorPos = mSelectionStart;
 | |
| 		DeleteSelection();
 | |
| 	}
 | |
| 
 | |
| 	pCmd->mRange.mFrom = mCursorPos;
 | |
| 
 | |
| 	if ( mInsertMode == FALSE )
 | |
| 	{
 | |
| 		TPosition nextPos( mCursorPos.mRow, mCursorPos.mCol + 1 );
 | |
| 		DeleteRange( mCursorPos, nextPos );
 | |
| 
 | |
| 		SetPostPos( mCursorPos );
 | |
| 	}
 | |
| 
 | |
| 	TTextIterator iter = CreateIterator( mCursorPos );
 | |
| 
 | |
| 	size_t lineLen = iter.GetLineLen();
 | |
| 
 | |
| 	bool indentAdded = FALSE;
 | |
| 
 | |
| 	if ( mCursorPos.mCol > lineLen )
 | |
| 	{
 | |
| 
 | |
| 		wxString s( ' ', mCursorPos.mCol - lineLen );
 | |
| 		InsertText( TPosition( mCursorPos.mRow, lineLen ), s.c_str(), s.length() );
 | |
| 
 | |
| 		SetPostPos( mCursorPos );
 | |
| 
 | |
| 		indentAdded = TRUE;
 | |
| 	}
 | |
| 
 | |
| 	if ( CanPrependCommand( pCmd ) || indentAdded )
 | |
| 
 | |
| 		PrependCommand( pCmd );
 | |
| 	else
 | |
| 		ExecuteCommand( pCmd );
 | |
| 
 | |
| 	++mCursorPos.mCol;
 | |
| 
 | |
| 	if ( is_eol_char( ch ) )
 | |
| 	{
 | |
| 		mCursorPos.mCol = 0;
 | |
| 		++mCursorPos.mRow;
 | |
| 
 | |
| 		SetPostPos( mCursorPos );
 | |
| 
 | |
| 		if ( mAutoIndentMode )
 | |
| 		{
 | |
| 			iter.ToStartOfLine();
 | |
| 			wxString indent;
 | |
| 
 | |
| 			while( !iter.IsEol() )
 | |
| 			{
 | |
| 				char ch = iter.GetChar();
 | |
| 
 | |
| 				if ( ch == '\t' || ch == ' ' )
 | |
| 
 | |
| 					indent += ch;
 | |
| 				else
 | |
| 					break;
 | |
| 
 | |
| 				iter.NextChar();
 | |
| 			}
 | |
| 
 | |
| 			if ( indent.length() )
 | |
| 			{
 | |
| 				// auto-indent is always prepended to the command which
 | |
| 				// caused it
 | |
| 
 | |
| 				mCursorPos = TPosition( mCursorPos.mRow, 0 );
 | |
| 
 | |
| 
 | |
| 				TCommand* pICmd = new TCommand();
 | |
| 				pICmd->mType    = TCMD_INSERT;
 | |
| 				pICmd->mData    = AllocCharacters( indent.length() );
 | |
| 				pICmd->mDataLen = indent.length();
 | |
| 				memcpy( pICmd->mData, indent, indent.length() );
 | |
| 
 | |
| 				pICmd->mRange.mFrom = TPosition( mCursorPos.mRow, 0 );
 | |
| 
 | |
| 				PrependCommand( pICmd );
 | |
| 
 | |
| 				SetPostPos( mCursorPos );
 | |
| 
 | |
| 				mCursorPos.mCol = indent.length();
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 		SetPostPos( mCursorPos );
 | |
| 
 | |
| 	FinishBatch();
 | |
| 
 | |
| 	NotifyAllViews();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnDelete()
 | |
| {
 | |
| 	PrepreForCommand();
 | |
| 	StartBatch();
 | |
| 
 | |
| 	if ( !SelectionIsEmpty() )
 | |
| 	{
 | |
| 		TPosition startPos = mSelectionStart;
 | |
| 		DeleteSelection();
 | |
| 		mCursorPos = startPos;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		TTextIterator iter = CreateIterator( mCursorPos );
 | |
| 		
 | |
| 		if ( iter.GetLineLen() == mCursorPos.mCol && !iter.IsLastLine() )
 | |
| 		{
 | |
| 			TPosition nextPos( mCursorPos.mRow+1, 0 );
 | |
| 			DeleteRange( mCursorPos, nextPos );
 | |
| 			NotifyTextChanged( mCursorPos.mRow, 2, CT_DELETED );
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			TPosition nextPos( mCursorPos.mRow, mCursorPos.mCol + 1 );
 | |
| 			DeleteRange( mCursorPos, nextPos );
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	SetPostPos( mCursorPos );
 | |
| 
 | |
| 	FinishBatch();
 | |
| 
 | |
| 	NotifyAllViews();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnDeleteBack()
 | |
| {
 | |
| 	PrepreForCommand();
 | |
| 	StartBatch();
 | |
| 
 | |
| 	if ( !SelectionIsEmpty() )
 | |
| 	{
 | |
| 		mCursorPos = mSelectionStart;
 | |
| 		DeleteSelection();
 | |
| 	}
 | |
| 	else
 | |
| 	if ( !(mCursorPos == TPosition(0,0)) )
 | |
| 	{
 | |
| 		TPosition prevPos;
 | |
| 
 | |
| 		if ( mCursorPos.mCol == 0 )
 | |
| 		{
 | |
| 			TTextIterator iter = CreateIterator( mCursorPos );
 | |
| 			iter.PreviousChar();
 | |
| 
 | |
| 			prevPos = iter.GetPosition();
 | |
| 		}
 | |
| 		else
 | |
| 			prevPos = TPosition( mCursorPos.mRow, mCursorPos.mCol - 1 );
 | |
| 
 | |
| 		DeleteRange( prevPos, mCursorPos );
 | |
| 
 | |
| 		mCursorPos = prevPos;
 | |
| 	}
 | |
| 
 | |
| 	SetPostPos( mCursorPos );
 | |
| 
 | |
| 	FinishBatch();
 | |
| 
 | |
| 	NotifyAllViews();
 | |
| 
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnDeleteLine()
 | |
| {
 | |
| 	PrepreForCommand();
 | |
| 	StartBatch();
 | |
| 
 | |
| 	DeleteSelection();
 | |
| 
 | |
| 	TTextIterator iter = CreateIterator( mCursorPos );
 | |
| 
 | |
| 	iter.ToStartOfLine();
 | |
| 
 | |
| 	TPosition from = iter.GetPosition();
 | |
| 
 | |
| 	iter.ToEndOfLine();
 | |
| 
 | |
| 	if ( iter.IsLastLine() == FALSE )
 | |
| 
 | |
| 		iter.NextChar(); // delete eol-char also, if it's not the last line
 | |
| 
 | |
| 	TPosition till = iter.GetPosition();
 | |
| 
 | |
| 	DeleteRange( from, till );
 | |
| 	SetPostPos( mCursorPos );
 | |
| 
 | |
| 	FinishBatch();
 | |
| 
 | |
| 	NotifyAllViews();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnShiftSelectionIndent( bool left )
 | |
| {
 | |
| 	if ( SelectionIsEmpty() ) return;
 | |
| 
 | |
| 	PrepreForCommand();
 | |
| 	StartBatch();
 | |
| 
 | |
| 	for( size_t row = mSelectionStart.mRow; row != mSelectionEnd.mRow; ++row )
 | |
| 	{
 | |
| 		TTextIterator iter = CreateIterator( TPosition( row, 0 ) );
 | |
| 
 | |
| 		if ( left )
 | |
| 		{
 | |
| 			int n = 0, pos = 0;
 | |
| 
 | |
| 			while( !iter.IsEol() && !iter.IsEof() )
 | |
| 			{
 | |
| 				char ch = iter.GetChar();
 | |
| 
 | |
| 				if ( pos == mTabSize ) break;
 | |
| 
 | |
| 				if ( ch != ' ' && ch != '\t' ) break;
 | |
| 
 | |
| 				++n;
 | |
| 
 | |
| 				if ( ch == '\t' ) break;
 | |
| 
 | |
| 				++pos;
 | |
| 
 | |
| 				iter.NextChar();
 | |
| 			}
 | |
| 
 | |
| 			if ( n ) DeleteRange( TPosition( row,0 ), TPosition( row, n ) );
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			char txt = '\t';
 | |
| 
 | |
| 			InsertText( TPosition( row, 0 ), &txt, sizeof(char) );
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	FinishBatch();
 | |
| 	NotifyAllViews();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnPaste()
 | |
| {
 | |
| 	// FIXME:: "wxLogQueryInterface(..)" linking problems with MSDev4.0
 | |
| 	
 | |
| #ifdef __HACK_MY_MSDEV40__
 | |
| 
 | |
| 	bool alreadyOpen=wxClipboardOpen();
 | |
| 	if (!alreadyOpen)
 | |
| 	{
 | |
| 		wxOpenClipboard();
 | |
| 	}
 | |
| 
 | |
| 	char* data = (char*)::wxGetClipboardData( wxDF_TEXT );
 | |
| 
 | |
| 	wxCloseClipboard();
 | |
| 
 | |
| 	if ( data == NULL ) return;
 | |
| 
 | |
| 	PrepreForCommand();
 | |
| 	StartBatch();
 | |
| 
 | |
| 	if ( !SelectionIsEmpty() )
 | |
| 	{
 | |
| 		mCursorPos = GetStartOfSelection();
 | |
| 		DeleteSelection();
 | |
| 	}
 | |
| 
 | |
| 	InsertText( mCursorPos, data, strlen( data ) );
 | |
| 
 | |
| 	delete [] data;
 | |
| #else
 | |
| 
 | |
| 	if ( !wxTheClipboard->Open() ) return;
 | |
| 
 | |
| 	wxTextDataObject data;
 | |
| 	if ( !wxTheClipboard->IsSupported(wxDF_TEXT) ) 
 | |
| 	{
 | |
| 		wxTheClipboard->Close();
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	wxTheClipboard->GetData(&data);
 | |
| 
 | |
| 	string txt = data.GetText();
 | |
| 
 | |
| 	wxTheClipboard->Close();
 | |
| 
 | |
| 	PrepreForCommand();
 | |
| 	StartBatch();
 | |
| 
 | |
| 	DeleteSelection();
 | |
| 
 | |
| 	InsertText( mCursorPos, txt.c_str(), txt.length() );
 | |
| #endif
 | |
| 
 | |
| 
 | |
| 	mCursorPos = mCommands.back()->mRange.mTill;
 | |
| 	SetPostPos( mCursorPos );
 | |
| 
 | |
| 	FinishBatch();
 | |
| 	NotifyAllViews();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnCut()
 | |
| {
 | |
| 	OnCopy();
 | |
| 
 | |
| 	PrepreForCommand();
 | |
| 	StartBatch();
 | |
| 
 | |
| 	DeleteSelection();
 | |
| 	SetPostPos( mCursorPos );
 | |
| 
 | |
| 
 | |
| 	FinishBatch();
 | |
| 	NotifyAllViews();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnCopy()
 | |
| {
 | |
| 	if ( !SelectionIsEmpty() )
 | |
| 	{
 | |
| 		size_t len  = 0;
 | |
| 		char*  text = NULL;
 | |
| 
 | |
| #ifndef __HACK_MY_MSDEV40__
 | |
| 
 | |
| 		if ( !wxTheClipboard->Open() ) return;
 | |
| 
 | |
| 		GetTextFromRange( mSelectionStart, mSelectionEnd, &text, len );
 | |
| 
 | |
| 		wxString s( text, len );
 | |
| 
 | |
| 	    wxTheClipboard->AddData( new wxTextDataObject(s) );
 | |
| 		wxTheClipboard->Close();
 | |
| 
 | |
| 		FreeCharacters( text );
 | |
| #else
 | |
| 		bool alreadyOpen=wxClipboardOpen();
 | |
| 		if (!alreadyOpen)
 | |
| 		{
 | |
| 			wxOpenClipboard();
 | |
| 			if (!wxEmptyClipboard()) 
 | |
| 			{
 | |
| 				wxCloseClipboard();
 | |
| 				return;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		GetTextFromRange( mSelectionStart, mSelectionEnd, &text, len );
 | |
| 
 | |
| 		wxString s( text, len );
 | |
| 
 | |
| 		bool success = ::wxEmptyClipboard();
 | |
| 
 | |
| 		success = wxSetClipboardData( wxDF_TEXT, (wxObject*)s.c_str(), 0,0 );
 | |
| 
 | |
| 		FreeCharacters( text );
 | |
| 
 | |
| 		wxCloseClipboard();
 | |
| 
 | |
| #endif
 | |
| 	}
 | |
| }
 | |
| 
 | |
| bool wxTextEditorModel::CanCopy()
 | |
| {
 | |
| 	return !SelectionIsEmpty();
 | |
| }
 | |
| 
 | |
| bool wxTextEditorModel::CanPaste()
 | |
| {
 | |
| 	if ( mIsReadOnly ) return FALSE;
 | |
| 
 | |
| #ifndef __HACK_MY_MSDEV40__
 | |
| 
 | |
| 	if ( !wxTheClipboard->Open() ) return FALSE;
 | |
| 
 | |
| 	if ( !wxTheClipboard->IsSupported(wxDF_TEXT) )
 | |
| 		return FALSE;
 | |
| 
 | |
| 	wxTheClipboard->Close();
 | |
| 
 | |
| 	return TRUE;
 | |
| 
 | |
| #else
 | |
| 
 | |
| 	bool success = ::wxClipboardOpen();
 | |
| 
 | |
| 	bool alreadyOpen=wxClipboardOpen();
 | |
| 	if (!alreadyOpen)
 | |
| 	{
 | |
| 		wxOpenClipboard();
 | |
| 	}
 | |
| 
 | |
| 	char* data = (char*)::wxGetClipboardData( wxDF_TEXT );
 | |
| 
 | |
| 	wxCloseClipboard();
 | |
| 
 | |
| 	if ( data != NULL && strlen(data) != 0 )
 | |
| 	{
 | |
| 		delete [] data;
 | |
| 		return TRUE;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		delete [] data;
 | |
| 		return FALSE;
 | |
| 	}
 | |
| 
 | |
| #endif
 | |
| 
 | |
| }
 | |
| 
 | |
| bool wxTextEditorModel::CanUndo()
 | |
| {
 | |
| 	return !( mCommands.size() == 0 ||
 | |
| 			  mCurCommand == 0 );
 | |
| }
 | |
| 
 | |
| bool wxTextEditorModel::CanRedo()
 | |
| {
 | |
| 	return mCurCommand != mCommands.size();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnUndo()
 | |
| {
 | |
| 	if ( !CanUndo() ) return;
 | |
| 
 | |
| 	PrepreForCommand();
 | |
| 	StartBatch();
 | |
| 
 | |
| 	ResetSelection();
 | |
| 
 | |
| 	UndoImpl();
 | |
| 
 | |
| 	FinishBatch();
 | |
| 	NotifyAllViews();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnRedo()
 | |
| {
 | |
| 	if ( !CanRedo() ) return;
 | |
| 
 | |
| 	PrepreForCommand();
 | |
| 	StartBatch();
 | |
| 
 | |
| 	ResetSelection();
 | |
| 
 | |
| 	RedoImpl();
 | |
| 
 | |
| 	FinishBatch();
 | |
| 	NotifyAllViews();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnMoveLeft()
 | |
| {
 | |
| 	PrepreForCommand();
 | |
| 	CheckSelection();
 | |
| 
 | |
| 	if ( mCursorPos.mCol == 0 )
 | |
| 	{
 | |
| 		if ( mCursorPos.mRow != 0 )
 | |
| 		{
 | |
| 			--mCursorPos.mRow;
 | |
| 
 | |
| 			TTextIterator iter = CreateIterator( mCursorPos );
 | |
| 
 | |
| 			iter.ToEndOfLine();
 | |
| 
 | |
| 			mCursorPos.mCol = iter.GetPosition().mCol;
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 		--mCursorPos.mCol;
 | |
| 
 | |
| 	TrackSelection();
 | |
| 	NotifyView();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnMoveRight()
 | |
| {
 | |
| 	PrepreForCommand();
 | |
| 	CheckSelection();
 | |
| 
 | |
| 	++mCursorPos.mCol;
 | |
| 
 | |
| 	TrackSelection();
 | |
| 	NotifyView();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnMoveUp()
 | |
| {
 | |
| 	PrepreForCommand();
 | |
| 	CheckSelection();
 | |
| 
 | |
| 	if ( mCursorPos.mRow != 0 )
 | |
| 	
 | |
| 		DoMoveCursor( -1,0 );
 | |
| 
 | |
| 	TrackSelection();
 | |
| 	NotifyView();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnMoveDown()
 | |
| {
 | |
| 	PrepreForCommand();
 | |
| 	CheckSelection();
 | |
| 
 | |
| 	if ( mCursorPos.mRow + 1 < GetTotalRowCount() )
 | |
| 
 | |
| 		DoMoveCursor( 1,0 );
 | |
| 
 | |
| 	TrackSelection();
 | |
| 	NotifyView();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnWordRight()
 | |
| {
 | |
| 	PrepreForCommand();
 | |
| 	CheckSelection();
 | |
| 
 | |
| 	TTextIterator iter = CreateIterator( mCursorPos );
 | |
| 
 | |
| 	iter.NextWord();
 | |
| 
 | |
| 	mCursorPos = iter.GetPosition();
 | |
| 
 | |
| 	TrackSelection();
 | |
| 	NotifyView();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnWordLeft()
 | |
| {
 | |
| 	PrepreForCommand();
 | |
| 	CheckSelection();
 | |
| 
 | |
| 	TTextIterator iter = CreateIterator( mCursorPos );
 | |
| 
 | |
| 	iter.PreviousWord();
 | |
| 
 | |
| 	mCursorPos = iter.GetPosition();
 | |
| 
 | |
| 	TrackSelection();
 | |
| 	NotifyView();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnMoveToPosition( const TPosition& pos )
 | |
| {
 | |
| 	PrepreForCommand();
 | |
| 	CheckSelection();
 | |
| 
 | |
| 	mCursorPos = pos;
 | |
| 
 | |
| 	TrackSelection();
 | |
| 	NotifyView();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnEndOfLine()
 | |
| {
 | |
| 	PrepreForCommand();
 | |
| 	CheckSelection();
 | |
| 
 | |
| 	TTextIterator iter = CreateIterator( mCursorPos );
 | |
| 	iter.ToEndOfLine();
 | |
| 
 | |
| 	mCursorPos = iter.GetPosition();
 | |
| 
 | |
| 	TrackSelection();
 | |
| 	NotifyView();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnStartOfLine()
 | |
| {
 | |
| 	PrepreForCommand();
 | |
| 	CheckSelection();
 | |
| 
 | |
| 	int prevCol = mCursorPos.mCol;
 | |
| 
 | |
| 	TTextIterator iter = CreateIterator( mCursorPos );
 | |
| 	iter.ToStartOfLine();
 | |
| 
 | |
| 	// bypass leading white-space at the begining of the line
 | |
| 
 | |
| 	while( !iter.IsEol() )
 | |
| 	{
 | |
| 		char ch = iter.GetChar();
 | |
| 
 | |
| 		if ( ch != ' ' && ch != '\t' ) break;
 | |
| 
 | |
| 		++mCursorPos.mCol;
 | |
| 
 | |
| 		iter.NextChar();
 | |
| 	}
 | |
| 
 | |
| 	mCursorPos = iter.GetPosition();
 | |
| 
 | |
| 	if ( mCursorPos.mCol == prevCol )
 | |
| 
 | |
| 		mCursorPos.mCol = 0;
 | |
| 
 | |
| 	TrackSelection();
 | |
| 	NotifyView();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnPageUp()
 | |
| {
 | |
| 	PrepreForCommand();
 | |
| 	CheckSelection();
 | |
| 
 | |
| 	if ( mCursorPos.mRow < mRowsPerPage )
 | |
| 
 | |
| 		mCursorPos.mRow = 0;
 | |
| 	else
 | |
| 		DoMoveCursor( -mRowsPerPage,0 );
 | |
| 
 | |
| 	mpActiveView->ScrollView( -(int)mRowsPerPage, 0 );
 | |
| 
 | |
| 	TrackSelection();
 | |
| 	NotifyView();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnPageDown()
 | |
| {
 | |
| 	PrepreForCommand();
 | |
| 	CheckSelection();
 | |
| 
 | |
| 	if ( mCursorPos.mRow + mRowsPerPage >= GetTotalRowCount() )
 | |
| 	{
 | |
| 		if ( GetTotalRowCount() != 0 )
 | |
| 
 | |
| 			mCursorPos.mRow = GetTotalRowCount() - 1;
 | |
| 		else
 | |
| 			mCursorPos.mRow = 0;
 | |
| 	}
 | |
| 	else
 | |
| 		DoMoveCursor( mRowsPerPage,0 );
 | |
| 
 | |
| 	mpActiveView->ScrollView( mRowsPerPage, 0 );
 | |
| 
 | |
| 	TrackSelection();
 | |
| 	NotifyView();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnSlideUp()
 | |
| {
 | |
| 	PrepreForCommand();
 | |
| 	
 | |
| 	if ( mpActiveView->GetPagePos().mRow + mRowsPerPage - 1 == mCursorPos.mRow  )
 | |
| 	{
 | |
| 		if ( mCursorPos.mRow == 0 )
 | |
| 
 | |
| 			return;
 | |
| 
 | |
| 		DoMoveCursor( -1,0 );
 | |
| 	}
 | |
| 
 | |
| 	mpActiveView->ScrollView( -1, 0 );
 | |
| 
 | |
| 	NotifyView();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnSlideDown()
 | |
| {
 | |
| 	PrepreForCommand();
 | |
| 	
 | |
| 	if ( mCursorPos.mRow == mpActiveView->GetPagePos().mRow )
 | |
| 	{
 | |
| 		if ( mCursorPos.mRow + 1 >= GetTotalRowCount() )
 | |
| 
 | |
| 			return;
 | |
| 
 | |
| 		DoMoveCursor( 1,0 );
 | |
| 	}
 | |
| 
 | |
| 	mpActiveView->ScrollView( 1, 0 );
 | |
| 
 | |
| 	NotifyView();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnStartOfText()
 | |
| {
 | |
| 	PrepreForCommand();
 | |
| 	CheckSelection();
 | |
| 
 | |
| 	mCursorPos.mRow = mCursorPos.mCol = 0;
 | |
| 
 | |
| 	TrackSelection();
 | |
| 	NotifyView();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnEndOfText()
 | |
| {
 | |
| 	PrepreForCommand();
 | |
| 	CheckSelection();
 | |
| 
 | |
| 	mCursorPos.mRow = GetTotalRowCount() - 1;
 | |
| 
 | |
| 	TTextIterator iter = CreateIterator( mCursorPos );
 | |
| 
 | |
| 	iter.ToEndOfLine();
 | |
| 
 | |
| 	mCursorPos = iter.GetPosition();
 | |
| 
 | |
| 	TrackSelection();
 | |
| 	NotifyView();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnSelectWord()
 | |
| {
 | |
| 	PrepreForCommand();
 | |
| 
 | |
| 	TTextIterator iter1 = CreateIterator( mCursorPos );
 | |
| 	iter1.GotoClosestPos();
 | |
| 
 | |
| 	if ( mCursorPos == iter1.GetPosition() )
 | |
| 	{
 | |
| 		TTextIterator iter2 = iter1;
 | |
| 
 | |
| 		// find the left-edge of the word
 | |
| 
 | |
| 		bool wasSeparator = TTextIterator::IsSeparator( iter1.GetChar() );
 | |
| 
 | |
| 		while( !iter1.IsEol() )
 | |
| 		{
 | |
| 			char ch = iter1.GetChar();
 | |
| 
 | |
| 			if ( ch == '\t' || 
 | |
| 				 ch == ' '  ||
 | |
| 				 wasSeparator != TTextIterator::IsSeparator( iter1.GetChar() )
 | |
| 			  ) 
 | |
| 			{
 | |
| 				iter1.NextChar();
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			iter1.PreviousChar();
 | |
| 		}
 | |
| 
 | |
| 		// find the left-edge of the word
 | |
| 
 | |
| 		while( !iter2.IsEol() )
 | |
| 		{
 | |
| 			char ch = iter2.GetChar();
 | |
| 
 | |
| 			if ( ch == '\t' || 
 | |
| 				 ch == ' '  ||
 | |
| 				 wasSeparator != TTextIterator::IsSeparator( iter2.GetChar() ) 
 | |
| 			)
 | |
| 				break;
 | |
| 
 | |
| 			iter2.NextChar();
 | |
| 		}
 | |
| 
 | |
| 		if ( !(iter1.GetPosition() == iter2.GetPosition()) )
 | |
| 		{
 | |
| 			mSelectionStart = iter1.GetPosition();
 | |
| 			mSelectionEnd   = iter2.GetPosition();
 | |
| 			mCursorPos      = iter2.GetPosition();
 | |
| 
 | |
| 			NotifyTextChanged( mSelectionStart.mRow, 1, CT_MODIFIED );
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	NotifyView();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnSelectAll()
 | |
| {
 | |
| 	PrepreForCommand();
 | |
| 
 | |
| 	ResetSelection();
 | |
| 
 | |
| 	mSelectionStart = TPosition(0,0);
 | |
| 	mSelectionEnd   = TPosition( GetTotalRowCount(), 1024 ); // FOR NOW:: hack
 | |
| 
 | |
| 	mCursorPos = mSelectionStart;
 | |
| 
 | |
| 	NotifyTextChanged( mSelectionStart.mRow, mSelectionEnd.mRow, CT_MODIFIED );
 | |
| 
 | |
| 	NotifyView();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnToggleBookmark()
 | |
| {
 | |
| 	size_t curRow = GetCursor().mRow;
 | |
| 
 | |
| 	if ( GetPinAt( curRow, TBookmarkPin::GetPinTypeCode() ) != NULL )
 | |
| 
 | |
| 		RemovePinAt( curRow, TBookmarkPin::GetPinTypeCode() );
 | |
| 	else
 | |
| 		AddPin( new TBookmarkPin( curRow ) );
 | |
| 
 | |
| 	MergeChange( curRow, 1 );
 | |
| 
 | |
| 	NotifyAllViews();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnNextBookmark()
 | |
| {
 | |
| 	size_t pinNo = FindNextPinFrom( mCursorPos.mRow + 1 );
 | |
| 
 | |
| 	while( pinNo != NPOS )
 | |
| 	{
 | |
| 		TPinBase& pin = *mPins[pinNo];
 | |
| 
 | |
| 		if ( pin.mTypeCode == BOOKMARK_PIN_TC )
 | |
| 		{
 | |
| 			OnGotoLine( pin.mRow, 0 );
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		if ( pinNo == mPins.size() ) break;
 | |
| 
 | |
| 		++pinNo;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnPreviousBookmark()
 | |
| {
 | |
| 	if ( mCursorPos.mRow == 0 ) return;
 | |
| 
 | |
| 	size_t pinNo = FindPreviousPinFrom( mCursorPos.mRow - 1 );
 | |
| 
 | |
| 	while( pinNo != NPOS )
 | |
| 	{
 | |
| 		TPinBase& pin = *mPins[pinNo];
 | |
| 
 | |
| 		if ( pin.mTypeCode == BOOKMARK_PIN_TC )
 | |
| 		{
 | |
| 			OnGotoLine( pin.mRow, 0 );
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		if ( pinNo == 0 ) break;
 | |
| 
 | |
| 		--pinNo;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| bool wxTextEditorModel::OnFind()
 | |
| {
 | |
| 	if ( !SelectionIsEmpty() )
 | |
| 	{
 | |
| 		if ( GetStartOfSelection().mRow == GetEndOfSelection().mRow )
 | |
| 		{
 | |
| 			char* buf = NULL; size_t len = 0;
 | |
| 
 | |
| 			GetSelection( &buf, len );
 | |
| 
 | |
| 			mLastFindExpr = string( buf, 0, len );
 | |
| 
 | |
| 			delete [] buf;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	wxFindTextDialog dlg( GetActiveView(), mLastFindExpr );
 | |
| 	//dlg.SetExpr( mLastFindExpr );
 | |
| 
 | |
| 	if( dlg.ShowModal() == wxID_OK )
 | |
| 	{
 | |
| 		mLastFindExpr = dlg.GetExpr();
 | |
| 
 | |
| 		GetActiveView()->SetFocus();
 | |
| 
 | |
| 		return OnFindNext();
 | |
| 	}
 | |
| 
 | |
| 	GetActiveView()->SetFocus();
 | |
| 
 | |
| 	return FALSE;
 | |
| }
 | |
| 
 | |
| bool wxTextEditorModel::OnFindNext()
 | |
| {
 | |
| 	PrepreForCommand();
 | |
| 
 | |
| 	string& val = mLastFindExpr;
 | |
| 	size_t  len = val.length();
 | |
| 
 | |
| 	if ( len == 0 ) 
 | |
| 	{
 | |
| 		NotifyView();
 | |
| 		wxMessageBox( "Secarch string not found!" );
 | |
| 
 | |
| 		GetActiveView()->SetFocus();
 | |
| 
 | |
| 		return FALSE;
 | |
| 	}
 | |
| 
 | |
| 	char ch1 = val[0];
 | |
| 
 | |
| 	TTextIterator iter = CreateIterator( mCursorPos );
 | |
| 
 | |
| 	while( !iter.IsEof() )
 | |
| 	{
 | |
| 		char ch = iter.GetChar();
 | |
| 
 | |
| 		if ( ch == ch1 )
 | |
| 		{
 | |
| 			size_t startCol = iter.mPos.mCol;
 | |
| 			iter.NextChar();
 | |
| 			ch = iter.GetChar();
 | |
| 
 | |
| 			size_t i = 1;
 | |
| 			while( i < len && !iter.IsEof() && ch == val[i] )
 | |
| 			{
 | |
| 				++i;
 | |
| 				iter.NextChar();
 | |
| 				ch = iter.GetChar();
 | |
| 			}
 | |
| 
 | |
| 			if ( i == len )
 | |
| 			{
 | |
| 				if ( !SelectionIsEmpty() )
 | |
| 
 | |
| 					ResetSelection();
 | |
| 
 | |
| 				SetStartOfSelection( TPosition( iter.mPos.mRow, startCol ) );
 | |
| 				SetEndOfSelection( iter.mPos );
 | |
| 
 | |
| 				MergeChange( iter.mPos.mRow, 1 );
 | |
| 
 | |
| 				mCursorPos = iter.mPos;
 | |
| 
 | |
| 				OnGotoLine( iter.mPos.mRow, iter.mPos.mCol );
 | |
| 				return TRUE;
 | |
| 			}
 | |
| 		}
 | |
| 		else
 | |
| 			iter.NextChar();
 | |
| 	}
 | |
| 
 | |
| 	NotifyView();
 | |
| 	MergeChange( mCursorPos.mRow, 2 );
 | |
| 	wxMessageBox( "Secarch string not found!" );
 | |
| 
 | |
| 	GetActiveView()->SetFocus();
 | |
| 
 | |
| 	return FALSE;
 | |
| }
 | |
| 
 | |
| bool wxTextEditorModel::OnFindPrevious()
 | |
| {
 | |
| 	// TBD::
 | |
| 	return FALSE;
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnGotoLine( int line, int col )
 | |
| {
 | |
| 	if ( mpActiveView == NULL ) return;
 | |
| 
 | |
| 	TPosition pagePos = mpActiveView->GetPagePos();
 | |
| 
 | |
| 	if ( line >= pagePos.mRow &&
 | |
| 		 line <  pagePos.mRow + mRowsPerPage )
 | |
| 	{
 | |
| 		mCursorPos.mRow = (size_t)line;
 | |
| 		mCursorPos.mCol = (size_t)col;
 | |
| 
 | |
| 		if ( col == - 1)
 | |
| 		{
 | |
| 			mCursorPos.mCol = 0;
 | |
| 			OnStartOfLine();
 | |
| 		}
 | |
| 		else
 | |
| 			NotifyView();
 | |
| 
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	size_t third = mRowsPerPage / 3;
 | |
| 	size_t newTop = 0;
 | |
| 
 | |
| 	if ( line < third )
 | |
| 
 | |
| 		newTop = 0;
 | |
| 	else
 | |
| 		newTop = line - third;
 | |
| 
 | |
| 
 | |
| 	mpActiveView->ScrollView( (int)newTop - (int)pagePos.mRow, -(int)pagePos.mCol );
 | |
| 
 | |
| 	mCursorPos.mRow = line;
 | |
| 	mCursorPos.mCol = col;
 | |
| 
 | |
| 	if ( col == - 1)
 | |
| 	{
 | |
| 		mCursorPos.mCol = 0;
 | |
| 		OnStartOfLine();
 | |
| 	}
 | |
| 	else
 | |
| 		NotifyView();
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::OnGotoLine()
 | |
| {
 | |
| 	wxTextEntryDialog* dlg = 
 | |
| 		new wxTextEntryDialog( mpActiveView, "Line number:", "Goto line", "" );
 | |
| 
 | |
| 	int nTries = 3;
 | |
| 
 | |
| 	while( dlg->ShowModal() == wxID_OK && nTries )
 | |
| 	{
 | |
| 		ResetSelection();
 | |
| 
 | |
| 		int i = -1;
 | |
| 		sscanf( dlg->GetValue(), "%d", &i );
 | |
| 
 | |
| 		if ( i == -1  )
 | |
| 		{
 | |
| 			wxMessageBox( "Please enter a number" );
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		if ( i == 0 ) ++i;
 | |
| 
 | |
| 		OnGotoLine( (size_t)(i-1), 0 );
 | |
| 		break;
 | |
| 
 | |
| 		--nTries;
 | |
| 	}
 | |
| 
 | |
| 	GetActiveView()->SetFocus();
 | |
| }
 | |
| 
 | |
| 
 | |
| bool wxTextEditorModel::IsReadOnly()
 | |
| {
 | |
| 	return mIsReadOnly;
 | |
| }
 | |
| 
 | |
| bool wxTextEditorModel::IsModified()
 | |
| {
 | |
| 	return mCurCommand != 0;
 | |
| }
 | |
| 
 | |
| bool wxTextEditorModel::IsInsertMode()
 | |
| {
 | |
| 	return mInsertMode;
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::SetCheckpoint()
 | |
| {
 | |
| 	mCheckPointDestroyed = FALSE;
 | |
| 	mCheckPointCmdNo     = mCurCommand;
 | |
| }
 | |
| 
 | |
| bool wxTextEditorModel::CheckpointModified()
 | |
| {
 | |
| 	if ( mCheckPointDestroyed ) return TRUE;
 | |
| 
 | |
| 	return mCheckPointCmdNo != mCurCommand;
 | |
| }
 | |
| 
 | |
| TPosition wxTextEditorModel::GetStartOfSelection()
 | |
| {
 | |
| 	ArrangePositions( mSelectionStart, mSelectionEnd );
 | |
| 
 | |
| 	return mSelectionStart;
 | |
| }
 | |
| 
 | |
| TPosition wxTextEditorModel::GetEndOfSelection()
 | |
| {
 | |
| 	ArrangePositions( mSelectionStart, mSelectionEnd );
 | |
| 
 | |
| 	return mSelectionEnd;
 | |
| }
 | |
| 
 | |
| TPosition wxTextEditorModel::GetCursor()
 | |
| {
 | |
| 	return mCursorPos;
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::SetStartOfSelection( const TPosition& pos )
 | |
| {
 | |
| 	mSelectionStart = pos;
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::SetEndOfSelection( const TPosition& pos )
 | |
| {
 | |
| 	mSelectionEnd = pos;
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::SetCursor( const TPosition& pos )
 | |
| {
 | |
| 	mCursorPos = pos;
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::AddView( wxTextEditorView* pView )
 | |
| {
 | |
| 	mViews.push_back( pView );
 | |
| 	pView->SetModel( this );
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::RemoveView( wxTextEditorView* pView )
 | |
| {
 | |
| 	for( size_t i = 0; i != mViews.size(); ++i )
 | |
| 
 | |
| 		if ( mViews[i] == pView )
 | |
| 		{
 | |
| 			mViews.erase( & mViews[i] );
 | |
| 			return;
 | |
| 		}
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::SetActiveView( wxTextEditorView* pView )
 | |
| {
 | |
| 	mpActiveView = pView;
 | |
| }
 | |
| 
 | |
| wxTextEditorView* wxTextEditorModel::GetActiveView()
 | |
| {
 | |
| 	return mpActiveView;
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::SetRowsPerPage( size_t n )
 | |
| {
 | |
| 	mRowsPerPage = n;
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::AddPin( TPinBase* pPin )
 | |
| {
 | |
| 	// FIXME:: binary search should be used
 | |
| 
 | |
| 	size_t beforePin = FindNextPinFrom( pPin->mRow );
 | |
| 
 | |
| 	if ( beforePin != NPOS )
 | |
| 	{
 | |
| 		// pins in the same row are ordered in the
 | |
| 		// descending order of their type-codes
 | |
| 
 | |
| 		while( beforePin < mPins.size() &&
 | |
| 			   mPins[beforePin]->mRow == pPin->mRow &&
 | |
| 			   mPins[beforePin]->mTypeCode < pPin->mTypeCode )
 | |
| 
 | |
| 			   ++beforePin;
 | |
| 
 | |
| 		if ( beforePin < mPins.size() )
 | |
| 			
 | |
| 			mPins.insert( &mPins[beforePin], pPin );
 | |
| 		else
 | |
| 			mPins.push_back( pPin );
 | |
| 	}
 | |
| 	else
 | |
| 		mPins.push_back( pPin );
 | |
| }
 | |
| 
 | |
| PinListT& wxTextEditorModel::GetPins()
 | |
| {
 | |
| 	return mPins;
 | |
| }
 | |
| 
 | |
| size_t wxTextEditorModel::FindFirstPinInRange( size_t fromRow, size_t tillRow )
 | |
| {
 | |
| 	// FIXME:: pefrom binary search instead
 | |
| 
 | |
| 	for( size_t i = 0; i != mPins.size(); ++i )
 | |
| 	{
 | |
| 		TPinBase& pin = *mPins[i];
 | |
| 
 | |
| 		if ( pin.mRow >= tillRow ) return NPOS;
 | |
| 
 | |
| 		if ( pin.mRow >= fromRow )
 | |
| 
 | |
| 			return i;
 | |
| 	}
 | |
| 
 | |
| 	return NPOS;
 | |
| }
 | |
| 
 | |
| size_t wxTextEditorModel::FindNextPinFrom( size_t fromRow )
 | |
| {
 | |
| 	// FIXME:: pefrom binary search instead
 | |
| 
 | |
| 	for( size_t i = 0; i != mPins.size(); ++i )
 | |
| 	{
 | |
| 		TPinBase& pin = *mPins[i];
 | |
| 
 | |
| 		if ( pin.mRow >= fromRow )
 | |
| 
 | |
| 			return i;
 | |
| 	}
 | |
| 
 | |
| 	return NPOS;
 | |
| 
 | |
| }
 | |
| 
 | |
| size_t wxTextEditorModel::FindPreviousPinFrom( size_t fromRow )
 | |
| {
 | |
| 	// FIXME:: pefrom binary search instead
 | |
| 
 | |
| 	if ( mPins.size() == 0 ) return NPOS;
 | |
| 
 | |
| 	size_t i = mPins.size() - 1;
 | |
| 
 | |
| 	for(;;)
 | |
| 	{
 | |
| 		TPinBase& pin = *mPins[i];
 | |
| 
 | |
| 		if ( pin.mRow <= fromRow )
 | |
| 
 | |
| 			return i;
 | |
| 
 | |
| 		if ( i == 0 ) break;
 | |
| 
 | |
| 		--i;
 | |
| 	}
 | |
| 
 | |
| 	return NPOS;
 | |
| }
 | |
| 
 | |
| size_t wxTextEditorModel::GetPinNoAt( size_t row, int pinTypeCode )
 | |
| {
 | |
| 	size_t curPin = FindNextPinFrom( row );
 | |
| 
 | |
| 	while( curPin != NPOS )
 | |
| 	{
 | |
| 		TPinBase& pin = *mPins[curPin];
 | |
| 
 | |
| 		if ( pin.mRow > row ) return NPOS;
 | |
| 
 | |
| 		if ( pin.mTypeCode == pinTypeCode ) return curPin;
 | |
| 
 | |
| 		++curPin;
 | |
| 
 | |
| 		if ( curPin == mPins.size() ) return NPOS;
 | |
| 	}
 | |
| 
 | |
| 	return NPOS;
 | |
| }
 | |
| 
 | |
| TPinBase* wxTextEditorModel::GetPinAt( size_t row, int pinTypeCode )
 | |
| {
 | |
| 	size_t pinNo = GetPinNoAt( row, pinTypeCode );
 | |
| 
 | |
| 	return ( pinNo == NPOS ) ? NULL : mPins[pinNo];
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::RemovePinAt( size_t row, int pinTypeCode )
 | |
| {
 | |
| 	size_t pinNo = GetPinNoAt( row, pinTypeCode );
 | |
| 
 | |
| 	if ( pinNo != NPOS )
 | |
| 
 | |
| 		mPins.erase( &mPins[pinNo] );
 | |
| }
 | |
| 
 | |
| void wxTextEditorModel::AddChangeListener( TTextChangeListenerBase* pListener )
 | |
| {
 | |
| 	mChangeListeners.push_back( pListener );
 | |
| }
 | |
| 
 | |
| /***** Implementation for class wxTextEditorView *****/
 | |
| 
 | |
| BEGIN_EVENT_TABLE( wxTextEditorView, wxScrolledWindow )
 | |
| 
 | |
| 	EVT_SIZE  ( wxTextEditorView::OnSize   )
 | |
| #if (( wxVERSION_NUMBER < 2100 ) || (( wxVERSION_NUMBER == 2100 ) && (wxBETA_NUMBER <= 4)))
 | |
| 	EVT_SCROLL( wxTextEditorView::OnScroll )
 | |
| #else
 | |
| 	EVT_SCROLLWIN( wxTextEditorView::OnScroll )
 | |
| #endif 
 | |
| 	EVT_PAINT ( wxTextEditorView::OnPaint  )
 | |
| 
 | |
| 	EVT_LEFT_DOWN  ( wxTextEditorView::OnLButtonDown )
 | |
| 	EVT_LEFT_UP    ( wxTextEditorView::OnLButtonUp   )
 | |
| 	EVT_MOTION     ( wxTextEditorView::OnMotion      )
 | |
| 	EVT_LEFT_DCLICK( wxTextEditorView::OnDblClick    )
 | |
| 
 | |
| 	EVT_SET_FOCUS  ( wxTextEditorView::OnSetFocus  )
 | |
| 	EVT_KILL_FOCUS ( wxTextEditorView::OnKillFocus )
 | |
| 
 | |
| 	EVT_CHAR( wxTextEditorView::OnChar )
 | |
| 	EVT_KEY_DOWN( wxTextEditorView::OnKeyDown )
 | |
| 
 | |
| 	EVT_ERASE_BACKGROUND( wxTextEditorView::OnEraseBackground )
 | |
| 
 | |
| 
 | |
| END_EVENT_TABLE()
 | |
| 
 | |
| TCursorTimer* wxTextEditorView::mpTimer = new TCursorTimer();
 | |
| 
 | |
| wxTextEditorView::wxTextEditorView( wxWindow* parent, 
 | |
| 									wxWindowID id, 
 | |
| 									wxTextEditorModel* pModel,
 | |
| 									int wndStyle,
 | |
| 									bool ownsModel )
 | |
| 
 | |
| 	: wxScrolledWindow( parent, id, wxPoint(32768,32768), wxSize(0,0),
 | |
| 						wxHSCROLL | wxVSCROLL | wndStyle
 | |
| 	                  ),
 | |
| 	  mPagePos( 0,0 ),
 | |
| 	  mDragStarted( FALSE ),
 | |
| 	  mpDraggedText( NULL ),
 | |
| 	  mAdjustScrollPending( FALSE ),
 | |
| 	  mLTMode( FALSE ),
 | |
| 	  mMaxColumns( 500 ),
 | |
| 
 | |
| 	  mScrollingOn( TRUE ),
 | |
| 	  mCursorOn  ( TRUE ),
 | |
| 	  mOwnsModel ( ownsModel ),
 | |
| 
 | |
| 	  mLastRowsTotal( (size_t)(-1) )
 | |
| {
 | |
| 	SetModel( pModel );
 | |
| 
 | |
| 	SetTextDefaults();
 | |
| 
 | |
| 	SetSourcePainter( new SourcePainter() );
 | |
| 
 | |
| 	mCashedIter.mPos = TPosition( (size_t)(-1), 0 );
 | |
| 
 | |
| 	// default
 | |
| 	AddPinPainter( new TBookmarkPainter() );
 | |
| }
 | |
| 
 | |
| wxTextEditorView::~wxTextEditorView()
 | |
| {
 | |
| 	if ( mpTimer->GetView() == this && 
 | |
| 		 mCursorOn && !mLTMode )
 | |
| 	{
 | |
| 		mpTimer->SetView( NULL );
 | |
| 		mpTimer->HideCursor( TRUE );
 | |
| 	}
 | |
| 
 | |
| 	if ( mOwnsModel && mpModel )
 | |
| 
 | |
| 		delete mpModel;
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::SetTextDefaults()
 | |
| {
 | |
| 	mLeftMargin   = 22;
 | |
| 	mRightMargin  = 0;
 | |
| 	mTopMargin    = 0;
 | |
| 	mBottomMargin = 0;
 | |
| 
 | |
| 	mCharDim.x = -1; // not detected yet
 | |
| 	mCharDim.y = -1;
 | |
| 
 | |
| 	mNormalTextCol       = *wxBLACK;
 | |
| 	mIndentifierTextCol  = *wxBLUE;
 | |
| 	mReservedWordTextCol = *wxRED;
 | |
| 	mCommentTextCol      = wxColour( 0,128,128 );
 | |
| 
 | |
| 	mNormalBkCol    = wxColour(255,255,255);//*wxWHITE;//wxColour( 128,220,128 );
 | |
| 	mSelectionFgCol = wxColour(255,255,255);//*wxWHITE;
 | |
| 	mSelectionBkCol = wxColour( 0,0,128 );
 | |
| 
 | |
| 	mNormalBkBrush   = wxBrush( mNormalBkCol,    wxSOLID );
 | |
| 	mSelectedBkBrush = wxBrush( mSelectionBkCol, wxSOLID );
 | |
| 
 | |
| #if defined(__WXMSW__) || defined(__WINDOWS__)
 | |
|     mFont.SetFaceName("Fixedsys");
 | |
|     mFont.SetStyle(40);
 | |
|     mFont.SetWeight(40);
 | |
|     mFont.SetPointSize( 11);
 | |
| #else
 | |
|     //mFont.SetFamily( wxSWISS );
 | |
| 	mFont = wxSystemSettings::GetSystemFont(wxSYS_OEM_FIXED_FONT);
 | |
| #endif
 | |
| 
 | |
| 
 | |
| #if defined(__WXMSW__) || defined(__WINDOWS__)
 | |
|     mFont.RealizeResource();
 | |
| #endif
 | |
| 
 | |
| 	// reduce flicker un wxGtk
 | |
| 	SetBackgroundColour( mNormalBkCol );
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::SetColours( const wxColour& normalBkCol,
 | |
| 								   const wxColour& selectedBkCol,
 | |
| 								   const wxColour& selectedTextCol )
 | |
| {
 | |
| 	mNormalBkCol    = normalBkCol;
 | |
| 	mSelectionFgCol = selectedTextCol;
 | |
| 	mSelectionBkCol = selectedBkCol;
 | |
| 
 | |
| 	mNormalBkBrush   = wxBrush( mNormalBkCol,    wxSOLID );
 | |
| 	mSelectedBkBrush = wxBrush( mSelectionBkCol, wxSOLID );
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::SetHeighlightingColours( const wxColour& normalTextCol,
 | |
| 											    const wxColour& identifierTextCol,
 | |
| 											    const wxColour& reservedWordTextCol,
 | |
| 											    const wxColour& commentTextCol )
 | |
| {
 | |
| 	mNormalTextCol       = normalTextCol;
 | |
| 	mIndentifierTextCol  = identifierTextCol;
 | |
| 	mReservedWordTextCol = reservedWordTextCol;
 | |
| 	mCommentTextCol      = commentTextCol;
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::SetMargins( int top, int left, int bottom, int right )
 | |
| {
 | |
| 	mLeftMargin   = left;
 | |
| 	mRightMargin  = right;
 | |
| 	mTopMargin    = top;
 | |
| 	mBottomMargin = bottom;
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::RecalcPagingInfo()
 | |
| {
 | |
| 	bool firstRefresh = mCharDim.x == -1;
 | |
| 
 | |
| 	if ( firstRefresh )
 | |
| 
 | |
| 		ObtainFontProperties();
 | |
| 
 | |
| 	int w = 0, h = 0;
 | |
| 	GetClientSize( &w, &h );
 | |
| 
 | |
| 	w -= mLeftMargin + mRightMargin;
 | |
| 	h -= mTopMargin  + mBottomMargin;
 | |
| 
 | |
| 	mColsPerPage = ( ( w / mCharDim.x ) +
 | |
| 				   ( ( w % mCharDim.x ) ? 0 : 0 ) );
 | |
| 
 | |
| 
 | |
| 	mRowsPerPage = ( ( h / mCharDim.y ) +
 | |
| 				   ( ( h % mCharDim.y ) ? 0 : 0 ) );
 | |
| 
 | |
| 	if ( mpModel->GetActiveView() == this )
 | |
| 
 | |
| 		mpModel->SetRowsPerPage( mRowsPerPage );
 | |
| 
 | |
| 	if ( firstRefresh ) 
 | |
| 	{
 | |
| 		// scrolling should not happen at DC-level
 | |
| 		EnableScrolling( FALSE, FALSE );
 | |
| 
 | |
| 		if ( mScrollingOn )
 | |
| 
 | |
| 			SetScrollbars( mCharDim.x, mCharDim.y,
 | |
| 						   mMaxColumns,
 | |
| 						   mpModel->GetTotalRowCount(),
 | |
| 						   mPagePos.mCol,
 | |
| 						   mPagePos.mRow,
 | |
| 						   TRUE
 | |
| 						 );
 | |
| 	}
 | |
| 
 | |
| 	PositionCursor();
 | |
| }
 | |
| 
 | |
| #if (( wxVERSION_NUMBER < 2100 ) || (( wxVERSION_NUMBER == 2100 ) && (wxBETA_NUMBER <= 4)))
 | |
|  // this changed in ver 2.1
 | |
| void wxTextEditorView::OnScroll( wxScrollEvent& event )
 | |
| #else
 | |
| void wxTextEditorView::OnScroll( wxScrollWinEvent& event )
 | |
| #endif
 | |
| {
 | |
| 	if ( !mScrollingOn ) return;
 | |
| 
 | |
| 	// overriden implementation of wxScrolledWindow::OnScroll,
 | |
| 	// to reduce flicker on wxGtk, by using wxClientDC 
 | |
| 	// instead of Refresh()
 | |
| 
 | |
| 	int orient = event.GetOrientation();
 | |
| 
 | |
| 	int nScrollInc = CalcScrollInc(event);
 | |
| 	if (nScrollInc == 0) return;
 | |
| 
 | |
| 	if (orient == wxHORIZONTAL)
 | |
| 	{
 | |
| 		int newPos = m_xScrollPosition + nScrollInc;
 | |
| 		SetScrollPos(wxHORIZONTAL, newPos, TRUE );
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		int newPos = m_yScrollPosition + nScrollInc;
 | |
| 		SetScrollPos(wxVERTICAL, newPos, TRUE );
 | |
| 	}
 | |
| 
 | |
| 	if (orient == wxHORIZONTAL)
 | |
| 	{
 | |
| 		m_xScrollPosition += nScrollInc;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		m_yScrollPosition += nScrollInc;
 | |
| 	}
 | |
| 
 | |
| 	int x,y;
 | |
| 	ViewStart( &x, &y );
 | |
| 
 | |
| 	mPagePos.mRow = y;
 | |
| 	mPagePos.mCol = x;
 | |
| 
 | |
| 	PositionCursor();
 | |
| 
 | |
| 	if ( mAdjustScrollPending )
 | |
| 	{
 | |
| 		mLastRowsTotal = mpModel->GetTotalRowCount();
 | |
| 		SetScrollbars( mCharDim.x, mCharDim.y,
 | |
| 					   mMaxColumns,   // FOR NOW:: maximal line-length not calculated
 | |
| 					   mLastRowsTotal,
 | |
| 					   mPagePos.mCol,
 | |
| 					   mPagePos.mRow,
 | |
| 					   TRUE
 | |
| 					 );
 | |
| 
 | |
| 		mLastViewStart = mPagePos;
 | |
| 
 | |
| 		mAdjustScrollPending = FALSE;
 | |
| 
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	wxClientDC dc( this );
 | |
| 
 | |
| 	mFullRefreshPending = TRUE;
 | |
| 
 | |
| 	PaintRows( mPagePos.mRow, mPagePos.mRow + mRowsPerPage, dc );
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::OnPaint( wxPaintEvent& event )
 | |
| {
 | |
| 	//wxScrolledWindow::OnPaint( event );
 | |
| 	if ( mCharDim.x == -1 ) ObtainFontProperties();
 | |
| 
 | |
| 	wxPaintDC dc( this );
 | |
| 
 | |
| 	mFullRefreshPending = TRUE;
 | |
| 
 | |
| 	PaintRows( mPagePos.mRow, mPagePos.mRow + mRowsPerPage, dc );
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::OnSize( wxSizeEvent& event )
 | |
| {
 | |
| 	RecalcPagingInfo();
 | |
| 
 | |
| 	SyncScrollbars();
 | |
| 
 | |
| 	event.Skip();
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::OnEraseBackground( wxEraseEvent& event )
 | |
| {
 | |
| #if 0
 | |
| 	int w = 0, h = 0;
 | |
| 
 | |
| 	GetClientSize( &w, &h );
 | |
| 	
 | |
| 	wxPaintDC dc( this );
 | |
| 
 | |
| 	dc.SetPen( *wxTRANSPARENT_PEN );
 | |
| 	dc.SetBrush( *wxWHITE_BRUSH );
 | |
| 	dc.DrawRectangle( 0,0, w,h );
 | |
| #endif
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::OnLButtonDown( wxMouseEvent& event )
 | |
| {
 | |
| 	if ( mDragStarted ) return;
 | |
| 
 | |
| 	mDragStarted = TRUE;
 | |
| 
 | |
| 	TPosition textPos;
 | |
| 	PixelsToTextPos( event.m_x, event.m_y, textPos );
 | |
| 
 | |
| 	mpModel->SetSelectionEditMode( FALSE );
 | |
| 
 | |
| 	mpModel->OnMoveToPosition( textPos );
 | |
| 
 | |
| 	mpModel->SetSelectionEditMode( TRUE );
 | |
| 
 | |
| 	SetFocus();
 | |
| 
 | |
| 	CaptureMouse();
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::OnLButtonUp( wxMouseEvent& event )
 | |
| {
 | |
| 	if ( mDragStarted )
 | |
| 	{
 | |
| 		OnMotion( event ); // simulate last motion event
 | |
| 
 | |
| 		mpModel->SetSelectionEditMode( FALSE );
 | |
| 
 | |
| 		ReleaseMouse();
 | |
| 		mDragStarted = FALSE;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::OnMotion( wxMouseEvent& event )
 | |
| {
 | |
| 	if ( mDragStarted )
 | |
| 	{
 | |
| 		TPosition textPos;
 | |
| 
 | |
| 		if ( event.m_y < 0 && mpModel->GetCursor().mRow == 0 )
 | |
| 		
 | |
| 			event.m_y = 0;
 | |
| 
 | |
| 		PixelsToTextPos( event.m_x, event.m_y, textPos );
 | |
| 
 | |
| 		mpModel->OnMoveToPosition( textPos );
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::OnDblClick( wxMouseEvent& event )
 | |
| {
 | |
| 	event.Skip();
 | |
| 	mpModel->OnSelectWord();
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::OnSetFocus( wxFocusEvent& event )
 | |
| {
 | |
| 	if ( !mLTMode && mCursorOn )
 | |
| 	{
 | |
| 		mpTimer->SetView( this );
 | |
| 		mpTimer->ShowCursor( TRUE );
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::OnKillFocus( wxFocusEvent& event )
 | |
| {
 | |
| 	if ( !mLTMode && mCursorOn )
 | |
| 	{
 | |
| 		mpTimer->HideCursor( TRUE );
 | |
| 		mpTimer->SetView( NULL );
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::HoldCursor( bool hold )
 | |
| {
 | |
| 	if ( mLTMode || !mCursorOn ) return;
 | |
| 
 | |
| 	if ( !hold )
 | |
| 	{
 | |
| 		if ( wxWindow::FindFocus() != this )
 | |
| 		{	
 | |
| 			mpTimer->HideCursor();
 | |
| 			mpTimer->SetView( NULL );
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		mpTimer->SetView( this );
 | |
| 		mpTimer->ShowCursor();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::OnKeyDown( wxKeyEvent& event )
 | |
| {
 | |
| 	// FOR NOW:: hard-coded key-bindings
 | |
| 
 | |
| 	mpModel->SetSelectionEditMode( event.ShiftDown() );
 | |
| 
 | |
| 	if ( event.ControlDown() )
 | |
| 	{
 | |
| 		if ( event.m_keyCode == WXK_LEFT ) 
 | |
| 		
 | |
| 			mpModel->OnWordLeft();
 | |
| 		else
 | |
| 		if ( event.m_keyCode == WXK_RIGHT ) 
 | |
| 		
 | |
| 			mpModel->OnWordRight();
 | |
| 		else
 | |
| 		if ( event.m_keyCode == WXK_UP ) 
 | |
| 		
 | |
| 			mpModel->OnSlideUp();
 | |
| 		else
 | |
| 		if ( event.m_keyCode == WXK_DOWN ) 
 | |
| 		
 | |
| 			mpModel->OnSlideDown();
 | |
| 		else
 | |
| 		if ( event.m_keyCode == WXK_HOME ) 
 | |
| 		
 | |
| 			mpModel->OnStartOfText();
 | |
| 		else
 | |
| 		if ( event.m_keyCode == WXK_END ) 
 | |
| 		
 | |
| 			mpModel->OnEndOfText();
 | |
| 		else
 | |
| 		if ( event.m_keyCode == WXK_INSERT )
 | |
| 
 | |
| 			mpModel->OnCopy();
 | |
| 		else
 | |
| 			event.Skip();
 | |
| 
 | |
| 		/*
 | |
| 		else
 | |
| 		if ( event.m_keyCode == WXK_NEXT ) 
 | |
| 		
 | |
| 			mpModel->();
 | |
| 		else
 | |
| 		if ( event.m_keyCode == WXK_PRIOR ) 
 | |
| 		
 | |
| 			mpModel->();
 | |
| 		*/
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		if ( event.m_keyCode == WXK_LEFT ) 
 | |
| 		
 | |
| 			mpModel->OnMoveLeft();
 | |
| 		else
 | |
| 		if ( event.m_keyCode == WXK_RIGHT ) 
 | |
| 		
 | |
| 			mpModel->OnMoveRight();
 | |
| 		else
 | |
| 		if ( event.m_keyCode == WXK_UP ) 
 | |
| 		
 | |
| 			mpModel->OnMoveUp();
 | |
| 		else
 | |
| 		if ( event.m_keyCode == WXK_DOWN ) 
 | |
| 		
 | |
| 			mpModel->OnMoveDown();
 | |
| 		else
 | |
| 		if ( event.m_keyCode == WXK_HOME ) 
 | |
| 		
 | |
| 			mpModel->OnStartOfLine();
 | |
| 		else
 | |
| 		if ( event.m_keyCode == WXK_END ) 
 | |
| 		
 | |
| 			mpModel->OnEndOfLine();
 | |
| 		else
 | |
| 		if ( event.m_keyCode == WXK_NEXT ) 
 | |
| 		
 | |
| 			mpModel->OnPageDown();
 | |
| 		else
 | |
| 		if ( event.m_keyCode == WXK_PRIOR ) 
 | |
| 		
 | |
| 			mpModel->OnPageUp();
 | |
| 		else
 | |
| 		if ( event.m_keyCode == WXK_DELETE )
 | |
| 
 | |
| 			mpModel->OnDelete();
 | |
| 		else
 | |
| 		if ( event.m_keyCode ==  WXK_INSERT && event.ShiftDown() )
 | |
| 		
 | |
| 			mpModel->OnPaste();
 | |
| 		else
 | |
| 			event.Skip();
 | |
| 	}	
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::OnChar( wxKeyEvent& event )
 | |
| {
 | |
| 	if ( event.ControlDown() )
 | |
| 	{
 | |
| 		if ( event.m_keyCode == 'y' )
 | |
| 
 | |
| 			mpModel->OnDeleteLine();
 | |
| 		else
 | |
| 		if ( event.m_keyCode == 'v' )
 | |
| 
 | |
| 			mpModel->OnPaste();
 | |
| 		else
 | |
| 		if ( event.m_keyCode == 'c' )
 | |
| 
 | |
| 			mpModel->OnCopy();
 | |
| 		else
 | |
| 		if ( event.m_keyCode == 'z' )
 | |
| 
 | |
| 			mpModel->OnUndo();
 | |
| 		else
 | |
| 		if ( event.m_keyCode == 'a' )
 | |
| 
 | |
| 			mpModel->OnRedo();
 | |
| 		else
 | |
| 			event.Skip();
 | |
| 	}
 | |
| 	else
 | |
| 	if ( event.AltDown() )
 | |
| 	{
 | |
| 		if ( event.m_keyCode == WXK_BACK ) 
 | |
| 
 | |
| 			mpModel->OnUndo();
 | |
| 		else
 | |
| 			event.Skip();
 | |
| 	}
 | |
| 	else
 | |
| 	if ( event.m_keyCode ==  WXK_BACK )
 | |
| 	
 | |
| 		mpModel->OnDeleteBack();
 | |
| 	else
 | |
| 	if ( event.m_keyCode == WXK_TAB && event.ShiftDown() )
 | |
| 
 | |
| 		mpModel->OnShiftSelectionIndent( TRUE );
 | |
| 	else
 | |
| 	{
 | |
| 		if ( !mpModel->SelectionIsEmpty() && event.m_keyCode == WXK_TAB )
 | |
| 
 | |
| 			mpModel->OnShiftSelectionIndent( FALSE );
 | |
| 		else
 | |
| 			mpModel->OnInsertChar( event.m_keyCode );
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::SetModel( wxTextEditorModel* pModel )
 | |
| {
 | |
| 	mpModel = pModel;
 | |
| 	mSelectionStart = pModel->GetStartOfSelection();
 | |
| 	mSelectionEnd   = pModel->GetEndOfSelection();
 | |
| 	mCursorPos      = pModel->GetCursor();
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::SetSourcePainter( SourcePainter* pPainter )
 | |
| {
 | |
| 	mpPainter = pPainter;
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::AddPinPainter( TPinPainterBase* pPainter )
 | |
| {
 | |
| 	mPinPainters.push_back( pPainter );
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::SetDefaultFont( const wxFont& font )
 | |
| {
 | |
| 	mFont = font;
 | |
| 
 | |
| #if defined(__WXMSW__) || defined(__WINDOWS__)
 | |
|     mFont.RealizeResource();
 | |
| #endif
 | |
| 
 | |
| 	mCharDim.x = -1;
 | |
| 	mCharDim.y = -1;
 | |
| 
 | |
| 	RecalcPagingInfo();
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::SetRowsPerPage( size_t n )
 | |
| {
 | |
| 	mpModel->SetRowsPerPage( n );
 | |
| 
 | |
| 	mRowsPerPage = n;
 | |
| 	SyncScrollbars();
 | |
| 	PositionCursor();
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::SetMaxColumns( size_t n )
 | |
| {
 | |
| 	mMaxColumns = n;
 | |
| 
 | |
| 	SyncScrollbars();
 | |
| 	PositionCursor();
 | |
| }
 | |
| 
 | |
| wxFont& wxTextEditorView::GetDefaultFont()
 | |
| {
 | |
| 	return mFont;
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::SetLineTrackingMode( bool on, const wxColour& col )
 | |
| {
 | |
| 	mLTColour = col;
 | |
| 	mLTMode   = on;
 | |
| 
 | |
| 	if ( mpTimer->GetView() == this )
 | |
| 
 | |
| 		mpTimer->HideCursor();
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::EnableCursor( bool enable )
 | |
| {
 | |
| 	mCursorOn = enable;
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::EnableScrollbars( bool enable )
 | |
| {
 | |
| 	mScrollingOn = enable;
 | |
| }
 | |
| 
 | |
| bool wxTextEditorView::IsActiveView()
 | |
| {
 | |
| 	return this == mpModel->GetActiveView();
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::PositionCursor()
 | |
| {
 | |
| 	if ( !IsActiveView() || 
 | |
| 		 mLTMode || !mCursorOn ) return;
 | |
| 
 | |
| 	mpTimer->HideCursor();
 | |
| 
 | |
| 	TextPosToScreenPos( mpModel->GetCursor(), mCursorScrPos );
 | |
| 
 | |
| 	mpTimer->ShowCursor();
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::PixelsToScrPos( int x, int y, int& scrRow, int& scrCol )
 | |
| {
 | |
| 	x -= mLeftMargin;
 | |
| 	y -= mTopMargin;
 | |
| 
 | |
| 	//if ( x < 0 ) x = 0; // FOR NOW:: horizontal auto-scroll disabled
 | |
| 
 | |
| 	scrCol = x / mCharDim.x;
 | |
| 	scrRow = y / mCharDim.y;
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::PixelsToTextPos( int x, int y, TPosition& textPos )
 | |
| {
 | |
| 	int scrRow = 0, scrCol = 0;
 | |
| 	PixelsToScrPos( x, y, scrRow, scrCol );
 | |
| 
 | |
| 	if ( scrRow + (int)mPagePos.mRow < 0 )
 | |
| 
 | |
| 		scrRow = -(int)mPagePos.mRow;
 | |
| 
 | |
| 	if ( scrCol + (int)mPagePos.mCol < 0 )
 | |
| 
 | |
| 		scrCol = -(int)mPagePos.mCol;
 | |
| 
 | |
| 	ScreenPosToTextPos( TPosition( scrRow, scrCol ), textPos );
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::ScreenPosToPixels( const TPosition& scrPos, int& x, int& y )
 | |
| {
 | |
| 	x = mLeftMargin + scrPos.mCol * mCharDim.x;
 | |
| 	y = mTopMargin  + scrPos.mRow * mCharDim.y;
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::TextPosToScreenPos( const TPosition& txtPos, TPosition& scrPos )
 | |
| {
 | |
| 	TTextIterator iter;
 | |
| 
 | |
| 	if ( txtPos.mRow != mCashedIter.mPos.mRow )
 | |
| 	{
 | |
| 		iter = mpModel->CreateIterator( txtPos );
 | |
| 		mCashedIter = iter;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		iter = mCashedIter;
 | |
| 		iter.mPos.mCol = txtPos.mCol;
 | |
| 	}
 | |
| 
 | |
| 	iter.ToStartOfLine();
 | |
| 
 | |
| 	size_t scrCol = 0;
 | |
| 	size_t txtCol = 0;
 | |
| 
 | |
| 	while( !iter.IsEol() && txtCol < txtPos.mCol )
 | |
| 	{
 | |
| 		if ( iter.GetChar() == '\t' )
 | |
| 		{
 | |
| 			size_t spacing = ( (scrCol / mpModel->mTabSize) + 1 ) * mpModel->mTabSize - scrCol;
 | |
| 
 | |
| 			scrCol += spacing;
 | |
| 		}
 | |
| 		else
 | |
| 			++scrCol;
 | |
| 
 | |
| 		++txtCol;
 | |
| 		iter.NextChar();
 | |
| 	}
 | |
| 
 | |
| 	TPosition actualPos = iter.GetPosition();
 | |
| 
 | |
| 	scrCol += txtPos.mCol - txtCol;
 | |
| 
 | |
| 	scrPos.mRow = actualPos.mRow - mPagePos.mRow;
 | |
| 	scrPos.mCol = scrCol - mPagePos.mCol;
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::ScreenPosToTextPos( const TPosition& scrPos, TPosition& txtPos )
 | |
| {
 | |
| 	TPosition absScrPos( scrPos.mRow + mPagePos.mRow, scrPos.mCol + mPagePos.mCol );
 | |
| 
 | |
| 	TTextIterator iter = mpModel->CreateIterator( TPosition( absScrPos.mRow, 0 ) );
 | |
| 
 | |
| 	size_t scrCol = 0;
 | |
| 	size_t txtCol = 0;
 | |
| 
 | |
| 	// iterate over all possible on-screen positions, and find one which matches "absScrPos"
 | |
| 
 | |
| 	while( !iter.IsEol() && scrCol < absScrPos.mCol )
 | |
| 	{
 | |
| 		if ( iter.GetChar() == '\t' )
 | |
| 		{
 | |
| 			size_t spacing = ( (scrCol / mpModel->mTabSize) + 1 ) * mpModel->mTabSize - scrCol;
 | |
| 
 | |
| 			scrCol += spacing;
 | |
| 		}
 | |
| 		else
 | |
| 			++scrCol;
 | |
| 
 | |
| 		++txtCol;
 | |
| 		iter.NextChar();
 | |
| 	}
 | |
| 
 | |
| 	TPosition actualPos = iter.GetPosition();
 | |
| 
 | |
| 	if ( scrCol == absScrPos.mCol )
 | |
| 	{
 | |
| 		txtPos = actualPos;
 | |
| 		return;
 | |
| 	}
 | |
| 	else
 | |
| 	if ( scrCol < absScrPos.mCol )
 | |
| 	{
 | |
| 		// the absScrPos points past the eol
 | |
| 
 | |
| 		txtPos = actualPos;
 | |
| 		txtPos.mCol += absScrPos.mCol - scrCol;
 | |
| 	}
 | |
| 	else
 | |
| 	if ( scrCol > absScrPos.mCol )
 | |
| 	{
 | |
| 		// there should have been a '\t' char, which made us jump too far forward
 | |
| 
 | |
| 		txtPos = actualPos;
 | |
| 		--txtPos.mCol;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| bool wxTextEditorView::IsClipboardCmd( wxKeyEvent& key )
 | |
| {
 | |
| 	if ( key.ControlDown() && key.m_keyCode == WXK_CONTROL )
 | |
| 
 | |
| 		return TRUE;
 | |
| 
 | |
| 	if ( key.ShiftDown() && key.m_keyCode == WXK_SHIFT )
 | |
| 
 | |
| 		return TRUE;
 | |
| 
 | |
| 	if ( key.ControlDown() )
 | |
| 	{
 | |
| 		return ( key.m_keyCode == 'C' ||
 | |
| 				 key.m_keyCode == 'c' ||
 | |
| 				 key.m_keyCode == WXK_INSERT );
 | |
| 	}
 | |
| 
 | |
| 	return FALSE;
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::ObtainFontProperties()
 | |
| {
 | |
| 	wxClientDC dc(this);
 | |
| 	dc.SetFont( mFont );
 | |
| 
 | |
| 	long w,h;
 | |
| 
 | |
| 	dc.GetTextExtent( "X", &w, &h );
 | |
| 
 | |
| 	mCharDim.x = w;
 | |
| 	mCharDim.y = h;
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::SyncViewPortPosition()
 | |
| {
 | |
| 
 | |
| 	TPosition pos = mpModel->GetCursor();
 | |
| 
 | |
| 	TextPosToScreenPos( pos, pos );
 | |
| 	pos.mRow += mPagePos.mRow;
 | |
| 	pos.mCol += mPagePos.mCol;
 | |
| 
 | |
| 	if ( pos.mRow < mPagePos.mRow )
 | |
| 	{
 | |
| 		mPagePos.mRow = pos.mRow;
 | |
| 		mFullRefreshPending = TRUE;
 | |
| 	}
 | |
| 	else
 | |
| 	if ( pos.mRow >= mPagePos.mRow + mRowsPerPage && mRowsPerPage != 0 )
 | |
| 	{
 | |
| 		mPagePos.mRow = pos.mRow - mRowsPerPage + 1;
 | |
| 		mFullRefreshPending = TRUE;
 | |
| 	}
 | |
| 
 | |
| 	if ( pos.mCol < mPagePos.mCol )
 | |
| 	{
 | |
| 		mPagePos.mCol = pos.mCol;
 | |
| 		mFullRefreshPending = TRUE;
 | |
| 	}
 | |
| 	else
 | |
| 	if ( pos.mCol >= mPagePos.mCol + mColsPerPage )
 | |
| 	{
 | |
| 		mPagePos.mCol = pos.mCol - mColsPerPage + 1;
 | |
| 		mFullRefreshPending = TRUE;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::SyncScrollbars()
 | |
| {
 | |
| 	if ( !mScrollingOn ) return;
 | |
| 
 | |
| 	size_t nRows = mpModel->GetTotalRowCount();
 | |
| 
 | |
| #if !defined(__WINDOWS__)
 | |
| 
 | |
| 	if ( mLastViewStart == mPagePos )
 | |
| 	{
 | |
| 		if ( mLastRowsTotal != nRows )
 | |
| 
 | |
| 			mAdjustScrollPending = TRUE;
 | |
| 
 | |
| 		 return;
 | |
| 	}
 | |
| #else
 | |
| 	if ( mLastViewStart == mPagePos &&
 | |
| 		 mLastRowsTotal == nRows )
 | |
| 
 | |
| 		 return;
 | |
| #endif
 | |
| 	SetScrollbars( mCharDim.x, mCharDim.y,
 | |
| 				   mMaxColumns,
 | |
| 				   nRows,
 | |
| 				   mPagePos.mCol,
 | |
| 				   mPagePos.mRow,
 | |
| 				   TRUE
 | |
| 				 );
 | |
| 
 | |
| 	mLastViewStart = mPagePos;
 | |
| 	mLastRowsTotal = nRows;
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::ScrollView( int rows, int cols )
 | |
| {
 | |
| 	int pageRow = (int)mPagePos.mRow; 
 | |
| 	int pageCol = (int)mPagePos.mCol;
 | |
| 
 | |
| 	if ( pageRow + rows < 0 ) 
 | |
| 		pageRow = 0;
 | |
| 	else
 | |
| 	if ( pageRow + rows > (int)mpModel->GetTotalRowCount() )
 | |
| 
 | |
| 		pageRow = mpModel->GetTotalRowCount();
 | |
| 	else
 | |
| 		pageRow = pageRow + rows;
 | |
| 
 | |
| 	mPagePos.mRow = (size_t)pageRow;
 | |
| 
 | |
| 	if ( pageCol + cols < 0 )
 | |
| 
 | |
| 		pageCol = 0;
 | |
| 	else
 | |
| 		pageCol = pageCol + cols;
 | |
| 
 | |
| 	mPagePos.mCol = pageCol;
 | |
| 
 | |
| 	mFullRefreshPending = TRUE;
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::OnModelChanged()
 | |
| {
 | |
| 	// invalidate pre-cached iterator
 | |
| 	mCashedIter.mPos = TPosition( (size_t)(-1), 0 );
 | |
| 
 | |
| 	SyncViewPortPosition();
 | |
| 
 | |
| 	if ( mLTMode ) mFullRefreshPending = TRUE;
 | |
| 
 | |
| 	if ( mpModel->mTextChanged && !mFullRefreshPending )
 | |
| 	{
 | |
| 		wxClientDC dc( this );
 | |
| 		PaintRows( mpModel->mChangedFromRow, mpModel->mChangedTillRow, dc );
 | |
| 	}
 | |
| 	else
 | |
| 	if ( mFullRefreshPending )
 | |
| 	{
 | |
| 		wxClientDC dc( this );
 | |
| 		PaintRows( mPagePos.mRow, mPagePos.mRow + mRowsPerPage, dc );
 | |
| 	}
 | |
| 
 | |
| 	if ( IsActiveView() )
 | |
| 	{	
 | |
| 		PositionCursor();
 | |
| 		SyncScrollbars();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::Activate()
 | |
| {
 | |
| 	mpModel->SetStartOfSelection( mSelectionStart );
 | |
| 	mpModel->SetEndOfSelection( mSelectionEnd );
 | |
| 	mpModel->SetCursor( mCursorPos );
 | |
| 
 | |
| 	mpModel->SetRowsPerPage( mRowsPerPage );
 | |
| 
 | |
| 	if ( !mLTMode && mCursorOn )
 | |
| 	{
 | |
| 		mpTimer->SetView( this );
 | |
| 		mpTimer->ShowCursor();
 | |
| 	}
 | |
| 
 | |
| 	mpModel->SetActiveView( this );
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::Deactivate()
 | |
| {
 | |
| 	mSelectionStart = mpModel->GetStartOfSelection();
 | |
| 	mSelectionEnd   = mpModel->GetEndOfSelection();
 | |
| 	mCursorPos      = mpModel->GetCursor();
 | |
| 
 | |
| 	if ( mpTimer->GetView() == this && 
 | |
| 		 !mLTMode && mCursorOn )
 | |
| 		
 | |
| 		mpTimer->HideCursor( TRUE );
 | |
| }
 | |
| 
 | |
| /*** protected methods ***/
 | |
| 
 | |
| char*  wxTextEditorView::mpLineBuffer    = NULL;
 | |
| size_t wxTextEditorView::mpLineBufferLen = 0;
 | |
| 
 | |
| char* wxTextEditorView::GetLineBuffer( size_t len )
 | |
| {
 | |
| 	if ( mpLineBuffer == NULL || mpLineBufferLen < len )
 | |
| 	{
 | |
| 		if ( !mpLineBuffer ) mpModel->FreeCharacters( mpLineBuffer );
 | |
| 
 | |
| 		mpLineBuffer = mpModel->AllocCharacters( len );
 | |
| 
 | |
| 		mpLineBufferLen = len;
 | |
| 	}
 | |
| 
 | |
| 	return mpLineBuffer;
 | |
| }
 | |
| 
 | |
| TPinPainterBase* wxTextEditorView::FindPainterForPin( TPinBase& pin )
 | |
| {
 | |
| 	int pinTc = pin.mTypeCode;
 | |
| 
 | |
| 	for( size_t i = 0; i != mPinPainters.size(); ++i )
 | |
| 
 | |
| 		if ( mPinPainters[i]->mPinTypeCode == pinTc )
 | |
| 
 | |
| 			return mPinPainters[i];
 | |
| 
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::PaintDecorations( size_t fromRow, 
 | |
| 										 size_t tillRow, 
 | |
| 										 wxDC& dc, TTextIterator& iter )
 | |
| {
 | |
| 	int dcY = ( fromRow - mPagePos.mRow ) * mCharDim.y + mTopMargin;
 | |
| 		
 | |
| 	size_t curPin = mpModel->FindFirstPinInRange( fromRow, tillRow );
 | |
| 
 | |
| 	PinListT& pins = mpModel->GetPins();
 | |
| 	TPinPainterBase* pPainter = NULL;
 | |
| 
 | |
| 	size_t prevRow = fromRow;
 | |
| 	int    prevY   = dcY;
 | |
| 
 | |
| 	wxPoint pos;
 | |
| 	wxSize  dim( mLeftMargin, mCharDim.y );
 | |
| 
 | |
| 	while( curPin != NPOS )
 | |
| 	{
 | |
| 		TPinBase& pin = *pins[curPin];
 | |
| 
 | |
| 		if ( pPainter == NULL ||
 | |
| 			 pPainter->mPinTypeCode != pin.mTypeCode )
 | |
| 
 | |
| 			 pPainter = FindPainterForPin( pin );
 | |
| 	
 | |
| 
 | |
| 		// only pins which have their painters can be "visualized"
 | |
| 
 | |
| 		if ( pPainter )
 | |
| 		{
 | |
| 			pos.x = 0;
 | |
| 			pos.y = ( pin.mRow - mPagePos.mRow )* mCharDim.y + mTopMargin;
 | |
| 
 | |
| 			if ( prevRow < pin.mRow )
 | |
| 			{
 | |
| 				// fill upper gap
 | |
| 
 | |
| 				dc.SetBrush( mNormalBkBrush );
 | |
| 				dc.SetPen( *wxTRANSPARENT_PEN );
 | |
| 				dc.DrawRectangle( 0, prevY, 
 | |
| 								  mLeftMargin + 1, 
 | |
| 								  mCharDim.y * ( pin.mRow - prevRow ) + 1 );
 | |
| 			}
 | |
| 
 | |
| 			pPainter->DrawPin( &pin, *this, dc, pos, dim );
 | |
| 
 | |
| 			prevRow = pin.mRow + 1;
 | |
| 			prevY = pos.y + mCharDim.y;
 | |
| 		}
 | |
| 
 | |
| 		++curPin;
 | |
| 
 | |
| 		if ( curPin >= pins.size() ||
 | |
| 			 pins[curPin]->mRow >= tillRow )
 | |
| 
 | |
| 			 break;
 | |
| 	}
 | |
| 
 | |
| 	// fill the reminder 
 | |
| 
 | |
| 	if ( prevRow < tillRow )
 | |
| 	{
 | |
| 		dc.SetBrush( mNormalBkBrush );
 | |
| 		dc.SetPen( *wxTRANSPARENT_PEN );
 | |
| 		dc.DrawRectangle( 0, prevY, 
 | |
| 						  mLeftMargin + 1, 
 | |
| 						  mCharDim.y * ( tillRow - prevRow ) + 1 );
 | |
| 	}
 | |
| 
 | |
| 	dc.SetPen( *wxTRANSPARENT_PEN );
 | |
| }
 | |
| 
 | |
| void wxTextEditorView::PaintRows( size_t fromRow, size_t tillRow, wxDC& dc )
 | |
| {
 | |
| 	// NOTE:: raws are painted from "fromRow" but not including "tillRow" - [fromRow,tillRow)
 | |
| 
 | |
| 	dc.SetPen( *wxTRANSPARENT_PEN );
 | |
| 
 | |
| 	// how much on-screen columns are visable?
 | |
| 
 | |
| 	size_t fromScrCol = mPagePos.mCol;
 | |
| 	size_t tillScrCol = fromScrCol + mColsPerPage;
 | |
| 
 | |
| 	TPosition selStart = mpModel->GetStartOfSelection();
 | |
| 	TPosition selEnd   = mpModel->GetEndOfSelection();
 | |
| 
 | |
| 	bool selectionIsEmpty = ( selStart == selEnd );
 | |
| 
 | |
| 	wxColour curFgCol;
 | |
| 	wxColour curBkCol;
 | |
| 
 | |
| 	wxBrush mLTBrush( mLTColour, wxSOLID );
 | |
| 
 | |
| 	// clip given row-region to the current page
 | |
| 
 | |
| 	if ( ( fromRow >= mPagePos.mRow + mRowsPerPage) || 
 | |
| 		 ( tillRow <= mPagePos.mRow )
 | |
| 	   )
 | |
| 
 | |
| 		return;
 | |
| 
 | |
| 	if ( fromRow < mPagePos.mRow ) fromRow = mPagePos.mRow;
 | |
| 	if ( tillRow > mPagePos.mRow + mRowsPerPage ) tillRow = mPagePos.mRow + mRowsPerPage;
 | |
| 
 | |
| 	if ( fromRow >= tillRow ) return;
 | |
| 
 | |
| 	// now start the renderng
 | |
| 
 | |
| 	if ( mpTimer->GetView() == this && mCursorOn && !mLTMode ) 
 | |
| 	{
 | |
| 		mpTimer->Lock();
 | |
| 		mpTimer->SetIsShown( FALSE );
 | |
| 	}
 | |
| 
 | |
| 	dc.SetFont( mFont );
 | |
| 	dc.SetBackgroundMode( wxSOLID );
 | |
| 
 | |
| 	TTextIterator iter = mpModel->CreateIterator( TPosition( fromRow, 0 ) );
 | |
| 
 | |
| 	PaintDecorations( fromRow, tillRow, dc, iter );
 | |
| 
 | |
| 	size_t cursorRow = mpModel->GetCursor().mRow;
 | |
| 
 | |
| 	size_t curRow = fromRow;
 | |
| 	for( ; curRow != tillRow; ++curRow )
 | |
| 	{
 | |
| 		// place text into line-buffer
 | |
| 
 | |
| 		iter.ToStartOfLine();
 | |
| 		size_t lineLen = iter.GetLineLen();
 | |
| 
 | |
| 		char* lineBuf = GetLineBuffer( lineLen + 1 );
 | |
| 
 | |
| 		size_t i = 0;
 | |
| 
 | |
|   		while( !iter.IsEof() && !iter.IsEol() )
 | |
|   		{
 | |
|   			lineBuf[i++] = iter.GetChar();
 | |
|   			iter.NextChar();
 | |
| 		}
 | |
| 
 | |
| 		iter.NextChar(); // skip eol
 | |
| 
 | |
| 		// obtain "highlights"
 | |
|   		
 | |
| 		mpPainter->SetState( FALSE, FALSE );
 | |
| 		mpPainter->Init( FALSE );
 | |
| 		mpPainter->ProcessSource( lineBuf, lineLen );
 | |
| 		IntListT& blocks = mpPainter->GetBlocks();
 | |
| 
 | |
| 		// setup state vars
 | |
| 
 | |
| 		int dcY = ( curRow - mPagePos.mRow ) * mCharDim.y + mTopMargin;
 | |
| 		
 | |
| 		size_t scrCol = 0;
 | |
| 		size_t txtCol = 0;
 | |
| 
 | |
| 		size_t curBlk = 0;
 | |
| 		size_t curBlkCol = 0;
 | |
| 
 | |
| 		int    chunkLen      = -1;
 | |
| 		size_t chunkTxtStart = 0;
 | |
| 		size_t chunkScrStart = 0;
 | |
| 
 | |
| 		// pre-detect occurance of selection
 | |
| 
 | |
| 		bool lineHasSelection = ( selStart.mRow == curRow ) ||
 | |
| 			                    ( selEnd.mRow == curRow   );
 | |
| 
 | |
| 		bool isInSelection    = ( selStart.mRow <= curRow ) &&
 | |
| 			                    ( selEnd.mRow >= curRow   );
 | |
| 
 | |
| 		if ( isInSelection && selStart.mRow == curRow &&
 | |
| 			 selStart.mCol != 0 )
 | |
| 
 | |
| 			 isInSelection = FALSE;
 | |
| 
 | |
| 		if ( selStart == selEnd ) 
 | |
| 		{
 | |
| 			lineHasSelection = FALSE;
 | |
| 			isInSelection = FALSE;
 | |
| 		}
 | |
| 		
 | |
| 		char ch = '\0';
 | |
| 
 | |
| 		// loop though the text in this row
 | |
| 
 | |
| 		do
 | |
| 		{
 | |
| 			TPosition curPos( curRow, txtCol );
 | |
| 
 | |
| 			// first check if we can finish the current chunk
 | |
| 
 | |
| 			bool finishChunk = FALSE;
 | |
| 
 | |
| 			if ( curBlk < blocks.size() &&
 | |
| 				 curBlkCol + get_src_block_len( blocks[curBlk] ) == txtCol )
 | |
| 			{
 | |
| 				curBlkCol += get_src_block_len( blocks[curBlk] );
 | |
| 				++curBlk;
 | |
| 				finishChunk = TRUE;
 | |
| 			}
 | |
| 			else
 | |
| 			if ( ( !selectionIsEmpty && ( curPos == selStart || curPos == selEnd ) )
 | |
| 				 || lineBuf[txtCol] == '\t' 
 | |
| 				 || txtCol == lineLen )
 | |
| 
 | |
| 				 finishChunk = TRUE;
 | |
| 			
 | |
| 			if ( finishChunk && chunkLen != -1 )
 | |
| 			{
 | |
| 				// is any part of the chunk visable?
 | |
| 
 | |
| 				size_t chunkScrEnd = chunkScrStart + chunkLen;
 | |
| 
 | |
| 				if ( ( // if hits from one side or is inside
 | |
| 					   ( chunkScrStart >= fromScrCol &&
 | |
| 					     chunkScrStart <  tillScrCol    ) ||
 | |
| 					   ( chunkScrEnd   >= fromScrCol &&
 | |
| 					     chunkScrEnd   <  tillScrCol    ) ) ||
 | |
| 
 | |
| 					   // if overlaps the whole range
 | |
| 					 (	chunkScrStart <   fromScrCol &&
 | |
| 					    chunkScrEnd   >=  tillScrCol      )
 | |
| 
 | |
| 				   )
 | |
| 				{
 | |
| 					// render chunk data to the given DC
 | |
| 
 | |
| 					dc.SetTextForeground( curFgCol );
 | |
| 					dc.SetTextBackground( curBkCol );
 | |
| 
 | |
| 					// clip left edge
 | |
| 
 | |
| 					if ( chunkScrStart < fromScrCol )
 | |
| 					{
 | |
| 						size_t diff = fromScrCol - chunkScrStart;
 | |
| 						chunkLen      -= diff;
 | |
| 						chunkTxtStart += diff;
 | |
| 						chunkScrStart += diff;
 | |
| 					}
 | |
| 
 | |
| 					// clip right edge
 | |
| 
 | |
| 					if ( chunkScrEnd > tillScrCol )
 | |
| 					{
 | |
| 						size_t diff  = chunkScrEnd - tillScrCol;
 | |
| 						chunkLen    -= diff;
 | |
| 						chunkScrEnd -= diff;
 | |
| 					}
 | |
| 
 | |
| 					// create string
 | |
| 
 | |
| 					char tmp = lineBuf[chunkTxtStart + chunkLen];
 | |
| 					
 | |
| 					lineBuf[chunkTxtStart + chunkLen] = '\0';
 | |
| 
 | |
| 					// use member-variable, reuse heap-buffer between outputs
 | |
| 					mFragment = lineBuf + chunkTxtStart;
 | |
| 
 | |
| 					lineBuf[chunkTxtStart + chunkLen] = tmp;
 | |
| 
 | |
| 					// draw it
 | |
| 
 | |
| 					int dcX = (chunkScrStart - fromScrCol) * mCharDim.x + mLeftMargin;
 | |
| 
 | |
| 					dc.DrawText( mFragment, dcX, dcY );
 | |
| 				}
 | |
| 
 | |
| 				chunkLen = -1;
 | |
| 
 | |
| 			} // end of "if ( finishChunk )"
 | |
| 
 | |
| 			if ( txtCol == lineLen ) 
 | |
| 				break;
 | |
| 
 | |
| 			if ( chunkLen == -1 )
 | |
| 			{
 | |
| 				// prepare the new chunk
 | |
| 
 | |
| 				if ( curBlk < blocks.size() )
 | |
| 				{
 | |
| 						switch( get_src_block_rank( blocks[curBlk] ) )
 | |
| 						{
 | |
| 							case RANK_BLACK : curFgCol = mNormalTextCol; break;
 | |
| 							case RANK_BLUE  : curFgCol = mIndentifierTextCol; break;
 | |
| 							case RANK_RED   : curFgCol = mReservedWordTextCol; break;
 | |
| 							case RANK_GREEN : curFgCol = mCommentTextCol; break;
 | |
| 							default : break;
 | |
| 						}
 | |
| 				}
 | |
| 
 | |
| 				// track occurence of selection
 | |
| 
 | |
| 				if ( lineHasSelection )
 | |
| 				{
 | |
| 					isInSelection = TRUE;
 | |
| 
 | |
| 					if ( selEnd.mRow == curRow &&
 | |
| 						 selEnd.mCol <= txtCol )
 | |
| 
 | |
| 						 isInSelection = FALSE;
 | |
| 
 | |
| 					if ( selStart.mRow == curRow &&
 | |
| 						 selStart.mCol > txtCol )
 | |
| 
 | |
| 						 isInSelection = FALSE;
 | |
| 				}
 | |
| 
 | |
| 				if ( isInSelection )
 | |
| 				{
 | |
| 					curFgCol = mSelectionFgCol;
 | |
| 					curBkCol = mSelectionBkCol;
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					if ( mLTMode && curRow == cursorRow ) 
 | |
| 
 | |
| 						curBkCol = mLTColour;
 | |
| 					else
 | |
| 						curBkCol = mNormalBkCol ;
 | |
| 				}
 | |
| 
 | |
| 				chunkScrStart = scrCol;
 | |
| 				chunkTxtStart = txtCol;
 | |
| 				chunkLen      = 0;
 | |
| 			}
 | |
| 
 | |
| 			
 | |
| 			ch = lineBuf[txtCol];
 | |
| 
 | |
| 			if ( ch == '\t' )
 | |
| 			{
 | |
| 				// tab's are treated specially (for simplicity and speed)
 | |
| 
 | |
| 				int dcX = (chunkScrStart - fromScrCol) * mCharDim.x + mLeftMargin;
 | |
| 
 | |
| 				if ( !isInSelection ) 
 | |
| 				{
 | |
| 					if ( mLTMode && curRow == cursorRow ) 
 | |
| 						
 | |
| 						dc.SetBrush( mLTBrush );
 | |
| 					else 
 | |
| 						dc.SetBrush( mNormalBkBrush );
 | |
| 				}
 | |
| 				else dc.SetBrush( mSelectedBkBrush );
 | |
| 
 | |
| 				// *** "the rule of TAB..." ***
 | |
| 
 | |
| 				size_t spacing = ( (scrCol / mpModel->mTabSize) + 1 ) * mpModel->mTabSize - scrCol;
 | |
| 
 | |
| 				int width = spacing * mCharDim.x + 1;
 | |
| 
 | |
| 				if ( dcX < mLeftMargin )
 | |
| 				{
 | |
| 					width -= mLeftMargin - dcX;
 | |
| 
 | |
| 					dcX = mLeftMargin;
 | |
| 				}
 | |
| 
 | |
| 				if ( width > 0 )
 | |
| 
 | |
| 					dc.DrawRectangle( dcX, dcY, width, mCharDim.y + 1 );
 | |
| 
 | |
| 				scrCol += spacing;
 | |
| 				txtCol += 1;
 | |
| 				
 | |
| 				// move chunk-start forward, after the occurance of '\t'
 | |
| 
 | |
| 				chunkLen = -1;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				// increase on-screen/in-text positions
 | |
| 
 | |
| 				++scrCol;
 | |
| 				++txtCol;
 | |
| 				++chunkLen;
 | |
| 			}
 | |
| 		
 | |
| 		} while( TRUE );
 | |
| 
 | |
| 		// fill the reminding white-space after eol
 | |
| 
 | |
| 		if ( scrCol < tillScrCol && 
 | |
| 			 ( !isInSelection ||
 | |
| 			   ( isInSelection && curRow == selEnd.mRow ) )
 | |
| 		   )
 | |
| 		{
 | |
| 			if ( scrCol < fromScrCol ) scrCol = fromScrCol;
 | |
| 
 | |
| 			int dcX = ( scrCol - fromScrCol ) * mCharDim.x + mLeftMargin;
 | |
| 
 | |
| 			if ( mLTMode && curRow == cursorRow )
 | |
| 
 | |
| 				dc.SetBrush ( mLTBrush );
 | |
| 			else
 | |
| 				dc.SetBrush( mNormalBkBrush );
 | |
| 
 | |
| 			dc.DrawRectangle( dcX, dcY, 
 | |
| 							  mCharDim.x * ( tillScrCol - scrCol ) + 1, 
 | |
| 							  mCharDim.y + 1 );
 | |
| 		}
 | |
| 
 | |
| 		// render selection which is located past the eol
 | |
| 
 | |
| 		if ( ( lineHasSelection || isInSelection ) && 
 | |
| 			 !( selEnd.mRow == curRow && selEnd.mCol <= txtCol )
 | |
| 		   )
 | |
| 		{
 | |
| 			// determine start of selection on-screen
 | |
| 
 | |
| 			size_t scrSelStart = scrCol + ( selStart.mCol - txtCol );
 | |
| 
 | |
| 			if ( isInSelection )
 | |
| 
 | |
| 				scrSelStart = scrCol;
 | |
| 
 | |
| 			size_t scrSelEnd = tillScrCol;
 | |
| 
 | |
| 			if ( selEnd.mRow == curRow )
 | |
| 
 | |
| 				scrSelEnd = scrCol + ( selEnd.mCol - txtCol );
 | |
| 
 | |
| 			// clipping 
 | |
| 
 | |
| 			if ( scrSelStart < fromScrCol ) scrSelStart = fromScrCol;
 | |
| 			if ( scrSelEnd   > tillScrCol ) scrSelEnd = tillScrCol;
 | |
| 
 | |
| 			// drawing 
 | |
| 
 | |
| 			if ( scrSelEnd > scrSelStart )
 | |
| 			{
 | |
| 				int dcX = ( scrSelStart - fromScrCol ) * mCharDim.x + mLeftMargin;
 | |
| 
 | |
| 				dc.SetBrush( mSelectedBkBrush );
 | |
| 				dc.DrawRectangle( dcX, dcY, 
 | |
| 								  mCharDim.x * ( scrSelEnd - scrSelStart ) + 1, 
 | |
| 								  mCharDim.y + 1 );
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if ( iter.IsEof() ) 
 | |
| 		{
 | |
| 			++curRow;
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 	} // end of "for(...)"
 | |
| 
 | |
| 	if ( curRow < tillRow )
 | |
| 	{
 | |
| 		dc.SetBrush( mNormalBkBrush );
 | |
| 
 | |
| 		int dcY = mTopMargin + (curRow - mPagePos.mRow)*mCharDim.y;
 | |
| 		int dcX = mLeftMargin;
 | |
| 
 | |
| 		dc.DrawRectangle( dcX, dcY, mColsPerPage*mCharDim.x + 1, 
 | |
| 						  ( tillRow - curRow ) * mCharDim.y + 1
 | |
| 				        );
 | |
| 	}
 | |
| 
 | |
| 	if ( mFullRefreshPending )
 | |
| 	{
 | |
| 		dc.SetBrush( mNormalBkBrush );
 | |
| 
 | |
| 		// fill in "corners" which are never reached by characters
 | |
| 		
 | |
| 		int w,h;
 | |
| 		GetClientSize( &w, &h );
 | |
| 
 | |
| 		dc.SetBrush( mNormalBkBrush );
 | |
| 
 | |
| 		int dcX = tillScrCol*mCharDim.x + mLeftMargin;
 | |
| 
 | |
| 		dc.DrawRectangle( dcX, mTopMargin, w - dcX + 1, h );
 | |
| 
 | |
| 		int dcY = mTopMargin + mRowsPerPage*mCharDim.y;
 | |
| 
 | |
| 		dc.DrawRectangle( 0, dcY, w, h - dcY + 2 );
 | |
| 
 | |
| 		++curRow;
 | |
| 
 | |
| 		// any past-the-eof lines left at the bottom?
 | |
| 	}
 | |
| 
 | |
| 	mFullRefreshPending = FALSE;
 | |
| 
 | |
| 	if ( mpTimer->GetView() == this && mCursorOn && !mLTMode ) 
 | |
| 
 | |
| 		mpTimer->Unlock();
 | |
| 
 | |
| } // end of PaintRows(..)
 | |
| 
 | |
| /***** Implementation for class TBookmarkPainter *****/
 | |
| 
 | |
| TBookmarkPainter::TBookmarkPainter() 
 | |
| 
 | |
| 	: TPinPainterBase( BOOKMARK_PIN_TC ),
 | |
| 	  mBkBrush( wxColour( 0,255,255 ), wxSOLID )
 | |
| {
 | |
| }
 | |
| 
 | |
| void TBookmarkPainter::DrawPin( TPinBase* pPin, wxTextEditorView& view, wxDC& dc, 
 | |
| 							    const wxPoint& pos, const wxSize& dim )
 | |
| {
 | |
| 	dc.SetPen( *wxBLACK_PEN );
 | |
| 	dc.SetBrush( mBkBrush );
 | |
| 	dc.DrawRoundedRectangle( pos.x+2, pos.y, dim.x-4, dim.y, 4 );
 | |
| }
 | |
| 
 | |
| /***** Implementation for class TBreakpointPainter *****/
 | |
| 
 | |
| TBreakpointPainter::TBreakpointPainter() 
 | |
| 
 | |
| 	: TPinPainterBase( BRKPOINT_PIN_TC ),
 | |
| 	  mBkBrush( wxColour( 196,0,0 ), wxSOLID )
 | |
| {
 | |
| }
 | |
| 
 | |
| void TBreakpointPainter::DrawPin( TPinBase* pPin, wxTextEditorView& view, wxDC& dc, 
 | |
| 								  const wxPoint& pos, const wxSize& dim )
 | |
| {
 | |
| 	dc.SetPen( *wxBLACK_PEN );
 | |
| 	dc.SetBrush( mBkBrush );
 | |
| 	dc.DrawRoundedRectangle( pos.x+6, pos.y+2, dim.x-12, dim.y-4, 30 );
 | |
| }
 | |
| 
 | |
| /***** Implementation for class TCursorTimer *****/
 | |
| 
 | |
| TCursorTimer::TCursorTimer()
 | |
| 
 | |
| 	: mIsLocked( FALSE ),
 | |
| 	  mIsShown ( FALSE ),
 | |
| 	  mBlinkInterval( 500 ),
 | |
| 	  mBrush( wxColour(0,0,0), wxSOLID ),
 | |
| 	  mMissOneTick( FALSE )
 | |
| {
 | |
| }
 | |
| 
 | |
| void TCursorTimer::Notify()
 | |
| {
 | |
| 	if ( mIsLocked ) return;
 | |
| 
 | |
| 	if ( mMissOneTick )
 | |
| 	{
 | |
| 		// this trick is used because it's not
 | |
| 		// possible to restart the timer under wxGtk
 | |
| 
 | |
| 		mMissOneTick = FALSE;
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	mIsLocked = TRUE;
 | |
| 
 | |
| 	DrawCursor();
 | |
| 
 | |
| 	mIsShown = !mIsShown;
 | |
| 
 | |
| 	mIsLocked = FALSE;
 | |
| }
 | |
| 
 | |
| void TCursorTimer::SetView( wxTextEditorView* pView )
 | |
| {
 | |
| 	mpView = pView;
 | |
| }
 | |
| 
 | |
| wxTextEditorView* TCursorTimer::GetView()
 | |
| {
 | |
| 	return mpView;
 | |
| 
 | |
| }
 | |
| 
 | |
| void TCursorTimer::HideCursor( bool forceHide )
 | |
| {
 | |
| 	Lock();
 | |
| 
 | |
| 	if ( mIsShown  ) 
 | |
| 	{
 | |
| 		DrawCursor();
 | |
| 		mIsShown = FALSE;
 | |
| 	}
 | |
| 
 | |
| 	Unlock();
 | |
| }
 | |
| 
 | |
| void TCursorTimer::ShowCursor( bool forceShow )
 | |
| {
 | |
| 	Lock();
 | |
| 
 | |
| 	if ( !forceShow ) 
 | |
| 	{
 | |
| 		DrawCursor();
 | |
| 		mIsShown = TRUE;
 | |
| 
 | |
| 		if ( mStarted )
 | |
| 			mMissOneTick = TRUE;
 | |
| 	}
 | |
| 
 | |
| 	Unlock();
 | |
| 
 | |
| 	if ( !mStarted )
 | |
| 	{
 | |
| 		Start( mBlinkInterval );
 | |
| 		mStarted = TRUE;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void TCursorTimer::Lock()
 | |
| {
 | |
| //	while( mIsLocked );
 | |
| 
 | |
| 	mIsLocked = TRUE;
 | |
| }
 | |
| 
 | |
| void TCursorTimer::Unlock()
 | |
| {
 | |
| 	mIsLocked = FALSE;
 | |
| }
 | |
| 
 | |
| void TCursorTimer::SetIsShown( bool isShown )
 | |
| {
 | |
| 	mIsShown = isShown;
 | |
| }
 | |
| 
 | |
| /*** protected methods ***/
 | |
| 
 | |
| void TCursorTimer::DrawCursor()
 | |
| {
 | |
| 	if ( mpView == NULL ) return;
 | |
| 
 | |
| 	wxClientDC dc( mpView );
 | |
| 
 | |
| 	int x = 0, y = 0;
 | |
| 
 | |
| 	mpView->ScreenPosToPixels( mpView->mCursorScrPos, x, y );
 | |
| 
 | |
| 	dc.SetLogicalFunction( wxINVERT );
 | |
| 	dc.SetBrush( mBrush );
 | |
| 
 | |
| 	dc.SetPen( *wxTRANSPARENT_PEN );
 | |
| 	dc.DrawRectangle( x,y, 3, mpView->mCharDim.y + 1 );
 | |
| 	dc.SetBackgroundMode( wxSOLID );
 | |
| }
 |