mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-29 04:33:41 +02:00
4737 lines
126 KiB
ObjectPascal
4737 lines
126 KiB
ObjectPascal
{
|
||
$Id$
|
||
This file is part of the Free Pascal Integrated Development Environment
|
||
Copyright (c) 1998 by Berczi Gabor
|
||
|
||
Code editor template objects
|
||
|
||
See the file COPYING.FPC, included in this distribution,
|
||
for details about the copyright.
|
||
|
||
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.
|
||
|
||
**********************************************************************}
|
||
{$I globdir.inc}
|
||
unit WEditor;
|
||
|
||
interface
|
||
|
||
uses
|
||
Dos,Objects,Drivers,Views,Menus,Commands;
|
||
|
||
const
|
||
cmFileNameChanged = 51234;
|
||
cmASCIIChar = 51235;
|
||
cmClearLineHighlights = 51236;
|
||
cmSaveCancelled = 51237;
|
||
cmBreakLine = 51238;
|
||
cmSelStart = 51239;
|
||
cmSelEnd = 51240;
|
||
cmLastCursorPos = 51241;
|
||
cmIndentBlock = 51242;
|
||
cmUnIndentBlock = 51243;
|
||
cmSelectLine = 51244;
|
||
cmWriteBlock = 51245;
|
||
cmReadBlock = 51246;
|
||
cmPrintBlock = 51247;
|
||
cmResetDebuggerRow = 51248;
|
||
|
||
EditorTextBufSize = {$ifdef FPC}32768{$else} 4096{$endif};
|
||
MaxLineLength = {$ifdef FPC} 255{$else} 255{$endif};
|
||
MaxLineCount = {$ifdef FPC}16380{$else}16380{$endif};
|
||
|
||
efBackupFiles = $00000001;
|
||
efInsertMode = $00000002;
|
||
efAutoIndent = $00000004;
|
||
efUseTabCharacters = $00000008;
|
||
efBackSpaceUnindents = $00000010;
|
||
efPersistentBlocks = $00000020;
|
||
efSyntaxHighlight = $00000040;
|
||
efBlockInsCursor = $00000080;
|
||
efVerticalBlocks = $00000100;
|
||
efHighlightColumn = $00000200;
|
||
efHighlightRow = $00000400;
|
||
efAutoBrackets = $00000800;
|
||
efExpandAllTabs = $00001000;
|
||
efKeepTrailingSpaces = $00002000;
|
||
efStoreContent = $80000000;
|
||
|
||
attrAsm = 1;
|
||
attrComment = 2;
|
||
attrForceFull = 128;
|
||
attrAll = attrAsm+attrComment;
|
||
|
||
edOutOfMemory = 0;
|
||
edReadError = 1;
|
||
edWriteError = 2;
|
||
edCreateError = 3;
|
||
edSaveModify = 4;
|
||
edSaveUntitled = 5;
|
||
edSaveAs = 6;
|
||
edFind = 7;
|
||
edSearchFailed = 8;
|
||
edReplace = 9;
|
||
edReplacePrompt = 10;
|
||
edTooManyLines = 11;
|
||
edGotoLine = 12;
|
||
edReplaceFile = 13;
|
||
edWriteBlock = 14;
|
||
edReadBlock = 15;
|
||
|
||
ffmOptions = $0007; ffsOptions = 0;
|
||
ffmDirection = $0008; ffsDirection = 3;
|
||
ffmScope = $0010; ffsScope = 4;
|
||
ffmOrigin = $0020; ffsOrigin = 5;
|
||
ffDoReplace = $0040;
|
||
ffReplaceAll = $0080;
|
||
|
||
|
||
ffCaseSensitive = $0001;
|
||
ffWholeWordsOnly = $0002;
|
||
ffPromptOnReplace = $0004;
|
||
|
||
ffForward = $0000;
|
||
ffBackward = $0008;
|
||
|
||
ffGlobal = $0000;
|
||
ffSelectedText = $0010;
|
||
|
||
ffFromCursor = $0000;
|
||
ffEntireScope = $0020;
|
||
|
||
coTextColor = 0;
|
||
coWhiteSpaceColor = 1;
|
||
coCommentColor = 2;
|
||
coReservedWordColor = 3;
|
||
coIdentifierColor = 4;
|
||
coStringColor = 5;
|
||
coNumberColor = 6;
|
||
coAssemblerColor = 7;
|
||
coSymbolColor = 8;
|
||
coDirectiveColor = 9;
|
||
coHexNumberColor = 10;
|
||
coTabColor = 11;
|
||
coBreakColor = 12;
|
||
coFirstColor = 0;
|
||
coLastColor = coBreakColor;
|
||
|
||
eaMoveCursor = 1;
|
||
eaInsertLine = 2;
|
||
eaInsertText = 3;
|
||
eaDeleteLine = 4;
|
||
eaDeleteText = 5;
|
||
eaSelectionChanged = 6;
|
||
|
||
CIndicator = #2#3#1;
|
||
CEditor = #33#34#35#36#37#38#39#40#41#42#43#44#45#46#47#48#49;
|
||
|
||
TAB = #9;
|
||
FindStrSize = 79;
|
||
|
||
type
|
||
PLine = ^TLine;
|
||
TLine = record
|
||
Text : PString;
|
||
Format : PString;
|
||
BeginsWithAsm,
|
||
EndsWithAsm : boolean;
|
||
IsBreakpoint : boolean;
|
||
BeginsWithComment,
|
||
EndsInSingleLineComment,
|
||
EndsWithComment : boolean;
|
||
BeginsWithDirective,
|
||
EndsWithDirective : boolean;
|
||
{BeginCommentType,}EndCommentType : byte;
|
||
end;
|
||
|
||
PLineCollection = ^TLineCollection;
|
||
TLineCollection = object(TCollection)
|
||
function At(Index: sw_Integer): PLine;
|
||
procedure FreeItem(Item: Pointer); virtual;
|
||
end;
|
||
|
||
PIndicator = ^TIndicator;
|
||
TIndicator = object(TView)
|
||
Location: TPoint;
|
||
Modified: Boolean;
|
||
constructor Init(var Bounds: TRect);
|
||
procedure Draw; virtual;
|
||
function GetPalette: PPalette; virtual;
|
||
procedure SetState(AState: Word; Enable: Boolean); virtual;
|
||
procedure SetValue(ALocation: TPoint; AModified: Boolean);
|
||
constructor Load(var S: TStream);
|
||
procedure Store(var S: TStream);
|
||
end;
|
||
|
||
PEditorAction = ^TEditorAction;
|
||
TEditorAction = packed record
|
||
Action : byte;
|
||
StartPos : TPoint;
|
||
EndPos : TPoint;
|
||
Text : PString;
|
||
end;
|
||
|
||
PEditorActionCollection = ^TEditorActionCollection;
|
||
TEditorActionCollection = object(TCollection)
|
||
procedure FreeItem(Item: Pointer); virtual;
|
||
end;
|
||
|
||
TSpecSymbolClass =
|
||
(ssCommentPrefix,ssCommentSingleLinePrefix,ssCommentSuffix,ssStringPrefix,ssStringSuffix,
|
||
ssDirectivePrefix,ssDirectiveSuffix,ssAsmPrefix,ssAsmSuffix);
|
||
|
||
TEditorBookMark = record
|
||
Valid : boolean;
|
||
Pos : TPoint;
|
||
end;
|
||
|
||
PCodeEditor = ^TCodeEditor;
|
||
TCodeEditor = object(TScroller)
|
||
Indicator : PIndicator;
|
||
Lines : PLineCollection;
|
||
SelStart : TPoint;
|
||
SelEnd : TPoint;
|
||
Highlight : TRect;
|
||
CurPos : TPoint;
|
||
CanUndo : Boolean;
|
||
StoreUndo : boolean;
|
||
Modified : Boolean;
|
||
IsReadOnly : Boolean;
|
||
NoSelect : Boolean;
|
||
Flags : longint;
|
||
TabSize : integer;
|
||
HighlightRow: sw_integer;
|
||
DebuggerRow: sw_integer;
|
||
constructor Init(var Bounds: TRect; AHScrollBar, AVScrollBar:
|
||
PScrollBar; AIndicator: PIndicator; AbufSize:Sw_Word);
|
||
procedure SetFlags(AFlags: longint); virtual;
|
||
procedure ConvertEvent(var Event: TEvent); virtual;
|
||
procedure HandleEvent(var Event: TEvent); virtual;
|
||
procedure SetState(AState: Word; Enable: Boolean); virtual;
|
||
procedure LocalMenu(P: TPoint); virtual;
|
||
function GetLocalMenu: PMenu; virtual;
|
||
function GetCommandTarget: PView; virtual;
|
||
function CreateLocalMenuView(var Bounds: TRect; M: PMenu): PMenuPopup; virtual;
|
||
procedure Draw; virtual;
|
||
procedure DrawCursor; virtual;
|
||
procedure TrackCursor(Center: boolean); virtual;
|
||
procedure UpdateIndicator; virtual;
|
||
procedure LimitsChanged; virtual;
|
||
procedure SelectionChanged; virtual;
|
||
procedure HighlightChanged; virtual;
|
||
procedure ModifiedChanged; virtual;
|
||
procedure Update; virtual;
|
||
procedure ScrollTo(X, Y: sw_Integer);
|
||
procedure SetModified(AModified: boolean); virtual;
|
||
procedure SetInsertMode(InsertMode: boolean); virtual;
|
||
procedure SetCurPtr(X,Y: sw_integer); virtual;
|
||
procedure SetSelection(A, B: TPoint); virtual;
|
||
procedure SetHighlight(A, B: TPoint); virtual;
|
||
procedure SetHighlightRow(Row: sw_integer); virtual;
|
||
procedure SetDebuggerRow(Row: sw_integer); virtual;
|
||
procedure SelectAll(Enable: boolean); virtual;
|
||
function InsertFrom(Editor: PCodeEditor): Boolean; virtual;
|
||
function InsertText(const S: string): Boolean; virtual;
|
||
function GetPalette: PPalette; virtual;
|
||
function IsClipboard: Boolean;
|
||
constructor Load(var S: TStream);
|
||
procedure Store(var S: TStream);
|
||
function LoadFromStream(Stream: PStream): boolean; virtual;
|
||
function SaveToStream(Stream: PStream): boolean; virtual;
|
||
function SaveAreaToStream(Stream: PStream; StartP,EndP: TPoint): boolean;
|
||
destructor Done; virtual;
|
||
public
|
||
{ Text & info storage abstraction }
|
||
function GetLineCount: sw_integer; virtual;
|
||
function CharIdxToLinePos(Line,CharIdx: sw_integer): sw_integer;
|
||
function LinePosToCharIdx(Line,X: sw_integer): sw_integer;
|
||
function GetLineText(I: sw_integer): string; virtual;
|
||
procedure SetDisplayText(I: sw_integer;const S: string); virtual;
|
||
function GetDisplayText(I: sw_integer): string; virtual;
|
||
procedure SetLineText(I: sw_integer;const S: string); virtual;
|
||
procedure SetLineBreakState(I : sw_integer;b : boolean);
|
||
procedure GetDisplayTextFormat(I: sw_integer;var DT,DF:string); virtual;
|
||
function GetLineFormat(I: sw_integer): string; virtual;
|
||
procedure SetLineFormat(I: sw_integer;const S: string); virtual;
|
||
procedure DeleteAllLines; virtual;
|
||
procedure DeleteLine(I: sw_integer); virtual;
|
||
procedure AddLine(const S: string); virtual;
|
||
function GetErrorMessage: string; virtual;
|
||
procedure SetErrorMessage(const S: string); virtual;
|
||
procedure AdjustSelection(DeltaX, DeltaY: sw_integer);
|
||
procedure AdjustSelectionPos(CurPosX, CurPosY: sw_integer; DeltaX, DeltaY: sw_integer);
|
||
procedure Lock;
|
||
procedure UnLock;
|
||
private
|
||
LastLocalCmd: word;
|
||
KeyState : Integer;
|
||
ErrorMessage: PString;
|
||
Actions : PEditorActionCollection;
|
||
Bookmarks : array[0..9] of TEditorBookmark;
|
||
LockFlag : integer;
|
||
DrawCalled : boolean;
|
||
CurEvent : PEvent;
|
||
function Overwrite: boolean;
|
||
function GetLine(I: sw_integer): PLine;
|
||
procedure CheckSels;
|
||
function UpdateAttrs(FromLine: sw_integer; Attrs: byte): sw_integer;
|
||
function UpdateAttrsRange(FromLine, ToLine: sw_integer; Attrs: byte): sw_integer;
|
||
procedure DrawLines(FirstLine: sw_integer);
|
||
procedure HideHighlight;
|
||
procedure AddAction(AAction: byte; AStartPos, AEndPos: TPoint; AText: string);
|
||
function ShouldExtend: boolean;
|
||
function ValidBlock: boolean;
|
||
public
|
||
{ Syntax highlight support }
|
||
function GetSpecSymbolCount(SpecClass: TSpecSymbolClass): integer; virtual;
|
||
function GetSpecSymbol(SpecClass: TSpecSymbolClass; Index: integer): string; virtual;
|
||
function IsReservedWord(const S: string): boolean; virtual;
|
||
public
|
||
SearchRunCount: integer;
|
||
InASCIIMode: boolean;
|
||
procedure Indent; virtual;
|
||
procedure CharLeft; virtual;
|
||
procedure CharRight; virtual;
|
||
procedure WordLeft; virtual;
|
||
procedure WordRight; virtual;
|
||
procedure LineStart; virtual;
|
||
procedure LineEnd; virtual;
|
||
procedure LineUp; virtual;
|
||
procedure LineDown; virtual;
|
||
procedure PageUp; virtual;
|
||
procedure PageDown; virtual;
|
||
procedure TextStart; virtual;
|
||
procedure TextEnd; virtual;
|
||
procedure JumpSelStart; virtual;
|
||
procedure JumpSelEnd; virtual;
|
||
procedure JumpMark(MarkIdx: integer); virtual;
|
||
procedure DefineMark(MarkIdx: integer); virtual;
|
||
procedure JumpToLastCursorPos; virtual;
|
||
function InsertLine: Sw_integer; virtual;
|
||
procedure BreakLine; virtual;
|
||
procedure BackSpace; virtual;
|
||
procedure DelChar; virtual;
|
||
procedure DelWord; virtual;
|
||
procedure DelStart; virtual;
|
||
procedure DelEnd; virtual;
|
||
procedure DelLine; virtual;
|
||
procedure InsMode; virtual;
|
||
procedure StartSelect; virtual;
|
||
procedure EndSelect; virtual;
|
||
procedure DelSelect; virtual;
|
||
procedure HideSelect; virtual;
|
||
procedure CopyBlock; virtual;
|
||
procedure MoveBlock; virtual;
|
||
procedure IndentBlock; virtual;
|
||
procedure UnindentBlock; virtual;
|
||
procedure SelectWord; virtual;
|
||
procedure SelectLine; virtual;
|
||
procedure WriteBlock; virtual;
|
||
procedure ReadBlock; virtual;
|
||
procedure PrintBlock; virtual;
|
||
procedure AddChar(C: char); virtual;
|
||
{$ifdef WinClipSupported}
|
||
function ClipCopyWin: Boolean; virtual;
|
||
function ClipPasteWin: Boolean; virtual;
|
||
{$endif WinClipSupported}
|
||
function ClipCopy: Boolean; virtual;
|
||
procedure ClipCut; virtual;
|
||
procedure ClipPaste; virtual;
|
||
function GetCurrentWord : string;
|
||
procedure Undo; virtual;
|
||
procedure Redo; virtual;
|
||
procedure Find; virtual;
|
||
procedure Replace; virtual;
|
||
procedure DoSearchReplace; virtual;
|
||
procedure GotoLine; virtual;
|
||
end;
|
||
|
||
PFileEditor = ^TFileEditor;
|
||
TFileEditor = object(TCodeEditor)
|
||
FileName: string;
|
||
constructor Init(var Bounds: TRect; AHScrollBar, AVScrollBar:
|
||
PScrollBar; AIndicator: PIndicator;const AFileName: string);
|
||
function Save: Boolean; virtual;
|
||
function SaveAs: Boolean; virtual;
|
||
function SaveAsk: Boolean; virtual;
|
||
function LoadFile: boolean; virtual;
|
||
function SaveFile: boolean; virtual;
|
||
function Valid(Command: Word): Boolean; virtual;
|
||
procedure HandleEvent(var Event: TEvent); virtual;
|
||
function ShouldSave: boolean; virtual;
|
||
constructor Load(var S: TStream);
|
||
procedure Store(var S: TStream);
|
||
end;
|
||
|
||
TCodeEditorDialog = function(Dialog: Integer; Info: Pointer): Word;
|
||
|
||
function DefUseSyntaxHighlight(Editor: PFileEditor): boolean;
|
||
function DefUseTabsPattern(Editor: PFileEditor): boolean;
|
||
|
||
const
|
||
DefaultCodeEditorFlags : longint =
|
||
efBackupFiles+efInsertMode+efAutoIndent+efPersistentBlocks+
|
||
{efUseTabCharacters+}efBackSpaceUnindents+efSyntaxHighlight+
|
||
efExpandAllTabs;
|
||
DefaultTabSize : integer = 8;
|
||
EOL : String[2] = {$ifdef Linux}#10;{$else}#13#10;{$endif}
|
||
|
||
{ used for ShiftDel and ShiftIns to avoid
|
||
GetShiftState to be considered for extending
|
||
selection (PM) }
|
||
|
||
cmCopyWin = 240;
|
||
cmPasteWin = 241;
|
||
|
||
{ History ID }
|
||
FileId = 101;
|
||
TextFindId = 105;
|
||
TextReplaceID = 106;
|
||
GotoID = 107;
|
||
TextGrepId = 108;
|
||
|
||
DontConsiderShiftState: boolean = false;
|
||
ToClipCmds : TCommandSet = ([cmCut,cmCopy,cmCopyWin]);
|
||
FromClipCmds : TCommandSet = ([cmPaste,cmPasteWin]);
|
||
NulClipCmds : TCommandSet = ([cmClear]);
|
||
UndoCmds : TCommandSet = ([cmUndo,cmRedo]);
|
||
|
||
function StdEditorDialog(Dialog: Integer; Info: Pointer): word;
|
||
|
||
const
|
||
EditorDialog : TCodeEditorDialog = StdEditorDialog;
|
||
Clipboard : PCodeEditor = nil;
|
||
FindStr : String[FindStrSize] = '';
|
||
ReplaceStr : String[FindStrSize] = '';
|
||
FindFlags : word = ffPromptOnReplace;
|
||
WhiteSpaceChars : set of char = [#0,#32,#255];
|
||
TabChars : set of char = [#9];
|
||
HashChars : set of char = ['#'];
|
||
AlphaChars : set of char = ['A'..'Z','a'..'z','_'];
|
||
NumberChars : set of char = ['0'..'9'];
|
||
RealNumberChars : set of char = ['E','e','.'{,'+','-'}];
|
||
DefaultSaveExt : string[12] = '.pas';
|
||
FileDir : DirStr = '';
|
||
|
||
UseSyntaxHighlight : function(Editor: PFileEditor): boolean = DefUseSyntaxHighlight;
|
||
UseTabsPattern : function(Editor: PFileEditor): boolean = DefUseTabsPattern;
|
||
|
||
procedure RegisterCodeEditors;
|
||
|
||
implementation
|
||
|
||
uses
|
||
MsgBox,Dialogs,App,StdDlg,HistList,Validate,
|
||
{$ifdef WinClipSupported}
|
||
Strings,WinClip,
|
||
{$endif WinClipSupported}
|
||
WUtils,WViews;
|
||
|
||
{$ifndef NOOBJREG}
|
||
const
|
||
RIndicator: TStreamRec = (
|
||
ObjType: 1100;
|
||
VmtLink: Ofs(TypeOf(TIndicator)^);
|
||
Load: @TIndicator.Load;
|
||
Store: @TIndicator.Store
|
||
);
|
||
RCodeEditor: TStreamRec = (
|
||
ObjType: 1101;
|
||
VmtLink: Ofs(TypeOf(TCodeEditor)^);
|
||
Load: @TCodeEditor.Load;
|
||
Store: @TCodeEditor.Store
|
||
);
|
||
RFileEditor: TStreamRec = (
|
||
ObjType: 1102;
|
||
VmtLink: Ofs(TypeOf(TFileEditor)^);
|
||
Load: @TFileEditor.Load;
|
||
Store: @TFileEditor.Store
|
||
);
|
||
{$endif}
|
||
|
||
type
|
||
TFindDialogRec = packed record
|
||
Find : String[FindStrSize];
|
||
Options : Word{longint};
|
||
{ checkboxes need 32 bits PM }
|
||
{ reverted to word in dialogs.TCluster for TP compatibility (PM) }
|
||
{ anyhow its complete nonsense : you can only have 16 fields
|
||
but use a longint to store it !! }
|
||
Direction: word;{ and tcluster has word size }
|
||
Scope : word;
|
||
Origin : word;
|
||
end;
|
||
|
||
TReplaceDialogRec = packed record
|
||
Find : String[FindStrSize];
|
||
Replace : String[FindStrSize];
|
||
Options : Word{longint};
|
||
Direction: word;
|
||
Scope : word;
|
||
Origin : word;
|
||
end;
|
||
|
||
TGotoLineDialogRec = packed record
|
||
LineNo : string[5];
|
||
Lines : sw_integer;
|
||
end;
|
||
|
||
const
|
||
kbShift = kbLeftShift+kbRightShift;
|
||
|
||
const
|
||
FirstKeyCount = 39;
|
||
FirstKeys: array[0..FirstKeyCount * 2] of Word = (FirstKeyCount,
|
||
Ord(^A), cmWordLeft, Ord(^B), cmASCIIChar, Ord(^C), cmPageDown,
|
||
Ord(^D), cmCharRight, Ord(^E), cmLineUp,
|
||
Ord(^F), cmWordRight, Ord(^G), cmDelChar,
|
||
Ord(^H), cmBackSpace, Ord(^J), cmJumpLine,
|
||
Ord(^K), $FF02, Ord(^L), cmSearchAgain,
|
||
Ord(^M), cmNewLine, Ord(^N), cmBreakLine, Ord(^Q), $FF01,
|
||
Ord(^R), cmPageUp, Ord(^S), cmCharLeft,
|
||
Ord(^T), cmDelWord, Ord(^U), cmUndo,
|
||
Ord(^V), cmInsMode, Ord(^X), cmLineDown,
|
||
Ord(^Y), cmDelLine, kbLeft, cmCharLeft,
|
||
kbRight, cmCharRight, kbCtrlLeft, cmWordLeft,
|
||
kbCtrlRight, cmWordRight, kbHome, cmLineStart,
|
||
kbEnd, cmLineEnd, kbUp, cmLineUp,
|
||
kbDown, cmLineDown, kbPgUp, cmPageUp,
|
||
kbPgDn, cmPageDown, kbCtrlPgUp, cmTextStart,
|
||
kbCtrlPgDn, cmTextEnd, kbIns, cmInsMode,
|
||
kbDel, cmDelChar, kbShiftIns, cmPaste,
|
||
kbShiftDel, cmCut, kbCtrlIns, cmCopy,
|
||
kbCtrlDel, cmClear);
|
||
QuickKeyCount = 23;
|
||
QuickKeys: array[0..QuickKeyCount * 2] of Word = (QuickKeyCount,
|
||
Ord('A'), cmReplace, Ord('C'), cmTextEnd,
|
||
Ord('D'), cmLineEnd, Ord('F'), cmFind,
|
||
Ord('H'), cmDelStart, Ord('R'), cmTextStart,
|
||
Ord('S'), cmLineStart, Ord('Y'), cmDelEnd,
|
||
Ord('G'), cmJumpLine, Ord('A'), cmReplace,
|
||
Ord('B'), cmSelStart, Ord('K'), cmSelEnd,
|
||
Ord('P'), cmLastCursorPos,
|
||
Ord('0'), cmJumpMark0, Ord('1'), cmJumpMark1, Ord('2'), cmJumpMark2,
|
||
Ord('3'), cmJumpMark3, Ord('4'), cmJumpMark4, Ord('5'), cmJumpMark5,
|
||
Ord('6'), cmJumpMark6, Ord('7'), cmJumpMark7, Ord('8'), cmJumpMark8,
|
||
Ord('9'), cmJumpMark9);
|
||
BlockKeyCount = 23;
|
||
BlockKeys: array[0..BlockKeyCount * 2] of Word = (BlockKeyCount,
|
||
Ord('B'), cmStartSelect, Ord('C'), cmCopyBlock,
|
||
Ord('H'), cmHideSelect, Ord('K'), cmEndSelect,
|
||
Ord('Y'), cmDelSelect, Ord('V'), cmMoveBlock,
|
||
Ord('I'), cmIndentBlock, Ord('U'), cmUnindentBlock,
|
||
Ord('T'), cmSelectWord, Ord('L'), cmSelectLine,
|
||
Ord('W'), cmWriteBlock, Ord('R'), cmReadBlock,
|
||
Ord('P'), cmPrintBlock,
|
||
Ord('0'), cmSetMark0, Ord('1'), cmSetMark1, Ord('2'), cmSetMark2,
|
||
Ord('3'), cmSetMark3, Ord('4'), cmSetMark4, Ord('5'), cmSetMark5,
|
||
Ord('6'), cmSetMark6, Ord('7'), cmSetMark7, Ord('8'), cmSetMark8,
|
||
Ord('9'), cmSetMark9);
|
||
KeyMap: array[0..2] of Pointer = (@FirstKeys, @QuickKeys, @BlockKeys);
|
||
|
||
function ScanKeyMap(KeyMap: Pointer; KeyCode: Word): Word;
|
||
type
|
||
pword = ^word;
|
||
var
|
||
p : pword;
|
||
count : sw_word;
|
||
begin
|
||
p:=keymap;
|
||
count:=p^;
|
||
inc(p);
|
||
while (count>0) do
|
||
begin
|
||
if (lo(p^)=lo(keycode)) and
|
||
((hi(p^)=0) or (hi(p^)=hi(keycode))) then
|
||
begin
|
||
inc(p);
|
||
scankeymap:=p^;
|
||
Exit;
|
||
end;
|
||
inc(p,2);
|
||
dec(count);
|
||
end;
|
||
scankeymap:=0;
|
||
end;
|
||
|
||
function IsWordSeparator(C: char): boolean;
|
||
begin
|
||
IsWordSeparator:=C in[' ',#0,#255,':','=','''','"','.',',','/',';','$','#','(',')','<','>','^','*','+','-','?','&'];
|
||
end;
|
||
|
||
function IsSpace(C: char): boolean;
|
||
begin
|
||
IsSpace:=C in[' ',#0,#255];
|
||
end;
|
||
|
||
function LTrim(S: string): string;
|
||
begin
|
||
while (length(S)>0) and (S[1] in [#0,TAB,#32]) do
|
||
Delete(S,1,1);
|
||
LTrim:=S;
|
||
end;
|
||
|
||
function RTrim(S: string): string;
|
||
begin
|
||
while (length(S)>0) and (S[length(S)] in [#0,TAB,#32]) do
|
||
Delete(S,length(S),1);
|
||
RTrim:=S;
|
||
end;
|
||
|
||
function Trim(S: string): string;
|
||
begin
|
||
Trim:=RTrim(LTrim(S));
|
||
end;
|
||
|
||
function EatIO: integer;
|
||
begin
|
||
EatIO:=IOResult;
|
||
end;
|
||
|
||
function ExistsFile(const FileName: string): boolean;
|
||
var f: file;
|
||
Exists: boolean;
|
||
begin
|
||
if FileName='' then Exists:=false else
|
||
begin
|
||
{$I-}
|
||
Assign(f,FileName);
|
||
Reset(f,1);
|
||
Exists:=EatIO=0;
|
||
Close(f);
|
||
EatIO;
|
||
{$I+}
|
||
end;
|
||
ExistsFile:=Exists;
|
||
end;
|
||
|
||
function Max(A,B: longint): longint;
|
||
begin
|
||
if A>B then Max:=A else Max:=B;
|
||
end;
|
||
|
||
function Min(A,B: longint): longint;
|
||
begin
|
||
if A<B then Min:=A else Min:=B;
|
||
end;
|
||
|
||
function StrToInt(const S: string): longint;
|
||
var L: longint;
|
||
C: integer;
|
||
begin
|
||
Val(S,L,C); if C<>0 then L:=-1;
|
||
StrToInt:=L;
|
||
end;
|
||
|
||
function RExpand(const S: string; MinLen: byte): string;
|
||
begin
|
||
if length(S)<MinLen then
|
||
RExpand:=S+CharStr(' ',MinLen-length(S))
|
||
else
|
||
RExpand:=S;
|
||
end;
|
||
|
||
function upper(const s : string) : string;
|
||
var
|
||
i : Sw_word;
|
||
begin
|
||
for i:=1 to length(s) do
|
||
if s[i] in ['a'..'z'] then
|
||
upper[i]:=char(byte(s[i])-32)
|
||
else
|
||
upper[i]:=s[i];
|
||
upper[0]:=s[0];
|
||
end;
|
||
|
||
function DirAndNameOf(const Path: string): string;
|
||
var D: DirStr; N: NameStr; E: ExtStr;
|
||
begin
|
||
FSplit(Path,D,N,E);
|
||
DirAndNameOf:=D+N;
|
||
end;
|
||
|
||
type TPosOfs = {$ifdef TP}longint{$endif}{$ifdef FPC}comp{$endif};
|
||
|
||
function PosToOfs(const X,Y: sw_integer): TPosOfs;
|
||
type TPosRec = record LoI, HiI: sw_integer; end;
|
||
var C: TPosRec;
|
||
begin
|
||
C.LoI:=X; C.HiI:=Y;
|
||
PosToOfs:=TPosOfs(C);
|
||
end;
|
||
|
||
function PosToOfsP(const P: TPoint): TPosOfs;
|
||
begin
|
||
PosToOfsP:=PosToOfs(P.X,P.Y);
|
||
end;
|
||
|
||
function PointOfs(P: TPoint): TPosOfs;
|
||
begin
|
||
PointOfs:={longint(P.Y)*MaxLineLength+P.X}PosToOfsP(P);
|
||
end;
|
||
|
||
function NewEditorAction(AAction: byte; AStartPos, AEndPos: TPoint; AText: string): PEditorAction;
|
||
var P: PEditorAction;
|
||
begin
|
||
New(P); FillChar(P^,SizeOf(P^),0);
|
||
with P^ do
|
||
begin
|
||
Action:=AAction;
|
||
StartPos:=AStartPos; EndPos:=AEndPos;
|
||
Text:=NewStr(AText);
|
||
end;
|
||
NewEditorAction:=P;
|
||
end;
|
||
|
||
procedure DisposeEditorAction(P: PEditorAction);
|
||
begin
|
||
if P<>nil then
|
||
begin
|
||
if P^.Text<>nil then DisposeStr(P^.Text); P^.Text:=nil;
|
||
Dispose(P);
|
||
end;
|
||
end;
|
||
|
||
function ExtractTabs(S: string; TabSize: Sw_integer): string;
|
||
var
|
||
P,PAdd: Sw_Word;
|
||
begin
|
||
p:=0;
|
||
while p<length(s) do
|
||
begin
|
||
inc(p);
|
||
if s[p]=#9 then
|
||
begin
|
||
PAdd:=TabSize-((p-1) mod TabSize);
|
||
s:=copy(S,1,P-1)+CharStr(' ',PAdd)+copy(S,P+1,255);
|
||
inc(P,PAdd-1);
|
||
end;
|
||
end;
|
||
ExtractTabs:=S;
|
||
end;
|
||
|
||
function CompressUsingTabs(S: string; TabSize: byte): string;
|
||
var TabS: string;
|
||
P: byte;
|
||
begin
|
||
TabS:=CharStr(' ',TabSize);
|
||
repeat
|
||
P:=Pos(TabS,S);
|
||
if P>0 then
|
||
S:=copy(S,1,P-1)+TAB+copy(S,P+TabSize,255);
|
||
until P=0;
|
||
CompressUsingTabs:=S;
|
||
end;
|
||
|
||
|
||
{*****************************************************************************
|
||
Forward/Backward Scanning
|
||
*****************************************************************************}
|
||
|
||
Const
|
||
{$ifndef FPC}
|
||
MaxBufLength = $7f00;
|
||
NotFoundValue = -1;
|
||
{$else}
|
||
MaxBufLength = $7fffff00;
|
||
NotFoundValue = -1;
|
||
{$endif}
|
||
|
||
Type
|
||
Btable = Array[0..255] of Byte;
|
||
Procedure BMFMakeTable(const s:string; Var t : Btable);
|
||
Var
|
||
x : sw_integer;
|
||
begin
|
||
FillChar(t,sizeof(t),length(s));
|
||
For x := length(s) downto 1 do
|
||
if (t[ord(s[x])] = length(s)) then
|
||
t[ord(s[x])] := length(s) - x;
|
||
end;
|
||
|
||
|
||
function BMFScan(var Block; Size: Sw_Word;const Str: String;const bt:BTable): Sw_Integer;
|
||
Var
|
||
buffer : Array[0..MaxBufLength-1] of Byte Absolute block;
|
||
s2 : String;
|
||
len,
|
||
numb : Sw_Word;
|
||
found : Boolean;
|
||
begin
|
||
len:=length(str);
|
||
if len>size then
|
||
begin
|
||
BMFScan := NotFoundValue;
|
||
exit;
|
||
end;
|
||
s2[0]:=chr(len); { sets the length to that of the search String }
|
||
found:=False;
|
||
numb:=pred(len);
|
||
While (not found) and (numb<size) do
|
||
begin
|
||
{ partial match }
|
||
if buffer[numb] = ord(str[len]) then
|
||
begin
|
||
{ less partial! }
|
||
if buffer[numb-pred(len)] = ord(str[1]) then
|
||
begin
|
||
move(buffer[numb-pred(len)],s2[1],len);
|
||
if (str=s2) then
|
||
begin
|
||
found:=true;
|
||
break;
|
||
end;
|
||
end;
|
||
inc(numb);
|
||
end
|
||
else
|
||
inc(numb,Bt[buffer[numb]]);
|
||
end;
|
||
if not found then
|
||
BMFScan := NotFoundValue
|
||
else
|
||
BMFScan := numb - pred(len);
|
||
end;
|
||
|
||
|
||
function BMFIScan(var Block; Size: Sw_Word;const Str: String;const bt:BTable): Sw_Integer;
|
||
Var
|
||
buffer : Array[0..MaxBufLength-1] of Char Absolute block;
|
||
len,
|
||
numb,
|
||
x : Sw_Word;
|
||
found : Boolean;
|
||
p : pchar;
|
||
c : char;
|
||
begin
|
||
len:=length(str);
|
||
if (len=0) or (len>size) then
|
||
begin
|
||
BMFIScan := NotFoundValue;
|
||
exit;
|
||
end;
|
||
found:=False;
|
||
numb:=pred(len);
|
||
While (not found) and (numb<size) do
|
||
begin
|
||
{ partial match }
|
||
c:=buffer[numb];
|
||
if c in ['a'..'z'] then
|
||
c:=chr(ord(c)-32);
|
||
if (c=str[len]) then
|
||
begin
|
||
{ less partial! }
|
||
p:=@buffer[numb-pred(len)];
|
||
x:=1;
|
||
while (x<=len) do
|
||
begin
|
||
if not(((p^ in ['a'..'z']) and (chr(ord(p^)-32)=str[x])) or
|
||
(p^=str[x])) then
|
||
break;
|
||
inc(p);
|
||
inc(x);
|
||
end;
|
||
if (x>len) then
|
||
begin
|
||
found:=true;
|
||
break;
|
||
end;
|
||
inc(numb);
|
||
end
|
||
else
|
||
inc(numb,Bt[ord(c)]);
|
||
end;
|
||
if not found then
|
||
BMFIScan := NotFoundValue
|
||
else
|
||
BMFIScan := numb - pred(len);
|
||
end;
|
||
|
||
|
||
Procedure BMBMakeTable(const s:string; Var t : Btable);
|
||
Var
|
||
x : sw_integer;
|
||
begin
|
||
FillChar(t,sizeof(t),length(s));
|
||
For x := 1 to length(s)do
|
||
if (t[ord(s[x])] = length(s)) then
|
||
t[ord(s[x])] := x-1;
|
||
end;
|
||
|
||
|
||
function BMBScan(var Block; Size: Sw_Word;const Str: String;const bt:BTable): Sw_Integer;
|
||
Var
|
||
buffer : Array[0..MaxBufLength-1] of Byte Absolute block;
|
||
s2 : String;
|
||
len,
|
||
numb : Sw_integer;
|
||
found : Boolean;
|
||
begin
|
||
len:=length(str);
|
||
if len>size then
|
||
begin
|
||
BMBScan := NotFoundValue;
|
||
exit;
|
||
end;
|
||
s2[0]:=chr(len); { sets the length to that of the search String }
|
||
found:=False;
|
||
numb:=size-pred(len);
|
||
While (not found) and (numb>0) do
|
||
begin
|
||
{ partial match }
|
||
if buffer[numb] = ord(str[1]) then
|
||
begin
|
||
{ less partial! }
|
||
if buffer[numb+pred(len)] = ord(str[len]) then
|
||
begin
|
||
move(buffer[numb],s2[1],len);
|
||
if (str=s2) then
|
||
begin
|
||
found:=true;
|
||
break;
|
||
end;
|
||
end;
|
||
dec(numb);
|
||
end
|
||
else
|
||
dec(numb,Bt[buffer[numb]]);
|
||
end;
|
||
if not found then
|
||
BMBScan := NotFoundValue
|
||
else
|
||
BMBScan := numb;
|
||
end;
|
||
|
||
|
||
function BMBIScan(var Block; Size: Sw_Word;const Str: String;const bt:BTable): Sw_Integer;
|
||
Var
|
||
buffer : Array[0..MaxBufLength-1] of Char Absolute block;
|
||
len,
|
||
numb,
|
||
x : Sw_integer;
|
||
found : Boolean;
|
||
p : pchar;
|
||
c : char;
|
||
begin
|
||
len:=length(str);
|
||
if (len=0) or (len>size) then
|
||
begin
|
||
BMBIScan := NotFoundValue;
|
||
exit;
|
||
end;
|
||
found:=False;
|
||
numb:=size-len;
|
||
While (not found) and (numb>0) do
|
||
begin
|
||
{ partial match }
|
||
c:=buffer[numb];
|
||
if c in ['a'..'z'] then
|
||
c:=chr(ord(c)-32);
|
||
if (c=str[1]) then
|
||
begin
|
||
{ less partial! }
|
||
p:=@buffer[numb];
|
||
x:=1;
|
||
while (x<=len) do
|
||
begin
|
||
if not(((p^ in ['a'..'z']) and (chr(ord(p^)-32)=str[x])) or
|
||
(p^=str[x])) then
|
||
break;
|
||
inc(p);
|
||
inc(x);
|
||
end;
|
||
if (x>len) then
|
||
begin
|
||
found:=true;
|
||
break;
|
||
end;
|
||
dec(numb);
|
||
end
|
||
else
|
||
dec(numb,Bt[ord(c)]);
|
||
end;
|
||
if not found then
|
||
BMBIScan := NotFoundValue
|
||
else
|
||
BMBIScan := numb;
|
||
end;
|
||
|
||
|
||
{*****************************************************************************
|
||
PLine,TLineCollection
|
||
*****************************************************************************}
|
||
|
||
function NewLine(S: string): PLine;
|
||
var P: PLine;
|
||
begin
|
||
New(P); FillChar(P^,SizeOf(P^),0);
|
||
P^.Text:=NewStr(S);
|
||
NewLine:=P;
|
||
end;
|
||
|
||
|
||
procedure DisposeLine(P: PLine);
|
||
begin
|
||
if P<>nil then
|
||
begin
|
||
if P^.Text<>nil then DisposeStr(P^.Text);
|
||
if P^.Format<>nil then DisposeStr(P^.Format);
|
||
Dispose(P);
|
||
end;
|
||
end;
|
||
|
||
function TLineCollection.At(Index: sw_Integer): PLine;
|
||
begin
|
||
At:=inherited At(Index);
|
||
end;
|
||
|
||
procedure TLineCollection.FreeItem(Item: Pointer);
|
||
begin
|
||
if Item<>nil then DisposeLine(Item);
|
||
end;
|
||
|
||
|
||
constructor TIndicator.Init(var Bounds: TRect);
|
||
begin
|
||
inherited Init(Bounds);
|
||
GrowMode := gfGrowLoY + gfGrowHiY;
|
||
end;
|
||
|
||
procedure TIndicator.Draw;
|
||
var
|
||
Color: Byte;
|
||
Frame: Char;
|
||
L: array[0..1] of Longint;
|
||
S: String[15];
|
||
B: TDrawBuffer;
|
||
begin
|
||
if (State and sfDragging = 0) and (State and sfActive <> 0) then
|
||
begin
|
||
Color := GetColor(1);
|
||
Frame := #205;
|
||
end
|
||
else
|
||
begin
|
||
if (State and sfDragging)<>0 then
|
||
Color := GetColor(2)
|
||
else
|
||
Color := GetColor(3);
|
||
Frame := #196;
|
||
end;
|
||
MoveChar(B, Frame, Color, Size.X);
|
||
if State and sfActive<>0 then
|
||
begin
|
||
if Modified then
|
||
WordRec (B[0]).Lo := ord('*');
|
||
L[0] := Location.Y + 1;
|
||
L[1] := Location.X + 1;
|
||
FormatStr(S, ' %d:%d ', L);
|
||
MoveStr(B[8 - Pos(':', S)], S, Color);
|
||
end;
|
||
WriteBuf(0, 0, Size.X, 1, B);
|
||
end;
|
||
|
||
function TIndicator.GetPalette: PPalette;
|
||
const
|
||
P: string[Length(CIndicator)] = CIndicator;
|
||
begin
|
||
GetPalette := @P;
|
||
end;
|
||
|
||
procedure TIndicator.SetState(AState: Word; Enable: Boolean);
|
||
begin
|
||
inherited SetState(AState, Enable);
|
||
if (AState = sfDragging) or (AState=sfActive) then
|
||
DrawView;
|
||
end;
|
||
|
||
procedure TIndicator.SetValue(ALocation: TPoint; AModified: Boolean);
|
||
begin
|
||
if (Location.X<>ALocation.X) or
|
||
(Location.Y<>ALocation.Y) or
|
||
(Modified <> AModified) then
|
||
begin
|
||
Location := ALocation;
|
||
Modified := AModified;
|
||
DrawView;
|
||
end;
|
||
end;
|
||
|
||
constructor TIndicator.Load(var S: TStream);
|
||
begin
|
||
inherited Load(S);
|
||
S.Read(Location,SizeOf(Location));
|
||
S.Read(Modified,SizeOf(Modified));
|
||
end;
|
||
|
||
procedure TIndicator.Store(var S: TStream);
|
||
begin
|
||
inherited Store(S);
|
||
S.Write(Location,SizeOf(Location));
|
||
S.Write(Modified,SizeOf(Modified));
|
||
end;
|
||
|
||
|
||
{*****************************************************************************
|
||
TCodeEditor
|
||
*****************************************************************************}
|
||
|
||
constructor TCodeEditor.Init(var Bounds: TRect; AHScrollBar, AVScrollBar:
|
||
PScrollBar; AIndicator: PIndicator; ABufSize:Sw_Word);
|
||
begin
|
||
inherited Init(Bounds,AHScrollBar,AVScrollBar);
|
||
StoreUndo:=false;
|
||
New(Actions, Init(500,1000));
|
||
New(Lines, Init(500,1000));
|
||
{ we have always need at least 1 line }
|
||
Lines^.Insert(NewLine(''));
|
||
{ ^^^ why? setlinetext() inserts automatically if neccessary and
|
||
getlinetext() checks whether you're in range...
|
||
because otherwise you search for line with index -1 (PM) }
|
||
SetState(sfCursorVis,true);
|
||
SetFlags(DefaultCodeEditorFlags); TabSize:=DefaultTabSize;
|
||
SetHighlightRow(-1);
|
||
SetDebuggerRow(-1);
|
||
SetCurPtr(0,0);
|
||
Indicator:=AIndicator;
|
||
UpdateIndicator; LimitsChanged;
|
||
end;
|
||
|
||
procedure TCodeEditor.SetFlags(AFlags: longint);
|
||
var I: sw_integer;
|
||
begin
|
||
Flags:=AFlags;
|
||
SetInsertMode((Flags and efInsertMode)<>0);
|
||
if (Flags and efSyntaxHighlight)<>0 then
|
||
UpdateAttrs(0,attrAll) else
|
||
for I:=0 to GetLineCount-1 do
|
||
SetLineFormat(I,'');
|
||
UpdateIndicator;
|
||
DrawView;
|
||
end;
|
||
|
||
function TCodeEditor.GetErrorMessage: string;
|
||
var S: string;
|
||
begin
|
||
if ErrorMessage=nil then S:='' else S:=ErrorMessage^;
|
||
GetErrorMessage:=S;
|
||
end;
|
||
|
||
procedure TCodeEditor.SetErrorMessage(const S: string);
|
||
begin
|
||
if ErrorMessage<>nil then DisposeStr(ErrorMessage);
|
||
ErrorMessage:=NewStr(S);
|
||
DrawView;
|
||
end;
|
||
|
||
procedure TCodeEditor.Lock;
|
||
begin
|
||
Inc(LockFlag);
|
||
end;
|
||
|
||
procedure TCodeEditor.UnLock;
|
||
begin
|
||
{$ifdef DEBUG}
|
||
if lockflag=0 then
|
||
Bug('negative lockflag',nil)
|
||
else
|
||
{$endif DEBUG}
|
||
Dec(LockFlag);
|
||
if (LockFlag=0) and DrawCalled then
|
||
DrawView;
|
||
end;
|
||
|
||
procedure TCodeEditor.AdjustSelectionPos(CurPosX, CurPosY: sw_integer; DeltaX, DeltaY: sw_integer);
|
||
var CP: TPoint;
|
||
begin
|
||
if ValidBlock=false then Exit;
|
||
|
||
CP.X:=CurPosX; CP.Y:=CurPosY;
|
||
if (PosToOfsP(SelStart)<=PosToOfsP(CP)) and (PosToOfsP(CP)<PosToOfsP(SelEnd)) then
|
||
begin
|
||
{ CurPos is IN selection }
|
||
Inc(SelEnd.Y,DeltaY);
|
||
if (CP.Y=SelEnd.Y) and
|
||
((SelStart.Y<>SelEnd.Y) or (SelStart.X<=CP.X)) and
|
||
(CP.X<=SelEnd.X) then
|
||
Inc(SelEnd.X,DeltaX);
|
||
SelectionChanged;
|
||
end
|
||
else
|
||
if (PosToOfsP(CP)<=PosToOfsP(SelStart)) then
|
||
begin
|
||
{ CurPos is BEFORE selection }
|
||
if (CP.Y=SelStart.Y) and (CP.Y=SelEnd.Y) and (DeltaY<0) then
|
||
begin
|
||
SelStart:=CurPos; SelEnd:=CurPos;
|
||
end
|
||
else
|
||
if (CP.Y=SelStart.Y) then
|
||
begin
|
||
if CP.X<SelStart.X then
|
||
Inc(SelStart.X,DeltaX);
|
||
end;
|
||
{ else}
|
||
begin
|
||
Inc(SelStart.Y,DeltaY);
|
||
Inc(SelEnd.Y,DeltaY);
|
||
end;
|
||
if SelEnd.Y=CurPos.Y then Inc(SelEnd.X,DeltaX);
|
||
SelectionChanged;
|
||
end
|
||
else
|
||
begin
|
||
{ CurPos is AFTER selection }
|
||
{ actually we don't have to do anything here }
|
||
end;
|
||
end;
|
||
|
||
procedure TCodeEditor.AdjustSelection(DeltaX, DeltaY: sw_integer);
|
||
begin
|
||
AdjustSelectionPos(CurPos.X,CurPos.Y,DeltaX,DeltaY);
|
||
end;
|
||
|
||
procedure TCodeEditor.TrackCursor(Center: boolean);
|
||
var D: TPoint;
|
||
begin
|
||
D:=Delta;
|
||
if CurPos.Y<Delta.Y then D.Y:=CurPos.Y else
|
||
if CurPos.Y>Delta.Y+Size.Y-1 then D.Y:=CurPos.Y-Size.Y+1;
|
||
if CurPos.X<Delta.X then D.X:=CurPos.X else
|
||
if CurPos.X>Delta.X+Size.X-1 then D.X:=CurPos.X-Size.X+1;
|
||
if {((Delta.X<>D.X) or (Delta.Y<>D.Y)) and }Center then
|
||
begin
|
||
{ loose centering for debugger PM }
|
||
while (CurPos.Y-D.Y)<(Size.Y div 3) do Dec(D.Y);
|
||
while (CurPos.Y-D.Y)>2*(Size.Y div 3) do Inc(D.Y);
|
||
end;
|
||
if (Delta.X<>D.X) or (Delta.Y<>D.Y) then
|
||
ScrollTo(D.X,D.Y);
|
||
DrawCursor;
|
||
UpdateIndicator;
|
||
end;
|
||
|
||
procedure TCodeEditor.ScrollTo(X, Y: sw_Integer);
|
||
begin
|
||
inherited ScrollTo(X,Y);
|
||
if (HScrollBar=nil) or (VScrollBar=nil) then
|
||
begin Delta.X:=X; Delta.Y:=Y; end;
|
||
DrawView;
|
||
end;
|
||
|
||
procedure TCodeEditor.UpdateIndicator;
|
||
begin
|
||
if Indicator<>nil then
|
||
begin
|
||
Indicator^.Location:=CurPos;
|
||
Indicator^.Modified:=Modified;
|
||
Indicator^.DrawView;
|
||
end;
|
||
end;
|
||
|
||
procedure TCodeEditor.LimitsChanged;
|
||
begin
|
||
SetLimit(MaxLineLength+1,GetLineCount);
|
||
end;
|
||
|
||
procedure TCodeEditor.ConvertEvent(var Event: TEvent);
|
||
var
|
||
Key: Word;
|
||
begin
|
||
if Event.What = evKeyDown then
|
||
begin
|
||
if (Event.KeyShift and kbShift <> 0) and
|
||
(Event.ScanCode >= $47) and (Event.ScanCode <= $51) then
|
||
Event.CharCode := #0;
|
||
Key := Event.KeyCode;
|
||
if KeyState <> 0 then
|
||
begin
|
||
if (Lo(Key) >= $01) and (Lo(Key) <= $1A) then Inc(Key, $40);
|
||
if (Lo(Key) >= $61) and (Lo(Key) <= $7A) then Dec(Key, $20);
|
||
end;
|
||
Key := ScanKeyMap(KeyMap[KeyState], Key);
|
||
if (KeyState<>0) and (Key=0) then
|
||
ClearEvent(Event); { eat second key if unrecognized after ^Q or ^K }
|
||
KeyState := 0;
|
||
if Key <> 0 then
|
||
if Hi(Key) = $FF then
|
||
begin
|
||
KeyState := Lo(Key);
|
||
ClearEvent(Event);
|
||
end
|
||
else
|
||
begin
|
||
Event.What := evCommand;
|
||
Event.Command := Key;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TCodeEditor.HandleEvent(var Event: TEvent);
|
||
var DontClear : boolean;
|
||
|
||
procedure CheckScrollBar(P: PScrollBar; var D: Sw_Integer);
|
||
begin
|
||
if (Event.InfoPtr = P) and (P^.Value <> D) then
|
||
begin
|
||
D := P^.Value;
|
||
DrawView;
|
||
end;
|
||
end;
|
||
|
||
procedure GetMousePos(var P: TPoint);
|
||
begin
|
||
MakeLocal(Event.Where,P);
|
||
Inc(P.X,Delta.X); Inc(P.Y,Delta.Y);
|
||
end;
|
||
|
||
var
|
||
StartP,P: TPoint;
|
||
E: TEvent;
|
||
OldEvent : PEvent;
|
||
begin
|
||
E:=Event;
|
||
OldEvent:=CurEvent;
|
||
if (E.what and (evMouse or evKeyboard))<>0 then
|
||
CurEvent:=@E;
|
||
if (InASCIIMode=false) or (Event.What<>evKeyDown) then
|
||
ConvertEvent(Event);
|
||
case Event.What of
|
||
evMouseDown :
|
||
if MouseInView(Event.Where) then
|
||
if (Event.Buttons=mbRightButton) then
|
||
begin
|
||
MakeLocal(Event.Where,P); Inc(P.X); Inc(P.Y);
|
||
LocalMenu(P);
|
||
ClearEvent(Event);
|
||
end else
|
||
if Event.Buttons=mbLeftButton then
|
||
begin
|
||
GetMousePos(P);
|
||
StartP:=P;
|
||
SetCurPtr(P.X,P.Y);
|
||
repeat
|
||
GetMousePos(P);
|
||
if PointOfs(P)<PointOfs(StartP)
|
||
then SetSelection(P,StartP)
|
||
else SetSelection(StartP,P);
|
||
SetCurPtr(P.X,P.Y);
|
||
DrawView;
|
||
until not MouseEvent(Event, evMouseMove+evMouseAuto);
|
||
DrawView;
|
||
end;
|
||
evKeyDown :
|
||
begin
|
||
{ Scancode is almost never zero PM }
|
||
if InASCIIMode {and (Event.CharCode<>0)} then
|
||
AddChar(Event.CharCode)
|
||
else
|
||
begin
|
||
DontClear:=false;
|
||
case Event.KeyCode of
|
||
kbAltF10 : Message(@Self,evCommand,cmLocalMenu,@Self);
|
||
else
|
||
case Event.CharCode of
|
||
#9,#32..#255 :
|
||
begin
|
||
NoSelect:=true;
|
||
AddChar(Event.CharCode);
|
||
NoSelect:=false;
|
||
end;
|
||
else
|
||
DontClear:=true;
|
||
end; { case Event.CharCode .. }
|
||
end; { case Event.KeyCode .. }
|
||
if not DontClear then
|
||
ClearEvent(Event);
|
||
end;
|
||
InASCIIMode:=false;
|
||
end;
|
||
evCommand :
|
||
begin
|
||
DontClear:=false;
|
||
case Event.Command of
|
||
cmASCIIChar : InASCIIMode:=not InASCIIMode;
|
||
cmCharLeft : CharLeft;
|
||
cmCharRight : CharRight;
|
||
cmWordLeft : WordLeft;
|
||
cmWordRight : WordRight;
|
||
cmLineStart : LineStart;
|
||
cmLineEnd : LineEnd;
|
||
cmLineUp : LineUp;
|
||
cmLineDown : LineDown;
|
||
cmPageUp : PageUp;
|
||
cmPageDown : PageDown;
|
||
cmTextStart : TextStart;
|
||
cmTextEnd : TextEnd;
|
||
cmNewLine : InsertLine;
|
||
cmBreakLine : BreakLine;
|
||
cmBackSpace : BackSpace;
|
||
cmDelChar : DelChar;
|
||
cmDelWord : DelWord;
|
||
cmDelStart : DelStart;
|
||
cmDelEnd : DelEnd;
|
||
cmDelLine : DelLine;
|
||
cmInsMode : InsMode;
|
||
cmStartSelect : StartSelect;
|
||
cmHideSelect : HideSelect;
|
||
cmUpdateTitle : ;
|
||
cmEndSelect : EndSelect;
|
||
cmDelSelect : DelSelect;
|
||
cmCopyBlock : CopyBlock;
|
||
cmMoveBlock : MoveBlock;
|
||
cmIndentBlock : IndentBlock;
|
||
cmUnindentBlock : UnindentBlock;
|
||
cmSelStart : JumpSelStart;
|
||
cmSelEnd : JumpSelEnd;
|
||
cmLastCursorPos : JumpToLastCursorPos;
|
||
cmJumpMark0..cmJumpMark9 : JumpMark(Event.Command-cmJumpMark0);
|
||
cmSetMark0..cmSetMark9 : DefineMark(Event.Command-cmSetMark0);
|
||
cmSelectWord : SelectWord;
|
||
cmSelectLine : SelectLine;
|
||
cmWriteBlock : WriteBlock;
|
||
cmReadBlock : ReadBlock;
|
||
cmPrintBlock : PrintBlock;
|
||
{ ------ }
|
||
cmFind : Find;
|
||
cmReplace : Replace;
|
||
cmSearchAgain : DoSearchReplace;
|
||
cmJumpLine : GotoLine;
|
||
{ ------ }
|
||
cmCut : ClipCut;
|
||
cmCopy : ClipCopy;
|
||
cmPaste : ClipPaste;
|
||
{$ifdef WinClipSupported}
|
||
cmCopyWin : ClipCopyWin;
|
||
cmPasteWin : ClipPasteWin;
|
||
{$endif WinClipSupported}
|
||
cmUndo : Undo;
|
||
cmRedo : Redo;
|
||
cmClear : DelSelect;
|
||
cmLocalMenu :
|
||
begin
|
||
P:=CurPos; Inc(P.X); Inc(P.Y);
|
||
LocalMenu(P);
|
||
end;
|
||
else DontClear:=true;
|
||
end;
|
||
if DontClear=false then ClearEvent(Event);
|
||
end;
|
||
evBroadcast :
|
||
case Event.Command of
|
||
cmUpdate :
|
||
Update;
|
||
cmClearLineHighlights :
|
||
SetHighlightRow(-1);
|
||
cmResetDebuggerRow :
|
||
SetDebuggerRow(-1);
|
||
cmScrollBarChanged:
|
||
if (Event.InfoPtr = HScrollBar) or
|
||
(Event.InfoPtr = VScrollBar) then
|
||
begin
|
||
CheckScrollBar(HScrollBar, Delta.X);
|
||
CheckScrollBar(VScrollBar, Delta.Y);
|
||
end;
|
||
end;
|
||
end;
|
||
inherited HandleEvent(Event);
|
||
CurEvent:=OldEvent;
|
||
end;
|
||
|
||
procedure TCodeEditor.Update;
|
||
begin
|
||
LimitsChanged;
|
||
SelectionChanged; HighlightChanged;
|
||
UpdateIndicator;
|
||
DrawView;
|
||
end;
|
||
|
||
function TCodeEditor.GetLocalMenu: PMenu;
|
||
begin
|
||
GetLocalMenu:=nil;
|
||
end;
|
||
|
||
function TCodeEditor.GetCommandTarget: PView;
|
||
begin
|
||
GetCommandTarget:=@Self;
|
||
end;
|
||
|
||
function TCodeEditor.CreateLocalMenuView(var Bounds: TRect; M: PMenu): PMenuPopup;
|
||
var MV: PMenuPopup;
|
||
begin
|
||
New(MV, Init(Bounds, M));
|
||
CreateLocalMenuView:=MV;
|
||
end;
|
||
|
||
procedure TCodeEditor.LocalMenu(P: TPoint);
|
||
var M: PMenu;
|
||
MV: PMenuPopUp;
|
||
R: TRect;
|
||
Re: word;
|
||
begin
|
||
M:=GetLocalMenu;
|
||
if M=nil then Exit;
|
||
if LastLocalCmd<>0 then
|
||
M^.Default:=SearchMenuItem(M,LastLocalCmd);
|
||
Desktop^.GetExtent(R);
|
||
MakeGlobal(P,R.A); {Desktop^.MakeLocal(R.A,R.A);}
|
||
MV:=CreateLocalMenuView(R,M);
|
||
Re:=Application^.ExecView(MV);
|
||
if M^.Default=nil then LastLocalCmd:=0
|
||
else LastLocalCmd:=M^.Default^.Command;
|
||
Dispose(MV, Done);
|
||
if Re<>0 then
|
||
Message(GetCommandTarget,evCommand,Re,@Self);
|
||
end;
|
||
|
||
|
||
procedure TCodeEditor.Draw;
|
||
var SelectColor,
|
||
HighlightColColor,
|
||
HighlightRowColor,
|
||
ErrorMessageColor : word;
|
||
B: TDrawBuffer;
|
||
X,Y,AX,AY,MaxX: sw_integer;
|
||
PX: TPoint;
|
||
LineCount: sw_integer;
|
||
Line: PLine;
|
||
LineText,Format: string;
|
||
isBreak : boolean;
|
||
C: char;
|
||
FreeFormat: array[0..255] of boolean;
|
||
Color: word;
|
||
ColorTab: array[coFirstColor..coLastColor] of word;
|
||
ErrorLine: integer;
|
||
ErrorMsg: string[MaxViewWidth];
|
||
function CombineColors(Orig,Modifier: byte): byte;
|
||
var Color: byte;
|
||
begin
|
||
if (Modifier and $0f)=0 then
|
||
Color:=(Orig and $0f) or (Modifier and $f0)
|
||
else
|
||
Color:=(Orig and $f0) or (Modifier and $0f);
|
||
{ do not allow invisible }
|
||
{ use white as foreground in this case }
|
||
if (Color and $f) = ((Color div $10) and $7) then
|
||
Color:=(Color and $F0) or $F;
|
||
CombineColors:=Color;
|
||
end;
|
||
const NulLine : TLine = (Text: nil; Format: nil);
|
||
begin
|
||
|
||
if LockFlag>0 then
|
||
begin
|
||
DrawCalled:=true;
|
||
Exit;
|
||
end;
|
||
DrawCalled:=false;
|
||
|
||
ErrorMsg:=copy(GetErrorMessage,1,MaxViewWidth);
|
||
if ErrorMsg='' then ErrorLine:=-1 else
|
||
if (CurPos.Y-Delta.Y)<(Size.Y div 2) then ErrorLine:=Size.Y-1
|
||
else ErrorLine:=0;
|
||
LineCount:=GetLineCount;
|
||
ColorTab[coTextColor]:=GetColor(1);
|
||
ColorTab[coWhiteSpaceColor]:=GetColor(2);
|
||
ColorTab[coCommentColor]:=GetColor(3);
|
||
ColorTab[coReservedWordColor]:=GetColor(4);
|
||
ColorTab[coIdentifierColor]:=GetColor(5);
|
||
ColorTab[coStringColor]:=GetColor(6);
|
||
ColorTab[coNumberColor]:=GetColor(7);
|
||
ColorTab[coAssemblerColor]:=GetColor(8);
|
||
ColorTab[coSymbolColor]:=GetColor(9);
|
||
ColorTab[coDirectiveColor]:=GetColor(13);
|
||
ColorTab[coHexNumberColor]:=GetColor(14);
|
||
ColorTab[coTabColor]:=GetColor(15);
|
||
{ break same as error }
|
||
ColorTab[coBreakColor]:=GetColor(16);
|
||
SelectColor:=GetColor(10);
|
||
HighlightColColor:=GetColor(11);
|
||
HighlightRowColor:=GetColor(12);
|
||
ErrorMessageColor:=GetColor(16);
|
||
for Y:=0 to Size.Y-1 do
|
||
if Y=ErrorLine then
|
||
begin
|
||
MoveChar(B,' ',ErrorMessageColor,Size.X);
|
||
MoveStr(B,ErrorMsg,ErrorMessageColor);
|
||
WriteLine(0,Y,Size.X,1,B);
|
||
end else
|
||
begin
|
||
AY:=Delta.Y+Y;
|
||
Color:=ColorTab[coTextColor];
|
||
FillChar(FreeFormat,SizeOf(FreeFormat),1);
|
||
MoveChar(B,' ',Color,Size.X);
|
||
if AY<LineCount then
|
||
begin
|
||
Line:=GetLine(AY);
|
||
IsBreak:=Lines^.at(AY)^.isBreakpoint;
|
||
end
|
||
else
|
||
begin
|
||
Line:=@NulLine;
|
||
IsBreak:=false;
|
||
end;
|
||
GetDisplayTextFormat(AY,LineText,Format);
|
||
|
||
{ if (Flags and efSyntaxHighlight)<>0 then MaxX:=length(LineText)+1
|
||
else }MaxX:=Size.X+Delta.X;
|
||
for X:=1 to Min(MaxX,255) do
|
||
begin
|
||
AX:=Delta.X+X-1;
|
||
if X<=length(LineText) then C:=LineText[X] else C:=' ';
|
||
|
||
PX.X:=AX-Delta.X; PX.Y:=AY;
|
||
if (Highlight.A.X<>Highlight.B.X) or (Highlight.A.Y<>Highlight.B.Y) then
|
||
begin
|
||
if (PointOfs(Highlight.A)<=PointOfs(PX)) and (PointOfs(PX)<PointOfs(Highlight.B)) then
|
||
begin
|
||
Color:=SelectColor;
|
||
FreeFormat[X]:=false;
|
||
end;
|
||
end else
|
||
{ no highlight }
|
||
begin
|
||
if (Flags and efVerticalBlocks<>0) then
|
||
begin
|
||
if (SelStart.X<=AX) and (AX<=SelEnd.X) and
|
||
(SelStart.Y<=AY) and (AY<=SelEnd.Y) then
|
||
begin Color:=SelectColor; FreeFormat[X]:=false; end;
|
||
end else
|
||
if PointOfs(SelStart)<>PointOfs(SelEnd) then
|
||
if (PointOfs(SelStart)<=PointOfs(PX)) and (PointOfs(PX)<PointOfs(SelEnd)) then
|
||
begin Color:=SelectColor; FreeFormat[X]:=false; end;
|
||
end;
|
||
if FreeFormat[X] then
|
||
if X<=length(Format) then
|
||
{Color:=ColorTab[ord(Format[X])] else Color:=ColorTab[coTextColor];
|
||
this give BoundsCheckError with -Cr quite often PM }
|
||
Color:=ColorTab[ord(Format[X]) mod (coLastColor + 1)] else Color:=ColorTab[coTextColor];
|
||
|
||
if ( ((Flags and efHighlightRow) <>0) and
|
||
(PX.Y=CurPos.Y) ) and (HighlightRow=-1) then
|
||
begin
|
||
Color:=CombineColors(Color,HighlightRowColor);
|
||
FreeFormat[X]:=false;
|
||
end;
|
||
if ( ((Flags and efHighlightColumn)<>0) and (PX.X=CurPos.X) ) then
|
||
begin
|
||
Color:=CombineColors(Color,HighlightColColor);
|
||
FreeFormat[X]:=false;
|
||
end;
|
||
|
||
if HighlightRow=AY then
|
||
begin
|
||
Color:=CombineColors(Color,HighlightRowColor);
|
||
FreeFormat[X]:=false;
|
||
end;
|
||
if DebuggerRow=AY then
|
||
begin
|
||
Color:=CombineColors(Color,HighlightRowColor);
|
||
FreeFormat[X]:=false;
|
||
end;
|
||
if isbreak then
|
||
begin
|
||
Color:=ColorTab[coBreakColor];
|
||
FreeFormat[X]:=false;
|
||
end;
|
||
|
||
if (0<=X-1-Delta.X) and (X-1-Delta.X<MaxViewWidth) then
|
||
MoveChar(B[X-1-Delta.X],C,Color,1);
|
||
end;
|
||
WriteLine(0,Y,Size.X,1,B);
|
||
end;
|
||
DrawCursor;
|
||
end;
|
||
|
||
procedure TCodeEditor.DrawCursor;
|
||
begin
|
||
SetCursor(CurPos.X-Delta.X,CurPos.Y-Delta.Y);
|
||
SetState(sfCursorIns,Overwrite);
|
||
end;
|
||
|
||
function TCodeEditor.Overwrite: boolean;
|
||
begin
|
||
Overwrite:=(Flags and efInsertMode)=0;
|
||
end;
|
||
|
||
function TCodeEditor.GetLineCount: sw_integer;
|
||
begin
|
||
GetLineCount:=Lines^.Count;
|
||
end;
|
||
|
||
function TCodeEditor.GetLine(I: sw_integer): PLine;
|
||
begin
|
||
GetLine:=Lines^.At(I);
|
||
end;
|
||
|
||
function TCodeEditor.CharIdxToLinePos(Line,CharIdx: sw_integer): sw_integer;
|
||
var S: string;
|
||
CP,RX: sw_integer;
|
||
begin
|
||
S:=GetLineText(Line);
|
||
CP:=1; RX:=0;
|
||
while (CP<=length(S)) and (CP<CharIdx) do
|
||
begin
|
||
if S[CP]=TAB then
|
||
Inc(RX,TabSize-(RX mod TabSize))
|
||
else
|
||
Inc(RX);
|
||
Inc(CP);
|
||
end;
|
||
CharIdxToLinePos:=RX;
|
||
end;
|
||
|
||
function TCodeEditor.LinePosToCharIdx(Line,X: sw_integer): sw_integer;
|
||
var S: string;
|
||
CP,RX: sw_integer;
|
||
begin
|
||
S:=GetLineText(Line);
|
||
if S='' then
|
||
CP:=0
|
||
else
|
||
begin
|
||
CP:=0; RX:=0;
|
||
while (RX<X) and (CP<length(S)) do
|
||
begin
|
||
Inc(CP);
|
||
if S[CP]=TAB then
|
||
Inc(RX,TabSize-(RX mod TabSize))
|
||
else
|
||
Inc(RX);
|
||
end;
|
||
end;
|
||
LinePosToCharIdx:=CP;
|
||
end;
|
||
|
||
{function TCodeEditor.GetLineTextPos(Line,X: integer): integer;
|
||
var
|
||
S: string;
|
||
rx,i : Sw_integer;
|
||
begin
|
||
S:=GetLineText(Line);
|
||
i:=0; rx:=0;
|
||
while (RX<X) and (i<Length(s)) do
|
||
begin
|
||
inc(i);
|
||
inc(rx);
|
||
if s[i]=#9 then
|
||
inc(rx,TabSize-(rx mod tabsize));
|
||
end;
|
||
if RX<X then Inc(I,X-RX);
|
||
GetLineTextPos:=i;
|
||
end;
|
||
|
||
function TCodeEditor.GetDisplayTextPos(Line,X: integer): integer;
|
||
var
|
||
S: string;
|
||
L: PLine;
|
||
rx,i : Sw_integer;
|
||
begin
|
||
S:='';
|
||
if Line<Lines^.Count then
|
||
begin
|
||
L:=Lines^.At(Line);
|
||
if assigned(L^.Text) then
|
||
S:=L^.Text^;
|
||
end;
|
||
i:=0;
|
||
rx:=0;
|
||
while (i<X) and (i<Length(s)) do
|
||
begin
|
||
inc(i);
|
||
inc(rx);
|
||
if s[i]=#9 then
|
||
inc(rx,TabSize-(rx mod tabsize));
|
||
end;
|
||
GetDisplayTextPos:=rx;
|
||
end;}
|
||
|
||
function TCodeEditor.GetLineText(I: sw_integer): string;
|
||
var
|
||
L : PLine;
|
||
begin
|
||
GetLineText:='';
|
||
if I<Lines^.Count then
|
||
begin
|
||
L:=Lines^.At(I);
|
||
if assigned(L^.Text) then
|
||
GetLineText:=L^.Text^;
|
||
end;
|
||
end;
|
||
|
||
procedure TCodeEditor.SetLineText(I: sw_integer;const S: string);
|
||
var
|
||
L : PLine;
|
||
AddCount : Sw_Integer;
|
||
begin
|
||
AddCount:=0;
|
||
while (Lines^.Count<I+1) do
|
||
begin
|
||
Lines^.Insert(NewLine(''));
|
||
Inc(AddCount);
|
||
end;
|
||
if AddCount>0 then
|
||
LimitsChanged;
|
||
L:=Lines^.At(I);
|
||
if assigned(L^.Text) then
|
||
DisposeStr(L^.Text);
|
||
L^.Text:=NewStr(S);
|
||
end;
|
||
|
||
procedure TCodeEditor.SetLineBreakState(I : sw_integer;b : boolean);
|
||
var PL : PLine;
|
||
begin
|
||
if (i>0) and (i<=Lines^.Count) then
|
||
PL:=Lines^.At(i-1)
|
||
else
|
||
exit;
|
||
if assigned(PL) then
|
||
PL^.isbreakpoint:=b;
|
||
DrawView;
|
||
end;
|
||
|
||
function TCodeEditor.GetDisplayText(I: sw_integer): string;
|
||
begin
|
||
GetDisplayText:=ExtractTabs(GetLineText(I),TabSize);
|
||
end;
|
||
|
||
procedure TCodeEditor.SetDisplayText(I: sw_integer;const S: string);
|
||
begin
|
||
if ((Flags and efUseTabCharacters)<>0) and (TabSize>0) then
|
||
SetLineText(I,CompressUsingTabs(S,TabSize))
|
||
else
|
||
SetLineText(I,S);
|
||
end;
|
||
|
||
procedure TCodeEditor.GetDisplayTextFormat(I: sw_integer;var DT,DF:string);
|
||
var
|
||
L : PLine;
|
||
P,PAdd : SW_Integer;
|
||
begin
|
||
DF:='';
|
||
DT:='';
|
||
if I<Lines^.Count then
|
||
begin
|
||
L:=Lines^.At(I);
|
||
if assigned(L^.Text) then
|
||
begin
|
||
if assigned(L^.Format)=false then DF:='' else
|
||
DF:=L^.Format^;
|
||
DT:=L^.Text^;
|
||
p:=0;
|
||
while p<length(DT) do
|
||
begin
|
||
inc(p);
|
||
if DT[p]=#9 then
|
||
begin
|
||
PAdd:=TabSize-((p-1) mod TabSize);
|
||
if DF<>'' then
|
||
DF:=copy(DF,1,P-1)+CharStr(DF[p],PAdd)+copy(DF,P+1,255);
|
||
DT:=copy(DT,1,P-1)+CharStr(' ',PAdd)+copy(DT,P+1,255);
|
||
inc(P,PAdd-1);
|
||
end;
|
||
end;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
function TCodeEditor.GetLineFormat(I: sw_integer): string;
|
||
var P: PLine;
|
||
S: string;
|
||
begin
|
||
if I<GetLineCount then P:=Lines^.At(I) else P:=nil;
|
||
if (P=nil) or (P^.Format=nil) then S:='' else
|
||
S:=P^.Format^;
|
||
GetLineFormat:=S;
|
||
end;
|
||
|
||
procedure TCodeEditor.SetLineFormat(I: sw_integer;const S: string);
|
||
var P: PLine;
|
||
begin
|
||
if I<GetLineCount then
|
||
begin
|
||
P:=Lines^.At(I);
|
||
if P^.Format<>nil then DisposeStr(P^.Format);
|
||
P^.Format:=NewStr(S);
|
||
end;
|
||
end;
|
||
|
||
procedure TCodeEditor.DeleteAllLines;
|
||
begin
|
||
if Assigned(Lines) then
|
||
Lines^.FreeAll;
|
||
end;
|
||
|
||
procedure TCodeEditor.DeleteLine(I: sw_integer);
|
||
begin
|
||
if I<Lines^.Count then
|
||
Lines^.AtFree(I);
|
||
end;
|
||
|
||
procedure TCodeEditor.AddLine(const S: string);
|
||
begin
|
||
Lines^.Insert(NewLine(S));
|
||
end;
|
||
|
||
function TCodeEditor.GetSpecSymbolCount(SpecClass: TSpecSymbolClass): integer;
|
||
begin
|
||
GetSpecSymbolCount:=0;
|
||
end;
|
||
|
||
function TCodeEditor.GetSpecSymbol(SpecClass: TSpecSymbolClass; Index: integer): string;
|
||
begin
|
||
GetSpecSymbol:='';
|
||
Abstract;
|
||
end;
|
||
|
||
function TCodeEditor.IsReservedWord(const S: string): boolean;
|
||
begin
|
||
IsReservedWord:=false;
|
||
end;
|
||
|
||
procedure TCodeEditor.Indent;
|
||
var S, PreS: string;
|
||
Shift: integer;
|
||
begin
|
||
S:=GetLineText(CurPos.Y);
|
||
if CurPos.Y>0 then
|
||
PreS:=RTrim(GetLineText(CurPos.Y-1))
|
||
else
|
||
PreS:='';
|
||
if CurPos.X>=length(PreS) then
|
||
Shift:=TabSize
|
||
else
|
||
begin
|
||
Shift:=1;
|
||
while (CurPos.X+Shift<length(PreS)) and (PreS[CurPos.X+Shift]<>' ') do
|
||
Inc(Shift);
|
||
end;
|
||
SetLineText(CurPos.Y,RExpand(copy(S,1,CurPos.X+1),CurPos.X+1)+CharStr(' ',Shift)+copy(S,CurPos.X+2,255));
|
||
SetCurPtr(CurPos.X+Shift,CurPos.Y);
|
||
UpdateAttrs(CurPos.Y,attrAll);
|
||
DrawLines(CurPos.Y);
|
||
SetModified(true);
|
||
end;
|
||
|
||
procedure TCodeEditor.CharLeft;
|
||
begin
|
||
if CurPos.X=0 then Exit;
|
||
|
||
SetCurPtr(CurPos.X-1,CurPos.Y);
|
||
end;
|
||
|
||
procedure TCodeEditor.CharRight;
|
||
begin
|
||
if CurPos.X>=MaxLineLength then
|
||
Exit;
|
||
SetCurPtr(CurPos.X+1,CurPos.Y);
|
||
end;
|
||
|
||
procedure TCodeEditor.WordLeft;
|
||
var X, Y: sw_integer;
|
||
Line: string;
|
||
GotIt,FoundNonSeparator: boolean;
|
||
begin
|
||
X:=CurPos.X;
|
||
Y:=CurPos.Y;
|
||
GotIt:=false;
|
||
FoundNonSeparator:=false;
|
||
while (Y>=0) do
|
||
begin
|
||
if Y=CurPos.Y then
|
||
begin
|
||
X:=length(GetDisplayText(Y));
|
||
if CurPos.X<X then
|
||
X:=CurPos.X; Dec(X);
|
||
if (X=-1) then
|
||
begin
|
||
Dec(Y);
|
||
if Y>=0 then
|
||
X:=length(GetDisplayText(Y));
|
||
Break;
|
||
end;
|
||
end
|
||
else
|
||
X:=length(GetDisplayText(Y))-1;
|
||
Line:=GetDisplayText(Y);
|
||
while (X>=0) and (GotIt=false) do
|
||
begin
|
||
if FoundNonSeparator then
|
||
begin
|
||
if IsWordSeparator(Line[X+1]) then
|
||
begin
|
||
Inc(X);
|
||
GotIt:=true;
|
||
Break;
|
||
end;
|
||
end
|
||
else
|
||
if not IsWordSeparator(Line[X+1]) then
|
||
FoundNonSeparator:=true;
|
||
Dec(X);
|
||
if (X=0) and (IsWordSeparator(Line[1])=false) then
|
||
begin
|
||
GotIt:=true;
|
||
Break;
|
||
end;
|
||
end;
|
||
if GotIt then
|
||
Break;
|
||
X:=0;
|
||
Dec(Y);
|
||
if Y>=0 then
|
||
begin
|
||
X:=length(GetDisplayText(Y));
|
||
Break;
|
||
end;
|
||
end;
|
||
if Y<0 then Y:=0; if X<0 then X:=0;
|
||
SetCurPtr(X,Y);
|
||
end;
|
||
|
||
procedure TCodeEditor.WordRight;
|
||
var X, Y: sw_integer;
|
||
Line: string;
|
||
GotIt: boolean;
|
||
begin
|
||
X:=CurPos.X; Y:=CurPos.Y; GotIt:=false;
|
||
while (Y<GetLineCount) do
|
||
begin
|
||
if Y=CurPos.Y then
|
||
begin
|
||
X:=CurPos.X; Inc(X);
|
||
if (X>length(GetDisplayText(Y))-1) then
|
||
begin Inc(Y); X:=0; end;
|
||
end else X:=0;
|
||
Line:=GetDisplayText(Y);
|
||
while (X<=length(Line)+1) and (GotIt=false) and (Line<>'') do
|
||
begin
|
||
if X=length(Line)+1 then begin GotIt:=true; Dec(X); Break end;
|
||
if IsWordSeparator(Line[X]) then
|
||
begin
|
||
while (Y<GetLineCount) and
|
||
(X<=length(Line)) and (IsWordSeparator(Line[X])) do
|
||
begin
|
||
Inc(X);
|
||
if X>=length(Line) then
|
||
begin GotIt:=true; Dec(X); Break; end;
|
||
end;
|
||
if (GotIt=false) and (X<length(Line)) then
|
||
begin
|
||
Dec(X);
|
||
GotIt:=true;
|
||
Break;
|
||
end;
|
||
end;
|
||
Inc(X);
|
||
end;
|
||
if GotIt then Break;
|
||
X:=0;
|
||
Inc(Y);
|
||
if (Y<GetLineCount) then
|
||
begin
|
||
Line:=GetDisplayText(Y);
|
||
if (Line<>'') and (IsWordSeparator(Line[1])=false) then Break;
|
||
end;
|
||
end;
|
||
if Y=GetLineCount then Y:=GetLineCount-1;
|
||
SetCurPtr(X,Y);
|
||
end;
|
||
|
||
procedure TCodeEditor.LineStart;
|
||
begin
|
||
SetCurPtr(0,CurPos.Y);
|
||
end;
|
||
|
||
procedure TCodeEditor.LineEnd;
|
||
var
|
||
s : string;
|
||
i : longint;
|
||
begin
|
||
if CurPos.Y<GetLineCount then
|
||
begin
|
||
s:=GetDisplayText(CurPos.Y);
|
||
i:=length(s);
|
||
while (i>0) and (s[i]=' ') do
|
||
dec(i);
|
||
SetCurPtr(i,CurPos.Y);
|
||
end
|
||
else
|
||
SetCurPtr(0,CurPos.Y);
|
||
end;
|
||
|
||
procedure TCodeEditor.LineUp;
|
||
begin
|
||
if CurPos.Y>0 then
|
||
SetCurPtr(CurPos.X,CurPos.Y-1);
|
||
end;
|
||
|
||
procedure TCodeEditor.LineDown;
|
||
begin
|
||
if CurPos.Y<GetLineCount-1 then
|
||
SetCurPtr(CurPos.X,CurPos.Y+1);
|
||
end;
|
||
|
||
procedure TCodeEditor.PageUp;
|
||
begin
|
||
ScrollTo(Delta.X,Max(Delta.Y-Size.Y,0));
|
||
SetCurPtr(CurPos.X,Max(0,CurPos.Y-(Size.Y)));
|
||
end;
|
||
|
||
procedure TCodeEditor.PageDown;
|
||
begin
|
||
ScrollTo(Delta.X,Min(Delta.Y+Size.Y,GetLineCount-1));
|
||
SetCurPtr(CurPos.X,Min(GetLineCount-1,CurPos.Y+(Size.Y{-1})));
|
||
end;
|
||
|
||
procedure TCodeEditor.TextStart;
|
||
begin
|
||
SetCurPtr(0,0);
|
||
end;
|
||
|
||
procedure TCodeEditor.TextEnd;
|
||
var s : string;
|
||
i : longint;
|
||
begin
|
||
s:=GetDisplayText(GetLineCount-1);
|
||
i:=length(s);
|
||
while (i>0) and (s[i]=' ') do
|
||
dec(i);
|
||
SetCurPtr(i,GetLineCount-1);
|
||
end;
|
||
|
||
procedure TCodeEditor.JumpSelStart;
|
||
begin
|
||
if ValidBlock then
|
||
SetCurPtr(SelStart.X,SelStart.Y);
|
||
end;
|
||
|
||
procedure TCodeEditor.JumpSelEnd;
|
||
begin
|
||
if ValidBlock then
|
||
SetCurPtr(SelEnd.X,SelEnd.Y);
|
||
end;
|
||
|
||
procedure TCodeEditor.JumpMark(MarkIdx: integer);
|
||
begin
|
||
if (MarkIdx<Low(Bookmarks)) or (MarkIdx>High(Bookmarks)) then
|
||
begin ErrorBox('Invalid mark index ('+IntToStr(MarkIdx)+')',nil); Exit; end;
|
||
|
||
with Bookmarks[MarkIdx] do
|
||
if Valid=false then
|
||
InformationBox('Mark '+IntToStr(MarkIdx)+' not set.',nil)
|
||
else
|
||
SetCurPtr(Pos.X,Pos.Y);
|
||
end;
|
||
|
||
procedure TCodeEditor.DefineMark(MarkIdx: integer);
|
||
begin
|
||
if (MarkIdx<Low(Bookmarks)) or (MarkIdx>High(Bookmarks)) then
|
||
begin ErrorBox('Invalid mark index ('+IntToStr(MarkIdx)+')',nil); Exit; end;
|
||
|
||
with Bookmarks[MarkIdx] do
|
||
begin
|
||
Pos:=CurPos;
|
||
Valid:=true;
|
||
end;
|
||
end;
|
||
|
||
procedure TCodeEditor.JumpToLastCursorPos;
|
||
begin
|
||
end;
|
||
|
||
function TCodeEditor.InsertLine: Sw_integer;
|
||
var Ind: Sw_integer;
|
||
S,IndentStr: string;
|
||
procedure CalcIndent(LineOver: Sw_integer);
|
||
begin
|
||
if (LineOver<0) or (LineOver>GetLineCount) then Ind:=0 else
|
||
begin
|
||
IndentStr:=GetLineText(LineOver);
|
||
Ind:=0;
|
||
while (Ind<length(IndentStr)) and (IndentStr[Ind+1]=' ') do
|
||
Inc(Ind);
|
||
end;
|
||
IndentStr:=CharStr(' ',Ind);
|
||
end;
|
||
var SelBack: sw_integer;
|
||
SCP: TPoint;
|
||
begin
|
||
if IsReadOnly then begin InsertLine:=-1; Exit; end;
|
||
SCP:=CurPos;
|
||
if CurPos.Y<GetLineCount then S:=GetLineText(CurPos.Y) else S:='';
|
||
if Overwrite=false then
|
||
begin
|
||
SelBack:=0;
|
||
if GetLineCount>0 then
|
||
begin
|
||
S:=GetDisplayText(CurPos.Y);
|
||
SelBack:=length(S)-SelEnd.X;
|
||
SetDisplayText(CurPos.Y,RTrim(S));
|
||
end;
|
||
SetDisplayText(CurPos.Y,copy(S,1,CurPos.X-1+1));
|
||
CalcIndent(CurPos.Y);
|
||
Lines^.AtInsert(CurPos.Y+1,NewLine(IndentStr+copy(S,CurPos.X+1,255)));
|
||
LimitsChanged;
|
||
(* if PointOfs(SelStart)<>PointOfs(SelEnd) then { !!! check it - it's buggy !!! }
|
||
begin SelEnd.Y:=CurPos.Y+1; SelEnd.X:=length(GetLineText(CurPos.Y+1))-SelBack; end;*)
|
||
UpdateAttrs(CurPos.Y,attrAll);
|
||
SetCurPtr(Ind,CurPos.Y+1);
|
||
AdjustSelection(CurPos.X-SCP.X,CurPos.Y-SCP.Y);
|
||
end else
|
||
begin
|
||
if CurPos.Y=GetLineCount-1 then
|
||
CalcIndent(CurPos.Y);
|
||
begin
|
||
Lines^.Insert(NewLine(IndentStr));
|
||
AdjustSelection(0,1);
|
||
LimitsChanged;
|
||
end;
|
||
UpdateAttrs(CurPos.Y,attrAll);
|
||
SetCurPtr(Ind,CurPos.Y+1);
|
||
end;
|
||
DrawLines(CurPos.Y);
|
||
SetModified(true);
|
||
end;
|
||
|
||
procedure TCodeEditor.BreakLine;
|
||
begin
|
||
NotImplemented; Exit;
|
||
end;
|
||
|
||
procedure TCodeEditor.BackSpace;
|
||
var S,PreS: string;
|
||
OI,CI,CP,Y,TX: Sw_integer;
|
||
SCP: TPoint;
|
||
begin
|
||
if IsReadOnly then Exit;
|
||
SCP:=CurPos;
|
||
if CurPos.X=0 then
|
||
begin
|
||
if CurPos.Y>0 then
|
||
begin
|
||
S:=GetLineText(CurPos.Y-1);
|
||
SetLineText(CurPos.Y-1,S+GetLineText(CurPos.Y));
|
||
DeleteLine(CurPos.Y);
|
||
LimitsChanged;
|
||
SetCurPtr(length(S),CurPos.Y-1);
|
||
end;
|
||
end
|
||
else
|
||
begin
|
||
S:=GetDisplayText(CurPos.Y);
|
||
CP:=CurPos.X-1;
|
||
if (Flags and efBackspaceUnindents)<>0 then
|
||
if Trim(copy(S,1,CP+1))='' then
|
||
begin
|
||
Y:=CurPos.Y;
|
||
while (Y>0) do
|
||
begin
|
||
Dec(Y);
|
||
PreS:=GetDisplayText(Y);
|
||
if Trim(copy(PreS,1,CP+1))<>'' then Break;
|
||
end;
|
||
if Y<0 then PreS:='';
|
||
{ while (CP>0) and
|
||
( (CP>length(S)) or (S[CP]=' ') ) and
|
||
( (CP>length(PreS)) or (PreS[CP]<>' ') ) do
|
||
Dec(CP);}
|
||
TX:=0;
|
||
while (TX<length(PreS)) and (PreS[TX+1]=' ') do
|
||
Inc(TX);
|
||
if TX<CP then CP:=TX;
|
||
end;
|
||
S:=GetLineText(CurPos.Y);
|
||
OI:=LinePosToCharIdx(CurPos.Y,CurPos.X);
|
||
CI:=LinePosToCharIdx(CurPos.Y,CP);
|
||
SetLineText(CurPos.Y,copy(S,1,CI+1-1)+copy(S,OI+1,255));
|
||
SetCurPtr(CP,CurPos.Y);
|
||
end;
|
||
UpdateAttrs(CurPos.Y,attrAll);
|
||
AdjustSelection(CurPos.X-SCP.X,CurPos.Y-SCP.Y);
|
||
DrawLines(CurPos.Y);
|
||
SetModified(true);
|
||
end;
|
||
|
||
procedure TCodeEditor.DelChar;
|
||
var S: string;
|
||
SDX,SDY: integer;
|
||
begin
|
||
if IsReadOnly then Exit;
|
||
S:=GetLineText(CurPos.Y);
|
||
if CurPos.X=length(S) then
|
||
begin
|
||
if CurPos.Y<GetLineCount-1 then
|
||
begin
|
||
SetLineText(CurPos.Y,S+GetLineText(CurPos.Y+1));
|
||
DeleteLine(CurPos.Y+1);
|
||
LimitsChanged;
|
||
SDX:=0; SDY:=-1;
|
||
end;
|
||
end
|
||
else
|
||
begin
|
||
Delete(S,CurPos.X+1,1);
|
||
SetLineText(CurPos.Y,S);
|
||
SDX:=-1; SDY:=0;
|
||
end;
|
||
SetCurPtr(CurPos.X,CurPos.Y);
|
||
UpdateAttrs(CurPos.Y,attrAll);
|
||
AdjustSelection(SDX,SDY);
|
||
DrawLines(CurPos.Y);
|
||
SetModified(true);
|
||
end;
|
||
|
||
procedure TCodeEditor.DelWord;
|
||
begin
|
||
if IsReadOnly then Exit;
|
||
|
||
NotImplemented; Exit;
|
||
|
||
SetModified(true);
|
||
end;
|
||
|
||
procedure TCodeEditor.DelStart;
|
||
begin
|
||
if IsReadOnly then Exit;
|
||
|
||
NotImplemented; Exit;
|
||
|
||
SetModified(true);
|
||
end;
|
||
|
||
procedure TCodeEditor.DelEnd;
|
||
var S: string;
|
||
begin
|
||
if IsReadOnly then Exit;
|
||
S:=GetLineText(CurPos.Y);
|
||
if (S<>'') and (CurPos.X<>length(S)) then
|
||
begin
|
||
SetLineText(CurPos.Y,copy(S,1,CurPos.X));
|
||
SetCurPtr(CurPos.X,CurPos.Y);
|
||
UpdateAttrs(CurPos.Y,attrAll);
|
||
DrawLines(CurPos.Y);
|
||
SetModified(true);
|
||
end;
|
||
end;
|
||
|
||
procedure TCodeEditor.DelLine;
|
||
begin
|
||
if IsReadOnly then Exit;
|
||
if GetLineCount>0 then
|
||
begin
|
||
DeleteLine(CurPos.Y);
|
||
LimitsChanged;
|
||
AdjustSelection(0,-1);
|
||
SetCurPtr(0,CurPos.Y);
|
||
UpdateAttrs(Max(0,CurPos.Y-1),attrAll);
|
||
DrawLines(CurPos.Y);
|
||
SetModified(true);
|
||
end;
|
||
end;
|
||
|
||
procedure TCodeEditor.InsMode;
|
||
begin
|
||
SetInsertMode(Overwrite);
|
||
end;
|
||
|
||
function TCodeEditor.GetCurrentWord : string;
|
||
const WordChars = ['A'..'Z','a'..'z','0'..'9','_'];
|
||
var P : TPoint;
|
||
S : String;
|
||
StartPos,EndPos : byte;
|
||
begin
|
||
P:=CurPos;
|
||
S:=GetLineText(P.Y);
|
||
StartPos:=P.X+1;
|
||
EndPos:=StartPos;
|
||
if not (S[StartPos] in WordChars) then
|
||
GetCurrentWord:=''
|
||
else
|
||
begin
|
||
While (StartPos>0) and (S[StartPos-1] in WordChars) do
|
||
Dec(StartPos);
|
||
While (EndPos<Length(S)) and (S[EndPos+1] in WordChars) do
|
||
Inc(EndPos);
|
||
GetCurrentWord:=Copy(S,StartPos,EndPos-StartPos+1);
|
||
end;
|
||
end;
|
||
|
||
procedure TCodeEditor.StartSelect;
|
||
var P1,P2: TPoint;
|
||
begin
|
||
if ValidBlock=false then
|
||
begin
|
||
{ SetSelection(SelStart,Limit);}
|
||
P1:=CurPos; P1.X:=0; P2:=CurPos; {P2.X:=length(GetLineText(P2.Y))+1;}
|
||
SetSelection(P1,P2);
|
||
end
|
||
else
|
||
SetSelection(CurPos,SelEnd);
|
||
if PointOfs(SelEnd)<PointOfs(SelStart) then
|
||
SetSelection(SelStart,SelStart);
|
||
CheckSels;
|
||
DrawView;
|
||
end;
|
||
|
||
procedure TCodeEditor.EndSelect;
|
||
var P: TPoint;
|
||
LS: sw_integer;
|
||
begin
|
||
P:=CurPos;
|
||
{ P.X:=Min(SelEnd.X,length(GetLineText(SelEnd.Y)));}
|
||
LS:=length(GetLineText(SelEnd.Y));
|
||
if LS<P.X then P.X:=LS;
|
||
CheckSels;
|
||
SetSelection(SelStart,P);
|
||
DrawView;
|
||
end;
|
||
|
||
procedure TCodeEditor.DelSelect;
|
||
var LineDelta, LineCount, CurLine: Sw_integer;
|
||
StartX,EndX,LastX: Sw_integer;
|
||
S: string;
|
||
begin
|
||
if IsReadOnly or (ValidBlock=false) then Exit;
|
||
|
||
Lock;
|
||
LineCount:=(SelEnd.Y-SelStart.Y)+1;
|
||
LineDelta:=0; LastX:=CurPos.X;
|
||
CurLine:=SelStart.Y;
|
||
while (LineDelta<LineCount) do
|
||
begin
|
||
S:=GetDisplayText(CurLine);
|
||
if LineDelta=0 then StartX:=SelStart.X else StartX:=0;
|
||
if LineDelta=LineCount-1 then EndX:=SelEnd.X else EndX:=length(S);
|
||
if (LineDelta<LineCount-1) and
|
||
( (StartX=0) and (EndX>=length(S)) )
|
||
then begin
|
||
DeleteLine(CurLine);
|
||
if CurLine>0 then LastX:=length(GetDisplayText(CurLine-1))
|
||
else LastX:=0;
|
||
end
|
||
else begin
|
||
SetDisplayText(CurLine,copy(S,1,StartX)+copy(S,EndX+1,255));
|
||
LastX:=StartX;
|
||
if (StartX=0) and (0<LineDelta) and
|
||
not(((LineDelta=LineCount-1) and (StartX=0) and (StartX=EndX))) then
|
||
begin
|
||
S:=GetDisplayText(CurLine-1);
|
||
SetDisplayText(CurLine-1,S+GetLineText(CurLine));
|
||
DeleteLine(CurLine);
|
||
LastX:=length(S);
|
||
end else
|
||
Inc(CurLine);
|
||
end;
|
||
Inc(LineDelta);
|
||
end;
|
||
HideSelect;
|
||
SetCurPtr(LastX,CurLine-1);
|
||
UpdateAttrs(CurPos.Y,attrAll);
|
||
DrawLines(CurPos.Y);
|
||
SetModified(true);
|
||
UnLock;
|
||
end;
|
||
|
||
procedure TCodeEditor.HideSelect;
|
||
begin
|
||
SetSelection(CurPos,CurPos);
|
||
DrawLines(Delta.Y);
|
||
end;
|
||
|
||
procedure TCodeEditor.CopyBlock;
|
||
var Temp: PCodeEditor;
|
||
R: TRect;
|
||
begin
|
||
if IsReadOnly or (ValidBlock=false) then Exit;
|
||
|
||
Lock;
|
||
GetExtent(R);
|
||
New(Temp, Init(R, nil, nil, nil,0));
|
||
Temp^.InsertFrom(@Self);
|
||
InsertFrom(Temp);
|
||
Dispose(Temp, Done);
|
||
UnLock;
|
||
end;
|
||
|
||
procedure TCodeEditor.MoveBlock;
|
||
var Temp: PCodeEditor;
|
||
R: TRect;
|
||
OldPos: TPoint;
|
||
begin
|
||
if IsReadOnly then Exit;
|
||
if (SelStart.X=SelEnd.X) and (SelStart.Y=SelEnd.Y) then Exit;
|
||
Lock;
|
||
GetExtent(R);
|
||
New(Temp, Init(R, nil, nil, nil,0));
|
||
Temp^.InsertFrom(@Self);
|
||
OldPos:=CurPos;
|
||
if CurPos.Y>SelStart.Y then
|
||
Dec(OldPos.Y,Temp^.GetLineCount-1);
|
||
DelSelect;
|
||
SetCurPtr(OldPos.X,OldPos.Y);
|
||
InsertFrom(Temp);
|
||
Dispose(Temp, Done);
|
||
UnLock;
|
||
end;
|
||
|
||
procedure TCodeEditor.IndentBlock;
|
||
var
|
||
ey,i : Sw_integer;
|
||
S : String;
|
||
begin
|
||
if IsReadOnly then Exit;
|
||
if (SelStart.X=SelEnd.X) and (SelStart.Y=SelEnd.Y) then Exit;
|
||
Lock;
|
||
ey:=selend.y;
|
||
if selend.x=0 then
|
||
dec(ey);
|
||
for i:=selstart.y to ey do
|
||
begin
|
||
S:=GetLineText(i);
|
||
SetLineText(i,' '+S);
|
||
end;
|
||
SetCurPtr(CurPos.X,CurPos.Y);
|
||
UpdateAttrsRange(SelStart.Y,SelEnd.Y,attrAll);
|
||
DrawLines(CurPos.Y);
|
||
SetModified(true);
|
||
UnLock;
|
||
end;
|
||
|
||
procedure TCodeEditor.UnindentBlock;
|
||
var
|
||
ey,i : Sw_integer;
|
||
S : String;
|
||
begin
|
||
if IsReadOnly then Exit;
|
||
if (SelStart.X=SelEnd.X) and (SelStart.Y=SelEnd.Y) then Exit;
|
||
Lock;
|
||
ey:=selend.y;
|
||
if selend.x=0 then
|
||
dec(ey);
|
||
for i:=selstart.y to ey do
|
||
begin
|
||
S:=GetLineText(i);
|
||
if (length(s)>1) and (S[1]=' ') then
|
||
Delete(s,1,1);
|
||
SetLineText(i,S);
|
||
end;
|
||
SetCurPtr(CurPos.X,CurPos.Y);
|
||
UpdateAttrsRange(SelStart.Y,SelEnd.Y,attrAll);
|
||
DrawLines(CurPos.Y);
|
||
SetModified(true);
|
||
UnLock;
|
||
end;
|
||
|
||
procedure TCodeEditor.SelectWord;
|
||
begin
|
||
NotImplemented; Exit;
|
||
end;
|
||
|
||
procedure TCodeEditor.SelectLine;
|
||
var A,B: TPoint;
|
||
begin
|
||
if CurPos.Y<GetLineCount then
|
||
begin
|
||
A.Y:=CurPos.Y; A.X:=0;
|
||
B.Y:=CurPos.Y+1; B.X:=0;
|
||
SetSelection(A,B);
|
||
end;
|
||
end;
|
||
|
||
procedure TCodeEditor.WriteBlock;
|
||
var FileName: string;
|
||
S: PBufStream;
|
||
begin
|
||
if ValidBlock=false then Exit;
|
||
|
||
FileName:='';
|
||
if EditorDialog(edWriteBlock, @FileName) <> cmCancel then
|
||
begin
|
||
FileName := FExpand(FileName);
|
||
|
||
New(S, Init(FileName, stCreate, 4096));
|
||
if (S=nil) or (S^.Status<>stOK) then
|
||
EditorDialog(edCreateError,@FileName)
|
||
else
|
||
if SaveAreaToStream(S,SelStart,SelEnd)=false then
|
||
EditorDialog(edWriteError,@FileName);
|
||
if Assigned(S) then Dispose(S, Done);
|
||
end;
|
||
end;
|
||
|
||
procedure TCodeEditor.ReadBlock;
|
||
var FileName: string;
|
||
S: PBufStream;
|
||
E: PCodeEditor;
|
||
R: TRect;
|
||
begin
|
||
FileName:='';
|
||
if EditorDialog(edReadBlock, @FileName) <> cmCancel then
|
||
begin
|
||
FileName := FExpand(FileName);
|
||
|
||
New(S, Init(FileName, stOpenRead, 4096));
|
||
if (S=nil) or (S^.Status<>stOK) then
|
||
EditorDialog(edReadError,@FileName)
|
||
else
|
||
begin
|
||
R.Assign(0,0,0,0);
|
||
New(E, Init(R,nil,nil,nil,0));
|
||
if E^.LoadFromStream(S)=false then
|
||
EditorDialog(edReadError,@FileName)
|
||
else
|
||
begin
|
||
E^.SelectAll(true);
|
||
Self.InsertFrom(E);
|
||
end;
|
||
Dispose(E, Done);
|
||
end;
|
||
if Assigned(S) then Dispose(S, Done);
|
||
end;
|
||
end;
|
||
|
||
|
||
procedure TCodeEditor.PrintBlock;
|
||
begin
|
||
NotImplemented; Exit;
|
||
end;
|
||
|
||
procedure TCodeEditor.AddChar(C: char);
|
||
const OpenBrackets : string[10] = '[({';
|
||
CloseBrackets : string[10] = '])}';
|
||
var S,SC,TabS: string;
|
||
BI: byte;
|
||
CI,TabStart : Sw_integer;
|
||
SP: TPoint;
|
||
begin
|
||
if IsReadOnly then Exit;
|
||
Lock;
|
||
SP:=CurPos;
|
||
if (C<>TAB) or ((Flags and efUseTabCharacters)<>0) then
|
||
SC:=C
|
||
else
|
||
SC:=CharStr(' ',TabSize);
|
||
S:=GetLineText(CurPos.Y);
|
||
if CharIdxToLinePos(CurPos.Y,length(S))<CurPos.X then
|
||
begin
|
||
S:=S+CharStr(' ',CurPos.X-CharIdxToLinePos(CurPos.Y,length(S)){-1});
|
||
SetLineText(CurPos.Y,S);
|
||
end;
|
||
CI:=LinePosToCharIdx(CurPos.Y,CurPos.X);
|
||
if (S[CI]=TAB) then
|
||
begin
|
||
TabStart:=CharIdxToLinePos(CurPos.Y,CI);
|
||
if SC=Tab then TabS:='' else
|
||
TabS:=CharStr(' ',CurPos.X-TabStart);
|
||
SetLineText(CurPos.Y,copy(S,1,CI-1)+TabS+SC+copy(S,CI,255));
|
||
SetCurPtr(CharIdxToLinePos(CurPos.Y,CI+length(TabS)+length(SC)),CurPos.Y);
|
||
end
|
||
else
|
||
begin
|
||
if Overwrite and (CI<length(S)) then
|
||
SetLineText(CurPos.Y,copy(S,1,CI)+SC+copy(S,CI+2,255))
|
||
else
|
||
SetLineText(CurPos.Y,copy(S,1,CI)+SC+copy(S,CI+1,255));
|
||
SetCurPtr(CharIdxToLinePos(CurPos.Y,CI+length(SC)+1),CurPos.Y);
|
||
{ if PointOfs(SelStart)<>PointOfs(SelEnd) then
|
||
if (CurPos.Y=SelEnd.Y) and (CurPos.X<SelEnd.X) then
|
||
Inc(SelEnd.X);
|
||
CharRight;}
|
||
end;
|
||
BI:=Pos(C,OpenBrackets);
|
||
if ((Flags and efAutoBrackets)<>0) and (BI>0) then
|
||
begin
|
||
AddChar(CloseBrackets[BI]);
|
||
SetCurPtr(CurPos.X-1,CurPos.Y);
|
||
end;
|
||
UpdateAttrs(CurPos.Y,attrAll);
|
||
AdjustSelection(CurPos.X-SP.X,CurPos.Y-SP.Y);
|
||
DrawLines(CurPos.Y);
|
||
SetModified(true);
|
||
UnLock;
|
||
end;
|
||
|
||
function TCodeEditor.ClipCopy: Boolean;
|
||
var OK: boolean;
|
||
begin
|
||
Lock;
|
||
OK:=Clipboard<>nil;
|
||
if OK then OK:=Clipboard^.InsertFrom(@Self);
|
||
ClipCopy:=OK;
|
||
UnLock;
|
||
end;
|
||
|
||
procedure TCodeEditor.ClipCut;
|
||
begin
|
||
if IsReadOnly then Exit;
|
||
Lock;
|
||
DontConsiderShiftState:=true;
|
||
if Clipboard<>nil then
|
||
if Clipboard^.InsertFrom(@Self) then
|
||
begin
|
||
if not IsClipBoard then
|
||
DelSelect;
|
||
SetModified(true);
|
||
end;
|
||
UnLock;
|
||
DontConsiderShiftState:=false;
|
||
end;
|
||
|
||
procedure TCodeEditor.ClipPaste;
|
||
begin
|
||
if IsReadOnly then Exit;
|
||
DontConsiderShiftState:=true;
|
||
Lock;
|
||
if Clipboard<>nil then
|
||
begin
|
||
InsertFrom(Clipboard);
|
||
SetModified(true);
|
||
end;
|
||
UnLock;
|
||
DontConsiderShiftState:=false;
|
||
end;
|
||
|
||
{$ifdef WinClipSupported}
|
||
function TCodeEditor.ClipPasteWin: Boolean;
|
||
var OK: boolean;
|
||
l,i : longint;
|
||
p,p10,p2,p13 : pchar;
|
||
s : string;
|
||
StorePos : TPoint;
|
||
first : boolean;
|
||
begin
|
||
Lock;
|
||
OK:=WinClipboardSupported;
|
||
if OK then
|
||
begin
|
||
first:=true;
|
||
StorePos:=CurPos;
|
||
i:=CurPos.Y;
|
||
l:=GetTextWinClipboardSize;
|
||
if l=0 then
|
||
OK:=false
|
||
else
|
||
OK:=GetTextWinClipBoardData(p,l);
|
||
if OK then
|
||
begin
|
||
p2:=p;
|
||
p13:=strpos(p,#13);
|
||
p10:=strpos(p,#10);
|
||
while assigned(p10) do
|
||
begin
|
||
if p13+1=p10 then
|
||
p13[0]:=#0
|
||
else
|
||
p10[0]:=#0;
|
||
s:=strpas(p2);
|
||
if first then
|
||
begin
|
||
{ we need to cut the line in two
|
||
if not at end of line PM }
|
||
InsertLine;
|
||
SetCurPtr(StorePos.X,StorePos.Y);
|
||
InsertText(s);
|
||
first:=false;
|
||
end
|
||
else
|
||
begin
|
||
Inc(i);
|
||
Lines^.AtInsert(i,NewLine(s));
|
||
end;
|
||
if p13+1=p10 then
|
||
p13[0]:=#13
|
||
else
|
||
p10[0]:=#10;
|
||
p2:=@p10[1];
|
||
p13:=strpos(p2,#13);
|
||
p10:=strpos(p2,#10);
|
||
end;
|
||
if strlen(p2)>0 then
|
||
begin
|
||
s:=strpas(p2);
|
||
if not first then
|
||
SetCurPtr(0,i);
|
||
InsertText(s);
|
||
end;
|
||
SetCurPtr(StorePos.X,StorePos.Y);
|
||
{ we must free the allocated memory }
|
||
freemem(p,l);
|
||
end;
|
||
end;
|
||
ClipPasteWin:=OK;
|
||
UnLock;
|
||
end;
|
||
|
||
function TCodeEditor.ClipCopyWin: Boolean;
|
||
var OK: boolean;
|
||
p,p2 : pchar;
|
||
s : string;
|
||
i,str_begin,str_end,NumLines,PcLength : longint;
|
||
begin
|
||
NumLines:=SelEnd.Y-SelStart.Y;
|
||
if (NumLines>0) or (SelEnd.X>SelStart.X) then
|
||
Inc(NumLines);
|
||
if NumLines=0 then
|
||
exit;
|
||
Lock;
|
||
{ First calculate needed size }
|
||
{ for newlines first + 1 for terminal #0 }
|
||
PcLength:=Length(EOL)*(NumLines-1)+1;
|
||
|
||
{ overestimated but can not be that big PM }
|
||
for i:=SelStart.Y to SelEnd.Y do
|
||
PCLength:=PCLength+Length(GetLineText(i));
|
||
getmem(p,PCLength);
|
||
i:=SelStart.Y;
|
||
s:=GetLineText(i);
|
||
str_begin:=LinePosToCharIdx(i,SelStart.X+1);
|
||
if SelEnd.Y>SelStart.Y then
|
||
str_end:=255
|
||
else
|
||
str_end:=LinePosToCharIdx(i,SelEnd.X);
|
||
s:=copy(s,str_begin,str_end-str_begin+1);
|
||
strpcopy(p,s);
|
||
p2:=strend(p);
|
||
inc(i);
|
||
while i<SelEnd.Y do
|
||
begin
|
||
strpcopy(p2,EOL+GetLineText(i));
|
||
p2:=strend(p2);
|
||
Inc(i);
|
||
end;
|
||
if SelEnd.Y>SelStart.Y then
|
||
begin
|
||
s:=copy(GetLineText(i),1,LinePosToCharIdx(i,SelEnd.X));
|
||
strpcopy(p2,EOL+s);
|
||
end;
|
||
OK:=WinClipboardSupported;
|
||
if OK then
|
||
begin
|
||
OK:=SetTextWinClipBoardData(p,strlen(p));
|
||
end;
|
||
ClipCopyWin:=OK;
|
||
Freemem(p,PCLength);
|
||
UnLock;
|
||
end;
|
||
{$endif WinClipSupported}
|
||
|
||
procedure TCodeEditor.Undo;
|
||
begin
|
||
NotImplemented; Exit;
|
||
end;
|
||
|
||
procedure TCodeEditor.Redo;
|
||
begin
|
||
NotImplemented; Exit;
|
||
end;
|
||
|
||
procedure TCodeEditor.GotoLine;
|
||
var
|
||
GotoRec: TGotoLineDialogRec;
|
||
begin
|
||
with GotoRec do
|
||
begin
|
||
LineNo:='1';
|
||
Lines:=GetLineCount;
|
||
if EditorDialog(edGotoLine, @GotoRec) <> cmCancel then
|
||
begin
|
||
SetCurPtr(0,StrToInt(LineNo)-1);
|
||
TrackCursor(true);
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TCodeEditor.Find;
|
||
var
|
||
FindRec: TFindDialogRec;
|
||
DoConf: boolean;
|
||
begin
|
||
with FindRec do
|
||
begin
|
||
Find := FindStr;
|
||
if GetCurrentWord<>'' then
|
||
Find:=GetCurrentWord;
|
||
Options := (FindFlags and ffmOptions) shr ffsOptions;
|
||
Direction := (FindFlags and ffmDirection) shr ffsDirection;
|
||
Scope := (FindFlags and ffmScope) shr ffsScope;
|
||
Origin := (FindFlags and ffmOrigin) shr ffsOrigin;
|
||
DoConf:= (FindFlags and ffPromptOnReplace)<>0;
|
||
if EditorDialog(edFind, @FindRec) <> cmCancel then
|
||
begin
|
||
FindStr := Find;
|
||
FindFlags := (Options shl ffsOptions) or (Direction shl ffsDirection) or
|
||
(Scope shl ffsScope) or (Origin shl ffsOrigin);
|
||
FindFlags := FindFlags and not ffDoReplace;
|
||
if DoConf then
|
||
FindFlags := (FindFlags or ffPromptOnReplace);
|
||
SearchRunCount:=0;
|
||
DoSearchReplace;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TCodeEditor.Replace;
|
||
var
|
||
ReplaceRec: TReplaceDialogRec;
|
||
Re: word;
|
||
begin
|
||
if IsReadOnly then Exit;
|
||
with ReplaceRec do
|
||
begin
|
||
Find := FindStr;
|
||
if GetCurrentWord<>'' then
|
||
Find:=GetCurrentWord;
|
||
Replace := ReplaceStr;
|
||
Options := (FindFlags and ffmOptions) shr ffsOptions;
|
||
Direction := (FindFlags and ffmDirection) shr ffsDirection;
|
||
Scope := (FindFlags and ffmScope) shr ffsScope;
|
||
Origin := (FindFlags and ffmOrigin) shr ffsOrigin;
|
||
Re:=EditorDialog(edReplace, @ReplaceRec);
|
||
if Re <> cmCancel then
|
||
begin
|
||
FindStr := Find;
|
||
ReplaceStr := Replace;
|
||
FindFlags := (Options shl ffsOptions) or (Direction shl ffsDirection) or
|
||
(Scope shl ffsScope) or (Origin shl ffsOrigin);
|
||
FindFlags := FindFlags or ffDoReplace;
|
||
if Re = cmYes then
|
||
FindFlags := FindFlags or ffReplaceAll;
|
||
SearchRunCount:=0;
|
||
DoSearchReplace;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TCodeEditor.DoSearchReplace;
|
||
var S: string;
|
||
DX,DY,P,Y,X: sw_integer;
|
||
Count: sw_integer;
|
||
Found,CanExit: boolean;
|
||
SForward,DoReplace,DoReplaceAll: boolean;
|
||
LeftOK,RightOK: boolean;
|
||
FoundCount: sw_integer;
|
||
A,B: TPoint;
|
||
AreaStart,AreaEnd: TPoint;
|
||
CanReplace,Confirm: boolean;
|
||
Re: word;
|
||
IFindStr : string;
|
||
BT : BTable;
|
||
|
||
function ContainsText(const SubS:string;var S: string; Start: Sw_word): Sw_integer;
|
||
var
|
||
P: Sw_Integer;
|
||
begin
|
||
if Start<=0 then
|
||
P:=0
|
||
else
|
||
begin
|
||
if SForward then
|
||
begin
|
||
if Start>length(s) then
|
||
P:=0
|
||
else if FindFlags and ffCaseSensitive<>0 then
|
||
P:=BMFScan(S[Start],length(s)+1-Start,FindStr,Bt)+1
|
||
else
|
||
P:=BMFIScan(S[Start],length(s)+1-Start,IFindStr,Bt)+1;
|
||
if P>0 then
|
||
Inc(P,Start-1);
|
||
end
|
||
else
|
||
begin
|
||
if start>length(s) then
|
||
start:=length(s);
|
||
if FindFlags and ffCaseSensitive<>0 then
|
||
P:=BMBScan(S[1],Start,FindStr,Bt)+1
|
||
else
|
||
P:=BMBIScan(S[1],Start,IFindStr,Bt)+1;
|
||
end;
|
||
end;
|
||
ContainsText:=P;
|
||
end;
|
||
|
||
function InArea(X,Y: sw_integer): boolean;
|
||
begin
|
||
InArea:=((AreaStart.Y=Y) and (AreaStart.X<=X)) or
|
||
((AreaStart.Y<Y) and (Y<AreaEnd.Y)) or
|
||
((AreaEnd.Y=Y) and (X<=AreaEnd.X));
|
||
end;
|
||
var CurDY: sw_integer;
|
||
begin
|
||
Inc(SearchRunCount);
|
||
|
||
SForward:=(FindFlags and ffmDirection)=ffForward;
|
||
DoReplace:=(FindFlags and ffDoReplace)<>0;
|
||
Confirm:=(FindFlags and ffPromptOnReplace)<>0;
|
||
DoReplaceAll:=(FindFlags and ffReplaceAll)<>0;
|
||
Count:=GetLineCount; FoundCount:=0;
|
||
|
||
if SForward then
|
||
DY:=1
|
||
else
|
||
DY:=-1;
|
||
DX:=DY;
|
||
|
||
if (FindFlags and ffmScope)=ffGlobal then
|
||
begin
|
||
AreaStart.X:=0;
|
||
AreaStart.Y:=0;
|
||
AreaEnd.X:=length(GetDisplayText(Count-1));
|
||
AreaEnd.Y:=Count-1;
|
||
end
|
||
else
|
||
begin
|
||
AreaStart:=SelStart;
|
||
AreaEnd:=SelEnd;
|
||
end;
|
||
|
||
X:=CurPos.X-DX;
|
||
Y:=CurPos.Y;;
|
||
if SearchRunCount=1 then
|
||
if (FindFlags and ffmOrigin)=ffEntireScope then
|
||
if SForward then
|
||
begin
|
||
X:=AreaStart.X-1;
|
||
Y:=AreaStart.Y;
|
||
end
|
||
else
|
||
begin
|
||
X:=AreaEnd.X+1;
|
||
Y:=AreaEnd.Y;
|
||
end;
|
||
|
||
if FindFlags and ffCaseSensitive<>0 then
|
||
begin
|
||
if SForward then
|
||
BMFMakeTable(FindStr,bt)
|
||
else
|
||
BMBMakeTable(FindStr,bt);
|
||
end
|
||
else
|
||
begin
|
||
IFindStr:=Upper(FindStr);
|
||
if SForward then
|
||
BMFMakeTable(IFindStr,bt)
|
||
else
|
||
BMBMakeTable(IFindStr,bt);
|
||
end;
|
||
|
||
inc(X,DX);
|
||
CanExit:=false;
|
||
if (DoReplace=false) or ((Confirm=false) and (Owner<>nil)) then
|
||
Owner^.Lock;
|
||
if InArea(X,Y) then
|
||
repeat
|
||
CurDY:=DY;
|
||
S:=GetDisplayText(Y);
|
||
P:=ContainsText(FindStr,S,X+1);
|
||
Found:=P<>0;
|
||
if Found then
|
||
begin
|
||
A.X:=P-1;
|
||
A.Y:=Y;
|
||
B.Y:=Y;
|
||
B.X:=A.X+length(FindStr);
|
||
end;
|
||
Found:=Found and InArea(A.X,A.Y);
|
||
|
||
if Found and ((FindFlags and ffWholeWordsOnly)<>0) then
|
||
begin
|
||
LeftOK:=(A.X<=0) or (not( (S[A.X] in AlphaChars) or (S[A.X] in NumberChars) ));
|
||
RightOK:=(B.X>=length(S)) or (not( (S[B.X+1] in AlphaChars) or (S[B.X+1] in NumberChars) ));
|
||
Found:=LeftOK and RightOK;
|
||
if Found=false then
|
||
begin
|
||
CurDY:=0;
|
||
X:=B.X+1;
|
||
end;
|
||
end;
|
||
|
||
if Found then
|
||
Inc(FoundCount);
|
||
|
||
if Found then
|
||
begin
|
||
Lock;
|
||
if SForward then
|
||
SetCurPtr(B.X,B.Y)
|
||
else
|
||
SetCurPtr(A.X,A.Y);
|
||
TrackCursor(true);
|
||
SetHighlight(A,B);
|
||
UnLock;
|
||
CurDY:=0;
|
||
if (DoReplace=false) then
|
||
begin
|
||
CanExit:=true;
|
||
If SForward then
|
||
begin
|
||
X:=B.X;
|
||
Y:=B.Y;
|
||
end
|
||
else
|
||
begin
|
||
X:=A.X;
|
||
Y:=A.Y;
|
||
end;
|
||
end
|
||
else
|
||
begin
|
||
if Confirm=false then CanReplace:=true else
|
||
begin
|
||
Re:=EditorDialog(edReplacePrompt,@CurPos);
|
||
case Re of
|
||
cmYes :
|
||
CanReplace:=true;
|
||
cmNo :
|
||
CanReplace:=false;
|
||
else {cmCancel}
|
||
begin
|
||
CanReplace:=false;
|
||
CanExit:=true;
|
||
end;
|
||
end;
|
||
end;
|
||
if CanReplace then
|
||
begin
|
||
Lock;
|
||
SetSelection(A,B);
|
||
DelSelect;
|
||
InsertText(ReplaceStr);
|
||
if SForward then
|
||
begin
|
||
X:=CurPos.X;
|
||
Y:=CurPos.Y;
|
||
end
|
||
else
|
||
begin
|
||
X:=A.X;
|
||
Y:=A.Y;
|
||
end;
|
||
UnLock;
|
||
end
|
||
else
|
||
begin
|
||
If SForward then
|
||
begin
|
||
X:=B.X;
|
||
Y:=B.Y;
|
||
end
|
||
else
|
||
begin
|
||
X:=A.X;
|
||
Y:=A.Y;
|
||
end;
|
||
end;
|
||
if (DoReplaceAll=false) then
|
||
CanExit:=true;
|
||
end;
|
||
end;
|
||
|
||
if (CanExit=false) and (CurDY<>0) then
|
||
begin
|
||
inc(Y,CurDY);
|
||
if SForward then
|
||
X:=0
|
||
else
|
||
X:=254;
|
||
CanExit:=(Y>=Count) or (Y<0);
|
||
end;
|
||
if not CanExit then
|
||
CanExit:=not InArea(X,Y);
|
||
until CanExit;
|
||
if (FoundCount=0) or (DoReplace) then
|
||
SetHighlight(CurPos,CurPos);
|
||
if (DoReplace=false) or ((Confirm=false) and (Owner<>nil)) then
|
||
Owner^.UnLock;
|
||
{if (DoReplace=false) or (Confirm=false) then
|
||
UnLock;}
|
||
if (FoundCount=0) then
|
||
EditorDialog(edSearchFailed,nil);
|
||
if (FindFlags and ffmScope)=ffSelectedText then
|
||
{ restore selection PM }
|
||
begin
|
||
SetSelection(AreaStart,AreaEnd);
|
||
end;
|
||
end;
|
||
|
||
procedure TCodeEditor.SetInsertMode(InsertMode: boolean);
|
||
begin
|
||
if InsertMode then Flags:=Flags or efInsertMode
|
||
else Flags:=Flags and (not efInsertMode);
|
||
DrawCursor;
|
||
end;
|
||
|
||
procedure TCodeEditor.SetModified(AModified: boolean);
|
||
begin
|
||
if AModified<>Modified then
|
||
begin
|
||
Modified:=AModified;
|
||
ModifiedChanged;
|
||
end;
|
||
end;
|
||
|
||
{ there is a problem with ShiftDel here
|
||
because GetShitState tells to extend the
|
||
selection which gives wrong results (PM) }
|
||
|
||
function TCodeEditor.ShouldExtend: boolean;
|
||
var ShiftInEvent: boolean;
|
||
begin
|
||
ShiftInEvent:=false;
|
||
if Assigned(CurEvent) then
|
||
if CurEvent^.What=evKeyDown then
|
||
ShiftInEvent:=((CurEvent^.KeyShift and kbShift)<>0);
|
||
ShouldExtend:=ShiftInEvent and
|
||
not DontConsiderShiftState;
|
||
end;
|
||
|
||
procedure TCodeEditor.SetCurPtr(X,Y: sw_integer);
|
||
var OldPos,OldSEnd,OldSStart: TPoint;
|
||
Extended: boolean;
|
||
begin
|
||
Lock;
|
||
X:=Max(0,Min(MaxLineLength+1,X));
|
||
Y:=Max(0,Min(GetLineCount-1,Y));
|
||
OldPos:=CurPos;
|
||
OldSEnd:=SelEnd;
|
||
OldSStart:=SelStart;
|
||
CurPos.X:=X;
|
||
CurPos.Y:=Y;
|
||
TrackCursor(false);
|
||
if (NoSelect=false) and (ShouldExtend) then
|
||
begin
|
||
CheckSels;
|
||
Extended:=false;
|
||
if PointOfs(OldPos)=PointOfs(SelStart) then
|
||
begin SetSelection(CurPos,SelEnd); Extended:=true; end;
|
||
CheckSels;
|
||
if Extended=false then
|
||
if PointOfs(OldPos)=PointOfs(SelEnd) then
|
||
begin
|
||
if ValidBlock=false then
|
||
SetSelection(CurPos,CurPos);
|
||
SetSelection(SelStart,CurPos); Extended:=true;
|
||
end;
|
||
CheckSels;
|
||
if (Extended=false) then
|
||
if PointOfs(OldPos)<=PointOfs(CurPos)
|
||
then begin SetSelection(OldPos,CurPos); Extended:=true; end
|
||
else begin SetSelection(CurPos,OldPos); Extended:=true; end;
|
||
DrawView;
|
||
end else
|
||
if (Flags and efPersistentBlocks)=0 then
|
||
begin HideSelect; DrawView; end;
|
||
{ if PointOfs(SelStart)=PointOfs(SelEnd) then
|
||
SetSelection(CurPos,CurPos);}
|
||
if (Flags and (efHighlightColumn+efHighlightRow))<>0 then
|
||
DrawView;
|
||
if ((CurPos.X<>OldPos.X) or (CurPos.Y<>OldPos.Y)) and
|
||
((Highlight.A.X<>HighLight.B.X) or (Highlight.A.Y<>HighLight.B.Y)) then
|
||
HideHighlight;
|
||
if (OldPos.Y<>CurPos.Y) and (0<=OldPos.Y) and (OldPos.Y<GetLineCount) then
|
||
SetLineText(OldPos.Y,RTrim(GetLineText(OldPos.Y)));
|
||
if ((CurPos.X<>OldPos.X) or (CurPos.Y<>OldPos.Y)) and (GetErrorMessage<>'') then
|
||
SetErrorMessage('');
|
||
if ((CurPos.X<>OldPos.X) or (CurPos.Y<>OldPos.Y)) and (HighlightRow<>-1) then
|
||
SetHighlightRow(-1);
|
||
if ((CurPos.X<>OldPos.X) or (CurPos.Y<>OldPos.Y)) then
|
||
AddAction(eaMoveCursor,OldPos,CurPos,'');
|
||
if ((CurPos.X<>OldPos.X) or (CurPos.Y<>OldPos.Y)) then
|
||
UpdateIndicator;
|
||
UnLock;
|
||
end;
|
||
|
||
procedure TCodeEditor.CheckSels;
|
||
begin
|
||
if (SelStart.Y>SelEnd.Y) or
|
||
( (SelStart.Y=SelEnd.Y) and (SelStart.X>SelEnd.X) ) then
|
||
SetSelection(SelEnd,SelStart);
|
||
end;
|
||
|
||
function TCodeEditor.UpdateAttrs(FromLine: sw_integer; Attrs: byte): sw_integer;
|
||
type
|
||
TCharClass = (ccWhiteSpace,ccTab,ccAlpha,ccNumber,ccRealNumber,ccHash,ccSymbol);
|
||
var
|
||
SymbolIndex: Sw_integer;
|
||
CurrentCommentType : Byte;
|
||
LastCC: TCharClass;
|
||
InAsm,InComment,InSingleLineComment,InDirective,InString: boolean;
|
||
X,ClassStart: Sw_integer;
|
||
SymbolConcat: string;
|
||
LineText,Format: string;
|
||
|
||
function MatchSymbol(const What, S: string): boolean;
|
||
var Match: boolean;
|
||
begin
|
||
Match:=false;
|
||
if length(What)>=length(S) then
|
||
if copy(What,1+length(What)-length(S),length(S))=S then
|
||
Match:=true;
|
||
MatchSymbol:=Match;
|
||
end;
|
||
|
||
var MatchedSymbol: boolean;
|
||
MatchingSymbol: string;
|
||
type TPartialType = (pmNone,pmLeft,pmRight,pmAny);
|
||
function MatchesAnySpecSymbol(What: string; SClass: TSpecSymbolClass; PartialMatch: TPartialType;
|
||
CaseInsensitive: boolean): boolean;
|
||
var S: string;
|
||
I: Sw_integer;
|
||
Match,Found: boolean;
|
||
begin
|
||
Found:=false;
|
||
if CaseInsensitive then
|
||
What:=UpcaseStr(What);
|
||
if What<>'' then
|
||
for I:=1 to GetSpecSymbolCount(SClass) do
|
||
begin
|
||
SymbolIndex:=I;
|
||
S:=GetSpecSymbol(SClass,I-1);
|
||
if (length(What)<length(S)) or
|
||
((PartialMatch=pmNone) and (length(S)<>length(What)))
|
||
then
|
||
Match:=false
|
||
else
|
||
begin
|
||
if CaseInsensitive then
|
||
S:=UpcaseStr(S);
|
||
case PartialMatch of
|
||
pmNone : Match:=What=S;
|
||
pmRight:
|
||
Match:=copy(What,length(What)-length(S)+1,length(S))=S;
|
||
else Match:=MatchSymbol(What,S);
|
||
end;
|
||
end;
|
||
if Match then
|
||
begin
|
||
MatchingSymbol:=S; Found:=true; Break;
|
||
end;
|
||
end;
|
||
MatchedSymbol:=MatchedSymbol or Found;
|
||
MatchesAnySpecSymbol:=Found;
|
||
end;
|
||
|
||
function IsCommentPrefix: boolean;
|
||
begin
|
||
IsCommentPrefix:=MatchesAnySpecSymbol(SymbolConcat,ssCommentPrefix,pmLeft,false);
|
||
end;
|
||
|
||
function IsSingleLineCommentPrefix: boolean;
|
||
begin
|
||
IsSingleLineCommentPrefix:=MatchesAnySpecSymbol(SymbolConcat,ssCommentSingleLinePrefix,pmLeft,false);
|
||
end;
|
||
|
||
function IsCommentSuffix: boolean;
|
||
begin
|
||
IsCommentSuffix:=(MatchesAnySpecSymbol(SymbolConcat,ssCommentSuffix,pmRight,false))
|
||
and (CurrentCommentType=SymbolIndex);
|
||
end;
|
||
|
||
function IsStringPrefix: boolean;
|
||
begin
|
||
IsStringPrefix:=MatchesAnySpecSymbol(SymbolConcat,ssStringPrefix,pmLeft,false);
|
||
end;
|
||
|
||
function IsStringSuffix: boolean;
|
||
begin
|
||
IsStringSuffix:=MatchesAnySpecSymbol(SymbolConcat,ssStringSuffix,pmRight,false);
|
||
end;
|
||
|
||
function IsDirectivePrefix: boolean;
|
||
begin
|
||
IsDirectivePrefix:=MatchesAnySpecSymbol(SymbolConcat,ssDirectivePrefix,pmLeft,false);
|
||
end;
|
||
|
||
function IsDirectiveSuffix: boolean;
|
||
begin
|
||
IsDirectiveSuffix:=MatchesAnySpecSymbol(SymbolConcat,ssDirectiveSuffix,pmRight,false);
|
||
end;
|
||
|
||
function IsAsmPrefix(const WordS: string): boolean;
|
||
begin
|
||
IsAsmPrefix:=MatchesAnySpecSymbol(WordS,ssAsmPrefix,pmNone,true);
|
||
end;
|
||
|
||
function IsAsmSuffix(const WordS: string): boolean;
|
||
begin
|
||
IsAsmSuffix:=MatchesAnySpecSymbol(WordS,ssAsmSuffix,pmNone,true);
|
||
end;
|
||
|
||
function GetCharClass(C: char): TCharClass;
|
||
var CC: TCharClass;
|
||
begin
|
||
if C in WhiteSpaceChars then CC:=ccWhiteSpace else
|
||
if C in TabChars then CC:=ccTab else
|
||
if C in HashChars then CC:=ccHash else
|
||
if C in AlphaChars then CC:=ccAlpha else
|
||
if C in NumberChars then CC:=ccNumber else
|
||
if (LastCC=ccNumber) and (C in RealNumberChars) then CC:=ccRealNumber else
|
||
CC:=ccSymbol;
|
||
GetCharClass:=CC;
|
||
end;
|
||
|
||
procedure FormatWord(SClass: TCharClass; StartX:Sw_integer;EndX: Sw_integer);
|
||
var
|
||
C: byte;
|
||
WordS: string;
|
||
begin
|
||
C:=0;
|
||
WordS:=copy(LineText,StartX,EndX-StartX+1);
|
||
if IsAsmSuffix(WordS) and (InAsm=true) and (InComment=false) and
|
||
(InString=false) and (InDirective=false) then InAsm:=false;
|
||
if InDirective then C:=coDirectiveColor else
|
||
if InComment then C:=coCommentColor else
|
||
if InString then C:=coStringColor else
|
||
if InAsm then C:=coAssemblerColor else
|
||
case SClass of
|
||
ccWhiteSpace : C:=coWhiteSpaceColor;
|
||
ccTab : C:=coTabColor;
|
||
ccNumber :
|
||
if copy(WordS,1,1)='$' then
|
||
C:=coHexNumberColor
|
||
else
|
||
C:=coNumberColor;
|
||
ccHash :
|
||
C:=coStringColor;
|
||
ccSymbol :
|
||
C:=coSymbolColor;
|
||
ccAlpha :
|
||
begin
|
||
if IsReservedWord(WordS) then
|
||
C:=coReservedWordColor
|
||
else
|
||
C:=coIdentifierColor;
|
||
end;
|
||
end;
|
||
if EndX+1>=StartX then
|
||
FillChar(Format[StartX],EndX+1-StartX,C);
|
||
if IsAsmPrefix(WordS) and
|
||
(InAsm=false) and (InComment=false) and (InDirective=false) then
|
||
InAsm:=true;
|
||
end;
|
||
|
||
procedure ProcessChar(C: char);
|
||
var CC: TCharClass;
|
||
EX: Sw_integer;
|
||
begin
|
||
CC:=GetCharClass(C);
|
||
if ( (CC<>LastCC) and
|
||
( (CC<>ccAlpha) or (LastCC<>ccNumber) ) and
|
||
( (CC<>ccNumber) or (LastCC<>ccAlpha) ) and
|
||
( (CC<>ccNumber) or (LastCC<>ccHash) ) and
|
||
( (CC<>ccRealNumber) or (LastCC<>ccNumber) )
|
||
) or
|
||
(X>length(LineText)) or (CC=ccSymbol) then
|
||
begin
|
||
MatchedSymbol:=false;
|
||
EX:=X-1;
|
||
if (CC=ccSymbol) then
|
||
begin
|
||
if length(SymbolConcat)>=High(SymbolConcat) then
|
||
Delete(SymbolConcat,1,1);
|
||
SymbolConcat:=SymbolConcat+C;
|
||
end;
|
||
case CC of
|
||
ccSymbol :
|
||
if IsCommentSuffix and (InComment) then
|
||
Inc(EX) else
|
||
if IsStringSuffix and (InString) then
|
||
Inc(EX) else
|
||
if IsDirectiveSuffix and (InDirective) then
|
||
Inc(EX);
|
||
end;
|
||
if (C='$') and (MatchedSymbol=false) and (IsDirectivePrefix=false) then
|
||
CC:=ccNumber;
|
||
if CC<>ccSymbol then SymbolConcat:='';
|
||
FormatWord(LastCC,ClassStart,EX);
|
||
ClassStart:=EX+1;
|
||
case CC of
|
||
ccAlpha : ;
|
||
ccNumber :
|
||
if (LastCC<>ccAlpha) then;
|
||
ccSymbol :
|
||
if IsDirectivePrefix and {(InComment=false) and }(InDirective=false) then
|
||
begin InDirective:=true; InComment:=false; Dec(ClassStart,length(MatchingSymbol)-1); end else
|
||
if IsDirectiveSuffix and (InComment=false) and (InDirective=true) then
|
||
InDirective:=false else
|
||
if IsCommentPrefix and (InComment=false) and (InString=false) then
|
||
begin
|
||
InComment:=true;
|
||
CurrentCommentType:=SymbolIndex;
|
||
InSingleLineComment:=IsSingleLineCommentPrefix;
|
||
{InString:=false; }
|
||
Dec(ClassStart,length(MatchingSymbol)-1);
|
||
end
|
||
else
|
||
if IsCommentSuffix and (InComment) then
|
||
begin InComment:=false; InString:=false; end else
|
||
if IsStringPrefix and (InComment=false) and (InString=false) then
|
||
begin InString:=true; Dec(ClassStart,length(MatchingSymbol)-1); end else
|
||
if IsStringSuffix and (InComment=false) and (InString=true) then
|
||
InString:=false;
|
||
end;
|
||
if MatchedSymbol and (InComment=false) then
|
||
SymbolConcat:='';
|
||
LastCC:=CC;
|
||
end;
|
||
end;
|
||
|
||
var CurLine: Sw_integer;
|
||
Line,NextLine,PrevLine,OldLine: PLine;
|
||
begin
|
||
if ((Flags and efSyntaxHighlight)=0) or (FromLine>=GetLineCount) then
|
||
begin
|
||
SetLineFormat(FromLine,'');
|
||
UpdateAttrs:=GetLineCount-1;
|
||
Exit;
|
||
end;
|
||
CurLine:=FromLine;
|
||
if CurLine>0 then PrevLine:=Lines^.At(CurLine-1) else PrevLine:=nil;
|
||
repeat
|
||
Line:=Lines^.At(CurLine);
|
||
InSingleLineComment:=false;
|
||
if PrevLine<>nil then
|
||
begin
|
||
InAsm:=PrevLine^.EndsWithAsm;
|
||
InComment:=PrevLine^.EndsWithComment and not PrevLine^.EndsInSingleLineComment;
|
||
CurrentCommentType:=PrevLine^.EndCommentType;
|
||
InDirective:=PrevLine^.EndsWithDirective;
|
||
end
|
||
else
|
||
begin
|
||
InAsm:=false;
|
||
InComment:=false;
|
||
CurrentCommentType:=0;
|
||
InDirective:=false;
|
||
end;
|
||
OldLine:=Line;
|
||
Line^.BeginsWithAsm:=InAsm;
|
||
Line^.BeginsWithComment:=InComment;
|
||
Line^.BeginsWithDirective:=InDirective;
|
||
LineText:=GetLineText(CurLine);
|
||
Format:=CharStr(chr(coTextColor),length(LineText));
|
||
LastCC:=ccWhiteSpace;
|
||
ClassStart:=1;
|
||
SymbolConcat:='';
|
||
InString:=false;
|
||
if LineText<>'' then
|
||
begin
|
||
for X:=1 to length(LineText) do
|
||
ProcessChar(LineText[X]);
|
||
Inc(X);
|
||
ProcessChar(' ');
|
||
end;
|
||
SetLineFormat(CurLine,Format);
|
||
Line^.EndsWithAsm:=InAsm;
|
||
Line^.EndsWithComment:=InComment;
|
||
Line^.EndsInSingleLineComment:=InSingleLineComment;
|
||
Line^.EndCommentType:=CurrentCommentType;
|
||
Line^.EndsWithDirective:=InDirective;
|
||
Inc(CurLine);
|
||
if CurLine>=GetLineCount then
|
||
Break;
|
||
NextLine:=Lines^.At(CurLine);
|
||
if (Attrs and attrForceFull)=0 then
|
||
if (InAsm=false) and (NextLine^.BeginsWithAsm=false) and
|
||
(InComment=false) and (NextLine^.BeginsWithComment=false) and
|
||
(InDirective=false) and (NextLine^.BeginsWithDirective=false) and
|
||
(OldLine^.EndsWithComment=Line^.EndsWithComment) and
|
||
(OldLine^.EndsWithAsm=Line^.EndsWithAsm) and
|
||
(OldLine^.EndsWithDirective=Line^.EndsWithDirective) and
|
||
(NextLine^.BeginsWithAsm=Line^.EndsWithAsm) and
|
||
(NextLine^.BeginsWithComment=Line^.EndsWithComment) and
|
||
(NextLine^.BeginsWithDirective=Line^.EndsWithDirective) and
|
||
(NextLine^.Format<>nil) then
|
||
Break;
|
||
PrevLine:=Line;
|
||
until false;
|
||
UpdateAttrs:=CurLine;
|
||
end;
|
||
|
||
|
||
function TCodeEditor.UpdateAttrsRange(FromLine, ToLine: sw_integer; Attrs: byte): sw_integer;
|
||
var Line: Sw_integer;
|
||
begin
|
||
Line:=FromLine;
|
||
repeat
|
||
Line:=UpdateAttrs(Line,Attrs);
|
||
until (Line>GetLineCount) or (Line>ToLine);
|
||
UpdateAttrsRange:=Line;
|
||
end;
|
||
|
||
|
||
procedure TCodeEditor.DrawLines(FirstLine: sw_integer);
|
||
begin
|
||
if FirstLine>=(Delta.Y+Size.Y) then Exit; { falls outside of the screen }
|
||
DrawView;
|
||
end;
|
||
|
||
function TCodeEditor.InsertText(const S: string): Boolean;
|
||
var I: sw_integer;
|
||
OldPos: TPoint;
|
||
begin
|
||
Lock;
|
||
OldPos:=CurPos;
|
||
for I:=1 to length(S) do
|
||
AddChar(S[I]);
|
||
AddAction(eaInsertText,OldPos,CurPos,S);
|
||
InsertText:=true;
|
||
UnLock;
|
||
end;
|
||
|
||
function TCodeEditor.InsertFrom(Editor: PCodeEditor): Boolean;
|
||
var OK: boolean;
|
||
LineDelta,LineCount: Sw_integer;
|
||
StartPos,DestPos: TPoint;
|
||
LineStartX,LineEndX: Sw_integer;
|
||
S,OrigS,AfterS: string;
|
||
OneLineOnly,VerticalBlock: boolean;
|
||
SEnd: TPoint;
|
||
begin
|
||
Lock;
|
||
OK:=(Editor^.SelStart.X<>Editor^.SelEnd.X) or (Editor^.SelStart.Y<>Editor^.SelEnd.Y);
|
||
if OK then
|
||
begin
|
||
StartPos:=CurPos; DestPos:=CurPos;
|
||
VerticalBlock:=(Editor^.Flags and efVerticalBlocks)<>0;
|
||
LineDelta:=0; LineCount:=(Editor^.SelEnd.Y-Editor^.SelStart.Y)+1;
|
||
OK:=GetLineCount<MaxLineCount;
|
||
{ BUG:: this is wrong if we do not insert at begin of line !!! PM }
|
||
while OK and (LineDelta<LineCount) do
|
||
begin
|
||
if (LineDelta<LineCount-1) and (VerticalBlock=false) then
|
||
if (LineDelta<>0) or (Editor^.SelEnd.X=0) then
|
||
begin
|
||
Lines^.AtInsert(DestPos.Y,NewLine(''));
|
||
LimitsChanged;
|
||
end;
|
||
|
||
if (LineDelta=0) or VerticalBlock then
|
||
LineStartX:=Editor^.SelStart.X
|
||
else
|
||
LineStartX:=0;
|
||
|
||
if (LineDelta=LineCount-1) or VerticalBlock then
|
||
LineEndX:=Editor^.SelEnd.X-1
|
||
else
|
||
LineEndX:=255;
|
||
|
||
if LineEndX<LineStartX then
|
||
S:=''
|
||
else
|
||
S:=RExpand(copy(Editor^.GetLineText(Editor^.SelStart.Y+LineDelta),LineStartX+1,LineEndX-LineStartX+1),
|
||
Min(LineEndX-LineStartX+1,255));
|
||
if VerticalBlock=false then
|
||
begin
|
||
If LineDelta=0 then
|
||
begin
|
||
OrigS:=GetDisplayText(DestPos.Y);
|
||
AfterS:=Copy(OrigS,DestPos.X+1,255);
|
||
end
|
||
else
|
||
OrigS:='';
|
||
if LineDelta=LineCount-1 then
|
||
SetLineText(DestPos.Y,RExpand(copy(OrigS,1,DestPos.X),DestPos.X)+S+AfterS)
|
||
else
|
||
SetLineText(DestPos.Y,RExpand(copy(OrigS,1,DestPos.X),DestPos.X)+S);
|
||
if LineDelta=LineCount-1 then
|
||
begin
|
||
SEnd.Y:=DestPos.Y;
|
||
SEnd.X:=DestPos.X+length(S);
|
||
end
|
||
else
|
||
begin
|
||
Inc(DestPos.Y);
|
||
DestPos.X:=0;
|
||
end;
|
||
end
|
||
else { if VerticalBlock=false then .. else }
|
||
begin
|
||
{ this is not yet implemented !! PM }
|
||
S:=RExpand(S,LineEndX-LineStartX+1);
|
||
end;
|
||
Inc(LineDelta);
|
||
OK:=GetLineCount<MaxLineCount;
|
||
end;
|
||
if OK=false then EditorDialog(edTooManyLines,nil);
|
||
SetCurPtr(CurPos.X,CurPos.Y);
|
||
UpdateAttrs(StartPos.Y,attrAll);
|
||
SetModified(true);
|
||
LimitsChanged;
|
||
SetSelection(CurPos,SEnd);
|
||
if IsClipboard then
|
||
begin
|
||
Inc(DestPos.X,length(S));
|
||
SetCurPtr(DestPos.X,DestPos.Y);
|
||
end;
|
||
DrawView;
|
||
end;
|
||
UnLock;
|
||
InsertFrom:=OK;
|
||
end;
|
||
|
||
function TCodeEditor.IsClipboard: Boolean;
|
||
begin
|
||
IsClipboard:=(Clipboard=@Self);
|
||
end;
|
||
|
||
procedure TCodeEditor.HideHighlight;
|
||
begin
|
||
SetHighlight(CurPos,CurPos);
|
||
end;
|
||
|
||
procedure TCodeEditor.AddAction(AAction: byte; AStartPos, AEndPos: TPoint; AText: string);
|
||
begin
|
||
if (Actions=nil) or (not StoreUndo) then Exit;
|
||
Actions^.Insert(NewEditorAction(AAction,AStartPos,AEndPos,AText));
|
||
end;
|
||
|
||
function TCodeEditor.ValidBlock: boolean;
|
||
begin
|
||
ValidBlock:=(SelStart.X<>SelEnd.X) or (SelStart.Y<>SelEnd.Y);
|
||
end;
|
||
|
||
procedure TCodeEditor.SetSelection(A, B: TPoint);
|
||
var WV: boolean;
|
||
OS,OE: TPoint;
|
||
begin
|
||
WV:=ValidBlock;
|
||
OS:=SelStart; OE:=SelEnd;
|
||
SelStart:=A; SelEnd:=B;
|
||
if (WV=false) and (ValidBlock=false) then { do nothing } else
|
||
if (OS.X<>SelStart.X) or (OS.Y<>SelStart.Y) or
|
||
(OE.X<>SelEnd.X) or (OE.Y<>SelEnd.Y) then
|
||
SelectionChanged;
|
||
end;
|
||
|
||
procedure TCodeEditor.SetHighlight(A, B: TPoint);
|
||
begin
|
||
Highlight.A:=A; Highlight.B:=B;
|
||
HighlightChanged;
|
||
end;
|
||
|
||
procedure TCodeEditor.SetHighlightRow(Row: sw_integer);
|
||
begin
|
||
HighlightRow:=Row;
|
||
DrawView;
|
||
end;
|
||
|
||
procedure TCodeEditor.SetDebuggerRow(Row: sw_integer);
|
||
begin
|
||
DebuggerRow:=Row;
|
||
DrawView;
|
||
end;
|
||
|
||
procedure TCodeEditor.SelectAll(Enable: boolean);
|
||
var A,B: TPoint;
|
||
begin
|
||
if (Enable=false) or (GetLineCount=0) then
|
||
begin A:=CurPos; B:=CurPos end
|
||
else
|
||
begin
|
||
A.X:=0; A.Y:=0;
|
||
{ B.Y:=GetLineCount-1;
|
||
B.X:=length(GetLineText(B.Y));}
|
||
B.Y:=GetLineCount; B.X:=0;
|
||
end;
|
||
SetSelection(A,B);
|
||
DrawView;
|
||
end;
|
||
|
||
procedure TCodeEditor.SelectionChanged;
|
||
var Enable,CanPaste: boolean;
|
||
begin
|
||
if GetLineCount=0 then
|
||
begin
|
||
SelStart.X:=0; SelStart.Y:=0; SelEnd:=SelStart;
|
||
end
|
||
else
|
||
if SelEnd.Y>GetLineCount-1 then
|
||
if (SelEnd.Y<>GetLineCount) or (SelEnd.X<>0) then
|
||
begin
|
||
SelEnd.Y:=GetLineCount-1;
|
||
SelEnd.X:=length(GetDisplayText(SelEnd.Y));
|
||
end;
|
||
|
||
Enable:=((SelStart.X<>SelEnd.X) or (SelStart.Y<>SelEnd.Y)) and (Clipboard<>nil);
|
||
SetCmdState(ToClipCmds,Enable and (Clipboard<>@Self));
|
||
SetCmdState(NulClipCmds,Enable);
|
||
CanPaste:=(Clipboard<>nil) and ((Clipboard^.SelStart.X<>Clipboard^.SelEnd.X) or
|
||
(Clipboard^.SelStart.Y<>Clipboard^.SelEnd.Y));
|
||
SetCmdState(FromClipCmds,CanPaste and (Clipboard<>@Self));
|
||
Message(Application,evBroadcast,cmCommandSetChanged,nil);
|
||
DrawView;
|
||
end;
|
||
|
||
procedure TCodeEditor.HighlightChanged;
|
||
begin
|
||
DrawView;
|
||
end;
|
||
|
||
procedure TCodeEditor.ModifiedChanged;
|
||
begin
|
||
UpdateIndicator;
|
||
end;
|
||
|
||
procedure TCodeEditor.SetState(AState: Word; Enable: Boolean);
|
||
begin
|
||
inherited SetState(AState,Enable);
|
||
if (AState and (sfActive+sfSelected+sfFocused))<>0 then
|
||
SelectionChanged;
|
||
end;
|
||
|
||
function TCodeEditor.GetPalette: PPalette;
|
||
const P: string[length(CEditor)] = CEditor;
|
||
begin
|
||
GetPalette:=@P;
|
||
end;
|
||
|
||
constructor TCodeEditor.Load(var S: TStream);
|
||
var TS: PSubStream;
|
||
TSize: longint;
|
||
begin
|
||
inherited Load(S);
|
||
|
||
New(Actions, Init(500,1000));
|
||
New(Lines, Init(500,1000));
|
||
{ we have always need at least 1 line }
|
||
Lines^.Insert(NewLine(''));
|
||
|
||
GetPeerViewPtr(S,Indicator);
|
||
S.Read(Flags,SizeOf(Flags));
|
||
S.Read(TabSize,SizeOf(TabSize));
|
||
|
||
if (Flags and efStoreContent)<>0 then
|
||
begin
|
||
S.Read(TSize,SizeOf(TSize));
|
||
New(TS, Init(@S,S.GetPos,TSize));
|
||
LoadFromStream(TS);
|
||
Dispose(TS, Done);
|
||
end;
|
||
|
||
S.Read(SelStart,SizeOf(SelStart));
|
||
S.Read(SelEnd,SizeOf(SelEnd));
|
||
S.Read(Highlight,SizeOf(Highlight));
|
||
S.Read(CurPos,SizeOf(CurPos));
|
||
S.Read(StoreUndo,SizeOf(StoreUndo));
|
||
S.Read(IsReadOnly,SizeOf(IsReadOnly));
|
||
S.Read(NoSelect,SizeOf(NoSelect));
|
||
S.Read(HighlightRow,SizeOf(HighlightRow));
|
||
SetDebuggerRow(-1);
|
||
|
||
LimitsChanged;
|
||
SelectionChanged; HighlightChanged;
|
||
UpdateIndicator;
|
||
end;
|
||
|
||
procedure TCodeEditor.Store(var S: TStream);
|
||
var NS: TNulStream;
|
||
TSize: longint;
|
||
begin
|
||
inherited Store(S);
|
||
|
||
PutPeerViewPtr(S,Indicator);
|
||
S.Write(Flags,SizeOf(Flags));
|
||
S.Write(TabSize,SizeOf(TabSize));
|
||
|
||
if (Flags and efStoreContent)<>0 then
|
||
begin
|
||
NS.Init;
|
||
SaveToStream(@NS);
|
||
TSize:=NS.GetSize;
|
||
NS.Done;
|
||
|
||
S.Write(TSize,SizeOf(TSize));
|
||
SaveToStream(@S);
|
||
end;
|
||
|
||
S.Write(SelStart,SizeOf(SelStart));
|
||
S.Write(SelEnd,SizeOf(SelEnd));
|
||
S.Write(Highlight,SizeOf(Highlight));
|
||
S.Write(CurPos,SizeOf(CurPos));
|
||
S.Write(StoreUndo,SizeOf(StoreUndo));
|
||
S.Write(IsReadOnly,SizeOf(IsReadOnly));
|
||
S.Write(NoSelect,SizeOf(NoSelect));
|
||
S.Write(HighlightRow,SizeOf(HighlightRow));
|
||
end;
|
||
|
||
function TCodeEditor.LoadFromStream(Stream: PStream): boolean;
|
||
var S: string;
|
||
OK: boolean;
|
||
begin
|
||
DeleteAllLines;
|
||
OK:=(Stream^.Status=stOK);
|
||
if eofstream(Stream) then
|
||
AddLine('')
|
||
else
|
||
while OK and (eofstream(Stream)=false) and (GetLineCount<MaxLineCount) do
|
||
begin
|
||
readlnfromstream(Stream,S);
|
||
OK:=OK and (Stream^.Status=stOK);
|
||
if OK then AddLine(S);
|
||
end;
|
||
LimitsChanged;
|
||
if (Flags and efSyntaxHighlight)<>0 then
|
||
UpdateAttrsRange(0,GetLineCount-1,attrAll+attrForceFull);
|
||
TextStart;
|
||
LoadFromStream:=OK;
|
||
end;
|
||
|
||
function TCodeEditor.SaveToStream(Stream: PStream): boolean;
|
||
var A,B: TPoint;
|
||
begin
|
||
A.Y:=0; A.X:=0;
|
||
B.Y:=GetLineCount-1;
|
||
if GetLineCount>0 then
|
||
B.X:=length(GetDisplayText(B.Y))
|
||
else
|
||
B.X:=0;
|
||
SaveToStream:=SaveAreaToStream(Stream,A,B);
|
||
end;
|
||
|
||
function TCodeEditor.SaveAreaToStream(Stream: PStream; StartP,EndP: TPoint): boolean;
|
||
var S: string;
|
||
OK: boolean;
|
||
Line: Sw_integer;
|
||
P: PLine;
|
||
begin
|
||
if EndP.X=0 then
|
||
begin
|
||
if EndP.Y>0 then
|
||
begin
|
||
Dec(EndP.Y);
|
||
EndP.X:=length(GetDisplayText(EndP.Y));
|
||
end
|
||
else
|
||
EndP.X:=0;
|
||
end
|
||
else
|
||
Dec(EndP.X);
|
||
OK:=(Stream^.Status=stOK); Line:=StartP.Y;
|
||
while OK and (Line<=EndP.Y) and (Line<GetLineCount) do
|
||
begin
|
||
P:=Lines^.At(Line);
|
||
if P^.Text=nil then S:='' else
|
||
begin
|
||
S:=P^.Text^;
|
||
if Line=EndP.Y then S:=copy(S,1,EndP.Y+1);
|
||
if Line=StartP.Y then S:=copy(S,StartP.Y+1,255);
|
||
end;
|
||
{ Remove all traling spaces PM }
|
||
if (Flags and efKeepTrailingSpaces)=0 then
|
||
While (Length(S)>0) and (S[Length(S)]=' ') do
|
||
Dec(S[0]);
|
||
if (Flags and efUseTabCharacters)<>0 then
|
||
S:=CompressUsingTabs(S,TabSize);
|
||
Stream^.Write(S[1],length(S));
|
||
Stream^.Write(EOL[1],length(EOL));
|
||
Inc(Line);
|
||
OK:=OK and (Stream^.Status=stOK);
|
||
end;
|
||
SaveAreaToStream:=OK;
|
||
end;
|
||
|
||
destructor TCodeEditor.Done;
|
||
begin
|
||
inherited Done;
|
||
if assigned(Lines) then
|
||
Dispose(Lines, Done);
|
||
If assigned(Actions) then
|
||
Dispose(Actions, Done);
|
||
end;
|
||
|
||
procedure TEditorActionCollection.FreeItem(Item: Pointer);
|
||
begin
|
||
if assigned(Item) then
|
||
freemem(Item,Sizeof(TEditorAction));
|
||
end;
|
||
|
||
constructor TFileEditor.Init(var Bounds: TRect; AHScrollBar, AVScrollBar:
|
||
PScrollBar; AIndicator: PIndicator;const AFileName: string);
|
||
begin
|
||
inherited Init(Bounds,AHScrollBAr,AVScrollBAr,AIndicator,0);
|
||
FileName:=AFileName;
|
||
UpdateIndicator;
|
||
Message(@Self,evBroadcast,cmFileNameChanged,@Self);
|
||
end;
|
||
|
||
(*function TFileEditor.LoadFile: boolean;
|
||
var S: string;
|
||
OK: boolean;
|
||
f: text;
|
||
FM,Line: Sw_integer;
|
||
Buf : Pointer;
|
||
begin
|
||
DeleteAllLines;
|
||
GetMem(Buf,EditorTextBufSize);
|
||
{$I-}
|
||
EatIO;
|
||
FM:=FileMode; FileMode:=0;
|
||
Assign(f,FileName);
|
||
SetTextBuf(f,Buf^,EditorTextBufSize);
|
||
Reset(f);
|
||
OK:=(IOResult=0);
|
||
if Eof(f) then
|
||
AddLine('')
|
||
else
|
||
while OK and (Eof(f)=false) and (GetLineCount<MaxLineCount) do
|
||
begin
|
||
readln(f,S);
|
||
OK:=OK and (IOResult=0);
|
||
if OK AddLine(S);
|
||
end;
|
||
FileMode:=FM;
|
||
Close(F);
|
||
EatIO;
|
||
{$I+}
|
||
LimitsChanged;
|
||
if (Flags and efSyntaxHighlight)<>0 then
|
||
UpdateAttrsRange(0,GetLineCount-1,attrAll+attrForceFull);
|
||
TextStart;
|
||
LoadFile:=OK;
|
||
FreeMem(Buf,EditorTextBufSize);
|
||
end;*)
|
||
|
||
function TFileEditor.LoadFile: boolean;
|
||
var S: PBufStream;
|
||
OK: boolean;
|
||
begin
|
||
New(S, Init(FileName,stOpenRead,EditorTextBufSize));
|
||
OK:=Assigned(S);
|
||
if OK then OK:=LoadFromStream(S);
|
||
if Assigned(S) then Dispose(S, Done);
|
||
|
||
LoadFile:=OK;
|
||
end;
|
||
|
||
function TFileEditor.SaveFile: boolean;
|
||
var OK: boolean;
|
||
BAKName: string;
|
||
S: PBufStream;
|
||
f: text;
|
||
begin
|
||
{$I-}
|
||
if (Flags and efBackupFiles)<>0 then
|
||
begin
|
||
BAKName:=DirAndNameOf(FileName)+'.bak';
|
||
Assign(f,BAKName);
|
||
Erase(f);
|
||
EatIO;
|
||
Assign(f,FileName);
|
||
Rename(F,BAKName);
|
||
EatIO;
|
||
end;
|
||
{$I+}
|
||
New(S, Init(FileName,stCreate,EditorTextBufSize));
|
||
OK:=Assigned(S);
|
||
if OK then OK:=SaveToStream(S);
|
||
if Assigned(S) then Dispose(S, Done);
|
||
if OK then SetModified(false);
|
||
SaveFile:=OK;
|
||
end;
|
||
|
||
function TFileEditor.ShouldSave: boolean;
|
||
begin
|
||
ShouldSave:=Modified or (FileName='');
|
||
end;
|
||
|
||
function TFileEditor.Save: Boolean;
|
||
begin
|
||
if ShouldSave=false then begin Save:=true; Exit; end;
|
||
if FileName = '' then Save := SaveAs else Save := SaveFile;
|
||
end;
|
||
|
||
function TFileEditor.SaveAs: Boolean;
|
||
begin
|
||
SaveAs := False;
|
||
if EditorDialog(edSaveAs, @FileName) <> cmCancel then
|
||
begin
|
||
FileName := FExpand(FileName);
|
||
Message(Owner, evBroadcast, cmUpdateTitle, @Self);
|
||
SaveAs := SaveFile;
|
||
if IsClipboard then FileName := '';
|
||
Message(Application,evBroadcast,cmFileNameChanged,@Self);
|
||
end;
|
||
end;
|
||
|
||
function TFileEditor.SaveAsk: boolean;
|
||
var OK: boolean;
|
||
D: Sw_integer;
|
||
begin
|
||
OK:=Modified=false;
|
||
if OK=false then
|
||
begin
|
||
if FileName = '' then D := edSaveUntitled else D := edSaveModify;
|
||
case EditorDialog(D, @FileName) of
|
||
cmYes : OK := Save;
|
||
cmNo : begin Modified := False; OK:=true; end;
|
||
cmCancel : begin
|
||
OK := False;
|
||
Message(Application,evBroadcast,cmSaveCancelled,@Self);
|
||
end;
|
||
end;
|
||
end;
|
||
SaveAsk:=OK;
|
||
end;
|
||
|
||
procedure TFileEditor.HandleEvent(var Event: TEvent);
|
||
var SH,B: boolean;
|
||
begin
|
||
case Event.What of
|
||
evBroadcast :
|
||
case Event.Command of
|
||
cmFileNameChanged :
|
||
if (Event.InfoPtr=nil) or (Event.InfoPtr=@Self) then
|
||
begin
|
||
B:=(Flags and efSyntaxHighlight)<>0;
|
||
SH:=UseSyntaxHighlight(@Self);
|
||
if SH<>B then
|
||
if SH then
|
||
SetFlags(Flags or efSyntaxHighlight)
|
||
else
|
||
SetFlags(Flags and not efSyntaxHighlight);
|
||
if UseTabsPattern(@Self) then
|
||
SetFlags(Flags or efUseTabCharacters);
|
||
end;
|
||
end;
|
||
end;
|
||
inherited HandleEvent(Event);
|
||
end;
|
||
|
||
function TFileEditor.Valid(Command: Word): Boolean;
|
||
var OK: boolean;
|
||
begin
|
||
OK:=inherited Valid(Command);
|
||
if OK and ((Command=cmClose) or (Command=cmQuit)) then
|
||
if IsClipboard=false then
|
||
OK:=SaveAsk;
|
||
Valid:=OK;
|
||
end;
|
||
|
||
constructor TFileEditor.Load(var S: TStream);
|
||
var P: PString;
|
||
SSP,SEP,CP,DP: TPoint;
|
||
HR: TRect;
|
||
begin
|
||
inherited Load(S);
|
||
P:=S.ReadStr;
|
||
FileName:=GetStr(P);
|
||
if P<>nil then DisposeStr(P);
|
||
|
||
UpdateIndicator;
|
||
{ Message(@Self,evBroadcast,cmFileNameChanged,@Self);}
|
||
|
||
SSP:=SelStart; SEP:=SelEnd;
|
||
CP:=CurPos;
|
||
HR:=Highlight;
|
||
DP:=Delta;
|
||
|
||
if FileName<>'' then
|
||
LoadFile;
|
||
|
||
SetHighlight(HR.A,HR.B);
|
||
SetSelection(SSP,SEP);
|
||
SetCurPtr(CP.X,CP.Y);
|
||
ScrollTo(DP.X,DP.Y);
|
||
SetModified(false);
|
||
|
||
LimitsChanged;
|
||
end;
|
||
|
||
procedure TFileEditor.Store(var S: TStream);
|
||
begin
|
||
inherited Store(S);
|
||
S.WriteStr(@FileName);
|
||
end;
|
||
|
||
function CreateFindDialog: PDialog;
|
||
var R,R1,R2: TRect;
|
||
D: PDialog;
|
||
IL1: PInputLine;
|
||
Control : PView;
|
||
CB1: PCheckBoxes;
|
||
RB1,RB2,RB3: PRadioButtons;
|
||
begin
|
||
R.Assign(0,0,56,15);
|
||
New(D, Init(R, 'Find'));
|
||
with D^ do
|
||
begin
|
||
Options:=Options or ofCentered;
|
||
GetExtent(R); R.Grow(-3,-2);
|
||
R1.Copy(R); R1.B.X:=17; R1.B.Y:=R1.A.Y+1;
|
||
R2.Copy(R); R2.B.X:=R2.B.X-3;R2.A.X:=17; R2.B.Y:=R2.A.Y+1;
|
||
New(IL1, Init(R2, FindStrSize));
|
||
IL1^.Data^:=FindStr;
|
||
Insert(IL1);
|
||
Insert(New(PLabel, Init(R1, '~T~ext to find', IL1)));
|
||
R1.Assign(R2.B.X, R2.A.Y, R2.B.X+3, R2.B.Y);
|
||
Control := New(PHistory, Init(R1, IL1, TextFindId));
|
||
Insert(Control);
|
||
|
||
R1.Copy(R); Inc(R1.A.Y,2); R1.B.Y:=R1.A.Y+1; R1.B.X:=R1.A.X+(R1.B.X-R1.A.X) div 2-1;
|
||
R2.Copy(R1); R2.Move(0,1); R2.B.Y:=R2.A.Y+2;
|
||
New(CB1, Init(R2,
|
||
NewSItem('~C~ase sensitive',
|
||
NewSItem('~W~hole words only',
|
||
nil))));
|
||
Insert(CB1);
|
||
Insert(New(PLabel, Init(R1, 'Options', CB1)));
|
||
|
||
R1.Copy(R); Inc(R1.A.Y,2); R1.B.Y:=R1.A.Y+1; R1.A.X:=R1.B.X-(R1.B.X-R1.A.X) div 2+1;
|
||
R2.Copy(R1); R2.Move(0,1); R2.B.Y:=R2.A.Y+2;
|
||
New(RB1, Init(R2,
|
||
NewSItem('Forwar~d~',
|
||
NewSItem('~B~ackward',
|
||
nil))));
|
||
Insert(RB1);
|
||
Insert(New(PLabel, Init(R1, 'Direction', RB1)));
|
||
|
||
R1.Copy(R); Inc(R1.A.Y,6); R1.B.Y:=R1.A.Y+1; R1.B.X:=R1.A.X+(R1.B.X-R1.A.X) div 2-1;
|
||
R2.Copy(R1); R2.Move(0,1); R2.B.Y:=R2.A.Y+2;
|
||
New(RB2, Init(R2,
|
||
NewSItem('~G~lobal',
|
||
NewSItem('~S~elected text',
|
||
nil))));
|
||
Insert(RB2);
|
||
Insert(New(PLabel, Init(R1, 'Scope', RB2)));
|
||
|
||
R1.Copy(R); Inc(R1.A.Y,6); R1.B.Y:=R1.A.Y+1; R1.A.X:=R1.B.X-(R1.B.X-R1.A.X) div 2+1;
|
||
R2.Copy(R1); R2.Move(0,1); R2.B.Y:=R2.A.Y+2;
|
||
New(RB3, Init(R2,
|
||
NewSItem('~F~rom cursor',
|
||
NewSItem('~E~ntire scope',
|
||
nil))));
|
||
Insert(RB3);
|
||
Insert(New(PLabel, Init(R1, 'Origin', RB3)));
|
||
|
||
GetExtent(R); R.Grow(-13,-1); R.A.Y:=R.B.Y-2; R.B.X:=R.A.X+10;
|
||
Insert(New(PButton, Init(R, 'O~K', cmOK, bfDefault)));
|
||
R.Move(19,0);
|
||
Insert(New(PButton, Init(R, 'Cancel', cmCancel, bfNormal)));
|
||
end;
|
||
IL1^.Select;
|
||
CreateFindDialog := D;
|
||
end;
|
||
|
||
function CreateReplaceDialog: PDialog;
|
||
var R,R1,R2: TRect;
|
||
D: PDialog;
|
||
Control : PView;
|
||
IL1,IL2: PInputLine;
|
||
CB1: PCheckBoxes;
|
||
RB1,RB2,RB3: PRadioButtons;
|
||
begin
|
||
R.Assign(0,0,56,18);
|
||
New(D, Init(R, 'Replace'));
|
||
with D^ do
|
||
begin
|
||
Options:=Options or ofCentered;
|
||
GetExtent(R); R.Grow(-3,-2);
|
||
R1.Copy(R); R1.B.X:=17; R1.B.Y:=R1.A.Y+1;
|
||
R2.Copy(R); R2.B.X:=R2.B.X-3;R2.A.X:=17; R2.B.Y:=R2.A.Y+1;
|
||
New(IL1, Init(R2, FindStrSize));
|
||
IL1^.Data^:=FindStr;
|
||
Insert(IL1);
|
||
Insert(New(PLabel, Init(R1, '~T~ext to find', IL1)));
|
||
R1.Assign(R2.B.X, R2.A.Y, R2.B.X+3, R2.B.Y);
|
||
Control := New(PHistory, Init(R1, IL1, TextFindId));
|
||
Insert(Control);
|
||
|
||
R1.Copy(R); R1.Move(0,2); R1.B.X:=17; R1.B.Y:=R1.A.Y+1;
|
||
R2.Copy(R); R2.Move(0,2);R2.B.X:=R2.B.X-3;
|
||
R2.A.X:=17; R2.B.Y:=R2.A.Y+1;
|
||
New(IL2, Init(R2, FindStrSize));
|
||
IL2^.Data^:=ReplaceStr;
|
||
Insert(IL2);
|
||
Insert(New(PLabel, Init(R1, ' ~N~ew text', IL2)));
|
||
R1.Assign(R2.B.X, R2.A.Y, R2.B.X+3, R2.B.Y);
|
||
Control := New(PHistory, Init(R1, IL2, TextReplaceId));
|
||
Insert(Control);
|
||
|
||
R1.Copy(R); Inc(R1.A.Y,4); R1.B.Y:=R1.A.Y+1; R1.B.X:=R1.A.X+(R1.B.X-R1.A.X) div 2-1;
|
||
R2.Copy(R1); R2.Move(0,1); R2.B.Y:=R2.A.Y+3;
|
||
New(CB1, Init(R2,
|
||
NewSItem('~C~ase sensitive',
|
||
NewSItem('~W~hole words only',
|
||
NewSItem('~P~rompt on replace',
|
||
nil)))));
|
||
Insert(CB1);
|
||
Insert(New(PLabel, Init(R1, 'Options', CB1)));
|
||
|
||
R1.Copy(R); Inc(R1.A.Y,4); R1.B.Y:=R1.A.Y+1; R1.A.X:=R1.B.X-(R1.B.X-R1.A.X) div 2+1;
|
||
R2.Copy(R1); R2.Move(0,1); R2.B.Y:=R2.A.Y+2;
|
||
New(RB1, Init(R2,
|
||
NewSItem('Forwar~d~',
|
||
NewSItem('~B~ackward',
|
||
nil))));
|
||
Insert(RB1);
|
||
Insert(New(PLabel, Init(R1, 'Direction', RB1)));
|
||
|
||
R1.Copy(R); Inc(R1.A.Y,9); R1.B.Y:=R1.A.Y+1; R1.B.X:=R1.A.X+(R1.B.X-R1.A.X) div 2-1;
|
||
R2.Copy(R1); R2.Move(0,1); R2.B.Y:=R2.A.Y+2;
|
||
New(RB2, Init(R2,
|
||
NewSItem('~G~lobal',
|
||
NewSItem('~S~elected text',
|
||
nil))));
|
||
Insert(RB2);
|
||
Insert(New(PLabel, Init(R1, 'Scope', RB2)));
|
||
|
||
R1.Copy(R); Inc(R1.A.Y,9); R1.B.Y:=R1.A.Y+1; R1.A.X:=R1.B.X-(R1.B.X-R1.A.X) div 2+1;
|
||
R2.Copy(R1); R2.Move(0,1); R2.B.Y:=R2.A.Y+2;
|
||
New(RB3, Init(R2,
|
||
NewSItem('~F~rom cursor',
|
||
NewSItem('~E~ntire scope',
|
||
nil))));
|
||
Insert(RB3);
|
||
Insert(New(PLabel, Init(R1, 'Origin', RB3)));
|
||
|
||
GetExtent(R); R.Grow(-13,-1); R.A.Y:=R.B.Y-2; R.B.X:=R.A.X+10; R.Move(-10,0);
|
||
Insert(New(PButton, Init(R, 'O~K~', cmOK, bfDefault)));
|
||
R.Move(11,0); R.B.X:=R.A.X+14;
|
||
Insert(New(PButton, Init(R, 'Change ~a~ll', cmYes, bfNormal)));
|
||
R.Move(15,0); R.B.X:=R.A.X+10;
|
||
Insert(New(PButton, Init(R, 'Cancel', cmCancel, bfNormal)));
|
||
end;
|
||
IL1^.Select;
|
||
CreateReplaceDialog := D;
|
||
end;
|
||
|
||
function CreateGotoLineDialog(Info: pointer): PDialog;
|
||
var D: PDialog;
|
||
R,R1,R2: TRect;
|
||
Control : PView;
|
||
IL: PInputLine;
|
||
begin
|
||
R.Assign(0,0,40,7);
|
||
New(D, Init(R, 'Goto line'));
|
||
with D^ do
|
||
begin
|
||
Options:=Options or ofCentered;
|
||
GetExtent(R); R.Grow(-3,-2); R.B.Y:=R.A.Y+1;
|
||
R1.Copy(R); R1.B.X:=27; R2.Copy(R);
|
||
R2.B.X:=R2.B.X-3;R2.A.X:=27;
|
||
New(IL, Init(R2,5));
|
||
with TGotoLineDialogRec(Info^) do
|
||
IL^.SetValidator(New(PRangeValidator, Init(1, Lines)));
|
||
Insert(IL);
|
||
Insert(New(PLabel, Init(R1, 'Enter new line ~n~umber', IL)));
|
||
R1.Assign(R2.B.X, R2.A.Y, R2.B.X+3, R2.B.Y);
|
||
Control := New(PHistory, Init(R1, IL, GotoId));
|
||
Insert(Control);
|
||
|
||
GetExtent(R); R.Grow(-8,-1); R.A.Y:=R.B.Y-2; R.B.X:=R.A.X+10;
|
||
Insert(New(PButton, Init(R, 'O~K', cmOK, bfDefault)));
|
||
R.Move(15,0);
|
||
Insert(New(PButton, Init(R, 'Cancel', cmCancel, bfNormal)));
|
||
end;
|
||
IL^.Select;
|
||
CreateGotoLineDialog:=D;
|
||
end;
|
||
|
||
function StdEditorDialog(Dialog: Integer; Info: Pointer): Word;
|
||
var
|
||
R: TRect;
|
||
T: TPoint;
|
||
Re: word;
|
||
Name: string;
|
||
DriveNumber : byte;
|
||
StoreDir,StoreDir2 : DirStr;
|
||
Title,DefExt: string;
|
||
AskOW: boolean;
|
||
begin
|
||
case Dialog of
|
||
edOutOfMemory:
|
||
StdEditorDialog := MessageBox('Not enough memory for this operation.',
|
||
nil, mfInsertInApp+ mfError + mfOkButton);
|
||
edReadError:
|
||
StdEditorDialog := MessageBox('Error reading file %s.',
|
||
@Info, mfInsertInApp+ mfError + mfOkButton);
|
||
edWriteError:
|
||
StdEditorDialog := MessageBox('Error writing file %s.',
|
||
@Info, mfInsertInApp+ mfError + mfOkButton);
|
||
edCreateError:
|
||
StdEditorDialog := MessageBox('Error creating file %s.',
|
||
@Info, mfInsertInApp+ mfError + mfOkButton);
|
||
edSaveModify:
|
||
StdEditorDialog := MessageBox('%s has been modified. Save?',
|
||
@Info, mfInsertInApp+ mfInformation + mfYesNoCancel);
|
||
edSaveUntitled:
|
||
StdEditorDialog := MessageBox('Save untitled file?',
|
||
nil, mfInsertInApp+ mfInformation + mfYesNoCancel);
|
||
edSaveAs,edWriteBlock,edReadBlock:
|
||
begin
|
||
Name:=PString(Info)^;
|
||
GetDir(0,StoreDir);
|
||
DriveNumber:=0;
|
||
if (Length(FileDir)>1) and (FileDir[2]=':') then
|
||
begin
|
||
{ does not assume that lowercase are greater then uppercase ! }
|
||
if (FileDir[1]>='a') and (FileDir[1]>='z') then
|
||
DriveNumber:=Ord(FileDir[1])-ord('a')+1
|
||
else
|
||
DriveNumber:=Ord(FileDir[1])-ord('A')+1;
|
||
GetDir(DriveNumber,StoreDir2);
|
||
ChDir(Copy(FileDir,1,2));
|
||
end;
|
||
if FileDir<>'' then
|
||
ChDir(FileDir);
|
||
case Dialog of
|
||
edSaveAs :
|
||
begin
|
||
Title:='Save File As';
|
||
DefExt:='*'+DefaultSaveExt;
|
||
end;
|
||
edWriteBlock :
|
||
begin
|
||
Title:='Write Block to File';
|
||
DefExt:='';
|
||
end;
|
||
edReadBlock :
|
||
begin
|
||
Title:='Read Block from File';
|
||
DefExt:='';
|
||
end;
|
||
else begin Title:='???'; DefExt:=''; end;
|
||
end;
|
||
Re:=Application^.ExecuteDialog(New(PFileDialog, Init(DefExt,
|
||
Title, '~N~ame', fdOkButton, FileId)), @Name);
|
||
case Dialog of
|
||
edSaveAs : AskOW:=(Name<>PString(Info)^);
|
||
edWriteBlock : AskOW:=true;
|
||
edReadBlock : AskOW:=false;
|
||
else AskOW:=true;
|
||
end;
|
||
if (Re<>cmCancel) and AskOW then
|
||
begin
|
||
FileDir:=DirOf(FExpand(Name));
|
||
if ExistsFile(Name) then
|
||
if EditorDialog(edReplaceFile,@Name)<>cmYes then
|
||
Re:=cmCancel;
|
||
end;
|
||
if DriveNumber<>0 then
|
||
ChDir(StoreDir2);
|
||
{$ifdef TP}
|
||
if (Length(StoreDir)>1) and (StoreDir[2]=':') then
|
||
ChDir(Copy(StoreDir,1,2));
|
||
{$endif}
|
||
if StoreDir<>'' then
|
||
ChDir(StoreDir);
|
||
|
||
if Re<>cmCancel then
|
||
PString(Info)^:=Name;
|
||
StdEditorDialog := Re;
|
||
end;
|
||
edGotoLine:
|
||
StdEditorDialog :=
|
||
Application^.ExecuteDialog(CreateGotoLineDialog(Info), Info);
|
||
edFind:
|
||
StdEditorDialog :=
|
||
Application^.ExecuteDialog(CreateFindDialog, Info);
|
||
edSearchFailed:
|
||
StdEditorDialog := MessageBox('Search string not found.',
|
||
nil, mfInsertInApp+ mfError + mfOkButton);
|
||
edReplace:
|
||
StdEditorDialog :=
|
||
Application^.ExecuteDialog(CreateReplaceDialog, Info);
|
||
edReplacePrompt:
|
||
begin
|
||
{ Avoid placing the dialog on the same line as the cursor }
|
||
R.Assign(0, 1, 40, 8);
|
||
R.Move((Desktop^.Size.X - R.B.X) div 2, 0);
|
||
Desktop^.MakeGlobal(R.B, T);
|
||
Inc(T.Y);
|
||
if PPoint(Info)^.Y <= T.Y then
|
||
R.Move(0, Desktop^.Size.Y - R.B.Y - 2);
|
||
StdEditorDialog := MessageBoxRect(R, 'Replace this occurence?',
|
||
nil, mfInsertInApp+ mfYesNoCancel + mfInformation);
|
||
end;
|
||
edReplaceFile :
|
||
StdEditorDialog :=
|
||
MessageBox('File %s already exists. Overwrite?',@Info,mfInsertInApp+mfConfirmation+
|
||
mfYesButton+mfNoButton);
|
||
end;
|
||
end;
|
||
|
||
function DefUseSyntaxHighlight(Editor: PFileEditor): boolean;
|
||
begin
|
||
DefUseSyntaxHighlight:=(Editor^.Flags and efSyntaxHighlight)<>0;
|
||
end;
|
||
|
||
function DefUseTabsPattern(Editor: PFileEditor): boolean;
|
||
begin
|
||
DefUseTabsPattern:=(Editor^.Flags and efUseTabCharacters)<>0;
|
||
end;
|
||
|
||
procedure RegisterCodeEditors;
|
||
begin
|
||
{$ifndef NOOBJREG}
|
||
RegisterType(RIndicator);
|
||
RegisterType(RCodeEditor);
|
||
RegisterType(RFileEditor);
|
||
{$endif}
|
||
end;
|
||
|
||
END.
|
||
{
|
||
$Log$
|
||
Revision 1.50 1999-09-28 23:44:13 pierre
|
||
* text insertion in middle of line was buggy
|
||
|
||
Revision 1.49 1999/09/23 16:33:30 pierre
|
||
* ^B^A now prints out the ascii 1 char
|
||
* In SearchReplace Several occurence of a pattern in the same line
|
||
should now be found correctly
|
||
|
||
Revision 1.48 1999/09/22 16:16:26 pierre
|
||
+ added HistLists for several dialogs
|
||
|
||
Revision 1.47 1999/09/21 17:08:59 pierre
|
||
+ Windows clipboard for win32
|
||
|
||
Revision 1.46 1999/09/13 16:24:44 peter
|
||
+ clock
|
||
* backspace unident like tp7
|
||
|
||
Revision 1.45 1999/09/09 12:05:33 pierre
|
||
+ Copy/Paste to Windows Clipboard
|
||
+ efLeaveTrailingSpaces added to editor flags
|
||
(if not set then spaces at the end of a line are
|
||
removed on writing the file)
|
||
|
||
Revision 1.44 1999/08/27 15:07:44 pierre
|
||
+ cmResetDebuggerRow
|
||
|
||
Revision 1.43 1999/08/24 22:04:35 pierre
|
||
+ TCodeEditor.SetDebuggerRow
|
||
works like SetHighlightRow but is only disposed by a SetDebuggerRow(-1)
|
||
so the current stop point in debugging is not lost if
|
||
we move the cursor
|
||
|
||
Revision 1.42 1999/08/22 22:20:30 pierre
|
||
* selection extension bug removed, via oldEvent pointer in TCodeEditor.HandleEvent
|
||
|
||
Revision 1.41 1999/08/16 18:25:28 peter
|
||
* Adjusting the selection when the editor didn't contain any line.
|
||
* Reserved word recognition redesigned, but this didn't affect the overall
|
||
syntax highlight speed remarkably (at least not on my Amd-K6/350).
|
||
The syntax scanner loop is a bit slow but the main problem is the
|
||
recognition of special symbols. Switching off symbol processing boosts
|
||
the performance up to ca. 200%...
|
||
* The editor didn't allow copying (for ex to clipboard) of a single character
|
||
* 'File|Save as' caused permanently run-time error 3. Not any more now...
|
||
* Compiler Messages window (actually the whole desktop) did not act on any
|
||
keypress when compilation failed and thus the window remained visible
|
||
+ Message windows are now closed upon pressing Esc
|
||
+ At 'Run' the IDE checks whether any sources are modified, and recompiles
|
||
only when neccessary
|
||
+ BlockRead and BlockWrite (Ctrl+K+R/W) implemented in TCodeEditor
|
||
+ LineSelect (Ctrl+K+L) implemented
|
||
* The IDE had problems closing help windows before saving the desktop
|
||
|
||
Revision 1.40 1999/08/03 20:22:42 peter
|
||
+ TTab acts now on Ctrl+Tab and Ctrl+Shift+Tab...
|
||
+ Desktop saving should work now
|
||
- History saved
|
||
- Clipboard content saved
|
||
- Desktop saved
|
||
- Symbol info saved
|
||
* syntax-highlight bug fixed, which compared special keywords case sensitive
|
||
(for ex. 'asm' caused asm-highlighting, while 'ASM' didn't)
|
||
* with 'whole words only' set, the editor didn't found occourences of the
|
||
searched text, if the text appeared previously in the same line, but didn't
|
||
satisfied the 'whole-word' condition
|
||
* ^QB jumped to (SelStart.X,SelEnd.X) instead of (SelStart.X,SelStart.Y)
|
||
(ie. the beginning of the selection)
|
||
* when started typing in a new line, but not at the start (X=0) of it,
|
||
the editor inserted the text one character more to left as it should...
|
||
* TCodeEditor.HideSelection (Ctrl-K+H) didn't update the screen
|
||
* Shift shouldn't cause so much trouble in TCodeEditor now...
|
||
* Syntax highlight had problems recognizing a special symbol if it was
|
||
prefixed by another symbol character in the source text
|
||
* Auto-save also occours at Dos shell, Tool execution, etc. now...
|
||
|
||
Revision 1.39 1999/07/28 23:11:26 peter
|
||
* fixes from gabor
|
||
|
||
Revision 1.38 1999/07/12 13:14:24 pierre
|
||
* LineEnd bug corrected, now goes end of text even if selected
|
||
+ Until Return for debugger
|
||
+ Code for Quit inside GDB Window
|
||
|
||
Revision 1.37 1999/06/29 22:50:16 peter
|
||
* more fixes from gabor
|
||
|
||
Revision 1.36 1999/06/29 08:51:34 pierre
|
||
* lockflag problems fixed
|
||
|
||
Revision 1.35 1999/06/28 19:32:32 peter
|
||
* fixes from gabor
|
||
|
||
Revision 1.34 1999/06/28 15:58:07 pierre
|
||
* ShiftDel problem solved
|
||
|
||
Revision 1.33 1999/06/25 00:31:51 pierre
|
||
+ FileDir remembers the last directory for Open and Save
|
||
|
||
Revision 1.32 1999/06/21 23:36:12 pierre
|
||
* Size for Cluster is word (TP compatibility)
|
||
|
||
Revision 1.31 1999/05/22 13:44:35 peter
|
||
* fixed couple of bugs
|
||
|
||
Revision 1.30 1999/04/15 08:58:10 peter
|
||
* syntax highlight fixes
|
||
* browser updates
|
||
|
||
Revision 1.29 1999/04/07 21:55:59 peter
|
||
+ object support for browser
|
||
* html help fixes
|
||
* more desktop saving things
|
||
* NODEBUG directive to exclude debugger
|
||
|
||
Revision 1.28 1999/03/23 15:11:39 peter
|
||
* desktop saving things
|
||
* vesa mode
|
||
* preferences dialog
|
||
|
||
Revision 1.27 1999/03/08 14:58:17 peter
|
||
+ prompt with dialogs for tools
|
||
|
||
Revision 1.26 1999/03/07 22:58:57 pierre
|
||
* FindRec needs longint for CheckBoxes
|
||
|
||
Revision 1.25 1999/03/05 17:39:39 pierre
|
||
* Actions item freeing
|
||
|
||
Revision 1.24 1999/03/03 16:45:07 pierre
|
||
* Actions were not dispose in TCodeEditor.Done
|
||
|
||
Revision 1.23 1999/03/01 15:42:10 peter
|
||
+ Added dummy entries for functions not yet implemented
|
||
* MenuBar didn't update itself automatically on command-set changes
|
||
* Fixed Debugging/Profiling options dialog
|
||
* TCodeEditor converts spaces to tabs at save only if efUseTabChars is set
|
||
* efBackSpaceUnindents works correctly
|
||
+ 'Messages' window implemented
|
||
+ Added '$CAP MSG()' and '$CAP EDIT' to available tool-macros
|
||
+ Added TP message-filter support (for ex. you can call GREP thru
|
||
GREP2MSG and view the result in the messages window - just like in TP)
|
||
* A 'var' was missing from the param-list of THelpFacility.TopicSearch,
|
||
so topic search didn't work...
|
||
* In FPHELP.PAS there were still context-variables defined as word instead
|
||
of THelpCtx
|
||
* StdStatusKeys() was missing from the statusdef for help windows
|
||
+ Topic-title for index-table can be specified when adding a HTML-files
|
||
|
||
Revision 1.22 1999/02/22 02:15:25 peter
|
||
+ default extension for save in the editor
|
||
+ Separate Text to Find for the grep dialog
|
||
* fixed redir crash with tp7
|
||
|
||
Revision 1.21 1999/02/20 15:18:33 peter
|
||
+ ctrl-c capture with confirm dialog
|
||
+ ascii table in the tools menu
|
||
+ heapviewer
|
||
* empty file fixed
|
||
* fixed callback routines in fpdebug to have far for tp7
|
||
|
||
Revision 1.20 1999/02/18 17:27:57 pierre
|
||
* find/replace dialogs need packed records !!
|
||
|
||
Revision 1.19 1999/02/18 13:44:36 peter
|
||
* search fixed
|
||
+ backward search
|
||
* help fixes
|
||
* browser updates
|
||
|
||
Revision 1.18 1999/02/15 15:12:25 pierre
|
||
+ TLine remembers Comment type
|
||
|
||
Revision 1.17 1999/02/15 09:32:58 pierre
|
||
* single line comment // fix : comments intermix still wrong !!
|
||
|
||
Revision 1.16 1999/02/11 19:07:26 pierre
|
||
* GDBWindow redesigned :
|
||
normal editor apart from
|
||
that any kbEnter will send the line (for begin to cursor)
|
||
to GDB command !
|
||
GDBWindow opened in Debugger Menu
|
||
still buggy :
|
||
-echo should not be present if at end of text
|
||
-GDBWindow becomes First after each step (I don't know why !)
|
||
|
||
Revision 1.15 1999/02/09 09:29:59 pierre
|
||
* avoid invisible characters in CombineColors
|
||
|
||
Revision 1.14 1999/02/05 13:51:45 peter
|
||
* unit name of FPSwitches -> FPSwitch which is easier to use
|
||
* some fixes for tp7 compiling
|
||
|
||
Revision 1.13 1999/02/05 13:22:43 pierre
|
||
* bug that caused crash for empty files
|
||
|
||
Revision 1.12 1999/02/05 12:04:56 pierre
|
||
+ 'loose' centering for debugger
|
||
|
||
Revision 1.11 1999/02/04 17:19:26 peter
|
||
* linux fixes
|
||
|
||
Revision 1.10 1999/02/04 10:13:00 pierre
|
||
+ GetCurrentWord (used in Find/Replace)
|
||
+ DefUseTabsPattern (pattern forcing tabs to be kept)
|
||
used for all makefiles !!
|
||
|
||
Revision 1.9 1999/01/29 10:34:33 peter
|
||
+ needobjdir,needlibdir
|
||
|
||
Revision 1.8 1999/01/21 11:54:31 peter
|
||
+ tools menu
|
||
+ speedsearch in symbolbrowser
|
||
* working run command
|
||
|
||
Revision 1.7 1999/01/14 21:41:17 peter
|
||
* use * as modified indicator
|
||
* fixed syntax highlighting
|
||
|
||
Revision 1.6 1999/01/12 14:29:44 peter
|
||
+ Implemented still missing 'switch' entries in Options menu
|
||
+ Pressing Ctrl-B sets ASCII mode in editor, after which keypresses (even
|
||
ones with ASCII < 32 ; entered with Alt+<###>) are interpreted always as
|
||
ASCII chars and inserted directly in the text.
|
||
+ Added symbol browser
|
||
* splitted fp.pas to fpide.pas
|
||
|
||
Revision 1.5 1999/01/07 15:02:40 peter
|
||
* better tab support
|
||
|
||
Revision 1.4 1999/01/04 11:49:55 peter
|
||
* 'Use tab characters' now works correctly
|
||
+ Syntax highlight now acts on File|Save As...
|
||
+ Added a new class to syntax highlight: 'hex numbers'.
|
||
* There was something very wrong with the palette managment. Now fixed.
|
||
+ Added output directory (-FE<xxx>) support to 'Directories' dialog...
|
||
* Fixed some possible bugs in Running/Compiling, and the compilation/run
|
||
process revised
|
||
|
||
Revision 1.2 1998/12/28 15:47:55 peter
|
||
+ Added user screen support, display & window
|
||
+ Implemented Editor,Mouse Options dialog
|
||
+ Added location of .INI and .CFG file
|
||
+ Option (INI) file managment implemented (see bottom of Options Menu)
|
||
+ Switches updated
|
||
+ Run program
|
||
|
||
Revision 1.4 1998/12/27 12:01:23 gabor
|
||
* efXXXX constants revised for BP compatibility
|
||
* fixed column and row highlighting (needs to rewrite default palette in the INI)
|
||
|
||
Revision 1.3 1998/12/22 10:39:54 peter
|
||
+ options are now written/read
|
||
+ find and replace routines
|
||
|
||
}
|
||
|