git-svn-id: https://192.168.0.254/svn/Componentes.Terceros.jvcl@12 7f62d464-2af8-f54e-996c-e91b33f51cbe
2773 lines
73 KiB
ObjectPascal
2773 lines
73 KiB
ObjectPascal
{-----------------------------------------------------------------------------
|
|
The contents of this file are subject to the Mozilla Public License
|
|
Version 1.1 (the "License"); you may not use this file except in compliance
|
|
with the License. You may obtain a copy of the License at
|
|
http://www.mozilla.org/MPL/MPL-1.1.html
|
|
|
|
Software distributed under the License is distributed on an "AS IS" basis,
|
|
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
|
|
the specific language governing rights and limitations under the License.
|
|
|
|
The Original Code is: JvUnicodeEditor.PAS, released on 2004-01-25.
|
|
|
|
The Initial Developers of the Original Code are: Andrei Prygounkov <a dott prygounkov att gmx dott de>
|
|
Copyright (c) 1999, 2002 Andrei Prygounkov
|
|
All Rights Reserved.
|
|
|
|
Contributor(s):
|
|
Burov Dmitry, translation of russian text.
|
|
Andreas Hausladen
|
|
Peter Thörnqvist
|
|
Remko Bonte
|
|
|
|
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
|
|
located at http://jvcl.sourceforge.net
|
|
|
|
component : TJvEditor
|
|
description : 'Delphi IDE'-like Editor (unicode)
|
|
|
|
Known Issues:
|
|
Some russian comments were translated to english; these comments are marked
|
|
with [translated]
|
|
-----------------------------------------------------------------------------}
|
|
// $Id: JvUnicodeEditor.pas 11966 2008-10-18 09:48:18Z ahuser $
|
|
|
|
unit JvUnicodeEditor;
|
|
|
|
{$I jvcl.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
{$IFDEF UNITVERSIONING}
|
|
JclUnitVersioning,
|
|
{$ENDIF UNITVERSIONING}
|
|
Windows, Messages, Classes, Controls,
|
|
JclWideStrings,
|
|
JvEditorCommon;
|
|
|
|
type
|
|
TJvCustomWideEditor = class;
|
|
|
|
TJvEditorWideStrings = class(TWStringList)
|
|
private
|
|
FJvEditor: TJvCustomWideEditor;
|
|
procedure StringsChanged(Sender: TObject);
|
|
procedure SetInternal(Index: Integer; const Value: WideString);
|
|
procedure ReLine;
|
|
procedure SetLockText(const Text: WideString);
|
|
protected
|
|
procedure Put(Index: Integer; const S: {$IFDEF SUPPORTS_UNICODE}string{$ELSE}WideString{$ENDIF SUPPORTS_UNICODE}); override;
|
|
procedure InternalPut(Index: Integer; const Value: WideString);
|
|
|
|
property Internal[Index: Integer]: WideString write SetInternal;
|
|
property JvEditor: TJvCustomWideEditor read FJvEditor;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure Assign(Source: TPersistent); override;
|
|
procedure AddStrings(Strings: TWStrings); override;
|
|
procedure SetTextStr(const Value: {$IFDEF SUPPORTS_UNICODE}string{$ELSE}WideString{$ENDIF SUPPORTS_UNICODE}); override;
|
|
function Add(const S: {$IFDEF SUPPORTS_UNICODE}string{$ELSE}WideString{$ENDIF SUPPORTS_UNICODE}): Integer; override;
|
|
procedure Insert(Index: Integer; const S: {$IFDEF SUPPORTS_UNICODE}string{$ELSE}WideString{$ENDIF SUPPORTS_UNICODE}); override;
|
|
procedure Delete(Index: Integer); override;
|
|
procedure DeleteText(BegX, BegY, EndX, EndY: Integer);
|
|
procedure InsertText(X, Y: Integer; const Text: WideString);
|
|
procedure DeleteColumnText(BegX, BegY, EndX, EndY: Integer);
|
|
procedure InsertColumnText(X, Y: Integer; const Text: WideString);
|
|
end;
|
|
|
|
TJvWideCompletion = class;
|
|
TOnCompletionApplyW = procedure(Sender: TObject; const OldString: WideString;
|
|
var NewString: WideString) of object;
|
|
TJvGetLineAttrEventW = procedure(Sender: TObject; var Line: WideString; Index: Integer;
|
|
var Attrs: TLineAttrs) of object;
|
|
|
|
TJvCustomWideEditor = class(TJvCustomEditorBase)
|
|
private
|
|
{ internal objects }
|
|
FLines: TJvEditorWideStrings;
|
|
|
|
{ events }
|
|
FOnGetLineAttr: TJvGetLineAttrEventW;
|
|
FOnCompletionApply: TOnCompletionApplyW;
|
|
|
|
procedure WMGetText(var Msg: TWMGetText); message WM_GETTEXT;
|
|
|
|
{ get/set for properties }
|
|
function GetLines: TWStrings;
|
|
procedure SetLines(ALines: TWStrings);
|
|
function GetCompletion: TJvWideCompletion;
|
|
procedure SetCompletion(const Value: TJvWideCompletion);
|
|
protected
|
|
function GetLineCount: Integer; override;
|
|
function GetLineLength(Index: Integer): Integer; override;
|
|
function FindNotBlankCharPosInLine(Line: Integer): Integer; override;
|
|
|
|
{$IFDEF SUPPORTS_UNICODE}
|
|
function GetUnicodeTextLine(Y: Integer; out Text: UnicodeString): Boolean; override;
|
|
function GetUnicodeWordOnCaret: UnicodeString; override;
|
|
{$ELSE}
|
|
function GetAnsiTextLine(Y: Integer; out Text: AnsiString): Boolean; override;
|
|
function GetAnsiWordOnCaret: AnsiString; override;
|
|
{$ENDIF SUPPORTS_UNICODE}
|
|
|
|
procedure ReLine; override;
|
|
function GetTabStop(X, Y: Integer; Next: Boolean): Integer; override;
|
|
function GetBackStop(X, Y: Integer): Integer; override;
|
|
procedure TextAllChangedInternal(Unselect: Boolean); override;
|
|
protected
|
|
procedure PaintLineText(Line: Integer; ColBeg, ColEnd: Integer;
|
|
var ColPainted: Integer); override;
|
|
procedure InsertChar(const Value: Word); override;
|
|
protected
|
|
procedure SetLockText(const Text: WideString);
|
|
function ExpandTabs(const S: WideString): WideString;
|
|
function GetAutoIndentStop(Y: Integer): Integer; override;
|
|
|
|
{ triggers for descendants }
|
|
procedure GetLineAttr(var Str: WideString; Line, ColBeg, ColEnd: Integer); virtual;
|
|
function DoCommand(ACommand: TEditCommand; var X, Y: Integer;
|
|
var CaretUndo: Boolean): Boolean; override;
|
|
{ TextModified is called when the editor content has changed. }
|
|
procedure TextModified(ACaretX, ACaretY: Integer; Action: TModifiedAction;
|
|
const Text: WideString); dynamic;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
|
|
procedure ClipboardCopy; override;
|
|
procedure ClipboardPaste; override;
|
|
procedure DeleteSelected; override;
|
|
procedure Clear;
|
|
|
|
function GetSelText: WideString;
|
|
procedure SetSelText(const AValue: WideString);
|
|
function GetWordOnCaret: WideString;
|
|
procedure SelectWordOnCaret; override;
|
|
|
|
procedure InsertText(const Text: WideString);
|
|
procedure InsertColumnText(X, Y: Integer; const Text: WideString);
|
|
procedure ReplaceWord(const NewString: WideString);
|
|
procedure ReplaceWord2(const NewString: WideString);
|
|
procedure IndentColumns(X: Integer; BegY, EndY: Integer); override;
|
|
procedure UnIndentColumns(X: Integer; BegY, EndY: Integer); override;
|
|
|
|
property SelText: WideString read GetSelText write SetSelText;
|
|
public
|
|
{ published in descendants }
|
|
property Lines: TWStrings read GetLines write SetLines;
|
|
property Completion: TJvWideCompletion read GetCompletion write SetCompletion;
|
|
property OnGetLineAttr: TJvGetLineAttrEventW read FOnGetLineAttr write FOnGetLineAttr;
|
|
property OnCompletionApply: TOnCompletionApplyW read FOnCompletionApply write FOnCompletionApply;
|
|
end;
|
|
|
|
TJvWideEditor = class(TJvCustomWideEditor)
|
|
published
|
|
property BeepOnError;
|
|
property BorderStyle;
|
|
property Lines;
|
|
property ScrollBars;
|
|
property GutterWidth;
|
|
property GutterColor;
|
|
property RightMarginVisible;
|
|
property RightMargin;
|
|
property RightMarginColor;
|
|
property InsertMode;
|
|
property ReadOnly;
|
|
property DoubleClickLine;
|
|
property HideCaret;
|
|
property Completion;
|
|
property TabStops;
|
|
property SmartTab;
|
|
property BackSpaceUnindents;
|
|
property AutoIndent;
|
|
property KeepTrailingBlanks;
|
|
property CursorBeyondEOF;
|
|
property BracketHighlighting;
|
|
property SelForeColor;
|
|
property SelBackColor;
|
|
property SelBlockFormat;
|
|
|
|
property OnGetLineAttr;
|
|
property OnChangeStatus;
|
|
property OnScroll;
|
|
property OnResize;
|
|
property OnKeyDown;
|
|
property OnKeyPress;
|
|
property OnKeyUp;
|
|
property OnChange;
|
|
property OnSelectionChange;
|
|
property OnMouseDown;
|
|
property OnMouseMove;
|
|
property OnMouseUp;
|
|
property OnDblClick;
|
|
property OnPaintGutter;
|
|
property OnCompletionIdentifier;
|
|
property OnCompletionTemplate;
|
|
property OnCompletionDrawItem;
|
|
property OnCompletionMeasureItem;
|
|
property OnCompletionApply;
|
|
|
|
{ TCustomControl }
|
|
property Align;
|
|
property Enabled;
|
|
property Color;
|
|
property Font;
|
|
property ParentColor;
|
|
property ParentFont;
|
|
property ParentShowHint;
|
|
property PopupMenu;
|
|
property ShowHint;
|
|
property TabStop;
|
|
property Visible;
|
|
property Anchors;
|
|
property AutoSize;
|
|
property BiDiMode;
|
|
property Constraints;
|
|
property UseDockManager default True;
|
|
property DockSite;
|
|
property DragKind;
|
|
property ParentBiDiMode;
|
|
property OnCanResize;
|
|
property OnConstrainedResize;
|
|
property OnDockDrop;
|
|
property OnDockOver;
|
|
property OnEndDock;
|
|
property OnGetSiteInfo;
|
|
property OnStartDock;
|
|
property OnUnDock;
|
|
property OnEnter;
|
|
property OnExit;
|
|
end;
|
|
|
|
TJvWideCompletion = class(TJvCompletionBase)
|
|
private
|
|
FIdentifiers: TWStrings;
|
|
FTemplates: TWStrings;
|
|
FCaretChar: WideChar;
|
|
FCRLF: WideString;
|
|
FSeparator: WideString;
|
|
function GetStrings(Index: Integer): TWStrings;
|
|
procedure SetStrings(Index: Integer; AValue: TWStrings);
|
|
procedure ReplaceWord(const NewString: WideString);
|
|
protected
|
|
procedure FindSelItem(var Eq: Boolean); override;
|
|
procedure MakeItems; override;
|
|
procedure ReplaceWordItemIndex(SubStrStart: Integer); override;
|
|
function GetTemplateCount: Integer; override;
|
|
function GetIdentifierCount: Integer; override;
|
|
{$IFDEF SUPPORTS_UNICODE}
|
|
function GetUnicodeSeparator: UnicodeString; override;
|
|
{$ELSE}
|
|
function GetAnsiSeparator: AnsiString; override;
|
|
{$ENDIF SUPPORTS_UNICODE}
|
|
public
|
|
constructor Create(AJvEditor: TJvCustomWideEditor);
|
|
destructor Destroy; override;
|
|
published
|
|
property Identifiers: TWStrings index 0 read GetStrings write SetStrings;
|
|
property Templates: TWStrings index 1 read GetStrings write SetStrings;
|
|
property CaretChar: WideChar read FCaretChar write FCaretChar;
|
|
property CRLF: WideString read FCRLF write FCRLF;
|
|
property Separator: WideString read FSeparator write FSeparator;
|
|
end;
|
|
|
|
{$IFDEF UNITVERSIONING}
|
|
const
|
|
UnitVersioning: TUnitVersionInfo = (
|
|
RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/branches/JVCL3_36_PREPARATION/run/JvUnicodeEditor.pas $';
|
|
Revision: '$Revision: 11966 $';
|
|
Date: '$Date: 2008-10-18 11:48:18 +0200 (sam., 18 oct. 2008) $';
|
|
LogPath: 'JVCL\run'
|
|
);
|
|
{$ENDIF UNITVERSIONING}
|
|
|
|
implementation
|
|
|
|
uses
|
|
Consts,
|
|
{$IFDEF HAS_UNIT_RTLCONSTS}
|
|
RTLConsts,
|
|
{$ENDIF HAS_UNIT_RTLCONSTS}
|
|
SysUtils, Math, Graphics, Clipbrd,
|
|
JvUnicodeCanvas, JvJCLUtils, JvThemes, JvConsts, JvResources;
|
|
|
|
type
|
|
TJvInsertUndo = class(TJvCaretUndo)
|
|
private
|
|
FText: WideString;
|
|
function GetEditor: TJvCustomWideEditor;
|
|
public
|
|
constructor Create(AJvEditor: TJvCustomWideEditor; ACaretX, ACaretY: Integer;
|
|
const AText: WideString);
|
|
procedure Undo; override;
|
|
end;
|
|
|
|
TJvOverwriteUndo = class(TJvCaretUndo)
|
|
private
|
|
FOldText: WideString;
|
|
FNewText: WideString;
|
|
function GetEditor: TJvCustomWideEditor;
|
|
public
|
|
constructor Create(AJvEditor: TJvCustomWideEditor; ACaretX, ACaretY: Integer;
|
|
const AOldText, ANewText: WideString);
|
|
procedure Undo; override;
|
|
end;
|
|
|
|
TJvReLineUndo = class(TJvInsertUndo, IJvUndoCompound);
|
|
|
|
TJvInsertTabUndo = class(TJvInsertUndo);
|
|
|
|
TJvInsertColumnUndo = class(TJvInsertUndo)
|
|
public
|
|
procedure Undo; override;
|
|
end;
|
|
|
|
TJvDeleteUndo = class(TJvInsertUndo)
|
|
public
|
|
procedure Undo; override;
|
|
end;
|
|
|
|
TJvDeleteLineUndo = class(TJvInsertUndo)
|
|
private
|
|
FLastLineDelete: Boolean;
|
|
public
|
|
constructor Create(AJvEditor: TJvCustomWideEditor; ACaretX, ACaretY: Integer;
|
|
const AText: WideString; ALastLineDelete: Boolean);
|
|
procedure Undo; override;
|
|
//procedure Redo; override;
|
|
end;
|
|
|
|
TJvDeleteTrailUndo = class(TJvDeleteUndo, IJvUndoCompound);
|
|
|
|
TJvBackspaceUndo = class(TJvDeleteUndo, IJvBackspaceUndo)
|
|
public
|
|
procedure Undo; override;
|
|
end;
|
|
|
|
TJvBackspaceUnindentUndo = class(TJvDeleteUndo, IJvBackspaceUnindentUndo)
|
|
public
|
|
procedure Undo; override;
|
|
end;
|
|
|
|
TJvReplaceUndo = class(TJvCaretUndo)
|
|
private
|
|
FBegX: Integer;
|
|
FBegY: Integer;
|
|
FText: WideString;
|
|
FNewText: WideString;
|
|
function GetEditor: TJvCustomWideEditor;
|
|
public
|
|
constructor Create(AJvEditor: TJvCustomWideEditor; ACaretX, ACaretY: Integer;
|
|
ABegX, ABegY: Integer; const AText, ANewText: WideString);
|
|
procedure Undo; override;
|
|
end;
|
|
|
|
TJvDeleteSelectedUndo = class(TJvDeleteUndo)
|
|
public
|
|
constructor Create(AJvEditor: TJvCustomWideEditor; ACaretX, ACaretY: Integer;
|
|
const AText: WideString);
|
|
procedure Undo; override;
|
|
end;
|
|
|
|
(* // (ahuser) make Delphi 5 compiler happy
|
|
TJvIndentColumnUndo = class(TJvInsertColumnUndo)
|
|
private
|
|
FNewCaretX: Integer;
|
|
FNewCaretY: Integer;
|
|
public
|
|
constructor Create(AJvEditor: TJvCustomWideEditor; ACaretX, ACaretY: Integer;
|
|
ABegX, ABegY: Integer; const AText: WideString);
|
|
procedure Undo; override;
|
|
end;
|
|
*)
|
|
|
|
TJvUnindentColumnUndo = class(TJvInsertUndo)
|
|
private
|
|
FBegX: Integer;
|
|
FBegY: Integer;
|
|
public
|
|
constructor Create(AJvEditor: TJvCustomWideEditor; ACaretX, ACaretY,
|
|
ABegX, ABegY: Integer; const AText: WideString);
|
|
procedure Undo; override;
|
|
end;
|
|
|
|
TJvUndoBufferAccessProtected = class(TJvUndoBuffer);
|
|
|
|
//=== { TJvEditorWideStrings } ===============================================
|
|
|
|
constructor TJvEditorWideStrings.Create;
|
|
begin
|
|
inherited Create;
|
|
OnChange := StringsChanged;
|
|
end;
|
|
|
|
destructor TJvEditorWideStrings.Destroy;
|
|
begin
|
|
OnChange := nil;
|
|
OnChanging := nil;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TJvEditorWideStrings.Assign(Source: TPersistent);
|
|
begin
|
|
JvEditor.BeginUpdate;
|
|
try
|
|
inherited Assign(Source);
|
|
JvEditor.NotUndoable;
|
|
JvEditor.TextAllChanged;
|
|
finally
|
|
JvEditor.EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvEditorWideStrings.AddStrings(Strings: TWStrings);
|
|
begin
|
|
JvEditor.BeginUpdate;
|
|
try
|
|
inherited AddStrings(Strings);
|
|
JvEditor.NotUndoable;
|
|
finally
|
|
JvEditor.EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvEditorWideStrings.SetTextStr(const Value: {$IFDEF SUPPORTS_UNICODE}string{$ELSE}WideString{$ENDIF SUPPORTS_UNICODE});
|
|
begin
|
|
inherited SetTextStr(JvEditor.ExpandTabs(Value));
|
|
if JvEditor.UpdateLock = 0 then
|
|
JvEditor.NotUndoable;
|
|
JvEditor.TextAllChanged;
|
|
end;
|
|
|
|
procedure TJvEditorWideStrings.StringsChanged(Sender: TObject);
|
|
begin
|
|
if JvEditor.UpdateLock = 0 then
|
|
JvEditor.TextAllChanged;
|
|
end;
|
|
|
|
procedure TJvEditorWideStrings.SetLockText(const Text: WideString);
|
|
begin
|
|
JvEditor.LockUpdate;
|
|
try
|
|
inherited SetTextStr(Text)
|
|
finally
|
|
JvEditor.UnlockUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvEditorWideStrings.SetInternal(Index: Integer; const Value: WideString);
|
|
begin
|
|
JvEditor.LockUpdate;
|
|
try
|
|
InternalPut(Index, Value);
|
|
finally
|
|
JvEditor.UnlockUpdate;
|
|
end;
|
|
end;
|
|
|
|
function TJvEditorWideStrings.Add(const S: {$IFDEF SUPPORTS_UNICODE}string{$ELSE}WideString{$ENDIF SUPPORTS_UNICODE}): Integer;
|
|
begin
|
|
Result := inherited Add(JvEditor.ExpandTabs(S));
|
|
end;
|
|
|
|
procedure TJvEditorWideStrings.Insert(Index: Integer; const S: {$IFDEF SUPPORTS_UNICODE}string{$ELSE}WideString{$ENDIF SUPPORTS_UNICODE});
|
|
begin
|
|
inherited Insert(Index, JvEditor.ExpandTabs(S));
|
|
JvEditor.LineInserted(Index);
|
|
end;
|
|
|
|
procedure TJvEditorWideStrings.Delete(Index: Integer);
|
|
begin
|
|
inherited Delete(Index);
|
|
JvEditor.LineDeleted(Index);
|
|
end;
|
|
|
|
procedure TJvEditorWideStrings.Put(Index: Integer; const S: {$IFDEF SUPPORTS_UNICODE}string{$ELSE}WideString{$ENDIF SUPPORTS_UNICODE});
|
|
var
|
|
L: Integer;
|
|
begin
|
|
if JvEditor.KeepTrailingBlanks then
|
|
inherited Put(Index, S)
|
|
else
|
|
begin
|
|
L := Length(S) - TrimRightLengthW(S);
|
|
if L = 0 then
|
|
inherited Put(Index, S)
|
|
else
|
|
begin
|
|
{--- UNDO ---}
|
|
TJvDeleteTrailUndo.Create(JvEditor, Length(S), Index, SpacesW(L));
|
|
{--- /UNDO ---}
|
|
inherited Put(Index, TrimRightW(S));
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvEditorWideStrings.ReLine;
|
|
var
|
|
L: Integer;
|
|
S: WideString;
|
|
begin
|
|
JvEditor.LockUpdate;
|
|
try
|
|
if Count = 0 then
|
|
L := JvEditor.CaretX
|
|
else
|
|
L := Length(Strings[Count - 1]);
|
|
while JvEditor.CaretY > Count - 1 do
|
|
begin
|
|
{--- UNDO ---}
|
|
TJvReLineUndo.Create(JvEditor, L, JvEditor.CaretY, sLineBreakStr);
|
|
{--- /UNDO ---}
|
|
L := 0;
|
|
Add('');
|
|
end;
|
|
S := Strings[JvEditor.CaretY];
|
|
if JvEditor.CaretX > Length(S) then
|
|
begin
|
|
L := JvEditor.CaretX - Length(S);
|
|
{--- UNDO ---}
|
|
{ TJvReLineUndo.Create(JvEditor, Length(S),
|
|
JvEditor.CaretY, SpacesW(L)); } {disabled: moves the caret to wrong undo position }
|
|
{--- /UNDO ---}
|
|
inherited Put(JvEditor.CaretY, S + SpacesW(L));
|
|
end;
|
|
finally
|
|
JvEditor.UnlockUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvEditorWideStrings.InternalPut(Index: Integer; const Value: WideString);
|
|
begin
|
|
if JvEditor.KeepTrailingBlanks then
|
|
inherited Put(Index, JvEditor.ExpandTabs(Value))
|
|
else
|
|
inherited Put(Index, TrimRightW(JvEditor.ExpandTabs(Value)));
|
|
end;
|
|
|
|
procedure TJvEditorWideStrings.DeleteText(BegX, BegY, EndX, EndY: Integer);
|
|
{ delete text from [BegX..EndY] [BegY..EndY] all inclusive.
|
|
BegX,EndX: [0..Max_X] }
|
|
var
|
|
BegLine, EndLine: WideString;
|
|
I, L: Integer;
|
|
begin
|
|
if BegY < 0 then
|
|
begin
|
|
BegY := 0;
|
|
BegX := 0;
|
|
end;
|
|
if BegY >= Count then
|
|
Exit; // nothing to delete
|
|
if EndY >= Count then
|
|
begin
|
|
EndY := Count - 1;
|
|
EndX := MaxInt - 1;
|
|
end;
|
|
if BegX < 0 then
|
|
BegX := 0;
|
|
|
|
JvEditor.LockUpdate;
|
|
BeginUpdate;
|
|
try
|
|
BegLine := Strings[BegY];
|
|
// expand BegLine if necessary
|
|
L := (BegX + 1) - Length(BegLine) - 1;
|
|
if L > 0 then
|
|
BegLine := BegLine + SpacesW(L);
|
|
|
|
EndLine := Strings[EndY];
|
|
|
|
// delete lines between and end line
|
|
for I := EndY downto BegY + 1 do
|
|
Delete(I);
|
|
|
|
System.Delete(BegLine, BegX + 1, MaxInt);
|
|
System.Delete(EndLine, 1, EndX + 1);
|
|
|
|
Internal[BegY] := BegLine + EndLine;
|
|
finally
|
|
EndUpdate;
|
|
JvEditor.UnlockUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvEditorWideStrings.InsertText(X, Y: Integer; const Text: WideString);
|
|
{ insert text on X:[0..Max_X], Y }
|
|
var
|
|
BegLine, EndLine: WideString;
|
|
YStart: Integer;
|
|
F, P: PWideChar;
|
|
S, FirstLine: WideString;
|
|
Len: Integer;
|
|
begin
|
|
Inc(X); // increment for WideString functions
|
|
if Y < 0 then
|
|
Y := 0;
|
|
while Y >= Count do
|
|
Add('');
|
|
|
|
BegLine := Strings[Y];
|
|
EndLine := System.Copy(BegLine, X, MaxInt);
|
|
System.Delete(BegLine, X, MaxInt);
|
|
|
|
// line is too small -> expand it with spaces
|
|
Len := Length(BegLine);
|
|
if Len < X then
|
|
begin
|
|
SetLength(BegLine, X - 1);
|
|
FillChar(BegLine[Len + 1], X - Len - 1, ' ');
|
|
end;
|
|
|
|
JvEditor.LockUpdate;
|
|
BeginUpdate;
|
|
try
|
|
P := PWideChar(Text);
|
|
F := P;
|
|
while (P[0] <> #0) and (P[0] <> Lf) and (P[0] <> Cr) do
|
|
Inc(P);
|
|
|
|
SetString(S, F, P - F);
|
|
|
|
YStart := Y;
|
|
FirstLine := BegLine + S; // set Internal[YStart] later so we keep the trailing spaces for concat EndLine
|
|
|
|
while P[0] <> #0 do
|
|
begin
|
|
if P[0] = Cr then
|
|
Inc(P);
|
|
if P[0] = Lf then
|
|
Inc(P);
|
|
F := P;
|
|
|
|
while (P[0] <> #0) and (P[0] <> Lf) and (P[0] <> Cr) do
|
|
Inc(P);
|
|
SetString(S, F, P - F);
|
|
Inc(Y);
|
|
Insert(Y, S);
|
|
end;
|
|
|
|
if Y = YStart then
|
|
Internal[YStart] := FirstLine + EndLine
|
|
else
|
|
begin
|
|
Internal[YStart] := FirstLine;
|
|
Internal[Y] := Strings[Y] + EndLine;
|
|
end;
|
|
finally
|
|
EndUpdate;
|
|
JvEditor.UnlockUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvEditorWideStrings.DeleteColumnText(BegX, BegY, EndX, EndY: Integer);
|
|
{ delete column text from [BegX..EndY] [BegY..EndY] all inclusive.
|
|
BegX,EndX: [0..Max_X] }
|
|
var
|
|
S: WideString;
|
|
I: Integer;
|
|
begin
|
|
if BegY < 0 then
|
|
begin
|
|
BegY := 0;
|
|
BegX := 0;
|
|
end;
|
|
if BegY >= Count then
|
|
Exit; // nothing to delete
|
|
if EndY >= Count then
|
|
begin
|
|
EndY := Count - 1;
|
|
EndX := MaxInt - 1;
|
|
end;
|
|
if BegX < 0 then
|
|
BegX := 0;
|
|
|
|
JvEditor.LockUpdate;
|
|
BeginUpdate;
|
|
try
|
|
for I := BegY to EndY do
|
|
begin
|
|
S := JvEditor.FLines[I];
|
|
System.Delete(S, BegX + 1, EndX - BegX + 1);
|
|
JvEditor.FLines.Internal[I] := S;
|
|
end;
|
|
finally
|
|
EndUpdate;
|
|
JvEditor.UnlockUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvEditorWideStrings.InsertColumnText(X, Y: Integer; const Text: WideString);
|
|
{ insert column text on X:[0..Max_X], Y }
|
|
var
|
|
S, Line: WideString;
|
|
P, F: PWideChar;
|
|
L: Integer;
|
|
begin
|
|
Inc(X); // increment for WideString functions
|
|
if Y < 0 then
|
|
Y := 0;
|
|
|
|
JvEditor.LockUpdate;
|
|
BeginUpdate;
|
|
try
|
|
P := PWideChar(Text);
|
|
F := P;
|
|
while P[0] <> #0 do
|
|
begin
|
|
while (P[0] <> #0) and (P[0] <> Lf) and (P[0] <> Cr) do
|
|
Inc(P);
|
|
SetString(S, F, P - F);
|
|
|
|
while Y >= Count do
|
|
Add('');
|
|
Line := Strings[Y];
|
|
L := (X - 1) - Length(Line);
|
|
if L > 0 then
|
|
Line := Line + SpacesW(L);
|
|
System.Insert(S, Line, X);
|
|
Internal[Y] := Line;
|
|
|
|
if P[0] = Cr then
|
|
Inc(P);
|
|
if P[0] = Lf then
|
|
Inc(P);
|
|
F := P;
|
|
Inc(Y);
|
|
end;
|
|
finally
|
|
EndUpdate;
|
|
JvEditor.UnlockUpdate;
|
|
end;
|
|
end;
|
|
|
|
//=== { TJvCustomWideEditor } ================================================
|
|
|
|
constructor TJvCustomWideEditor.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
FLines := TJvEditorWideStrings.Create;
|
|
FLines.FJvEditor := Self;
|
|
FLines.OnChange := DoLinesChange;
|
|
Completion := TJvWideCompletion.Create(Self);
|
|
end;
|
|
|
|
destructor TJvCustomWideEditor.Destroy;
|
|
begin
|
|
FLines.Free;
|
|
Completion.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TJvCustomWideEditor.PaintLineText(Line: Integer; ColBeg, ColEnd: Integer;
|
|
var ColPainted: Integer);
|
|
var
|
|
Ch: WideString;
|
|
iC, jC, SL, MX: Integer;
|
|
R: TRect;
|
|
S: WideString;
|
|
LA: TLineAttr;
|
|
jCStart, Len: Integer;
|
|
MyDi: TDynIntArray;
|
|
begin
|
|
with EditorClient do
|
|
begin
|
|
S := FLines[Line];
|
|
|
|
Len := Max(Length(S), Max_X) + 1;
|
|
if Len > Length(LineAttrs) then
|
|
SetLength(LineAttrs, Len)
|
|
else
|
|
if Len + 128 < Length(LineAttrs) then
|
|
SetLength(LineAttrs, Len);
|
|
|
|
GetLineAttr(S, Line, ColBeg, ColEnd);
|
|
|
|
{left line}
|
|
if Canvas.Brush.Color <> LineAttrs[LeftCol + 1].BC then // change GDI object only if necessary
|
|
Canvas.Brush.Color := LineAttrs[LeftCol + 1].BC;
|
|
|
|
Canvas.FillRect(Bounds(EditorClient.Left, (Line - TopRow) *
|
|
CellRect.Height, 1, CellRect.Height));
|
|
{optimized, paint group of chars with identical attributes}
|
|
SL := Length(S);
|
|
MX := ColEnd;
|
|
|
|
if Length(FMyDi) < MX then
|
|
begin
|
|
SetLength(MyDi, MX);
|
|
for iC := 0 to High(MyDi) do
|
|
MyDi[iC] := CellRect.Width;
|
|
end
|
|
else
|
|
MyDi := FMyDi;
|
|
|
|
while ColPainted < MX do
|
|
begin
|
|
with Canvas do
|
|
begin
|
|
iC := ColPainted + 1;
|
|
LA := LineAttrs[iC];
|
|
jC := iC + 1;
|
|
if iC <= SL then
|
|
Ch := S[iC]
|
|
else
|
|
Ch := ' ';
|
|
jCStart := jC;
|
|
while (jC <= MX + 1) and
|
|
CompareMem(@LA, @LineAttrs[jC], SizeOf(LineAttrs[1])) do
|
|
Inc(jC);
|
|
Ch := Copy(S, jCStart - 1, jC - jCStart + 1);
|
|
if jC > SL + 1 then
|
|
Ch := Ch + Spaces(jC - SL - 1);
|
|
|
|
if Brush.Color <> LA.BC then // change GDI object only if necessary
|
|
Brush.Color := LA.BC;
|
|
Font.Assign(FontCacheFind(LA));
|
|
|
|
R := CalcCellRect(ColPainted - LeftCol, Line - TopRow);
|
|
{bottom line}
|
|
FillRect(Bounds(R.Left, R.Bottom - 1, CellRect.Width * Length(Ch), 1));
|
|
|
|
TJvUnicodeCanvas(Canvas).ExtTextOutW(R.Left, R.Top, [etoOpaque, etoClipped], nil, Ch, @FMyDi[0]);
|
|
ErrorHighlighting.PaintError(Canvas, ColPainted + 1, Line, R, Length(Ch), MyDi);
|
|
|
|
if LA.Border <> clNone then
|
|
begin
|
|
Pen.Color := LA.Border;
|
|
R.Right := R.Left + CellRect.Width * Length(Ch);
|
|
Dec(R.Left);
|
|
Brush.Style := bsClear;
|
|
Rectangle(R);
|
|
Brush.Style := bsSolid;
|
|
end;
|
|
|
|
ColPainted := jC - 1;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomWideEditor.GetLineAttr(var Str: WideString; Line, ColBeg, ColEnd: Integer);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if ColBeg < 0 then
|
|
ColBeg := 0;
|
|
if ColEnd > Max_X then
|
|
ColEnd := Max_X;
|
|
|
|
SetLength(LineAttrs, Max(Length(LineAttrs), Max_X + 1));
|
|
|
|
LineAttrs[ColBeg].Style := Font.Style;
|
|
LineAttrs[ColBeg].FC := Font.Color;
|
|
LineAttrs[ColBeg].BC := Color;
|
|
LineAttrs[ColBeg].Border := clNone;
|
|
|
|
{ for I := ColBeg + 1 to ColEnd do
|
|
Move(LineAttrs[ColBeg], LineAttrs[I], SizeOf(LineAttrs[1]));}
|
|
for I := ColBeg + 1 to ColEnd do
|
|
LineAttrs[I] := LineAttrs[ColBeg];
|
|
|
|
GetAttr(Line, ColBeg, ColEnd);
|
|
if Assigned(FOnGetLineAttr) then
|
|
FOnGetLineAttr(Self, Str, Line, LineAttrs);
|
|
ChangeAttr(Line, ColBeg, ColEnd);
|
|
end;
|
|
|
|
{$IFDEF SUPPORTS_UNICODE}
|
|
function TJvCustomWideEditor.GetUnicodeTextLine(Y: Integer; out Text: UnicodeString): Boolean;
|
|
begin
|
|
if (Y >= 0) and (Y < Lines.Count) then
|
|
begin
|
|
Text := Lines[Y];
|
|
Result := True;
|
|
end
|
|
else
|
|
begin
|
|
Text := '';
|
|
Result := False;
|
|
end;
|
|
end;
|
|
|
|
function TJvCustomWideEditor.GetUnicodeWordOnCaret: UnicodeString;
|
|
begin
|
|
Result := GetWordOnCaret;
|
|
end;
|
|
|
|
{$ELSE}
|
|
|
|
function TJvCustomWideEditor.GetAnsiTextLine(Y: Integer; out Text: AnsiString): Boolean;
|
|
begin
|
|
if (Y >= 0) and (Y < Lines.Count) then
|
|
begin
|
|
Text := Lines[Y];
|
|
Result := True;
|
|
end
|
|
else
|
|
begin
|
|
Text := '';
|
|
Result := False;
|
|
end;
|
|
end;
|
|
|
|
function TJvCustomWideEditor.GetAnsiWordOnCaret: AnsiString;
|
|
begin
|
|
Result := GetWordOnCaret;
|
|
end;
|
|
{$ENDIF SUPPORTS_UNICODE}
|
|
|
|
procedure TJvCustomWideEditor.ReLine;
|
|
begin
|
|
FLines.ReLine;
|
|
end;
|
|
|
|
procedure TJvCustomWideEditor.InsertChar(const Value: Word);
|
|
var
|
|
S: WideString;
|
|
X, Y, iBeg: Integer;
|
|
WasSelected: Boolean;
|
|
Key: WideChar;
|
|
begin
|
|
Key := WideChar(Value);
|
|
WasSelected := (FSelection.IsSelected) and (not PersistentBlocks);
|
|
if Value >= 32 then
|
|
//if Key in [#32..#255] then
|
|
begin
|
|
if not HasChar(Char(Key), JvEditorCompletionChars) then
|
|
Completion.DoKeyPress(Char(Key));
|
|
|
|
RemoveSelectedBlock;
|
|
|
|
ReLine; // need ReLine after DeleteSelection
|
|
S := FLines[CaretY];
|
|
if InsertMode then
|
|
begin
|
|
{--- UNDO ---}
|
|
TJvInsertUndo.Create(Self, CaretX, CaretY, Key);
|
|
{--- /UNDO ---}
|
|
Insert(Key, S, CaretX + 1);
|
|
|
|
AdjustPersistentBlockSelection(CaretX, CaretY, amInsert, [1]);
|
|
end
|
|
else
|
|
begin
|
|
{--- UNDO ---}
|
|
if CaretX + 1 <= Length(S) then
|
|
TJvOverwriteUndo.Create(Self, CaretX, CaretY, S[CaretX + 1], Key)
|
|
else
|
|
TJvOverwriteUndo.Create(Self, CaretX, CaretY, '', Key);
|
|
{--- /UNDO ---}
|
|
if CaretX + 1 <= Length(S) then
|
|
S[CaretX + 1] := WideChar(Key)
|
|
else
|
|
S := S + Key
|
|
end;
|
|
FLines.Internal[CaretY] := S;
|
|
SetCaretInternal(CaretX + 1, CaretY);
|
|
TextModified(CaretX, CaretY, maInsert, Key);
|
|
PaintLine(CaretY, -1, -1);
|
|
Changed;
|
|
|
|
if HasChar(Char(Key), JvEditorCompletionChars) then
|
|
Completion.DoKeyPress(Char(Key));
|
|
end
|
|
else
|
|
case Key of
|
|
Cr:
|
|
begin
|
|
if InsertMode then
|
|
begin
|
|
if WasSelected then // compound only on selection deletion
|
|
BeginCompound;
|
|
LockUpdate;
|
|
try
|
|
RemoveSelectedBlock; // adjusts CaretX, CaretY
|
|
X := CaretX;
|
|
Y := CaretY;
|
|
{ --- UNDO --- }
|
|
TJvInsertUndo.Create(Self, CaretX, CaretY, sLineBreakStr);
|
|
{ --- /UNDO --- }
|
|
if FLines.Count = 0 then
|
|
FLines.Add('');
|
|
ReLine;
|
|
|
|
S := Copy(FLines[Y], X + 1, MaxInt);
|
|
FLines.Insert(Y + 1, S);
|
|
FLines.Internal[Y] := Copy(FLines[Y], 1, X);
|
|
Inc(Y);
|
|
{ auto indent }
|
|
if AutoIndent and
|
|
(((Length(FLines[CaretY]) > 0) and
|
|
(FLines[CaretY][1] = ' ')) or
|
|
((TrimW(FLines[CaretY]) = '') and (X > 0))) then
|
|
begin
|
|
X := GetAutoIndentStop(Y);
|
|
if X > 0 then
|
|
begin
|
|
{ --- UNDO --- }
|
|
TJvInsertUndo.Create(Self, 0, Y, SpacesW(X));
|
|
{ --- /UNDO --- }
|
|
FLines.Internal[Y] := SpacesW(X) + FLines[Y];
|
|
end;
|
|
end
|
|
else
|
|
X := 0;
|
|
|
|
// persistent blocks: adjust selection
|
|
AdjustPersistentBlockSelection(CaretX, CaretY, amLineBreak, []);
|
|
|
|
UpdateEditorSize;
|
|
TextModified(CaretX - 1, CaretY, maInsert, sLineBreakStr);
|
|
finally
|
|
UnlockUpdate;
|
|
if WasSelected then
|
|
EndCompound;
|
|
end;
|
|
Invalidate;
|
|
Changed;
|
|
end
|
|
else // Overwrite-mode
|
|
begin
|
|
if WasSelected then // compound only on selection deletion
|
|
BeginCompound;
|
|
try
|
|
RemoveSelectedBlock;
|
|
X := CaretX;
|
|
Y := CaretY;
|
|
Inc(Y);
|
|
if Y >= FLines.Count then
|
|
begin
|
|
LockUpdate;
|
|
try
|
|
{ --- UNDO --- }
|
|
TJvInsertUndo.Create(Self, CaretX, CaretY, sLineBreakStr);
|
|
{ --- /UNDO --- }
|
|
FLines.Add('');
|
|
finally
|
|
UnlockUpdate;
|
|
end;
|
|
TextModified(0, Y - 1, maInsert, sLineBreakStr);
|
|
UpdateEditorSize;
|
|
Invalidate;
|
|
Changed;
|
|
end;
|
|
if Y < FLines.Count then
|
|
begin
|
|
S := FLines[Y];
|
|
if Length(S) > 0 then
|
|
begin
|
|
iBeg := FindNotBlankCharPos(S) - 1;
|
|
if iBeg < X then
|
|
X := iBeg;
|
|
end;
|
|
end;
|
|
finally
|
|
if WasSelected then
|
|
EndCompound;
|
|
end;
|
|
end;
|
|
SetCaretInternal(X, Y);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomWideEditor.SelectWordOnCaret;
|
|
var
|
|
iBeg, iEnd: Integer;
|
|
begin
|
|
if (CaretY >= 0) and (CaretY < LineCount) and (TrimW(FLines[CaretY]) <> '') then
|
|
begin
|
|
iEnd := Length(TrimRightW(FLines[CaretY]));
|
|
if FCaretX < iEnd then
|
|
while FLines[FCaretY][FCaretX + 1] <= ' ' do
|
|
Inc(FCaretX)
|
|
else
|
|
begin
|
|
FCaretX := iEnd - 1;
|
|
while FLines[FCaretY][FCaretX + 1] <= ' ' do
|
|
Dec(FCaretX);
|
|
end;
|
|
if GetWordOnPosEx(FLines[FCaretY] + ' ', FCaretX + 1, iBeg, iEnd) <> '' then
|
|
begin
|
|
PaintCaret(False);
|
|
SetSel(iBeg - 1, FCaretY);
|
|
SetSel(iEnd - 1, FCaretY);
|
|
SetCaret(iEnd - 1, FCaretY);
|
|
PaintCaret(True);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TJvCustomWideEditor.DoCommand(ACommand: TEditCommand; var X, Y: Integer;
|
|
var CaretUndo: Boolean): Boolean;
|
|
|
|
type
|
|
TPr = procedure of object;
|
|
|
|
procedure DoAndCorrectXY(Pr: TPr);
|
|
begin
|
|
Pr;
|
|
X := CaretX;
|
|
Y := CaretY;
|
|
CaretUndo := False;
|
|
end;
|
|
|
|
procedure SetSel1(X, Y: Integer);
|
|
begin
|
|
SetSel(X, Y);
|
|
CaretUndo := False;
|
|
end;
|
|
|
|
procedure SetSelText1(const S: WideString);
|
|
begin
|
|
SelText := S;
|
|
CaretUndo := False;
|
|
end;
|
|
|
|
var
|
|
F: Integer;
|
|
S, S2: WideString;
|
|
B: Boolean;
|
|
iBeg, iEnd: Integer;
|
|
begin
|
|
Result := True;
|
|
X := CaretX;
|
|
Y := CaretY;
|
|
case ACommand of
|
|
{ caret movements }
|
|
ecPrevWord, ecSelPrevWord, ecBackspaceWord:
|
|
begin
|
|
if (ACommand = ecSelPrevWord) and IsNewSelection then
|
|
SetSel1(CaretX, CaretY);
|
|
if Y >= FLines.Count then
|
|
Exit;
|
|
|
|
S := FLines[Y];
|
|
B := False;
|
|
if CaretX > Length(S) then
|
|
begin
|
|
X := Length(S);
|
|
SetSel1(X, Y);
|
|
end
|
|
else
|
|
begin
|
|
for F := X - 1 downto 0 do
|
|
begin
|
|
if B then
|
|
begin
|
|
if CharInSetW(S[F + 1], Separators) then
|
|
begin
|
|
X := F + 1;
|
|
Break;
|
|
end;
|
|
end
|
|
else
|
|
if not CharInSetW(S[F + 1], Separators) then
|
|
B := True;
|
|
end;
|
|
|
|
if X = CaretX then
|
|
X := 0;
|
|
if ACommand <> ecBackspaceWord then
|
|
begin
|
|
{ Jump to previous line and last word ending }
|
|
if (X = 0) and (Y > 0) then
|
|
begin
|
|
if (Y > FLines.Count) or (CaretX = 0) or (FLines[Y] = '') or
|
|
CharInSetW(FLines[Y][1], Separators) then
|
|
begin
|
|
Y := Y - 1;
|
|
X := Length(FLines[Y]);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if ACommand = ecSelPrevWord then
|
|
SetSel1(X, Y)
|
|
else
|
|
PersistentBlocksSetUnSelected;
|
|
|
|
if (ACommand = ecBackspaceWord) and (Y >= 0) and (X <> CaretX) then
|
|
begin
|
|
if not ReadOnly then
|
|
begin
|
|
BeginCompound;
|
|
try
|
|
SelectRange(X, CaretY, CaretX, CaretY);
|
|
DeleteSelected;
|
|
finally
|
|
EndCompound;
|
|
end;
|
|
ReLine;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
ecNextWord, ecSelNextWord:
|
|
begin
|
|
if (ACommand = ecSelNextWord) and IsNewSelection then
|
|
SetSel1(CaretX, CaretY);
|
|
if Y >= FLines.Count then
|
|
begin
|
|
Y := FLines.Count - 1;
|
|
if Y < 0 then
|
|
Exit;
|
|
X := Length(FLines[Y]);
|
|
end;
|
|
S := FLines[Y];
|
|
B := False;
|
|
if CaretX >= Length(S) then
|
|
begin
|
|
if Y < FLines.Count - 1 then
|
|
begin
|
|
Y := CaretY + 1;
|
|
X := 0;
|
|
if Y < FLines.Count then
|
|
while (X < Length(FLines[Y])) and (CharInSetW(FLines[Y][X + 1], Separators)) do
|
|
Inc(X);
|
|
if ACommand = ecSelNextWord then // this code is copied from [ecPrevWord, ecSelPrevWord]
|
|
SetSel1(X, Y)
|
|
else
|
|
PersistentBlocksSetUnSelected;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
for F := X to Length(S) - 1 do
|
|
if B then
|
|
begin
|
|
if not CharInSetW(S[F + 1], Separators) then
|
|
begin
|
|
X := F;
|
|
Break;
|
|
end
|
|
end
|
|
else
|
|
if CharInSetW(S[F + 1], Separators) then
|
|
B := True;
|
|
if X = CaretX then
|
|
X := Length(S);
|
|
if ACommand = ecSelNextWord then
|
|
SetSel1(X, Y)
|
|
else
|
|
PersistentBlocksSetUnSelected;
|
|
end;
|
|
end;
|
|
ecSelWord:
|
|
if IsNewSelection and (GetWordOnPosEx(FLines[Y] + ' ', X + 1, iBeg,
|
|
iEnd) <> '') then
|
|
begin
|
|
SetSel1(iBeg - 1, Y);
|
|
SetSel1(iEnd - 1, Y);
|
|
X := iEnd - 1;
|
|
end;
|
|
ecBackspace:
|
|
if not ReadOnly then
|
|
if X > 0 then
|
|
begin
|
|
// in the middle of line
|
|
if (not PersistentBlocks) and FSelection.IsSelected then
|
|
DoAndCorrectXY(RemoveSelectedBlock)
|
|
else
|
|
begin
|
|
ReLine;
|
|
if BackSpaceUnindents then
|
|
X := GetBackStop(CaretX, CaretY)
|
|
else
|
|
X := CaretX - 1;
|
|
|
|
S := Copy(FLines[CaretY], X + 1, CaretX - X);
|
|
|
|
{ --- UNDO --- }
|
|
if X = CaretX - 1 then
|
|
TJvBackspaceUndo.Create(Self, CaretX, CaretY, S)
|
|
else
|
|
TJvBackspaceUnindentUndo.Create(Self, CaretX, CaretY, S);
|
|
CaretUndo := False;
|
|
{ --- /UNDO --- }
|
|
|
|
// persistent blocks: adjust selection
|
|
AdjustPersistentBlockSelection(CaretX, CaretY, amDelete, [1]);
|
|
|
|
FLines.DeleteText(X, Y, CaretX - 1, Y);
|
|
|
|
TextModified(CaretX, CaretY, maDelete, S);
|
|
PaintLine(Y, -1, -1);
|
|
end;
|
|
Changed;
|
|
end
|
|
else
|
|
if Y > 0 then
|
|
begin
|
|
// at the start of line
|
|
if FSelection.IsSelected then
|
|
begin
|
|
BeginCompound;
|
|
try
|
|
DoAndCorrectXY(RemoveSelectedBlock);
|
|
ReLine;
|
|
finally
|
|
EndCompound;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
LockUpdate;
|
|
try
|
|
X := Length(FLines[Y - 1]);
|
|
|
|
{ --- UNDO --- }
|
|
TJvBackspaceUndo.Create(Self, X + 1, CaretY - 1, Lf);
|
|
CaretUndo := False;
|
|
{ --- /UNDO --- }
|
|
|
|
// persistent blocks: adjust selection
|
|
AdjustPersistentBlockSelection(CaretX, CaretY, amLineConcat, [X, CaretY - 1]);
|
|
|
|
FLines.DeleteText(X, Y - 1, -1, Y);
|
|
Dec(Y);
|
|
finally
|
|
UnlockUpdate;
|
|
end;
|
|
UpdateEditorSize;
|
|
TextModified(X, Y, maDelete, sLineBreakStr);
|
|
Invalidate;
|
|
Changed;
|
|
end;
|
|
end;
|
|
ecDelete:
|
|
if not ReadOnly then
|
|
begin
|
|
LockUpdate;
|
|
try
|
|
if FLines.Count = 0 then
|
|
FLines.Add('');
|
|
finally
|
|
UnlockUpdate;
|
|
end;
|
|
if not PersistentBlocks and FSelection.IsSelected then
|
|
DoAndCorrectXY(RemoveSelectedBlock)
|
|
else
|
|
if X < Length(FLines[Y]) then
|
|
begin
|
|
//{ inside line - â ñåðåäèíå ñòðîêè}
|
|
{ --- UNDO --- }
|
|
TJvDeleteUndo.Create(Self, CaretX, CaretY, FLines[Y][X + 1]);
|
|
CaretUndo := False;
|
|
{ --- /UNDO --- }
|
|
|
|
// persistent blocks: adjust selection (before DeleteText)
|
|
AdjustPersistentBlockSelection(CaretX + 1, CaretY, amDelete, [1]);
|
|
|
|
S := FLines[Y][X + 1];
|
|
FLines.DeleteText(X, Y, X, Y);
|
|
|
|
TextModified(CaretX, CaretY, maDelete, S);
|
|
PaintLine(CaretY, -1, -1);
|
|
Changed;
|
|
end
|
|
else
|
|
if (Y >= 0) and (Y <= FLines.Count - 2) then
|
|
begin
|
|
//{ at the end of line - â êîíöå ñòðîêè}
|
|
{ --- UNDO --- }
|
|
TJvDeleteUndo.Create(Self, CaretX, CaretY, sLineBreakStr);
|
|
CaretUndo := False;
|
|
{ --- /UNDO --- }
|
|
// persistent blocks: adjust selection (before DeleteText)
|
|
AdjustPersistentBlockSelection(0, CaretY + 1, amLineConcat, [CaretX, CaretY]);
|
|
|
|
FLines.DeleteText(X, Y, -1, Y + 1);
|
|
|
|
UpdateEditorSize;
|
|
TextModified(CaretX, CaretY, maDelete, sLineBreakStr);
|
|
Invalidate;
|
|
Changed;
|
|
end;
|
|
end;
|
|
ecTab, ecBackTab:
|
|
begin
|
|
X := GetTabStop(CaretX, CaretY, ACommand = ecTab);
|
|
if not ReadOnly then
|
|
begin
|
|
if FSelection.IsSelected then
|
|
if (ACommand = ecTab) and InsertMode then
|
|
DeleteSelected;
|
|
ReLine;
|
|
if (ACommand = ecTab) and InsertMode then
|
|
begin
|
|
S := FLines[CaretY];
|
|
S2 := SpacesW(X - CaretX);
|
|
{ --- UNDO --- }
|
|
TJvInsertTabUndo.Create(Self, CaretX, CaretY, S2);
|
|
CaretUndo := False;
|
|
{ --- /UNDO --- }
|
|
FLines.InsertText(CaretX, CaretY, S2);
|
|
|
|
TextModified(CaretX, CaretY, maInsert, S2);
|
|
PaintLine(CaretY, -1, -1);
|
|
Changed;
|
|
end;
|
|
end;
|
|
{ else }
|
|
{ move cursor - oh yes!, it's already moved: X := GetTabStop(..); }
|
|
end;
|
|
ecDeleteLine:
|
|
if not ReadOnly then
|
|
begin
|
|
if (CaretY >= 0) and (CaretY < FLines.Count) then
|
|
begin
|
|
S := FLines[CaretY];
|
|
if (CaretY >= FLines.Count - 1) and (S = '') then
|
|
Exit;
|
|
|
|
LockUpdate;
|
|
try
|
|
{ --- UNDO --- }
|
|
TJvDeleteLineUndo.Create(Self, CaretX, CaretY, S, CaretY >= FLines.Count - 1);
|
|
{ --- /UNDO --- }
|
|
if CaretY < FLines.Count - 1 then
|
|
FLines.Delete(CaretY)
|
|
else
|
|
FLines[CaretY] := '';
|
|
SetCaretInternal(0, CaretY); // set caret to 0/Y when in last line
|
|
finally
|
|
UnlockUpdate;
|
|
end;
|
|
AdjustPersistentBlockSelection(CaretX, CaretY, amDeleteLine, []);
|
|
TextModified(0, CaretY, maDelete, S);
|
|
Invalidate;
|
|
Changed;
|
|
end;
|
|
Exit;
|
|
end;
|
|
ecToUpperCase:
|
|
if not ReadOnly then
|
|
SelText := AnsiUpperCase(SelText);
|
|
ecToLowerCase:
|
|
if not ReadOnly then
|
|
SelText := AnsiLowerCase(SelText);
|
|
ecChangeCase:
|
|
if not ReadOnly then
|
|
SelText := AnsiChangeCase(SelText);
|
|
end;
|
|
Result := False;
|
|
end;
|
|
|
|
function TJvCustomWideEditor.GetSelText: WideString;
|
|
var
|
|
S: WideString;
|
|
I: Integer;
|
|
Len, CLen: Integer;
|
|
P: PWideChar;
|
|
begin
|
|
with FSelection do
|
|
begin
|
|
Len := GetSelLength; // memory size to allocate
|
|
Result := '';
|
|
if Len = 0 then
|
|
Exit;
|
|
SetLength(Result, Len);
|
|
|
|
if SelBlockFormat = bfColumn then
|
|
begin
|
|
if Len > 0 then
|
|
begin
|
|
P := Pointer(Result);
|
|
for I := SelBegY to SelEndY do
|
|
begin
|
|
S := FLines[I];
|
|
CLen := Length(S) - SelBegX;
|
|
if CLen < 0 then
|
|
CLen := 0;
|
|
if CLen > SelEndX - SelBegX + 1 then
|
|
CLen := SelEndX - SelBegX + 1;
|
|
if CLen <> 0 then
|
|
begin
|
|
MoveWideChar(S[SelBegX + 1], P^, CLen);
|
|
Inc(P, CLen);
|
|
end;
|
|
|
|
if I < SelEndY then
|
|
begin
|
|
MoveWideChar(sLineBreakStr[1], P^, sLineBreakLen);
|
|
Inc(P, sLineBreakLen);
|
|
end;
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
if SelBegY = SelEndY then
|
|
MoveWideChar(FLines[SelEndY][SelBegX + 1], Result[1], Len)
|
|
else
|
|
begin
|
|
P := PWideChar(Result);
|
|
|
|
// first line
|
|
S := FLines[SelBegY];
|
|
CLen := Length(S) - SelBegX;
|
|
if CLen > 0 then
|
|
begin
|
|
MoveWideChar(S[SelBegX + 1], P^, CLen);
|
|
Inc(P, CLen);
|
|
end;
|
|
|
|
// line break
|
|
MoveWideChar(sLineBreakStr[1], P^, sLineBreakLen);
|
|
Inc(P, sLineBreakLen);
|
|
|
|
// lines between
|
|
for I := SelBegY + 1 to SelEndY - 1 do
|
|
begin
|
|
// line
|
|
S := FLines[I];
|
|
MoveWideChar(S[1], P^, Length(S));
|
|
Inc(P, Length(S));
|
|
|
|
// line break
|
|
MoveWideChar(sLineBreakStr[1], P^, sLineBreakLen);
|
|
Inc(P, sLineBreakLen);
|
|
end;
|
|
|
|
// last line
|
|
S := FLines[SelEndY];
|
|
CLen := SelEndX + Ord(SelBlockFormat = bfInclusive);
|
|
if CLen > Length(S) then
|
|
CLen := Length(S);
|
|
if CLen > 0 then
|
|
MoveWideChar(S[1], P^, CLen);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomWideEditor.SetSelText(const AValue: WideString);
|
|
begin
|
|
BeginUpdate;
|
|
BeginCompound;
|
|
try
|
|
with FSelection do
|
|
begin
|
|
if IsSelected then
|
|
DeleteSelected
|
|
else
|
|
begin
|
|
SelBegX := CaretX;
|
|
SelBegY := CaretY;
|
|
end;
|
|
if FSelection.SelBlockFormat = bfColumn then
|
|
InsertColumnText(FSelection.SelBegX, FSelection.SelBegY, AValue)
|
|
else
|
|
InsertText(AValue);
|
|
|
|
IsSelected := Length(AValue) > 0;
|
|
Selecting := False;
|
|
GetEndPosCaretW(AValue, SelBegX, SelBegY, SelEndX, SelEndY);
|
|
if IsSelected then
|
|
Inc(SelEndX);
|
|
SetSelUpdateRegion(SelBegY, SelEndY);
|
|
end;
|
|
finally
|
|
EndCompound;
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomWideEditor.ClipboardCopy;
|
|
var
|
|
S: string;
|
|
begin
|
|
S := GetSelText; // convert to ANSI
|
|
Clipboard.SetTextBuf(PChar(S));
|
|
SetClipboardBlockFormat(SelBlockFormat);
|
|
end;
|
|
|
|
procedure TJvCustomWideEditor.InsertText(const Text: WideString);
|
|
var
|
|
X, Y: Integer;
|
|
begin
|
|
PaintCaret(False);
|
|
try
|
|
{ --- UNDO --- }
|
|
TJvInsertUndo.Create(Self, CaretX, CaretY, Text);
|
|
{ --- /UNDO --- }
|
|
FLines.InsertText(CaretX, CaretY, Text);
|
|
TextModified(CaretX, CaretY, maInsert, Text);
|
|
|
|
GetEndPosCaretW(Text, CaretX, CaretY, X, Y); // get new caret position
|
|
SetCaretInternal(X + 1, Y);
|
|
|
|
Changed;
|
|
finally
|
|
PaintCaret(True);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomWideEditor.InsertColumnText(X, Y: Integer; const Text: WideString);
|
|
begin
|
|
if X < 0 then
|
|
X := 0;
|
|
if Y < 0 then
|
|
Y := 0;
|
|
{ --- UNDO --- }
|
|
TJvInsertColumnUndo.Create(Self, X, Y, Text);
|
|
{ --- /UNDO --- }
|
|
FLines.InsertColumnText(X, Y, Text);
|
|
TextModified(X, Y, maInsertColumn, Text);
|
|
end;
|
|
|
|
// Substitutes a word in a cursor position on NewString
|
|
// WideString NewString should not contain Cr, Lf [translated]
|
|
|
|
procedure TJvCustomWideEditor.ReplaceWord(const NewString: WideString);
|
|
var
|
|
iBeg, iEnd: Integer;
|
|
S, W: WideString;
|
|
X: Integer;
|
|
begin
|
|
BeginUpdate;
|
|
PaintCaret(False);
|
|
try
|
|
S := FLines[CaretY];
|
|
while CaretX > Length(S) do
|
|
S := S + ' ';
|
|
W := TrimW(GetWordOnPos2W(S, CaretX, iBeg, iEnd));
|
|
if W = '' then
|
|
begin
|
|
iBeg := CaretX + 1;
|
|
iEnd := CaretX
|
|
end;
|
|
{ --- UNDO --- }
|
|
NotUndoable;
|
|
//TJvReplaceUndo.Create(Self, CaretX, CaretY, iBegSX - 1, CaretY, W, NewString);
|
|
{ --- /UNDO --- }
|
|
Delete(S, iBeg, iEnd - iBeg);
|
|
Insert(NewString, S, iBeg);
|
|
FLines.Internal[CaretY] := S;
|
|
X := iBeg + Length(NewString) - 1;
|
|
TextModified(CaretX, CaretY, maInsert, NewString);
|
|
PaintLine(CaretY, -1, -1);
|
|
SetCaretInternal(X, CaretY);
|
|
Changed;
|
|
finally
|
|
PaintCaret(True);
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
{ Substitutes a word on the cursor position by NewString [translated] }
|
|
|
|
procedure TJvCustomWideEditor.ReplaceWord2(const NewString: WideString);
|
|
var
|
|
S, W: WideString;
|
|
iBegSX, iEndSX: Integer; { [1..Length] }
|
|
X, Y: Integer;
|
|
begin
|
|
S := '';
|
|
if CaretY < FLines.Count then
|
|
S := FLines[CaretY];
|
|
|
|
W := TrimW(GetWordOnPosExW(S, CaretX + 1, iBegSX, iEndSX));
|
|
if W <> NewString then
|
|
begin
|
|
PaintCaret(False);
|
|
try
|
|
BeginCompound;
|
|
try
|
|
ReLine;
|
|
if Length(W) = 0 then
|
|
begin
|
|
iBegSX := CaretX + 1;
|
|
iEndSX := CaretX;
|
|
end;
|
|
{ --- UNDO --- }
|
|
TJvReplaceUndo.Create(Self, CaretX, CaretY, iBegSX - 1, CaretY, W, NewString);
|
|
{ --- /UNDO --- }
|
|
|
|
if iBegSX <= iEndSX then
|
|
FLines.DeleteText(iBegSX - 1, CaretY, iEndSX - 1, CaretY);
|
|
FLines.InsertText(iBegSX - 1, CaretY, NewString);
|
|
TextModified(iBegSX - 1, CaretY, maReplace, NewString);
|
|
|
|
GetEndPosCaretW(NewString, iBegSX - 1, CaretY, X, Y); // get end caret position
|
|
SetCaretInternal(X + 1, Y);
|
|
finally
|
|
EndCompound;
|
|
end;
|
|
Changed;
|
|
finally
|
|
PaintCaret(True);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomWideEditor.IndentColumns(X, BegY, EndY: Integer);
|
|
var
|
|
Y: Integer;
|
|
S: WideString;
|
|
begin
|
|
if BegY < 0 then
|
|
BegY := 0;
|
|
if BegY >= FLines.Count then
|
|
BegY := FLines.Count - 1;
|
|
if EndY < 0 then
|
|
EndY := 0;
|
|
if EndY >= FLines.Count then
|
|
EndY := FLines.Count - 1;
|
|
if EndY < BegY then
|
|
Exit;
|
|
if X < 0 then
|
|
X := 0;
|
|
|
|
S := SpacesW(2);
|
|
for Y := BegY to EndY - 1 do
|
|
S := S + sLineBreakStr + SpacesW(2);
|
|
|
|
InsertColumnText(X, BegY, S);
|
|
|
|
Changed;
|
|
if UpdateLock = 0 then
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TJvCustomWideEditor.UnIndentColumns(X: Integer; BegY, EndY: Integer);
|
|
var
|
|
S, UnindentedText: WideString;
|
|
Y: Integer;
|
|
Len, L: Integer;
|
|
begin
|
|
if BegY < 0 then
|
|
BegY := 0;
|
|
if BegY >= FLines.Count then
|
|
BegY := FLines.Count - 1;
|
|
if EndY < 0 then
|
|
EndY := 0;
|
|
if EndY >= FLines.Count then
|
|
EndY := FLines.Count - 1;
|
|
if EndY < BegY then
|
|
Exit;
|
|
if X < 0 then
|
|
X := 0;
|
|
|
|
Inc(X); // for WideString operations
|
|
|
|
LockUpdate;
|
|
try
|
|
UnindentedText := '';
|
|
for Y := BegY to EndY do
|
|
begin
|
|
S := FLines[Y];
|
|
Len := Length(S);
|
|
|
|
// how many spaces to delete
|
|
L := 0;
|
|
while (X + L <= Len) and (L < 2) and (S[X + L] = ' ') do
|
|
Inc(L);
|
|
|
|
if L > 0 then
|
|
begin
|
|
UnindentedText := UnindentedText + SpacesW(L);
|
|
Delete(S, X, L);
|
|
FLines.Internal[Y] := S;
|
|
end;
|
|
if Y < EndY then
|
|
UnindentedText := UnindentedText + sLineBreakStr;
|
|
end;
|
|
finally
|
|
UnlockUpdate;
|
|
end;
|
|
|
|
Dec(X); // for caret operations
|
|
if Length(UnindentedText) > 0 then
|
|
begin
|
|
{ --- UNDO --- }
|
|
TJvUnindentColumnUndo.Create(Self, CaretX, CaretY, X, BegY, UnindentedText);
|
|
{ --- /UNDO --- }
|
|
TextModified(X, BegY, maDelete, UnindentedText);
|
|
|
|
Changed;
|
|
if UpdateLock = 0 then
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomWideEditor.ClipboardPaste;
|
|
var
|
|
ClipS: string;
|
|
Len: Integer;
|
|
H: THandle;
|
|
X, Y, EndX, EndY: Integer;
|
|
begin
|
|
if (CaretY > FLines.Count - 1) and (FLines.Count > 0) then
|
|
if BeepOnError then
|
|
Beep;
|
|
H := Clipboard.GetAsHandle(CF_TEXT);
|
|
Len := GlobalSize(H);
|
|
if Len = 0 then
|
|
Exit;
|
|
|
|
BeginUpdate;
|
|
try
|
|
SetLength(ClipS, Len);
|
|
SetLength(ClipS, Clipboard.GetTextBuf(PChar(ClipS), Len));
|
|
ClipS := {$IFDEF SUPPORTS_UNICODE}ExpandTabsUnicode{$ELSE}ExpandTabsAnsi{$ENDIF SUPPORTS_UNICODE}(AdjustLineBreaks(ClipS));
|
|
PaintCaret(False);
|
|
|
|
ReLine;
|
|
with FSelection do
|
|
begin
|
|
X := CaretX;
|
|
Y := CaretY;
|
|
BeginCompound;
|
|
try
|
|
if IsSelected then
|
|
begin
|
|
if BlockOverwrite and not PersistentBlocks then
|
|
begin
|
|
X := SelBegX;
|
|
Y := SelBegY;
|
|
end;
|
|
RemoveSelectedBlock;
|
|
end;
|
|
if FLines.Count > 0 then
|
|
ReLine;
|
|
|
|
SelBlockFormat := GetClipboardBlockFormat;
|
|
if SelBlockFormat in [bfInclusive, bfNonInclusive, bfLine] then
|
|
begin
|
|
// special line block mode handling
|
|
if SelBlockFormat = bfLine then
|
|
begin
|
|
X := 0;
|
|
if (ClipS = '') or (ClipS[Length(ClipS)] <> Lf) then
|
|
ClipS := ClipS + sLineBreakStr;
|
|
end;
|
|
|
|
{ --- UNDO --- }
|
|
TJvInsertUndo.Create(Self, X, Y, ClipS);
|
|
{ --- /UNDO --- }
|
|
|
|
FLines.InsertText(X, Y, ClipS);
|
|
TextModified(X, Y, maInsert, ClipS);
|
|
|
|
// get new caret position
|
|
GetEndPosCaretW(ClipS, X, Y, EndX, EndY);
|
|
Inc(EndX);
|
|
|
|
if PersistentBlocks then
|
|
begin
|
|
SelBegX := X;
|
|
SelBegY := Y;
|
|
// special line block mode handling
|
|
if SelBlockFormat = bfLine then
|
|
begin
|
|
Dec(EndY);
|
|
SelEndX := Max_X;
|
|
end
|
|
else
|
|
SelEndX := EndX;
|
|
|
|
SelEndY := EndY;
|
|
IsSelected := True;
|
|
Selecting := False;
|
|
SetSelUpdateRegion(SelBegY, SelEndY);
|
|
end;
|
|
X := EndX;
|
|
Y := EndY;
|
|
end
|
|
else
|
|
if SelBlockFormat = bfColumn then
|
|
begin
|
|
InsertColumnText(X, Y, ClipS);
|
|
GetEndPosCaretW(ClipS, X, Y, X, Y);
|
|
X := CaretX - 1;
|
|
Inc(X);
|
|
end;
|
|
finally
|
|
EndCompound;
|
|
end;
|
|
end; // with
|
|
|
|
SetCaretInternal(X, Y);
|
|
|
|
Changed;
|
|
finally
|
|
PaintCaret(True);
|
|
EndUpdate; {!!! Causes copying all [translated] }
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomWideEditor.DeleteSelected;
|
|
var
|
|
S: WideString;
|
|
X, Y: Integer;
|
|
begin
|
|
with FSelection do
|
|
begin
|
|
X := SelBegX;
|
|
Y := SelBegY;
|
|
if IsSelected then
|
|
begin
|
|
BeginUpdate;
|
|
PaintCaret(False);
|
|
try
|
|
S := GetSelText;
|
|
{--- UNDO ---}
|
|
TJvDeleteSelectedUndo.Create(Self, CaretX, CaretY, S);
|
|
{--- /UNDO ---}
|
|
IsSelected := False;
|
|
Selecting := False;
|
|
if SelBlockFormat in [bfInclusive, bfNonInclusive, bfLine] then
|
|
begin
|
|
FLines.DeleteText(X, Y, SelEndX - 1 + Ord(SelBlockFormat = bfInclusive), SelEndY);
|
|
TextModified(SelBegX, SelBegY, maDelete, S);
|
|
end
|
|
else
|
|
if SelBlockFormat = bfColumn then
|
|
begin
|
|
Y := CaretY;
|
|
FLines.DeleteColumnText(SelBegX, SelBegY, SelEndX, SelEndY);
|
|
TextModified(SelBegX, SelBegY, maDeleteColumn, S);
|
|
end;
|
|
SetCaretInternal(X, Y);
|
|
Changed;
|
|
finally
|
|
PaintCaret(True);
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomWideEditor.Clear;
|
|
begin
|
|
FLines.Clear;
|
|
end;
|
|
|
|
function TJvCustomWideEditor.GetLines: TWStrings;
|
|
begin
|
|
Result := FLines;
|
|
end;
|
|
|
|
procedure TJvCustomWideEditor.SetLines(ALines: TWStrings);
|
|
begin
|
|
if ALines <> nil then
|
|
FLines.Assign(ALines);
|
|
{--- UNDO ---}
|
|
NotUndoable;
|
|
{--- /UNDO ---}
|
|
end;
|
|
|
|
procedure TJvCustomWideEditor.TextAllChangedInternal(Unselect: Boolean);
|
|
begin
|
|
inherited TextAllChangedInternal(Unselect);
|
|
TextModified(0, 0, maAll, '');
|
|
UpdateEditorView;
|
|
end;
|
|
|
|
function TJvCustomWideEditor.ExpandTabs(const S: WideString): WideString;
|
|
var
|
|
ps, I: Integer;
|
|
Sp: WideString;
|
|
Tabs, LenSp: Integer;
|
|
P: PWideChar;
|
|
begin
|
|
ps := Pos(Tab, S);
|
|
if ps > 0 then
|
|
begin
|
|
// How many Tab chars?
|
|
Tabs := 1;
|
|
for I := ps + 1 to Length(S) do
|
|
if S[I] = Tab then
|
|
Inc(Tabs);
|
|
|
|
Sp := SpacesW(GetDefTabStop(0, True));
|
|
LenSp := Length(Sp);
|
|
|
|
// needed memory
|
|
SetLength(Result, Length(S) - Tabs + Tabs * LenSp);
|
|
P := PWideChar(Result);
|
|
|
|
// copy the chars before the Tab
|
|
if ps > 1 then
|
|
begin
|
|
MoveWideChar(S[1], P[0], ps - 1);
|
|
Inc(P, ps - 1);
|
|
end;
|
|
|
|
for I := ps to Length(S) do
|
|
begin
|
|
if S[I] <> Tab then
|
|
begin
|
|
P[0] := S[I];
|
|
Inc(P);
|
|
end
|
|
else
|
|
if LenSp > 0 then
|
|
begin
|
|
MoveWideChar(Sp[1], P[0], LenSp);
|
|
Inc(P, LenSp);
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
Result := S;
|
|
end;
|
|
|
|
procedure TJvCustomWideEditor.TextModified(ACaretX, ACaretY: Integer; Action: TModifiedAction; const Text: WideString);
|
|
begin
|
|
end;
|
|
|
|
function TJvCustomWideEditor.GetLineCount: Integer;
|
|
begin
|
|
Result := FLines.Count;
|
|
end;
|
|
|
|
function TJvCustomWideEditor.GetLineLength(Index: Integer): Integer;
|
|
begin
|
|
Result := Length(FLines[Index]);
|
|
end;
|
|
|
|
function TJvCustomWideEditor.FindNotBlankCharPosInLine(Line: Integer): Integer;
|
|
begin
|
|
Result := FindNotBlankCharPos(FLines[Line]);
|
|
end;
|
|
|
|
procedure TJvCustomWideEditor.SetLockText(const Text: WideString);
|
|
begin
|
|
FLines.SetLockText(Text);
|
|
end;
|
|
|
|
function TJvCustomWideEditor.GetWordOnCaret: WideString;
|
|
begin
|
|
if CaretY < FLines.Count then
|
|
Result := GetWordOnPos(FLines[CaretY], CaretX + 1)
|
|
else
|
|
Result := '';
|
|
end;
|
|
|
|
function TJvCustomWideEditor.GetAutoIndentStop(Y: Integer): Integer;
|
|
var
|
|
I, Len: Integer;
|
|
S: WideString;
|
|
begin
|
|
Result := 0;
|
|
|
|
// find non-empty line
|
|
Dec(Y);
|
|
while Y > 0 do
|
|
begin
|
|
S := FLines[Y];
|
|
if Length(TrimW(S)) > 0 then
|
|
Break;
|
|
Dec(Y);
|
|
end;
|
|
if Y < 0 then
|
|
Exit;
|
|
|
|
Len := Length(S);
|
|
I := 1;
|
|
while (I <= Len) and (S[I] = ' ') do
|
|
Inc(I);
|
|
Result := I - 1;
|
|
end;
|
|
|
|
function TJvCustomWideEditor.GetTabStop(X, Y: Integer; Next: Boolean): Integer;
|
|
var
|
|
I: Integer;
|
|
|
|
procedure UpdateTabStops;
|
|
var
|
|
S: WideString;
|
|
J, I: Integer;
|
|
begin
|
|
FillChar(FTabPos, SizeOf(FTabPos), False);
|
|
if SmartTab then
|
|
begin
|
|
J := 1;
|
|
I := 1;
|
|
while Y - J >= 0 do
|
|
begin
|
|
S := TrimRightW(FLines[Y - J]);
|
|
if Length(S) > I then
|
|
FTabPos[Length(S)] := True;
|
|
while I <= Length(S) do
|
|
begin
|
|
if CharInSetW(S[I], IdentifierSymbols) then
|
|
begin
|
|
FTabPos[I - 1] := True;
|
|
while (I <= Length(S)) and CharInSetW(S[I], IdentifierSymbols) do
|
|
Inc(I);
|
|
end;
|
|
Inc(I);
|
|
end; { for }
|
|
|
|
if I >= Max_X_Scroll then
|
|
Break;
|
|
if J >= VisibleRowCount * 2 then
|
|
Break;
|
|
Inc(J);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
UpdateTabStops;
|
|
Result := X;
|
|
if Next then
|
|
begin
|
|
for I := X + 1 to High(FTabPos) do
|
|
if FTabPos[I] then
|
|
begin
|
|
Result := I;
|
|
Exit;
|
|
end;
|
|
if Result = X then
|
|
Result := GetDefTabStop(X, True);
|
|
end
|
|
else
|
|
begin
|
|
if Result = X then
|
|
Result := GetDefTabStop(X, False);
|
|
end;
|
|
end;
|
|
|
|
function TJvCustomWideEditor.GetBackStop(X, Y: Integer): Integer;
|
|
var
|
|
I: Integer;
|
|
S: WideString;
|
|
|
|
procedure UpdateBackStops;
|
|
var
|
|
S: WideString;
|
|
J, I, K: Integer;
|
|
begin
|
|
J := 1;
|
|
I := X - 1;
|
|
FillChar(FTabPos, SizeOf(FTabPos), False);
|
|
FTabPos[0] := True;
|
|
while Y - J >= 0 do
|
|
begin
|
|
S := FLines[Y - J];
|
|
for K := 1 to Min(Length(S), I) do
|
|
if S[K] <> ' ' then
|
|
begin
|
|
I := K;
|
|
FTabPos[I - 1] := True;
|
|
Break;
|
|
end;
|
|
if I = 1 then
|
|
Break;
|
|
if J >= VisibleRowCount * 2 then
|
|
Break;
|
|
Inc(J);
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
Result := X - 1;
|
|
S := TrimRightW(FLines[Y]);
|
|
if (TrimW(Copy(S, 1, X)) = '') and
|
|
((X + 1 > Length(S)) or (S[X + 1] <> ' ')) then
|
|
begin
|
|
UpdateBackStops;
|
|
for I := X downto 0 do
|
|
if FTabPos[I] then
|
|
begin
|
|
Result := I;
|
|
Exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomWideEditor.WMGetText(var Msg: TWMGetText);
|
|
var
|
|
S: string;
|
|
begin
|
|
if Msg.Text = nil then
|
|
Msg.Result := 0
|
|
else
|
|
begin
|
|
S := FLines.Text;
|
|
Msg.Result := Min(Length(S) + 1, Msg.TextMax);
|
|
if Msg.Result > 0 then
|
|
MoveWideChar(S[1], Msg.Text^, Msg.Result);
|
|
end;
|
|
end;
|
|
|
|
//=== { TJvInsertUndo } ======================================================
|
|
|
|
constructor TJvInsertUndo.Create(AJvEditor: TJvCustomWideEditor;
|
|
ACaretX, ACaretY: Integer; const AText: WideString);
|
|
begin
|
|
inherited Create(AJvEditor, ACaretX, ACaretY);
|
|
FText := AText;
|
|
if JvEditor.PersistentBlocks then
|
|
SaveSelection;
|
|
end;
|
|
|
|
function TJvInsertUndo.GetEditor: TJvCustomWideEditor;
|
|
begin
|
|
Result := TJvCustomWideEditor(FJvEditor);
|
|
end;
|
|
|
|
procedure TJvInsertUndo.Undo;
|
|
var
|
|
Text: WideString;
|
|
EndX, EndY: Integer;
|
|
du: TJvInsertUndo;
|
|
begin
|
|
Text := '';
|
|
with TJvUndoBufferAccessProtected(UndoBuffer) do
|
|
begin
|
|
while (FPtr >= 0) and not IsNewGroup(Self) do
|
|
begin
|
|
Text := TJvInsertUndo(LastUndo).FText + Text;
|
|
Dec(FPtr);
|
|
if not JvEditor.GroupUndo then
|
|
Break;
|
|
end;
|
|
Inc(FPtr);
|
|
|
|
du := TJvInsertUndo(Items[FPtr]);
|
|
end;
|
|
|
|
GetEndPosCaretW(Text, du.CaretX, du.CaretY, EndX, EndY); // get end caret position
|
|
TJvCustomWideEditor(JvEditor).FLines.DeleteText(du.CaretX, du.CaretY, EndX, EndY);
|
|
TJvCustomWideEditor(JvEditor).TextModified(du.CaretX, du.CaretY, maDelete, Text);
|
|
|
|
TJvCustomWideEditor(JvEditor).SetCaretInternal(du.CaretX, du.CaretY);
|
|
end;
|
|
|
|
//=== { TJvOverwriteUndo } ===================================================
|
|
|
|
constructor TJvOverwriteUndo.Create(AJvEditor: TJvCustomWideEditor;
|
|
ACaretX, ACaretY: Integer; const AOldText, ANewText: WideString);
|
|
begin
|
|
inherited Create(AJvEditor, ACaretX, ACaretY);
|
|
FOldText := AOldText;
|
|
FNewText := ANewText;
|
|
end;
|
|
|
|
function TJvOverwriteUndo.GetEditor: TJvCustomWideEditor;
|
|
begin
|
|
Result := TJvCustomWideEditor(FJvEditor);
|
|
end;
|
|
|
|
procedure TJvOverwriteUndo.Undo;
|
|
var
|
|
OldText, NewText: WideString;
|
|
EndX, EndY: Integer;
|
|
du: TJvOverwriteUndo;
|
|
begin
|
|
OldText := '';
|
|
NewText := '';
|
|
with TJvUndoBufferAccessProtected(UndoBuffer) do
|
|
begin
|
|
while (FPtr >= 0) and not IsNewGroup(Self) do
|
|
begin
|
|
OldText := TJvOverwriteUndo(LastUndo).FOldText + OldText;
|
|
NewText := TJvOverwriteUndo(LastUndo).FNewText + NewText;
|
|
Dec(FPtr);
|
|
if not GetEditor.GroupUndo then
|
|
Break;
|
|
end;
|
|
Inc(FPtr);
|
|
du := TJvOverwriteUndo(Items[FPtr]);
|
|
end;
|
|
with du do
|
|
begin
|
|
GetEndPosCaretW(NewText, du.CaretX, du.CaretY, EndX, EndY); // get end caret position
|
|
GetEditor.FLines.DeleteText(CaretX, CaretY, EndX, EndY);
|
|
GetEditor.FLines.InsertText(CaretX, CaretY, OldText);
|
|
GetEditor.TextModified(CaretX, CaretY, maReplace, OldText);
|
|
|
|
GetEditor.SetCaretInternal(CaretX, CaretY);
|
|
end;
|
|
end;
|
|
|
|
//=== { TJvInsertColumnUndo } ================================================
|
|
|
|
procedure TJvInsertColumnUndo.Undo;
|
|
var
|
|
I: Integer;
|
|
SS: TWStringList;
|
|
S: WideString;
|
|
begin
|
|
{ Do not call GetEditor.FLines.DeleteColumnText() here because it has not
|
|
the functionality needed in this context. It deletes the columns from
|
|
[BegX..EndX] even if the inserted line was not as long as EndX-BegX+1. }
|
|
|
|
SS := TWStringList.Create;
|
|
try
|
|
SS.Text := FText;
|
|
for I := 0 to SS.Count - 1 do
|
|
begin
|
|
S := GetEditor.FLines[CaretY + I];
|
|
Delete(S, CaretX + 1, Length(SS[I]));
|
|
GetEditor.FLines.Internal[CaretY + I] := S;
|
|
end;
|
|
finally
|
|
SS.Free;
|
|
end;
|
|
GetEditor.TextModified(CaretX, CaretY, maDelete, FText);
|
|
|
|
GetEditor.SetCaretInternal(CaretX, CaretY);
|
|
end;
|
|
|
|
//=== { TJvUnindentColumnUndo } ==============================================
|
|
|
|
constructor TJvUnindentColumnUndo.Create(AJvEditor: TJvCustomWideEditor;
|
|
ACaretX, ACaretY, ABegX, ABegY: Integer; const AText: WideString);
|
|
begin
|
|
inherited Create(AJvEditor, ACaretX, ACaretY, AText);
|
|
SaveSelection;
|
|
FBegX := ABegX;
|
|
FBegY := ABegY;
|
|
end;
|
|
|
|
procedure TJvUnindentColumnUndo.Undo;
|
|
var
|
|
BegX, BegY: Integer;
|
|
begin
|
|
BegX := FBegX;
|
|
BegY := FBegY;
|
|
with TJvUndoBufferAccessProtected(UndoBuffer) do
|
|
begin
|
|
while (FPtr >= 0) and not IsNewGroup(Self) do
|
|
begin
|
|
with TJvUnindentColumnUndo(LastUndo) do
|
|
begin
|
|
GetEditor.FLines.InsertColumnText(FBegX, FBegY, FText);
|
|
if BegX > FBegX then
|
|
BegX := FBegX;
|
|
if BegY > FBegY then
|
|
BegY := FBegY;
|
|
end;
|
|
Dec(FPtr);
|
|
if not GetEditor.GroupUndo then
|
|
Break;
|
|
end;
|
|
Inc(FPtr);
|
|
end;
|
|
GetEditor.TextModified(BegX, BegY, maInsert, GetEditor.FLines[BegY]);
|
|
|
|
RestoreSelection;
|
|
with TJvUnindentColumnUndo(TJvUndoBufferAccessProtected(UndoBuffer).LastUndo) do
|
|
GetEditor.SetCaretInternal(CaretX, CaretY);
|
|
end;
|
|
|
|
//=== { TJvDeleteUndo } ======================================================
|
|
|
|
procedure TJvDeleteUndo.Undo;
|
|
var
|
|
Text: WideString;
|
|
begin
|
|
Text := '';
|
|
with TJvUndoBufferAccessProtected(UndoBuffer) do
|
|
begin
|
|
while (FPtr >= 0) and not IsNewGroup(Self) do
|
|
begin
|
|
Text := TJvDeleteUndo(LastUndo).FText + Text;
|
|
Dec(FPtr);
|
|
if not GetEditor.GroupUndo then
|
|
Break;
|
|
end;
|
|
Inc(FPtr);
|
|
|
|
with TJvDeleteUndo(Items[FPtr]) do
|
|
begin
|
|
GetEditor.FLines.InsertText(CaretX, CaretY, Text);
|
|
GetEditor.TextModified(CaretX, CaretY, maInsert, Text);
|
|
|
|
GetEditor.SetCaretInternal(CaretX, CaretY);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
//=== { TJvDeleteLineUndo } ==================================================
|
|
|
|
{procedure TJvDeleteLineUndo.Redo;
|
|
begin
|
|
GetEditor.FLines.Insert(CaretY, FText);
|
|
end;}
|
|
|
|
constructor TJvDeleteLineUndo.Create(AJvEditor: TJvCustomWideEditor; ACaretX, ACaretY: Integer;
|
|
const AText: WideString; ALastLineDelete: Boolean);
|
|
begin
|
|
inherited Create(AJvEditor, ACaretX, ACaretY, AText);
|
|
FLastLineDelete := ALastLineDelete;
|
|
end;
|
|
|
|
procedure TJvDeleteLineUndo.Undo;
|
|
begin
|
|
GetEditor.LockUpdate;
|
|
try
|
|
if FLastLineDelete then
|
|
begin
|
|
GetEditor.FLines[CaretY] := FText;
|
|
GetEditor.TextModified(CaretX, CaretY, maReplace, FText);
|
|
end
|
|
else
|
|
begin
|
|
GetEditor.FLines.Insert(CaretY, FText);
|
|
GetEditor.TextModified(CaretX, CaretY, maInsert, FText);
|
|
end;
|
|
finally
|
|
GetEditor.UnlockUpdate;
|
|
end;
|
|
GetEditor.SetCaretInternal(CaretX, CaretY);
|
|
end;
|
|
|
|
//=== { TJvBackspaceUndo } ===================================================
|
|
|
|
procedure TJvBackspaceUndo.Undo;
|
|
var
|
|
Text: WideString;
|
|
StartPtr: Integer;
|
|
begin
|
|
Text := '';
|
|
with TJvUndoBufferAccessProtected(UndoBuffer) do
|
|
begin
|
|
StartPtr := FPtr;
|
|
while (FPtr >= 0) and not IsNewGroup(Self) do
|
|
begin
|
|
Text := Text + TJvDeleteUndo(LastUndo).FText;
|
|
Dec(FPtr);
|
|
if not GetEditor.GroupUndo then
|
|
Break;
|
|
end;
|
|
Inc(FPtr);
|
|
end;
|
|
|
|
with TJvDeleteUndo(UndoBuffer.Items[StartPtr]) do
|
|
begin
|
|
GetEditor.FLines.InsertText(CaretX - 1, CaretY, Text);
|
|
GetEditor.TextModified(CaretX - 1, CaretY, maInsert, Text);
|
|
end;
|
|
|
|
// set caret on last backspace undo's position
|
|
with TJvDeleteUndo(UndoBuffer.Items[TJvUndoBufferAccessProtected(UndoBuffer).FPtr]) do
|
|
if (FText = Lf) or (FText = Cr) then // a line was removed by backspace
|
|
GetEditor.SetCaretInternal(0, CaretY + 1)
|
|
else
|
|
GetEditor.SetCaretInternal(CaretX, CaretY);
|
|
end;
|
|
|
|
procedure TJvBackspaceUnindentUndo.Undo;
|
|
var
|
|
Text: WideString;
|
|
begin
|
|
Text := '';
|
|
with TJvUndoBufferAccessProtected(UndoBuffer) do
|
|
begin
|
|
while (FPtr >= 0) and not IsNewGroup(Self) do
|
|
begin
|
|
Text := Text + TJvDeleteUndo(LastUndo).FText;
|
|
Dec(FPtr);
|
|
if not GetEditor.GroupUndo then
|
|
Break;
|
|
end;
|
|
Inc(FPtr);
|
|
|
|
with TJvDeleteUndo(Items[FPtr]) do
|
|
begin
|
|
GetEditor.FLines.InsertText(CaretX - Length(Text), CaretY, Text);
|
|
GetEditor.TextModified(CaretX - Length(Text), CaretY, maInsert, Text);
|
|
// set caret on last backspace undo's position
|
|
GetEditor.SetCaretInternal(CaretX, CaretY);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
//=== { TJvReplaceUndo } =====================================================
|
|
|
|
constructor TJvReplaceUndo.Create(AJvEditor: TJvCustomWideEditor;
|
|
ACaretX, ACaretY: Integer; ABegX, ABegY: Integer; const AText, ANewText: WideString);
|
|
begin
|
|
inherited Create(AJvEditor, ACaretX, ACaretY);
|
|
FBegX := ABegX;
|
|
FBegY := ABegY;
|
|
FText := AText;
|
|
FNewText := ANewText;
|
|
end;
|
|
|
|
function TJvReplaceUndo.GetEditor: TJvCustomWideEditor;
|
|
begin
|
|
Result := TJvCustomWideEditor(FJvEditor);
|
|
end;
|
|
|
|
procedure TJvReplaceUndo.Undo;
|
|
var
|
|
EndX, EndY: Integer;
|
|
begin
|
|
GetEndPosCaretW(FNewText, FBegX, FBegY, EndX, EndY);
|
|
GetEditor.FLines.DeleteText(FBegX, FBegY, EndX, EndY);
|
|
GetEditor.FLines.InsertText(FBegX, FBegY, FText);
|
|
GetEditor.TextModified(FBegX, FBegY, maReplace, FText);
|
|
|
|
GetEditor.SetCaretInternal(CaretX, CaretY);
|
|
end;
|
|
|
|
//=== { TJvDeleteSelectedUndo } ==============================================
|
|
|
|
constructor TJvDeleteSelectedUndo.Create(AJvEditor: TJvCustomWideEditor;
|
|
ACaretX, ACaretY: Integer; const AText: WideString);
|
|
begin
|
|
inherited Create(AJvEditor, ACaretX, ACaretY, AText);
|
|
SaveSelection;
|
|
end;
|
|
|
|
procedure TJvDeleteSelectedUndo.Undo;
|
|
var
|
|
S: WideString;
|
|
I: Integer;
|
|
begin
|
|
with FSelection^ do
|
|
begin
|
|
if SelBlockFormat in [bfInclusive, bfNonInclusive, bfLine] then
|
|
begin
|
|
GetEditor.FLines.InsertText(SelBegX, SelBegY, FText);
|
|
GetEditor.TextModified(SelBegX, SelBegY, maInsert, FText);
|
|
end
|
|
else
|
|
if SelBlockFormat = bfColumn then
|
|
begin
|
|
for I := SelBegY to SelEndY do
|
|
begin
|
|
S := GetEditor.FLines[I];
|
|
Insert(SubStrBySeparatorW(FText, I - SelBegY, sLineBreakStr), S, SelBegX + 1);
|
|
GetEditor.FLines.Internal[I] := S;
|
|
end;
|
|
GetEditor.TextModified(SelBegX, SelBegY, maInsertColumn, FText);
|
|
end;
|
|
|
|
RestoreSelection;
|
|
GetEditor.SetCaretInternal(CaretX, CaretY);
|
|
end; // with
|
|
end;
|
|
|
|
//=== { TJvEditorCompletion } ================================================
|
|
|
|
constructor TJvWideCompletion.Create(AJvEditor: TJvCustomWideEditor);
|
|
begin
|
|
inherited Create(AJvEditor);
|
|
FIdentifiers := TWStringList.Create;
|
|
FTemplates := TWStringList.Create;
|
|
FCaretChar := '|';
|
|
FCRLF := '/n';
|
|
FSeparator := '=';
|
|
end;
|
|
|
|
destructor TJvWideCompletion.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
FIdentifiers.Free;
|
|
FTemplates.Free;
|
|
end;
|
|
|
|
{ Substitutes word on the cursor position by NewString [translated] }
|
|
|
|
procedure TJvWideCompletion.ReplaceWord(const NewString: WideString);
|
|
var
|
|
S, W: WideString;
|
|
X, Y: Integer;
|
|
iBegSX, iEndSX: Integer;
|
|
NewCaret: Integer;
|
|
begin
|
|
with TJvCustomWideEditor(JvEditor) do
|
|
begin
|
|
if CaretY < FLines.Count then
|
|
S := FLines[CaretY];
|
|
W := GetNextWordPosExW(S, CaretX, iBegSX, iEndSX);
|
|
if W <> NewString then
|
|
begin
|
|
BeginUpdate;
|
|
PaintCaret(False);
|
|
try
|
|
BeginCompound;
|
|
try
|
|
ClearSelection;
|
|
ReLine;
|
|
|
|
if Length(W) = 0 then
|
|
begin
|
|
iBegSX := CaretX + 1;
|
|
iEndSX := CaretX;
|
|
end;
|
|
case Mode of
|
|
cmIdentifiers:
|
|
begin
|
|
S := NewString;
|
|
if Assigned(FOnCompletionApply) then
|
|
FOnCompletionApply(Self, W, S);
|
|
NewCaret := -1;
|
|
end;
|
|
cmTemplates:
|
|
begin
|
|
S := ReplaceString(NewString, FCRLF, sLineBreakStr + SpacesW(CaretX -
|
|
Length(W)));
|
|
S := ReplaceString(S, FCaretChar, '');
|
|
NewCaret := Pos(FCaretChar, ReplaceString(NewString, FCRLF, sLineBreakStr)) - 1;
|
|
end;
|
|
else
|
|
raise EJvEditorError.CreateRes(@RsEInvalidCompletionMode);
|
|
end;
|
|
{--- UNDO ---}
|
|
TJvReplaceUndo.Create(TJvCustomWideEditor(JvEditor), CaretX, CaretY,
|
|
iBegSX - 1, CaretY, W, S);
|
|
{--- /UNDO ---}
|
|
if iBegSX <= iEndSX then
|
|
FLines.DeleteText(iBegSX - 1, CaretY, iEndSX - 1, CaretY);
|
|
FLines.InsertText(iBegSX - 1, CaretY, S);
|
|
TextModified(iBegSX - 1, CaretY, maReplace, S);
|
|
|
|
if NewCaret >= 0 then
|
|
SetLength(S, NewCaret); // truncate S to the new caret position
|
|
GetEndPosCaretW(S, iBegSX - 1, CaretY, X, Y);
|
|
SetCaretInternal(X + 1, Y);
|
|
finally
|
|
EndCompound;
|
|
end;
|
|
Changed;
|
|
finally
|
|
PaintCaret(True);
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvWideCompletion.MakeItems;
|
|
var
|
|
I: Integer;
|
|
S: WideString;
|
|
begin
|
|
Items.Clear;
|
|
case Mode of
|
|
cmIdentifiers:
|
|
for I := 0 to FIdentifiers.Count - 1 do
|
|
Items.Add(FIdentifiers[I]);
|
|
cmTemplates:
|
|
begin
|
|
with TJvCustomWideEditor(JvEditor) do
|
|
if FLines.Count > CaretY then
|
|
S := GetWordOnPosW(FLines[CaretY], CaretX)
|
|
else
|
|
S := '';
|
|
for I := 0 to FTemplates.Count - 1 do
|
|
if StrLICompW(PWideChar(FTemplates[I]), PWideChar(S), Length(S)) = 0 then
|
|
Items.Add(FTemplates[I]);
|
|
if Items.Count = 0 then
|
|
Items.Assign(FTemplates);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvWideCompletion.FindSelItem(var Eq: Boolean);
|
|
var
|
|
S: WideString;
|
|
|
|
function FindFirst(SS: TStrings; S: string): Integer;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
for I := 0 to SS.Count - 1 do
|
|
if StrLIComp(PChar(SS[I]), PChar(S), Length(S)) = 0 then
|
|
begin
|
|
Result := I;
|
|
Exit;
|
|
end;
|
|
Result := -1;
|
|
end;
|
|
|
|
begin
|
|
with TJvCustomWideEditor(JvEditor) do
|
|
if FLines.Count > 0 then
|
|
S := GetWordOnPosW(FLines[CaretY], CaretX)
|
|
else
|
|
S := '';
|
|
if TrimW(S) = '' then
|
|
ItemIndex := -1
|
|
else
|
|
ItemIndex := FindFirst(Items, S);
|
|
Eq := (ItemIndex > -1) and SameText(TrimW(SubStrBySeparatorW(Items[ItemIndex], 0, FSeparator)), S);
|
|
end;
|
|
|
|
function TJvWideCompletion.GetStrings(Index: Integer): TWStrings;
|
|
begin
|
|
case Index of
|
|
0:
|
|
Result := FIdentifiers;
|
|
1:
|
|
Result := FTemplates;
|
|
else
|
|
Result := nil;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvWideCompletion.SetStrings(Index: Integer; AValue: TWStrings);
|
|
begin
|
|
case Index of
|
|
0:
|
|
FIdentifiers.Assign(AValue);
|
|
1:
|
|
FTemplates.Assign(AValue);
|
|
end;
|
|
end;
|
|
|
|
function TJvWideCompletion.GetIdentifierCount: Integer;
|
|
begin
|
|
Result := FIdentifiers.Count;
|
|
end;
|
|
|
|
function TJvWideCompletion.GetTemplateCount: Integer;
|
|
begin
|
|
Result := FTemplates.Count;
|
|
end;
|
|
|
|
procedure TJvWideCompletion.ReplaceWordItemIndex(SubStrStart: Integer);
|
|
begin
|
|
ReplaceWord(SubStrBySeparatorW(Items[ItemIndex], SubStrStart, FSeparator));
|
|
end;
|
|
|
|
function TJvCustomWideEditor.GetCompletion: TJvWideCompletion;
|
|
begin
|
|
Result := TJvWideCompletion(inherited Completion);
|
|
end;
|
|
|
|
procedure TJvCustomWideEditor.SetCompletion(const Value: TJvWideCompletion);
|
|
begin
|
|
inherited Completion := Value;
|
|
end;
|
|
|
|
{$IFDEF SUPPORTS_UNICODE}
|
|
function TJvWideCompletion.GetUnicodeSeparator: UnicodeString;
|
|
begin
|
|
Result := FSeparator;
|
|
end;
|
|
|
|
{$ELSE}
|
|
|
|
function TJvWideCompletion.GetAnsiSeparator: AnsiString;
|
|
begin
|
|
Result := FSeparator;
|
|
end;
|
|
{$ENDIF SUPPORTS_UNICODE}
|
|
|
|
{$IFDEF UNITVERSIONING}
|
|
initialization
|
|
RegisterUnitVersion(HInstance, UnitVersioning);
|
|
|
|
finalization
|
|
UnregisterUnitVersion(HInstance);
|
|
{$ENDIF UNITVERSIONING}
|
|
|
|
end.
|
|
|