dexed/src/ce_editoroptions.pas

661 lines
20 KiB
Plaintext

unit ce_editoroptions;
{$I ce_defines.inc}
interface
uses
Classes, SysUtils, Graphics, SynEdit, SynEditMouseCmds, SynEditMiscClasses,
SynEditKeyCmds, Menus, LCLProc,
ce_interfaces, ce_observer, ce_common, ce_writableComponent, ce_synmemo,
ce_d2syn, ce_txtsyn;
type
(**
* Container for the editor and highlither options.
* The base class is also used to backup the settings
* to allow a to preview and restore the settings when rejected.
*
* note: when adding a new property, the default value must be set in
* the constructor according to the default value of the member binded
* to the property.
*)
TCEEditorOptionsBase = class(TWritableLfmTextComponent)
private
// note this is how a TComponent can be edited in
// a basic TTIGrid: in the ctor create the component
// but expose it as a published TPersistent.
fD2Syn: TPersistent;
fTxtSyn: TPersistent;
//
fShortCuts: TCollection;
//
fCurrLineAttribs: TSynSelectedColor;
fSelAttribs: TSynSelectedColor;
fFoldedColor: TSynSelectedColor;
fMouseLinkAttribs: TSynSelectedColor;
fBracketMatchAttribs: TSynSelectedColor;
fIdentifierMarkup: TSynSelectedColor;
fFont: TFont;
//
fIdentiMatchOpts: TIdentifierMatchOptions;
fLineNumEvery: Integer;
fDDocDelay: Integer;
fAutoDotDelay: Integer;
fTabWidth: Integer;
fBlockIdent: Integer;
fLineSpacing: Integer;
fCharSpacing: Integer;
fRightEdge: Integer;
fBackground: TColor;
fRightEdgeColor: TColor;
fOptions1: TSynEditorOptions;
fOptions2: TSynEditorOptions2;
fMouseOptions: TSynEditorMouseOptions;
fCompletionMenuCaseCare: boolean;
fCompletionMenuWidth: integer;
fCompletionMenuLines: Byte;
fAutoCLoseCurlyBrace: TBraceAutoCloseStyle;
//
procedure setFont(value: TFont);
procedure setSelCol(value: TSynSelectedColor);
procedure setFoldedColor(value: TSynSelectedColor);
procedure setMouseLinkColor(value: TSynSelectedColor);
procedure setBracketMatchColor(value: TSynSelectedColor);
procedure setIdentifierMarkup(value: TSynSelectedColor);
procedure setCurrLineAttribs(value: TSynSelectedColor);
procedure setD2Syn(value: TPersistent);
procedure setTxtSyn(value: TPersistent);
procedure setShortcuts(value: TCollection);
procedure setDDocDelay(value: Integer);
procedure setAutoDotDelay(value: Integer);
procedure setCompletionMenuLines(value: byte);
procedure setLineNumEvery(value: integer);
published
property autoCloseCurlyBrace: TBraceAutoCloseStyle read fAutoCLoseCurlyBrace write fAutoCLoseCurlyBrace default TBraceAutoCloseStyle.autoCloseNever;
property currentLine: TSynSelectedColor read fCurrLineAttribs write setCurrLineAttribs;
property completionMenuCaseCare: boolean read fCompletionMenuCaseCare write fCompletionMenuCaseCare;
property completionMenuWidth: integer read fCompletionMenuWidth write fCompletionMenuWidth;
property completionMenuLines: byte read fCompletionMenuLines write setCompletionMenuLines;
property autoDotDelay: integer read fAutoDotDelay write SetautoDotDelay;
property hintDelay: Integer read fDDocDelay write setDDocDelay stored false; deprecated;
property ddocDelay: Integer read fDDocDelay write setDDocDelay;
property bracketMatch: TSynSelectedColor read fBracketMatchAttribs write setBracketMatchColor;
property mouseLink: TSynSelectedColor read fMouseLinkAttribs write setMouseLinkColor;
property selection: TSynSelectedColor read fSelAttribs write setSelCol;
property folding: TSynSelectedColor read fFoldedColor write setFoldedColor;
property identifierMatch: TSynSelectedColor read fIdentifierMarkup write SetIdentifierMarkup;
property background: TColor read fBackground write fBackground default clWhite;
property tabulationWidth: Integer read fTabWidth write fTabWidth default 4;
property blockIdentation: Integer read fBlockIdent write fBlockIdent default 4;
property lineSpacing: Integer read fLineSpacing write fLineSpacing default 0;
property characterSpacing: Integer read fCharSpacing write fCharSpacing default 0;
property rightEdge: Integer read fRightEdge write fRightEdge default 80;
property rightEdgeColor: TColor read fRightEdgeColor write fRightEdgeColor default clSilver;
property font: TFont read fFont write setFont;
property options1: TSynEditorOptions read fOptions1 write fOptions1;
property options2: TSynEditorOptions2 read fOptions2 write fOptions2;
property mouseOptions: TSynEditorMouseOptions read fMouseOptions write fMouseOptions;
property highlighterDlang: TPersistent read fD2Syn write setD2Syn;
property highlighterGeneric: TPersistent read fTxtSyn write setTxtSyn;
property shortcuts: TCollection read fShortCuts write setShortcuts;
property lineNumberEvery: integer read fLineNumEvery write setLineNumEvery default 5;
property identifierMatchOptions: TIdentifierMatchOptions read fIdentiMatchOpts write fIdentiMatchOpts default [caseSensitive];
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
//
procedure Assign(src: TPersistent); override;
end;
(**
* Manages and exposes all the editor and highligther options to an TCEOptionsEditor.
* It's also responsible to give the current options to a new editor.
*)
TCEEditorOptions = class(TCEEditorOptionsBase, ICEEditableOptions, ICEMultiDocObserver, ICEEDitableShortcut)
private
fBackup: TCEEditorOptionsBase;
fShortcutCount: Integer;
//
function optionedWantCategory(): string;
function optionedWantEditorKind: TOptionEditorKind;
function optionedWantContainer: TPersistent;
procedure optionedEvent(anEvent: TOptionEditorEvent);
function optionedOptionsModified: boolean;
//
procedure docNew(aDoc: TCESynMemo);
procedure docFocused(aDoc: TCESynMemo);
procedure docChanged(aDoc: TCESynMemo);
procedure docClosing(aDoc: TCESynMemo);
//
function scedWantFirst: boolean;
function scedWantNext(out category, identifier: string; out aShortcut: TShortcut): boolean;
procedure scedSendItem(const category, identifier: string; aShortcut: TShortcut);
procedure scedSendDone;
//
procedure applyChangesFromSelf;
procedure applyChangeToEditor(anEditor: TCESynMemo);
protected
procedure afterLoad; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
implementation
const
edoptFname = 'editor.txt';
var
EditorOptions: TCEEditorOptions;
{$REGION Standard Comp/Obj -----------------------------------------------------}
constructor TCEEditorOptionsBase.Create(AOwner: TComponent);
var
i: integer;
shc: TCEPersistentShortcut;
ed: TSynEdit;
begin
inherited;
//
fFont := TFont.Create;
fFont.Name := 'Courier New';
fFont.Quality := fqProof;
fFont.Pitch := fpFixed;
fFont.Size := 10;
//
fD2Syn := TSynD2Syn.Create(self);
fD2Syn.Assign(D2Syn);
fTxtSyn := TSynTxtSyn.Create(self);
fTxtSyn.Assign(TxtSyn);
//
fDDocDelay:=200;
fAutoDotDelay:=20;
fCurrLineAttribs := TSynSelectedColor.Create;
fSelAttribs := TSynSelectedColor.Create;
fFoldedColor := TSynSelectedColor.Create;
fMouseLinkAttribs := TSynSelectedColor.Create;
fBracketMatchAttribs := TSynSelectedColor.Create;
fIdentifierMarkup := TSynSelectedColor.Create;
//
// note: default values come from TSynEditFoldedView ctor.
fFoldedColor.Background := clNone;
fFoldedColor.Foreground := clDkGray;
fFoldedColor.FrameColor := clDkGray;
//
fMouseLinkAttribs.Style := [fsUnderline, fsBold];
fMouseLinkAttribs.StyleMask := [];
fMouseLinkAttribs.Foreground := clNone;
fMouseLinkAttribs.Background := clNone;
//
fBracketMatchAttribs.Foreground := clRed;
fBracketMatchAttribs.Background := clNone;
//
fIdentifierMarkup.Foreground:= clNone;
fIdentifierMarkup.Background:= clSilver;
fIdentifierMarkup.BackAlpha:=70;
fIdentiMatchOpts := [caseSensitive];
//
fCompletionMenuWidth:= 160;
fCompletionMenuLines:= 15;
//
fLineNumEvery := 5;
rightEdge := 80;
tabulationWidth := 4;
blockIdentation := 4;
fBackground := clWhite;
fRightEdgeColor := clSilver;
//
fCurrLineAttribs.Background := fBackground - $080808;
fCurrLineAttribs.Foreground := clNone;
//
options1 :=
[eoAutoIndent, eoBracketHighlight, eoGroupUndo, eoTabsToSpaces,
eoDragDropEditing, eoShowCtrlMouseLinks, eoEnhanceHomeKey, eoTabIndent];
options2 :=
[eoEnhanceEndKey, eoFoldedCopyPaste, eoOverwriteBlock];
//
mouseOptions := MouseOptions +
[emAltSetsColumnMode, emDragDropEditing, emCtrlWheelZoom, emShowCtrlMouseLinks];
//
fShortCuts := TCollection.Create(TCEPersistentShortcut);
ed := TSynEdit.Create(nil);
try
// note: cant use a TCESynMemo because it'd be added to the EntitiesConnector
SetDefaultCoeditKeystrokes(ed);
for i:= 0 to ed.Keystrokes.Count-1 do
begin
shc := TCEPersistentShortcut(fShortCuts.Add);
shc.actionName:= EditorCommandToCodeString(ed.Keystrokes.Items[i].Command);
shc.shortcut := ed.Keystrokes.Items[i].ShortCut;
end;
finally
ed.free;
end;
end;
destructor TCEEditorOptionsBase.Destroy;
begin
fFont.Free;
fCurrLineAttribs.Free;
fSelAttribs.Free;
fShortCuts.Free;
fFoldedColor.Free;
fMouseLinkAttribs.Free;
fBracketMatchAttribs.Free;
fIdentifierMarkup.Free;
inherited;
end;
procedure TCEEditorOptionsBase.Assign(src: TPersistent);
var
srcopt: TCEEditorOptionsBase;
begin
if (src is TCEEditorOptionsBase) then
begin
srcopt := TCEEditorOptionsBase(src);
//
fAutoCLoseCurlyBrace := srcopt.fAutoCLoseCurlyBrace;
fCompletionMenuWidth:=srcopt.fCompletionMenuWidth;
fCompletionMenuLines:=srcopt.fCompletionMenuLines;
fCompletionMenuCaseCare:=srcopt.fCompletionMenuCaseCare;
fAutoDotDelay:=srcopt.fAutoDotDelay;
fDDocDelay:=srcopt.fDDocDelay;
fFont.Assign(srcopt.fFont);
fSelAttribs.Assign(srcopt.fSelAttribs);
fFoldedColor.Assign(srcopt.fFoldedColor);
fMouseLinkAttribs.Assign(srcopt.fMouseLinkAttribs);
fBracketMatchAttribs.Assign(srcopt.fBracketMatchAttribs);
fCurrLineAttribs.Assign(srcopt.fCurrLineAttribs);
fD2Syn.Assign(srcopt.fD2Syn);
fTxtSyn.Assign(srcopt.fTxtSyn);
background := srcopt.background;
lineNumberEvery := srcopt.lineNumberEvery;
identifierMatchOptions:=srcopt.identifierMatchOptions;
tabulationWidth := srcopt.tabulationWidth;
blockIdentation := srcopt.blockIdentation;
lineSpacing := srcopt.lineSpacing;
characterSpacing := srcopt.characterSpacing;
options1 := srcopt.options1;
options2 := srcopt.options2;
mouseOptions := srcopt.mouseOptions;
rightEdge := srcopt.rightEdge;
rightEdgeColor := srcopt.rightEdgeColor;
fShortCuts.Assign(srcopt.fShortCuts);
end
else
inherited;
end;
procedure TCEEditorOptionsBase.setDDocDelay(value: Integer);
begin
if value > 2000 then value := 2000
else if value < 20 then value := 20;
fDDocDelay:=value;
end;
procedure TCEEditorOptionsBase.setAutoDotDelay(value: Integer);
begin
if value > 2000 then value := 2000
else if value < 0 then value := 0;
fAutoDotDelay:=value;
end;
procedure TCEEditorOptionsBase.setCompletionMenuLines(value: byte);
begin
if value < 5 then value := 5
else if value > 64 then value := 64;
fCompletionMenuLines := value;
end;
procedure TCEEditorOptionsBase.setLineNumEvery(value: integer);
begin
if value < 1 then value := 1
else if value > 10 then value := 10;
fLineNumEvery := value;
end;
procedure TCEEditorOptionsBase.setShortcuts(value: TCollection);
begin
fShortCuts.Assign(value);
end;
procedure TCEEditorOptionsBase.setFont(value: TFont);
begin
fFont.Assign(value);
end;
procedure TCEEditorOptionsBase.setSelCol(value: TSynSelectedColor);
begin
fSelAttribs.Assign(value);
end;
procedure TCEEditorOptionsBase.setFoldedColor(value: TSynSelectedColor);
begin
fFoldedColor.Assign(value);
end;
procedure TCEEditorOptionsBase.setMouseLinkColor(value: TSynSelectedColor);
begin
fMouseLinkAttribs.Assign(value);
end;
procedure TCEEditorOptionsBase.setBracketMatchColor(value: TSynSelectedColor);
begin
fBracketMatchAttribs.Assign(value);
end;
procedure TCEEditorOptionsBase.SetIdentifierMarkup(value: TSynSelectedColor);
begin
fIdentifierMarkup.Assign(value);
end;
procedure TCEEditorOptionsBase.setCurrLineAttribs(value: TSynSelectedColor);
begin
fCurrLineAttribs.Assign(value);
end;
procedure TCEEditorOptionsBase.setD2Syn(value: TPersistent);
begin
D2Syn.Assign(value);
end;
procedure TCEEditorOptionsBase.setTxtSyn(value: TPersistent);
begin
TxtSyn.Assign(value);
end;
constructor TCEEditorOptions.Create(AOwner: TComponent);
var
fname: string;
begin
inherited;
fBackup := TCEEditorOptionsBase.Create(self);
EntitiesConnector.addObserver(self);
//
fname := getCoeditDocPath + edoptFname;
if fileExists(fname) then
loadFromFile(fname);
end;
destructor TCEEditorOptions.Destroy;
begin
saveToFile(getCoeditDocPath + edoptFname);
//
EntitiesConnector.removeObserver(self);
inherited;
end;
procedure TCEEditorOptions.afterLoad;
var
ed: TSynEdit;
shc: TCEPersistentShortcut;
i,j: integer;
exists: boolean;
begin
inherited;
D2Syn.Assign(fD2Syn);
TxtSyn.Assign(fTxtSyn);
//
ed := TSynEdit.Create(nil);
try
SetDefaultCoeditKeystrokes(ed);
// new version with more shortcuts
for i:= 0 to ed.Keystrokes.Count-1 do
begin
exists := false;
for j := 0 to fShortcuts.count-1 do
begin
if TCEPersistentShortcut(fShortCuts.Items[j]).actionName <>
EditorCommandToCodeString(ed.Keystrokes.Items[i].Command) then
continue;
exists := true;
break;
end;
if exists then
continue;
shc := TCEPersistentShortcut(fShortCuts.Add);
shc.actionName := EditorCommandToCodeString(ed.Keystrokes.Items[i].Command);
shc.shortcut := ed.Keystrokes.Items[i].ShortCut;
end;
// new version wih less shortcuts
for j := fShortcuts.count-1 downto 0 do
begin
exists := false;
for i:= 0 to ed.Keystrokes.Count-1 do
begin
if TCEPersistentShortcut(fShortCuts.Items[j]).actionName <>
EditorCommandToCodeString(ed.Keystrokes.Items[i].Command) then
continue;
exists := true;
break;
end;
if exists then
continue;
fShortCuts.Delete(j);
end;
finally
ed.free;
end;
end;
{$ENDREGION}
{$REGION ICEMultiDocObserver ---------------------------------------------------}
procedure TCEEditorOptions.docNew(aDoc: TCESynMemo);
begin
//apply...des not modify font size to preserve current zoom
// when called after the options are edited
applyChangeToEditor(aDoc);
// must be set manually for a new doc
aDoc.Font.Size:=self.font.Size;
end;
procedure TCEEditorOptions.docFocused(aDoc: TCESynMemo);
begin
end;
procedure TCEEditorOptions.docChanged(aDoc: TCESynMemo);
begin
end;
procedure TCEEditorOptions.docClosing(aDoc: TCESynMemo);
begin
fCompletionMenuWidth := aDoc.completionMenu.TheForm.Width;
fCompletionMenuLines := aDoc.completionMenu.LinesInWindow;
end;
{$ENDREGION}
{$REGION ICEEDitableShortcut ---------------------------------------------------}
function TCEEditorOptions.scedWantFirst: boolean;
begin
result := fShortCuts.Count > 0;
fShortcutCount := 0;
end;
function TCEEditorOptions.scedWantNext(out category, identifier: string; out aShortcut: TShortcut): boolean;
var
shrct: TCEPersistentShortcut;
begin
shrct := TCEPersistentShortcut(fShortCuts.Items[fShortcutCount]);
category := 'Code editor';
identifier:= shrct.actionName;
// SynEdit shortcuts start with 'ec'
if identifier.length > 2 then
identifier := identifier[3..identifier.length];
aShortcut := shrct.shortcut;
//
fShortcutCount += 1;
result := fShortcutCount < fShortCuts.Count;
end;
procedure TCEEditorOptions.scedSendItem(const category, identifier: string; aShortcut: TShortcut);
var
i: Integer;
shc: TCEPersistentShortcut;
begin
if category <> 'Code editor' then exit;
//
for i:= 0 to fShortCuts.Count-1 do
begin
shc := TCEPersistentShortcut(fShortCuts.Items[i]);
if shc.actionName.length > 2 then
begin
if shc.actionName[3..shc.actionName.length] <> identifier then
continue;
end else if shc.actionName <> identifier then
continue;
shc.shortcut:= aShortcut;
break;
end;
// note: shortcut modifications are not reversible,
// they are sent from another option editor.
end;
procedure TCEEditorOptions.scedSendDone;
begin
applyChangesFromSelf;
end;
{$ENDREGION}
{$REGION ICEEditableOptions ----------------------------------------------------}
function TCEEditorOptions.optionedWantCategory(): string;
begin
exit('Editor');
end;
function TCEEditorOptions.optionedWantEditorKind: TOptionEditorKind;
begin
exit(oekGeneric);
end;
function TCEEditorOptions.optionedWantContainer: TPersistent;
begin
fD2Syn := D2Syn;
fTxtSyn := TxtSyn;
fBackup.Assign(self);
fBackup.fD2Syn.Assign(D2Syn);
fBackup.fTxtSyn.Assign(TxtSyn);
exit(self);
end;
procedure TCEEditorOptions.optionedEvent(anEvent: TOptionEditorEvent);
begin
// restores
if anEvent = oeeCancel then
begin
self.Assign(fBackup);
D2Syn.Assign(fBackup.fD2Syn);
TxtSyn.Assign(fBackup.fTxtSyn);
end;
// apply, if change/accept event
// to get a live preview
if anEvent <> oeeSelectCat then
applyChangesFromSelf;
// new backup values based on accepted values.
if anEvent = oeeAccept then
begin
fBackup.Assign(self);
fBackup.fD2Syn.Assign(D2Syn);
fBackup.fTxtSyn.Assign(TxtSyn);
end;
end;
function TCEEditorOptions.optionedOptionsModified: boolean;
begin
exit(false);
end;
{$ENDREGION}
{$REGION ICEEditableOptions ----------------------------------------------------}
procedure TCEEditorOptions.applyChangesFromSelf;
var
multied: ICEMultiDocHandler;
i: Integer;
begin
multied := getMultiDocHandler;
for i := 0 to multied.documentCount - 1 do
applyChangeToEditor(multied.document[i]);
end;
procedure TCEEditorOptions.applyChangeToEditor(anEditor: TCESynMemo);
var
i, j, k: Integer;
shc: TCEPersistentShortcut;
kst: TSynEditKeyStroke;
dup: boolean;
savedSize: integer;
begin
anEditor.D2Highlighter.Assign(D2Syn);
anEditor.TxtHighlighter.Assign(TxtSyn);
anEditor.autoDotDelay:=fAutoDotDelay;
anEditor.ddocDelay:=fDDocDelay;
savedSize := anEditor.Font.Size;
anEditor.defaultFontSize := font.Size;
anEditor.Font.Assign(font);
anEditor.Font.Size := savedSize;
anEditor.autoCloseCurlyBrace := fAutoCLoseCurlyBrace;
anEditor.completionMenu.TheForm.Width := fCompletionMenuWidth;
anEditor.completionMenu.LinesInWindow := fCompletionMenuLines;
anEditor.completionMenu.CaseSensitive := fCompletionMenuCaseCare;
anEditor.Gutter.LineNumberPart.ShowOnlyLineNumbersMultiplesOf:=fLineNumEvery;
anEditor.SelectedColor.Assign(fSelAttribs);
anEditor.FoldedCodeColor.Assign(fFoldedColor);
anEditor.MouseLinkColor.Assign(fMouseLinkAttribs);
anEditor.BracketMatchColor.Assign(fBracketMatchAttribs);
anEditor.HighlightAllColor.Assign(fIdentifierMarkup);
anEditor.LineHighlightColor.Assign(fCurrLineAttribs);
anEditor.TabWidth := tabulationWidth;
anEditor.BlockIndent := blockIdentation;
anEditor.ExtraLineSpacing := lineSpacing;
anEditor.ExtraCharSpacing := characterSpacing;
anEditor.Options := options1;
anEditor.Options2 := options2;
anEditor.MouseOptions := mouseOptions;
anEditor.Color := background;
anEditor.RightEdge := rightEdge;
anEditor.RightEdgeColor := rightEdgeColor;
anEditor.IdentifierMatchOptions:= identifierMatchOptions;
for i := 0 to anEditor.Keystrokes.Count-1 do
begin
kst := anEditor.Keystrokes.Items[i];
for j := 0 to fShortCuts.Count-1 do
begin
dup := false;
shc := TCEPersistentShortcut(fShortCuts.Items[j]);
if shc.actionName = EditorCommandToCodeString(kst.Command) then
begin
try
for k := 0 to i-1 do
if anEditor.Keystrokes.Items[k].shortCut = shc.shortcut then
if shc.shortCut <> 0 then
dup := true;
if not dup then
kst.shortCut := shc.shortcut;
except
kst.shortCut := 0;
shc.shortcut := 0;
// in case of conflict synedit raises an exception.
end;
break;
end;
end;
end;
end;
{$ENDREGION}
initialization
EditorOptions := TCEEditorOptions.Create(nil);
finalization
EditorOptions.Free;
end.