+/////////////////////////////////////////////////////////////////////////////
+// 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 );
+}