wxWidgets/utils/wxPython/modules/lseditor/tdefs.cpp
Harco de Hilster 4b123bb9cc lots'o' wxpython modules files
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@3345 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
1999-08-11 17:14:49 +00:00

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