]> git.saurik.com Git - wxWidgets.git/blame - utils/wxPython/modules/lseditor/tdefs.cpp
2c : check menu was inversely set
[wxWidgets.git] / utils / wxPython / modules / lseditor / tdefs.cpp
CommitLineData
4b123bb9
HH
1/////////////////////////////////////////////////////////////////////////////
2// Name: No names yet.
3// Purpose: Contrib. demo
4// Author: Aleksandras Gluchovas
5// Modified by:
6// Created: 03/04/1999
7// RCS-ID: $Id$
8// Copyright: (c) Aleksandars Gluchovas
9// Licence: GNU General Public License
10/////////////////////////////////////////////////////////////////////////////
11//
12// This program is free software; you can redistribute it and/or modify
13// it under the terms of the GNU General Public License as published by
14// the Free Software Foundation; either version 2 of the License, or
15// (at your option) any later version.
16//
17// This program is distributed in the hope that it will be useful,
18// but WITHOUT ANY WARRANTY; without even the implied warranty of
19// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20// GNU General Public License for more details.
21//
22// You should have received a copy of the GNU General Public License
23// along with this program; if not, write to the Free Software
24// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25/////////////////////////////////////////////////////////////////////////////
26
27
28// For compilers that support precompilation, includes "wx/wx.h".
29#include "wx/wxprec.h"
30
31#ifdef __BORLANDC__
32#pragma hdrstop
33#endif
34
35#ifndef WX_PRECOMP
36#include "wx/wx.h"
37#endif
38
39#include "wx/file.h"
40#include "wx/textdlg.h"
41#include "wx/clipbrd.h"
42#include "wx/dataobj.h"
43
44#include <stdio.h>
45
46#include "tdefs.h"
47#include "finddlg.h"
48
49#include <memory.h>
50
51/***** Implementation for class TBlock *****/
52
53void TBlock::RecalcBlockProperties()
54{
55 char* cur = mBuf;
56 char* end = mBuf + mTextLen;
57 mRowCount = 0;
58
59 while( cur < end )
60 {
61 if ( is_eol_char( *cur ) ) ++mRowCount;
62
63 ++cur;
64 }
65}
66
67/***** Implementation for class TTextIterator *****/
68
69string TTextIterator::mSeparators = ",.()[]\t\\+-*/|=<>:;\t\n~?!%";
70
71bool TTextIterator::IsSeparator( char ch )
72{
73 size_t sz = mSeparators.size();
74
75 for( size_t i = 0; i != sz; ++i )
76
77 if ( mSeparators[i] == ch ) return TRUE;
78
79 return FALSE;
80}
81
82char* TTextIterator::GetClosestPos()
83{
84 char* end = GetBlockEnd();
85 char* cur = mpCurRowStart;
86 size_t col = 0;
87
88 while( cur < end && col < mPos.mCol && !is_eol_char(*cur) )
89 {
90 if ( !is_DOS_eol_char( *cur ) ) ++col;
91 ++cur;
92 }
93
94 if ( is_DOS_eol_char( *cur ) ) ++cur;
95
96 return cur;
97}
98
99char* TTextIterator::GotoClosestPos()
100{
101 char* end = GetBlockEnd();
102 char* cur = mpCurRowStart;
103 size_t col = 0;
104
105 while( cur < end && col < mPos.mCol && !is_eol_char(*cur) )
106 {
107 if ( !is_DOS_eol_char( *cur ) ) ++col;
108 ++cur;
109 }
110
111 mPos.mCol = col;
112
113 if ( is_DOS_eol_char( *cur ) ) ++cur;
114
115 return cur;
116}
117
118TTextIterator::TTextIterator()
119
120 : mIsEof( FALSE )
121{}
122
123bool TTextIterator::IsLastLine()
124{
125 TBlockIteratorT nextBlk = mBlockIter;
126 ++nextBlk;
127
128 if ( nextBlk != mEndOfListIter ) return FALSE;
129
130 char* cur = mpCurRowStart;
131 char* end = GetBlockEnd();
132
133 while( cur < end && !is_eol_char( *cur ) ) ++cur;
134
135 if ( cur == end ) return TRUE;
136
137 ++cur;
138
139 return ( cur == end );
140}
141
142char TTextIterator::GetChar()
143{
144 char* cur = GetClosestPos();
145
146 if ( is_DOS_eol_char( *cur ) )
147
148 return *(cur+1);
149 else
150 return *cur;
151}
152
153bool TTextIterator::IsEol()
154{
155 return is_eol_char( GetChar() ) || mIsEof;
156}
157
158bool TTextIterator::IsEof()
159{
160 return mIsEof;
161}
162
163int TTextIterator::GetDistFromEol()
164{
165 return 0; // TBD::
166}
167
168void TTextIterator::NextChar()
169{
170 char* cur = GotoClosestPos();
171
172 if ( cur + 1 >= GetBlockEnd() )
173 {
174 TBlockIteratorT nextBlk = mBlockIter;
175 ++nextBlk;
176
177 if ( nextBlk == mEndOfListIter )
178 {
179 if ( cur != GetBlockEnd() )
180 ++mPos.mCol;
181
182 mIsEof = TRUE;
183 return;
184 }
185
186 ++mPos.mRow ;
187 mPos.mCol = 0;
188
189 mBlockIter = nextBlk;
190
191 mFirstRowInBlock = mPos.mRow;
192 mActualRow = mPos.mRow;
193 mpCurRowStart = (*mBlockIter).mBuf;
194
195 mIsEof = ( (*mBlockIter).mTextLen == 0 );
196 }
197 else
198 {
199 if ( is_eol_char( *cur ) )
200 {
201 ++mPos.mRow;
202 mPos.mCol = 0;
203
204 mActualRow = mPos.mRow;
205 mpCurRowStart = cur + 1;
206 }
207 else
208 ++mPos.mCol;
209 }
210
211 mIsEof = (mpCurRowStart + mPos.mCol) == GetBlockEnd();
212}
213
214void TTextIterator::PreviousChar()
215{
216 char* cur = GotoClosestPos();
217
218 if ( cur == (*mBlockIter).mBuf )
219 {
220 TBlockIteratorT prevBlk = mBlockIter;
221 --prevBlk;
222
223 if ( prevBlk == mEndOfListIter )
224 {
225 mIsEof = TRUE;
226 return;
227 }
228
229 --mPos.mRow;
230
231 mBlockIter = prevBlk;
232
233 cur = GetBlockEnd() - 1;
234
235 char* eolPos = cur;
236 --cur; // skip EOL
237 char* start = (*mBlockIter).mBuf;
238
239 while( cur != start && !is_eol_char( *cur ) ) --cur; // goto start of line
240
241 if ( is_eol_char( *cur ) ) ++cur;
242
243 mPos.mCol = (size_t)(eolPos - cur);
244 mpCurRowStart = cur;
245
246 mFirstRowInBlock = mPos.mRow;
247 mActualRow = mPos.mRow;
248 }
249 else
250 {
251 do
252 {
253 // FIXME FIXME:: this is more then messy .... !
254
255 if ( is_eol_char( *(cur-1) ) )
256 {
257 --cur; // goto EOL
258
259 --mPos.mRow;
260
261 char* eolPos = cur;
262 --cur; // skip EOL
263 char* start = (*mBlockIter).mBuf;
264
265 while( cur != start && !is_eol_char( *cur ) ) --cur; // goto start of line
266
267 if ( is_eol_char( *cur ) ) ++cur;
268
269 mPos.mCol = (size_t)(eolPos - cur);
270 mpCurRowStart = cur;
271
272 if ( eolPos != cur && is_DOS_eol_char( *(eolPos-1) ) ) --mPos.mCol;
273
274 mActualRow = mPos.mRow;
275
276 break;
277 }
278 else
279 if ( is_DOS_eol_char( *(cur-1) ) )
280 {
281 --cur;
282
283 if ( cur != (*mBlockIter).mBuf && is_eol_char( *(cur-1) ) )
284
285 continue;
286 else
287 {
288 --mPos.mCol;
289 --cur;
290 }
291 }
292 else
293 {
294 --mPos.mCol;
295 break;
296 }
297
298 } while( 1 );
299 }
300
301 mIsEof = (mpCurRowStart + mPos.mCol) == GetBlockEnd();
302}
303
304void TTextIterator::NextWord()
305{
306 GotoClosestPos();
307
308 // skip non-white space ahead
309
310 bool wasSeparator = IsSeparator( GetChar() );
311
312 while( !IsEof() )
313 {
314 char ch = GetChar();
315
316 if ( ch == ' ' ||
317 ch == '\t' ||
318 is_eol_char(ch) ||
319 wasSeparator != IsSeparator(ch) )
320
321 break;
322
323 NextChar();
324 }
325
326 // skip all white stpace if any
327 while( !IsEof() )
328 {
329 char ch = GetChar();
330
331 if ( ch != ' ' && ch != '\t' && !is_eol_char(ch) )
332
333 break;
334
335 NextChar();
336 }
337}
338
339void TTextIterator::PreviousWord()
340{
341 GotoClosestPos();
342
343 PreviousChar();
344
345 // skip all white stpace if any
346 while( !IsEof() )
347 {
348 char ch = GetChar();
349
350 if ( ch != ' ' && ch != '\t' && !is_eol_char(ch) )
351
352 break;
353
354 PreviousChar();
355 }
356
357 bool wasSeparator = IsSeparator( GetChar() );
358
359 // skip word;
360 while( !IsEof() )
361 {
362 char ch = GetChar();
363
364 if ( ch == ' ' ||
365 ch == '\t' ||
366 is_eol_char(ch) ||
367 wasSeparator != IsSeparator(ch)
368 )
369 {
370 NextChar();
371 break;
372 }
373
374 PreviousChar();
375 }
376}
377
378void TTextIterator::ToEndOfLine()
379{
380 GotoClosestPos();
381
382 while( !IsEof() )
383 {
384 char ch = GetChar();
385
386 if ( is_eol_char( ch ) ) break;
387
388 NextChar();
389 }
390}
391
392void TTextIterator::ToStartOfLine()
393{
394 GotoClosestPos();
395
396 mPos.mCol = 0;
397 mPos.mRow = mActualRow;
398}
399
400size_t TTextIterator::GetLineLen()
401{
402 char* cur = mpCurRowStart;
403 char* end = GetBlockEnd();
404
405 size_t len = 0;
406
407 while( cur < end && !is_eol_char( *cur ) )
408 {
409 if ( !is_DOS_eol_char( *cur ) ) ++len;
410 ++cur;
411 }
412
413 return len;
414}
415
416TPosition TTextIterator::GetPosition()
417{
418 return mPos;
419}
420
421bool TTextIterator::IsInLastBlock()
422{
423 TBlockIteratorT next = mBlockIter;
424 ++next;
425
426 return next == mEndOfListIter;
427}
428
429bool TTextIterator::DetectUnixText()
430{
431 char* cur = GetBlockStart();
432 char* end = GetBlockEnd();
433
434 bool isUnixText = IS_UNIX_TEXT_BY_DEFAULT;
435
436 while( cur < end )
437 {
438 if ( is_DOS_eol_char( *cur ) ) return FALSE;
439
440 if ( is_eol_char( *cur ) ) return TRUE;
441
442 ++cur;
443 }
444
445 return isUnixText;
446}
447
448/***** Implementation for class TCppJavaHighlightListener *****/
449
450void TCppJavaHighlightListener::OnTextChanged( wxTextEditorModel* pModel,
451 size_t atRow, size_t nRows,
452 TEXT_CHANGE_TYPE ct )
453{
454 mpModel = pModel;
455
456 /*
457
458 int state = GetStateAtRow( atRow );
459
460 if ( ct == CT_INSERTED )
461 {
462 RemoveCommentTags( atRow, atRow + nRows + 1 );
463 GenerateTagsForRange( atRows, atRows + nRows + 1 );
464 }
465 else
466 if ( ct == CT_DELETED )
467 {
468 RemoveCommentTags( atRow, atRow + 1 );
469 GenerateTagsForRange( atRows, atRows + 1 );
470 }
471 */
472}
473
474/***** Implementation for class wxTextEditorModel *****/
475
476/*** protected methods ***/
477
478size_t wxTextEditorModel::GetLineCountInRange( char* from, char* till )
479{
480 size_t nLines = 0;
481
482 while( from != till )
483 {
484 if ( is_eol_char( *from ) ) ++nLines;
485
486 ++from;
487 }
488
489 return nLines;
490}
491
492void wxTextEditorModel::DoInsertText( const TPosition& pos,
493 char* text, size_t len,
494 TRange& actualRange )
495{
496 // FOR NOW:: very dummy imp.
497
498 char* end = text + len;
499
500 TTextIterator iter = CreateIterator( pos );
501
502 TBlock& blk = (*iter.mBlockIter);
503
504 char* cur = text;
505
506 char* insertPos = iter.GotoClosestPos();
507 actualRange.mFrom = iter.GetPosition();
508
509 if ( is_eol_char( *insertPos ) &&
510 insertPos != iter.GetBlockStart() &&
511 is_DOS_eol_char( *(insertPos-1) )
512 )
513 --insertPos;
514
515 size_t sizeAfter = (size_t)(iter.GetBlockEnd() - insertPos);
516
517 size_t nLines = GetLineCountInRange( text, text + len );
518
519 if ( blk.mTextLen + len < FILLED_BLOCK_LEN )
520 {
521 memmove( insertPos + len, insertPos, sizeAfter );
522
523 memcpy( insertPos, text, len );
524
525 blk.mTextLen += len;
526
527
528 blk.RecalcBlockProperties();
529
530 if ( iter.IsInLastBlock() )
531
532 ++blk.mRowCount; // last block have always the-last-row-to-spare -
533 // the "nature" of most text editors
534
535 char* endPos = insertPos + len;
536
537 bool found = FALSE;
538
539 /*
540 // OLD STUFF:: slow & buggy
541
542 while( !iter.IsEof() )
543 {
544 if ( iter.GetClosestPos() == endPos )
545 {
546 actualRange.mTill = iter.GetPosition();
547 found = TRUE;
548 break;
549 }
550
551 iter.NextChar();
552 }
553
554 if ( !found )
555 {
556 actualRange.mTill = iter.GetPosition();
557 ++actualRange.mTill.mCol;
558
559 //T_ASSERT( found ); // DBG::
560 }
561 */
562
563 actualRange.mTill = actualRange.mFrom;
564 actualRange.mTill.mRow += nLines;
565
566 if ( nLines == 0 )
567
568 actualRange.mTill.mCol = actualRange.mFrom.mCol + (len);
569 else
570 {
571 cur = end;
572
573 while( cur != insertPos && !is_eol_char( *cur ) )
574
575 --cur;
576
577 if ( is_eol_char( *cur ) ) ++cur;
578
579 actualRange.mTill.mCol = (int)(end - cur);
580 }
581
582 NotifyTextChanged( pos.mRow, nLines, CT_INSERTED );
583 }
584 else
585 {
586 // TBD:::
587
588 char buf[16];
589 sprintf( buf, "%d", FILLED_BLOCK_LEN );
590 string msg = "Sorry!!! Currently editor is limited to files less then ";
591 msg += buf;
592 msg += " bytes\n(the requested text length is " +
593 sprintf( buf, "%d", blk.mTextLen + len );
594 msg += buf;
595 msg += " bytes)\n Please, close this file without making any changes.";
596
597 wxMessageBox( msg );
598
599 GetActiveView()->SetFocus();
600
601 //T_ASSERT(0); // DBG:: for now
602 }
603}
604
605void wxTextEditorModel::DoDeleteRange( const TPosition& from, const TPosition& till,
606 TRange& actualRange
607 )
608{
609 // FOR NOW:: very dummy imp.
610
611 TTextIterator iterFrom = CreateIterator( from );
612 TTextIterator iterTill = CreateIterator( till );
613
614 if ( iterFrom.mBlockIter == iterTill.mBlockIter )
615 {
616 char* fromPos = iterFrom.GotoClosestPos();
617 char* tillPos = iterTill.GotoClosestPos();
618 char* blockStart = (*iterFrom.mBlockIter).mBuf;
619
620 if ( is_eol_char( *fromPos ) &&
621 fromPos != blockStart &&
622 is_DOS_eol_char( *(fromPos-1) )
623 )
624 --fromPos;
625
626 if ( is_eol_char( *tillPos ) &&
627 tillPos != blockStart &&
628 is_DOS_eol_char( *(tillPos-1) )
629 )
630 --tillPos;
631
632 size_t len = (size_t)( tillPos -fromPos );
633
634 size_t nLines = GetLineCountInRange( fromPos, fromPos + len );
635
636 size_t sizeAfter = (size_t)(iterFrom.GetBlockEnd() - tillPos);
637
638 memmove( fromPos, tillPos, sizeAfter );
639
640 (*iterFrom.mBlockIter).mTextLen -= len;
641
642 (*iterFrom.mBlockIter).RecalcBlockProperties();
643
644 if ( iterFrom.IsInLastBlock() )
645
646 ++(*iterFrom.mBlockIter).mRowCount; // last block have always the-last-row-to-spare -
647 // the "nature" of most text editors
648
649 actualRange.mFrom = iterFrom.GetPosition();
650 actualRange.mTill = iterTill.GetPosition();
651
652 NotifyTextChanged( from.mRow, nLines, CT_DELETED );
653 }
654 else
655 T_ASSERT(0); // DBG:: for now
656}
657
658void wxTextEditorModel::GetTextFromRange( const TPosition& from, const TPosition& till,
659 char** text, size_t& textLen
660 )
661{
662 TTextIterator iterFrom = CreateIterator( from );
663 TTextIterator iterTill = CreateIterator( till );
664
665 if ( iterFrom.mBlockIter == iterTill.mBlockIter )
666 {
667 char* blockStart = (*iterFrom.mBlockIter).mBuf;
668
669 char* fromPos = iterFrom.GetClosestPos();
670 char* tillPos = iterTill.GetClosestPos();
671
672 if ( is_eol_char( *fromPos ) &&
673 fromPos != blockStart &&
674 is_DOS_eol_char( *(fromPos-1) )
675 )
676 --fromPos;
677
678 if ( is_eol_char( *tillPos ) &&
679 tillPos != blockStart &&
680 is_DOS_eol_char( *(tillPos-1) )
681 )
682 --tillPos;
683
684 textLen = (size_t)( tillPos -fromPos );
685
686 *text = AllocCharacters( textLen );
687
688 memcpy( *text, fromPos, textLen );
689 }
690 else
691 T_ASSERT(0); // DBG:: for now
692}
693
694void wxTextEditorModel::LoadTextFromFile( const wxString& fname )
695{
696 T_ASSERT( wxFile::Exists( fname ) );
697
698 DeleteAllText();
699
700 wxFile fl( fname );
701
702 char* buf = AllocCharacters( fl.Length() );
703
704 fl.Read( buf, fl.Length() );
705
706 TRange result;
707 DoInsertText( TPosition( 0,0 ), buf, fl.Length(), result );
708
709 FreeCharacters( buf );
710
711 TTextIterator iter = CreateIterator( TPosition( 0,0 ) );
712
713 mIsUnixText = iter.DetectUnixText();
714
715 ClearUndoBuffer();
716
717 NotifyAllViews();
718}
719
720void wxTextEditorModel::SaveTextToFile( const wxString& fname )
721{
722 wxFile fl( fname, wxFile::write );
723
724 char* text = 0;
725 size_t len = 0;
726
727 GetTextFromRange( TPosition(0,0), TPosition( GetTotalRowCount()+1,0 ), &text, len );
728
729 fl.Write( text, len );
730 fl.Close();
731
732 FreeCharacters( text );
733}
734
735void wxTextEditorModel::NotifyTextChanged( size_t atRow, size_t nRows, TEXT_CHANGE_TYPE ct )
736{
737 if ( nRows > 0 )
738
739 MergeChange( atRow, mRowsPerPage );
740 else
741 MergeChange( atRow, 1 );
742
743 // reposition bookmarsk
744
745 if ( nRows > 0 )
746 {
747 if ( ct == CT_INSERTED )
748 {
749 size_t curPin = FindNextPinFrom( atRow + 1 );
750
751 while( curPin != NPOS )
752 {
753 mPins[curPin]->mRow += nRows;
754
755 ++curPin;
756
757 if ( curPin == mPins.size() ) break;
758 }
759 }
760 else
761 if ( ct == CT_DELETED )
762 {
763 size_t curPin = FindNextPinFrom( atRow + 1 );
764 size_t fromPin = curPin;
765 size_t tillRow = atRow + nRows;
766
767 while( curPin != NPOS && mPins[curPin]->mRow < tillRow )
768 {
769 ++curPin;
770
771 if ( curPin == mPins.size() ) break;
772 }
773
774 if ( fromPin != NPOS && nRows != 0 )
775 {
776 mPins.erase( &mPins[fromPin], &mPins[curPin] );
777
778 while( curPin < mPins.size() )
779 {
780 mPins[curPin]->mRow -= nRows;
781 ++curPin;
782 }
783 }
784 }
785 }
786
787
788 // send notificaitons
789
790 for( size_t i = 0; i != mChangeListeners.size(); ++i )
791
792 mChangeListeners[i]->OnTextChanged( this, atRow, nRows, ct );
793}
794
795void wxTextEditorModel::NotifyTextChanged( TPosition from, TPosition till, TEXT_CHANGE_TYPE ct )
796{
797 ArrangePositions( from, till );
798
799 NotifyTextChanged( from.mRow, till.mRow - from.mRow + 1, ct );
800}
801
802void wxTextEditorModel::DoExecuteNewCommand( TCommand& cmd )
803{
804 if ( cmd.mType == TCMD_INSERT )
805 {
806 cmd.mPrePos = mCursorPos;
807 DoInsertText( cmd.mRange.mFrom, cmd.mData, cmd.mDataLen, cmd.mRange );
808 }
809 else
810 if ( cmd.mType == TCMD_DELETE )
811 {
812 cmd.mPrePos = mCursorPos;
813 DoDeleteRange( cmd.mRange.mFrom, cmd.mRange.mTill, cmd.mRange );
814 }
815}
816
817void wxTextEditorModel::DoReexecuteCommand( TCommand& cmd )
818{
819 NotifyTextChanged( mCursorPos.mRow, 1, CT_MODIFIED ); // indicate update of current cursor position
820
821 if ( cmd.mType == TCMD_INSERT )
822 {
823 DoInsertText( cmd.mRange.mFrom, cmd.mData, cmd.mDataLen, cmd.mRange );
824 mCursorPos = cmd.mPostPos;
825 }
826 else
827 if ( cmd.mType == TCMD_DELETE )
828 {
829 DoDeleteRange( cmd.mRange.mFrom, cmd.mRange.mTill, cmd.mRange );
830 mCursorPos = cmd.mPostPos;
831 }
832
833 NotifyTextChanged( mCursorPos.mRow, 1, CT_MODIFIED ); // indicate update of current cursor position
834}
835
836void wxTextEditorModel::DoUnexecuteCommand( TCommand& cmd )
837{
838 NotifyTextChanged( mCursorPos.mRow, 1, CT_MODIFIED ); // indicate update of current cursor position
839
840 if ( cmd.mType == TCMD_INSERT )
841 {
842 DoDeleteRange( cmd.mRange.mFrom, cmd.mRange.mTill, cmd.mRange );
843 mCursorPos = cmd.mPrePos;
844 }
845 else
846 if ( cmd.mType == TCMD_DELETE )
847 {
848 DoInsertText( cmd.mRange.mFrom, cmd.mData, cmd.mDataLen, cmd.mRange );
849 mCursorPos = cmd.mPrePos;
850 }
851
852 NotifyTextChanged( mCursorPos.mRow, 1, CT_MODIFIED ); // indicate update of current cursor position
853}
854
855void wxTextEditorModel::UndoImpl()
856{
857 --mCurCommand;
858
859 DoUnexecuteCommand( *mCommands[mCurCommand] );
860}
861
862void wxTextEditorModel::RedoImpl()
863{
864 DoReexecuteCommand( *mCommands[mCurCommand] );
865
866 ++mCurCommand;
867}
868
869void wxTextEditorModel::ExecuteCommand( TCommand* pCmd )
870{
871 if ( mCurCommand < mCheckPointCmdNo )
872
873 // new command is executed before the checkpoint,
874 // and every thing is sliced - invalidate it
875
876 mCheckPointDestroyed = TRUE;
877
878 // slice undo-able commands ahead in the queue,
879 // they wont ever be reexecuted
880
881 while( mCommands.size() > mCurCommand )
882 {
883 delete mCommands.back();
884
885 mCommands.pop_back();
886 }
887
888 mCommands.push_back( pCmd );
889
890 DoExecuteNewCommand( *pCmd );
891 ++mCurCommand;
892}
893
894bool wxTextEditorModel::CanPrependCommand( TCommand* pCmd )
895{
896 if ( mCommands.size() != mCurCommand ||
897 mCommands.size() == 0 )
898
899 return FALSE;
900
901 TCommand& prevCmd = *mCommands.back();
902
903 if ( !(prevCmd.mRange.mTill == pCmd->mRange.mFrom) )
904
905 return FALSE;
906
907 char prevCh = prevCmd.mData[ prevCmd.mDataLen - 1];
908 char curCh = pCmd->mData[0];
909
910 if ( prevCh == curCh ) return TRUE;
911
912 if ( prevCh == ' ' || curCh == ' ') return FALSE;
913
914 if ( TTextIterator::IsSeparator(prevCh) !=
915 TTextIterator::IsSeparator(curCh) )
916
917 return FALSE;
918
919 return TRUE;
920}
921
922void wxTextEditorModel::PrependCommand( TCommand* pCmd )
923{
924 if ( mCheckPointCmdNo == mCurCommand )
925
926 mCheckPointDestroyed = TRUE;
927
928 TCommand& prevCmd = *mCommands.back();
929
930 DoExecuteNewCommand( *pCmd );
931
932 TCommand* pComb = new TCommand();
933
934 pComb->mType = TCMD_INSERT;
935 pComb->mDataLen = prevCmd.mDataLen + pCmd->mDataLen;
936
937 pComb->mData = AllocCharacters( pComb->mDataLen );
938 pComb->mRange.mFrom = prevCmd.mRange.mFrom;
939 pComb->mRange.mTill = pCmd->mRange.mTill;
940 pComb->mPrePos = prevCmd.mPrePos;
941 pComb->mPostPos = pCmd->mPostPos;
942
943 memcpy( pComb->mData, prevCmd.mData, prevCmd.mDataLen );
944 memcpy( pComb->mData + prevCmd.mDataLen, pCmd->mData, pCmd->mDataLen );
945
946 FreeCharacters( prevCmd.mData );
947 FreeCharacters( pCmd->mData );
948
949 delete &prevCmd;
950 delete pCmd;
951
952 mCommands[ mCommands.size() - 1 ] = pComb;
953}
954
955void wxTextEditorModel::SetPostPos( const TPosition& pos )
956{
957 mCommands[mCurCommand-1]->mPostPos = pos;
958}
959
960bool wxTextEditorModel::SelectionIsEmpty()
961{
962 return mSelectionStart == mSelectionEnd;
963}
964
965void wxTextEditorModel::StartBatch()
966{
967 // TBD::
968}
969
970void wxTextEditorModel::FinishBatch()
971{
972 // TBD::
973}
974
975void wxTextEditorModel::DeleteRange( const TPosition& from, const TPosition& till )
976{
977 TCommand* pCmd = new TCommand();
978
979 pCmd->mType = TCMD_DELETE;
980
981 pCmd->mRange.mFrom = from;
982 pCmd->mRange.mTill = till;
983 pCmd->mPrePos = mCursorPos;
984
985 GetTextFromRange( from, till, &pCmd->mData, pCmd->mDataLen );
986
987 ExecuteCommand( pCmd );
988}
989
990void wxTextEditorModel::InsertText( const TPosition& pos, const char* text, size_t len )
991{
992 TCommand* pCmd = new TCommand();
993
994 pCmd->mType = TCMD_INSERT;
995
996 pCmd->mRange.mFrom = pos;
997
998 pCmd->mData = AllocCharacters( len, text ),
999 pCmd->mDataLen = len;
1000 pCmd->mPrePos = mCursorPos;
1001
1002 ExecuteCommand( pCmd );
1003}
1004
1005void wxTextEditorModel::DeleteSelection()
1006{
1007 DeleteRange( mSelectionStart, mSelectionEnd );
1008
1009 ResetSelection();
1010}
1011
1012bool wxTextEditorModel::IsLastLine( const TPosition& pos )
1013{
1014 return FALSE;
1015}
1016
1017TTextIterator wxTextEditorModel::CreateIterator( const TPosition& pos )
1018{
1019 size_t curRow = 0;
1020
1021 TBlockIteratorT bIter = mBlocks.begin();
1022
1023 TTextIterator tIter;
1024
1025 while( bIter != mBlocks.end() )
1026 {
1027 TBlockIteratorT nextBlk = bIter;
1028 ++nextBlk;
1029
1030 if ( nextBlk == mBlocks.end() ||
1031 ( pos.mRow >= curRow &&
1032 pos.mRow <= curRow + (*bIter).mRowCount )
1033 )
1034 {
1035 tIter.mFirstRowInBlock = curRow;
1036
1037 char* cur = (*bIter).mBuf;
1038 char* end = cur + (*bIter).mTextLen;
1039
1040 // slightly optimized
1041
1042 if ( curRow < pos.mRow )
1043 {
1044 while( cur < end )
1045 {
1046 if ( is_eol_char( *cur ) )
1047 {
1048 ++curRow;
1049
1050 if ( !(curRow < pos.mRow) )
1051 {
1052 ++cur;
1053 break;
1054 }
1055 }
1056
1057 ++cur;
1058 }
1059 }
1060
1061 tIter.mActualRow = curRow;
1062 tIter.mpCurRowStart = cur;
1063 tIter.mPos = pos;
1064
1065 // FOR NOW:: positioning past the end of file is not supported
1066 tIter.mPos.mRow = curRow;
1067
1068 tIter.mBlockIter = bIter;
1069 tIter.mEndOfListIter = mBlocks.end();
1070
1071 break;
1072 }
1073 else
1074 {
1075 curRow += (*bIter).mRowCount;
1076 ++bIter;
1077 }
1078 }
1079
1080 return tIter;
1081}
1082
1083void wxTextEditorModel::ArrangePositions( TPosition& upper, TPosition& lower )
1084{
1085 if ( upper > lower )
1086 {
1087 TPosition tmp( lower );
1088 lower = upper;
1089 upper = tmp;
1090 }
1091}
1092
1093void wxTextEditorModel::ArrangePositions( size_t& upper, size_t& lower )
1094{
1095 if ( upper > lower )
1096 {
1097 size_t tmp = lower;
1098 lower = upper;
1099 upper = tmp;
1100 }
1101}
1102
1103void wxTextEditorModel::MergeChange( size_t fromRow, size_t nRows )
1104{
1105 if ( mTextChanged == FALSE )
1106 {
1107 mChangedFromRow = fromRow;
1108 mChangedTillRow = fromRow + nRows;
1109 mTextChanged = TRUE;
1110 }
1111 else
1112 {
1113 if ( mChangedFromRow > fromRow )
1114
1115 mChangedFromRow = fromRow;
1116
1117 if ( mChangedTillRow < fromRow + nRows )
1118
1119 mChangedTillRow = fromRow + nRows;
1120 }
1121}
1122
1123void wxTextEditorModel::TrackSelection()
1124{
1125 if ( !mIsSelectionEditMode ) return;
1126
1127 if ( mPrevCursorPos == mSelectionStart )
1128
1129 mSelectionStart = mCursorPos;
1130 else
1131 mSelectionEnd = mCursorPos;
1132
1133 ArrangePositions( mSelectionStart, mSelectionEnd );
1134
1135 NotifyTextChanged( mSelectionStart, mPrevSelectionStart, CT_MODIFIED );
1136 NotifyTextChanged( mSelectionEnd, mPrevSelectionEnd, CT_MODIFIED );
1137}
1138
1139void wxTextEditorModel::CheckSelection()
1140{
1141 ArrangePositions( mSelectionStart, mSelectionEnd );
1142
1143 if ( mIsSelectionEditMode && SelectionIsEmpty() )
1144 {
1145 mSelectionStart = mCursorPos;
1146 mSelectionEnd = mCursorPos;
1147 }
1148
1149 if ( !mIsSelectionEditMode && !SelectionIsEmpty() )
1150 {
1151 ResetSelection();
1152 }
1153
1154 mPrevSelectionStart = mSelectionStart;
1155 mPrevSelectionEnd = mSelectionEnd;
1156 mPrevCursorPos = mCursorPos;
1157}
1158
1159void wxTextEditorModel::ResetSelection()
1160{
1161 if ( SelectionIsEmpty() ) return;
1162
1163 MergeChange( mSelectionStart.mRow,
1164 mSelectionEnd.mRow - mSelectionStart.mRow + 1 );
1165
1166 NotifyTextChanged( mSelectionStart, mSelectionEnd, CT_MODIFIED );
1167
1168 mSelectionStart = TPosition(0,0);
1169 mSelectionEnd = TPosition(0,0);
1170
1171}
1172
1173void wxTextEditorModel::ClearUndoBuffer()
1174{
1175 for( size_t i = 0; i != mCommands.size(); ++i )
1176 {
1177 TCommand& cmd = *mCommands[i];
1178
1179 if ( cmd.mData ) delete [] cmd.mData;
1180
1181 delete &cmd;
1182 }
1183
1184 mCommands.erase( mCommands.begin(), mCommands.end() );
1185
1186 mCurCommand = 0;
1187}
1188
1189void wxTextEditorModel::GetAllText( char** text, size_t& textLen )
1190{
1191 GetTextFromRange( TPosition(0,0), TPosition( GetTotalRowCount()+1, 0 ),
1192 text, textLen
1193 );
1194}
1195
1196void wxTextEditorModel::DeleteAllText()
1197{
1198 ResetSelection();
1199
1200 DeleteRange( TPosition(0,0), TPosition( GetTotalRowCount()+1, 0 ) );
1201}
1202
1203void wxTextEditorModel::SetSelectionEditMode( bool editIsOn )
1204{
1205 mIsSelectionEditMode = editIsOn;
1206}
1207
1208size_t wxTextEditorModel::GetTotalRowCount()
1209{
1210 size_t nRows = 0;
1211
1212 for( TBlockIteratorT i = mBlocks.begin(); i != mBlocks.end(); ++i )
1213
1214 nRows += (*i).mRowCount;
1215
1216 return nRows;
1217}
1218
1219void wxTextEditorModel::GetSelection( char** text, size_t& textLen )
1220{
1221 GetTextFromRange( GetStartOfSelection(), GetEndOfSelection(), text, textLen );
1222}
1223
1224void wxTextEditorModel::NotifyView()
1225{
1226 mpActiveView->OnModelChanged();
1227}
1228
1229void wxTextEditorModel::NotifyAllViews()
1230{
1231 for( size_t i = 0; i != mViews.size(); ++i )
1232
1233 mViews[i]->OnModelChanged();
1234}
1235
1236void wxTextEditorModel::PrepreForCommand()
1237{
1238 mTextChanged = 0;
1239 mChangedFromRow = 0;
1240 mChangedTillRow = 0;
1241}
1242
1243size_t wxTextEditorModel::TextToScrColumn( const TPosition& pos )
1244{
1245 TPosition spos;
1246
1247 mpActiveView->TextPosToScreenPos( pos, spos );
1248
1249 return spos.mCol + mpActiveView->GetPagePos().mCol;
1250}
1251
1252size_t wxTextEditorModel::ScrToTextColumn( TPosition pos )
1253{
1254 TPosition tpos;
1255
1256 pos.mCol -= mpActiveView->GetPagePos().mCol;
1257 pos.mRow -= mpActiveView->GetPagePos().mRow;
1258
1259 mpActiveView->ScreenPosToTextPos( pos, tpos );
1260
1261 return tpos.mCol;
1262}
1263
1264void wxTextEditorModel::DoMoveCursor( int rows, int cols )
1265{
1266 mCursorPos.mCol = TextToScrColumn( mCursorPos );
1267
1268 mCursorPos.mRow += rows;
1269 mCursorPos.mCol += cols;
1270
1271 mCursorPos.mCol = ScrToTextColumn( mCursorPos );
1272}
1273
1274/*** public interface ***/
1275
1276wxTextEditorModel::wxTextEditorModel()
1277
1278 :
1279 mpActiveView( NULL ),
1280 mTabSize( 4 ),
1281 mIsSelectionEditMode( FALSE ),
1282 mRowsPerPage( 0 ),
1283 mTextChanged( FALSE ),
1284 mCurCommand( 0 ),
1285
1286 mInsertMode ( TRUE ),
1287 mAutoIndentMode ( TRUE ),
1288 mSmartIndentMode( TRUE ),
1289 mWasChanged ( FALSE ),
1290 mIsReadOnly ( FALSE ),
1291 mIsUnixText ( IS_UNIX_TEXT_BY_DEFAULT )
1292{
1293 // at least one block should be present
1294 // (otherwise text-iterators wont work)
1295
1296 mBlocks.push_back( TBlock() );
1297}
1298
1299wxTextEditorModel::~wxTextEditorModel()
1300{
1301 ClearUndoBuffer();
1302}
1303
1304char* wxTextEditorModel::AllocCharacters( size_t n )
1305{
1306 return new char[n];
1307}
1308
1309char* wxTextEditorModel::AllocCharacters( size_t n, const char* srcBuf )
1310{
1311 char* destBuf = AllocCharacters( n );
1312
1313 memcpy( destBuf, srcBuf, n );
1314
1315 return destBuf;
1316}
1317
1318void wxTextEditorModel::FreeCharacters( char* buf )
1319{
1320 delete [] buf;
1321}
1322
1323void wxTextEditorModel::OnInsertChar( char ch )
1324{
1325 if ( ch == 27 ) return; // hack
1326
1327 if ( is_DOS_eol_char( ch ) ) ch = '\n';
1328
1329 PrepreForCommand();
1330 StartBatch();
1331
1332 TCommand* pCmd = new TCommand();
1333
1334 pCmd->mType = TCMD_INSERT;
1335
1336 if ( ch == '\n' && !mIsUnixText )
1337 {
1338 // DOS text with CR-LF pair
1339 pCmd->mData = AllocCharacters( 2 );
1340 pCmd->mDataLen = 2;
1341 pCmd->mData[0] = (char)13;
1342 pCmd->mData[1] = (char)10;
1343 }
1344 else
1345 {
1346 pCmd->mData = AllocCharacters( 1 );
1347 pCmd->mDataLen = 1;
1348 pCmd->mData[0] = ch;
1349 }
1350
1351
1352 if ( !SelectionIsEmpty() )
1353 {
1354 mCursorPos = mSelectionStart;
1355 DeleteSelection();
1356 }
1357
1358 pCmd->mRange.mFrom = mCursorPos;
1359
1360 if ( mInsertMode == FALSE )
1361 {
1362 TPosition nextPos( mCursorPos.mRow, mCursorPos.mCol + 1 );
1363 DeleteRange( mCursorPos, nextPos );
1364
1365 SetPostPos( mCursorPos );
1366 }
1367
1368 TTextIterator iter = CreateIterator( mCursorPos );
1369
1370 size_t lineLen = iter.GetLineLen();
1371
1372 bool indentAdded = FALSE;
1373
1374 if ( mCursorPos.mCol > lineLen )
1375 {
1376
1377 wxString s( ' ', mCursorPos.mCol - lineLen );
1378 InsertText( TPosition( mCursorPos.mRow, lineLen ), s.c_str(), s.length() );
1379
1380 SetPostPos( mCursorPos );
1381
1382 indentAdded = TRUE;
1383 }
1384
1385 if ( CanPrependCommand( pCmd ) || indentAdded )
1386
1387 PrependCommand( pCmd );
1388 else
1389 ExecuteCommand( pCmd );
1390
1391 ++mCursorPos.mCol;
1392
1393 if ( is_eol_char( ch ) )
1394 {
1395 mCursorPos.mCol = 0;
1396 ++mCursorPos.mRow;
1397
1398 SetPostPos( mCursorPos );
1399
1400 if ( mAutoIndentMode )
1401 {
1402 iter.ToStartOfLine();
1403 wxString indent;
1404
1405 while( !iter.IsEol() )
1406 {
1407 char ch = iter.GetChar();
1408
1409 if ( ch == '\t' || ch == ' ' )
1410
1411 indent += ch;
1412 else
1413 break;
1414
1415 iter.NextChar();
1416 }
1417
1418 if ( indent.length() )
1419 {
1420 // auto-indent is always prepended to the command which
1421 // caused it
1422
1423 mCursorPos = TPosition( mCursorPos.mRow, 0 );
1424
1425
1426 TCommand* pICmd = new TCommand();
1427 pICmd->mType = TCMD_INSERT;
1428 pICmd->mData = AllocCharacters( indent.length() );
1429 pICmd->mDataLen = indent.length();
1430 memcpy( pICmd->mData, indent, indent.length() );
1431
1432 pICmd->mRange.mFrom = TPosition( mCursorPos.mRow, 0 );
1433
1434 PrependCommand( pICmd );
1435
1436 SetPostPos( mCursorPos );
1437
1438 mCursorPos.mCol = indent.length();
1439 }
1440 }
1441 }
1442 else
1443 SetPostPos( mCursorPos );
1444
1445 FinishBatch();
1446
1447 NotifyAllViews();
1448}
1449
1450void wxTextEditorModel::OnDelete()
1451{
1452 PrepreForCommand();
1453 StartBatch();
1454
1455 if ( !SelectionIsEmpty() )
1456 {
1457 TPosition startPos = mSelectionStart;
1458 DeleteSelection();
1459 mCursorPos = startPos;
1460 }
1461 else
1462 {
1463 TTextIterator iter = CreateIterator( mCursorPos );
1464
1465 if ( iter.GetLineLen() == mCursorPos.mCol && !iter.IsLastLine() )
1466 {
1467 TPosition nextPos( mCursorPos.mRow+1, 0 );
1468 DeleteRange( mCursorPos, nextPos );
1469 NotifyTextChanged( mCursorPos.mRow, 2, CT_DELETED );
1470 }
1471 else
1472 {
1473 TPosition nextPos( mCursorPos.mRow, mCursorPos.mCol + 1 );
1474 DeleteRange( mCursorPos, nextPos );
1475 }
1476 }
1477
1478 SetPostPos( mCursorPos );
1479
1480 FinishBatch();
1481
1482 NotifyAllViews();
1483}
1484
1485void wxTextEditorModel::OnDeleteBack()
1486{
1487 PrepreForCommand();
1488 StartBatch();
1489
1490 if ( !SelectionIsEmpty() )
1491 {
1492 mCursorPos = mSelectionStart;
1493 DeleteSelection();
1494 }
1495 else
1496 if ( !(mCursorPos == TPosition(0,0)) )
1497 {
1498 TPosition prevPos;
1499
1500 if ( mCursorPos.mCol == 0 )
1501 {
1502 TTextIterator iter = CreateIterator( mCursorPos );
1503 iter.PreviousChar();
1504
1505 prevPos = iter.GetPosition();
1506 }
1507 else
1508 prevPos = TPosition( mCursorPos.mRow, mCursorPos.mCol - 1 );
1509
1510 DeleteRange( prevPos, mCursorPos );
1511
1512 mCursorPos = prevPos;
1513 }
1514
1515 SetPostPos( mCursorPos );
1516
1517 FinishBatch();
1518
1519 NotifyAllViews();
1520
1521}
1522
1523void wxTextEditorModel::OnDeleteLine()
1524{
1525 PrepreForCommand();
1526 StartBatch();
1527
1528 DeleteSelection();
1529
1530 TTextIterator iter = CreateIterator( mCursorPos );
1531
1532 iter.ToStartOfLine();
1533
1534 TPosition from = iter.GetPosition();
1535
1536 iter.ToEndOfLine();
1537
1538 if ( iter.IsLastLine() == FALSE )
1539
1540 iter.NextChar(); // delete eol-char also, if it's not the last line
1541
1542 TPosition till = iter.GetPosition();
1543
1544 DeleteRange( from, till );
1545 SetPostPos( mCursorPos );
1546
1547 FinishBatch();
1548
1549 NotifyAllViews();
1550}
1551
1552void wxTextEditorModel::OnShiftSelectionIndent( bool left )
1553{
1554 if ( SelectionIsEmpty() ) return;
1555
1556 PrepreForCommand();
1557 StartBatch();
1558
1559 for( size_t row = mSelectionStart.mRow; row != mSelectionEnd.mRow; ++row )
1560 {
1561 TTextIterator iter = CreateIterator( TPosition( row, 0 ) );
1562
1563 if ( left )
1564 {
1565 int n = 0, pos = 0;
1566
1567 while( !iter.IsEol() && !iter.IsEof() )
1568 {
1569 char ch = iter.GetChar();
1570
1571 if ( pos == mTabSize ) break;
1572
1573 if ( ch != ' ' && ch != '\t' ) break;
1574
1575 ++n;
1576
1577 if ( ch == '\t' ) break;
1578
1579 ++pos;
1580
1581 iter.NextChar();
1582 }
1583
1584 if ( n ) DeleteRange( TPosition( row,0 ), TPosition( row, n ) );
1585 }
1586 else
1587 {
1588 char txt = '\t';
1589
1590 InsertText( TPosition( row, 0 ), &txt, sizeof(char) );
1591 }
1592 }
1593
1594 FinishBatch();
1595 NotifyAllViews();
1596}
1597
1598void wxTextEditorModel::OnPaste()
1599{
1600 // FIXME:: "wxLogQueryInterface(..)" linking problems with MSDev4.0
1601
1602#ifdef __HACK_MY_MSDEV40__
1603
1604 bool alreadyOpen=wxClipboardOpen();
1605 if (!alreadyOpen)
1606 {
1607 wxOpenClipboard();
1608 }
1609
1610 char* data = (char*)::wxGetClipboardData( wxDF_TEXT );
1611
1612 wxCloseClipboard();
1613
1614 if ( data == NULL ) return;
1615
1616 PrepreForCommand();
1617 StartBatch();
1618
1619 if ( !SelectionIsEmpty() )
1620 {
1621 mCursorPos = GetStartOfSelection();
1622 DeleteSelection();
1623 }
1624
1625 InsertText( mCursorPos, data, strlen( data ) );
1626
1627 delete [] data;
1628#else
1629
1630 if ( !wxTheClipboard->Open() ) return;
1631
1632 wxTextDataObject data;
1633 if ( !wxTheClipboard->IsSupported(wxDF_TEXT) )
1634 {
1635 wxTheClipboard->Close();
1636 return;
1637 }
1638
1639 wxTheClipboard->GetData(&data);
1640
1641 string txt = data.GetText();
1642
1643 wxTheClipboard->Close();
1644
1645 PrepreForCommand();
1646 StartBatch();
1647
1648 DeleteSelection();
1649
1650 InsertText( mCursorPos, txt.c_str(), txt.length() );
1651#endif
1652
1653
1654 mCursorPos = mCommands.back()->mRange.mTill;
1655 SetPostPos( mCursorPos );
1656
1657 FinishBatch();
1658 NotifyAllViews();
1659}
1660
1661void wxTextEditorModel::OnCut()
1662{
1663 OnCopy();
1664
1665 PrepreForCommand();
1666 StartBatch();
1667
1668 DeleteSelection();
1669 SetPostPos( mCursorPos );
1670
1671
1672 FinishBatch();
1673 NotifyAllViews();
1674}
1675
1676void wxTextEditorModel::OnCopy()
1677{
1678 if ( !SelectionIsEmpty() )
1679 {
1680 size_t len = 0;
1681 char* text = NULL;
1682
1683#ifndef __HACK_MY_MSDEV40__
1684
1685 if ( !wxTheClipboard->Open() ) return;
1686
1687 GetTextFromRange( mSelectionStart, mSelectionEnd, &text, len );
1688
1689 wxString s( text, len );
1690
1691 wxTheClipboard->AddData( new wxTextDataObject(s) );
1692 wxTheClipboard->Close();
1693
1694 FreeCharacters( text );
1695#else
1696 bool alreadyOpen=wxClipboardOpen();
1697 if (!alreadyOpen)
1698 {
1699 wxOpenClipboard();
1700 if (!wxEmptyClipboard())
1701 {
1702 wxCloseClipboard();
1703 return;
1704 }
1705 }
1706
1707 GetTextFromRange( mSelectionStart, mSelectionEnd, &text, len );
1708
1709 wxString s( text, len );
1710
1711 bool success = ::wxEmptyClipboard();
1712
1713 success = wxSetClipboardData( wxDF_TEXT, (wxObject*)s.c_str(), 0,0 );
1714
1715 FreeCharacters( text );
1716
1717 wxCloseClipboard();
1718
1719#endif
1720 }
1721}
1722
1723bool wxTextEditorModel::CanCopy()
1724{
1725 return !SelectionIsEmpty();
1726}
1727
1728bool wxTextEditorModel::CanPaste()
1729{
1730 if ( mIsReadOnly ) return FALSE;
1731
1732#ifndef __HACK_MY_MSDEV40__
1733
1734 if ( !wxTheClipboard->Open() ) return FALSE;
1735
1736 if ( !wxTheClipboard->IsSupported(wxDF_TEXT) )
1737 return FALSE;
1738
1739 wxTheClipboard->Close();
1740
1741 return TRUE;
1742
1743#else
1744
1745 bool success = ::wxClipboardOpen();
1746
1747 bool alreadyOpen=wxClipboardOpen();
1748 if (!alreadyOpen)
1749 {
1750 wxOpenClipboard();
1751 }
1752
1753 char* data = (char*)::wxGetClipboardData( wxDF_TEXT );
1754
1755 wxCloseClipboard();
1756
1757 if ( data != NULL && strlen(data) != 0 )
1758 {
1759 delete [] data;
1760 return TRUE;
1761 }
1762 else
1763 {
1764 delete [] data;
1765 return FALSE;
1766 }
1767
1768#endif
1769
1770}
1771
1772bool wxTextEditorModel::CanUndo()
1773{
1774 return !( mCommands.size() == 0 ||
1775 mCurCommand == 0 );
1776}
1777
1778bool wxTextEditorModel::CanRedo()
1779{
1780 return mCurCommand != mCommands.size();
1781}
1782
1783void wxTextEditorModel::OnUndo()
1784{
1785 if ( !CanUndo() ) return;
1786
1787 PrepreForCommand();
1788 StartBatch();
1789
1790 ResetSelection();
1791
1792 UndoImpl();
1793
1794 FinishBatch();
1795 NotifyAllViews();
1796}
1797
1798void wxTextEditorModel::OnRedo()
1799{
1800 if ( !CanRedo() ) return;
1801
1802 PrepreForCommand();
1803 StartBatch();
1804
1805 ResetSelection();
1806
1807 RedoImpl();
1808
1809 FinishBatch();
1810 NotifyAllViews();
1811}
1812
1813void wxTextEditorModel::OnMoveLeft()
1814{
1815 PrepreForCommand();
1816 CheckSelection();
1817
1818 if ( mCursorPos.mCol == 0 )
1819 {
1820 if ( mCursorPos.mRow != 0 )
1821 {
1822 --mCursorPos.mRow;
1823
1824 TTextIterator iter = CreateIterator( mCursorPos );
1825
1826 iter.ToEndOfLine();
1827
1828 mCursorPos.mCol = iter.GetPosition().mCol;
1829 }
1830 }
1831 else
1832 --mCursorPos.mCol;
1833
1834 TrackSelection();
1835 NotifyView();
1836}
1837
1838void wxTextEditorModel::OnMoveRight()
1839{
1840 PrepreForCommand();
1841 CheckSelection();
1842
1843 ++mCursorPos.mCol;
1844
1845 TrackSelection();
1846 NotifyView();
1847}
1848
1849void wxTextEditorModel::OnMoveUp()
1850{
1851 PrepreForCommand();
1852 CheckSelection();
1853
1854 if ( mCursorPos.mRow != 0 )
1855
1856 DoMoveCursor( -1,0 );
1857
1858 TrackSelection();
1859 NotifyView();
1860}
1861
1862void wxTextEditorModel::OnMoveDown()
1863{
1864 PrepreForCommand();
1865 CheckSelection();
1866
1867 if ( mCursorPos.mRow + 1 < GetTotalRowCount() )
1868
1869 DoMoveCursor( 1,0 );
1870
1871 TrackSelection();
1872 NotifyView();
1873}
1874
1875void wxTextEditorModel::OnWordRight()
1876{
1877 PrepreForCommand();
1878 CheckSelection();
1879
1880 TTextIterator iter = CreateIterator( mCursorPos );
1881
1882 iter.NextWord();
1883
1884 mCursorPos = iter.GetPosition();
1885
1886 TrackSelection();
1887 NotifyView();
1888}
1889
1890void wxTextEditorModel::OnWordLeft()
1891{
1892 PrepreForCommand();
1893 CheckSelection();
1894
1895 TTextIterator iter = CreateIterator( mCursorPos );
1896
1897 iter.PreviousWord();
1898
1899 mCursorPos = iter.GetPosition();
1900
1901 TrackSelection();
1902 NotifyView();
1903}
1904
1905void wxTextEditorModel::OnMoveToPosition( const TPosition& pos )
1906{
1907 PrepreForCommand();
1908 CheckSelection();
1909
1910 mCursorPos = pos;
1911
1912 TrackSelection();
1913 NotifyView();
1914}
1915
1916void wxTextEditorModel::OnEndOfLine()
1917{
1918 PrepreForCommand();
1919 CheckSelection();
1920
1921 TTextIterator iter = CreateIterator( mCursorPos );
1922 iter.ToEndOfLine();
1923
1924 mCursorPos = iter.GetPosition();
1925
1926 TrackSelection();
1927 NotifyView();
1928}
1929
1930void wxTextEditorModel::OnStartOfLine()
1931{
1932 PrepreForCommand();
1933 CheckSelection();
1934
1935 int prevCol = mCursorPos.mCol;
1936
1937 TTextIterator iter = CreateIterator( mCursorPos );
1938 iter.ToStartOfLine();
1939
1940 // bypass leading white-space at the begining of the line
1941
1942 while( !iter.IsEol() )
1943 {
1944 char ch = iter.GetChar();
1945
1946 if ( ch != ' ' && ch != '\t' ) break;
1947
1948 ++mCursorPos.mCol;
1949
1950 iter.NextChar();
1951 }
1952
1953 mCursorPos = iter.GetPosition();
1954
1955 if ( mCursorPos.mCol == prevCol )
1956
1957 mCursorPos.mCol = 0;
1958
1959 TrackSelection();
1960 NotifyView();
1961}
1962
1963void wxTextEditorModel::OnPageUp()
1964{
1965 PrepreForCommand();
1966 CheckSelection();
1967
1968 if ( mCursorPos.mRow < mRowsPerPage )
1969
1970 mCursorPos.mRow = 0;
1971 else
1972 DoMoveCursor( -mRowsPerPage,0 );
1973
1974 mpActiveView->ScrollView( -(int)mRowsPerPage, 0 );
1975
1976 TrackSelection();
1977 NotifyView();
1978}
1979
1980void wxTextEditorModel::OnPageDown()
1981{
1982 PrepreForCommand();
1983 CheckSelection();
1984
1985 if ( mCursorPos.mRow + mRowsPerPage >= GetTotalRowCount() )
1986 {
1987 if ( GetTotalRowCount() != 0 )
1988
1989 mCursorPos.mRow = GetTotalRowCount() - 1;
1990 else
1991 mCursorPos.mRow = 0;
1992 }
1993 else
1994 DoMoveCursor( mRowsPerPage,0 );
1995
1996 mpActiveView->ScrollView( mRowsPerPage, 0 );
1997
1998 TrackSelection();
1999 NotifyView();
2000}
2001
2002void wxTextEditorModel::OnSlideUp()
2003{
2004 PrepreForCommand();
2005
2006 if ( mpActiveView->GetPagePos().mRow + mRowsPerPage - 1 == mCursorPos.mRow )
2007 {
2008 if ( mCursorPos.mRow == 0 )
2009
2010 return;
2011
2012 DoMoveCursor( -1,0 );
2013 }
2014
2015 mpActiveView->ScrollView( -1, 0 );
2016
2017 NotifyView();
2018}
2019
2020void wxTextEditorModel::OnSlideDown()
2021{
2022 PrepreForCommand();
2023
2024 if ( mCursorPos.mRow == mpActiveView->GetPagePos().mRow )
2025 {
2026 if ( mCursorPos.mRow + 1 >= GetTotalRowCount() )
2027
2028 return;
2029
2030 DoMoveCursor( 1,0 );
2031 }
2032
2033 mpActiveView->ScrollView( 1, 0 );
2034
2035 NotifyView();
2036}
2037
2038void wxTextEditorModel::OnStartOfText()
2039{
2040 PrepreForCommand();
2041 CheckSelection();
2042
2043 mCursorPos.mRow = mCursorPos.mCol = 0;
2044
2045 TrackSelection();
2046 NotifyView();
2047}
2048
2049void wxTextEditorModel::OnEndOfText()
2050{
2051 PrepreForCommand();
2052 CheckSelection();
2053
2054 mCursorPos.mRow = GetTotalRowCount() - 1;
2055
2056 TTextIterator iter = CreateIterator( mCursorPos );
2057
2058 iter.ToEndOfLine();
2059
2060 mCursorPos = iter.GetPosition();
2061
2062 TrackSelection();
2063 NotifyView();
2064}
2065
2066void wxTextEditorModel::OnSelectWord()
2067{
2068 PrepreForCommand();
2069
2070 TTextIterator iter1 = CreateIterator( mCursorPos );
2071 iter1.GotoClosestPos();
2072
2073 if ( mCursorPos == iter1.GetPosition() )
2074 {
2075 TTextIterator iter2 = iter1;
2076
2077 // find the left-edge of the word
2078
2079 bool wasSeparator = TTextIterator::IsSeparator( iter1.GetChar() );
2080
2081 while( !iter1.IsEol() )
2082 {
2083 char ch = iter1.GetChar();
2084
2085 if ( ch == '\t' ||
2086 ch == ' ' ||
2087 wasSeparator != TTextIterator::IsSeparator( iter1.GetChar() )
2088 )
2089 {
2090 iter1.NextChar();
2091 break;
2092 }
2093
2094 iter1.PreviousChar();
2095 }
2096
2097 // find the left-edge of the word
2098
2099 while( !iter2.IsEol() )
2100 {
2101 char ch = iter2.GetChar();
2102
2103 if ( ch == '\t' ||
2104 ch == ' ' ||
2105 wasSeparator != TTextIterator::IsSeparator( iter2.GetChar() )
2106 )
2107 break;
2108
2109 iter2.NextChar();
2110 }
2111
2112 if ( !(iter1.GetPosition() == iter2.GetPosition()) )
2113 {
2114 mSelectionStart = iter1.GetPosition();
2115 mSelectionEnd = iter2.GetPosition();
2116 mCursorPos = iter2.GetPosition();
2117
2118 NotifyTextChanged( mSelectionStart.mRow, 1, CT_MODIFIED );
2119 }
2120 }
2121
2122 NotifyView();
2123}
2124
2125void wxTextEditorModel::OnSelectAll()
2126{
2127 PrepreForCommand();
2128
2129 ResetSelection();
2130
2131 mSelectionStart = TPosition(0,0);
2132 mSelectionEnd = TPosition( GetTotalRowCount(), 1024 ); // FOR NOW:: hack
2133
2134 mCursorPos = mSelectionStart;
2135
2136 NotifyTextChanged( mSelectionStart.mRow, mSelectionEnd.mRow, CT_MODIFIED );
2137
2138 NotifyView();
2139}
2140
2141void wxTextEditorModel::OnToggleBookmark()
2142{
2143 size_t curRow = GetCursor().mRow;
2144
2145 if ( GetPinAt( curRow, TBookmarkPin::GetPinTypeCode() ) != NULL )
2146
2147 RemovePinAt( curRow, TBookmarkPin::GetPinTypeCode() );
2148 else
2149 AddPin( new TBookmarkPin( curRow ) );
2150
2151 MergeChange( curRow, 1 );
2152
2153 NotifyAllViews();
2154}
2155
2156void wxTextEditorModel::OnNextBookmark()
2157{
2158 size_t pinNo = FindNextPinFrom( mCursorPos.mRow + 1 );
2159
2160 while( pinNo != NPOS )
2161 {
2162 TPinBase& pin = *mPins[pinNo];
2163
2164 if ( pin.mTypeCode == BOOKMARK_PIN_TC )
2165 {
2166 OnGotoLine( pin.mRow, 0 );
2167 break;
2168 }
2169
2170 if ( pinNo == mPins.size() ) break;
2171
2172 ++pinNo;
2173 }
2174}
2175
2176void wxTextEditorModel::OnPreviousBookmark()
2177{
2178 if ( mCursorPos.mRow == 0 ) return;
2179
2180 size_t pinNo = FindPreviousPinFrom( mCursorPos.mRow - 1 );
2181
2182 while( pinNo != NPOS )
2183 {
2184 TPinBase& pin = *mPins[pinNo];
2185
2186 if ( pin.mTypeCode == BOOKMARK_PIN_TC )
2187 {
2188 OnGotoLine( pin.mRow, 0 );
2189 break;
2190 }
2191
2192 if ( pinNo == 0 ) break;
2193
2194 --pinNo;
2195 }
2196}
2197
2198bool wxTextEditorModel::OnFind()
2199{
2200 if ( !SelectionIsEmpty() )
2201 {
2202 if ( GetStartOfSelection().mRow == GetEndOfSelection().mRow )
2203 {
2204 char* buf = NULL; size_t len = 0;
2205
2206 GetSelection( &buf, len );
2207
2208 mLastFindExpr = string( buf, 0, len );
2209
2210 delete [] buf;
2211 }
2212 }
2213
2214 wxFindTextDialog dlg( GetActiveView(), mLastFindExpr );
2215 //dlg.SetExpr( mLastFindExpr );
2216
2217 if( dlg.ShowModal() == wxID_OK )
2218 {
2219 mLastFindExpr = dlg.GetExpr();
2220
2221 GetActiveView()->SetFocus();
2222
2223 return OnFindNext();
2224 }
2225
2226 GetActiveView()->SetFocus();
2227
2228 return FALSE;
2229}
2230
2231bool wxTextEditorModel::OnFindNext()
2232{
2233 PrepreForCommand();
2234
2235 string& val = mLastFindExpr;
2236 size_t len = val.length();
2237
2238 if ( len == 0 )
2239 {
2240 NotifyView();
2241 wxMessageBox( "Secarch string not found!" );
2242
2243 GetActiveView()->SetFocus();
2244
2245 return FALSE;
2246 }
2247
2248 char ch1 = val[0];
2249
2250 TTextIterator iter = CreateIterator( mCursorPos );
2251
2252 while( !iter.IsEof() )
2253 {
2254 char ch = iter.GetChar();
2255
2256 if ( ch == ch1 )
2257 {
2258 size_t startCol = iter.mPos.mCol;
2259 iter.NextChar();
2260 ch = iter.GetChar();
2261
2262 size_t i = 1;
2263 while( i < len && !iter.IsEof() && ch == val[i] )
2264 {
2265 ++i;
2266 iter.NextChar();
2267 ch = iter.GetChar();
2268 }
2269
2270 if ( i == len )
2271 {
2272 if ( !SelectionIsEmpty() )
2273
2274 ResetSelection();
2275
2276 SetStartOfSelection( TPosition( iter.mPos.mRow, startCol ) );
2277 SetEndOfSelection( iter.mPos );
2278
2279 MergeChange( iter.mPos.mRow, 1 );
2280
2281 mCursorPos = iter.mPos;
2282
2283 OnGotoLine( iter.mPos.mRow, iter.mPos.mCol );
2284 return TRUE;
2285 }
2286 }
2287 else
2288 iter.NextChar();
2289 }
2290
2291 NotifyView();
2292 MergeChange( mCursorPos.mRow, 2 );
2293 wxMessageBox( "Secarch string not found!" );
2294
2295 GetActiveView()->SetFocus();
2296
2297 return FALSE;
2298}
2299
2300bool wxTextEditorModel::OnFindPrevious()
2301{
2302 // TBD::
2303 return FALSE;
2304}
2305
2306void wxTextEditorModel::OnGotoLine( int line, int col )
2307{
2308 if ( mpActiveView == NULL ) return;
2309
2310 TPosition pagePos = mpActiveView->GetPagePos();
2311
2312 if ( line >= pagePos.mRow &&
2313 line < pagePos.mRow + mRowsPerPage )
2314 {
2315 mCursorPos.mRow = (size_t)line;
2316 mCursorPos.mCol = (size_t)col;
2317
2318 if ( col == - 1)
2319 {
2320 mCursorPos.mCol = 0;
2321 OnStartOfLine();
2322 }
2323 else
2324 NotifyView();
2325
2326 return;
2327 }
2328
2329 size_t third = mRowsPerPage / 3;
2330 size_t newTop = 0;
2331
2332 if ( line < third )
2333
2334 newTop = 0;
2335 else
2336 newTop = line - third;
2337
2338
2339 mpActiveView->ScrollView( (int)newTop - (int)pagePos.mRow, -(int)pagePos.mCol );
2340
2341 mCursorPos.mRow = line;
2342 mCursorPos.mCol = col;
2343
2344 if ( col == - 1)
2345 {
2346 mCursorPos.mCol = 0;
2347 OnStartOfLine();
2348 }
2349 else
2350 NotifyView();
2351}
2352
2353void wxTextEditorModel::OnGotoLine()
2354{
2355 wxTextEntryDialog* dlg =
2356 new wxTextEntryDialog( mpActiveView, "Line number:", "Goto line", "" );
2357
2358 int nTries = 3;
2359
2360 while( dlg->ShowModal() == wxID_OK && nTries )
2361 {
2362 ResetSelection();
2363
2364 int i = -1;
2365 sscanf( dlg->GetValue(), "%d", &i );
2366
2367 if ( i == -1 )
2368 {
2369 wxMessageBox( "Please enter a number" );
2370 continue;
2371 }
2372
2373
2374 if ( i == 0 ) ++i;
2375
2376 OnGotoLine( (size_t)(i-1), 0 );
2377 break;
2378
2379 --nTries;
2380 }
2381
2382 GetActiveView()->SetFocus();
2383}
2384
2385
2386bool wxTextEditorModel::IsReadOnly()
2387{
2388 return mIsReadOnly;
2389}
2390
2391bool wxTextEditorModel::IsModified()
2392{
2393 return mCurCommand != 0;
2394}
2395
2396bool wxTextEditorModel::IsInsertMode()
2397{
2398 return mInsertMode;
2399}
2400
2401void wxTextEditorModel::SetCheckpoint()
2402{
2403 mCheckPointDestroyed = FALSE;
2404 mCheckPointCmdNo = mCurCommand;
2405}
2406
2407bool wxTextEditorModel::CheckpointModified()
2408{
2409 if ( mCheckPointDestroyed ) return TRUE;
2410
2411 return mCheckPointCmdNo != mCurCommand;
2412}
2413
2414TPosition wxTextEditorModel::GetStartOfSelection()
2415{
2416 ArrangePositions( mSelectionStart, mSelectionEnd );
2417
2418 return mSelectionStart;
2419}
2420
2421TPosition wxTextEditorModel::GetEndOfSelection()
2422{
2423 ArrangePositions( mSelectionStart, mSelectionEnd );
2424
2425 return mSelectionEnd;
2426}
2427
2428TPosition wxTextEditorModel::GetCursor()
2429{
2430 return mCursorPos;
2431}
2432
2433void wxTextEditorModel::SetStartOfSelection( const TPosition& pos )
2434{
2435 mSelectionStart = pos;
2436}
2437
2438void wxTextEditorModel::SetEndOfSelection( const TPosition& pos )
2439{
2440 mSelectionEnd = pos;
2441}
2442
2443void wxTextEditorModel::SetCursor( const TPosition& pos )
2444{
2445 mCursorPos = pos;
2446}
2447
2448void wxTextEditorModel::AddView( wxTextEditorView* pView )
2449{
2450 mViews.push_back( pView );
2451 pView->SetModel( this );
2452}
2453
2454void wxTextEditorModel::RemoveView( wxTextEditorView* pView )
2455{
2456 for( size_t i = 0; i != mViews.size(); ++i )
2457
2458 if ( mViews[i] == pView )
2459 {
2460 mViews.erase( & mViews[i] );
2461 return;
2462 }
2463}
2464
2465void wxTextEditorModel::SetActiveView( wxTextEditorView* pView )
2466{
2467 mpActiveView = pView;
2468}
2469
2470wxTextEditorView* wxTextEditorModel::GetActiveView()
2471{
2472 return mpActiveView;
2473}
2474
2475void wxTextEditorModel::SetRowsPerPage( size_t n )
2476{
2477 mRowsPerPage = n;
2478}
2479
2480void wxTextEditorModel::AddPin( TPinBase* pPin )
2481{
2482 // FIXME:: binary search should be used
2483
2484 size_t beforePin = FindNextPinFrom( pPin->mRow );
2485
2486 if ( beforePin != NPOS )
2487 {
2488 // pins in the same row are ordered in the
2489 // descending order of their type-codes
2490
2491 while( beforePin < mPins.size() &&
2492 mPins[beforePin]->mRow == pPin->mRow &&
2493 mPins[beforePin]->mTypeCode < pPin->mTypeCode )
2494
2495 ++beforePin;
2496
2497 if ( beforePin < mPins.size() )
2498
2499 mPins.insert( &mPins[beforePin], pPin );
2500 else
2501 mPins.push_back( pPin );
2502 }
2503 else
2504 mPins.push_back( pPin );
2505}
2506
2507PinListT& wxTextEditorModel::GetPins()
2508{
2509 return mPins;
2510}
2511
2512size_t wxTextEditorModel::FindFirstPinInRange( size_t fromRow, size_t tillRow )
2513{
2514 // FIXME:: pefrom binary search instead
2515
2516 for( size_t i = 0; i != mPins.size(); ++i )
2517 {
2518 TPinBase& pin = *mPins[i];
2519
2520 if ( pin.mRow >= tillRow ) return NPOS;
2521
2522 if ( pin.mRow >= fromRow )
2523
2524 return i;
2525 }
2526
2527 return NPOS;
2528}
2529
2530size_t wxTextEditorModel::FindNextPinFrom( size_t fromRow )
2531{
2532 // FIXME:: pefrom binary search instead
2533
2534 for( size_t i = 0; i != mPins.size(); ++i )
2535 {
2536 TPinBase& pin = *mPins[i];
2537
2538 if ( pin.mRow >= fromRow )
2539
2540 return i;
2541 }
2542
2543 return NPOS;
2544
2545}
2546
2547size_t wxTextEditorModel::FindPreviousPinFrom( size_t fromRow )
2548{
2549 // FIXME:: pefrom binary search instead
2550
2551 if ( mPins.size() == 0 ) return NPOS;
2552
2553 size_t i = mPins.size() - 1;
2554
2555 for(;;)
2556 {
2557 TPinBase& pin = *mPins[i];
2558
2559 if ( pin.mRow <= fromRow )
2560
2561 return i;
2562
2563 if ( i == 0 ) break;
2564
2565 --i;
2566 }
2567
2568 return NPOS;
2569}
2570
2571size_t wxTextEditorModel::GetPinNoAt( size_t row, int pinTypeCode )
2572{
2573 size_t curPin = FindNextPinFrom( row );
2574
2575 while( curPin != NPOS )
2576 {
2577 TPinBase& pin = *mPins[curPin];
2578
2579 if ( pin.mRow > row ) return NPOS;
2580
2581 if ( pin.mTypeCode == pinTypeCode ) return curPin;
2582
2583 ++curPin;
2584
2585 if ( curPin == mPins.size() ) return NPOS;
2586 }
2587
2588 return NPOS;
2589}
2590
2591TPinBase* wxTextEditorModel::GetPinAt( size_t row, int pinTypeCode )
2592{
2593 size_t pinNo = GetPinNoAt( row, pinTypeCode );
2594
2595 return ( pinNo == NPOS ) ? NULL : mPins[pinNo];
2596}
2597
2598void wxTextEditorModel::RemovePinAt( size_t row, int pinTypeCode )
2599{
2600 size_t pinNo = GetPinNoAt( row, pinTypeCode );
2601
2602 if ( pinNo != NPOS )
2603
2604 mPins.erase( &mPins[pinNo] );
2605}
2606
2607void wxTextEditorModel::AddChangeListener( TTextChangeListenerBase* pListener )
2608{
2609 mChangeListeners.push_back( pListener );
2610}
2611
2612/***** Implementation for class wxTextEditorView *****/
2613
2614BEGIN_EVENT_TABLE( wxTextEditorView, wxScrolledWindow )
2615
2616 EVT_SIZE ( wxTextEditorView::OnSize )
2617#if (( wxVERSION_NUMBER < 2100 ) || (( wxVERSION_NUMBER == 2100 ) && (wxBETA_NUMBER <= 4)))
2618 EVT_SCROLL( wxTextEditorView::OnScroll )
2619#else
2620 EVT_SCROLLWIN( wxTextEditorView::OnScroll )
2621#endif
2622 EVT_PAINT ( wxTextEditorView::OnPaint )
2623
2624 EVT_LEFT_DOWN ( wxTextEditorView::OnLButtonDown )
2625 EVT_LEFT_UP ( wxTextEditorView::OnLButtonUp )
2626 EVT_MOTION ( wxTextEditorView::OnMotion )
2627 EVT_LEFT_DCLICK( wxTextEditorView::OnDblClick )
2628
2629 EVT_SET_FOCUS ( wxTextEditorView::OnSetFocus )
2630 EVT_KILL_FOCUS ( wxTextEditorView::OnKillFocus )
2631
2632 EVT_CHAR( wxTextEditorView::OnChar )
2633 EVT_KEY_DOWN( wxTextEditorView::OnKeyDown )
2634
2635 EVT_ERASE_BACKGROUND( wxTextEditorView::OnEraseBackground )
2636
2637
2638END_EVENT_TABLE()
2639
2640TCursorTimer* wxTextEditorView::mpTimer = new TCursorTimer();
2641
2642wxTextEditorView::wxTextEditorView( wxWindow* parent,
2643 wxWindowID id,
2644 wxTextEditorModel* pModel,
2645 int wndStyle,
2646 bool ownsModel )
2647
2648 : wxScrolledWindow( parent, id, wxPoint(32768,32768), wxSize(0,0),
2649 wxHSCROLL | wxVSCROLL | wndStyle
2650 ),
2651 mPagePos( 0,0 ),
2652 mDragStarted( FALSE ),
2653 mpDraggedText( NULL ),
2654 mAdjustScrollPending( FALSE ),
2655 mLTMode( FALSE ),
2656 mMaxColumns( 500 ),
2657
2658 mScrollingOn( TRUE ),
2659 mCursorOn ( TRUE ),
2660 mOwnsModel ( ownsModel ),
2661
2662 mLastRowsTotal( (size_t)(-1) )
2663{
2664 SetModel( pModel );
2665
2666 SetTextDefaults();
2667
2668 SetSourcePainter( new SourcePainter() );
2669
2670 mCashedIter.mPos = TPosition( (size_t)(-1), 0 );
2671
2672 // default
2673 AddPinPainter( new TBookmarkPainter() );
2674}
2675
2676wxTextEditorView::~wxTextEditorView()
2677{
2678 if ( mpTimer->GetView() == this &&
2679 mCursorOn && !mLTMode )
2680 {
2681 mpTimer->SetView( NULL );
2682 mpTimer->HideCursor( TRUE );
2683 }
2684
2685 if ( mOwnsModel && mpModel )
2686
2687 delete mpModel;
2688}
2689
2690void wxTextEditorView::SetTextDefaults()
2691{
2692 mLeftMargin = 22;
2693 mRightMargin = 0;
2694 mTopMargin = 0;
2695 mBottomMargin = 0;
2696
2697 mCharDim.x = -1; // not detected yet
2698 mCharDim.y = -1;
2699
2700 mNormalTextCol = *wxBLACK;
2701 mIndentifierTextCol = *wxBLUE;
2702 mReservedWordTextCol = *wxRED;
2703 mCommentTextCol = wxColour( 0,128,128 );
2704
2705 mNormalBkCol = wxColour(255,255,255);//*wxWHITE;//wxColour( 128,220,128 );
2706 mSelectionFgCol = wxColour(255,255,255);//*wxWHITE;
2707 mSelectionBkCol = wxColour( 0,0,128 );
2708
2709 mNormalBkBrush = wxBrush( mNormalBkCol, wxSOLID );
2710 mSelectedBkBrush = wxBrush( mSelectionBkCol, wxSOLID );
2711
2712#if defined(__WXMSW__) || defined(__WINDOWS__)
2713 mFont.SetFaceName("Fixedsys");
2714 mFont.SetStyle(40);
2715 mFont.SetWeight(40);
2716 mFont.SetPointSize( 11);
2717#else
2718 //mFont.SetFamily( wxSWISS );
2719 mFont = wxSystemSettings::GetSystemFont(wxSYS_OEM_FIXED_FONT);
2720#endif
2721
2722
2723#if defined(__WXMSW__) || defined(__WINDOWS__)
2724 mFont.RealizeResource();
2725#endif
2726
2727 // reduce flicker un wxGtk
2728 SetBackgroundColour( mNormalBkCol );
2729}
2730
2731void wxTextEditorView::SetColours( const wxColour& normalBkCol,
2732 const wxColour& selectedBkCol,
2733 const wxColour& selectedTextCol )
2734{
2735 mNormalBkCol = normalBkCol;
2736 mSelectionFgCol = selectedTextCol;
2737 mSelectionBkCol = selectedBkCol;
2738
2739 mNormalBkBrush = wxBrush( mNormalBkCol, wxSOLID );
2740 mSelectedBkBrush = wxBrush( mSelectionBkCol, wxSOLID );
2741}
2742
2743void wxTextEditorView::SetHeighlightingColours( const wxColour& normalTextCol,
2744 const wxColour& identifierTextCol,
2745 const wxColour& reservedWordTextCol,
2746 const wxColour& commentTextCol )
2747{
2748 mNormalTextCol = normalTextCol;
2749 mIndentifierTextCol = identifierTextCol;
2750 mReservedWordTextCol = reservedWordTextCol;
2751 mCommentTextCol = commentTextCol;
2752}
2753
2754void wxTextEditorView::SetMargins( int top, int left, int bottom, int right )
2755{
2756 mLeftMargin = left;
2757 mRightMargin = right;
2758 mTopMargin = top;
2759 mBottomMargin = bottom;
2760}
2761
2762void wxTextEditorView::RecalcPagingInfo()
2763{
2764 bool firstRefresh = mCharDim.x == -1;
2765
2766 if ( firstRefresh )
2767
2768 ObtainFontProperties();
2769
2770 int w = 0, h = 0;
2771 GetClientSize( &w, &h );
2772
2773 w -= mLeftMargin + mRightMargin;
2774 h -= mTopMargin + mBottomMargin;
2775
2776 mColsPerPage = ( ( w / mCharDim.x ) +
2777 ( ( w % mCharDim.x ) ? 0 : 0 ) );
2778
2779
2780 mRowsPerPage = ( ( h / mCharDim.y ) +
2781 ( ( h % mCharDim.y ) ? 0 : 0 ) );
2782
2783 if ( mpModel->GetActiveView() == this )
2784
2785 mpModel->SetRowsPerPage( mRowsPerPage );
2786
2787 if ( firstRefresh )
2788 {
2789 // scrolling should not happen at DC-level
2790 EnableScrolling( FALSE, FALSE );
2791
2792 if ( mScrollingOn )
2793
2794 SetScrollbars( mCharDim.x, mCharDim.y,
2795 mMaxColumns,
2796 mpModel->GetTotalRowCount(),
2797 mPagePos.mCol,
2798 mPagePos.mRow,
2799 TRUE
2800 );
2801 }
2802
2803 PositionCursor();
2804}
2805
2806#if (( wxVERSION_NUMBER < 2100 ) || (( wxVERSION_NUMBER == 2100 ) && (wxBETA_NUMBER <= 4)))
2807 // this changed in ver 2.1
2808void wxTextEditorView::OnScroll( wxScrollEvent& event )
2809#else
2810void wxTextEditorView::OnScroll( wxScrollWinEvent& event )
2811#endif
2812{
2813 if ( !mScrollingOn ) return;
2814
2815 // overriden implementation of wxScrolledWindow::OnScroll,
2816 // to reduce flicker on wxGtk, by using wxClientDC
2817 // instead of Refresh()
2818
2819 int orient = event.GetOrientation();
2820
2821 int nScrollInc = CalcScrollInc(event);
2822 if (nScrollInc == 0) return;
2823
2824 if (orient == wxHORIZONTAL)
2825 {
2826 int newPos = m_xScrollPosition + nScrollInc;
2827 SetScrollPos(wxHORIZONTAL, newPos, TRUE );
2828 }
2829 else
2830 {
2831 int newPos = m_yScrollPosition + nScrollInc;
2832 SetScrollPos(wxVERTICAL, newPos, TRUE );
2833 }
2834
2835 if (orient == wxHORIZONTAL)
2836 {
2837 m_xScrollPosition += nScrollInc;
2838 }
2839 else
2840 {
2841 m_yScrollPosition += nScrollInc;
2842 }
2843
2844 int x,y;
2845 ViewStart( &x, &y );
2846
2847 mPagePos.mRow = y;
2848 mPagePos.mCol = x;
2849
2850 PositionCursor();
2851
2852 if ( mAdjustScrollPending )
2853 {
2854 mLastRowsTotal = mpModel->GetTotalRowCount();
2855 SetScrollbars( mCharDim.x, mCharDim.y,
2856 mMaxColumns, // FOR NOW:: maximal line-length not calculated
2857 mLastRowsTotal,
2858 mPagePos.mCol,
2859 mPagePos.mRow,
2860 TRUE
2861 );
2862
2863 mLastViewStart = mPagePos;
2864
2865 mAdjustScrollPending = FALSE;
2866
2867 return;
2868 }
2869
2870 wxClientDC dc( this );
2871
2872 mFullRefreshPending = TRUE;
2873
2874 PaintRows( mPagePos.mRow, mPagePos.mRow + mRowsPerPage, dc );
2875}
2876
2877void wxTextEditorView::OnPaint( wxPaintEvent& event )
2878{
2879 //wxScrolledWindow::OnPaint( event );
2880 if ( mCharDim.x == -1 ) ObtainFontProperties();
2881
2882 wxPaintDC dc( this );
2883
2884 mFullRefreshPending = TRUE;
2885
2886 PaintRows( mPagePos.mRow, mPagePos.mRow + mRowsPerPage, dc );
2887}
2888
2889void wxTextEditorView::OnSize( wxSizeEvent& event )
2890{
2891 RecalcPagingInfo();
2892
2893 SyncScrollbars();
2894
2895 event.Skip();
2896}
2897
2898void wxTextEditorView::OnEraseBackground( wxEraseEvent& event )
2899{
2900#if 0
2901 int w = 0, h = 0;
2902
2903 GetClientSize( &w, &h );
2904
2905 wxPaintDC dc( this );
2906
2907 dc.SetPen( *wxTRANSPARENT_PEN );
2908 dc.SetBrush( *wxWHITE_BRUSH );
2909 dc.DrawRectangle( 0,0, w,h );
2910#endif
2911}
2912
2913void wxTextEditorView::OnLButtonDown( wxMouseEvent& event )
2914{
2915 if ( mDragStarted ) return;
2916
2917 mDragStarted = TRUE;
2918
2919 TPosition textPos;
2920 PixelsToTextPos( event.m_x, event.m_y, textPos );
2921
2922 mpModel->SetSelectionEditMode( FALSE );
2923
2924 mpModel->OnMoveToPosition( textPos );
2925
2926 mpModel->SetSelectionEditMode( TRUE );
2927
2928 SetFocus();
2929
2930 CaptureMouse();
2931}
2932
2933void wxTextEditorView::OnLButtonUp( wxMouseEvent& event )
2934{
2935 if ( mDragStarted )
2936 {
2937 OnMotion( event ); // simulate last motion event
2938
2939 mpModel->SetSelectionEditMode( FALSE );
2940
2941 ReleaseMouse();
2942 mDragStarted = FALSE;
2943 }
2944}
2945
2946void wxTextEditorView::OnMotion( wxMouseEvent& event )
2947{
2948 if ( mDragStarted )
2949 {
2950 TPosition textPos;
2951
2952 if ( event.m_y < 0 && mpModel->GetCursor().mRow == 0 )
2953
2954 event.m_y = 0;
2955
2956 PixelsToTextPos( event.m_x, event.m_y, textPos );
2957
2958 mpModel->OnMoveToPosition( textPos );
2959 }
2960}
2961
2962void wxTextEditorView::OnDblClick( wxMouseEvent& event )
2963{
2964 event.Skip();
2965 mpModel->OnSelectWord();
2966}
2967
2968void wxTextEditorView::OnSetFocus( wxFocusEvent& event )
2969{
2970 if ( !mLTMode && mCursorOn )
2971 {
2972 mpTimer->SetView( this );
2973 mpTimer->ShowCursor( TRUE );
2974 }
2975}
2976
2977void wxTextEditorView::OnKillFocus( wxFocusEvent& event )
2978{
2979 if ( !mLTMode && mCursorOn )
2980 {
2981 mpTimer->HideCursor( TRUE );
2982 mpTimer->SetView( NULL );
2983 }
2984}
2985
2986void wxTextEditorView::HoldCursor( bool hold )
2987{
2988 if ( mLTMode || !mCursorOn ) return;
2989
2990 if ( !hold )
2991 {
2992 if ( wxWindow::FindFocus() != this )
2993 {
2994 mpTimer->HideCursor();
2995 mpTimer->SetView( NULL );
2996 }
2997 }
2998 else
2999 {
3000 mpTimer->SetView( this );
3001 mpTimer->ShowCursor();
3002 }
3003}
3004
3005void wxTextEditorView::OnKeyDown( wxKeyEvent& event )
3006{
3007 // FOR NOW:: hard-coded key-bindings
3008
3009 mpModel->SetSelectionEditMode( event.ShiftDown() );
3010
3011 if ( event.ControlDown() )
3012 {
3013 if ( event.m_keyCode == WXK_LEFT )
3014
3015 mpModel->OnWordLeft();
3016 else
3017 if ( event.m_keyCode == WXK_RIGHT )
3018
3019 mpModel->OnWordRight();
3020 else
3021 if ( event.m_keyCode == WXK_UP )
3022
3023 mpModel->OnSlideUp();
3024 else
3025 if ( event.m_keyCode == WXK_DOWN )
3026
3027 mpModel->OnSlideDown();
3028 else
3029 if ( event.m_keyCode == WXK_HOME )
3030
3031 mpModel->OnStartOfText();
3032 else
3033 if ( event.m_keyCode == WXK_END )
3034
3035 mpModel->OnEndOfText();
3036 else
3037 if ( event.m_keyCode == WXK_INSERT )
3038
3039 mpModel->OnCopy();
3040 else
3041 event.Skip();
3042
3043 /*
3044 else
3045 if ( event.m_keyCode == WXK_NEXT )
3046
3047 mpModel->();
3048 else
3049 if ( event.m_keyCode == WXK_PRIOR )
3050
3051 mpModel->();
3052 */
3053 }
3054 else
3055 {
3056 if ( event.m_keyCode == WXK_LEFT )
3057
3058 mpModel->OnMoveLeft();
3059 else
3060 if ( event.m_keyCode == WXK_RIGHT )
3061
3062 mpModel->OnMoveRight();
3063 else
3064 if ( event.m_keyCode == WXK_UP )
3065
3066 mpModel->OnMoveUp();
3067 else
3068 if ( event.m_keyCode == WXK_DOWN )
3069
3070 mpModel->OnMoveDown();
3071 else
3072 if ( event.m_keyCode == WXK_HOME )
3073
3074 mpModel->OnStartOfLine();
3075 else
3076 if ( event.m_keyCode == WXK_END )
3077
3078 mpModel->OnEndOfLine();
3079 else
3080 if ( event.m_keyCode == WXK_NEXT )
3081
3082 mpModel->OnPageDown();
3083 else
3084 if ( event.m_keyCode == WXK_PRIOR )
3085
3086 mpModel->OnPageUp();
3087 else
3088 if ( event.m_keyCode == WXK_DELETE )
3089
3090 mpModel->OnDelete();
3091 else
3092 if ( event.m_keyCode == WXK_INSERT && event.ShiftDown() )
3093
3094 mpModel->OnPaste();
3095 else
3096 event.Skip();
3097 }
3098}
3099
3100void wxTextEditorView::OnChar( wxKeyEvent& event )
3101{
3102 if ( event.ControlDown() )
3103 {
3104 if ( event.m_keyCode == 'y' )
3105
3106 mpModel->OnDeleteLine();
3107 else
3108 if ( event.m_keyCode == 'v' )
3109
3110 mpModel->OnPaste();
3111 else
3112 if ( event.m_keyCode == 'c' )
3113
3114 mpModel->OnCopy();
3115 else
3116 if ( event.m_keyCode == 'z' )
3117
3118 mpModel->OnUndo();
3119 else
3120 if ( event.m_keyCode == 'a' )
3121
3122 mpModel->OnRedo();
3123 else
3124 event.Skip();
3125 }
3126 else
3127 if ( event.AltDown() )
3128 {
3129 if ( event.m_keyCode == WXK_BACK )
3130
3131 mpModel->OnUndo();
3132 else
3133 event.Skip();
3134 }
3135 else
3136 if ( event.m_keyCode == WXK_BACK )
3137
3138 mpModel->OnDeleteBack();
3139 else
3140 if ( event.m_keyCode == WXK_TAB && event.ShiftDown() )
3141
3142 mpModel->OnShiftSelectionIndent( TRUE );
3143 else
3144 {
3145 if ( !mpModel->SelectionIsEmpty() && event.m_keyCode == WXK_TAB )
3146
3147 mpModel->OnShiftSelectionIndent( FALSE );
3148 else
3149 mpModel->OnInsertChar( event.m_keyCode );
3150 }
3151}
3152
3153void wxTextEditorView::SetModel( wxTextEditorModel* pModel )
3154{
3155 mpModel = pModel;
3156 mSelectionStart = pModel->GetStartOfSelection();
3157 mSelectionEnd = pModel->GetEndOfSelection();
3158 mCursorPos = pModel->GetCursor();
3159}
3160
3161void wxTextEditorView::SetSourcePainter( SourcePainter* pPainter )
3162{
3163 mpPainter = pPainter;
3164}
3165
3166void wxTextEditorView::AddPinPainter( TPinPainterBase* pPainter )
3167{
3168 mPinPainters.push_back( pPainter );
3169}
3170
3171void wxTextEditorView::SetDefaultFont( const wxFont& font )
3172{
3173 mFont = font;
3174
3175#if defined(__WXMSW__) || defined(__WINDOWS__)
3176 mFont.RealizeResource();
3177#endif
3178
3179 mCharDim.x = -1;
3180 mCharDim.y = -1;
3181
3182 RecalcPagingInfo();
3183}
3184
3185void wxTextEditorView::SetRowsPerPage( size_t n )
3186{
3187 mpModel->SetRowsPerPage( n );
3188
3189 mRowsPerPage = n;
3190 SyncScrollbars();
3191 PositionCursor();
3192}
3193
3194void wxTextEditorView::SetMaxColumns( size_t n )
3195{
3196 mMaxColumns = n;
3197
3198 SyncScrollbars();
3199 PositionCursor();
3200}
3201
3202wxFont& wxTextEditorView::GetDefaultFont()
3203{
3204 return mFont;
3205}
3206
3207void wxTextEditorView::SetLineTrackingMode( bool on, const wxColour& col )
3208{
3209 mLTColour = col;
3210 mLTMode = on;
3211
3212 if ( mpTimer->GetView() == this )
3213
3214 mpTimer->HideCursor();
3215}
3216
3217void wxTextEditorView::EnableCursor( bool enable )
3218{
3219 mCursorOn = enable;
3220}
3221
3222void wxTextEditorView::EnableScrollbars( bool enable )
3223{
3224 mScrollingOn = enable;
3225}
3226
3227bool wxTextEditorView::IsActiveView()
3228{
3229 return this == mpModel->GetActiveView();
3230}
3231
3232void wxTextEditorView::PositionCursor()
3233{
3234 if ( !IsActiveView() ||
3235 mLTMode || !mCursorOn ) return;
3236
3237 mpTimer->HideCursor();
3238
3239 TextPosToScreenPos( mpModel->GetCursor(), mCursorScrPos );
3240
3241 mpTimer->ShowCursor();
3242}
3243
3244void wxTextEditorView::PixelsToScrPos( int x, int y, int& scrRow, int& scrCol )
3245{
3246 x -= mLeftMargin;
3247 y -= mTopMargin;
3248
3249 //if ( x < 0 ) x = 0; // FOR NOW:: horizontal auto-scroll disabled
3250
3251 scrCol = x / mCharDim.x;
3252 scrRow = y / mCharDim.y;
3253}
3254
3255void wxTextEditorView::PixelsToTextPos( int x, int y, TPosition& textPos )
3256{
3257 int scrRow = 0, scrCol = 0;
3258 PixelsToScrPos( x, y, scrRow, scrCol );
3259
3260 if ( scrRow + (int)mPagePos.mRow < 0 )
3261
3262 scrRow = -(int)mPagePos.mRow;
3263
3264 if ( scrCol + (int)mPagePos.mCol < 0 )
3265
3266 scrCol = -(int)mPagePos.mCol;
3267
3268 ScreenPosToTextPos( TPosition( scrRow, scrCol ), textPos );
3269}
3270
3271void wxTextEditorView::ScreenPosToPixels( const TPosition& scrPos, int& x, int& y )
3272{
3273 x = mLeftMargin + scrPos.mCol * mCharDim.x;
3274 y = mTopMargin + scrPos.mRow * mCharDim.y;
3275}
3276
3277void wxTextEditorView::TextPosToScreenPos( const TPosition& txtPos, TPosition& scrPos )
3278{
3279 TTextIterator iter;
3280
3281 if ( txtPos.mRow != mCashedIter.mPos.mRow )
3282 {
3283 iter = mpModel->CreateIterator( txtPos );
3284 mCashedIter = iter;
3285 }
3286 else
3287 {
3288 iter = mCashedIter;
3289 iter.mPos.mCol = txtPos.mCol;
3290 }
3291
3292 iter.ToStartOfLine();
3293
3294 size_t scrCol = 0;
3295 size_t txtCol = 0;
3296
3297 while( !iter.IsEol() && txtCol < txtPos.mCol )
3298 {
3299 if ( iter.GetChar() == '\t' )
3300 {
3301 size_t spacing = ( (scrCol / mpModel->mTabSize) + 1 ) * mpModel->mTabSize - scrCol;
3302
3303 scrCol += spacing;
3304 }
3305 else
3306 ++scrCol;
3307
3308 ++txtCol;
3309 iter.NextChar();
3310 }
3311
3312 TPosition actualPos = iter.GetPosition();
3313
3314 scrCol += txtPos.mCol - txtCol;
3315
3316 scrPos.mRow = actualPos.mRow - mPagePos.mRow;
3317 scrPos.mCol = scrCol - mPagePos.mCol;
3318}
3319
3320void wxTextEditorView::ScreenPosToTextPos( const TPosition& scrPos, TPosition& txtPos )
3321{
3322 TPosition absScrPos( scrPos.mRow + mPagePos.mRow, scrPos.mCol + mPagePos.mCol );
3323
3324 TTextIterator iter = mpModel->CreateIterator( TPosition( absScrPos.mRow, 0 ) );
3325
3326 size_t scrCol = 0;
3327 size_t txtCol = 0;
3328
3329 // iterate over all possible on-screen positions, and find one which matches "absScrPos"
3330
3331 while( !iter.IsEol() && scrCol < absScrPos.mCol )
3332 {
3333 if ( iter.GetChar() == '\t' )
3334 {
3335 size_t spacing = ( (scrCol / mpModel->mTabSize) + 1 ) * mpModel->mTabSize - scrCol;
3336
3337 scrCol += spacing;
3338 }
3339 else
3340 ++scrCol;
3341
3342 ++txtCol;
3343 iter.NextChar();
3344 }
3345
3346 TPosition actualPos = iter.GetPosition();
3347
3348 if ( scrCol == absScrPos.mCol )
3349 {
3350 txtPos = actualPos;
3351 return;
3352 }
3353 else
3354 if ( scrCol < absScrPos.mCol )
3355 {
3356 // the absScrPos points past the eol
3357
3358 txtPos = actualPos;
3359 txtPos.mCol += absScrPos.mCol - scrCol;
3360 }
3361 else
3362 if ( scrCol > absScrPos.mCol )
3363 {
3364 // there should have been a '\t' char, which made us jump too far forward
3365
3366 txtPos = actualPos;
3367 --txtPos.mCol;
3368 }
3369}
3370
3371bool wxTextEditorView::IsClipboardCmd( wxKeyEvent& key )
3372{
3373 if ( key.ControlDown() && key.m_keyCode == WXK_CONTROL )
3374
3375 return TRUE;
3376
3377 if ( key.ShiftDown() && key.m_keyCode == WXK_SHIFT )
3378
3379 return TRUE;
3380
3381 if ( key.ControlDown() )
3382 {
3383 return ( key.m_keyCode == 'C' ||
3384 key.m_keyCode == 'c' ||
3385 key.m_keyCode == WXK_INSERT );
3386 }
3387
3388 return FALSE;
3389}
3390
3391void wxTextEditorView::ObtainFontProperties()
3392{
3393 wxClientDC dc(this);
3394 dc.SetFont( mFont );
3395
3396 long w,h;
3397
3398 dc.GetTextExtent( "X", &w, &h );
3399
3400 mCharDim.x = w;
3401 mCharDim.y = h;
3402}
3403
3404void wxTextEditorView::SyncViewPortPosition()
3405{
3406
3407 TPosition pos = mpModel->GetCursor();
3408
3409 TextPosToScreenPos( pos, pos );
3410 pos.mRow += mPagePos.mRow;
3411 pos.mCol += mPagePos.mCol;
3412
3413 if ( pos.mRow < mPagePos.mRow )
3414 {
3415 mPagePos.mRow = pos.mRow;
3416 mFullRefreshPending = TRUE;
3417 }
3418 else
3419 if ( pos.mRow >= mPagePos.mRow + mRowsPerPage && mRowsPerPage != 0 )
3420 {
3421 mPagePos.mRow = pos.mRow - mRowsPerPage + 1;
3422 mFullRefreshPending = TRUE;
3423 }
3424
3425 if ( pos.mCol < mPagePos.mCol )
3426 {
3427 mPagePos.mCol = pos.mCol;
3428 mFullRefreshPending = TRUE;
3429 }
3430 else
3431 if ( pos.mCol >= mPagePos.mCol + mColsPerPage )
3432 {
3433 mPagePos.mCol = pos.mCol - mColsPerPage + 1;
3434 mFullRefreshPending = TRUE;
3435 }
3436}
3437
3438void wxTextEditorView::SyncScrollbars()
3439{
3440 if ( !mScrollingOn ) return;
3441
3442 size_t nRows = mpModel->GetTotalRowCount();
3443
3444#if !defined(__WINDOWS__)
3445
3446 if ( mLastViewStart == mPagePos )
3447 {
3448 if ( mLastRowsTotal != nRows )
3449
3450 mAdjustScrollPending = TRUE;
3451
3452 return;
3453 }
3454#else
3455 if ( mLastViewStart == mPagePos &&
3456 mLastRowsTotal == nRows )
3457
3458 return;
3459#endif
3460 SetScrollbars( mCharDim.x, mCharDim.y,
3461 mMaxColumns,
3462 nRows,
3463 mPagePos.mCol,
3464 mPagePos.mRow,
3465 TRUE
3466 );
3467
3468 mLastViewStart = mPagePos;
3469 mLastRowsTotal = nRows;
3470}
3471
3472void wxTextEditorView::ScrollView( int rows, int cols )
3473{
3474 int pageRow = (int)mPagePos.mRow;
3475 int pageCol = (int)mPagePos.mCol;
3476
3477 if ( pageRow + rows < 0 )
3478 pageRow = 0;
3479 else
3480 if ( pageRow + rows > (int)mpModel->GetTotalRowCount() )
3481
3482 pageRow = mpModel->GetTotalRowCount();
3483 else
3484 pageRow = pageRow + rows;
3485
3486 mPagePos.mRow = (size_t)pageRow;
3487
3488 if ( pageCol + cols < 0 )
3489
3490 pageCol = 0;
3491 else
3492 pageCol = pageCol + cols;
3493
3494 mPagePos.mCol = pageCol;
3495
3496 mFullRefreshPending = TRUE;
3497}
3498
3499void wxTextEditorView::OnModelChanged()
3500{
3501 // invalidate pre-cached iterator
3502 mCashedIter.mPos = TPosition( (size_t)(-1), 0 );
3503
3504 SyncViewPortPosition();
3505
3506 if ( mLTMode ) mFullRefreshPending = TRUE;
3507
3508 if ( mpModel->mTextChanged && !mFullRefreshPending )
3509 {
3510 wxClientDC dc( this );
3511 PaintRows( mpModel->mChangedFromRow, mpModel->mChangedTillRow, dc );
3512 }
3513 else
3514 if ( mFullRefreshPending )
3515 {
3516 wxClientDC dc( this );
3517 PaintRows( mPagePos.mRow, mPagePos.mRow + mRowsPerPage, dc );
3518 }
3519
3520 if ( IsActiveView() )
3521 {
3522 PositionCursor();
3523 SyncScrollbars();
3524 }
3525}
3526
3527void wxTextEditorView::Activate()
3528{
3529 mpModel->SetStartOfSelection( mSelectionStart );
3530 mpModel->SetEndOfSelection( mSelectionEnd );
3531 mpModel->SetCursor( mCursorPos );
3532
3533 mpModel->SetRowsPerPage( mRowsPerPage );
3534
3535 if ( !mLTMode && mCursorOn )
3536 {
3537 mpTimer->SetView( this );
3538 mpTimer->ShowCursor();
3539 }
3540
3541 mpModel->SetActiveView( this );
3542}
3543
3544void wxTextEditorView::Deactivate()
3545{
3546 mSelectionStart = mpModel->GetStartOfSelection();
3547 mSelectionEnd = mpModel->GetEndOfSelection();
3548 mCursorPos = mpModel->GetCursor();
3549
3550 if ( mpTimer->GetView() == this &&
3551 !mLTMode && mCursorOn )
3552
3553 mpTimer->HideCursor( TRUE );
3554}
3555
3556/*** protected methods ***/
3557
3558char* wxTextEditorView::mpLineBuffer = NULL;
3559size_t wxTextEditorView::mpLineBufferLen = 0;
3560
3561char* wxTextEditorView::GetLineBuffer( size_t len )
3562{
3563 if ( mpLineBuffer == NULL || mpLineBufferLen < len )
3564 {
3565 if ( !mpLineBuffer ) mpModel->FreeCharacters( mpLineBuffer );
3566
3567 mpLineBuffer = mpModel->AllocCharacters( len );
3568
3569 mpLineBufferLen = len;
3570 }
3571
3572 return mpLineBuffer;
3573}
3574
3575TPinPainterBase* wxTextEditorView::FindPainterForPin( TPinBase& pin )
3576{
3577 int pinTc = pin.mTypeCode;
3578
3579 for( size_t i = 0; i != mPinPainters.size(); ++i )
3580
3581 if ( mPinPainters[i]->mPinTypeCode == pinTc )
3582
3583 return mPinPainters[i];
3584
3585 return NULL;
3586}
3587
3588void wxTextEditorView::PaintDecorations( size_t fromRow,
3589 size_t tillRow,
3590 wxDC& dc, TTextIterator& iter )
3591{
3592 int dcY = ( fromRow - mPagePos.mRow ) * mCharDim.y + mTopMargin;
3593
3594 size_t curPin = mpModel->FindFirstPinInRange( fromRow, tillRow );
3595
3596 PinListT& pins = mpModel->GetPins();
3597 TPinPainterBase* pPainter = NULL;
3598
3599 size_t prevRow = fromRow;
3600 int prevY = dcY;
3601
3602 wxPoint pos;
3603 wxSize dim( mLeftMargin, mCharDim.y );
3604
3605 while( curPin != NPOS )
3606 {
3607 TPinBase& pin = *pins[curPin];
3608
3609 if ( pPainter == NULL ||
3610 pPainter->mPinTypeCode != pin.mTypeCode )
3611
3612 pPainter = FindPainterForPin( pin );
3613
3614
3615 // only pins which have their painters can be "visualized"
3616
3617 if ( pPainter )
3618 {
3619 pos.x = 0;
3620 pos.y = ( pin.mRow - mPagePos.mRow )* mCharDim.y + mTopMargin;
3621
3622 if ( prevRow < pin.mRow )
3623 {
3624 // fill upper gap
3625
3626 dc.SetBrush( mNormalBkBrush );
3627 dc.SetPen( *wxTRANSPARENT_PEN );
3628 dc.DrawRectangle( 0, prevY,
3629 mLeftMargin + 1,
3630 mCharDim.y * ( pin.mRow - prevRow ) + 1 );
3631 }
3632
3633 pPainter->DrawPin( &pin, *this, dc, pos, dim );
3634
3635 prevRow = pin.mRow + 1;
3636 prevY = pos.y + mCharDim.y;
3637 }
3638
3639 ++curPin;
3640
3641 if ( curPin >= pins.size() ||
3642 pins[curPin]->mRow >= tillRow )
3643
3644 break;
3645 }
3646
3647 // fill the reminder
3648
3649 if ( prevRow < tillRow )
3650 {
3651 dc.SetBrush( mNormalBkBrush );
3652 dc.SetPen( *wxTRANSPARENT_PEN );
3653 dc.DrawRectangle( 0, prevY,
3654 mLeftMargin + 1,
3655 mCharDim.y * ( tillRow - prevRow ) + 1 );
3656 }
3657
3658 dc.SetPen( *wxTRANSPARENT_PEN );
3659}
3660
3661void wxTextEditorView::PaintRows( size_t fromRow, size_t tillRow, wxDC& dc )
3662{
3663 // NOTE:: raws are painted from "fromRow" but not including "tillRow" - [fromRow,tillRow)
3664
3665 dc.SetPen( *wxTRANSPARENT_PEN );
3666
3667 // how much on-screen columns are visable?
3668
3669 size_t fromScrCol = mPagePos.mCol;
3670 size_t tillScrCol = fromScrCol + mColsPerPage;
3671
3672 TPosition selStart = mpModel->GetStartOfSelection();
3673 TPosition selEnd = mpModel->GetEndOfSelection();
3674
3675 bool selectionIsEmpty = ( selStart == selEnd );
3676
3677 wxColour curFgCol;
3678 wxColour curBkCol;
3679
3680 wxBrush mLTBrush( mLTColour, wxSOLID );
3681
3682 // clip given row-region to the current page
3683
3684 if ( ( fromRow >= mPagePos.mRow + mRowsPerPage) ||
3685 ( tillRow <= mPagePos.mRow )
3686 )
3687
3688 return;
3689
3690 if ( fromRow < mPagePos.mRow ) fromRow = mPagePos.mRow;
3691 if ( tillRow > mPagePos.mRow + mRowsPerPage ) tillRow = mPagePos.mRow + mRowsPerPage;
3692
3693 if ( fromRow >= tillRow ) return;
3694
3695 // now start the renderng
3696
3697 if ( mpTimer->GetView() == this && mCursorOn && !mLTMode )
3698 {
3699 mpTimer->Lock();
3700 mpTimer->SetIsShown( FALSE );
3701 }
3702
3703 dc.SetFont( mFont );
3704 dc.SetBackgroundMode( wxSOLID );
3705
3706 TTextIterator iter = mpModel->CreateIterator( TPosition( fromRow, 0 ) );
3707
3708 PaintDecorations( fromRow, tillRow, dc, iter );
3709
3710 size_t cursorRow = mpModel->GetCursor().mRow;
3711
3712 size_t curRow = fromRow;
3713 for( ; curRow != tillRow; ++curRow )
3714 {
3715 // place text into line-buffer
3716
3717 iter.ToStartOfLine();
3718 size_t lineLen = iter.GetLineLen();
3719
3720 char* lineBuf = GetLineBuffer( lineLen + 1 );
3721
3722 size_t i = 0;
3723
3724 while( !iter.IsEof() && !iter.IsEol() )
3725 {
3726 lineBuf[i++] = iter.GetChar();
3727 iter.NextChar();
3728 }
3729
3730 iter.NextChar(); // skip eol
3731
3732 // obtain "highlights"
3733
3734 mpPainter->SetState( FALSE, FALSE );
3735 mpPainter->Init( FALSE );
3736 mpPainter->ProcessSource( lineBuf, lineLen );
3737 IntListT& blocks = mpPainter->GetBlocks();
3738
3739 // setup state vars
3740
3741 int dcY = ( curRow - mPagePos.mRow ) * mCharDim.y + mTopMargin;
3742
3743 size_t scrCol = 0;
3744 size_t txtCol = 0;
3745
3746 size_t curBlk = 0;
3747 size_t curBlkCol = 0;
3748
3749 int chunkLen = -1;
3750 size_t chunkTxtStart = 0;
3751 size_t chunkScrStart = 0;
3752
3753 // pre-detect occurance of selection
3754
3755 bool lineHasSelection = ( selStart.mRow == curRow ) ||
3756 ( selEnd.mRow == curRow );
3757
3758 bool isInSelection = ( selStart.mRow <= curRow ) &&
3759 ( selEnd.mRow >= curRow );
3760
3761 if ( isInSelection && selStart.mRow == curRow &&
3762 selStart.mCol != 0 )
3763
3764 isInSelection = FALSE;
3765
3766 if ( selStart == selEnd )
3767 {
3768 lineHasSelection = FALSE;
3769 isInSelection = FALSE;
3770 }
3771
3772 char ch = '\0';
3773
3774 // loop though the text in this row
3775
3776 do
3777 {
3778 TPosition curPos( curRow, txtCol );
3779
3780 // first check if we can finish the current chunk
3781
3782 bool finishChunk = FALSE;
3783
3784 if ( curBlk < blocks.size() &&
3785 curBlkCol + get_src_block_len( blocks[curBlk] ) == txtCol )
3786 {
3787 curBlkCol += get_src_block_len( blocks[curBlk] );
3788 ++curBlk;
3789 finishChunk = TRUE;
3790 }
3791 else
3792 if ( ( !selectionIsEmpty && ( curPos == selStart || curPos == selEnd ) )
3793 || lineBuf[txtCol] == '\t'
3794 || txtCol == lineLen )
3795
3796 finishChunk = TRUE;
3797
3798 if ( finishChunk && chunkLen != -1 )
3799 {
3800 // is any part of the chunk visable?
3801
3802 size_t chunkScrEnd = chunkScrStart + chunkLen;
3803
3804 if ( ( // if hits from one side or is inside
3805 ( chunkScrStart >= fromScrCol &&
3806 chunkScrStart < tillScrCol ) ||
3807 ( chunkScrEnd >= fromScrCol &&
3808 chunkScrEnd < tillScrCol ) ) ||
3809
3810 // if overlaps the whole range
3811 ( chunkScrStart < fromScrCol &&
3812 chunkScrEnd >= tillScrCol )
3813
3814 )
3815 {
3816 // render chunk data to the given DC
3817
3818 dc.SetTextForeground( curFgCol );
3819 dc.SetTextBackground( curBkCol );
3820
3821 // clip left edge
3822
3823 if ( chunkScrStart < fromScrCol )
3824 {
3825 size_t diff = fromScrCol - chunkScrStart;
3826 chunkLen -= diff;
3827 chunkTxtStart += diff;
3828 chunkScrStart += diff;
3829 }
3830
3831 // clip right edge
3832
3833 if ( chunkScrEnd > tillScrCol )
3834 {
3835 size_t diff = chunkScrEnd - tillScrCol;
3836 chunkLen -= diff;
3837 chunkScrEnd -= diff;
3838 }
3839
3840 // create string
3841
3842 char tmp = lineBuf[chunkTxtStart + chunkLen];
3843
3844 lineBuf[chunkTxtStart + chunkLen] = '\0';
3845
3846 // use member-variable, reuse heap-buffer between outputs
3847 mFragment = lineBuf + chunkTxtStart;
3848
3849 lineBuf[chunkTxtStart + chunkLen] = tmp;
3850
3851 // draw it
3852
3853 int dcX = (chunkScrStart - fromScrCol) * mCharDim.x + mLeftMargin;
3854
3855 dc.DrawText( mFragment, dcX, dcY );
3856 }
3857
3858 chunkLen = -1;
3859
3860 } // end of "if ( finishChunk )"
3861
3862 if ( txtCol == lineLen )
3863 break;
3864
3865 if ( chunkLen == -1 )
3866 {
3867 // prepare the new chunk
3868
3869 if ( curBlk < blocks.size() )
3870 {
3871 switch( get_src_block_rank( blocks[curBlk] ) )
3872 {
3873 case RANK_BLACK : curFgCol = mNormalTextCol; break;
3874 case RANK_BLUE : curFgCol = mIndentifierTextCol; break;
3875 case RANK_RED : curFgCol = mReservedWordTextCol; break;
3876 case RANK_GREEN : curFgCol = mCommentTextCol; break;
3877 default : break;
3878 }
3879 }
3880
3881 // track occurence of selection
3882
3883 if ( lineHasSelection )
3884 {
3885 isInSelection = TRUE;
3886
3887 if ( selEnd.mRow == curRow &&
3888 selEnd.mCol <= txtCol )
3889
3890 isInSelection = FALSE;
3891
3892 if ( selStart.mRow == curRow &&
3893 selStart.mCol > txtCol )
3894
3895 isInSelection = FALSE;
3896 }
3897
3898 if ( isInSelection )
3899 {
3900 curFgCol = mSelectionFgCol;
3901 curBkCol = mSelectionBkCol;
3902 }
3903 else
3904 {
3905 if ( mLTMode && curRow == cursorRow )
3906
3907 curBkCol = mLTColour;
3908 else
3909 curBkCol = mNormalBkCol ;
3910 }
3911
3912 chunkScrStart = scrCol;
3913 chunkTxtStart = txtCol;
3914 chunkLen = 0;
3915 }
3916
3917
3918 ch = lineBuf[txtCol];
3919
3920 if ( ch == '\t' )
3921 {
3922 // tab's are treated specially (for simplicity and speed)
3923
3924 int dcX = (chunkScrStart - fromScrCol) * mCharDim.x + mLeftMargin;
3925
3926 if ( !isInSelection )
3927 {
3928 if ( mLTMode && curRow == cursorRow )
3929
3930 dc.SetBrush( mLTBrush );
3931 else
3932 dc.SetBrush( mNormalBkBrush );
3933 }
3934 else dc.SetBrush( mSelectedBkBrush );
3935
3936 // *** "the rule of TAB..." ***
3937
3938 size_t spacing = ( (scrCol / mpModel->mTabSize) + 1 ) * mpModel->mTabSize - scrCol;
3939
3940 int width = spacing * mCharDim.x + 1;
3941
3942 if ( dcX < mLeftMargin )
3943 {
3944 width -= mLeftMargin - dcX;
3945
3946 dcX = mLeftMargin;
3947 }
3948
3949 if ( width > 0 )
3950
3951 dc.DrawRectangle( dcX, dcY, width, mCharDim.y + 1 );
3952
3953 scrCol += spacing;
3954 txtCol += 1;
3955
3956 // move chunk-start forward, after the occurance of '\t'
3957
3958 chunkLen = -1;
3959 }
3960 else
3961 {
3962 // increase on-screen/in-text positions
3963
3964 ++scrCol;
3965 ++txtCol;
3966 ++chunkLen;
3967 }
3968
3969 } while( TRUE );
3970
3971 // fill the reminding white-space after eol
3972
3973 if ( scrCol < tillScrCol &&
3974 ( !isInSelection ||
3975 ( isInSelection && curRow == selEnd.mRow ) )
3976 )
3977 {
3978 if ( scrCol < fromScrCol ) scrCol = fromScrCol;
3979
3980 int dcX = ( scrCol - fromScrCol ) * mCharDim.x + mLeftMargin;
3981
3982 if ( mLTMode && curRow == cursorRow )
3983
3984 dc.SetBrush ( mLTBrush );
3985 else
3986 dc.SetBrush( mNormalBkBrush );
3987
3988 dc.DrawRectangle( dcX, dcY,
3989 mCharDim.x * ( tillScrCol - scrCol ) + 1,
3990 mCharDim.y + 1 );
3991 }
3992
3993 // render selection which is located past the eol
3994
3995 if ( ( lineHasSelection || isInSelection ) &&
3996 !( selEnd.mRow == curRow && selEnd.mCol <= txtCol )
3997 )
3998 {
3999 // determine start of selection on-screen
4000
4001 size_t scrSelStart = scrCol + ( selStart.mCol - txtCol );
4002
4003 if ( isInSelection )
4004
4005 scrSelStart = scrCol;
4006
4007 size_t scrSelEnd = tillScrCol;
4008
4009 if ( selEnd.mRow == curRow )
4010
4011 scrSelEnd = scrCol + ( selEnd.mCol - txtCol );
4012
4013 // clipping
4014
4015 if ( scrSelStart < fromScrCol ) scrSelStart = fromScrCol;
4016 if ( scrSelEnd > tillScrCol ) scrSelEnd = tillScrCol;
4017
4018 // drawing
4019
4020 if ( scrSelEnd > scrSelStart )
4021 {
4022 int dcX = ( scrSelStart - fromScrCol ) * mCharDim.x + mLeftMargin;
4023
4024 dc.SetBrush( mSelectedBkBrush );
4025 dc.DrawRectangle( dcX, dcY,
4026 mCharDim.x * ( scrSelEnd - scrSelStart ) + 1,
4027 mCharDim.y + 1 );
4028 }
4029 }
4030
4031 if ( iter.IsEof() )
4032 {
4033 ++curRow;
4034 break;
4035 }
4036
4037 } // end of "for(...)"
4038
4039 if ( curRow < tillRow )
4040 {
4041 dc.SetBrush( mNormalBkBrush );
4042
4043 int dcY = mTopMargin + (curRow - mPagePos.mRow)*mCharDim.y;
4044 int dcX = mLeftMargin;
4045
4046 dc.DrawRectangle( dcX, dcY, mColsPerPage*mCharDim.x + 1,
4047 ( tillRow - curRow ) * mCharDim.y + 1
4048 );
4049 }
4050
4051 if ( mFullRefreshPending )
4052 {
4053 dc.SetBrush( mNormalBkBrush );
4054
4055 // fill in "corners" which are never reached by characters
4056
4057 int w,h;
4058 GetClientSize( &w, &h );
4059
4060 dc.SetBrush( mNormalBkBrush );
4061
4062 int dcX = tillScrCol*mCharDim.x + mLeftMargin;
4063
4064 dc.DrawRectangle( dcX, mTopMargin, w - dcX + 1, h );
4065
4066 int dcY = mTopMargin + mRowsPerPage*mCharDim.y;
4067
4068 dc.DrawRectangle( 0, dcY, w, h - dcY + 2 );
4069
4070 ++curRow;
4071
4072 // any past-the-eof lines left at the bottom?
4073 }
4074
4075 mFullRefreshPending = FALSE;
4076
4077 if ( mpTimer->GetView() == this && mCursorOn && !mLTMode )
4078
4079 mpTimer->Unlock();
4080
4081} // end of PaintRows(..)
4082
4083/***** Implementation for class TBookmarkPainter *****/
4084
4085TBookmarkPainter::TBookmarkPainter()
4086
4087 : TPinPainterBase( BOOKMARK_PIN_TC ),
4088 mBkBrush( wxColour( 0,255,255 ), wxSOLID )
4089{
4090}
4091
4092void TBookmarkPainter::DrawPin( TPinBase* pPin, wxTextEditorView& view, wxDC& dc,
4093 const wxPoint& pos, const wxSize& dim )
4094{
4095 dc.SetPen( *wxBLACK_PEN );
4096 dc.SetBrush( mBkBrush );
4097 dc.DrawRoundedRectangle( pos.x+2, pos.y, dim.x-4, dim.y, 4 );
4098}
4099
4100/***** Implementation for class TBreakpointPainter *****/
4101
4102TBreakpointPainter::TBreakpointPainter()
4103
4104 : TPinPainterBase( BRKPOINT_PIN_TC ),
4105 mBkBrush( wxColour( 196,0,0 ), wxSOLID )
4106{
4107}
4108
4109void TBreakpointPainter::DrawPin( TPinBase* pPin, wxTextEditorView& view, wxDC& dc,
4110 const wxPoint& pos, const wxSize& dim )
4111{
4112 dc.SetPen( *wxBLACK_PEN );
4113 dc.SetBrush( mBkBrush );
4114 dc.DrawRoundedRectangle( pos.x+6, pos.y+2, dim.x-12, dim.y-4, 30 );
4115}
4116
4117/***** Implementation for class TCursorTimer *****/
4118
4119TCursorTimer::TCursorTimer()
4120
4121 : mIsLocked( FALSE ),
4122 mIsShown ( FALSE ),
4123 mBlinkInterval( 500 ),
4124 mBrush( wxColour(0,0,0), wxSOLID ),
4125 mMissOneTick( FALSE )
4126{
4127}
4128
4129void TCursorTimer::Notify()
4130{
4131 if ( mIsLocked ) return;
4132
4133 if ( mMissOneTick )
4134 {
4135 // this trick is used because it's not
4136 // possible to restart the timer under wxGtk
4137
4138 mMissOneTick = FALSE;
4139 return;
4140 }
4141
4142
4143 mIsLocked = TRUE;
4144
4145 DrawCursor();
4146
4147 mIsShown = !mIsShown;
4148
4149 mIsLocked = FALSE;
4150}
4151
4152void TCursorTimer::SetView( wxTextEditorView* pView )
4153{
4154 mpView = pView;
4155}
4156
4157wxTextEditorView* TCursorTimer::GetView()
4158{
4159 return mpView;
4160
4161}
4162
4163void TCursorTimer::HideCursor( bool forceHide )
4164{
4165 Lock();
4166
4167 if ( mIsShown )
4168 {
4169 DrawCursor();
4170 mIsShown = FALSE;
4171 }
4172
4173 Unlock();
4174}
4175
4176void TCursorTimer::ShowCursor( bool forceShow )
4177{
4178 Lock();
4179
4180 if ( !forceShow )
4181 {
4182 DrawCursor();
4183 mIsShown = TRUE;
4184
4185 if ( mStarted )
4186 mMissOneTick = TRUE;
4187 }
4188
4189 Unlock();
4190
4191 if ( !mStarted )
4192 {
4193 Start( mBlinkInterval );
4194 mStarted = TRUE;
4195 }
4196}
4197
4198void TCursorTimer::Lock()
4199{
4200// while( mIsLocked );
4201
4202 mIsLocked = TRUE;
4203}
4204
4205void TCursorTimer::Unlock()
4206{
4207 mIsLocked = FALSE;
4208}
4209
4210void TCursorTimer::SetIsShown( bool isShown )
4211{
4212 mIsShown = isShown;
4213}
4214
4215/*** protected methods ***/
4216
4217void TCursorTimer::DrawCursor()
4218{
4219 if ( mpView == NULL ) return;
4220
4221 wxClientDC dc( mpView );
4222
4223 int x = 0, y = 0;
4224
4225 mpView->ScreenPosToPixels( mpView->mCursorScrPos, x, y );
4226
4227 dc.SetLogicalFunction( wxINVERT );
4228 dc.SetBrush( mBrush );
4229
4230 dc.SetPen( *wxTRANSPARENT_PEN );
4231 dc.DrawRectangle( x,y, 3, mpView->mCharDim.y + 1 );
4232 dc.SetBackgroundMode( wxSOLID );
4233}