mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-07 21:07:54 +02:00
7324 lines
198 KiB
ObjectPascal
7324 lines
198 KiB
ObjectPascal
{
|
|
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
|
|
{tes}
|
|
uses
|
|
Dos,Objects,Drivers,Views,Dialogs,Menus,
|
|
FVConsts,
|
|
WUtils,WViews;
|
|
|
|
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;
|
|
cmAddChar = 51249;
|
|
cmExpandCodeTemplate = 51250;
|
|
cmUpperCase = 51251;
|
|
cmLowerCase = 51252;
|
|
cmWindowStart = 51253;
|
|
cmWindowEnd = 51254;
|
|
cmFindMatchingDelimiter= 51255;
|
|
cmFindMatchingDelimiterBack=51256;
|
|
cmActivateMenu = 51257;
|
|
cmWordLowerCase = 51258;
|
|
cmWordUpperCase = 51259;
|
|
cmOpenAtCursor = 51260;
|
|
cmBrowseAtCursor = 51261;
|
|
cmInsertOptions = 51262;
|
|
cmToggleCase = 51263;
|
|
cmCreateFold = 51264;
|
|
cmToggleFold = 51265;
|
|
cmCollapseFold = 51266;
|
|
cmExpandFold = 51267;
|
|
cmDelToEndOfWord = 51268;
|
|
cmInputLineLen = 51269;
|
|
|
|
EditorTextBufSize = 32768;
|
|
MaxLineLength = 255;
|
|
MaxLineCount = 2000000;
|
|
|
|
CodeTemplateCursorChar = '|'; { char to signal cursor pos in templates }
|
|
|
|
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;
|
|
efCodeComplete = $00004000;
|
|
efFolds = $00008000;
|
|
efNoIndent = $00010000;
|
|
efKeepLineAttr = $00020000;
|
|
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;
|
|
edFileOnDiskChanged = 16;
|
|
edChangedOnloading = 17;
|
|
edSaveError = 18;
|
|
edReloadDiskmodifiedFile = 19;
|
|
edReloadDiskAndIDEModifiedFile = 20;
|
|
|
|
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;
|
|
|
|
{$ifdef TEST_REGEXP}
|
|
ffUseRegExp = $0100;
|
|
ffmUseRegExpFind = $0004;
|
|
ffmOptionsFind = $0003;
|
|
ffsUseRegExpFind = 8 - 2;
|
|
ffmUseRegExpReplace = $0008;
|
|
ffsUseRegExpReplace = 8 - 3;
|
|
{$endif TEST_REGEXP}
|
|
|
|
coTextColor = 0;
|
|
coWhiteSpaceColor = 1;
|
|
coCommentColor = 2;
|
|
coReservedWordColor = 3;
|
|
coIdentifierColor = 4;
|
|
coStringColor = 5;
|
|
coNumberColor = 6;
|
|
coAssemblerColor = 7;
|
|
coSymbolColor = 8;
|
|
coDirectiveColor = 9;
|
|
coHexNumberColor = 10;
|
|
coTabColor = 11;
|
|
coAsmReservedColor = 12;
|
|
coBreakColor = 13;
|
|
coFirstColor = 0;
|
|
coLastColor = coBreakColor;
|
|
|
|
lfBreakpoint = $0001;
|
|
lfHighlightRow = $0002;
|
|
lfDebuggerRow = $0004;
|
|
lfSpecialRow = $0008;
|
|
|
|
eaMoveCursor = 1;
|
|
eaInsertLine = 2;
|
|
eaInsertText = 3;
|
|
eaDeleteLine = 4;
|
|
eaDeleteText = 5;
|
|
eaSelectionChanged = 6;
|
|
eaCut = 7;
|
|
eaPaste = 8;
|
|
eaPasteWin = 9;
|
|
eaDelChar = 10;
|
|
eaClear = 11;
|
|
eaCopyBlock = 12;
|
|
eaMoveBlock = 13;
|
|
eaDelBlock = 14;
|
|
eaReadBlock = 15;
|
|
eaIndentBlock = 16;
|
|
eaUnindentBlock = 17;
|
|
eaOverwriteText = 18;
|
|
eaUpperCase = 19;
|
|
eaLowerCase = 20;
|
|
eaToggleCase = 21;
|
|
eaDummy = 22;
|
|
LastAction = eaDummy;
|
|
|
|
ActionString : array [0..LastAction-1] of string[13] =
|
|
('','Move','InsLine','InsText','DelLine','DelText',
|
|
'SelChange','Cut','Paste','PasteWin','DelChar','Clear',
|
|
'CopyBlock','MoveBlock','DelBlock',
|
|
'ReadBlock','IndentBlock','UnindentBlock','Overwrite',
|
|
'UpperCase','LowerCase','ToggleCase');
|
|
|
|
CIndicator = #2#3#1;
|
|
CEditor = #33#34#35#36#37#38#39#40#41#42#43#44#45#46#47#48#49#50;
|
|
|
|
TAB = #9;
|
|
FindStrSize = 79;
|
|
|
|
|
|
type
|
|
Tcentre = (do_not_centre,do_centre);
|
|
|
|
PCustomCodeEditor = ^TCustomCodeEditor;
|
|
PEditorLineInfo = ^TEditorLineInfo;
|
|
PFoldCollection = ^TFoldCollection;
|
|
|
|
PFold = ^TFold;
|
|
TFold = object(TObject)
|
|
constructor Init(AEditor: PCustomCodeEditor; AParentFold: PFold; ACollapsed: boolean);
|
|
procedure AddReference(P: PObject);
|
|
procedure RemoveReference(P: PObject);
|
|
procedure AddLineReference(Line: PEditorLineInfo);
|
|
procedure RemoveLineReference(Line: PEditorLineInfo);
|
|
procedure AddChildReference(Fold: PFold);
|
|
procedure RemoveChildReference(Fold: PFold);
|
|
function CanDispose: boolean;
|
|
function IsCollapsed: boolean;
|
|
function IsParent(AFold: PFold): boolean;
|
|
function GetLineCount: sw_integer;
|
|
procedure Collapse(ACollapse: boolean);
|
|
procedure Changed;
|
|
function GetLevel: sw_integer;
|
|
destructor Done; virtual;
|
|
public
|
|
ParentFold: PFold;
|
|
Collapsed_: boolean;
|
|
ReferenceCount: sw_integer;
|
|
Editor: PCustomCodeEditor;
|
|
LineCount_: sw_integer;
|
|
Childs: PFoldCollection;
|
|
end;
|
|
|
|
TFoldCollection = object(TCollection)
|
|
function At(Index: sw_Integer): PFold;
|
|
end;
|
|
|
|
TEditorLineInfo = object(TObject)
|
|
Editor: PCustomCodeEditor;
|
|
Format : PString;
|
|
BeginsWithAsm,
|
|
EndsWithAsm : boolean;
|
|
BeginsWithComment,
|
|
EndsInSingleLineComment,
|
|
EndsWithComment : boolean;
|
|
BeginsWithDirective,
|
|
EndsWithDirective : boolean;
|
|
BeginCommentType,EndCommentType : byte;
|
|
Fold: PFold;
|
|
constructor Init(AEditor: PCustomCodeEditor);
|
|
destructor Done; virtual;
|
|
function GetFormat: string;
|
|
procedure SetFormat(const AFormat: string);
|
|
procedure SetFold(AFold: PFold);
|
|
{ Syntax information is now generated separately for each editor instance.
|
|
This is not neccessary for a one-language IDE, but this unit contains
|
|
a _generic_ editor object, which should be (and is) as flexible as
|
|
possible.
|
|
The overhead caused by generating the same syntax info for ex.
|
|
twice isn't so much... - Gabor }
|
|
end;
|
|
|
|
PEditorLineInfoCollection = ^TEditorLineInfoCollection;
|
|
TEditorLineInfoCollection = object(TCollection)
|
|
function At(Index: sw_Integer): PEditorLineInfo;
|
|
end;
|
|
|
|
PCustomLine = ^TCustomLine;
|
|
TCustomLine = object(TObject)
|
|
constructor Init(const AText: string; AFlags: longint);
|
|
{a}function GetText: string; virtual;
|
|
{a}procedure SetText(const AText: string); virtual;
|
|
{a}function GetEditorInfo(Editor: PCustomCodeEditor): PEditorLineInfo; virtual;
|
|
{a}function GetFlags: longint; virtual;
|
|
{a}procedure SetFlags(AFlags: longint); virtual;
|
|
function IsFlagSet(AFlag: longint): boolean; {$ifdef USEINLINE}inline;{$endif}
|
|
procedure SetFlagState(AFlag: longint; ASet: boolean);
|
|
destructor Done; virtual;
|
|
public { internal use only! }
|
|
{a}procedure AddEditorInfo(Index: sw_integer; AEditor: PCustomCodeEditor); virtual;
|
|
{a}procedure RemoveEditorInfo(AEditor: PCustomCodeEditor); virtual;
|
|
end;
|
|
|
|
PLineCollection = ^TLineCollection;
|
|
TLineCollection = object(TCollection)
|
|
function At(Index: sw_Integer): PCustomLine;
|
|
end;
|
|
|
|
PEditorAction = ^TEditorAction;
|
|
TEditorAction = object(TObject)
|
|
StartPos : TPoint;
|
|
EndPos : TPoint;
|
|
Text : PString;
|
|
ActionCount : longint;
|
|
Flags : longint;
|
|
Action : byte;
|
|
IsGrouped : boolean;
|
|
TimeStamp : longint; { this is needed to keep track of line number &
|
|
position changes (for ex. for symbol browser)
|
|
the line&pos references (eg. symbol info) should
|
|
also contain such a timestamp. this will enable
|
|
to determine which changes have been made since
|
|
storage of the information and thus calculate
|
|
the (probably) changed line & position information,
|
|
so, we can still jump to the right position in the
|
|
editor even when it is heavily modified - Gabor }
|
|
constructor init(act:byte; StartP,EndP:TPoint;Txt:String;AFlags : longint);
|
|
constructor init_group(act:byte);
|
|
function is_grouped_action : boolean;
|
|
destructor done; virtual;
|
|
end;
|
|
|
|
PEditorActionCollection = ^TEditorActionCollection;
|
|
TEditorActionCollection = object(TCollection)
|
|
CurrentGroupedAction : PEditorAction;
|
|
GroupLevel : longint;
|
|
function At(Idx : sw_integer) : PEditorAction;
|
|
end;
|
|
|
|
TSpecSymbolClass =
|
|
(ssCommentPrefix,ssCommentSingleLinePrefix,ssCommentSuffix,ssStringPrefix,ssStringSuffix,
|
|
ssDirectivePrefix,ssDirectiveSuffix,ssAsmPrefix,ssAsmSuffix);
|
|
|
|
TEditorBookMark = record
|
|
Valid : boolean;
|
|
Pos : TPoint;
|
|
end;
|
|
|
|
TCompleteState = (csInactive,csOffering,csDenied);
|
|
|
|
PEditorBinding = ^TEditorBinding;
|
|
|
|
PEditorBindingCollection = ^TEditorBindingCollection;
|
|
TEditorBindingCollection = object(TCollection)
|
|
function At(Index: sw_Integer): PEditorBinding;
|
|
end;
|
|
|
|
TEditorBinding = object(TObject)
|
|
Editor : PCustomCodeEditor;
|
|
constructor Init(AEditor: PCustomCodeEditor);
|
|
destructor Done; virtual;
|
|
end;
|
|
|
|
PCustomCodeEditorCore = ^TCustomCodeEditorCore;
|
|
TCustomCodeEditorCore = object(TObject)
|
|
protected
|
|
Bindings : PEditorBindingCollection;
|
|
LockFlag : sw_integer;
|
|
ChangedLine : sw_integer;
|
|
ContentsChangedCalled : boolean;
|
|
LimitsChangedCalled : boolean;
|
|
ModifiedChangedCalled : boolean;
|
|
TabSizeChangedCalled : boolean;
|
|
StoreUndoChangedCalled : boolean;
|
|
{$ifdef TEST_PARTIAL_SYNTAX}
|
|
LastSyntaxedLine : sw_integer;
|
|
SyntaxComplete : boolean;
|
|
{$endif TEST_PARTIAL_SYNTAX}
|
|
public
|
|
constructor Init;
|
|
procedure BindEditor(AEditor: PCustomCodeEditor);
|
|
procedure UnBindEditor(AEditor: PCustomCodeEditor);
|
|
function IsEditorBound(AEditor: PCustomCodeEditor): boolean;
|
|
function GetBindingCount: sw_integer;
|
|
function GetBindingIndex(AEditor: PCustomCodeEditor): sw_integer;
|
|
function SearchBinding(AEditor: PCustomCodeEditor): PEditorBinding;
|
|
function CanDispose: boolean;
|
|
destructor Done; virtual;
|
|
public
|
|
{a}function GetModified: boolean; virtual;
|
|
function GetChangedLine: sw_integer;
|
|
{a}procedure SetModified(AModified: boolean); virtual;
|
|
{a}function GetStoreUndo: boolean; virtual;
|
|
{a}procedure SetStoreUndo(AStore: boolean); virtual;
|
|
{a}function GetSyntaxCompleted: boolean; virtual;
|
|
{a}procedure SetSyntaxCompleted(SC: boolean); virtual;
|
|
{a}function GetTabSize: integer; virtual;
|
|
{a}procedure SetTabSize(ATabSize: integer); virtual;
|
|
{a}function GetIndentSize: integer; virtual;
|
|
{a}procedure SetIndentSize(AIndentSize: integer); virtual;
|
|
function IsClipboard: Boolean;
|
|
public
|
|
{ Notifications }
|
|
procedure BindingsChanged;
|
|
procedure ContentsChanged;
|
|
procedure LimitsChanged;
|
|
procedure ModifiedChanged;
|
|
procedure TabSizeChanged;
|
|
procedure StoreUndoChanged;
|
|
{a}procedure DoContentsChanged; virtual;
|
|
{a}procedure DoLimitsChanged; virtual;
|
|
{a}procedure DoModifiedChanged; virtual;
|
|
{a}procedure DoTabSizeChanged; virtual;
|
|
{a}procedure DoStoreUndoChanged; virtual;
|
|
{a}procedure DoSyntaxStateChanged; virtual;
|
|
function GetLastVisibleLine : sw_integer;
|
|
public
|
|
{ Storage }
|
|
function LoadFromStream(Editor: PCustomCodeEditor; Stream: PFastBufStream): boolean; virtual;
|
|
function SaveToStream(Editor: PCustomCodeEditor; Stream: PStream): boolean; virtual;
|
|
function SaveAreaToStream(Editor: PCustomCodeEditor; Stream: PStream; StartP,EndP: TPoint): boolean; virtual;
|
|
protected
|
|
{ Text & info storage abstraction }
|
|
{a}procedure ISetLineFlagState(Binding: PEditorBinding; LineNo: sw_integer; Flag: longint; ASet: boolean); virtual;
|
|
{a}procedure IGetDisplayTextFormat(Binding: PEditorBinding; LineNo: sw_integer;var DT,DF:string); virtual;
|
|
{a}function IGetLineFormat(Binding: PEditorBinding; LineNo: sw_integer): string; virtual;
|
|
{a}procedure ISetLineFormat(Binding: PEditorBinding; LineNo: sw_integer;const S: string); virtual;
|
|
public
|
|
{ Text & info storage abstraction }
|
|
function CharIdxToLinePos(Line,CharIdx: sw_integer): sw_integer;
|
|
function LinePosToCharIdx(Line,X: sw_integer): sw_integer;
|
|
{a}function GetLineCount: sw_integer; virtual;
|
|
{a}function GetLine(LineNo: sw_integer): PCustomLine; virtual;
|
|
{a}function GetLineText(LineNo: sw_integer): string; virtual;
|
|
{a}procedure SetDisplayText(I: sw_integer;const S: string); virtual;
|
|
{a}function GetDisplayText(I: sw_integer): string; virtual;
|
|
{a}procedure SetLineText(I: sw_integer;const S: string); virtual;
|
|
procedure GetDisplayTextFormat(Editor: PCustomCodeEditor; I: sw_integer;var DT,DF:string); virtual;
|
|
function GetLineFormat(Editor: PCustomCodeEditor; I: sw_integer): string; virtual;
|
|
procedure SetLineFormat(Editor: PCustomCodeEditor; I: sw_integer;const S: string); virtual;
|
|
{a}procedure DeleteAllLines; virtual;
|
|
{a}procedure DeleteLine(I: sw_integer); virtual;
|
|
{a}function InsertLine(LineNo: sw_integer; const S: string): PCustomLine; virtual;
|
|
{a}procedure AddLine(const S: string); virtual;
|
|
{a}procedure GetContent(ALines: PUnsortedStringCollection); virtual;
|
|
{a}procedure SetContent(ALines: PUnsortedStringCollection); virtual;
|
|
public
|
|
procedure Lock(AEditor: PCustomCodeEditor);
|
|
procedure UnLock(AEditor: PCustomCodeEditor);
|
|
function Locked: boolean;
|
|
public
|
|
{ Syntax highlight }
|
|
function UpdateAttrs(FromLine: sw_integer; Attrs: byte): sw_integer; virtual;
|
|
function UpdateAttrsRange(FromLine, ToLine: sw_integer; Attrs: byte): sw_integer; virtual;
|
|
function DoUpdateAttrs(Editor: PCustomCodeEditor; FromLine: sw_integer; Attrs: byte): sw_integer; virtual;
|
|
function DoUpdateAttrsRange(Editor: PCustomCodeEditor; FromLine, ToLine: sw_integer;
|
|
Attrs: byte): sw_integer; virtual;
|
|
public
|
|
{ Undo info storage }
|
|
{a}procedure AddAction(AAction: byte; AStartPos, AEndPos: TPoint; AText: string;AFlags : longint); virtual;
|
|
{a}procedure AddGroupedAction(AAction : byte); virtual;
|
|
{a}procedure CloseGroupedAction(AAction : byte); virtual;
|
|
{a}function GetUndoActionCount: sw_integer; virtual;
|
|
{a}function GetRedoActionCount: sw_integer; virtual;
|
|
procedure UpdateUndoRedo(cm : word; action : byte);virtual;
|
|
end;
|
|
|
|
TCaseAction = (caToLowerCase,caToUpperCase,caToggleCase);
|
|
|
|
TCustomCodeEditor = object(TScroller)
|
|
SelStart : TPoint;
|
|
SelEnd : TPoint;
|
|
Highlight : TRect;
|
|
CurPos : TPoint;
|
|
ELockFlag : integer;
|
|
NoSelect : Boolean;
|
|
AlwaysShowScrollBars: boolean;
|
|
public
|
|
{ constructor Load(var S: TStream);
|
|
procedure Store(var S: TStream);}
|
|
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;
|
|
function GetPalette: PPalette; virtual;
|
|
public
|
|
procedure Draw; virtual;
|
|
procedure DrawCursor; virtual;
|
|
{ this is the only way I found to avoid
|
|
having the cursor being updated if lock is on PM }
|
|
procedure ResetCursor; virtual;
|
|
procedure DrawIndicator; virtual;
|
|
public
|
|
{a}function GetFlags: longint; virtual;
|
|
{a}procedure SetFlags(AFlags: longint); virtual;
|
|
{a}function GetModified: boolean; virtual;
|
|
{a}procedure SetModified(AModified: boolean); virtual;
|
|
{a}function GetStoreUndo: boolean; virtual;
|
|
{a}procedure SetStoreUndo(AStore: boolean); virtual;
|
|
{a}function GetSyntaxCompleted: boolean; virtual;
|
|
{a}procedure SetSyntaxCompleted(SC: boolean); virtual;
|
|
{a}function GetLastSyntaxedLine: sw_integer; virtual;
|
|
{a}procedure SetLastSyntaxedLine(ALine: sw_integer); virtual;
|
|
function IsFlagSet(AFlag: longint): boolean;{$ifdef USEINLINE}inline;{$endif}
|
|
function GetReservedColCount: sw_integer; virtual;
|
|
{a}function GetTabSize: integer; virtual;
|
|
{a}procedure SetTabSize(ATabSize: integer); virtual;
|
|
{a}function GetIndentSize: integer; virtual;
|
|
{a}procedure SetIndentSize(AIndentSize: integer); virtual;
|
|
{a}function IsReadOnly: boolean; virtual;
|
|
{a}function IsClipboard: Boolean; virtual;
|
|
{a}function GetInsertMode: boolean; virtual;
|
|
{a}procedure SetInsertMode(InsertMode: boolean); virtual;
|
|
procedure SetCurPtr(X,Y: sw_integer); virtual;
|
|
procedure GetSelectionArea(var StartP,EndP: TPoint); virtual;
|
|
procedure SetSelection(A, B: TPoint); virtual;
|
|
procedure SetHighlight(A, B: TPoint); virtual;
|
|
procedure ChangeCaseArea(StartP,EndP: TPoint; CaseAction: TCaseAction); virtual;
|
|
procedure SetLineFlagState(LineNo: sw_integer; Flags: longint; ASet: boolean);
|
|
procedure SetLineFlagExclusive(Flags: longint; LineNo: sw_integer);
|
|
procedure Update; virtual;
|
|
procedure ScrollTo(X, Y: sw_Integer);
|
|
procedure TrackCursor(centre:Tcentre); virtual;
|
|
procedure Lock; virtual;
|
|
procedure UnLock; virtual;
|
|
public
|
|
{ Text & info storage abstraction }
|
|
{a}function GetLineCount: sw_integer; virtual;
|
|
{a}function GetLine(LineNo: sw_integer): PCustomLine; virtual;
|
|
{a}function CharIdxToLinePos(Line,CharIdx: sw_integer): sw_integer; virtual;
|
|
{a}function LinePosToCharIdx(Line,X: sw_integer): sw_integer; virtual;
|
|
{a}function GetLineText(I: sw_integer): string; virtual;
|
|
{a}procedure SetDisplayText(I: sw_integer;const S: string); virtual;
|
|
{a}function GetDisplayText(I: sw_integer): string; virtual;
|
|
{a}procedure SetLineText(I: sw_integer;const S: string); virtual;
|
|
{a}procedure GetDisplayTextFormat(I: sw_integer;var DT,DF:string); virtual;
|
|
{a}function GetLineFormat(I: sw_integer): string; virtual;
|
|
{a}procedure SetLineFormat(I: sw_integer;const S: string); virtual;
|
|
{a}procedure DeleteAllLines; virtual;
|
|
{a}procedure DeleteLine(I: sw_integer); virtual;
|
|
{a}function InsertLine(LineNo: sw_integer; const S: string): PCustomLine; virtual;
|
|
{a}procedure AddLine(const S: string); virtual;
|
|
{a}function GetErrorMessage: string; virtual;
|
|
{a}procedure SetErrorMessage(const S: string); virtual;
|
|
{a}procedure AdjustSelection(DeltaX, DeltaY: sw_integer);
|
|
{a}procedure AdjustSelectionBefore(DeltaX, DeltaY: sw_integer);
|
|
{a}procedure AdjustSelectionPos(OldCurPosX, OldCurPosY: sw_integer; DeltaX, DeltaY: sw_integer);
|
|
{a}procedure GetContent(ALines: PUnsortedStringCollection); virtual;
|
|
{a}procedure SetContent(ALines: PUnsortedStringCollection); virtual;
|
|
{a}function LoadFromStream(Stream: PFastBufStream): boolean; virtual;
|
|
{a}function SaveToStream(Stream: PStream): boolean; virtual;
|
|
{a}function SaveAreaToStream(Stream: PStream; StartP,EndP: TPoint): boolean;virtual;
|
|
function LoadFromFile(const AFileName: string): boolean; virtual;
|
|
function SaveToFile(const AFileName: string): boolean; virtual;
|
|
public
|
|
{a}function InsertFrom(Editor: PCustomCodeEditor): Boolean; virtual;
|
|
{a}function InsertText(const S: string): Boolean; virtual;
|
|
public
|
|
procedure FlagsChanged(OldFlags: longint); virtual;
|
|
{a}procedure BindingsChanged; virtual;
|
|
procedure ContentsChanged; virtual;
|
|
procedure LimitsChanged; virtual;
|
|
procedure ModifiedChanged; virtual;
|
|
procedure PositionChanged; virtual;
|
|
procedure TabSizeChanged; virtual;
|
|
procedure SyntaxStateChanged; virtual;
|
|
procedure StoreUndoChanged; virtual;
|
|
procedure SelectionChanged; virtual;
|
|
procedure HighlightChanged; virtual;
|
|
{a}procedure DoLimitsChanged; virtual;
|
|
public
|
|
{ Syntax highlight support }
|
|
{a}function GetSpecSymbolCount(SpecClass: TSpecSymbolClass): integer; virtual;
|
|
{a}function GetSpecSymbol(SpecClass: TSpecSymbolClass; Index: integer): pstring; virtual;
|
|
{a}function IsReservedWord(const S: string): boolean; virtual;
|
|
{a}function IsAsmReservedWord(const S: string): boolean; virtual;
|
|
public
|
|
{ CodeTemplate support }
|
|
{a}function TranslateCodeTemplate(var Shortcut: string; ALines: PUnsortedStringCollection): boolean; virtual;
|
|
function SelectCodeTemplate(var ShortCut: string): boolean; virtual;
|
|
{ CodeComplete support }
|
|
{a}function CompleteCodeWord(const WordS: string; var Text: string): boolean; virtual;
|
|
{a}function GetCodeCompleteWord: string; virtual;
|
|
{a}procedure SetCodeCompleteWord(const S: string); virtual;
|
|
{a}function GetCodeCompleteFrag: string; virtual;
|
|
{a}procedure SetCodeCompleteFrag(const S: string); virtual;
|
|
function GetCompleteState: TCompleteState; virtual;
|
|
procedure SetCompleteState(AState: TCompleteState); virtual;
|
|
procedure ClearCodeCompleteWord; virtual;
|
|
{ Fold support }
|
|
function GetMaxFoldLevel: sw_integer; virtual;
|
|
function GetFoldStringWidth: sw_integer; virtual;
|
|
procedure GetFoldStrings(EditorLine: sw_integer; var Prefix, Suffix: openstring); virtual;
|
|
{a}function GetFoldCount: sw_integer; virtual;
|
|
{a}function GetFold(Index: sw_integer): PFold; virtual;
|
|
{a}procedure RegisterFold(AFold: PFold); virtual;
|
|
{a}procedure UnRegisterFold(AFold: PFold); virtual;
|
|
function ViewToEditorLine(ViewLine: sw_integer): sw_integer;
|
|
function EditorToViewLine(EditorLine: sw_integer): sw_integer;
|
|
procedure ViewToEditorPoint(P: TPoint; var NP: TPoint);
|
|
procedure EditorToViewPoint(P: TPoint; var NP: TPoint);
|
|
{ Fold support }
|
|
function CreateFold(StartY,EndY: sw_integer; Collapsed: boolean): boolean; virtual;
|
|
procedure FoldChanged(Fold: PFold); virtual;
|
|
procedure RemoveAllFolds; virtual;
|
|
public
|
|
{ Syntax highlight }
|
|
{a}function UpdateAttrs(FromLine: sw_integer; Attrs: byte): sw_integer; virtual;
|
|
{a}function UpdateAttrsRange(FromLine, ToLine: sw_integer; Attrs: byte): sw_integer; virtual;
|
|
public
|
|
{ Undo info storage }
|
|
{a}procedure AddAction(AAction: byte; AStartPos, AEndPos: TPoint; AText: string;AFlags : longint); virtual;
|
|
{a}procedure AddGroupedAction(AAction : byte); virtual;
|
|
{a}procedure CloseGroupedAction(AAction : byte); virtual;
|
|
{a}function GetUndoActionCount: sw_integer; virtual;
|
|
{a}function GetRedoActionCount: sw_integer; virtual;
|
|
protected
|
|
LastLocalCmd: word;
|
|
KeyState : Integer;
|
|
Bookmarks : array[0..9] of TEditorBookmark;
|
|
DrawCalled,
|
|
DrawCursorCalled: boolean;
|
|
CurEvent : PEvent;
|
|
procedure DrawLines(FirstLine: sw_integer);
|
|
function Overwrite: boolean;
|
|
function IsModal: boolean;
|
|
procedure CheckSels;
|
|
procedure CodeCompleteCheck;
|
|
procedure CodeCompleteApply;
|
|
procedure CodeCompleteCancel;
|
|
procedure UpdateUndoRedo(cm : word; action : byte);
|
|
procedure HideHighlight;
|
|
function ShouldExtend: boolean;
|
|
function ValidBlock: boolean;
|
|
function GetLineFold(EditorLine: sw_integer): PFold;
|
|
function IsLineVisible(EditorLine: sw_integer): boolean; virtual;
|
|
function NextVisibleLine(StartLine: sw_integer; Down: boolean): sw_integer;
|
|
procedure PushInfo(Const st : string);virtual;
|
|
procedure PopInfo;virtual;
|
|
public
|
|
{ Editor primitives }
|
|
procedure SelectAll(Enable: boolean); virtual;
|
|
public
|
|
{ Editor commands }
|
|
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 WindowStart; virtual;
|
|
procedure WindowEnd; virtual;
|
|
procedure JumpSelStart; virtual;
|
|
procedure JumpSelEnd; virtual;
|
|
procedure JumpMark(MarkIdx: integer); virtual;
|
|
procedure DefineMark(MarkIdx: integer); virtual;
|
|
procedure JumpToLastCursorPos; virtual;
|
|
procedure FindMatchingDelimiter(ScanForward: boolean); virtual;
|
|
procedure CreateFoldFromBlock; virtual;
|
|
procedure ToggleFold; virtual;
|
|
procedure CollapseFold; virtual;
|
|
procedure ExpandFold; virtual;
|
|
procedure UpperCase; virtual;
|
|
procedure LowerCase; virtual;
|
|
procedure WordLowerCase; virtual;
|
|
procedure WordUpperCase; virtual;
|
|
procedure InsertOptions; virtual;
|
|
procedure ToggleCase; virtual;
|
|
function InsertNewLine: Sw_integer; virtual;
|
|
procedure BreakLine; virtual;
|
|
procedure BackSpace; virtual;
|
|
procedure DelChar; virtual;
|
|
procedure DelWord; virtual;
|
|
procedure DelToEndOfWord; 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 ExpandCodeTemplate; 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;
|
|
function GetCurrentWordArea(var StartP,EndP: TPoint): boolean;
|
|
procedure Undo; virtual;
|
|
procedure Redo; virtual;
|
|
procedure Find; virtual;
|
|
procedure Replace; virtual;
|
|
procedure DoSearchReplace; virtual;
|
|
procedure GotoLine; virtual;
|
|
end;
|
|
|
|
TCodeEditorDialog = function(Dialog: Integer; Info: Pointer): Word;
|
|
|
|
TEditorInputLine = object(TInputLine)
|
|
Procedure HandleEvent(var Event : TEvent);virtual;
|
|
end;
|
|
PEditorInputLine = ^TEditorInputLine;
|
|
|
|
TSearchHelperDialog = object(TDialog)
|
|
OkButton: PButton;
|
|
Procedure HandleEvent(var Event : TEvent);virtual;
|
|
end;
|
|
|
|
PSearchHelperDialog = ^TSearchHelperDialog;
|
|
|
|
const
|
|
{ used for ShiftDel and ShiftIns to avoid
|
|
GetShiftState to be considered for extending
|
|
selection (PM) }
|
|
DontConsiderShiftState: boolean = false;
|
|
|
|
CodeCompleteMinLen : byte = 4; { minimum length of text to try to complete }
|
|
|
|
ToClipCmds : TCommandSet = ([cmCut,cmCopy,cmCopyWin,
|
|
{ cmUnselect should because like cut, copy, copywin:
|
|
if there is a selection, it is active, else it isn't }
|
|
cmUnselect]);
|
|
FromClipCmds : TCommandSet = ([cmPaste]);
|
|
NulClipCmds : TCommandSet = ([cmClear]);
|
|
UndoCmd : TCommandSet = ([cmUndo]);
|
|
RedoCmd : TCommandSet = ([cmRedo]);
|
|
|
|
function ExtractTabs(S: string; TabSize: Sw_integer): string;
|
|
|
|
function StdEditorDialog(Dialog: Integer; Info: Pointer): word;
|
|
|
|
const
|
|
DefaultSaveExt : string[12] = '.pas';
|
|
FileDir : DirStr = '';
|
|
|
|
EditorDialog : TCodeEditorDialog = {$ifdef fpc}@{$endif}StdEditorDialog;
|
|
Clipboard : PCustomCodeEditor = nil;
|
|
FindStr : String[FindStrSize] = '';
|
|
ReplaceStr : String[FindStrSize] = '';
|
|
FindReplaceEditor : PCustomCodeEditor = nil;
|
|
FindFlags : word = ffPromptOnReplace;
|
|
{$ifndef NO_UNTYPEDSET}
|
|
{$define USE_UNTYPEDSET}
|
|
{$endif ndef NO_UNTYPEDSET}
|
|
WhiteSpaceChars {$ifdef USE_UNTYPEDSET}: set of char {$endif} = [#0,#32,#255];
|
|
TabChars {$ifdef USE_UNTYPEDSET}: set of char {$endif} = [#9];
|
|
HashChars {$ifdef USE_UNTYPEDSET}: set of char {$endif} = ['#'];
|
|
AlphaChars {$ifdef USE_UNTYPEDSET}: set of char {$endif} = ['A'..'Z','a'..'z','_'];
|
|
NumberChars {$ifdef USE_UNTYPEDSET}: set of char {$endif} = ['0'..'9'];
|
|
HexNumberChars {$ifdef USE_UNTYPEDSET}: set of char {$endif} = ['0'..'9','A'..'F','a'..'f'];
|
|
RealNumberChars {$ifdef USE_UNTYPEDSET}: set of char {$endif} = ['E','e','.'{,'+','-'}];
|
|
|
|
procedure RegisterWEditor;
|
|
|
|
implementation
|
|
|
|
uses
|
|
Strings,Video,MsgBox,App,StdDlg,Validate,
|
|
{$ifdef WinClipSupported}
|
|
WinClip,
|
|
{$endif WinClipSupported}
|
|
{$ifdef TEST_REGEXP}
|
|
{$ifdef USE_OLD_REGEXP}
|
|
oldregexpr,
|
|
{$else not USE_OLD_REGEXP}
|
|
regexpr,
|
|
{$endif not USE_OLD_REGEXP}
|
|
{$endif TEST_REGEXP}
|
|
WConsts,WCEdit;
|
|
|
|
type
|
|
RecordWord = sw_word;
|
|
|
|
TFindDialogRec = packed record
|
|
Find : String[FindStrSize];
|
|
Options : RecordWord{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: RecordWord;{ and tcluster has word size }
|
|
Scope : RecordWord;
|
|
Origin : RecordWord;
|
|
end;
|
|
|
|
TReplaceDialogRec = packed record
|
|
Find : String[FindStrSize];
|
|
Replace : String[FindStrSize];
|
|
Options : RecordWord{longint};
|
|
Direction: RecordWord;
|
|
Scope : RecordWord;
|
|
Origin : RecordWord;
|
|
end;
|
|
|
|
TGotoLineDialogRec = packed record
|
|
LineNo : string[5];
|
|
Lines : sw_integer;
|
|
end;
|
|
|
|
const
|
|
kbShift = kbLeftShift+kbRightShift;
|
|
|
|
const
|
|
FirstKeyCount = 46;
|
|
FirstKeys: array[0..FirstKeyCount * 2] of Word = (FirstKeyCount,
|
|
Ord(^A), cmWordLeft, Ord(^B), cmJumpLine, Ord(^C), cmPageDown,
|
|
Ord(^D), cmCharRight, Ord(^E), cmLineUp,
|
|
Ord(^F), cmWordRight, Ord(^G), cmDelChar,
|
|
Ord(^H), cmBackSpace, Ord(^J), cmExpandCodeTemplate,
|
|
Ord(^K), $FF02, Ord(^L), cmSearchAgain,
|
|
Ord(^M), cmNewLine, Ord(^N), cmBreakLine,
|
|
Ord(^O), $FF03,
|
|
Ord(^P), cmASCIIChar, Ord(^Q), $FF01,
|
|
Ord(^R), cmPageUp, Ord(^S), cmCharLeft,
|
|
Ord(^T), cmDelToEndOfWord, Ord(^U), cmUndo,
|
|
Ord(^V), cmInsMode, Ord(^X), cmLineDown,
|
|
Ord(^Y), cmDelLine, kbLeft, cmCharLeft,
|
|
kbRight, cmCharRight, kbCtrlLeft, cmWordLeft,
|
|
kbCtrlRight, cmWordRight, kbHome, cmLineStart,
|
|
kbCtrlHome, cmWindowStart, kbCtrlEnd, cmWindowEnd,
|
|
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,
|
|
kbCtrlGrayMul, cmToggleFold, kbCtrlGrayMinus, cmCollapseFold, kbCtrlGrayPlus, cmExpandFold);
|
|
QuickKeyCount = 29;
|
|
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('E'), cmWindowStart, Ord('T'), cmWindowStart,
|
|
Ord('U'), cmWindowEnd, Ord('X'), cmWindowEnd,
|
|
Ord('['), cmFindMatchingDelimiter, Ord(']'), cmFindMatchingDelimiterBack,
|
|
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 = 30;
|
|
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('N'), cmUpperCase, Ord('O'), cmLowerCase,
|
|
Ord('D'), cmActivateMenu,
|
|
Ord('E'), cmWordLowerCase, Ord('F'), cmWordUpperCase,
|
|
Ord('S'), cmSave, Ord('A'), cmCreateFold,
|
|
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);
|
|
MiscKeyCount = 6;
|
|
MiscKeys: array[0..MiscKeyCount * 2] of Word = (MiscKeyCount,
|
|
Ord('A'), cmOpenAtCursor, Ord('B'), cmBrowseAtCursor,
|
|
Ord('G'), cmJumpLine, Ord('O'), cmInsertOptions,
|
|
Ord('U'), cmToggleCase, Ord('L'), cmSelectLine);
|
|
KeyMap: array[0..3] of Pointer = (@FirstKeys, @QuickKeys, @BlockKeys, @MiscKeys);
|
|
|
|
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;
|
|
|
|
{ TAB are not same as spaces if UseTabs is set PM }
|
|
function RTrim(S: string;cut_tabs : boolean): string;
|
|
begin
|
|
while (length(S)>0) and
|
|
((S[length(S)] in [#0,#32]) or
|
|
((S[Length(S)]=TAB) and cut_tabs)) do
|
|
Delete(S,length(S),1);
|
|
RTrim:=S;
|
|
end;
|
|
|
|
function Trim(S: string): string;
|
|
begin
|
|
Trim:=RTrim(LTrim(S),true);
|
|
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 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;
|
|
}
|
|
type TPosOfs = int64;
|
|
|
|
function PosToOfs(const X,Y: sw_integer): TPosOfs;
|
|
begin
|
|
PosToOfs:=TPosOfs(y) shl (sizeof(sw_integer)*8) or x;
|
|
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 ExtractTabs(S: string; TabSize: Sw_integer): string;
|
|
var
|
|
P,PAdd: Sw_integer;
|
|
begin
|
|
p:=0;
|
|
while p<length(s) do
|
|
begin
|
|
inc(p);
|
|
if s[p]=TAB then
|
|
begin
|
|
PAdd:=TabSize-((p-1) mod TabSize);
|
|
s:=copy(S,1,P-1)+CharStr(' ',PAdd)+copy(S,P+1,High(s));
|
|
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,High(S));
|
|
until P=0;
|
|
CompressUsingTabs:=S;
|
|
end;}
|
|
|
|
|
|
{*****************************************************************************
|
|
Forward/Backward Scanning
|
|
*****************************************************************************}
|
|
|
|
Const
|
|
MaxBufLength = $7fffff00;
|
|
NotFoundValue = -1;
|
|
|
|
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;
|
|
SetLength(s2,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 : Sw_Word;
|
|
numb : Sw_Integer;
|
|
found : Boolean;
|
|
begin
|
|
len:=length(str);
|
|
if len>size then
|
|
begin
|
|
BMBScan := NotFoundValue;
|
|
exit;
|
|
end;
|
|
SetLength(S2,len); { sets the length to that of the search String }
|
|
found:=False;
|
|
numb:=size-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,
|
|
x : Sw_Word;
|
|
numb : 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
|
|
*****************************************************************************}
|
|
|
|
constructor TCustomLine.Init(const AText: string; AFlags: longint);
|
|
begin
|
|
inherited Init;
|
|
SetText(AText);
|
|
end;
|
|
|
|
function TCustomLine.GetText: string;
|
|
begin
|
|
Abstract;GetText:='';
|
|
end;
|
|
|
|
procedure TCustomLine.SetText(const AText: string);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
function TCustomLine.GetEditorInfo(Editor: PCustomCodeEditor): PEditorLineInfo;
|
|
begin
|
|
Abstract;
|
|
GetEditorInfo:=nil;
|
|
end;
|
|
|
|
function TCustomLine.GetFlags: longint;
|
|
begin
|
|
Abstract;
|
|
GetFlags:=0;
|
|
end;
|
|
|
|
procedure TCustomLine.SetFlags(AFlags: longint);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
function TCustomLine.IsFlagSet(AFlag: longint): boolean;{$ifdef USEINLINE}inline;{$endif}
|
|
begin
|
|
IsFlagSet:=(GetFlags and AFlag)=AFlag;
|
|
end;
|
|
|
|
procedure TCustomLine.SetFlagState(AFlag: longint; ASet: boolean);
|
|
var N,O: longint;
|
|
begin
|
|
O:=GetFlags; N:=O;
|
|
if ASet then
|
|
N:=N or AFlag
|
|
else
|
|
N:=N and (not AFlag);
|
|
if N<>O then
|
|
SetFlags(N);
|
|
end;
|
|
|
|
procedure TCustomLine.AddEditorInfo(Index: sw_integer; AEditor: PCustomCodeEditor);
|
|
begin
|
|
{ Abstract }
|
|
end;
|
|
|
|
procedure TCustomLine.RemoveEditorInfo(AEditor: PCustomCodeEditor);
|
|
begin
|
|
{ Abstract }
|
|
end;
|
|
|
|
destructor TCustomLine.Done;
|
|
begin
|
|
inherited Done;
|
|
end;
|
|
|
|
function TLineCollection.At(Index: sw_Integer): PCustomLine;
|
|
begin
|
|
At:=inherited At(Index);
|
|
end;
|
|
|
|
constructor TFold.Init(AEditor: PCustomCodeEditor; AParentFold: PFold; ACollapsed: boolean);
|
|
begin
|
|
inherited Init;
|
|
New(Childs, Init(10,10));
|
|
Editor:=AEditor;
|
|
ParentFold:=AParentFold;
|
|
if Assigned(ParentFold) then
|
|
ParentFold^.AddChildReference(@Self);
|
|
Collapsed_:=ACollapsed;
|
|
if Assigned(AEditor) then
|
|
Editor^.RegisterFold(@Self);
|
|
end;
|
|
|
|
procedure TFold.AddReference(P: PObject);
|
|
begin
|
|
Inc(ReferenceCount);
|
|
end;
|
|
|
|
procedure TFold.RemoveReference(P: PObject);
|
|
begin
|
|
Dec(ReferenceCount);
|
|
if CanDispose then
|
|
Free;
|
|
end;
|
|
|
|
procedure TFold.AddLineReference(Line: PEditorLineInfo);
|
|
begin
|
|
Inc(LineCount_);
|
|
AddReference(Line);
|
|
end;
|
|
|
|
procedure TFold.RemoveLineReference(Line: PEditorLineInfo);
|
|
begin
|
|
Dec(LineCount_);
|
|
RemoveReference(Line);
|
|
end;
|
|
|
|
procedure TFold.AddChildReference(Fold: PFold);
|
|
begin
|
|
Childs^.Insert(Fold);
|
|
AddReference(Fold);
|
|
end;
|
|
|
|
procedure TFold.RemoveChildReference(Fold: PFold);
|
|
begin
|
|
Childs^.Delete(Fold);
|
|
RemoveReference(Fold);
|
|
end;
|
|
|
|
function TFold.CanDispose: boolean;
|
|
begin
|
|
CanDispose:=ReferenceCount<=0;
|
|
end;
|
|
|
|
function TFold.IsCollapsed: boolean;
|
|
var C: boolean;
|
|
begin
|
|
C:=Collapsed_;
|
|
if Assigned(ParentFold) then C:=C or ParentFold^.IsCollapsed;
|
|
IsCollapsed:=C;
|
|
end;
|
|
|
|
function TFold.IsParent(AFold: PFold): boolean;
|
|
var P: boolean;
|
|
begin
|
|
P:=(ParentFold=AFold);
|
|
if Assigned(ParentFold) then P:=P or ParentFold^.IsParent(AFold);
|
|
IsParent:=P;
|
|
end;
|
|
|
|
function TFold.GetLineCount: sw_integer;
|
|
var Count: sw_integer;
|
|
procedure AddIt(P: PFold);
|
|
begin
|
|
Inc(Count,P^.GetLineCount);
|
|
end;
|
|
begin
|
|
Count:=LineCount_;
|
|
if assigned(Childs) then Childs^.ForEach(@AddIt);
|
|
GetLineCount:=Count;
|
|
end;
|
|
|
|
procedure TFold.Collapse(ACollapse: boolean);
|
|
begin
|
|
if ACollapse<>Collapsed_ then
|
|
begin
|
|
Collapsed_:=ACollapse;
|
|
if (not Collapsed_) and Assigned(ParentFold) then
|
|
ParentFold^.Collapse(false);
|
|
Changed;
|
|
end;
|
|
end;
|
|
|
|
procedure TFold.Changed;
|
|
begin
|
|
if Assigned(Editor) then
|
|
Editor^.FoldChanged(@Self);
|
|
end;
|
|
|
|
function TFold.GetLevel: sw_integer;
|
|
var Level: sw_integer;
|
|
begin
|
|
Level:=0;
|
|
if Assigned(ParentFold) then
|
|
Inc(Level,1+ParentFold^.GetLevel);
|
|
GetLevel:=Level;
|
|
end;
|
|
|
|
destructor TFold.Done;
|
|
begin
|
|
if Assigned(ParentFold) then
|
|
ParentFold^.RemoveChildReference(@Self);
|
|
if Assigned(Editor) then
|
|
Editor^.UnRegisterFold(@Self);
|
|
Childs^.DeleteAll; Dispose(Childs, Done);
|
|
inherited Done;
|
|
end;
|
|
|
|
function TFoldCollection.At(Index: sw_Integer): PFold;
|
|
begin
|
|
At:=inherited At(Index);
|
|
end;
|
|
|
|
constructor TEditorLineInfo.Init(AEditor: PCustomCodeEditor);
|
|
begin
|
|
inherited Init;
|
|
Editor:=AEditor;
|
|
end;
|
|
|
|
function TEditorLineInfo.GetFormat: string;
|
|
begin
|
|
GetFormat:=GetStr(Format);
|
|
end;
|
|
|
|
procedure TEditorLineInfo.SetFormat(const AFormat: string);
|
|
begin
|
|
SetStr(Format,AFormat);
|
|
end;
|
|
|
|
procedure TEditorLineInfo.SetFold(AFold: PFold);
|
|
begin
|
|
if Assigned(Fold) then
|
|
Fold^.RemoveLineReference(@Self);
|
|
Fold:=AFold;
|
|
if Assigned(Fold) then
|
|
Fold^.AddLineReference(@Self);
|
|
end;
|
|
|
|
destructor TEditorLineInfo.Done;
|
|
begin
|
|
if Format<>nil then
|
|
DisposeStr(Format);
|
|
Format:=nil;
|
|
SetFold(nil);
|
|
inherited Done;
|
|
end;
|
|
|
|
function TEditorLineInfoCollection.At(Index: sw_Integer): PEditorLineInfo;
|
|
begin
|
|
At:=inherited At(Index);
|
|
end;
|
|
|
|
function TEditorBindingCollection.At(Index: sw_Integer): PEditorBinding;
|
|
begin
|
|
At:=inherited At(Index);
|
|
end;
|
|
|
|
constructor TEditorBinding.Init(AEditor: PCustomCodeEditor);
|
|
begin
|
|
inherited Init;
|
|
Editor:=AEditor;
|
|
end;
|
|
|
|
destructor TEditorBinding.Done;
|
|
begin
|
|
inherited Done;
|
|
end;
|
|
|
|
constructor TCustomCodeEditorCore.Init;
|
|
begin
|
|
inherited Init;
|
|
New(Bindings, Init(10,10));
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.BindEditor(AEditor: PCustomCodeEditor);
|
|
var B: PEditorBinding;
|
|
Count,I,Idx: sw_integer;
|
|
L: PCustomLine;
|
|
begin
|
|
assert(Aeditor<>nil);
|
|
New(B, Init(AEditor));
|
|
Bindings^.Insert(B);
|
|
Idx:=Bindings^.IndexOf(B);
|
|
Count:=GetLineCount;
|
|
for I:=0 to Count-1 do
|
|
begin
|
|
L:=GetLine(I);
|
|
if Assigned(L) then
|
|
L^.AddEditorInfo(Idx,AEditor);
|
|
end;
|
|
|
|
BindingsChanged;
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.UnBindEditor(AEditor: PCustomCodeEditor);
|
|
var B: PEditorBinding;
|
|
Count,I: sw_integer;
|
|
L: PCustomLine;
|
|
begin
|
|
assert(Aeditor<>nil);
|
|
B:=SearchBinding(AEditor);
|
|
if Assigned(B) then
|
|
begin
|
|
Count:=GetLineCount;
|
|
for I:=0 to Count-1 do
|
|
begin
|
|
L:=GetLine(I);
|
|
if Assigned(L) then
|
|
L^.RemoveEditorInfo(AEditor);
|
|
end;
|
|
Bindings^.Free(B);
|
|
|
|
BindingsChanged;
|
|
end;
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.IsEditorBound(AEditor: PCustomCodeEditor): boolean;
|
|
begin
|
|
IsEditorBound:=SearchBinding(AEditor)<>nil;
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.GetBindingCount: sw_integer;
|
|
begin
|
|
GetBindingCount:=Bindings^.Count;
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.GetBindingIndex(AEditor: PCustomCodeEditor): sw_integer;
|
|
var B: PEditorBinding;
|
|
begin
|
|
B:=SearchBinding(AEditor);
|
|
GetBindingIndex:=Bindings^.IndexOf(B);
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.SearchBinding(AEditor: PCustomCodeEditor): PEditorBinding;
|
|
function SearchEditor(P: PEditorBinding): boolean;
|
|
begin
|
|
SearchEditor:=P^.Editor=AEditor;
|
|
end;
|
|
begin
|
|
SearchBinding:=Bindings^.FirstThat(@SearchEditor);
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.CanDispose: boolean;
|
|
begin
|
|
CanDispose:=Assigned(Bindings) and (Bindings^.Count=0);
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.GetModified: boolean;
|
|
begin
|
|
Abstract;
|
|
GetModified:=true;
|
|
end;
|
|
function TCustomCodeEditorCore.GetChangedLine: sw_integer;
|
|
begin
|
|
GetChangedLine:=ChangedLine;
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.SetModified(AModified: boolean);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.GetStoreUndo: boolean;
|
|
begin
|
|
Abstract;
|
|
GetStoreUndo:=false;
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.SetStoreUndo(AStore: boolean);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.GetSyntaxCompleted: boolean;
|
|
begin
|
|
Abstract;
|
|
GetSyntaxCompleted:=true;
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.SetSyntaxCompleted(SC : boolean);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
|
|
function TCustomCodeEditorCore.IsClipboard: Boolean;
|
|
function IsClip(P: PEditorBinding): boolean;
|
|
begin
|
|
IsClip:=(P^.Editor=Clipboard);
|
|
end;
|
|
begin
|
|
IsClipBoard:=Bindings^.FirstThat(@IsClip)<>nil;
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.GetTabSize: integer;
|
|
begin
|
|
Abstract;
|
|
GetTabSize:=0;
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.SetTabSize(ATabSize: integer);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.GetIndentSize: integer;
|
|
begin
|
|
Abstract;
|
|
GetIndentSize:=0;
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.SetIndentSize(AIndentSize: integer);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.LimitsChanged;
|
|
begin
|
|
if Locked then
|
|
LimitsChangedCalled:=true
|
|
else
|
|
DoLimitsChanged;
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.ContentsChanged;
|
|
begin
|
|
if Locked then
|
|
ContentsChangedCalled:=true
|
|
else
|
|
DoContentsChanged;
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.ModifiedChanged;
|
|
begin
|
|
if Locked then
|
|
ModifiedChangedCalled:=true
|
|
else
|
|
DoModifiedChanged;
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.TabSizeChanged;
|
|
begin
|
|
if Locked then
|
|
TabSizeChangedCalled:=true
|
|
else
|
|
DoTabSizeChanged;
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.StoreUndoChanged;
|
|
begin
|
|
if Locked then
|
|
StoreUndoChangedCalled:=true
|
|
else
|
|
DoStoreUndoChanged;
|
|
end;
|
|
|
|
|
|
procedure TCustomCodeEditorCore.BindingsChanged;
|
|
procedure CallIt(P: PEditorBinding);
|
|
begin
|
|
P^.Editor^.BindingsChanged;
|
|
end;
|
|
begin
|
|
Bindings^.ForEach(@CallIt);
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.DoLimitsChanged;
|
|
procedure CallIt(P: PEditorBinding);
|
|
begin
|
|
P^.Editor^.DoLimitsChanged;
|
|
end;
|
|
begin
|
|
Bindings^.ForEach(@CallIt);
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.DoContentsChanged;
|
|
procedure CallIt(P: PEditorBinding);
|
|
begin
|
|
P^.Editor^.ContentsChanged;
|
|
end;
|
|
begin
|
|
Bindings^.ForEach(@CallIt);
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.DoModifiedChanged;
|
|
procedure CallIt(P: PEditorBinding);
|
|
begin
|
|
P^.Editor^.ModifiedChanged;
|
|
end;
|
|
begin
|
|
Bindings^.ForEach(@CallIt);
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.DoTabSizeChanged;
|
|
procedure CallIt(P: PEditorBinding);
|
|
begin
|
|
P^.Editor^.TabSizeChanged;
|
|
end;
|
|
begin
|
|
Bindings^.ForEach(@CallIt);
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.UpdateUndoRedo(cm : word; action : byte);
|
|
procedure CallIt(P: PEditorBinding);
|
|
begin
|
|
if (P^.Editor^.State and sfActive)<>0 then
|
|
begin
|
|
P^.Editor^.UpdateUndoRedo(cm,action);
|
|
if cm=cmUndo then
|
|
begin
|
|
P^.Editor^.SetCmdState(UndoCmd,true);
|
|
P^.Editor^.SetCmdState(RedoCmd,false);
|
|
Message(Application,evBroadcast,cmCommandSetChanged,nil);
|
|
end;
|
|
end;
|
|
end;
|
|
begin
|
|
Bindings^.ForEach(@CallIt);
|
|
end;
|
|
|
|
|
|
procedure TCustomCodeEditorCore.DoStoreUndoChanged;
|
|
procedure CallIt(P: PEditorBinding);
|
|
begin
|
|
P^.Editor^.StoreUndoChanged;
|
|
end;
|
|
begin
|
|
Bindings^.ForEach(@CallIt);
|
|
end;
|
|
procedure TCustomCodeEditorCore.DoSyntaxStateChanged;
|
|
procedure CallIt(P: PEditorBinding);
|
|
begin
|
|
P^.Editor^.SyntaxStateChanged;
|
|
end;
|
|
begin
|
|
Bindings^.ForEach(@CallIt);
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.GetLastVisibleLine : sw_integer;
|
|
var
|
|
y : sw_integer;
|
|
procedure CallIt(P: PEditorBinding);
|
|
begin
|
|
if y < P^.Editor^.Delta.Y+P^.Editor^.Size.Y then
|
|
y:=P^.Editor^.Delta.Y+P^.Editor^.Size.Y;
|
|
end;
|
|
begin
|
|
y:=0;
|
|
Bindings^.ForEach(@CallIt);
|
|
GetLastVisibleLine:=y;
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.SaveToStream(Editor: PCustomCodeEditor; 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(Editor,Stream,A,B);
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.ISetLineFlagState(Binding: PEditorBinding; LineNo: sw_integer; Flag: longint; ASet: boolean);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.IGetDisplayTextFormat(Binding: PEditorBinding; LineNo: sw_integer;var DT,DF:string);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.IGetLineFormat(Binding: PEditorBinding; LineNo: sw_integer): string;
|
|
begin
|
|
Abstract;
|
|
IGetLineFormat:='';
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.ISetLineFormat(Binding: PEditorBinding; LineNo: sw_integer;const S: string);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.CharIdxToLinePos(Line,CharIdx: sw_integer): sw_integer;
|
|
var S: string;
|
|
TabSize,CP,RX,NextInc: sw_integer;
|
|
begin
|
|
S:=GetLineText(Line);
|
|
(* this would fasten the code
|
|
but UseTabCharacters is set for Editor not for EditorCore
|
|
objects,which is dangerous anyway and should be changed ... PM
|
|
if not IsFlagSet(efUseTabCharacters) then
|
|
begin
|
|
if CharIdx<=Length(S) then
|
|
CharIdxToLinePos:=CharIdx-1
|
|
else
|
|
CharIdxToLinePos:=Length(S)-1;
|
|
exit;
|
|
end; *)
|
|
|
|
TabSize:=GetTabSize;
|
|
CP:=1; RX:=0;
|
|
NextInc:=0;
|
|
while {(CP<=length(S)) and }(CP<=CharIdx) do
|
|
begin
|
|
if NextInc>0 then
|
|
Inc(RX,NextInc);
|
|
if (CP<=length(S)) and (S[CP]=TAB) then
|
|
NextInc:=TabSize-(RX mod TabSize) -1
|
|
else
|
|
NextInc:=0;
|
|
Inc(RX);
|
|
Inc(CP);
|
|
end;
|
|
CharIdxToLinePos:=RX-1;
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.LinePosToCharIdx(Line,X: sw_integer): sw_integer;
|
|
var S: string;
|
|
TabSize,CP,RX: sw_integer;
|
|
begin
|
|
TabSize:=GetTabSize;
|
|
S:=GetLineText(Line);
|
|
(*
|
|
if not IsFlagSet(efUseTabCharacters) then
|
|
begin
|
|
if S='' then
|
|
CP:=0
|
|
else if (Line<Length(S)) then
|
|
LinePosToCharIdx:=Line+1
|
|
else
|
|
LinePosToCharIdx:=Length(S);
|
|
exit;
|
|
end; *)
|
|
if S='' then
|
|
CP:=0
|
|
else
|
|
begin
|
|
CP:=0; RX:=0;
|
|
while (RX<=X) and (CP<=length(S)) do
|
|
begin
|
|
Inc(CP);
|
|
if (CP<=length(S)) and
|
|
(S[CP]=TAB) then
|
|
Inc(RX,TabSize-(RX mod TabSize))
|
|
else
|
|
Inc(RX);
|
|
end;
|
|
end;
|
|
LinePosToCharIdx:=CP;
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.GetLineCount: sw_integer;
|
|
begin
|
|
Abstract;
|
|
GetLineCount:=0;
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.GetLine(LineNo: sw_integer): PCustomLine;
|
|
begin
|
|
Abstract;
|
|
GetLine:=nil;
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.GetLineText(LineNo: sw_integer): string;
|
|
begin
|
|
Abstract;
|
|
GetLineText:='';
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.SetDisplayText(I: sw_integer;const S: string);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.GetDisplayText(I: sw_integer): string;
|
|
begin
|
|
Abstract;
|
|
GetDisplayText:='';
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.SetLineText(I: sw_integer;const S: string);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.GetDisplayTextFormat(Editor: PCustomCodeEditor; I: sw_integer;var DT,DF:string);
|
|
begin
|
|
IGetDisplayTextFormat(SearchBinding(Editor),I,DT,DF);
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.GetLineFormat(Editor: PCustomCodeEditor; I: sw_integer): string;
|
|
begin
|
|
GetLineFormat:=IGetLineFormat(SearchBinding(Editor),I);
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.SetLineFormat(Editor: PCustomCodeEditor; I: sw_integer; const S: string);
|
|
begin
|
|
ISetLineFormat(SearchBinding(Editor),I,S);
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.DeleteAllLines;
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.DeleteLine(I: sw_integer);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.InsertLine(LineNo: sw_integer; const S: string): PCustomLine;
|
|
begin
|
|
Abstract;
|
|
InsertLine:=nil; { eliminate compiler warning }
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.AddLine(const S: string);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.GetContent(ALines: PUnsortedStringCollection);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.SetContent(ALines: PUnsortedStringCollection);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.Locked: boolean;
|
|
begin
|
|
Locked:=LockFlag>0;
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.Lock(AEditor: PCustomCodeEditor);
|
|
begin
|
|
Inc(LockFlag);
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.UnLock(AEditor: PCustomCodeEditor);
|
|
begin
|
|
{$ifdef DEBUG}
|
|
if LockFlag=0 then
|
|
Bug('negative lockflag',nil)
|
|
else
|
|
{$endif DEBUG}
|
|
Dec(LockFlag);
|
|
if (LockFlag>0) then
|
|
Exit;
|
|
|
|
if LimitsChangedCalled then
|
|
begin
|
|
DoLimitsChanged;
|
|
LimitsChangedCalled:=false;
|
|
end;
|
|
|
|
if ModifiedChangedCalled then
|
|
begin
|
|
DoModifiedChanged;
|
|
ModifiedChangedCalled:=false;
|
|
end;
|
|
|
|
if TabSizeChangedCalled then
|
|
begin
|
|
DoTabSizeChanged;
|
|
TabSizeChangedCalled:=false;
|
|
end;
|
|
|
|
if StoreUndoChangedCalled then
|
|
begin
|
|
DoStoreUndoChanged;
|
|
StoreUndoChangedCalled:=false;
|
|
end;
|
|
|
|
if ContentsChangedCalled then
|
|
begin
|
|
DoContentsChanged;
|
|
ContentsChangedCalled:=false;
|
|
end;
|
|
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.UpdateAttrs(FromLine: sw_integer; Attrs: byte): sw_integer;
|
|
var MinLine: sw_integer;
|
|
procedure CallIt(P: PEditorBinding);
|
|
var I: sw_integer;
|
|
begin
|
|
I:=DoUpdateAttrs(P^.Editor,FromLine,Attrs);
|
|
if (I<MinLine) or (MinLine=-1) then MinLine:=I;
|
|
end;
|
|
begin
|
|
MinLine:=-1;
|
|
Bindings^.ForEach(@CallIt);
|
|
UpdateAttrs:=MinLine;
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.UpdateAttrsRange(FromLine, ToLine: sw_integer; Attrs: byte): sw_integer;
|
|
var MinLine: sw_integer;
|
|
procedure CallIt(P: PEditorBinding);
|
|
var I: sw_integer;
|
|
begin
|
|
I:=DoUpdateAttrsRange(P^.Editor,FromLine,ToLine,Attrs);
|
|
if (I<MinLine) or (MinLine=-1) then MinLine:=I;
|
|
end;
|
|
begin
|
|
MinLine:=-1;
|
|
Bindings^.ForEach(@CallIt);
|
|
UpdateAttrsRange:=MinLine;
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.DoUpdateAttrs(Editor: PCustomCodeEditor; FromLine: sw_integer; Attrs: byte): sw_integer;
|
|
type
|
|
TCharClass = (ccWhiteSpace,ccTab,ccAlpha,
|
|
ccNumber,ccHexNumber,ccRealNumber,
|
|
ccHash,ccSymbol);
|
|
var
|
|
SymbolIndex: Sw_integer;
|
|
CurrentCommentType : Byte;
|
|
FirstCC,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(SClass: TSpecSymbolClass; PartialMatch: TPartialType): boolean;
|
|
var S: pstring;
|
|
I: Sw_integer;
|
|
Match,Found: boolean;
|
|
begin
|
|
Found:=false;
|
|
if SymbolConcat<>'' then
|
|
for I:=1 to Editor^.GetSpecSymbolCount(SClass) do
|
|
begin
|
|
SymbolIndex:=I;
|
|
S:=Editor^.GetSpecSymbol(SClass,I-1);
|
|
if (length(SymbolConcat)<length(S^)) or
|
|
((PartialMatch=pmNone) and (length(S^)<>length(SymbolConcat)))
|
|
then
|
|
Match:=false
|
|
else
|
|
begin
|
|
case PartialMatch of
|
|
pmNone : Match:=SymbolConcat=S^;
|
|
pmRight:
|
|
Match:=copy(SymbolConcat,length(SymbolConcat)-length(S^)+1,length(S^))=S^;
|
|
else Match:=MatchSymbol(SymbolConcat,S^);
|
|
end;
|
|
end;
|
|
if Match then
|
|
begin
|
|
MatchingSymbol:=S^; Found:=true; Break;
|
|
end;
|
|
end;
|
|
MatchedSymbol:=MatchedSymbol or Found;
|
|
MatchesAnySpecSymbol:=Found;
|
|
end;
|
|
|
|
function MatchesAsmSpecSymbol(Const OrigWhat: string; SClass: TSpecSymbolClass): boolean;
|
|
var What : String;
|
|
S: pstring;
|
|
I: Sw_integer;
|
|
Match,Found: boolean;
|
|
begin
|
|
Found:=false;
|
|
What:=UpcaseStr(OrigWhat);
|
|
if What<>'' then
|
|
for I:=1 to Editor^.GetSpecSymbolCount(SClass) do
|
|
begin
|
|
SymbolIndex:=I;
|
|
S:=Editor^.GetSpecSymbol(SClass,I-1);
|
|
if (length(S^)<>length(What)) then
|
|
Match:=false
|
|
else
|
|
begin
|
|
{if CaseInsensitive then
|
|
S:=UpcaseStr(S); asm symbols need to be uppercased PM }
|
|
{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;
|
|
MatchesAsmSpecSymbol:=Found;
|
|
end;
|
|
|
|
function IsCommentPrefix: boolean;
|
|
begin
|
|
IsCommentPrefix:=MatchesAnySpecSymbol(ssCommentPrefix,pmLeft);
|
|
end;
|
|
{** **}
|
|
function IsSingleLineCommentPrefix: boolean;
|
|
begin
|
|
IsSingleLineCommentPrefix:=MatchesAnySpecSymbol(ssCommentSingleLinePrefix,pmLeft);
|
|
end;
|
|
|
|
function IsCommentSuffix: boolean;
|
|
begin
|
|
IsCommentSuffix:=(MatchesAnySpecSymbol(ssCommentSuffix,pmRight))
|
|
and (CurrentCommentType=SymbolIndex);
|
|
end;
|
|
|
|
function IsStringPrefix: boolean;
|
|
begin
|
|
IsStringPrefix:=MatchesAnySpecSymbol(ssStringPrefix,pmLeft);
|
|
end;
|
|
|
|
function IsStringSuffix: boolean;
|
|
begin
|
|
IsStringSuffix:=MatchesAnySpecSymbol(ssStringSuffix,pmRight);
|
|
end;
|
|
|
|
function IsDirectivePrefix: boolean;
|
|
begin
|
|
IsDirectivePrefix:=MatchesAnySpecSymbol(ssDirectivePrefix,pmLeft);
|
|
end;
|
|
|
|
function IsDirectiveSuffix: boolean;
|
|
begin
|
|
IsDirectiveSuffix:=MatchesAnySpecSymbol(ssDirectiveSuffix,pmRight);
|
|
end;
|
|
|
|
function IsAsmPrefix(const WordS: string): boolean;
|
|
{ var
|
|
StoredMatchedSymbol : boolean;}
|
|
begin
|
|
{StoredMatchedSymbol:=MatchedSymbol;}
|
|
IsAsmPrefix:=MatchesAsmSpecSymbol(WordS,ssAsmPrefix);
|
|
{MatchedSymbol:=StoredMatchedSymbol;}
|
|
end;
|
|
|
|
function IsAsmSuffix(const WordS: string): boolean;
|
|
{var
|
|
StoredMatchedSymbol : boolean;}
|
|
begin
|
|
{StoredMatchedSymbol:=MatchedSymbol;}
|
|
IsAsmSuffix:=MatchesAsmSpecSymbol(WordS,ssAsmSuffix);
|
|
{MatchedSymbol:=StoredMatchedSymbol;}
|
|
end;
|
|
|
|
function GetCharClass(C: char): TCharClass;
|
|
var CC: TCharClass;
|
|
begin
|
|
(*
|
|
WhiteSpaceChars {$ifdef USE_UNTYPEDSET}: set of char {$endif} = [#0,#32,#255];
|
|
TabChars {$ifdef USE_UNTYPEDSET}: set of char {$endif} = [#9];
|
|
HashChars {$ifdef USE_UNTYPEDSET}: set of char {$endif} = ['#'];
|
|
AlphaChars {$ifdef USE_UNTYPEDSET}: set of char {$endif} = ['A'..'Z','a'..'z','_'];
|
|
NumberChars {$ifdef USE_UNTYPEDSET}: set of char {$endif} = ['0'..'9'];
|
|
HexNumberChars {$ifdef USE_UNTYPEDSET}: set of char {$endif} = ['0'..'9','A'..'F','a'..'f'];
|
|
RealNumberChars {$ifdef USE_UNTYPEDSET}: set of char {$endif} = ['E','e','.'{,'+','-'}];
|
|
*)
|
|
if C in {$ifdef USE_UNTYPEDSET}[#0,#32,#255]{$else}WhiteSpaceChars{$endif} then
|
|
CC:=ccWhiteSpace
|
|
else if C in {$ifdef USE_UNTYPEDSET}[#9]{$else}TabChars{$endif} then
|
|
CC:=ccTab
|
|
else if C in {$ifdef USE_UNTYPEDSET}['#']{$else}HashChars{$endif} then
|
|
CC:=ccHash
|
|
else if (LastCC=ccHexNumber) and (C in {$ifdef USE_UNTYPEDSET}['0'..'9','A'..'F','a'..'f']{$else}HexNumberChars{$endif}) then
|
|
CC:=ccHexNumber
|
|
else if C in {$ifdef USE_UNTYPEDSET}['0'..'9']{$else}NumberChars{$endif} then
|
|
CC:=ccNumber
|
|
else if (LastCC=ccNumber) and (C in {$ifdef USE_UNTYPEDSET}['E','e','.']{$else}RealNumberChars{$endif}) then
|
|
begin
|
|
if (C='.') then
|
|
begin
|
|
if (X>=length(LineText)) or
|
|
(LineText[X+1]='.') then
|
|
cc:=ccSymbol
|
|
else
|
|
cc:=ccRealNumber;
|
|
end
|
|
else {'E','e'}
|
|
begin
|
|
if (X>=length(LineText)) or
|
|
(LineText[X+1]in ['+','-','0'..'9']) then
|
|
cc:=ccRealNumber
|
|
else
|
|
cc:=ccAlpha
|
|
end;
|
|
end
|
|
else if C in {$ifdef USE_UNTYPEDSET}['A'..'Z','a'..'z','_']{$else}AlphaChars{$endif} then CC:=ccAlpha 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 (InAsm=true) and (InComment=false) and (InString=false) and
|
|
(InDirective=false) and (SClass=ccAlpha) and IsAsmSuffix(WordS) then InAsm:=false;
|
|
if InDirective then C:=coDirectiveColor else
|
|
if InComment then C:=coCommentColor else
|
|
if InString then C:=coStringColor else
|
|
if InAsm then
|
|
begin
|
|
if (SClass=ccAlpha) and Editor^.IsAsmReservedWord(WordS) then
|
|
C:=coReservedWordColor
|
|
else
|
|
C:=coAssemblerColor;
|
|
end
|
|
else
|
|
case SClass of
|
|
ccWhiteSpace :
|
|
C:=coWhiteSpaceColor;
|
|
ccTab :
|
|
C:=coTabColor;
|
|
ccHexNumber:
|
|
C:=coHexNumberColor;
|
|
ccNumber,
|
|
ccRealNumber :
|
|
C:=coNumberColor;
|
|
ccHash :
|
|
C:=coStringColor;
|
|
ccSymbol :
|
|
C:=coSymbolColor;
|
|
ccAlpha :
|
|
begin
|
|
if Editor^.IsReservedWord(WordS) then
|
|
C:=coReservedWordColor
|
|
else
|
|
C:=coIdentifierColor;
|
|
end;
|
|
end;
|
|
if EndX+1>=StartX then
|
|
FillChar(Format[StartX],EndX+1-StartX,C);
|
|
if (InString=false) and (InAsm=false) and (InComment=false) and
|
|
(InDirective=false) and (SClass=ccAlpha) and IsAsmPrefix(WordS) then
|
|
InAsm:=true;
|
|
end;
|
|
|
|
procedure ProcessChar(C: char);
|
|
var CC: TCharClass;
|
|
EX: Sw_integer;
|
|
EndComment: pstring;
|
|
begin
|
|
CC:=GetCharClass(C);
|
|
if ClassStart=X then
|
|
FirstCC:=CC;
|
|
if ( (CC<>LastCC) and
|
|
(
|
|
((FirstCC=ccNumber) and (CC<>ccRealNumber) {and (CC<>ccNumber)}) or
|
|
(((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;
|
|
if InComment and IsCommentSuffix then
|
|
Inc(EX) else
|
|
if InString and IsStringSuffix then
|
|
Inc(EX) else
|
|
if InDirective and IsDirectiveSuffix then
|
|
Inc(EX);
|
|
end;
|
|
if CC=ccRealNumber then
|
|
Inc(EX);
|
|
if (C='$') and (MatchedSymbol=false) and (IsDirectivePrefix=false) then
|
|
CC:=ccHexNumber;
|
|
if CC<>ccSymbol then SymbolConcat:='';
|
|
FormatWord(LastCC,ClassStart,EX);
|
|
ClassStart:=EX+1;
|
|
if ClassStart=X then
|
|
FirstCC:=CC;
|
|
case CC of
|
|
ccAlpha : ;
|
|
ccNumber :
|
|
if (LastCC<>ccAlpha) then;
|
|
ccSymbol :
|
|
if (InComment=true) and (CurrentCommentType=1) and
|
|
(InDirective=false) and IsDirectivePrefix then
|
|
begin
|
|
InDirective:=true;
|
|
InComment:=false;
|
|
Dec(ClassStart,length(MatchingSymbol)-1);
|
|
end
|
|
else if (InComment=false) and
|
|
(InDirective=true) and IsDirectiveSuffix then
|
|
InDirective:=false
|
|
else if (InComment=false) and
|
|
(InString=false) and (InDirective=false) and IsCommentPrefix then
|
|
begin
|
|
InComment:=true;
|
|
CurrentCommentType:=SymbolIndex;
|
|
InSingleLineComment:=IsSingleLineCommentPrefix;
|
|
{InString:=false; }
|
|
Dec(ClassStart,length(MatchingSymbol)-1);
|
|
{ Remove (* from SymbolConcat to avoid problem with (*) PM }
|
|
{ fixes part of bug 1617 }
|
|
{ but removed proper directive prefix detection ... }
|
|
EndComment:=Editor^.GetSpecSymbol(ssCommentSuffix,SymbolIndex);
|
|
if MatchingSymbol[length(MatchingSymbol)]=EndComment^[1] then
|
|
Delete(SymbolConcat,1,length(MatchingSymbol));
|
|
end
|
|
else if InComment and IsCommentSuffix then
|
|
begin
|
|
InComment:=false;
|
|
InString:=false;
|
|
end
|
|
else if (InComment=false) and (InString=false) and IsStringPrefix then
|
|
begin
|
|
InString:=true;
|
|
Dec(ClassStart,length(MatchingSymbol)-1);
|
|
end
|
|
else if (InComment=false) and (InString=true) and IsStringSuffix then
|
|
InString:=false;
|
|
end;
|
|
if MatchedSymbol and (InComment=false) then
|
|
SymbolConcat:='';
|
|
LastCC:=CC;
|
|
end;
|
|
end;
|
|
|
|
var CurLineNr: Sw_integer;
|
|
Line,NextLine,PrevLine{,OldLine}: PCustomLine;
|
|
PrevLI,LI,nextLI: PEditorLineInfo;
|
|
begin
|
|
if (not Editor^.IsFlagSet(efSyntaxHighlight)) or (FromLine>=GetLineCount) then
|
|
begin
|
|
SetLineFormat(Editor,FromLine,'');
|
|
DoUpdateAttrs:=GetLineCount;
|
|
{$ifdef TEST_PARTIAL_SYNTAX}
|
|
LastSyntaxedLine:=GetLineCount;
|
|
if not SyntaxComplete then
|
|
begin
|
|
SyntaxComplete:=true;
|
|
DoSyntaxStateChanged;
|
|
end;
|
|
(* { no Idle necessary }
|
|
EventMask:=EventMask and not evIdle;*)
|
|
{$endif TEST_PARTIAL_SYNTAX}
|
|
Editor^.SyntaxStateChanged;
|
|
Exit;
|
|
end;
|
|
{$ifdef TEST_PARTIAL_SYNTAX}
|
|
If Editor^.IsFlagSet(efSyntaxHighlight) and (LastSyntaxedLine<FromLine)
|
|
and (FromLine<GetLineCount) then
|
|
CurLineNr:=LastSyntaxedLine
|
|
else
|
|
{$endif TEST_PARTIAL_SYNTAX}
|
|
CurLineNr:=FromLine;
|
|
if CurLineNr>0 then
|
|
PrevLine:=GetLine(CurLineNr-1)
|
|
else
|
|
PrevLine:=nil;
|
|
repeat
|
|
Line:=GetLine(CurLineNr);
|
|
if Assigned(PrevLine) then PrevLI:=PrevLine^.GetEditorInfo(Editor) else PrevLI:=nil;
|
|
if Assigned(Line) then LI:=Line^.GetEditorInfo(Editor) else LI:=nil;
|
|
InSingleLineComment:=false;
|
|
if PrevLI<>nil then
|
|
begin
|
|
InAsm:=PrevLI^.EndsWithAsm;
|
|
InComment:=PrevLI^.EndsWithComment and not PrevLI^.EndsInSingleLineComment;
|
|
CurrentCommentType:=PrevLI^.EndCommentType;
|
|
InDirective:=PrevLI^.EndsWithDirective;
|
|
end
|
|
else
|
|
begin
|
|
InAsm:=false;
|
|
InComment:=false;
|
|
CurrentCommentType:=0;
|
|
InDirective:=false;
|
|
end;
|
|
{ OldLine:=Line;}
|
|
if (not Editor^.IsFlagSet(efKeepLineAttr)) then
|
|
begin
|
|
LI^.BeginsWithAsm:=InAsm;
|
|
LI^.BeginsWithComment:=InComment;
|
|
LI^.BeginsWithDirective:=InDirective;
|
|
LI^.BeginCommentType:=CurrentCommentType;
|
|
end
|
|
else
|
|
begin
|
|
InAsm:=LI^.BeginsWithAsm;
|
|
InComment:=LI^.BeginsWithComment;
|
|
InDirective:=LI^.BeginsWithDirective;
|
|
CurrentCommentType:=LI^.BeginCommentType;
|
|
end;
|
|
LineText:=GetLineText(CurLineNr);
|
|
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(Editor,CurLineNr,Format);
|
|
LI^.EndsWithAsm:=InAsm;
|
|
LI^.EndsWithComment:=InComment;
|
|
LI^.EndsInSingleLineComment:=InSingleLineComment;
|
|
LI^.EndCommentType:=CurrentCommentType;
|
|
LI^.EndsWithDirective:=InDirective;
|
|
Inc(CurLineNr);
|
|
if CurLineNr>=GetLineCount then
|
|
Break;
|
|
NextLine:=GetLine(CurLineNr);
|
|
if Assigned(NextLine) then NextLI:=NextLine^.GetEditorInfo(Editor) else NextLI:=nil;
|
|
if ((Attrs and attrForceFull)=0) then
|
|
if (* Why should we go
|
|
(InAsm=false) and (NextLI^.BeginsWithAsm=false) and
|
|
(InComment=false) and (NextLI^.BeginsWithComment=false) and
|
|
(InDirective=false) and (NextLI^.BeginsWithDirective=false) and
|
|
{ OldLine = Line so this is nonsense}
|
|
(PrevLI^.EndsWithComment=LI^.EndsWithComment) and
|
|
(PrevLI^.EndsWithAsm=LI^.EndsWithAsm) and
|
|
(PrevLI^.EndsWithDirective=LI^.EndsWithDirective) and *)
|
|
{$ifdef TEST_PARTIAL_SYNTAX}
|
|
(CurLineNr>FromLine) and
|
|
{$endif TEST_PARTIAL_SYNTAX}
|
|
(NextLI^.BeginsWithAsm=LI^.EndsWithAsm) and
|
|
(NextLI^.BeginsWithComment=LI^.EndsWithComment) and
|
|
(NextLI^.BeginsWithDirective=LI^.EndsWithDirective) and
|
|
(NextLI^.BeginCommentType=LI^.EndCommentType) and
|
|
(NextLI^.Format<>nil) then
|
|
Break;
|
|
{$ifdef TEST_PARTIAL_SYNTAX}
|
|
if (CurLineNr<GetLineCount) and
|
|
(CurLineNr>FromLine) and
|
|
((Attrs and attrForceFull)=0) and
|
|
(CurLineNr>GetLastVisibleLine) then
|
|
begin
|
|
If SyntaxComplete then
|
|
begin
|
|
SyntaxComplete:=false;
|
|
DoSyntaxStateChanged;
|
|
end;
|
|
LastSyntaxedLine:=CurLineNr-1;
|
|
break;
|
|
end;
|
|
{$endif TEST_PARTIAL_SYNTAX}
|
|
PrevLine:=Line;
|
|
until false;
|
|
DoUpdateAttrs:=CurLineNr;
|
|
{$ifdef TEST_PARTIAL_SYNTAX}
|
|
If LastSyntaxedLine<CurLineNr-1 then
|
|
LastSyntaxedLine:=CurLineNr-1;
|
|
if CurLineNr=GetLineCount then
|
|
begin
|
|
SyntaxComplete:=true;
|
|
DoSyntaxStateChanged;
|
|
end;
|
|
{$endif TEST_PARTIAL_SYNTAX}
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.DoUpdateAttrsRange(Editor: PCustomCodeEditor; FromLine, ToLine: sw_integer;
|
|
Attrs: byte): sw_integer;
|
|
var Line: Sw_integer;
|
|
begin
|
|
Lock(Editor);
|
|
Line:=FromLine;
|
|
repeat
|
|
Line:=DoUpdateAttrs(Editor,Line,Attrs);
|
|
until (Line>=GetLineCount) or (Line>ToLine);
|
|
DoUpdateAttrsRange:=Line;
|
|
Unlock(Editor);
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.AddAction(AAction: byte; AStartPos, AEndPos: TPoint; AText: string;AFlags : longint);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.AddGroupedAction(AAction : byte);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
procedure TCustomCodeEditorCore.CloseGroupedAction(AAction : byte);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.GetUndoActionCount: sw_integer;
|
|
begin
|
|
Abstract;
|
|
GetUndoActionCount:=0;
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.GetRedoActionCount: sw_integer;
|
|
begin
|
|
Abstract;
|
|
GetRedoActionCount:=0;
|
|
end;
|
|
|
|
destructor TCustomCodeEditorCore.Done;
|
|
begin
|
|
{$ifdef DEBUG}
|
|
if Bindings^.Count>0 then
|
|
ErrorBox('Internal error: there are still '+IntToStr(Bindings^.Count)+' editors '+
|
|
'registered at TCodeEditorCode.Done!!!',nil);
|
|
{$endif}
|
|
if Assigned(Bindings) then Dispose(Bindings, Done); Bindings:=nil;
|
|
inherited Done;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.Lock;
|
|
begin
|
|
Inc(ELockFlag);
|
|
LockScreenUpdate;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.UnLock;
|
|
begin
|
|
{$ifdef DEBUG}
|
|
if Elockflag=0 then
|
|
Bug('negative lockflag',nil)
|
|
else
|
|
{$endif DEBUG}
|
|
UnlockScreenUpdate;
|
|
Dec(ELockFlag);
|
|
if (ELockFlag>0) then
|
|
Exit;
|
|
|
|
if DrawCalled then
|
|
DrawView;
|
|
|
|
If DrawCursorCalled then
|
|
Begin
|
|
DrawCursor;
|
|
DrawCursorCalled:=false;
|
|
End;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.DrawIndicator;
|
|
begin
|
|
{ Abstract }
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.AdjustSelectionPos(OldCurPosX, OldCurPosY: sw_integer; DeltaX, DeltaY: sw_integer);
|
|
var CP: TPoint;
|
|
begin
|
|
if ValidBlock=false then Exit;
|
|
|
|
CP.X:=OldCurPosX; CP.Y:=OldCurPosY;
|
|
if (PosToOfsP(SelStart)<=PosToOfsP(CP)) and (PosToOfsP(CP)<PosToOfsP(SelEnd)) then
|
|
begin
|
|
{ OldCurPos is IN selection }
|
|
if (CP.Y=SelEnd.Y) then
|
|
begin
|
|
if ((SelStart.Y<>SelEnd.Y) or (SelStart.X<=CP.X)) and
|
|
(CP.X<=SelEnd.X) then
|
|
Inc(SelEnd.X,DeltaX);
|
|
end
|
|
else if (CP.Y=SelEnd.Y+DeltaY) then
|
|
Inc(SelEnd.X,DeltaX);
|
|
Inc(SelEnd.Y,DeltaY);
|
|
SelectionChanged;
|
|
end
|
|
else
|
|
if (PosToOfsP(CP)<=PosToOfsP(SelStart)) then
|
|
begin
|
|
{ OldCurPos 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
|
|
{ OldCurPos is AFTER selection }
|
|
{ actually we don't have to do anything here }
|
|
end;
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetFlags: longint;
|
|
begin
|
|
{ Abstract }
|
|
GetFlags:=0;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.SetFlags(AFlags: longint);
|
|
begin
|
|
{ Abstract }
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetModified: boolean;
|
|
begin
|
|
{ Abstract }
|
|
GetModified:=true;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.SetModified(AModified: boolean);
|
|
begin
|
|
{ Abstract }
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetStoreUndo: boolean;
|
|
begin
|
|
{ Abstract }
|
|
GetStoreUndo:=false;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.SetStoreUndo(AStore: boolean);
|
|
begin
|
|
{ Abstract }
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetSyntaxCompleted: boolean;
|
|
begin
|
|
{ Abstract }
|
|
GetSyntaxCompleted:=true;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.SetSyntaxCompleted(SC : boolean);
|
|
begin
|
|
{ Abstract }
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetLastSyntaxedLine: sw_integer;
|
|
begin
|
|
Abstract;
|
|
GetLastSyntaxedLine:=0;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.SetLastSyntaxedLine(ALine: sw_integer);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
function TCustomCodeEditor.IsFlagSet(AFlag: longint): boolean;{$ifdef USEINLINE}inline;{$endif}
|
|
begin
|
|
IsFlagSet:=(GetFlags and AFlag)=AFlag;
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetTabSize: integer;
|
|
begin
|
|
{ Abstract }
|
|
GetTabSize:=5;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.SetTabSize(ATabSize: integer);
|
|
begin
|
|
{ Abstract }
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetIndentSize: integer;
|
|
begin
|
|
{ Abstract }
|
|
GetIndentSize:=1;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.SetIndentSize(AIndentSize: integer);
|
|
begin
|
|
{ Abstract }
|
|
end;
|
|
|
|
function TCustomCodeEditor.IsReadOnly: boolean;
|
|
begin
|
|
{ Abstract }
|
|
IsReadOnly:=false;
|
|
end;
|
|
|
|
function TCustomCodeEditor.IsClipboard: Boolean;
|
|
begin
|
|
{ Abstract }
|
|
IsClipboard:=false;
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetLineCount: sw_integer;
|
|
begin
|
|
Abstract;
|
|
GetLineCount:=0;
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetLine(LineNo: sw_integer): PCustomLine;
|
|
begin
|
|
Abstract;
|
|
GetLine:=nil;
|
|
end;
|
|
|
|
function TCustomCodeEditor.CharIdxToLinePos(Line,CharIdx: sw_integer): sw_integer;
|
|
begin
|
|
Abstract;
|
|
CharIdxToLinePos:=0;
|
|
end;
|
|
|
|
function TCustomCodeEditor.LinePosToCharIdx(Line,X: sw_integer): sw_integer;
|
|
begin
|
|
Abstract;
|
|
LinePosToCharIdx:=0;
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetLineText(I: sw_integer): string;
|
|
begin
|
|
Abstract;
|
|
GetLineText:='';
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.SetDisplayText(I: sw_integer;const S: string);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetDisplayText(I: sw_integer): string;
|
|
begin
|
|
Abstract;
|
|
GetDisplayText:='';
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.SetLineText(I: sw_integer;const S: string);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.GetDisplayTextFormat(I: sw_integer;var DT,DF:string);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetLineFormat(I: sw_integer): string;
|
|
begin
|
|
{ Abstract }
|
|
GetLineFormat:='';
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.SetLineFormat(I: sw_integer;const S: string);
|
|
begin
|
|
{ Abstract }
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.DeleteAllLines;
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.DeleteLine(I: sw_integer);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
function TCustomCodeEditor.InsertLine(LineNo: sw_integer; const S: string): PCustomLine;
|
|
begin
|
|
Abstract;
|
|
InsertLine:=nil; { eliminate compiler warning }
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.AddLine(const S: string);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetErrorMessage: string;
|
|
begin
|
|
Abstract;
|
|
GetErrorMessage:='';
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.SetErrorMessage(const S: string);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.GetContent(ALines: PUnsortedStringCollection);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.SetContent(ALines: PUnsortedStringCollection);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
function TCustomCodeEditor.LoadFromStream(Stream: PFastBufStream): boolean;
|
|
begin
|
|
Abstract;
|
|
LoadFromStream:=false;
|
|
end;
|
|
|
|
function TCustomCodeEditor.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 TCustomCodeEditor.SaveAreaToStream(Stream: PStream; StartP,EndP: TPoint): boolean;
|
|
begin
|
|
Abstract;
|
|
SaveAreaToStream:=false;
|
|
end;
|
|
|
|
function TCustomCodeEditor.LoadFromFile(const AFileName: string): boolean;
|
|
var S: PFastBufStream;
|
|
OK: boolean;
|
|
begin
|
|
New(S, Init(AFileName,stOpenRead,EditorTextBufSize));
|
|
OK:=Assigned(S);
|
|
{$ifdef TEST_PARTIAL_SYNTAX}
|
|
SetSyntaxCompleted(false);
|
|
{ Idle necessary }
|
|
EventMask:=EventMask or evIdle;
|
|
{$endif TEST_PARTIAL_SYNTAX}
|
|
if OK then OK:=LoadFromStream(S);
|
|
if Assigned(S) then Dispose(S, Done);
|
|
LoadFromFile:=OK;
|
|
end;
|
|
|
|
function TCustomCodeEditor.SaveToFile(const AFileName: string): boolean;
|
|
var OK: boolean;
|
|
S: PBufStream;
|
|
begin
|
|
New(S, Init(AFileName,stCreate,EditorTextBufSize));
|
|
OK:=Assigned(S) and (S^.Status=stOK);
|
|
if OK then OK:=SaveToStream(S);
|
|
if Assigned(S) then Dispose(S, Done);
|
|
SaveToFile:=OK;
|
|
end;
|
|
|
|
|
|
function TCustomCodeEditor.InsertFrom(Editor: PCustomCodeEditor): Boolean;
|
|
var OK: boolean;
|
|
CP,RX,RSX,LineDelta,LineCount: Sw_integer;
|
|
StartPos,DestPos,BPos,EPos: TPoint;
|
|
LineStartX,LineEndX: Sw_integer;
|
|
TabSize,CharIdxStart,CharIdxEnd: Sw_integer;
|
|
S,DS,BeforeS,OrigS,AfterS: string;
|
|
VerticalBlock: boolean;
|
|
SEnd: TPoint;
|
|
begin
|
|
if Editor^.IsFlagSet(efVerticalBlocks) then
|
|
begin
|
|
NotImplemented;
|
|
Exit;
|
|
end;
|
|
Lock;
|
|
|
|
{ every data in the clipboard gets a new line }
|
|
if (Clipboard=@Self) and (CurPos.X>0) then
|
|
InsertNewLine;
|
|
|
|
OK:=(Editor^.SelStart.X<>Editor^.SelEnd.X) or (Editor^.SelStart.Y<>Editor^.SelEnd.Y);
|
|
if OK then
|
|
begin
|
|
StartPos:=CurPos; DestPos:=CurPos;
|
|
EPos:=CurPos;
|
|
VerticalBlock:=Editor^.IsFlagSet(efVerticalBlocks);
|
|
LineDelta:=0; LineCount:=(Editor^.SelEnd.Y-Editor^.SelStart.Y)+1;
|
|
OK:=GetLineCount<MaxLineCount;
|
|
OrigS:=GetLineText(DestPos.Y);
|
|
BeforeS:=Copy(OrigS,1,LinePosToCharIdx(DestPos.Y,DestPos.X-1));
|
|
{ we might need to add some spaces here,
|
|
but how many ? }
|
|
TabSize:=GetTabSize;
|
|
CP:=1; RX:=0;
|
|
while (CP<=length(BeforeS)) do
|
|
begin
|
|
if (BeforeS[CP]=TAB) then
|
|
Inc(RX,TabSize-(RX mod TabSize))
|
|
else
|
|
Inc(RX);
|
|
Inc(CP);
|
|
end;
|
|
BeforeS:=BeforeS+CharStr(' ',DestPos.X-RX);
|
|
AfterS:=Copy(OrigS,LinePosToCharIdx(DestPos.Y,DestPos.X),High(OrigS));
|
|
BPos:=CurPos;
|
|
while OK and (LineDelta<LineCount) do
|
|
begin
|
|
if (LineDelta>0) and (VerticalBlock=false) then
|
|
begin
|
|
InsertLine(DestPos.Y,'');
|
|
EPOS.X:=0;EPos.Y:=DestPos.Y;
|
|
AddAction(eaInsertLine,BPos,EPos,'',GetFlags);
|
|
LimitsChanged;
|
|
end;
|
|
|
|
If LineDelta>0 then
|
|
BeforeS:='';
|
|
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:=High(S);
|
|
|
|
CharIdxStart:=Editor^.LinePosToCharIdx(Editor^.SelStart.Y+LineDelta,LineStartX);
|
|
CharIdxEnd:=Editor^.LinePosToCharIdx(Editor^.SelStart.Y+LineDelta,LineEndX);
|
|
if LineEndX<LineStartX then
|
|
S:=''
|
|
else if VerticalBlock then
|
|
S:=RExpand(copy(Editor^.GetLineText(Editor^.SelStart.Y+LineDelta),CharIdxStart,CharIdxEnd-CharIdxStart+1),
|
|
Min(CharIdxEnd-CharIdxStart+1,High(S)))
|
|
else
|
|
S:=copy(Editor^.GetLineText(Editor^.SelStart.Y+LineDelta),CharIdxStart,CharIdxEnd-CharIdxStart+1);
|
|
if VerticalBlock=false then
|
|
begin
|
|
DS:=BeforeS+S;
|
|
CP:=1; RX:=0;
|
|
RSX :=0;
|
|
while (CP<=length(DS)) do
|
|
begin
|
|
if (DS[CP]=TAB) then
|
|
Inc(RX,TabSize-(RX mod TabSize))
|
|
else
|
|
Inc(RX);
|
|
if CP=length(BeforeS) then
|
|
RSX:=RX;
|
|
Inc(CP);
|
|
end;
|
|
|
|
if LineDelta=LineCount-1 then
|
|
begin
|
|
SetLineText(DestPos.Y,DS+AfterS);
|
|
BPos.X:=DestPos.X;BPos.Y:=DestPos.Y;
|
|
EPOS.X:=DestPos.X+RX-RSX;EPos.Y:=DestPos.Y;
|
|
AddAction(eaInsertText,BPos,EPos,S,GetFlags);
|
|
end
|
|
else
|
|
begin
|
|
SetLineText(DestPos.Y,DS);
|
|
BPos.X:=DestPos.X;BPos.Y:=DestPos.Y;
|
|
EPOS.X:=DestPos.X+RX-RSX;EPos.Y:=DestPos.Y;
|
|
AddAction(eaInsertText,BPos,EPos,S,GetFlags);
|
|
end;
|
|
BPos.X:=EPos.X;
|
|
if LineDelta=LineCount-1 then
|
|
begin
|
|
SEnd.Y:=DestPos.Y;
|
|
SEnd.X:=DestPos.X+RX-RSX;
|
|
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 not OK then EditorDialog(edTooManyLines,nil);
|
|
{ mainly to force eaMove insertion }
|
|
if not IsClipboard then
|
|
SetCurPtr(EPos.X,EPos.Y);
|
|
SetCurPtr(StartPos.X,StartPos.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 TCustomCodeEditor.InsertText(const S: string): Boolean;
|
|
var I: sw_integer;
|
|
OldPos: TPoint;
|
|
HoldUndo : boolean;
|
|
begin
|
|
Lock;
|
|
OldPos:=CurPos;
|
|
HoldUndo:=GetStoreUndo;
|
|
SetStoreUndo(false);
|
|
for I:=1 to length(S) do
|
|
AddChar(S[I]);
|
|
InsertText:=true;
|
|
SetStoreUndo(HoldUndo);
|
|
AddAction(eaInsertText,OldPos,CurPos,S,GetFlags);
|
|
UnLock;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.ModifiedChanged;
|
|
begin
|
|
{ Abstract }
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.PositionChanged;
|
|
begin
|
|
{ Abstract }
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.TabSizeChanged;
|
|
begin
|
|
{ Abstract }
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.SyntaxStateChanged;
|
|
begin
|
|
{ Abstract }
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.StoreUndoChanged;
|
|
begin
|
|
{ Abstract }
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetSpecSymbolCount(SpecClass: TSpecSymbolClass): integer;
|
|
begin
|
|
{ Abstract }
|
|
GetSpecSymbolCount:=0;
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetSpecSymbol(SpecClass: TSpecSymbolClass; Index: integer): pstring;
|
|
begin
|
|
Abstract;
|
|
GetSpecSymbol:=nil;
|
|
end;
|
|
|
|
function TCustomCodeEditor.IsReservedWord(const S: string): boolean;
|
|
begin
|
|
{ Abstract }
|
|
IsReservedWord:=false;
|
|
end;
|
|
|
|
function TCustomCodeEditor.IsAsmReservedWord(const S: string): boolean;
|
|
begin
|
|
{ Abstract }
|
|
IsAsmReservedWord:=false;
|
|
end;
|
|
|
|
function TCustomCodeEditor.TranslateCodeTemplate(var Shortcut: string; ALines: PUnsortedStringCollection): boolean;
|
|
begin
|
|
{ Abstract }
|
|
TranslateCodeTemplate:=false;
|
|
end;
|
|
|
|
function TCustomCodeEditor.CompleteCodeWord(const WordS: string; var Text: string): boolean;
|
|
begin
|
|
{ Abstract }
|
|
Text:='';
|
|
CompleteCodeWord:=false;
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetCodeCompleteWord: string;
|
|
begin
|
|
{ Abstract }
|
|
GetCodeCompleteWord:='';
|
|
end;
|
|
|
|
function TCustomCodeEditor.CreateFold(StartY,EndY: sw_integer; Collapsed: boolean): boolean;
|
|
var F,ParentF: PFold;
|
|
L: PCustomLine;
|
|
EI: PEditorLineInfo;
|
|
Y: sw_integer;
|
|
OK: boolean;
|
|
begin
|
|
OK:=true;
|
|
Lock;
|
|
for Y:=StartY to EndY do
|
|
begin
|
|
L:=GetLine(Y);
|
|
if assigned(L) then
|
|
EI:=L^.GetEditorInfo(@Self)
|
|
else
|
|
begin
|
|
CreateFold:=False;
|
|
exit;
|
|
end;
|
|
if Y=StartY then
|
|
ParentF:=EI^.Fold
|
|
else
|
|
OK:=OK and (EI^.Fold=ParentF);
|
|
if not OK then
|
|
Break;
|
|
end;
|
|
if OK then
|
|
begin
|
|
New(F, Init(@Self,ParentF,Collapsed));
|
|
for Y:=StartY to EndY do
|
|
GetLine(Y)^.GetEditorInfo(@Self)^.SetFold(F);
|
|
DrawView;
|
|
end;
|
|
UnLock;
|
|
CreateFold:=OK;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.FoldChanged(Fold: PFold);
|
|
var F: PFold;
|
|
I: sw_integer;
|
|
begin
|
|
for I:=0 to GetFoldCount-1 do
|
|
begin
|
|
F:=GetFold(I);
|
|
if F^.ParentFold=Fold then
|
|
FoldChanged(F);
|
|
end;
|
|
if Fold^.IsCollapsed then
|
|
begin
|
|
F:=GetLineFold(CurPos.Y); I:=CurPos.Y;
|
|
if F=Fold then
|
|
begin
|
|
while GetLineFold(I-1)=Fold do
|
|
Dec(I);
|
|
if I<>CurPos.Y then
|
|
SetCurPtr(CurPos.X,I);
|
|
end;
|
|
end;
|
|
DrawView;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.RemoveAllFolds;
|
|
var I: sw_integer;
|
|
L: PCustomLine;
|
|
begin
|
|
|
|
for I:=0 to GetLineCount-1 do
|
|
begin
|
|
L:=GetLine(I);
|
|
if not assigned(L) then exit;
|
|
with L^ do
|
|
with GetEditorInfo(@Self)^ do
|
|
SetFold(nil);
|
|
end;
|
|
DrawView;
|
|
end;
|
|
|
|
{ to be called if CurPos has already been changed }
|
|
|
|
procedure TCustomCodeEditor.AdjustSelection(DeltaX, DeltaY: sw_integer);
|
|
begin
|
|
AdjustSelectionPos(CurPos.X-DeltaX,CurPos.Y-DeltaY,DeltaX,DeltaY);
|
|
end;
|
|
|
|
{ to be called if CurPos has not yet been changed }
|
|
|
|
procedure TCustomCodeEditor.AdjustSelectionBefore(DeltaX, DeltaY: sw_integer);
|
|
begin
|
|
AdjustSelectionPos(CurPos.X,CurPos.Y,DeltaX,DeltaY);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.TrackCursor(centre:Tcentre);
|
|
var D,CP: TPoint;
|
|
begin
|
|
D:=Delta;
|
|
EditorToViewPoint(D,D); EditorToViewPoint(CurPos,CP);
|
|
if CP.Y<Delta.Y then D.Y:=CP.Y else
|
|
if CP.Y>Delta.Y+Size.Y-1 then D.Y:=CP.Y-Size.Y+1;
|
|
if CP.X<Delta.X then D.X:=CP.X else
|
|
if CP.X>Delta.X+Size.X-1 then D.X:=CP.X-Size.X+1;
|
|
if {((Delta.X<>D.X) or (Delta.Y<>D.Y)) and }centre=do_centre then
|
|
begin
|
|
{ loose centering for debugger PM }
|
|
while (CP.Y-D.Y)<(Size.Y div 3) do Dec(D.Y);
|
|
while (CP.Y-D.Y)>2*(Size.Y div 3) do Inc(D.Y);
|
|
end;
|
|
ViewToEditorPoint(D,D);
|
|
if (Delta.X<>D.X) or (Delta.Y<>D.Y) then
|
|
ScrollTo(D.X,D.Y);
|
|
DrawCursor;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.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;
|
|
|
|
function TCustomCodeEditor.IsModal: boolean;
|
|
var IsM: boolean;
|
|
begin
|
|
IsM:=GetState(sfModal);
|
|
if Assigned(Owner) then
|
|
IsM:=IsM or Owner^.GetState(sfModal);
|
|
IsModal:=IsM;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.FlagsChanged(OldFlags: longint);
|
|
var I: sw_integer;
|
|
begin
|
|
Lock;
|
|
if ((OldFlags xor GetFlags) and efCodeComplete)<>0 then
|
|
ClearCodeCompleteWord;
|
|
SetInsertMode(IsFlagSet(efInsertMode));
|
|
if ((OldFlags xor GetFlags) and efFolds)<>0 then
|
|
if not IsFlagSet(efFolds) then
|
|
RemoveAllFolds;
|
|
if IsFlagSet(efSyntaxHighlight) then
|
|
UpdateAttrs(0,attrAll) else
|
|
for I:=0 to GetLineCount-1 do
|
|
SetLineFormat(I,'');
|
|
DrawView;
|
|
UnLock;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.LimitsChanged;
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.DoLimitsChanged;
|
|
begin
|
|
SetLimit(MaxLineLength+1,EditorToViewLine(GetLineCount));
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.BindingsChanged;
|
|
begin
|
|
{ Abstract }
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.ContentsChanged;
|
|
begin
|
|
DrawView;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.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 TCustomCodeEditor.SetLineFlagState(LineNo: sw_integer; Flags: longint; ASet: boolean);
|
|
var L: PCustomLine;
|
|
begin
|
|
{ Avoid crashes if file was shorten for instance }
|
|
if LineNo>=GetLineCount then
|
|
exit;
|
|
L:=GetLine(LineNo);
|
|
if Assigned(L) then
|
|
with L^ do
|
|
if ASet then
|
|
SetFlags(GetFlags or Flags)
|
|
else
|
|
SetFlags(GetFlags and not Flags);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.SetLineFlagExclusive(Flags: longint; LineNo: sw_integer);
|
|
var I,Count: sw_integer;
|
|
L: PCustomLine;
|
|
begin
|
|
Lock;
|
|
Count:=GetLineCount;
|
|
for I:=0 to Count-1 do
|
|
begin
|
|
L:=GetLine(I);
|
|
if not assigned(L) then break;
|
|
if I=LineNo then
|
|
L^.SetFlags(L^.GetFlags or Flags)
|
|
else
|
|
L^.SetFlags(L^.GetFlags and (not Flags));
|
|
end;
|
|
UnLock;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.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);
|
|
Dec(P.X,GetReservedColCount);
|
|
if P.X<0 then P.X:=0;
|
|
if P.Y<0 then P.Y:=0;
|
|
end;
|
|
type TCCAction = (ccCheck,ccClear,ccDontCare);
|
|
var
|
|
StartP,P: TPoint;
|
|
E: TEvent;
|
|
OldEvent : PEvent;
|
|
CCAction: TCCAction;
|
|
begin
|
|
CCAction:=ccClear;
|
|
E:=Event;
|
|
OldEvent:=CurEvent;
|
|
if (E.What and (evMouse or evKeyboard))<>0 then
|
|
CurEvent:=@E;
|
|
if (InASCIIMode=false) or (Event.What<>evKeyDown) then
|
|
if (Event.What<>evKeyDown) or (Event.KeyCode<>kbEnter) or (IsReadOnly=false) then
|
|
if (Event.What<>evKeyDown) or
|
|
((Event.KeyCode<>kbEnter) and (Event.KeyCode<>kbEsc)) or
|
|
(GetCompleteState<>csOffering) 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 }
|
|
{ this is supposed to enable entering of ASCII chars below 32,
|
|
which are normally interpreted as control chars. So, when you enter
|
|
Alt+24 (on the numeric pad) then this will normally move the cursor
|
|
one line down, but if you do it in ASCII mode (also after Ctrl+B)
|
|
then this will insert the ASCII #24 char (upper arrow) in the
|
|
source code. - Gabor }
|
|
if InASCIIMode {and (Event.CharCode<>0)} then
|
|
begin
|
|
AddChar(Event.CharCode);
|
|
if (GetCompleteState<>csDenied) or (Event.CharCode=#32) then
|
|
CCAction:=ccCheck
|
|
else
|
|
CCAction:=ccClear;
|
|
end
|
|
else
|
|
begin
|
|
DontClear:=false;
|
|
case Event.KeyCode of
|
|
kbAltF10 :
|
|
Message(@Self,evCommand,cmLocalMenu,@Self);
|
|
kbEnter :
|
|
if IsReadOnly then
|
|
DontClear:=true else
|
|
if GetCompleteState=csOffering then
|
|
CodeCompleteApply
|
|
else
|
|
Message(@Self,evCommand,cmNewLine,nil);
|
|
kbEsc :
|
|
if GetCompleteState=csOffering then
|
|
CodeCompleteCancel else
|
|
if IsModal then
|
|
DontClear:=true;
|
|
else
|
|
case Event.CharCode of
|
|
#9,#32..#255 :
|
|
if (Event.CharCode=#9) and IsModal then
|
|
DontClear:=true
|
|
else
|
|
begin
|
|
NoSelect:=true;
|
|
AddChar(Event.CharCode);
|
|
NoSelect:=false;
|
|
if (GetCompleteState<>csDenied) or (Event.CharCode=#32) then
|
|
CCAction:=ccCheck
|
|
else
|
|
CCAction:=ccClear;
|
|
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;
|
|
cmAddChar : AddChar(chr(longint(Event.InfoPtr)));
|
|
cmCharLeft : CharLeft;
|
|
cmCharRight : CharRight;
|
|
cmWordLeft : WordLeft;
|
|
cmWordRight : WordRight;
|
|
cmLineStart : LineStart;
|
|
cmLineEnd : LineEnd;
|
|
cmLineUp : LineUp;
|
|
cmLineDown : LineDown;
|
|
cmPageUp : PageUp;
|
|
cmPageDown : PageDown;
|
|
cmTextStart : TextStart;
|
|
cmTextEnd : TextEnd;
|
|
cmWindowStart : WindowStart;
|
|
cmWindowEnd : WindowEnd;
|
|
cmNewLine : begin
|
|
InsertNewLine;
|
|
TrackCursor(do_not_centre);
|
|
end;
|
|
cmBreakLine : BreakLine;
|
|
cmBackSpace : BackSpace;
|
|
cmDelChar : DelChar;
|
|
cmDelWord : DelWord;
|
|
cmDelToEndOfWord : DelToEndOfWord;
|
|
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;
|
|
cmFindMatchingDelimiter : FindMatchingDelimiter(true);
|
|
cmFindMatchingDelimiterBack : FindMatchingDelimiter(false);
|
|
cmUpperCase : UpperCase;
|
|
cmLowerCase : LowerCase;
|
|
cmWordLowerCase : WordLowerCase;
|
|
cmWordUpperCase : WordUpperCase;
|
|
cmInsertOptions : InsertOptions;
|
|
cmToggleCase : ToggleCase;
|
|
cmCreateFold : CreateFoldFromBlock;
|
|
cmToggleFold : ToggleFold;
|
|
cmExpandFold : ExpandFold;
|
|
cmCollapseFold : CollapseFold;
|
|
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;
|
|
|
|
cmSelectAll : SelectAll(true);
|
|
cmUnselect : SelectAll(false);
|
|
{$ifdef WinClipSupported}
|
|
cmCopyWin : ClipCopyWin;
|
|
cmPasteWin : ClipPasteWin;
|
|
{$endif WinClipSupported}
|
|
cmUndo : Undo;
|
|
cmRedo : Redo;
|
|
cmClear : DelSelect;
|
|
cmExpandCodeTemplate: ExpandCodeTemplate;
|
|
cmLocalMenu :
|
|
begin
|
|
P:=CurPos; Inc(P.X); Inc(P.Y);
|
|
LocalMenu(P);
|
|
end;
|
|
cmActivateMenu :
|
|
Message(Application,evCommand,cmMenu,nil);
|
|
else
|
|
begin
|
|
DontClear:=true;
|
|
CCAction:=ccDontCare;
|
|
end;
|
|
end;
|
|
if DontClear=false then
|
|
ClearEvent(Event);
|
|
end;
|
|
{$ifdef TEST_PARTIAL_SYNTAX}
|
|
evIdle :
|
|
begin
|
|
CCAction:=ccDontCare;
|
|
{ Complete syntax by 20 lines increment }
|
|
{ could already be quite lengthy on slow systems }
|
|
if not GetSyntaxCompleted then
|
|
UpdateAttrsRange(GetLastSyntaxedLine,GetLastSyntaxedLine+20,AttrAll);
|
|
end;
|
|
{$endif TEST_PARTIAL_SYNTAX}
|
|
evBroadcast :
|
|
begin
|
|
CCAction:=ccDontCare;
|
|
case Event.Command of
|
|
cmUpdate :
|
|
Update;
|
|
cmClearLineHighlights :
|
|
SetLineFlagExclusive(lfHighlightRow,-1);
|
|
cmResetDebuggerRow :
|
|
SetLineFlagExclusive(lfDebuggerRow,-1);
|
|
cmScrollBarChanged:
|
|
if (Event.InfoPtr = HScrollBar) or
|
|
(Event.InfoPtr = VScrollBar) then
|
|
begin
|
|
CheckScrollBar(HScrollBar, Delta.X);
|
|
CheckScrollBar(VScrollBar, Delta.Y);
|
|
end;
|
|
end;
|
|
end;
|
|
else CCAction:=ccDontCare;
|
|
end;
|
|
inherited HandleEvent(Event);
|
|
CurEvent:=OldEvent;
|
|
case CCAction of
|
|
ccCheck : CodeCompleteCheck;
|
|
ccClear : ClearCodeCompleteWord;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.UpdateUndoRedo(cm : word; action : byte);
|
|
var UndoMenu : PMenuItem;
|
|
begin
|
|
UndoMenu:=PAdvancedMenuBar(MenuBar)^.GetMenuItem(cm);
|
|
if assigned(UndoMenu) then
|
|
begin
|
|
If assigned(UndoMenu^.Param) then
|
|
DisposeStr(UndoMenu^.Param);
|
|
if action<lastaction then
|
|
UndoMenu^.Param:=NewStr(ActionString[action]);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TCustomCodeEditor.Update;
|
|
begin
|
|
Lock;
|
|
LimitsChanged;
|
|
SelectionChanged;
|
|
HighlightChanged;
|
|
UnLock;
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetLocalMenu: PMenu;
|
|
begin
|
|
GetLocalMenu:=nil;
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetCommandTarget: PView;
|
|
begin
|
|
GetCommandTarget:=@Self;
|
|
end;
|
|
|
|
function TCustomCodeEditor.CreateLocalMenuView(var Bounds: TRect; M: PMenu): PMenuPopup;
|
|
var MV: PMenuPopup;
|
|
begin
|
|
New(MV, Init(Bounds, M));
|
|
CreateLocalMenuView:=MV;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.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;
|
|
|
|
function TCustomCodeEditor.GetReservedColCount: sw_integer;
|
|
var LSX: sw_integer;
|
|
begin
|
|
if IsFlagSet(efFolds) then LSX:=GetFoldStringWidth else LSX:=0;
|
|
GetReservedColCount:=LSX;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.Draw;
|
|
function GetEIFold(EI: PEditorLineInfo): PFold;
|
|
begin
|
|
if Assigned(EI) then GetEIFold:=EI^.Fold else GetEIFold:=nil;
|
|
end;
|
|
var SelectColor,
|
|
HighlightColColor,
|
|
HighlightRowColor,
|
|
ErrorMessageColor : word;
|
|
B: TDrawBuffer;
|
|
X,Y,AX,AY,MaxX,LSX: sw_integer;
|
|
PX: TPoint;
|
|
LineCount: sw_integer;
|
|
Line: PCustomLine;
|
|
LineText,Format: string;
|
|
isBreak : boolean;
|
|
C: char;
|
|
FreeFormat: array[0..MaxLineLength] 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;
|
|
var
|
|
FoldPrefix,FoldSuffix: string;
|
|
{ SkipLine: boolean;}
|
|
{ FoldStartLine: sw_integer;}
|
|
begin
|
|
if ELockFlag>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);
|
|
ColorTab[coAsmReservedColor]:=GetColor(17);
|
|
SelectColor:=GetColor(10);
|
|
HighlightColColor:=GetColor(11);
|
|
HighlightRowColor:=GetColor(12);
|
|
ErrorMessageColor:=GetColor(16);
|
|
{$ifdef TEST_PARTIAL_SYNTAX}
|
|
If (not GetSyntaxCompleted) and (GetLastSyntaxedLine<Delta.Y+Size.Y) then
|
|
UpdateAttrsRange(GetLastSyntaxedLine,Delta.Y+Size.Y,AttrAll);
|
|
{$endif TEST_PARTIAL_SYNTAX}
|
|
LSX:=GetReservedColCount;
|
|
Y:=0; AY:=Delta.Y;
|
|
for Y:=0 to Size.Y-1 do
|
|
begin
|
|
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:=ViewToEditorLine(Delta.Y+Y);
|
|
if (0<=AY) and (AY<LineCount) then
|
|
begin
|
|
Line:=GetLine(AY);
|
|
if assigned(Line) then
|
|
begin
|
|
IsBreak:=Line^.IsFlagSet(lfBreakpoint);
|
|
end
|
|
else
|
|
begin
|
|
IsBreak:=false;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
Line:=nil;
|
|
IsBreak:=false;
|
|
end;
|
|
|
|
begin
|
|
Color:=ColorTab[coTextColor];
|
|
FillChar(FreeFormat,SizeOf(FreeFormat),1);
|
|
MoveChar(B,' ',Color,Size.X);
|
|
GetDisplayTextFormat(AY,LineText,Format);
|
|
|
|
{ if FlagSet(efSyntaxHighlight) then MaxX:=length(LineText)+1
|
|
else }MaxX:=Size.X+Delta.X;
|
|
for X:=1 to Min(MaxX,High(LineText)) 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
|
|
{ there's a highlight }
|
|
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 IsFlagSet(efVerticalBlocks) 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; { no highlight }
|
|
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 IsFlagSet(efHighlightRow) and
|
|
(PX.Y=CurPos.Y) then
|
|
begin
|
|
Color:=CombineColors(Color,HighlightRowColor);
|
|
FreeFormat[X]:=false;
|
|
end;
|
|
if IsFlagSet(efHighlightColumn) and (PX.X=CurPos.X) then
|
|
begin
|
|
Color:=CombineColors(Color,HighlightColColor);
|
|
FreeFormat[X]:=false;
|
|
end;
|
|
|
|
if Assigned(Line) and Line^.IsFlagSet(lfHighlightRow) then
|
|
begin
|
|
Color:=CombineColors(Color,HighlightRowColor);
|
|
FreeFormat[X]:=false;
|
|
end;
|
|
if isbreak then
|
|
begin
|
|
Color:=ColorTab[coBreakColor];
|
|
FreeFormat[X]:=false;
|
|
end;
|
|
if Assigned(Line) and Line^.isFlagSet(lfDebuggerRow) then
|
|
begin
|
|
Color:=CombineColors(Color,HighlightRowColor);
|
|
FreeFormat[X]:=false;
|
|
end;
|
|
|
|
if (0<=LSX+X-1-Delta.X) and (LSX+X-1-Delta.X<MaxViewWidth) then
|
|
MoveChar(B[LSX+X-1-Delta.X],C,Color,1);
|
|
end; { for X:=1 to ... }
|
|
if IsFlagSet(efFolds) then
|
|
begin
|
|
GetFoldStrings(AY,FoldPrefix,FoldSuffix);
|
|
MoveStr(B[0],FoldPrefix,ColorTab[coTextColor]);
|
|
if FoldSuffix<>'' then
|
|
MoveStr(B[Size.X-1-length(FoldSuffix)],FoldSuffix,ColorTab[coTextColor]);
|
|
end;
|
|
WriteLine(0,Y,Size.X,1,B);
|
|
end; { if not SkipLine ... }
|
|
end; { not errorline }
|
|
end; { while (Y<Size.Y) ... }
|
|
DrawCursor;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.DrawCursor;
|
|
begin
|
|
if Elockflag>0 then
|
|
DrawCursorCalled:=true
|
|
else
|
|
begin
|
|
SetCursor(GetReservedColCount+CurPos.X-Delta.X,EditorToViewLine(CurPos.Y)-Delta.Y);
|
|
SetState(sfCursorIns,Overwrite);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.ResetCursor;
|
|
begin
|
|
if Elockflag>0 then
|
|
begin
|
|
DrawCursorCalled:=true;
|
|
exit;
|
|
end
|
|
else
|
|
inherited ResetCursor;
|
|
end;
|
|
|
|
function TCustomCodeEditor.Overwrite: boolean;
|
|
begin
|
|
Overwrite:=not IsFlagSet(efInsertMode);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.SetCodeCompleteWord(const S: string);
|
|
begin
|
|
if S<>'' then
|
|
SetCompleteState(csOffering)
|
|
else
|
|
SetCompleteState(csInactive);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.ClearCodeCompleteWord;
|
|
begin
|
|
SetCodeCompleteWord('');
|
|
SetCompleteState(csInactive);
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetCompleteState: TCompleteState;
|
|
begin
|
|
{ Abstract }
|
|
GetCompleteState:=csInactive;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.SetCompleteState(AState: TCompleteState);
|
|
begin
|
|
{ Abstract }
|
|
end;
|
|
|
|
function TCustomCodeEditor.UpdateAttrs(FromLine: sw_integer; Attrs: byte): sw_integer;
|
|
begin
|
|
Abstract;
|
|
UpdateAttrs:=-1;
|
|
end;
|
|
|
|
function TCustomCodeEditor.UpdateAttrsRange(FromLine, ToLine: sw_integer; Attrs: byte): sw_integer;
|
|
begin
|
|
Abstract;
|
|
UpdateAttrsRange:=-1;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.AddAction(AAction: byte; AStartPos, AEndPos: TPoint; AText: string;AFlags : longint);
|
|
begin
|
|
{ Abstract }
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.AddGroupedAction(AAction : byte);
|
|
begin
|
|
{ Abstract }
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.CloseGroupedAction(AAction : byte);
|
|
begin
|
|
{ Abstract }
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetUndoActionCount: sw_integer;
|
|
begin
|
|
{ Abstract }
|
|
GetUndoActionCount:=0;
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetRedoActionCount: sw_integer;
|
|
begin
|
|
{ Abstract }
|
|
GetRedoActionCount:=0;
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetMaxFoldLevel: sw_integer;
|
|
var Max,L,I: sw_integer;
|
|
begin
|
|
Max:=0;
|
|
for I:=0 to GetFoldCount-1 do
|
|
begin
|
|
L:=GetFold(I)^.GetLevel;
|
|
if L>Max then Max:=L;
|
|
end;
|
|
GetMaxFoldLevel:=Max;
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetFoldStringWidth: sw_integer;
|
|
begin
|
|
GetFoldStringWidth:=GetMaxFoldLevel;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.GetFoldStrings(EditorLine: sw_integer; var Prefix, Suffix: openstring);
|
|
var F: PFold;
|
|
C: char;
|
|
begin
|
|
Prefix:=CharStr(' ',GetFoldStringWidth); Suffix:='';
|
|
F:=GetLineFold(EditorLine);
|
|
if Assigned(F) then
|
|
begin
|
|
if F^.Collapsed_ then C:=#27 else C:=#26;
|
|
Prefix[1+F^.GetLevel]:=C;
|
|
if F^.Collapsed_ then
|
|
Suffix:='('+IntToStr(F^.GetLineCount)+')';
|
|
end;
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetFoldCount: sw_integer;
|
|
begin
|
|
GetFoldCount:=0;
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetFold(Index: sw_integer): PFold;
|
|
begin
|
|
GetFold:=nil;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.RegisterFold(AFold: PFold);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.UnRegisterFold(AFold: PFold);
|
|
begin
|
|
Abstract;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.Indent;
|
|
var S, PreS: string;
|
|
Shift: integer;
|
|
begin
|
|
S:=GetLineText(CurPos.Y);
|
|
if CurPos.Y>0 then
|
|
PreS:=RTrim(GetLineText(CurPos.Y-1),not IsFlagSet(efUseTabCharacters))
|
|
else
|
|
PreS:='';
|
|
if CurPos.X>=length(PreS) then
|
|
Shift:=GetTabSize
|
|
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,High(S)));
|
|
SetCurPtr(CurPos.X+Shift,CurPos.Y);
|
|
UpdateAttrs(CurPos.Y,attrAll);
|
|
DrawLines(CurPos.Y);
|
|
SetModified(true);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.CharLeft;
|
|
begin
|
|
if CurPos.X=0 then Exit;
|
|
|
|
SetCurPtr(CurPos.X-1,CurPos.Y);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.CharRight;
|
|
begin
|
|
if CurPos.X>=MaxLineLength then
|
|
Exit;
|
|
SetCurPtr(CurPos.X+1,CurPos.Y);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.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 TCustomCodeEditor.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 TCustomCodeEditor.LineStart;
|
|
begin
|
|
SetCurPtr(0,CurPos.Y);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.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;
|
|
|
|
function TCustomCodeEditor.NextVisibleLine(StartLine: sw_integer; Down: boolean): sw_integer;
|
|
var Count,NL: sw_integer;
|
|
begin
|
|
if Down then
|
|
begin
|
|
Count:=GetLineCount;
|
|
NL:=StartLine;
|
|
while (NL<Count-1) and not IsLineVisible(NL) do
|
|
Inc(NL);
|
|
if NL>=Count then
|
|
NL:=-1;
|
|
end
|
|
else
|
|
begin
|
|
NL:=StartLine;
|
|
while (NL>0) and not IsLineVisible(NL) do
|
|
Dec(NL);
|
|
end;
|
|
if not IsLineVisible(NL) then
|
|
NL:=-1;
|
|
NextVisibleLine:=NL;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.LineUp;
|
|
var NL: sw_integer;
|
|
begin
|
|
NL:=NextVisibleLine(CurPos.Y-1,false);
|
|
if NL<>-1 then
|
|
SetCurPtr(CurPos.X,NL);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.LineDown;
|
|
var NL: sw_integer;
|
|
begin
|
|
NL:=NextVisibleLine(CurPos.Y+1,true);
|
|
if NL<>-1 then
|
|
SetCurPtr(CurPos.X,NL);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.PageUp;
|
|
var NL: sw_integer;
|
|
begin
|
|
ScrollTo(Delta.X,Max(Delta.Y-Size.Y,0));
|
|
NL:=Max(CurPos.Y-(Size.Y),0);
|
|
if not IsLineVisible(NL) then
|
|
NL:=NextVisibleLine(NL,false);
|
|
if NL>=0 then
|
|
SetCurPtr(CurPos.X,Max(0,NL));
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.PageDown;
|
|
var NL: sw_integer;
|
|
begin
|
|
ScrollTo(Delta.X,Min(Delta.Y+Size.Y,GetLineCount-1));
|
|
NL:=Min(CurPos.Y+(Size.Y{-1}),GetLineCount-1);
|
|
if not IsLineVisible(NL) then
|
|
NL:=NextVisibleLine(NL,true);
|
|
if NL>=0 then
|
|
SetCurPtr(CurPos.X,Min(GetLineCount-1,NL));
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.TextStart;
|
|
begin
|
|
SetCurPtr(0,0);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.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 TCustomCodeEditor.WindowStart;
|
|
begin
|
|
SetCurPtr(CurPos.X,Delta.Y);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.WindowEnd;
|
|
begin
|
|
SetCurPtr(CurPos.X,Delta.Y+Size.Y-1);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.JumpSelStart;
|
|
begin
|
|
if ValidBlock then
|
|
SetCurPtr(SelStart.X,SelStart.Y);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.JumpSelEnd;
|
|
begin
|
|
if ValidBlock then
|
|
SetCurPtr(SelEnd.X,SelEnd.Y);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.JumpMark(MarkIdx: integer);
|
|
begin
|
|
DontConsiderShiftState:=true;
|
|
if (MarkIdx<Low(Bookmarks)) or (MarkIdx>High(Bookmarks)) then
|
|
begin ErrorBox(FormatStrInt(msg_invalidmarkindex,MarkIdx),nil); Exit; end;
|
|
|
|
with Bookmarks[MarkIdx] do
|
|
if Valid=false then
|
|
InformationBox(FormatStrInt(msg_marknotset,MarkIdx),nil)
|
|
else
|
|
SetCurPtr(Pos.X,Pos.Y);
|
|
DontConsiderShiftState:=false;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.DefineMark(MarkIdx: integer);
|
|
begin
|
|
if (MarkIdx<Low(Bookmarks)) or (MarkIdx>High(Bookmarks)) then
|
|
begin
|
|
ErrorBox(FormatStrInt(msg_invalidmarkindex,MarkIdx),nil);
|
|
Exit;
|
|
end;
|
|
with Bookmarks[MarkIdx] do
|
|
begin
|
|
Pos:=CurPos;
|
|
Valid:=true;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.JumpToLastCursorPos;
|
|
begin
|
|
NotImplemented;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.UpperCase;
|
|
var StartP,EndP: TPoint;
|
|
begin
|
|
if ValidBlock=false then Exit;
|
|
GetSelectionArea(StartP,EndP);
|
|
AddGroupedAction(eaUpperCase);
|
|
ChangeCaseArea(StartP,EndP,caToUpperCase);
|
|
CloseGroupedAction(eaUpperCase);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.LowerCase;
|
|
var StartP,EndP: TPoint;
|
|
begin
|
|
if ValidBlock=false then Exit;
|
|
GetSelectionArea(StartP,EndP);
|
|
AddGroupedAction(eaLowerCase);
|
|
ChangeCaseArea(StartP,EndP,caToLowerCase);
|
|
CloseGroupedAction(eaLowerCase);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.ToggleCase;
|
|
var StartP,EndP: TPoint;
|
|
begin
|
|
if ValidBlock=false then Exit;
|
|
GetSelectionArea(StartP,EndP);
|
|
AddGroupedAction(eaToggleCase);
|
|
ChangeCaseArea(StartP,EndP,caToggleCase);
|
|
CloseGroupedAction(eaToggleCase);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.WordLowerCase;
|
|
var StartP,EndP: TPoint;
|
|
begin
|
|
if GetCurrentWordArea(StartP,EndP)=false then Exit;
|
|
AddGroupedAction(eaLowerCase);
|
|
ChangeCaseArea(StartP,EndP,caToLowerCase);
|
|
CloseGroupedAction(eaLowerCase);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.WordUpperCase;
|
|
var StartP,EndP: TPoint;
|
|
begin
|
|
if GetCurrentWordArea(StartP,EndP)=false then Exit;
|
|
AddGroupedAction(eaUpperCase);
|
|
ChangeCaseArea(StartP,EndP,caToUpperCase);
|
|
CloseGroupedAction(eaUpperCase);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.CreateFoldFromBlock;
|
|
var StartY,EndY: sw_integer;
|
|
begin
|
|
if not IsFlagSet(efFolds) then Exit;
|
|
if not ValidBlock then Exit;
|
|
StartY:=SelStart.Y; EndY:=SelEnd.Y;
|
|
if SelEnd.X=0 then Dec(EndY);
|
|
if CreateFold(StartY,EndY,false)=false then
|
|
ErrorBox(msg_foldboundsarenotvalid,nil);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.ToggleFold;
|
|
var F: PFold;
|
|
begin
|
|
if not IsFlagSet(efFolds) then Exit;
|
|
F:=GetLineFold(CurPos.Y);
|
|
if Assigned(F) then
|
|
F^.Collapse(not F^.Collapsed_);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.ExpandFold;
|
|
var F: PFold;
|
|
begin
|
|
if not IsFlagSet(efFolds) then Exit;
|
|
F:=GetLineFold(CurPos.Y);
|
|
if Assigned(F) then
|
|
F^.Collapse(false);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.CollapseFold;
|
|
var F: PFold;
|
|
begin
|
|
if not IsFlagSet(efFolds) then Exit;
|
|
F:=GetLineFold(CurPos.Y);
|
|
if Assigned(F) then
|
|
F^.Collapse(true);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.ChangeCaseArea(StartP,EndP: TPoint; CaseAction: TCaseAction);
|
|
var Y,X: sw_integer;
|
|
X1,X2: sw_integer;
|
|
S: string;
|
|
C: char;
|
|
StartPos : TPoint;
|
|
HoldUndo : boolean;
|
|
begin
|
|
Lock;
|
|
HoldUndo:=GetStoreUndo;
|
|
SetStoreUndo(false);
|
|
for Y:=StartP.Y to EndP.Y do
|
|
begin
|
|
S:=GetDisplayText(Y);
|
|
{ Pierre, please implement undo here! Gabor }
|
|
X1:=0; X2:=length(S)-1;
|
|
if Y=StartP.Y then X1:=StartP.X;
|
|
if Y=EndP.Y then X2:=EndP.X;
|
|
SetStoreUndo(HoldUndo);
|
|
StartPos.X:=X1;
|
|
StartPos.Y:=Y;
|
|
{ the only drawback is that we keep
|
|
the original text even if Toggle where
|
|
it is not really necessary PM }
|
|
Addaction(eaOverwriteText,StartPos,StartPos,Copy(S,X1+1,X2-X1+1),GetFlags);
|
|
SetStoreUndo(false);
|
|
for X:=X1 to X2 do
|
|
begin
|
|
C:=S[X+1];
|
|
case CaseAction of
|
|
caToLowerCase : C:=LowCase(C);
|
|
caToUpperCase : C:=UpCase(C);
|
|
caToggleCase : if C in['a'..'z'] then
|
|
C:=Upcase(C)
|
|
else
|
|
C:=LowCase(C);
|
|
end;
|
|
S[X+1]:=C;
|
|
end;
|
|
SetDisplayText(Y,S);
|
|
end;
|
|
UpdateAttrsRange(StartP.Y,EndP.Y,attrAll);
|
|
DrawLines(CurPos.Y);
|
|
SetModified(true);
|
|
Addaction(eaMoveCursor,StartPos,CurPos,'',GetFlags);
|
|
SetStoreUndo(HoldUndo);
|
|
UnLock;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.PushInfo(Const st : string);
|
|
begin
|
|
{ Dummies }
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.PopInfo;
|
|
begin
|
|
{ Dummies }
|
|
end;
|
|
|
|
|
|
procedure TCustomCodeEditor.InsertOptions;
|
|
begin
|
|
{ Abstract }
|
|
NotImplemented;
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetLineFold(EditorLine: sw_integer): PFold;
|
|
var L: PCustomLine;
|
|
LI: PEditorLineInfo;
|
|
F: PFold;
|
|
begin
|
|
F:=nil;
|
|
if IsFlagSet(efFolds) then
|
|
if (0<=EditorLine) and (EditorLine<GetLineCount) then
|
|
begin
|
|
L:=GetLine(EditorLine);
|
|
if Assigned(L) then
|
|
LI:=L^.GetEditorInfo(@Self)
|
|
else
|
|
LI:=nil;
|
|
if Assigned(LI) then
|
|
F:=LI^.Fold;
|
|
end;
|
|
GetLineFold:=F;
|
|
end;
|
|
|
|
function TCustomCodeEditor.IsLineVisible(EditorLine: sw_integer): boolean;
|
|
var V: boolean;
|
|
F,PrevF: PFold;
|
|
FoldHeadline: boolean;
|
|
begin
|
|
V:=true;
|
|
if IsFlagSet(efFolds) then
|
|
begin
|
|
F:=GetLineFold(EditorLine);
|
|
if Assigned(F) then
|
|
begin
|
|
PrevF:=GetLineFold(EditorLine-1);
|
|
FoldHeadline:=false;
|
|
if (PrevF<>F) and ((PrevF=nil) or (not PrevF^.IsParent(F))) then
|
|
FoldHeadline:=true;
|
|
if FoldHeadline then
|
|
begin
|
|
if Assigned(F^.ParentFold) and (F^.ParentFold^.IsCollapsed) then
|
|
V:=false;
|
|
end
|
|
else
|
|
if F^.IsCollapsed then
|
|
V:=false;
|
|
end;
|
|
end;
|
|
IsLineVisible:=V;
|
|
end;
|
|
|
|
function TCustomCodeEditor.ViewToEditorLine(ViewLine: sw_integer): sw_integer;
|
|
var I,Line,Count: sw_integer;
|
|
begin
|
|
if not IsFlagSet(efFolds) then
|
|
Line:=ViewLine
|
|
else
|
|
begin
|
|
Count:=GetLineCount;
|
|
I:=0; Line:=-1;
|
|
while (Line<ViewLine) and (I<Count) do
|
|
begin
|
|
if IsLineVisible(I) then
|
|
Inc(Line);
|
|
Inc(I);
|
|
end;
|
|
if Line<>ViewLine then
|
|
Line:=-1
|
|
else
|
|
Line:=I-1;
|
|
end;
|
|
ViewToEditorLine:=Line;
|
|
end;
|
|
|
|
function TCustomCodeEditor.EditorToViewLine(EditorLine: sw_integer): sw_integer;
|
|
var I,Line: sw_integer;
|
|
begin
|
|
if not IsFlagSet(efFolds) then
|
|
Line:=EditorLine
|
|
else
|
|
begin
|
|
Line:=-1;
|
|
for I:=0 to EditorLine do
|
|
if IsLineVisible(I) then
|
|
Inc(Line);
|
|
end;
|
|
EditorToViewLine:=Line;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.ViewToEditorPoint(P: TPoint; var NP: TPoint);
|
|
begin
|
|
NP.X:=P.X-GetReservedColCount;
|
|
NP.Y:=ViewToEditorLine(P.Y);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.EditorToViewPoint(P: TPoint; var NP: TPoint);
|
|
begin
|
|
NP.X:=P.X+GetReservedColCount;
|
|
NP.Y:=EditorToViewLine(P.Y);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.FindMatchingDelimiter(ScanForward: boolean);
|
|
const OpenSymbols : string[6] = '[{(<''"';
|
|
CloseSymbols : string[6] = ']})>''"';
|
|
var SymIdx: integer;
|
|
LineText,LineAttr: string;
|
|
CurChar: char;
|
|
X,Y: sw_integer;
|
|
LineCount: sw_integer;
|
|
JumpPos: TPoint;
|
|
BracketLevel: integer;
|
|
begin
|
|
JumpPos.X:=-1; JumpPos.Y:=-1;
|
|
LineText:=GetDisplayText(CurPos.Y);
|
|
LineText:=copy(LineText,CurPos.X+1,1);
|
|
if LineText='' then Exit;
|
|
CurChar:=LineText[1];
|
|
Y:=CurPos.Y; X:=CurPos.X; LineCount:=0;
|
|
BracketLevel:=1;
|
|
if ScanForward then
|
|
begin
|
|
SymIdx:=Pos(CurChar,OpenSymbols);
|
|
if SymIdx=0 then Exit;
|
|
repeat
|
|
Inc(LineCount);
|
|
GetDisplayTextFormat(Y,LineText,LineAttr);
|
|
if LineCount<>1 then X:=-1;
|
|
repeat
|
|
Inc(X);
|
|
if X<length(LineText) then
|
|
if copy(LineAttr,X+1,1)<>chr(attrComment) then
|
|
if (LineText[X+1]=CloseSymbols[SymIdx]) and (BracketLevel=1) then
|
|
begin
|
|
JumpPos.X:=X; JumpPos.Y:=Y;
|
|
end
|
|
else
|
|
if LineText[X+1]=OpenSymbols[SymIdx] then
|
|
Inc(BracketLevel)
|
|
else
|
|
if LineText[X+1]=CloseSymbols[SymIdx] then
|
|
if BracketLevel>1 then
|
|
Dec(BracketLevel);
|
|
until (X>=length(LineText)) or (JumpPos.X<>-1);
|
|
Inc(Y);
|
|
until (Y>=GetLineCount) or (JumpPos.X<>-1);
|
|
end
|
|
else
|
|
begin
|
|
SymIdx:=Pos(CurChar,CloseSymbols);
|
|
if SymIdx=0 then Exit;
|
|
repeat
|
|
Inc(LineCount);
|
|
GetDisplayTextFormat(Y,LineText,LineAttr);
|
|
if LineCount<>1 then X:=length(LineText);
|
|
repeat
|
|
Dec(X);
|
|
if X>0 then
|
|
if copy(LineAttr,X+1,1)<>chr(attrComment) then
|
|
if (LineText[X+1]=OpenSymbols[SymIdx]) and (BracketLevel=1) then
|
|
begin
|
|
JumpPos.X:=X; JumpPos.Y:=Y;
|
|
end
|
|
else
|
|
if LineText[X+1]=CloseSymbols[SymIdx] then
|
|
Inc(BracketLevel)
|
|
else
|
|
if LineText[X+1]=OpenSymbols[SymIdx] then
|
|
if BracketLevel>1 then
|
|
Dec(BracketLevel);
|
|
until (X<0) or (JumpPos.X<>-1);
|
|
Dec(Y);
|
|
until (Y<0) or (JumpPos.X<>-1);
|
|
end;
|
|
if JumpPos.X<>-1 then
|
|
begin
|
|
SetCurPtr(JumpPos.X,JumpPos.Y);
|
|
TrackCursor(do_centre);
|
|
end;
|
|
end;
|
|
|
|
function TCustomCodeEditor.InsertNewLine: Sw_integer;
|
|
var i,Ind: Sw_integer;
|
|
S,IndentStr: string;
|
|
procedure CalcIndent(LineOver: Sw_integer);
|
|
begin
|
|
if (LineOver<0) or (LineOver>GetLineCount) or ((GetFlags and efNoIndent)<>0) then
|
|
Ind:=0 else
|
|
begin
|
|
repeat
|
|
IndentStr:=GetDisplayText(LineOver);
|
|
Dec(LineOver);
|
|
until (LineOver<0) or (IndentStr<>'');
|
|
Ind:=0;
|
|
while (Ind<length(IndentStr)) and (IndentStr[Ind+1]=' ') do
|
|
Inc(Ind);
|
|
end;
|
|
IndentStr:=CharStr(' ',Ind);
|
|
end;
|
|
var {SelBack: sw_integer;}
|
|
SCP: TPoint;
|
|
CI : sw_integer;
|
|
HoldUndo : Boolean;
|
|
L,NewL: PCustomLine;
|
|
EI,NewEI: PEditorLineInfo;
|
|
begin
|
|
if IsReadOnly then begin InsertNewLine:=-1; Exit; end;
|
|
Lock;
|
|
SCP:=CurPos;
|
|
HoldUndo:=GetStoreUndo;
|
|
SetStoreUndo(false);
|
|
if CurPos.Y<GetLineCount then S:=GetLineText(CurPos.Y) else S:='';
|
|
if Overwrite=false then
|
|
begin
|
|
if CurPos.Y<GetLineCount then
|
|
begin
|
|
L:=GetLine(CurPos.Y);
|
|
if not assigned(L) then
|
|
EI:=nil
|
|
else
|
|
EI:=L^.GetEditorInfo(@Self);
|
|
end
|
|
else
|
|
EI:=nil;
|
|
{ SelBack:=0;}
|
|
CI:=LinePosToCharIdx(CurPos.Y,CurPos.X);
|
|
if GetLineCount>0 then
|
|
begin
|
|
S:=GetLineText(CurPos.Y);
|
|
{ SelBack:=length(S)-SelEnd.X;}
|
|
SetLineText(CurPos.Y,RTrim(S,not IsFlagSet(efUseTabCharacters)));
|
|
end;
|
|
SetLineText(CurPos.Y,copy(S,1,CI-1));
|
|
CalcIndent(CurPos.Y);
|
|
S:=copy(S,CI,High(S));
|
|
i:=1;
|
|
while (i<=length(s)) and (i<=length(IndentStr)) and (s[i]=' ') do
|
|
inc(i);
|
|
if i>1 then
|
|
Delete(IndentStr,1,i-1);
|
|
NewL:=InsertLine(CurPos.Y+1,IndentStr+S);
|
|
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);
|
|
NewEI:=NewL^.GetEditorInfo(@Self);
|
|
if Assigned(EI) and Assigned(NewEI) then
|
|
begin
|
|
NewEI^.SetFold(EI^.Fold);
|
|
if Assigned(EI^.Fold) then
|
|
if EI^.Fold^.IsCollapsed then
|
|
EI^.Fold^.Collapse(false);
|
|
end;
|
|
SetStoreUndo(HoldUndo);
|
|
{ obsolete IndentStr is taken care of by the Flags PM }
|
|
Addaction(eaInsertLine,SCP,CurPos,CharStr(' ',i-1){IndentStr},GetFlags);
|
|
SetStoreUndo(false);
|
|
AdjustSelectionPos(SCP.X,SCP.Y,CurPos.X-SCP.X,CurPos.Y-SCP.Y);
|
|
end else
|
|
begin
|
|
CalcIndent(CurPos.Y);
|
|
if CurPos.Y=GetLineCount-1 then
|
|
begin
|
|
AddLine(IndentStr);
|
|
AdjustSelectionBefore(0,1);
|
|
LimitsChanged;
|
|
SetStoreUndo(HoldUndo);
|
|
UpdateAttrs(CurPos.Y,attrAll);
|
|
SetCurPtr(Ind,CurPos.Y+1);
|
|
{ obsolete IndentStr is taken care of by the Flags PM }
|
|
Addaction(eaInsertLine,SCP,CurPos,''{IndentStr},GetFlags);
|
|
SetStoreUndo(false);
|
|
end
|
|
else
|
|
begin
|
|
UpdateAttrs(CurPos.Y,attrAll);
|
|
SetStoreUndo(HoldUndo);
|
|
SetCurPtr(Ind,CurPos.Y+1);
|
|
AddAction(eaMoveCursor,SCP,CurPos,'',GetFlags);
|
|
SetStoreUndo(false);
|
|
end;
|
|
end;
|
|
DrawLines(CurPos.Y);
|
|
SetStoreUndo(HoldUndo);
|
|
SetModified(true);
|
|
Unlock;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.BreakLine;
|
|
var
|
|
SCP: TPoint;
|
|
begin
|
|
{ Like insert new line, but leave current pos unchanged }
|
|
SCP:=CurPos;
|
|
InsertNewLine;
|
|
SetCurPtr(SCP.X,SCP.Y);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.BackSpace;
|
|
var S,PreS: string;
|
|
OI,CI,CP,Y,TX: Sw_integer;
|
|
SCP,SC1 : TPoint;
|
|
HoldUndo : Boolean;
|
|
begin
|
|
if IsReadOnly then Exit;
|
|
Lock;
|
|
SCP:=CurPos;
|
|
HoldUndo:=GetStoreUndo;
|
|
SetStoreUndo(false);
|
|
if CurPos.X=0 then
|
|
begin
|
|
if CurPos.Y>0 then
|
|
begin
|
|
CI:=Length(GetDisplayText(CurPos.Y-1));
|
|
S:=GetLineText(CurPos.Y-1);
|
|
SetLineText(CurPos.Y-1,S+GetLineText(CurPos.Y));
|
|
SC1.X:=Length(S);SC1.Y:=CurPOS.Y-1;
|
|
SetStoreUndo(HoldUndo);
|
|
AddAction(eaDeleteLine,SCP,SC1,GetLineText(CurPos.Y),GetFlags);
|
|
SetStoreUndo(false);
|
|
DeleteLine(CurPos.Y);
|
|
LimitsChanged;
|
|
SetCurPtr(CI,CurPos.Y-1);
|
|
AdjustSelectionPos(Ci,CurPos.Y,CurPos.X-SCP.X,CurPos.Y-SCP.Y);
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
CP:=CurPos.X-1;
|
|
S:=GetLineText(CurPos.Y);
|
|
CI:=LinePosToCharIdx(CurPos.Y,CP);
|
|
if (s[ci]=TAB) and (CharIdxToLinePos(Curpos.y,ci)=cp) then
|
|
CP:=CharIdxToLinePos(CurPos.Y,CI-1)+1;
|
|
if IsFlagSet(efBackspaceUnindents) then
|
|
begin
|
|
S:=GetDisplayText(CurPos.Y);
|
|
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:='';
|
|
TX:=0;
|
|
while (TX<length(PreS)) and (PreS[TX+1]=' ') do
|
|
Inc(TX);
|
|
if TX<CP then CP:=TX;
|
|
end;
|
|
end;
|
|
S:=GetLineText(CurPos.Y);
|
|
OI:=LinePosToCharIdx(CurPos.Y,CurPos.X);
|
|
CI:=LinePosToCharIdx(CurPos.Y,CP);
|
|
SetLineText(CurPos.Y,copy(S,1,CI-1)+copy(S,OI,High(S)));
|
|
SetCurPtr(CP,CurPos.Y);
|
|
SetStoreUndo(HoldUndo);
|
|
Addaction(eaDeleteText,SCP,CurPos,Copy(S,CI,OI-CI),GetFlags);
|
|
SetStoreUndo(false);
|
|
AdjustSelectionPos(SCP.X-1,SCP.Y,CurPos.X-SCP.X,CurPos.Y-SCP.Y);
|
|
end;
|
|
UpdateAttrs(CurPos.Y,attrAll);
|
|
DrawLines(CurPos.Y);
|
|
SetStoreUndo(HoldUndo);
|
|
SetModified(true);
|
|
Unlock;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.DelChar;
|
|
var S: string;
|
|
SDX,SDY,CI : sw_integer;
|
|
HoldUndo : boolean;
|
|
SCP : TPoint;
|
|
begin
|
|
if IsReadOnly then Exit;
|
|
Lock;
|
|
HoldUndo:=GetStoreUndo;
|
|
SetStoreUndo(false);
|
|
S:=GetLineText(CurPos.Y);
|
|
CI:=LinePosToCharIdx(CurPos.Y,CurPos.X);
|
|
if (CI>length(S)) or (S='') then
|
|
begin
|
|
if CurPos.Y<GetLineCount-1 then
|
|
begin
|
|
SetLineText(CurPos.Y,S+CharStr(' ',CurPOS.X-Length(S))+GetLineText(CurPos.Y+1));
|
|
SDX:=CurPos.X;
|
|
SetStoreUndo(HoldUndo);
|
|
SCP.X:=0;SCP.Y:=CurPos.Y+1;
|
|
AddGroupedAction(eaDelChar);
|
|
AddAction(eaMoveCursor,CurPos,SCP,'',GetFlags);
|
|
S:=GetLineText(CurPos.Y+1);
|
|
AddAction(eaDeleteLine,SCP,CurPos,S,GetFlags);
|
|
CloseGroupedAction(eaDelChar);
|
|
SetStoreUndo(false);
|
|
DeleteLine(CurPos.Y+1);
|
|
LimitsChanged;
|
|
SDY:=-1;
|
|
SetCurPtr(CurPos.X,CurPos.Y);
|
|
UpdateAttrs(CurPos.Y,attrAll);
|
|
AdjustSelectionPos(CurPos.X,CurPos.Y,SDX,SDY);
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
SCP:=CurPos;
|
|
{ Problem if S[CurPos.X+1]=TAB !! PM }
|
|
if S[CI]=TAB then
|
|
begin
|
|
{ we want to remove the tab if we are at the first place
|
|
of the tab, but the following test was true for the last position
|
|
in tab
|
|
if CharIdxToLinePos(Curpos.y,ci)=Curpos.x then }
|
|
if CharIdxToLinePos(Curpos.y,ci-1)=Curpos.x-1 then
|
|
Delete(S,Ci,1)
|
|
else
|
|
S:=Copy(S,1,CI-1)+CharStr(' ',GetTabSize-1)+Copy(S,CI+1,High(S));
|
|
SetStoreUndo(HoldUndo);
|
|
Addaction(eaDeleteText,CurPos,CurPos,#9,GetFlags);
|
|
SDX:=-1;
|
|
SetStoreUndo(false);
|
|
end
|
|
else
|
|
begin
|
|
SetStoreUndo(HoldUndo);
|
|
Addaction(eaDeleteText,CurPos,CurPos,S[CI],GetFlags);
|
|
SetStoreUndo(false);
|
|
SDX:=-1;
|
|
Delete(S,CI,1);
|
|
end;
|
|
SetLineText(CurPos.Y,S);
|
|
SDY:=0;
|
|
SetCurPtr(CurPos.X,CurPos.Y);
|
|
UpdateAttrs(CurPos.Y,attrAll);
|
|
AdjustSelectionPos(SCP.X,SCP.Y,SDX,SDY);
|
|
end;
|
|
DrawLines(CurPos.Y);
|
|
SetStoreUndo(HoldUndo);
|
|
SetModified(true);
|
|
Unlock;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.DelWord;
|
|
var
|
|
SP,EP : TPoint;
|
|
SelSize : sw_integer;
|
|
begin
|
|
if IsReadOnly then Exit;
|
|
Lock;
|
|
SP:=SelStart;
|
|
EP:=SelEnd;
|
|
SetSelection(SelStart,SelStart);
|
|
SelectWord;
|
|
SelSize:=SelEnd.X-SelStart.X;
|
|
DelSelect;
|
|
SetSelection(SP,EP);
|
|
AdjustSelectionPos(CurPos.X,CurPos.Y,-SelSize,0);
|
|
if SelSize>0 then
|
|
SetModified(true);
|
|
Unlock;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.DelToEndOfWord;
|
|
var
|
|
SP,EP : TPoint;
|
|
S : String;
|
|
SelSize : sw_integer;
|
|
begin
|
|
if IsReadOnly then Exit;
|
|
Lock;
|
|
SP:=SelStart;
|
|
EP:=SelEnd;
|
|
SetSelection(SelStart,SelStart);
|
|
SelectWord;
|
|
S:=GetDisplayText(CurPos.Y);
|
|
if ((SelStart.X=SelEnd.X) and (SelStart.Y=SelEnd.Y)) then
|
|
begin
|
|
if (Length(S) <= CurPos.X) then
|
|
begin
|
|
SetSelection(SP,EP);
|
|
DelChar;
|
|
Unlock;
|
|
exit;
|
|
end
|
|
else
|
|
begin
|
|
SelEnd.X:=CurPos.X+1;
|
|
SelEnd.Y:=CurPos.Y;
|
|
end;
|
|
end;
|
|
while (length(S)>= SelEnd.X+1) and
|
|
((S[SelEnd.X+1]=' ') or (S[SelEnd.X+1]=TAB)) do
|
|
inc(SelEnd.X);
|
|
SetSelection(CurPos,SelEnd);
|
|
SelSize:=SelEnd.X-SelStart.X;
|
|
DelSelect;
|
|
SetSelection(SP,EP);
|
|
AdjustSelectionPos(CurPos.X,CurPos.Y,-SelSize,0);
|
|
if SelSize>0 then
|
|
SetModified(true);
|
|
Unlock;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.DelStart;
|
|
var S: string;
|
|
OI: Sw_integer;
|
|
HoldUndo : Boolean;
|
|
SCP : TPoint;
|
|
begin
|
|
if IsReadOnly then Exit;
|
|
Lock;
|
|
HoldUndo:=GetStoreUndo;
|
|
SetStoreUndo(false);
|
|
SCP:=CurPos;
|
|
S:=GetLineText(CurPos.Y);
|
|
if (S<>'') and (CurPos.X<>0) then
|
|
begin
|
|
OI:=LinePosToCharIdx(CurPos.Y,CurPos.X);
|
|
SetLineText(CurPos.Y,copy(S,OI,High(S)));
|
|
SetCurPtr(0,CurPos.Y);
|
|
SetStoreUndo(HoldUndo);
|
|
Addaction(eaDeleteText,SCP,CurPos,copy(S,1,OI-1),GetFlags);
|
|
SetStoreUndo(false);
|
|
AdjustSelectionPos(CurPos.X,CurPos.Y,-length(copy(S,1,OI-1)),0);
|
|
UpdateAttrs(CurPos.Y,attrAll);
|
|
DrawLines(CurPos.Y);
|
|
SetModified(true);
|
|
end;
|
|
SetStoreUndo(HoldUndo);
|
|
Unlock;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.DelEnd;
|
|
var S: string;
|
|
OI: Sw_integer;
|
|
HoldUndo : Boolean;
|
|
SCP : TPoint;
|
|
begin
|
|
if IsReadOnly then Exit;
|
|
Lock;
|
|
HoldUndo:=GetStoreUndo;
|
|
SetStoreUndo(false);
|
|
SCP:=CurPos;
|
|
S:=GetLineText(CurPos.Y);
|
|
if (S<>'') and (CurPos.X<>length(S)) then
|
|
begin
|
|
OI:=LinePosToCharIdx(CurPos.Y,CurPos.X);
|
|
SetLineText(CurPos.Y,copy(S,1,OI-1));
|
|
SetCurPtr(CurPos.X,CurPos.Y);
|
|
SetStoreUndo(HoldUndo);
|
|
Addaction(eaDeleteText,SCP,CurPos,copy(S,OI,High(S)),GetFlags);
|
|
SetStoreUndo(false);
|
|
AdjustSelectionPos(CurPos.X+1,CurPos.Y,-length(copy(S,OI,High(S)))+1,0);
|
|
UpdateAttrs(CurPos.Y,attrAll);
|
|
DrawLines(CurPos.Y);
|
|
SetModified(true);
|
|
end;
|
|
SetStoreUndo(HoldUndo);
|
|
Unlock;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.DelLine;
|
|
var
|
|
HoldUndo : boolean;
|
|
SP : TPoint;
|
|
S : String;
|
|
begin
|
|
if IsReadOnly then Exit;
|
|
Lock;
|
|
if GetLineCount>0 then
|
|
begin
|
|
SP:=CurPos;
|
|
S:=GetLineText(CurPos.Y);
|
|
HoldUndo:=GetStoreUndo;
|
|
SetStoreUndo(false);
|
|
DeleteLine(CurPos.Y);
|
|
LimitsChanged;
|
|
AdjustSelectionBefore(0,-1);
|
|
SetCurPtr(0,CurPos.Y);
|
|
UpdateAttrs(Max(0,CurPos.Y-1),attrAll);
|
|
DrawLines(CurPos.Y);
|
|
SetStoreUndo(HoldUndo);
|
|
AddAction(eaDeleteLine,SP,CurPos,S,GetFlags);
|
|
SetModified(true);
|
|
end;
|
|
Unlock;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.InsMode;
|
|
begin
|
|
SetInsertMode(Overwrite);
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetCurrentWordArea(var StartP,EndP: TPoint): boolean;
|
|
const WordChars = ['A'..'Z','a'..'z','0'..'9','_'];
|
|
var P : TPoint;
|
|
S : String;
|
|
StartPos,EndPos : byte;
|
|
OK: boolean;
|
|
begin
|
|
P:=CurPos;
|
|
S:=GetLineText(P.Y);
|
|
StartPos:=P.X+1;
|
|
EndPos:=StartPos;
|
|
OK:=(S[StartPos] in WordChars);
|
|
if OK then
|
|
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);
|
|
StartP.X:=StartPos-1; StartP.Y:=CurPos.Y;
|
|
EndP.X:=EndPos-1; EndP.Y:=CurPos.Y;
|
|
end;
|
|
GetCurrentWordArea:=OK;
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetCurrentWord : string;
|
|
var S: string;
|
|
StartP,EndP: TPoint;
|
|
begin
|
|
if GetCurrentWordArea(StartP,EndP)=false then
|
|
S:=''
|
|
else
|
|
begin
|
|
S:=GetLineText(StartP.Y);
|
|
S:=copy(S,StartP.X+1,EndP.X-StartP.X+1);
|
|
end;
|
|
GetCurrentWord:=S;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.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 TCustomCodeEditor.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 TCustomCodeEditor.DelSelect;
|
|
var LineDelta, LineCount, CurLine: Sw_integer;
|
|
StartX,EndX,LastX: Sw_integer;
|
|
S: string;
|
|
SPos : TPoint;
|
|
begin
|
|
if IsReadOnly or (ValidBlock=false) then Exit;
|
|
|
|
Lock;
|
|
AddGroupedAction(eaDelBlock);
|
|
LineCount:=(SelEnd.Y-SelStart.Y)+1;
|
|
LineDelta:=0; LastX:=CurPos.X;
|
|
CurLine:=SelStart.Y;
|
|
{ single line : easy }
|
|
if LineCount=1 then
|
|
begin
|
|
S:=GetDisplayText(CurLine);
|
|
StartX:=SelStart.X;
|
|
EndX:=SelEnd.X;
|
|
SetDisplayText(CurLine,RExpand(copy(S,1,StartX),StartX)
|
|
+copy(S,EndX+1,High(S)));
|
|
if GetStoreUndo then
|
|
begin
|
|
SPos.X:=StartX;
|
|
SPos.Y:=CurLine;
|
|
AddAction(eaDeleteText,SPos,SPos,Copy(S,StartX+1,EndX-StartX),GetFlags);
|
|
end;
|
|
Inc(CurLine);
|
|
LastX:=SelStart.X;
|
|
end
|
|
{ several lines : a bit less easy }
|
|
else
|
|
begin
|
|
S:=GetDisplayText(CurLine);
|
|
StartX:=SelStart.X;
|
|
EndX:=SelEnd.X;
|
|
SetDisplayText(CurLine,RExpand(copy(S,1,StartX),StartX)
|
|
+copy(GetDisplayText(CurLine+LineCount-1),EndX+1,High(S)));
|
|
if GetStoreUndo then
|
|
begin
|
|
SPos.X:=StartX;
|
|
SPos.Y:=CurLine;
|
|
AddAction(eaDeleteText,SPos,SPos,Copy(S,StartX+1,High(S)),GetFlags);
|
|
S:=GetDisplayText(CurLine+LineCount-1);
|
|
end;
|
|
Inc(CurLine);
|
|
Inc(LineDelta);
|
|
LastX:=SelStart.X;
|
|
while (LineDelta<LineCount) do
|
|
begin
|
|
{ delete the complete line }
|
|
DeleteLine(CurLine);
|
|
Inc(LineDelta);
|
|
end;
|
|
if GetStoreUndo then
|
|
begin
|
|
AddAction(eaInsertText,SPos,SPos,Copy(S,EndX+1,High(S)),GetFlags);
|
|
end;
|
|
end;
|
|
HideSelect;
|
|
SetCurPtr(LastX,CurLine-1);
|
|
UpdateAttrs(CurPos.Y,attrAll);
|
|
DrawLines(CurPos.Y);
|
|
SetModified(true);
|
|
CloseGroupedAction(eaDelBlock);
|
|
UnLock;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.HideSelect;
|
|
begin
|
|
SetSelection(CurPos,CurPos);
|
|
DrawLines(Delta.Y);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.CopyBlock;
|
|
var Temp: PCodeEditor;
|
|
R: TRect;
|
|
begin
|
|
if IsReadOnly or (ValidBlock=false) then Exit;
|
|
|
|
Lock;
|
|
GetExtent(R);
|
|
AddGroupedAction(eaCopyBlock);
|
|
New(Temp, Init(R, nil, nil, nil,nil));
|
|
Temp^.InsertFrom(@Self);
|
|
(* Temp^.SelectAll(true);
|
|
{ this selects one line too much because
|
|
we have a empty line at creation to avoid
|
|
negative line problems so we need to decrease SelEnd.Y }
|
|
Dec(Temp^.SelEnd.Y);*)
|
|
|
|
|
|
InsertFrom(Temp);
|
|
Dispose(Temp, Done);
|
|
CloseGroupedAction(eaCopyBlock);
|
|
UnLock;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.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;
|
|
AddGroupedAction(eaMoveBlock);
|
|
GetExtent(R);
|
|
New(Temp, Init(R, nil, nil, nil,nil));
|
|
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);
|
|
CloseGroupedAction(eaMoveBlock);
|
|
UnLock;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.IndentBlock;
|
|
var
|
|
ey,i{,indlen} : Sw_integer;
|
|
S,Ind : String;
|
|
Pos : Tpoint;
|
|
begin
|
|
if IsReadOnly then Exit;
|
|
if (SelStart.X=SelEnd.X) and (SelStart.Y=SelEnd.Y) then Exit;
|
|
Lock;
|
|
AddGroupedAction(eaIndentBlock);
|
|
ey:=selend.y;
|
|
if selend.x=0 then
|
|
dec(ey);
|
|
S:='';
|
|
{ If AutoIndent try to align first line to
|
|
last line before selection }
|
|
{ DISABLED created problems PM
|
|
if IsFlagSet(efAutoIndent) and (SelStart.Y>0) then
|
|
begin
|
|
i:=SelStart.Y-1;
|
|
while (S='') and (i>=0) do
|
|
begin
|
|
S:=GetDisplayText(i);
|
|
dec(i);
|
|
end;
|
|
if (S='') or (S[1]<>' ') then
|
|
Ind:=' '
|
|
else
|
|
begin
|
|
i:=1;
|
|
while (i<=Length(S)) and (S[i]=' ') do
|
|
inc(i);
|
|
indlen:=i;
|
|
S:=GetDisplayText(SelStart.Y);
|
|
i:=1;
|
|
while (i<=Length(S)) and (S[i]=' ') do
|
|
inc(i);
|
|
indlen:=indlen-i;
|
|
if indlen<=0 then
|
|
indlen:=1;
|
|
Ind:=CharStr(' ',indlen);
|
|
end;
|
|
end
|
|
else
|
|
Ind:=' ';}
|
|
Ind:=CharStr(' ',GetIndentSize);
|
|
for i:=selstart.y to ey do
|
|
begin
|
|
S:=GetLineText(i);
|
|
SetLineText(i,Ind+S);
|
|
Pos.X:=0;Pos.Y:=i;
|
|
AddAction(eaInsertText,Pos,Pos,Ind,GetFlags);
|
|
end;
|
|
SetCurPtr(CurPos.X,CurPos.Y);
|
|
{ must be added manually here PM }
|
|
AddAction(eaMoveCursor,Pos,CurPos,'',GetFlags);
|
|
UpdateAttrsRange(SelStart.Y,SelEnd.Y,attrAll);
|
|
DrawLines(CurPos.Y);
|
|
SetModified(true);
|
|
CloseGroupedAction(eaIndentBlock);
|
|
UnLock;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.UnindentBlock;
|
|
var
|
|
ey,i,j,k,indlen : Sw_integer;
|
|
S : String;
|
|
Pos : TPoint;
|
|
begin
|
|
if IsReadOnly then Exit;
|
|
if (SelStart.X=SelEnd.X) and (SelStart.Y=SelEnd.Y) then Exit;
|
|
Lock;
|
|
AddGroupedAction(eaUnindentBlock);
|
|
ey:=selend.y;
|
|
if selend.x=0 then
|
|
dec(ey);
|
|
{ If AutoIndent try to align first line to
|
|
last line before selection }
|
|
{ Disabled created problems
|
|
if IsFlagSet(efAutoIndent) and (SelStart.Y>0) then
|
|
begin
|
|
S:=GetDisplayText(SelStart.Y);
|
|
i:=1;
|
|
while (i<=Length(S)) and (S[i]=' ') do
|
|
inc(i);
|
|
indlen:=i-1;
|
|
i:=SelStart.Y-1;
|
|
S:='';
|
|
while (S='') and (i>=0) do
|
|
begin
|
|
if Trim(Copy(GetDisplayText(i),1,indlen))='' then
|
|
S:=''
|
|
else
|
|
S:=GetDisplayText(i);
|
|
dec(i);
|
|
end;
|
|
if (S='') then
|
|
Indlen:=1
|
|
else
|
|
begin
|
|
i:=1;
|
|
while (i<=Length(S)) and (S[i]=' ') do
|
|
inc(i);
|
|
indlen:=indlen-i+1;
|
|
if indlen<=0 then
|
|
indlen:=1;
|
|
end;
|
|
end
|
|
else
|
|
Indlen:=1;}
|
|
Indlen:=GetIndentSize;
|
|
for i:=selstart.y to ey do
|
|
begin
|
|
S:=GetLineText(i);
|
|
k:=0;
|
|
for j:=1 to indlen do
|
|
if (length(s)>1) and (S[1]=' ') then
|
|
begin
|
|
Delete(s,1,1);
|
|
inc(k);
|
|
end;
|
|
SetLineText(i,S);
|
|
if k>0 then
|
|
begin
|
|
Pos.Y:=i;
|
|
Pos.X:=0;
|
|
AddAction(eaDeleteText,Pos,Pos,CharStr(' ',k),GetFlags);
|
|
end;
|
|
end;
|
|
SetCurPtr(CurPos.X,CurPos.Y);
|
|
UpdateAttrsRange(SelStart.Y,SelEnd.Y,attrAll);
|
|
DrawLines(CurPos.Y);
|
|
SetModified(true);
|
|
CloseGroupedAction(eaUnindentBlock);
|
|
UnLock;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.SelectWord;
|
|
const WordChars = ['A'..'Z','a'..'z','0'..'9','_'];
|
|
var S : String;
|
|
StartPos,EndPos : byte;
|
|
A,B: TPoint;
|
|
begin
|
|
A:=CurPos;
|
|
B:=CurPos;
|
|
S:=GetDisplayText(A.Y);
|
|
StartPos:=A.X+1;
|
|
EndPos:=StartPos;
|
|
if not (S[StartPos] in WordChars) then
|
|
exit
|
|
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);
|
|
A.X:=StartPos-1;
|
|
B.X:=EndPos;
|
|
SetSelection(A,B);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.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 TCustomCodeEditor.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 TCustomCodeEditor.ReadBlock;
|
|
var FileName: string;
|
|
S: PFastBufStream;
|
|
E: PCodeEditor;
|
|
R: TRect;
|
|
begin
|
|
if IsReadOnly then Exit;
|
|
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,nil));
|
|
AddGroupedAction(eaReadBlock);
|
|
if E^.LoadFromStream(S)=false then
|
|
EditorDialog(edReadError,@FileName)
|
|
else
|
|
begin
|
|
E^.SelectAll(true);
|
|
Self.InsertFrom(E);
|
|
end;
|
|
CloseGroupedAction(eaReadBlock);
|
|
Dispose(E, Done);
|
|
end;
|
|
if Assigned(S) then Dispose(S, Done);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.PrintBlock;
|
|
begin
|
|
NotImplemented; Exit;
|
|
end;
|
|
|
|
function TCustomCodeEditor.SelectCodeTemplate(var ShortCut: string): boolean;
|
|
begin
|
|
{ Abstract }
|
|
SelectCodeTemplate:=false;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.ExpandCodeTemplate;
|
|
var Line,ShortCutInEditor,ShortCut: string;
|
|
X,Y,I,LineIndent: sw_integer;
|
|
CodeLines: PUnsortedStringCollection;
|
|
CanJump: boolean;
|
|
CP: TPoint;
|
|
begin
|
|
{
|
|
The usage of editing primitives in this routine make it pretty slow, but
|
|
its speed is still acceptable and they make the implementation of Undo
|
|
much easier... - Gabor
|
|
}
|
|
if IsReadOnly then Exit;
|
|
|
|
Lock;
|
|
|
|
CP.X:=-1; CP.Y:=-1;
|
|
Line:=GetDisplayText(CurPos.Y);
|
|
X:=CurPos.X; ShortCut:='';
|
|
if X<=length(Line) then
|
|
while (X>0) and (Line[X] in (NumberChars+AlphaChars)) do
|
|
begin
|
|
ShortCut:=Line[X]+ShortCut;
|
|
Dec(X);
|
|
end;
|
|
ShortCutInEditor:=ShortCut;
|
|
|
|
New(CodeLines, Init(10,10));
|
|
if (ShortCut='') or (not TranslateCodeTemplate(ShortCut,CodeLines)) then
|
|
if SelectCodeTemplate(ShortCut) then
|
|
TranslateCodeTemplate(ShortCut,CodeLines);
|
|
|
|
if CodeLines^.Count>0 then
|
|
begin
|
|
LineIndent:=X;
|
|
SetCurPtr(X,CurPos.Y);
|
|
if Copy(ShortCut,1,length(ShortCutInEditor))=ShortCutInEditor then
|
|
begin
|
|
for I:=1 to length(ShortCutInEditor) do
|
|
DelChar;
|
|
end
|
|
else
|
|
{ restore correct position }
|
|
SetCurPtr(X+Length(ShortCutInEditor),CurPos.Y);
|
|
for Y:=0 to CodeLines^.Count-1 do
|
|
begin
|
|
Line:=GetStr(CodeLines^.At(Y));
|
|
CanJump:=false;
|
|
if (Y>0) then
|
|
begin
|
|
CanJump:=Trim(GetLineText(CurPos.Y))='';
|
|
if CanJump=false then
|
|
begin
|
|
(* for X:=1 to LineIndent do { indent template lines to align }
|
|
AddChar(' '); { them to the first line }*)
|
|
InsertText(CharStr(' ',LineIndent));
|
|
end
|
|
else
|
|
SetCurPtr(CurPos.X+LineIndent,CurPos.Y);
|
|
end;
|
|
I:=Pos(CodeTemplateCursorChar,Line);
|
|
if I>0 then
|
|
begin
|
|
Delete(Line,I,1);
|
|
CP.X:=CurPos.X+I-1;
|
|
CP.Y:=CurPos.Y;
|
|
end;
|
|
InsertText(Line);
|
|
if Y<CodeLines^.Count-1 then
|
|
begin
|
|
InsertNewLine; { line break }
|
|
if CanJump=false then
|
|
begin
|
|
while CurPos.X>0 do { unindent }
|
|
begin
|
|
SetCurPtr(CurPos.X-1,CurPos.Y);
|
|
DelChar;
|
|
end;
|
|
end
|
|
else
|
|
SetCurPtr(0,CurPos.Y);
|
|
end;
|
|
end;
|
|
end;
|
|
Dispose(CodeLines, Done);
|
|
|
|
if (CP.X<>-1) and (CP.Y<>-1) then
|
|
SetCurPtr(CP.X,CP.Y);
|
|
|
|
UnLock;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.AddChar(C: char);
|
|
const OpenBrackets : string[10] = '[({';
|
|
CloseBrackets : string[10] = '])}';
|
|
var S,SC,TabS: string;
|
|
BI: byte;
|
|
CI,TabStart,LocTabSize : Sw_integer;
|
|
SP: TPoint;
|
|
HoldUndo : boolean;
|
|
begin
|
|
if IsReadOnly then Exit;
|
|
|
|
Lock;
|
|
SP:=CurPos;
|
|
HoldUndo:=GetStoreUndo;
|
|
SetStoreUndo(false);
|
|
if (C<>TAB) or IsFlagSet(efUseTabCharacters) then
|
|
SC:=C
|
|
else
|
|
begin
|
|
LocTabSize:=GetTabSize - (CurPos.X mod GetTabSize);
|
|
if (CurPos.Y<=1) or not IsFlagSet(efAutoIndent) then
|
|
SC:=CharStr(' ',LocTabSize)
|
|
else
|
|
begin
|
|
S:=GetLineText(CurPos.Y-1);
|
|
BI:=CurPos.X+1;
|
|
while (BI<=Length(S)) and (S[BI]=' ') do
|
|
inc(BI);
|
|
if (BI=CurPos.X+1) or (BI>Length(S)) then
|
|
SC:=CharStr(' ',LocTabSize)
|
|
else
|
|
SC:=CharStr(' ',BI-CurPos.X-1);
|
|
end;
|
|
end;
|
|
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 CI>High(S) then
|
|
begin
|
|
Unlock;
|
|
exit;
|
|
end;
|
|
if (CI>0) and (S[CI]=TAB) and not IsFlagSet(efUseTabCharacters) then
|
|
begin
|
|
if CI=1 then
|
|
TabStart:=0
|
|
else
|
|
TabStart:=CharIdxToLinePos(CurPos.Y,CI-1)+1;
|
|
if SC=Tab then TabS:=Tab else
|
|
TabS:=CharStr(' ',CurPos.X-TabStart);
|
|
SetLineText(CurPos.Y,copy(S,1,CI-1)+TabS+SC+copy(S,CI+1,High(S)));
|
|
SetCurPtr(CharIdxToLinePos(CurPos.Y,CI+length(TabS)+length(SC)),CurPos.Y);
|
|
end
|
|
else
|
|
begin
|
|
if Overwrite and (CI<=length(S)) then
|
|
begin
|
|
SetLineText(CurPos.Y,copy(S,1,CI-1)+SC+copy(S,CI+length(SC),High(S)));
|
|
end
|
|
else
|
|
SetLineText(CurPos.Y,copy(S,1,CI-1)+SC+copy(S,CI,High(S)));
|
|
SetCurPtr(CharIdxToLinePos(CurPos.Y,CI+length(SC)),CurPos.Y);
|
|
end;
|
|
{ must be before CloseBrackets !! }
|
|
SetStoreUndo(HoldUndo);
|
|
if Overwrite then
|
|
Addaction(eaOverwriteText,SP,CurPos,Copy(S,CI,length(SC)),GetFlags)
|
|
else
|
|
Addaction(eaInsertText,SP,CurPos,SC,GetFlags);
|
|
SetStoreUndo(false);
|
|
if IsFlagSet(efAutoBrackets) then
|
|
begin
|
|
BI:=Pos(C,OpenBrackets);
|
|
if (BI>0) then
|
|
begin
|
|
SetStoreUndo(HoldUndo);
|
|
AddChar(CloseBrackets[BI]);
|
|
SetStoreUndo(false);
|
|
SetCurPtr(CurPos.X-1,CurPos.Y);
|
|
end;
|
|
end;
|
|
UpdateAttrs(CurPos.Y,attrAll);
|
|
if GetInsertMode then
|
|
AdjustSelection(CurPos.X-SP.X,CurPos.Y-SP.Y);
|
|
DrawLines(CurPos.Y);
|
|
SetStoreUndo(HoldUndo);
|
|
SetModified(true);
|
|
UnLock;
|
|
end;
|
|
|
|
{$ifdef WinClipSupported}
|
|
|
|
const
|
|
linelimit = 200;
|
|
|
|
function TCustomCodeEditor.ClipPasteWin: Boolean;
|
|
var
|
|
StorePos : TPoint;
|
|
first : boolean;
|
|
|
|
procedure InsertStringWrap(const s: string; var i : Longint);
|
|
var
|
|
BPos,EPos: TPoint;
|
|
begin
|
|
if first then
|
|
begin
|
|
{ we need to cut the line in two
|
|
if not at end of line PM }
|
|
InsertNewLine;
|
|
SetCurPtr(StorePos.X,StorePos.Y);
|
|
InsertText(s);
|
|
first:=false;
|
|
end
|
|
else
|
|
begin
|
|
Inc(i);
|
|
InsertLine(i,s);
|
|
BPos.X:=0;BPos.Y:=i;
|
|
EPOS.X:=Length(s);EPos.Y:=i;
|
|
AddAction(eaInsertLine,BPos,EPos,GetDisplayText(i),GetFlags);
|
|
end;
|
|
end;
|
|
|
|
var
|
|
OK: boolean;
|
|
l,i,len,len10 : longint;
|
|
p,p10,p2,p13 : pchar;
|
|
s : string;
|
|
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
|
|
if l>500 then
|
|
PushInfo(msg_readingwinclipboard);
|
|
AddGroupedAction(eaPasteWin);
|
|
p2:=p;
|
|
len:=strlen(p2);
|
|
// issue lines ((#13)#10 terminated) of maximally "linelimit" chars.
|
|
// does not take initial X position into account
|
|
repeat
|
|
p13:=strpos(p2,#13);
|
|
p10:=strpos(p2,#10);
|
|
if len> linelimit then
|
|
len:=linelimit;
|
|
if assigned(p10) then
|
|
begin
|
|
len10:=p10-p2;
|
|
if len10<len then
|
|
begin
|
|
if p13+1=p10 then
|
|
dec(len10);
|
|
len:=len10;
|
|
end
|
|
else
|
|
p10:=nil; // signal no cleanup
|
|
end;
|
|
setlength(s,len);
|
|
if len>0 then
|
|
move(p2^,s[1],len);
|
|
// cleanup
|
|
if assigned(p10) then
|
|
p2:=p10+1
|
|
else
|
|
inc(p2,len);
|
|
insertstringwrap(s,i);
|
|
len:=strlen(p2);
|
|
until len=0;
|
|
|
|
SetCurPtr(StorePos.X,StorePos.Y); // y+i to get after paste?
|
|
SetModified(true);
|
|
UpdateAttrs(StorePos.Y,attrAll);
|
|
CloseGroupedAction(eaPasteWin);
|
|
Update;
|
|
if l>500 then
|
|
PopInfo;
|
|
{ we must free the allocated memory }
|
|
freemem(p,l);
|
|
DrawView;
|
|
end;
|
|
end;
|
|
ClipPasteWin:=OK;
|
|
UnLock;
|
|
end;
|
|
|
|
function TCustomCodeEditor.ClipCopyWin: Boolean;
|
|
var OK,ShowInfo: 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;
|
|
ShowInfo:=SelEnd.Y-SelStart.Y>50;
|
|
if ShowInfo then
|
|
PushInfo(msg_copyingwinclipboard);
|
|
{ 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);
|
|
if SelEnd.Y>SelStart.Y then
|
|
str_end:=High(S)
|
|
else
|
|
str_end:=LinePosToCharIdx(i,SelEnd.X)-1;
|
|
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)-1);
|
|
strpcopy(p2,EOL+s);
|
|
end;
|
|
OK:=WinClipboardSupported;
|
|
if OK then
|
|
begin
|
|
OK:=SetTextWinClipBoardData(p,strlen(p));
|
|
end;
|
|
ClipCopyWin:=OK;
|
|
if ShowInfo then
|
|
PopInfo;
|
|
Freemem(p,PCLength);
|
|
UnLock;
|
|
end;
|
|
{$endif WinClipSupported}
|
|
|
|
function TCustomCodeEditor.ClipCopy: Boolean;
|
|
|
|
var ShowInfo,CanPaste: boolean;
|
|
|
|
begin
|
|
Lock;
|
|
{AddGroupedAction(eaCopy);
|
|
can we undo a copy ??
|
|
maybe as an Undo Paste in Clipboard !! }
|
|
clipcopy:=false;
|
|
showinfo:=false;
|
|
if (clipboard<>nil) and (clipboard<>@self) then
|
|
begin
|
|
ShowInfo:=SelEnd.Y-SelStart.Y>50;
|
|
if ShowInfo then
|
|
PushInfo(msg_copyingclipboard);
|
|
clipcopy:=Clipboard^.InsertFrom(@Self);
|
|
if ShowInfo then
|
|
PopInfo;
|
|
{Enable paste command.}
|
|
CanPaste:=((Clipboard^.SelStart.X<>Clipboard^.SelEnd.X) or
|
|
(Clipboard^.SelStart.Y<>Clipboard^.SelEnd.Y));
|
|
SetCmdState(FromClipCmds,CanPaste);
|
|
end;
|
|
UnLock;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.ClipCut;
|
|
var
|
|
ShowInfo,CanPaste : boolean;
|
|
begin
|
|
if IsReadOnly then Exit;
|
|
Lock;
|
|
AddGroupedAction(eaCut);
|
|
DontConsiderShiftState:=true;
|
|
if (clipboard<>nil) and (clipboard<>@self) then
|
|
begin
|
|
ShowInfo:=SelEnd.Y-SelStart.Y>50;
|
|
if ShowInfo then
|
|
PushInfo(msg_cutting);
|
|
if Clipboard^.InsertFrom(@Self) then
|
|
begin
|
|
if not IsClipBoard then
|
|
DelSelect;
|
|
SetModified(true);
|
|
end;
|
|
if ShowInfo then
|
|
PopInfo;
|
|
CanPaste:=((Clipboard^.SelStart.X<>Clipboard^.SelEnd.X) or
|
|
(Clipboard^.SelStart.Y<>Clipboard^.SelEnd.Y));
|
|
SetCmdState(FromClipCmds,CanPaste);
|
|
end;
|
|
CloseGroupedAction(eaCut);
|
|
UnLock;
|
|
DontConsiderShiftState:=false;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.ClipPaste;
|
|
var
|
|
ShowInfo : boolean;
|
|
begin
|
|
if IsReadOnly then Exit;
|
|
DontConsiderShiftState:=true;
|
|
Lock;
|
|
AddGroupedAction(eaPaste);
|
|
if Clipboard<>nil then
|
|
begin
|
|
ShowInfo:=Clipboard^.SelEnd.Y-Clipboard^.SelStart.Y>50;
|
|
if ShowInfo then
|
|
PushInfo(msg_pastingclipboard);
|
|
InsertFrom(Clipboard);
|
|
if ShowInfo then
|
|
PopInfo;
|
|
end;
|
|
CloseGroupedAction(eaPaste);
|
|
UnLock;
|
|
DontConsiderShiftState:=false;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.Undo;
|
|
begin
|
|
NotImplemented; Exit;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.Redo;
|
|
begin
|
|
NotImplemented; Exit;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.GotoLine;
|
|
var
|
|
GotoRec: TGotoLineDialogRec;
|
|
begin
|
|
with GotoRec do
|
|
begin
|
|
LineNo:='1';
|
|
Lines:=GetLineCount;
|
|
{Linecount can be 0, but in that case there still is a cursor blinking in top
|
|
of the window, which will become line 1 as soon as sometype hits a key.}
|
|
if lines=0 then
|
|
lines:=1;
|
|
if EditorDialog(edGotoLine, @GotoRec) <> cmCancel then
|
|
begin
|
|
Lock;
|
|
SetCurPtr(0,StrToInt(LineNo)-1);
|
|
TrackCursor(do_centre);
|
|
UnLock;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.Find;
|
|
var
|
|
FindRec: TFindDialogRec;
|
|
DoConf: boolean;
|
|
begin
|
|
with FindRec do
|
|
begin
|
|
Find := FindStr;
|
|
if GetCurrentWord<>'' then
|
|
Find:=GetCurrentWord;
|
|
{$ifdef TEST_REGEXP}
|
|
Options := ((FindFlags and ffmOptionsFind) shr ffsOptions) or
|
|
((FindFlags and ffUseRegExp) shr ffsUseRegExpFind);
|
|
{$else not TEST_REGEXP}
|
|
Options := (FindFlags and ffmOptions) shr ffsOptions;
|
|
{$endif TEST_REGEXP}
|
|
Direction := (FindFlags and ffmDirection) shr ffsDirection;
|
|
Scope := (FindFlags and ffmScope) shr ffsScope;
|
|
Origin := (FindFlags and ffmOrigin) shr ffsOrigin;
|
|
DoConf:= (FindFlags and ffPromptOnReplace)<>0;
|
|
FindReplaceEditor:=@self;
|
|
if EditorDialog(edFind, @FindRec) <> cmCancel then
|
|
begin
|
|
FindStr := Find;
|
|
{$ifdef TEST_REGEXP}
|
|
FindFlags := ((Options and ffmOptionsFind) shl ffsOptions) or (Direction shl ffsDirection) or
|
|
((Options and ffmUseRegExpFind) shl ffsUseRegExpFind) or
|
|
(Scope shl ffsScope) or (Origin shl ffsOrigin);
|
|
{$else : not TEST_REGEXP}
|
|
FindFlags := ((Options and ffmOptions) shl ffsOptions) or (Direction shl ffsDirection) or
|
|
(Scope shl ffsScope) or (Origin shl ffsOrigin);
|
|
{$endif TEST_REGEXP}
|
|
FindFlags := FindFlags and not ffDoReplace;
|
|
if DoConf then
|
|
FindFlags := (FindFlags or ffPromptOnReplace);
|
|
SearchRunCount:=0;
|
|
if FindStr<>'' then
|
|
DoSearchReplace
|
|
else
|
|
EditorDialog(edSearchFailed,nil);
|
|
end;
|
|
FindReplaceEditor:=nil;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.Replace;
|
|
var
|
|
ReplaceRec: TReplaceDialogRec;
|
|
Re: word;
|
|
begin
|
|
if IsReadOnly then Exit;
|
|
with ReplaceRec do
|
|
begin
|
|
Find := FindStr;
|
|
if GetCurrentWord<>'' then
|
|
Find:=GetCurrentWord;
|
|
Replace := ReplaceStr;
|
|
{$ifdef TEST_REGEXP}
|
|
Options := (FindFlags and ffmOptions) shr ffsOptions or
|
|
(FindFlags and ffUseRegExp) shr ffsUseRegExpReplace;
|
|
{$else not TEST_REGEXP}
|
|
Options := (FindFlags and ffmOptions) shr ffsOptions;
|
|
{$endif TEST_REGEXP}
|
|
Direction := (FindFlags and ffmDirection) shr ffsDirection;
|
|
Scope := (FindFlags and ffmScope) shr ffsScope;
|
|
Origin := (FindFlags and ffmOrigin) shr ffsOrigin;
|
|
FindReplaceEditor:=@self;
|
|
Re:=EditorDialog(edReplace, @ReplaceRec);
|
|
FindReplaceEditor:=nil;
|
|
if Re <> cmCancel then
|
|
begin
|
|
FindStr := Find;
|
|
ReplaceStr := Replace;
|
|
FindFlags := (Options shl ffsOptions) or (Direction shl ffsDirection) or
|
|
{$ifdef TEST_REGEXP}
|
|
((Options and ffmUseRegExpReplace) shl ffsUseRegExpReplace) or
|
|
{$endif TEST_REGEXP}
|
|
(Scope shl ffsScope) or (Origin shl ffsOrigin);
|
|
FindFlags := FindFlags or ffDoReplace;
|
|
if Re = cmYes then
|
|
FindFlags := FindFlags or ffReplaceAll;
|
|
SearchRunCount:=0;
|
|
if FindStr<>'' then
|
|
DoSearchReplace
|
|
else
|
|
EditorDialog(edSearchFailed,nil);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.DoSearchReplace;
|
|
var S: string;
|
|
DX,DY,P,Y,X: sw_integer;
|
|
Count: sw_integer;
|
|
Found,CanExit: boolean;
|
|
SForward,DoReplace,DoReplaceAll: boolean;
|
|
{$ifdef TEST_REGEXP}
|
|
UseRegExp : boolean;
|
|
RegExpEngine : TRegExprEngine;
|
|
RegExpFlags : tregexprflags;
|
|
regexpindex,regexplen : longint;
|
|
findstrpchar : pchar;
|
|
{$endif TEST_REGEXP}
|
|
LeftOK,RightOK: boolean;
|
|
FoundCount: sw_integer;
|
|
A,B: TPoint;
|
|
AreaStart,AreaEnd: TPoint;
|
|
CanReplace,Confirm: boolean;
|
|
Re: word;
|
|
IFindStr : string;
|
|
BT : BTable;
|
|
Overwriting : boolean;
|
|
|
|
function ContainsText(const SubS:string;var S: string; Start: Sw_integer): 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
|
|
if FindStr='' then
|
|
begin
|
|
Find;
|
|
{ Find will call DoFindReplace at end again
|
|
so we need to exit directly now PM }
|
|
exit;
|
|
end;
|
|
Inc(SearchRunCount);
|
|
|
|
SForward:=(FindFlags and ffmDirection)=ffForward;
|
|
DoReplace:=(FindFlags and ffDoReplace)<>0;
|
|
Confirm:=(FindFlags and ffPromptOnReplace)<>0;
|
|
DoReplaceAll:=(FindFlags and ffReplaceAll)<>0;
|
|
{$ifdef TEST_REGEXP}
|
|
UseRegExp:=(FindFlags and ffUseRegExp)<>0;
|
|
if UseRegExp then
|
|
begin
|
|
if FindFlags and ffCaseSensitive<>0 then
|
|
RegExpFlags:=[ref_caseinsensitive]
|
|
else
|
|
RegExpFlags:=[];
|
|
getmem(findstrpchar,length(findstr)+1);
|
|
strpcopy(findstrpchar,findstr);
|
|
RegExpEngine:=GenerateRegExprEngine(findstrpchar,RegExpFlags);
|
|
strdispose(findstrpchar);
|
|
end;
|
|
{$endif TEST_REGEXP}
|
|
Count:=GetLineCount;
|
|
FoundCount:=0;
|
|
{ Empty file ? }
|
|
if Count=0 then
|
|
begin
|
|
EditorDialog(edSearchFailed,nil);
|
|
exit;
|
|
end;
|
|
|
|
if SForward then
|
|
DY:=1
|
|
else
|
|
DY:=-1;
|
|
DX:=DY;
|
|
|
|
if FindStr<>'' then
|
|
PushInfo('Looking for "'+FindStr+'"');
|
|
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;
|
|
|
|
{ set a y value being inside the areal }
|
|
Y:=Min(CurPos.Y,Count-1);
|
|
|
|
if sForward then
|
|
X:=CurPos.X-1
|
|
else
|
|
{ if you change this, pleas check that repeated backward searching for single chars still works
|
|
and that data is still found if searching starts outside the current line }
|
|
X:=Min(CurPos.X,length(GetDisplayText(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:=upcase(FindStr);
|
|
if SForward then
|
|
BMFMakeTable(IFindStr,bt)
|
|
else
|
|
BMBMakeTable(IFindStr,bt);
|
|
end;
|
|
|
|
inc(X,DX);
|
|
CanExit:=false;
|
|
if not DoReplace or (not Confirm and (Owner<>nil)) then
|
|
Owner^.Lock;
|
|
if InArea(X,Y) then
|
|
repeat
|
|
CurDY:=DY;
|
|
S:=GetDisplayText(Y);
|
|
if X>length(S)-1 then
|
|
X:=length(S)-1;
|
|
{$ifdef TEST_REGEXP}
|
|
if UseRegExp then
|
|
begin
|
|
getmem(findstrpchar,length(Copy(S,X+1,high(S)))+1);
|
|
strpcopy(findstrpchar,Copy(S,X+1,high(S)));
|
|
{ If start of line is required do check other positions PM }
|
|
if (FindStr[1]='^') and (X<>0) then
|
|
Found:=false
|
|
else
|
|
Found:=RegExprPos(RegExpEngine,findstrpchar,regexpindex,regexplen);
|
|
strdispose(findstrpchar);
|
|
P:=regexpindex+X+1;
|
|
end
|
|
else
|
|
{$endif TEST_REGEXP}
|
|
begin
|
|
P:=ContainsText(FindStr,S,X+1);
|
|
Found:=P<>0;
|
|
end;
|
|
if Found then
|
|
begin
|
|
A.X:=P-1;
|
|
A.Y:=Y;
|
|
B.Y:=Y;
|
|
{$ifdef TEST_REGEXP}
|
|
if UseRegExp then
|
|
B.X:=A.X+regexplen
|
|
else
|
|
{$endif TEST_REGEXP}
|
|
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+NumberChars) ));
|
|
RightOK:=(B.X>=length(S)) or (not( (S[B.X+1] in AlphaChars+NumberChars) ));
|
|
Found:=LeftOK and RightOK;
|
|
if not Found then
|
|
begin
|
|
CurDY:=0;
|
|
If SForward then
|
|
begin
|
|
X:=B.X+1;
|
|
if X>length(S) then
|
|
CurDY:=DY;
|
|
end
|
|
else
|
|
begin
|
|
X:=A.X-1;
|
|
if X<0 then
|
|
CurDY:=DY;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if Found then
|
|
begin
|
|
Inc(FoundCount);
|
|
Lock;
|
|
if SForward then
|
|
SetCurPtr(B.X,B.Y)
|
|
else
|
|
SetCurPtr(A.X,A.Y);
|
|
TrackCursor(do_centre);
|
|
SetHighlight(A,B);
|
|
UnLock;
|
|
CurDY:=0;
|
|
if not DoReplace 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 not confirm 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;
|
|
{ don't use SetInsertMode here because it changes the cursor shape }
|
|
overwriting:=(GetFlags and efInsertMode)=0;
|
|
SetFlags(GetFlags or efInsertMode);
|
|
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;
|
|
if overwriting then
|
|
SetFlags(GetFlags and (not efInsertMode));
|
|
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) and sForward) or (Y<0);
|
|
end;
|
|
if not CanExit then
|
|
CanExit:=(not InArea(X,Y)) and sForward;
|
|
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 FindStr<>'' then
|
|
PopInfo;
|
|
{$ifdef TEST_REGEXP}
|
|
if UseRegExp then
|
|
DestroyRegExprEngine(RegExpEngine);
|
|
{$endif TEST_REGEXP}
|
|
if (FindFlags and ffmScope)=ffSelectedText then
|
|
{ restore selection PM }
|
|
begin
|
|
SetSelection(AreaStart,AreaEnd);
|
|
end;
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetInsertMode: boolean;
|
|
begin
|
|
GetInsertMode:=(GetFlags and efInsertMode)<>0;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.SetInsertMode(InsertMode: boolean);
|
|
begin
|
|
if InsertMode then
|
|
SetFlags(GetFlags or efInsertMode)
|
|
else
|
|
SetFlags(GetFlags and (not efInsertMode));
|
|
DrawCursor;
|
|
end;
|
|
|
|
{ there is a problem with ShiftDel here
|
|
because GetShitState tells to extend the
|
|
selection which gives wrong results (PM) }
|
|
|
|
function TCustomCodeEditor.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 TCustomCodeEditor.SetCurPtr(X,Y: sw_integer);
|
|
var OldPos{,OldSEnd,OldSStart}: TPoint;
|
|
Extended: boolean;
|
|
F: PFold;
|
|
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(do_not_centre);
|
|
if not IsLineVisible(CurPos.Y) then
|
|
begin
|
|
F:=GetLineFold(CurPos.Y);
|
|
if Assigned(F) then
|
|
F^.Collapse(false);
|
|
end;
|
|
if not NoSelect 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 not ValidBlock then
|
|
SetSelection(CurPos,CurPos);
|
|
SetSelection(SelStart,CurPos); Extended:=true;
|
|
end;
|
|
CheckSels;
|
|
if not Extended 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 not IsFlagSet(efPersistentBlocks) then
|
|
begin
|
|
HideSelect;
|
|
DrawView;
|
|
end;
|
|
{ if PointOfs(SelStart)=PointOfs(SelEnd) then
|
|
SetSelection(CurPos,CurPos);}
|
|
if (GetFlags 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),not IsFlagSet(efUseTabCharacters)));
|
|
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,'',GetFlags);
|
|
if ((CurPos.X<>OldPos.X) or (CurPos.Y<>OldPos.Y)) then
|
|
PositionChanged;{UpdateIndicator;}
|
|
UnLock;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.CheckSels;
|
|
begin
|
|
if (SelStart.Y>SelEnd.Y) or
|
|
( (SelStart.Y=SelEnd.Y) and (SelStart.X>SelEnd.X) ) then
|
|
SetSelection(SelEnd,SelStart);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.CodeCompleteApply;
|
|
var S: string;
|
|
FragLen,
|
|
I: integer;
|
|
begin
|
|
Lock;
|
|
|
|
{ here should be some kind or "mark" or "break" inserted in the Undo
|
|
information, so activating it "undoes" only the completition first and
|
|
doesn't delete the complete word at once... - Gabor }
|
|
|
|
FragLen:=Length(GetCodeCompleteFrag);
|
|
S:=GetCodeCompleteWord;
|
|
for I:=FragLen+1 to length(S) do
|
|
AddChar(S[I]);
|
|
|
|
UnLock;
|
|
SetCompleteState(csInactive);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.CodeCompleteCancel;
|
|
begin
|
|
SetCompleteState(csDenied);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.CodeCompleteCheck;
|
|
var Line: string;
|
|
X: sw_integer;
|
|
CurWord,NewWord: string;
|
|
begin
|
|
SetCodeCompleteFrag('');
|
|
if (not IsFlagSet(efCodeComplete)) or (IsReadOnly=true) then Exit;
|
|
|
|
Lock;
|
|
|
|
Line:=GetDisplayText(CurPos.Y);
|
|
X:=CurPos.X; CurWord:='';
|
|
if X<=length(Line) then
|
|
while (X>0) and (Line[X] in (NumberChars+AlphaChars)) do
|
|
begin
|
|
CurWord:=Line[X]+CurWord;
|
|
Dec(X);
|
|
end;
|
|
|
|
if (length(CurWord)>=CodeCompleteMinLen) and CompleteCodeWord(CurWord,NewWord) then
|
|
begin
|
|
SetCodeCompleteFrag(CurWord);
|
|
SetCodeCompleteWord(NewWord);
|
|
end
|
|
else
|
|
ClearCodeCompleteWord;
|
|
|
|
UnLock;
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetCodeCompleteFrag: string;
|
|
begin
|
|
{ Abstract }
|
|
GetCodeCompleteFrag:='';
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.SetCodeCompleteFrag(const S: string);
|
|
begin
|
|
{ Abstract }
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.DrawLines(FirstLine: sw_integer);
|
|
begin
|
|
if FirstLine>=(Delta.Y+Size.Y) then Exit; { falls outside of the screen }
|
|
DrawView;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.HideHighlight;
|
|
begin
|
|
SetHighlight(CurPos,CurPos);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.GetSelectionArea(var StartP,EndP: TPoint);
|
|
begin
|
|
StartP:=SelStart; EndP:=SelEnd;
|
|
if EndP.X=0 then
|
|
begin
|
|
Dec(EndP.Y);
|
|
EndP.X:=length(GetDisplayText(EndP.Y))-1;
|
|
end
|
|
else
|
|
Dec(EndP.X);
|
|
end;
|
|
|
|
function TCustomCodeEditor.ValidBlock: boolean;
|
|
begin
|
|
ValidBlock:=(SelStart.X<>SelEnd.X) or (SelStart.Y<>SelEnd.Y);
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.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 TCustomCodeEditor.SetHighlight(A, B: TPoint);
|
|
begin
|
|
Highlight.A:=A; Highlight.B:=B;
|
|
HighlightChanged;
|
|
end;
|
|
|
|
{procedure TCustomCodeEditor.SetHighlightRow(Row: sw_integer);
|
|
begin
|
|
HighlightRow:=Row;
|
|
DrawView;
|
|
end;}
|
|
|
|
{procedure TCodeEditor.SetDebuggerRow(Row: sw_integer);
|
|
begin
|
|
DebuggerRow:=Row;
|
|
DrawView;
|
|
end;}
|
|
|
|
procedure TCustomCodeEditor.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 TCustomCodeEditor.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;
|
|
|
|
{ we change the CurCommandSet, but only if we are top view }
|
|
if ((State and sfFocused)<>0) then
|
|
begin
|
|
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));
|
|
SetCmdState(UndoCmd,(GetUndoActionCount>0));
|
|
SetCmdState(RedoCmd,(GetRedoActionCount>0));
|
|
Message(Application,evBroadcast,cmCommandSetChanged,nil);
|
|
end;
|
|
DrawView;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.HighlightChanged;
|
|
begin
|
|
DrawView;
|
|
end;
|
|
|
|
procedure TCustomCodeEditor.SetState(AState: Word; Enable: Boolean);
|
|
procedure ShowSBar(SBar: PScrollBar);
|
|
begin
|
|
if Assigned(SBar) and (SBar^.GetState(sfVisible)=false) then
|
|
SBar^.Show;
|
|
end;
|
|
begin
|
|
inherited SetState(AState,Enable);
|
|
|
|
if AlwaysShowScrollBars then
|
|
begin
|
|
ShowSBar(HScrollBar);
|
|
ShowSBar(VScrollBar);
|
|
end;
|
|
|
|
if (AState and (sfActive+sfSelected+sfFocused))<>0 then
|
|
begin
|
|
SelectionChanged;
|
|
if ((State and sfFocused)=0) and (GetCompleteState=csOffering) then
|
|
ClearCodeCompleteWord;
|
|
end;
|
|
end;
|
|
|
|
function TCustomCodeEditor.GetPalette: PPalette;
|
|
const P: string[length(CEditor)] = CEditor;
|
|
begin
|
|
GetPalette:=@P;
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.LoadFromStream(Editor: PCustomCodeEditor; Stream: PFastBufStream): boolean;
|
|
var S: string;
|
|
AllLinesComplete,LineComplete,hasCR,OK: boolean;
|
|
begin
|
|
DeleteAllLines;
|
|
ChangedLine:=-1;
|
|
AllLinesComplete:=true;
|
|
OK:=(Stream^.Status=stOK);
|
|
if eofstream(Stream) then
|
|
AddLine('')
|
|
else
|
|
begin
|
|
while OK and (eofstream(Stream)=false) and (GetLineCount<MaxLineCount) do
|
|
begin
|
|
if not UseOldBufStreamMethod then
|
|
Stream^.Readline(S,LineComplete,hasCR)
|
|
else
|
|
ReadlnFromStream(Stream,S,LineComplete,hasCR);
|
|
AllLinesComplete:=AllLinesComplete and LineComplete;
|
|
OK:=OK and (Stream^.Status=stOK);
|
|
if OK then AddLine(S);
|
|
if not LineComplete and (ChangedLine=-1) then
|
|
ChangedLine:=GetLineCount;
|
|
end;
|
|
{ Do not remove the final newline if it exists PM }
|
|
if hasCR then
|
|
AddLine('');
|
|
end;
|
|
LimitsChanged;
|
|
if not AllLinesComplete then
|
|
SetModified(true);
|
|
if (GetLineCount=MaxLineCount) and not eofstream(stream) then
|
|
EditorDialog(edTooManyLines,nil);
|
|
LoadFromStream:=OK;
|
|
end;
|
|
|
|
function TCustomCodeEditorCore.SaveAreaToStream(Editor: PCustomCodeEditor; Stream: PStream; StartP,EndP: TPoint): boolean;
|
|
var S: string;
|
|
OK: boolean;
|
|
Line: Sw_integer;
|
|
begin
|
|
if EndP.X=0 then
|
|
begin
|
|
if EndP.Y>0 then
|
|
begin
|
|
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
|
|
S:=GetLineText(Line);
|
|
{ Remove all traling spaces PM }
|
|
if not Editor^.IsFlagSet(efKeepTrailingSpaces) then
|
|
s:=RTrim(S,False); // removes trailing #0 too
|
|
{ if FlagSet(efUseTabCharacters) then
|
|
S:=CompressUsingTabs(S,TabSize);
|
|
}
|
|
if Line=EndP.Y then S:=copy(S,1,LinePosToCharIdx(Line,EndP.X));
|
|
if Line=StartP.Y then S:=copy(S,LinePosToCharIdx(Line,StartP.X),High(S));
|
|
Stream^.Write(S[1],length(S));
|
|
if Line<EndP.Y then
|
|
Stream^.Write(EOL[1],length(EOL));
|
|
Inc(Line);
|
|
OK:=OK and (Stream^.Status=stOK);
|
|
end;
|
|
SaveAreaToStream:=OK;
|
|
end;
|
|
|
|
|
|
constructor TEditorAction.init(act:byte; StartP,EndP:TPoint;Txt:String;AFlags : longint);
|
|
begin
|
|
Action:=act;
|
|
StartPos:=StartP;
|
|
EndPos:=EndP;
|
|
Text:=NewStr(txt);
|
|
ActionCount:=0;
|
|
Flags:=AFlags;
|
|
TimeStamp:=Now;
|
|
IsGrouped:=false;
|
|
end;
|
|
|
|
constructor TEditorAction.init_group(act:byte);
|
|
begin
|
|
Action:=act;
|
|
ActionCount:=0;
|
|
Flags:=0;
|
|
IsGrouped:=true;
|
|
end;
|
|
|
|
function TEditorAction.Is_grouped_action : boolean;
|
|
begin
|
|
Is_grouped_action:=IsGrouped;
|
|
end;
|
|
|
|
destructor TEditorAction.done;
|
|
begin
|
|
DisposeStr(Text);
|
|
inherited done;
|
|
end;
|
|
|
|
|
|
function TEditorActionCollection.At(Idx : sw_integer) : PEditorAction;
|
|
begin
|
|
At:=PEditorAction(Inherited At(Idx));
|
|
end;
|
|
|
|
procedure TEditorInputLine.HandleEvent(var Event : TEvent);
|
|
var
|
|
s,s2 : string;
|
|
i : longint;
|
|
begin
|
|
If (Event.What=evKeyDown) then
|
|
begin
|
|
if (Event.KeyCode=kbRight) and
|
|
(CurPos = Length(Data^)) and
|
|
Assigned(FindReplaceEditor) then
|
|
Begin
|
|
s:=FindReplaceEditor^.GetDisplayText(FindReplaceEditor^.CurPos.Y);
|
|
s:=Copy(s,FindReplaceEditor^.CurPos.X + 1 -length(Data^),high(s));
|
|
i:=pos(Data^,s);
|
|
if i>0 then
|
|
begin
|
|
s:=Data^+s[i+length(Data^)];
|
|
If not assigned(validator) or
|
|
Validator^.IsValidInput(s,False) then
|
|
Begin
|
|
Event.CharCode:=s[length(s)];
|
|
Event.Scancode:=0;
|
|
Inherited HandleEvent(Event);
|
|
End;
|
|
end;
|
|
ClearEvent(Event);
|
|
End
|
|
else if (Event.KeyCode=kbShiftIns) and
|
|
Assigned(Clipboard) and (Clipboard^.ValidBlock) then
|
|
{ paste from clipboard }
|
|
begin
|
|
i:=Clipboard^.SelStart.Y;
|
|
s:=Clipboard^.GetDisplayText(i);
|
|
i:=Clipboard^.SelStart.X;
|
|
if i>0 then
|
|
s:=copy(s,i+1,high(s));
|
|
if (Clipboard^.SelStart.Y=Clipboard^.SelEnd.Y) then
|
|
begin
|
|
i:=Clipboard^.SelEnd.X-i;
|
|
s:=copy(s,1,i);
|
|
end;
|
|
for i:=1 to length(s) do
|
|
begin
|
|
s2:=Data^+s[i];
|
|
If not assigned(validator) or
|
|
Validator^.IsValidInput(s2,False) then
|
|
Begin
|
|
Event.What:=evKeyDown;
|
|
Event.CharCode:=s[i];
|
|
Event.Scancode:=0;
|
|
Inherited HandleEvent(Event);
|
|
End;
|
|
end;
|
|
ClearEvent(Event);
|
|
end
|
|
else if (Event.KeyCode=kbCtrlIns) and
|
|
Assigned(Clipboard) then
|
|
{ Copy to clipboard }
|
|
begin
|
|
s:=GetStr(Data);
|
|
s:=copy(s,selstart+1,selend-selstart);
|
|
Clipboard^.SelStart:=Clipboard^.CurPos;
|
|
Clipboard^.InsertText(s);
|
|
Clipboard^.SelEnd:=Clipboard^.CurPos;
|
|
ClearEvent(Event);
|
|
end
|
|
else if (Event.KeyCode=kbShiftDel) and
|
|
Assigned(Clipboard) then
|
|
{ Cut to clipboard }
|
|
begin
|
|
s:=GetStr(Data);
|
|
s:=copy(s,selstart+1,selend-selstart);
|
|
Clipboard^.SelStart:=Clipboard^.CurPos;
|
|
Clipboard^.InsertText(s);
|
|
Clipboard^.SelEnd:=Clipboard^.CurPos;
|
|
s2:=GetStr(Data);
|
|
{ now remove the selected part }
|
|
Event.keyCode:=kbDel;
|
|
inherited HandleEvent(Event);
|
|
ClearEvent(Event);
|
|
end
|
|
else
|
|
Inherited HandleEvent(Event);
|
|
End
|
|
else
|
|
Inherited HandleEvent(Event);
|
|
s:=getstr(data);
|
|
Message(Owner,evBroadCast,cminputlinelen,pointer(length(s)));
|
|
end;
|
|
|
|
procedure TSearchHelperDialog.HandleEvent(var Event : TEvent);
|
|
begin
|
|
case Event.What of
|
|
evBroadcast :
|
|
case Event.Command of
|
|
cminputlinelen : begin
|
|
if PtrInt(Event.InfoPtr)=0 then
|
|
okbutton^.DisableCommands([cmok])
|
|
else
|
|
okbutton^.EnableCommands([cmok]);
|
|
clearevent(event);
|
|
end;
|
|
end;
|
|
end;
|
|
inherited HandleEvent(Event);
|
|
end;
|
|
|
|
|
|
function CreateFindDialog: PDialog;
|
|
var R,R1,R2: TRect;
|
|
D: PSearchHelperDialog;
|
|
IL1: PEditorInputLine;
|
|
Control : PView;
|
|
CB1: PCheckBoxes;
|
|
RB1,RB2,RB3: PRadioButtons;
|
|
but : PButton;
|
|
begin
|
|
R.Assign(0,0,56,15);
|
|
New(D, Init(R, dialog_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, label_find_texttofind, 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+{$ifdef TEST_REGEXP}3{$else}2{$endif};
|
|
New(CB1, Init(R2,
|
|
NewSItem(label_find_casesensitive,
|
|
NewSItem(label_find_wholewordsonly,
|
|
{$ifdef TEST_REGEXP}
|
|
NewSItem(label_find_useregexp,
|
|
{$endif TEST_REGEXP}
|
|
nil)))){$ifdef TEST_REGEXP}){$endif TEST_REGEXP};
|
|
Insert(CB1);
|
|
Insert(New(PLabel, Init(R1, label_find_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(label_find_forward,
|
|
NewSItem(label_find_backward,
|
|
nil))));
|
|
Insert(RB1);
|
|
Insert(New(PLabel, Init(R1, label_find_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(label_find_global,
|
|
NewSItem(label_find_selectedtext,
|
|
nil))));
|
|
Insert(RB2);
|
|
Insert(New(PLabel, Init(R1, label_find_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(label_find_fromcursor,
|
|
NewSItem(label_find_entirescope,
|
|
nil))));
|
|
Insert(RB3);
|
|
Insert(New(PLabel, Init(R1, label_find_origin, RB3)));
|
|
|
|
GetExtent(R); R.Grow(-13,-1); R.A.Y:=R.B.Y-2; R.B.X:=R.A.X+10;
|
|
Okbutton:=New(PButton, Init(R, btn_OK, cmOK, bfDefault));
|
|
Insert(OkButton);
|
|
R.Move(19,0);
|
|
Insert(New(PButton, Init(R, btn_Cancel, cmCancel, bfNormal)));
|
|
end;
|
|
IL1^.Select;
|
|
CreateFindDialog := D;
|
|
end;
|
|
|
|
function CreateReplaceDialog: PDialog;
|
|
var R,R1,R2: TRect;
|
|
D: PDialog;
|
|
Control : PView;
|
|
IL1: PEditorInputLine;
|
|
IL2: PEditorInputLine;
|
|
CB1: PCheckBoxes;
|
|
RB1,RB2,RB3: PRadioButtons;
|
|
begin
|
|
R.Assign(0,0,56,18);
|
|
New(D, Init(R, dialog_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, label_replace_texttofind, 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, label_replace_newtext, 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+{$ifdef TEST_REGEXP}4{$else}3{$endif};
|
|
New(CB1, Init(R2,
|
|
NewSItem(label_replace_casesensitive,
|
|
NewSItem(label_replace_wholewordsonly,
|
|
NewSItem(label_replace_promptonreplace,
|
|
{$ifdef TEST_REGEXP}
|
|
NewSItem(label_find_useregexp,
|
|
{$endif TEST_REGEXP}
|
|
nil))))){$ifdef TEST_REGEXP}){$endif TEST_REGEXP};
|
|
Insert(CB1);
|
|
Insert(New(PLabel, Init(R1, label_replace_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(label_replace_forward,
|
|
NewSItem(label_replace_backward,
|
|
nil))));
|
|
Insert(RB1);
|
|
Insert(New(PLabel, Init(R1, label_replace_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(label_replace_global,
|
|
NewSItem(label_replace_selectedtext,
|
|
nil))));
|
|
Insert(RB2);
|
|
Insert(New(PLabel, Init(R1, label_replace_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(label_replace_fromcursor,
|
|
NewSItem(label_replace_entirescope,
|
|
nil))));
|
|
Insert(RB3);
|
|
Insert(New(PLabel, Init(R1, label_replace_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, btn_OK, cmOK, bfDefault)));
|
|
R.Move(11,0); R.B.X:=R.A.X+14;
|
|
Insert(New(PButton, Init(R, btn_replace_changeall, cmYes, bfNormal)));
|
|
R.Move(15,0); R.B.X:=R.A.X+10;
|
|
Insert(New(PButton, Init(R, btn_Cancel, cmCancel, bfNormal)));
|
|
end;
|
|
IL1^.Select;
|
|
CreateReplaceDialog := D;
|
|
end;
|
|
|
|
function CreateGotoLineDialog(Info: pointer): PDialog;
|
|
var D: PDialog;
|
|
R,R1,R2: TRect;
|
|
Control : PView;
|
|
IL: PEditorInputLine;
|
|
begin
|
|
R.Assign(0,0,40,7);
|
|
New(D, Init(R, dialog_gotoline));
|
|
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, label_gotoline_linenumber, 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, btn_OK, cmOK, bfDefault)));
|
|
R.Move(15,0);
|
|
Insert(New(PButton, Init(R, btn_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 := AdvMessageBox(msg_notenoughmemoryforthisoperation,
|
|
nil, mfInsertInApp+ mfError + mfOkButton);
|
|
edReadError:
|
|
StdEditorDialog := AdvMessageBox(msg_errorreadingfile,
|
|
@Info, mfInsertInApp+ mfError + mfOkButton);
|
|
edWriteError:
|
|
StdEditorDialog := AdvMessageBox(msg_errorwritingfile,
|
|
@Info, mfInsertInApp+ mfError + mfOkButton);
|
|
edSaveError:
|
|
StdEditorDialog := AdvMessageBox(msg_errorsavingfile,
|
|
@Info, mfInsertInApp+ mfError + mfOkButton);
|
|
edCreateError:
|
|
StdEditorDialog := AdvMessageBox(msg_errorcreatingfile,
|
|
@Info, mfInsertInApp+ mfError + mfOkButton);
|
|
edSaveModify:
|
|
StdEditorDialog := AdvMessageBox(msg_filehasbeenmodifiedsave,
|
|
@Info, mfInsertInApp+ mfInformation + mfYesNoCancel);
|
|
edSaveUntitled:
|
|
StdEditorDialog := AdvMessageBox(msg_saveuntitledfile,
|
|
nil, mfInsertInApp+ mfInformation + mfYesNoCancel);
|
|
edChangedOnloading:
|
|
StdEditorDialog := AdvMessageBox(msg_filehadtoolonglines,
|
|
Info, mfInsertInApp+ mfOKButton + mfInformation);
|
|
edFileOnDiskChanged:
|
|
StdEditorDialog := AdvMessageBox(msg_filewasmodified,
|
|
@info, mfInsertInApp+ mfInformation + mfYesNoCancel);
|
|
edReloadDiskmodifiedFile:
|
|
StdEditorDialog := AdvMessageBox(msg_reloaddiskmodifiedfile,
|
|
@info, mfInsertInApp+ mfInformation + mfYesNoCancel);
|
|
edReloadDiskAndIDEModifiedFile:
|
|
StdEditorDialog := AdvMessageBox(msg_reloaddiskandidemodifiedfile,
|
|
@info, 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);
|
|
{$I-}
|
|
ChDir(Copy(FileDir,1,2));
|
|
EatIO;
|
|
{$I+}
|
|
end;
|
|
if FileDir<>'' then
|
|
begin
|
|
{$I-}
|
|
ChDir(TrimEndSlash(FileDir));
|
|
EatIO;
|
|
{$I+}
|
|
end;
|
|
case Dialog of
|
|
edSaveAs :
|
|
begin
|
|
Title:=dialog_savefileas;
|
|
DefExt:='*'+DefaultSaveExt;
|
|
end;
|
|
edWriteBlock :
|
|
begin
|
|
Title:=dialog_writeblocktofile;
|
|
DefExt:='*.*';
|
|
end;
|
|
edReadBlock :
|
|
begin
|
|
Title:=dialog_readblockfromfile;
|
|
DefExt:='*.*';
|
|
end;
|
|
else begin Title:='???'; DefExt:=''; end;
|
|
end;
|
|
Re:=Application^.ExecuteDialog(New(PFileDialog, Init(DefExt,
|
|
Title, label_name, fdOkButton, FileId)), @Name);
|
|
case Dialog of
|
|
edSaveAs :
|
|
begin
|
|
if ExtOf(Name)='' then
|
|
Name:=Name+DefaultSaveExt;
|
|
AskOW:=(Name<>PString(Info)^);
|
|
end;
|
|
edWriteBlock :
|
|
begin
|
|
if ExtOf(Name)='' then
|
|
Name:=Name+DefaultSaveExt;
|
|
AskOW:=true;
|
|
end;
|
|
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);
|
|
if StoreDir<>'' then
|
|
ChDir(TrimEndSlash(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 := AdvMessageBox(msg_searchstringnotfound,
|
|
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 := AdvMessageBoxRect(R, msg_replacethisoccourence,
|
|
nil, mfInsertInApp+ mfYesNoCancel + mfInformation);
|
|
end;
|
|
edReplaceFile :
|
|
StdEditorDialog :=
|
|
AdvMessageBox(msg_fileexistsoverwrite,@Info,mfInsertInApp+mfConfirmation+
|
|
mfYesButton+mfNoButton);
|
|
end;
|
|
end;
|
|
|
|
procedure RegisterWEditor;
|
|
begin
|
|
{$ifndef NOOBJREG}
|
|
{$endif}
|
|
end;
|
|
|
|
END.
|