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