Issue #1482: text editor dialog and customize highlighter dialog

This commit is contained in:
Ansgar Becker
2025-03-24 14:23:33 +01:00
parent 3acdd774a1
commit 539e5bf7f1
8 changed files with 1222 additions and 8 deletions

View File

@ -250,6 +250,20 @@
<HasResources Value="True"/> <HasResources Value="True"/>
<ResourceBaseClass Value="Form"/> <ResourceBaseClass Value="Form"/>
</Unit> </Unit>
<Unit>
<Filename Value="source\texteditor.pas"/>
<IsPartOfProject Value="True"/>
<ComponentName Value="frmTextEditor"/>
<HasResources Value="True"/>
<ResourceBaseClass Value="Form"/>
</Unit>
<Unit>
<Filename Value="source\customize_highlighter.pas"/>
<IsPartOfProject Value="True"/>
<ComponentName Value="frmCustomizeHighlighter"/>
<HasResources Value="True"/>
<ResourceBaseClass Value="Form"/>
</Unit>
</Units> </Units>
</ProjectOptions> </ProjectOptions>
<CompilerOptions> <CompilerOptions>

View File

@ -19,8 +19,8 @@ uses
dbstructures.sqlite, change_password, loginform, data_sorting, extra_controls, dbstructures.sqlite, change_password, loginform, data_sorting, extra_controls,
column_selection, loaddata, csv_detector, createdatabase, editvar, copytable, column_selection, loaddata, csv_detector, createdatabase, editvar, copytable,
exportgrid, usermanager, selectdbobject, reformatter, searchreplace, exportgrid, usermanager, selectdbobject, reformatter, searchreplace,
connections, jsonregistry, sqlhelp, updatecheck, insertfiles {, printlist (EnablePrint not defined) } connections, jsonregistry, sqlhelp, updatecheck, insertfiles, texteditor,
; customize_highlighter;
{$R *.res} {$R *.res}
{.$R resources.rc} {.$R resources.rc}

View File

@ -0,0 +1,147 @@
object frmCustomizeHighlighter: TfrmCustomizeHighlighter
Left = 0
Height = 311
Top = 0
Width = 551
BorderStyle = bsDialog
Caption = 'Customize highlighter'
ClientHeight = 311
ClientWidth = 551
Color = clBtnFace
DesignTimePPI = 120
Font.Color = clWindowText
Font.Height = -15
Font.Name = 'Segoe UI'
OnCreate = FormCreate
OnDestroy = FormDestroy
OnShow = FormShow
Position = poOwnerFormCenter
object lblBackground: TLabel
Left = 199
Height = 20
Top = 46
Width = 82
Caption = 'Background:'
end
object lblForeground: TLabel
Left = 199
Height = 20
Top = 81
Width = 80
Caption = 'Foreground:'
end
object lblStyle: TLabel
Left = 199
Height = 20
Top = 112
Width = 35
Caption = 'Style:'
end
object comboHighlighter: TComboBox
Left = 10
Height = 28
Top = 10
Width = 181
ItemHeight = 20
Sorted = True
Style = csDropDownList
TabOrder = 0
OnSelect = comboHighlighterSelect
end
object listboxAttributes: TListBox
Left = 10
Height = 216
Top = 46
Width = 181
ItemHeight = 0
TabOrder = 1
OnClick = listboxAttributesClick
end
object chkBold: TCheckBox
Left = 346
Height = 24
Top = 112
Width = 195
Anchors = [akTop, akLeft, akRight]
Caption = 'Bold'
TabOrder = 4
OnClick = Modified
end
object chkItalic: TCheckBox
Left = 346
Height = 24
Top = 141
Width = 195
Anchors = [akTop, akLeft, akRight]
Caption = 'Italic'
TabOrder = 5
OnClick = Modified
end
object btnCancel: TButton
Left = 346
Height = 31
Top = 270
Width = 94
Anchors = [akRight, akBottom]
Cancel = True
Caption = 'Cancel'
ModalResult = 2
TabOrder = 7
end
object btnOK: TButton
Left = 245
Height = 31
Top = 270
Width = 94
Anchors = [akRight, akBottom]
Caption = 'OK'
Default = True
ModalResult = 1
TabOrder = 6
OnClick = SaveSettings
end
object editBackground: TEditButton
Left = 346
Height = 28
Top = 42
Width = 195
Anchors = [akTop, akLeft, akRight]
ButtonHint = 'Color picker'
ButtonWidth = 29
Images = MainForm.ImageListIcons8
ImageIndex = 33
MaxLength = 0
NumGlyphs = 1
OnButtonClick = editColorRightButtonClick
OnExit = Modified
PasswordChar = #0
TabOrder = 2
end
object editForeground: TEditButton
Left = 346
Height = 28
Top = 78
Width = 195
Anchors = [akTop, akLeft, akRight]
ButtonHint = 'Color picker'
ButtonWidth = 29
Images = MainForm.ImageListIcons8
ImageIndex = 33
MaxLength = 0
NumGlyphs = 1
OnButtonClick = editColorRightButtonClick
OnExit = Modified
PasswordChar = #0
TabOrder = 3
end
object btnApply: TButton
Left = 447
Height = 31
Top = 270
Width = 94
Anchors = [akRight, akBottom]
Caption = 'Apply'
TabOrder = 8
OnClick = SaveSettings
end
end

View File

@ -0,0 +1,198 @@
unit customize_highlighter;
{$mode delphi}{$H+}
interface
uses
SysUtils, Variants, Classes, Graphics, EditBtn,
Controls, Forms, Dialogs, StdCtrls, ExtCtrls, GraphUtil, Math,
StrUtils, SynEditHighlighter, apphelpers, extra_controls;
type
TfrmCustomizeHighlighter = class(TExtForm)
comboHighlighter: TComboBox;
listboxAttributes: TListBox;
lblBackground: TLabel;
lblForeground: TLabel;
lblStyle: TLabel;
chkBold: TCheckBox;
chkItalic: TCheckBox;
btnCancel: TButton;
btnOK: TButton;
editBackground: TEditButton;
editForeground: TEditButton;
btnApply: TButton;
procedure listboxAttributesClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure comboHighlighterSelect(Sender: TObject);
procedure SaveSettings(Sender: TObject);
procedure editColorRightButtonClick(Sender: TObject);
procedure Modified(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormShow(Sender: TObject);
private
{ Private-Deklarationen }
FHighlighter: TSynCustomHighlighter;
FAttr: TSynHighlighterAttributes;
FOnChange: TNotifyEvent;
procedure SetFriendlyLanguageName(FriendlyLanguageName: String);
function GetFriendlyLanguageName: String;
public
{ Public-Deklarationen }
property FriendlyLanguageName: String read GetFriendlyLanguageName write SetFriendlyLanguageName;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
end;
implementation
uses main;
{$R *.lfm}
procedure TfrmCustomizeHighlighter.SaveSettings(Sender: TObject);
begin
// Save highlighter settings
FHighlighter.SaveToFile(AppSettings.DirnameHighlighters + FHighlighter.LanguageName + '.ini');
if Assigned(FOnChange) then
FOnChange(Self);
end;
procedure TfrmCustomizeHighlighter.Modified(Sender: TObject);
begin
// Apply modification to current attribute
// Silence exception caused by invalid color strings
FAttr.Background := StringToColorDef(editBackground.Text, clNone);
FAttr.Foreground := StringToColorDef(editForeground.Text, clNone);
if chkBold.Checked then
FAttr.Style := FAttr.Style + [fsBold]
else
FAttr.Style := FAttr.Style - [fsBold];
if chkItalic.Checked then
FAttr.Style := FAttr.Style + [fsItalic]
else
FAttr.Style := FAttr.Style - [fsItalic];
end;
procedure TfrmCustomizeHighlighter.comboHighlighterSelect(Sender: TObject);
var
i: Integer;
Highlighters: TSynHighlighterList;
h: TSynCustomHighlighterClass;
begin
// Highlighter selected
listboxAttributes.Clear;
if Assigned(FHighlighter) then
FHighlighter.Free;
Highlighters := SynEditHighlighter.GetPlaceableHighlighters;
for i:=0 to Highlighters.Count-1 do begin
if Highlighters[i].GetLanguageName = comboHighlighter.Text then begin
FHighlighter := Highlighters[i].Create(Self);
Break;
end;
end;
FHighlighter.LoadFromFile(AppSettings.DirnameHighlighters + FHighlighter.GetLanguageName + '.ini');
for i:=0 to FHighlighter.AttrCount-1 do begin
listboxAttributes.Items.Add(FHighlighter.Attribute[i].Name);
end;
end;
procedure TfrmCustomizeHighlighter.editColorRightButtonClick(
Sender: TObject);
var
Dialog: TColorDialog;
Edit: TEditButton;
begin
// Color picker
Edit := Sender as TEditButton;
Dialog := TColorDialog.Create(Self);
//Dialog.Options := [cdFullOpen, cdAnyColor];
Dialog.Color := StringToColorDef(Edit.Text, clNone);
if Dialog.Execute then begin
Edit.Text := ColorToString(Dialog.Color);
end;
Dialog.Free;
Modified(Sender);
end;
procedure TfrmCustomizeHighlighter.FormCreate(Sender: TObject);
var
Highlighters: TSynHighlighterList;
i: Integer;
begin
// Form created
FHighlighter := nil;
FAttr := nil;
FOnChange := nil;
Highlighters := SynEditHighlighter.GetPlaceableHighlighters;
for i:=0 to Highlighters.Count-1 do begin
comboHighlighter.Items.Add(Highlighters[i].GetLanguageName);
end;
end;
procedure TfrmCustomizeHighlighter.FormDestroy(Sender: TObject);
begin
// Form destroyed
if Assigned(FHighlighter) then
FHighlighter.Free;
// causes an exception when closing:
//if Assigned(FAttr) then
// FAttr.Free;
end;
procedure TfrmCustomizeHighlighter.FormShow(Sender: TObject);
begin
// Ensure controls are disabled as long as no attribute is selected
listboxAttributes.OnClick(Sender);
end;
procedure TfrmCustomizeHighlighter.listboxAttributesClick(Sender: TObject);
var
i: Integer;
AttrSelected: Boolean;
begin
// Attribute selected
FAttr := nil;
if listboxAttributes.ItemIndex > -1 then begin
for i:=0 to FHighlighter.AttrCount-1 do begin
if listboxAttributes.Items[listboxAttributes.ItemIndex] = FHighlighter.Attribute[i].Name then begin
FAttr := FHighlighter.Attribute[i];
end;
end;
end;
// Enable/disable controls
AttrSelected := FAttr <> nil;
editBackground.Enabled := AttrSelected;
editForeground.Enabled := AttrSelected;
chkBold.Enabled := AttrSelected;
chkItalic.Enabled := AttrSelected;
// Overtake values
if AttrSelected then begin
editBackground.Text := IfThen(FAttr.Background <> clNone, ColorToString(FAttr.Background), '');
editForeground.Text := IfThen(FAttr.Foreground <> clNone, ColorToString(FAttr.Foreground), '');
chkBold.Checked := fsBold in FAttr.Style;
chkItalic.Checked := fsItalic in FAttr.Style;
end
else begin
editBackground.Text := '';
editForeground.Text := '';
chkBold.Checked := False;
chkItalic.Checked := False;
end;
end;
procedure TfrmCustomizeHighlighter.SetFriendlyLanguageName(FriendlyLanguageName: String);
begin
// Set current highlighter by its language name
comboHighlighter.ItemIndex := comboHighlighter.Items.IndexOf(FriendlyLanguageName);
comboHighlighter.OnSelect(comboHighlighter);
end;
function TfrmCustomizeHighlighter.GetFriendlyLanguageName: String;
begin
Result := FHighlighter.GetLanguageName;
end;
end.

View File

@ -1418,7 +1418,7 @@ implementation
uses uses
FileInfo, winpeimagereader, elfreader, machoreader, About, data_sorting, column_selection, loaddata, editvar, FileInfo, winpeimagereader, elfreader, machoreader, About, data_sorting, column_selection, loaddata, editvar,
copytable, csv_detector, exportgrid, usermanager, selectdbobject, reformatter, connections, sqlhelp, updatecheck, copytable, csv_detector, exportgrid, usermanager, selectdbobject, reformatter, connections, sqlhelp, updatecheck,
insertfiles; insertfiles, texteditor;
{$R *.lfm} {$R *.lfm}
@ -12995,9 +12995,9 @@ begin
end; end;
if (not Assigned(Result)) and QueryTabs.HasActiveTab then if (not Assigned(Result)) and QueryTabs.HasActiveTab then
Result := QueryTabs.ActiveMemo; Result := QueryTabs.ActiveMemo;
//if (not Assigned(Result)) and (Screen.ActiveForm is TfrmTextEditor) then begin if (not Assigned(Result)) and (Screen.ActiveForm is TfrmTextEditor) then begin
// Result := TfrmTextEditor(Screen.ActiveForm).MemoText; Result := TfrmTextEditor(Screen.ActiveForm).MemoText;
//end; end;
end; end;

View File

@ -8,7 +8,7 @@ uses
SysUtils, Classes, Controls, Forms, Dialogs, StdCtrls, SysUtils, Classes, Controls, Forms, Dialogs, StdCtrls,
ExtCtrls, SynEdit, SynEditTypes, laz.VirtualTrees, RegExpr, ExtCtrls, SynEdit, SynEditTypes, laz.VirtualTrees, RegExpr,
SynEditRegexSearch, SynEditMiscClasses, SynEditSearch, extra_controls, SynEditRegexSearch, SynEditMiscClasses, SynEditSearch, extra_controls,
Menus{, texteditor}; Menus, texteditor;
type type
TfrmSearchReplace = class(TExtForm) TfrmSearchReplace = class(TExtForm)
@ -123,7 +123,7 @@ begin
AnySynMemo := MainForm.ActiveSynMemo(True); AnySynMemo := MainForm.ActiveSynMemo(True);
if Assigned(AnySynMemo) then begin if Assigned(AnySynMemo) then begin
IsEditorWritable := not AnySynMemo.ReadOnly; // Support views and procedure editors IsEditorWritable := not AnySynMemo.ReadOnly; // Support views and procedure editors
IsGridTextEditor := False; //GetParentForm(AnySynMemo) is TfrmTextEditor; // Support grid text editor, read-only or not IsGridTextEditor := GetParentForm(AnySynMemo) is TfrmTextEditor; // Support grid text editor, read-only or not
if IsEditorWritable or IsGridTextEditor then if IsEditorWritable or IsGridTextEditor then
UsedSynMemo := AnySynMemo; UsedSynMemo := AnySynMemo;
end; end;

316
source/texteditor.lfm Normal file
View File

@ -0,0 +1,316 @@
object frmTextEditor: TfrmTextEditor
Left = 0
Height = 191
Top = 0
Width = 602
Caption = 'Text editor'
ClientHeight = 191
ClientWidth = 602
Color = clBtnFace
Constraints.MinHeight = 125
Constraints.MinWidth = 375
DesignTimePPI = 120
OnClose = FormClose
OnCreate = FormCreate
OnDestroy = FormDestroy
OnShow = FormShow
Position = poMainFormCenter
LCLVersion = '3.8.0.0'
object Panel1: TPanel
Left = 0
Height = 28
Top = 125
Width = 472
Align = alBottom
BevelOuter = bvNone
Caption = 'Panel1'
ClientHeight = 28
ClientWidth = 472
ParentBackground = False
TabOrder = 0
object lblTextLength: TLabel
Left = 409
Height = 16
Top = 3
Width = 76
Align = alLeft
Caption = 'lblTextLength'
Layout = tlCenter
ParentBidiMode = False
end
object tlbStandard: TToolBar
Left = 0
Height = 22
Top = 0
Width = 261
Align = alLeft
AutoSize = True
Caption = 'tlbStandard'
ParentShowHint = False
ShowHint = True
TabOrder = 0
Wrapable = False
object btnWrap: TToolButton
Left = 0
Hint = 'Wrap long lines'
Top = 0
Caption = 'Wrap long lines'
ImageIndex = 62
OnClick = btnWrapClick
end
object btnLinebreaks: TToolButton
Left = 29
Top = 0
Caption = 'Linebreaks'
DropdownMenu = popupLinebreaks
ImageIndex = 123
Style = tbsDropDown
end
object btnLoadText: TToolButton
Left = 81
Hint = 'Load textfile'
Top = 0
Caption = 'Load textfile'
ImageIndex = 52
OnClick = btnLoadTextClick
end
object btnCancel: TToolButton
Left = 110
Hint = 'Cancel'
Top = 0
Caption = 'Cancel'
ImageIndex = 26
OnClick = btnCancelClick
end
object btnApply: TToolButton
Left = 139
Hint = 'Apply changes'
Top = 0
Caption = 'Apply changes'
ImageIndex = 55
OnClick = btnApplyClick
end
object btnSeparator1: TToolButton
Left = 168
Top = 0
Width = 10
Caption = 'btnSeparator1'
ImageIndex = 60
Style = tbsSeparator
end
object btnSearchFind: TToolButton
Left = 178
Top = 0
Action = MainForm.actQueryFind
end
object btnSearchFindNext: TToolButton
Left = 206
Top = 0
Action = MainForm.actQueryFindAgain
end
object btnSearchReplace: TToolButton
Left = 235
Top = 0
Action = MainForm.actQueryReplace
end
object ToolButton1: TToolButton
Left = 264
Top = 0
Width = 10
Caption = 'ToolButton1'
ImageIndex = 60
Style = tbsSeparator
end
object btnCustomizeHighlighter: TToolButton
Left = 274
Top = 0
Caption = 'Customize highlighter'
DropdownMenu = popupHighlighter
ImageIndex = 39
OnClick = btnCustomizeHighlighterClick
Style = tbsDropDown
end
end
object comboHighlighter: TComboBox
Left = 261
Height = 22
Top = 0
Width = 181
Align = alLeft
ItemHeight = 0
Sorted = True
Style = csDropDownList
TabOrder = 1
OnSelect = comboHighlighterSelect
end
end
inline MemoText: TSynEdit
Left = 0
Height = 131
Top = 0
Width = 482
Align = alClient
Font.Color = clWindowText
Font.Height = -16
Font.Name = 'Courier New'
Font.Pitch = fpFixed
Font.Quality = fqNonAntialiased
ParentColor = False
ParentFont = False
PopupMenu = popupEditor
TabOrder = 1
OnClick = MemoTextClick
OnKeyDown = MemoTextKeyDown
Gutter.Width = 72
Gutter.MouseActions = <>
RightGutter.Width = 0
RightGutter.MouseActions = <>
Keystrokes = <>
MouseActions = <>
MouseTextActions = <>
MouseSelActions = <>
Lines.Strings = (
'MemoText'
)
Options = [eoAutoIndent, eoGroupUndo, eoShowScrollHint, eoSmartTabs, eoTabIndent, eoTabsToSpaces, eoDragDropEditing]
MouseOptions = [emDragDropEditing]
VisibleSpecialChars = [vscSpace, vscTabAtLast]
RightEdge = 0
SelectedColor.BackPriority = 50
SelectedColor.ForePriority = 50
SelectedColor.FramePriority = 50
SelectedColor.BoldPriority = 50
SelectedColor.ItalicPriority = 50
SelectedColor.UnderlinePriority = 50
SelectedColor.StrikeOutPriority = 50
BracketHighlightStyle = sbhsBoth
BracketMatchColor.Background = clNone
BracketMatchColor.Foreground = clNone
BracketMatchColor.Style = [fsBold]
FoldedCodeColor.Background = clNone
FoldedCodeColor.Foreground = clGray
FoldedCodeColor.FrameColor = clGray
MouseLinkColor.Background = clNone
MouseLinkColor.Foreground = clBlue
LineHighlightColor.Background = clNone
LineHighlightColor.Foreground = clNone
OnChange = MemoTextChange
inline SynLeftGutterPartList1: TSynGutterPartList
object SynGutterMarks1: TSynGutterMarks
Width = 30
MouseActions = <>
end
object SynGutterLineNumber1: TSynGutterLineNumber
Width = 21
MouseActions = <>
MarkupInfo.Background = clBtnFace
MarkupInfo.Foreground = clNone
DigitCount = 2
ShowOnlyLineNumbersMultiplesOf = 1
ZeroStart = False
LeadingZeros = False
end
object SynGutterChanges1: TSynGutterChanges
Width = 5
MouseActions = <>
ModifiedColor = 59900
SavedColor = clGreen
end
object SynGutterSeparator1: TSynGutterSeparator
Width = 3
MouseActions = <>
MarkupInfo.Background = clWhite
MarkupInfo.Foreground = clGray
end
object SynGutterCodeFolding1: TSynGutterCodeFolding
Width = 13
MouseActions = <>
MarkupInfo.Background = clNone
MarkupInfo.Foreground = clGray
MouseActionsExpanded = <>
MouseActionsCollapsed = <>
end
end
end
object popupLinebreaks: TPopupMenu
Left = 10
Top = 20
object menuWindowsLB: TMenuItem
Caption = 'Windows linebreaks'
ImageIndex = 123
OnClick = SelectLinebreaks
end
object menuUnixLB: TMenuItem
Caption = 'UNIX linebreaks'
ImageIndex = 125
OnClick = SelectLinebreaks
end
object menuMacLB: TMenuItem
Caption = 'Mac OS linebreaks'
ImageIndex = 124
OnClick = SelectLinebreaks
end
object menuWideLB: TMenuItem
Caption = 'Unicode linebreaks'
ImageIndex = 68
OnClick = SelectLinebreaks
end
object menuMixedLB: TMenuItem
Caption = 'Mixed linebreaks'
ImageIndex = 122
OnClick = SelectLinebreaks
end
end
object TimerMemoChange: TTimer
Interval = 200
OnTimer = TimerMemoChangeTimer
Left = 150
Top = 20
end
object popupEditor: TPopupMenu
Left = 300
Top = 20
object Selectall1: TMenuItem
Action = MainForm.actSelectAll
end
object Copy1: TMenuItem
Action = MainForm.actCopy
end
object Paste1: TMenuItem
Action = MainForm.actPaste
end
object Undo1: TMenuItem
Action = MainForm.actUndo
end
object N1: TMenuItem
Caption = '-'
end
object Findtext1: TMenuItem
Action = MainForm.actQueryFind
end
object Findorreplaceagain1: TMenuItem
Action = MainForm.actQueryFindAgain
end
object Replacetext1: TMenuItem
Action = MainForm.actQueryReplace
end
end
object popupHighlighter: TPopupMenu
Left = 440
Top = 40
object menuCustomizeHighlighter: TMenuItem
Caption = 'Customize highlighter'
ImageIndex = 39
OnClick = btnCustomizeHighlighterClick
end
object menuFormatCodeOnce: TMenuItem
Caption = 'Format code once'
OnClick = menuFormatCodeOnceClick
end
object menuAlwaysFormatCode: TMenuItem
AutoCheck = True
Caption = 'Always format code'
OnClick = menuAlwaysFormatCodeClick
end
end
end

539
source/texteditor.pas Normal file
View File

@ -0,0 +1,539 @@
unit texteditor;
interface
uses
Classes, Graphics, Forms, Controls, StdCtrls, laz.VirtualTrees,
ComCtrls, ToolWin, Dialogs, SysUtils, Menus, ExtDlgs, LCLType,
apphelpers, ActnList, extra_controls,
ExtCtrls, dbconnection, SynEdit, SynEditHighlighter, customize_highlighter,
reformatter,
SynHighlighterBat,
SynHighlighterCpp, SynHighlighterCss,
SynHighlighterHashEntries, SynHighlighterHtml,
SynHighlighterIni, SynHighlighterJScript,
SynHighlighterJava,
SynHighlighterPHP, SynHighlighterPas, SynHighlighterPerl,
SynHighlighterPython,
SynHighlighterSQL,
SynHighlighterTeX, SynHighlighterUNIXShellScript,
SynHighlighterVB,
SynHighlighterXML
;
{$I const.inc}
type
TfrmTextEditor = class(TExtForm)
Panel1: TPanel;
tlbStandard: TToolBar;
btnWrap: TToolButton;
btnLoadText: TToolButton;
btnApply: TToolButton;
btnCancel: TToolButton;
lblTextLength: TLabel;
btnLinebreaks: TToolButton;
popupLinebreaks: TPopupMenu;
menuWindowsLB: TMenuItem;
menuUnixLB: TMenuItem;
menuMacLB: TMenuItem;
menuMixedLB: TMenuItem;
menuWideLB: TMenuItem;
btnSearchFind: TToolButton;
btnSearchReplace: TToolButton;
btnSearchFindNext: TToolButton;
btnSeparator1: TToolButton;
TimerMemoChange: TTimer;
comboHighlighter: TComboBox;
MemoText: TSynEdit;
popupEditor: TPopupMenu;
Copy1: TMenuItem;
Paste1: TMenuItem;
Selectall1: TMenuItem;
Undo1: TMenuItem;
Findtext1: TMenuItem;
Findorreplaceagain1: TMenuItem;
Replacetext1: TMenuItem;
N1: TMenuItem;
ToolButton1: TToolButton;
btnCustomizeHighlighter: TToolButton;
popupHighlighter: TPopupMenu;
menuCustomizeHighlighter: TMenuItem;
menuFormatCodeOnce: TMenuItem;
menuAlwaysFormatCode: TMenuItem;
procedure btnApplyClick(Sender: TObject);
procedure btnCancelClick(Sender: TObject);
procedure btnLoadTextClick(Sender: TObject);
procedure btnWrapClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure MemoTextChange(Sender: TObject);
procedure MemoTextKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure MemoTextClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure SelectLinebreaks(Sender: TObject);
procedure TimerMemoChangeTimer(Sender: TObject);
procedure comboHighlighterSelect(Sender: TObject);
procedure btnCustomizeHighlighterClick(Sender: TObject);
procedure menuFormatCodeOnceClick(Sender: TObject);
procedure menuAlwaysFormatCodeClick(Sender: TObject);
private
{ Private declarations }
FModified: Boolean;
FClosingByApplyButton: Boolean;
FClosingByCancelButton: Boolean;
FDetectedLineBreaks,
FSelectedLineBreaks: TLineBreaks;
FMaxLength: Integer;
FTableColumn: TTableColumn;
FHighlighter: TSynCustomHighlighter;
FHighlighterFormatters: TStringList;
procedure SetModified(NewVal: Boolean);
procedure CustomizeHighlighterChanged(Sender: TObject);
public
function GetText: String;
procedure SetText(text: String);
procedure SetTitleText(Title: String);
procedure SetMaxLength(len: integer);
procedure SetFont(font: TFont);
property Modified: Boolean read FModified write SetModified;
property TableColumn: TTableColumn read FTableColumn write FTableColumn;
end;
implementation
uses main;
{$R *.lfm}
function TfrmTextEditor.GetText: String;
var
LB: String;
begin
Result := MemoText.Text;
// Convert linebreaks back to selected
LB := GetLineBreak(FSelectedLineBreaks);
if LB <> CRLF then
Result := StringReplace(Result, CRLF, LB, [rfReplaceAll]);
end;
procedure TfrmTextEditor.SetText(text: String);
var
Detected, Item: TMenuItem;
begin
// Apply text string, and detect type of line breaks in it
FDetectedLineBreaks := ScanLineBreaks(text);
Detected := nil;
if FDetectedLineBreaks = lbsNone then
FDetectedLineBreaks := TLineBreaks(AppSettings.ReadInt(asLineBreakStyle));
for Item in popupLinebreaks.Items do begin
if Item.Tag = Integer(FDetectedLineBreaks) then begin
Detected := Item;
end;
end;
if Assigned(Detected) then
SelectLineBreaks(Detected);
if (Length(text) > SIZE_MB) then begin
MainForm.LogSQL(_('Auto-disabling wordwrap for large text'));
btnWrap.Enabled := False;
end else begin
btnWrap.Enabled := True;
end;
MemoText.Text := text;
MemoText.SelectAll;
Modified := False;
end;
procedure TfrmTextEditor.SetTitleText(Title: String);
begin
// Add column name to window title bar
if Title <> '' then
Caption := Title + ' - ' + Caption;
end;
procedure TfrmTextEditor.TimerMemoChangeTimer(Sender: TObject);
var
MaxLen, CursorPos: String;
begin
// Timer based onchange handler, so we don't scan the whole text on every typed character
TimerMemoChange.Enabled := False;
if FMaxLength = 0 then
MaxLen := '?'
else
MaxLen := FormatNumber(FMaxLength);
CursorPos := FormatNumber(MemoText.CaretY) + ':' + FormatNumber(MemoText.CaretX);
lblTextLength.Caption := f_('%s characters (max: %s), %s lines, cursor at %s', [FormatNumber(MemoText.GetTextLen), MaxLen, FormatNumber(MemoText.Lines.Count), CursorPos]);
if MemoText.ReadOnly then
lblTextLength.Caption := lblTextLength.Caption + ', read-only';
end;
procedure TfrmTextEditor.btnCustomizeHighlighterClick(Sender: TObject);
var
Dialog: TfrmCustomizeHighlighter;
begin
// let user customize highlighter colors
Dialog := TfrmCustomizeHighlighter.Create(Self);
Dialog.FriendlyLanguageName := MemoText.Highlighter.GetLanguageName;
Dialog.OnChange := CustomizeHighlighterChanged;
Dialog.ShowModal;
Dialog.Free;
end;
procedure TfrmTextEditor.CustomizeHighlighterChanged(Sender: TObject);
var
Dialog: TfrmCustomizeHighlighter;
begin
Dialog := Sender as TfrmCustomizeHighlighter;
comboHighlighter.ItemIndex := comboHighlighter.Items.IndexOf(Dialog.FriendlyLanguageName);
comboHighlighter.OnSelect(comboHighlighter);
end;
procedure TfrmTextEditor.SelectLinebreaks(Sender: TObject);
var
Selected, Item: TMenuItem;
begin
Selected := Sender as TMenuItem;
menuWindowsLB.Caption := _('Windows linebreaks');
menuUnixLB.Caption := _('UNIX linebreaks');
menuMacLB.Caption := _('Mac OS linebreaks');
menuWideLB.Caption := _('Unicode linebreaks');
menuMixedLB.Caption := _('Mixed linebreaks');
for Item in popupLinebreaks.Items do begin
if Item.Tag = Integer(FDetectedLineBreaks) then begin
Item.Caption := Item.Caption + ' (' + _('detected') + ')';
end;
end;
Selected.Default := True;
btnLineBreaks.Hint := Selected.Caption;
btnLineBreaks.ImageIndex := Selected.ImageIndex;
FSelectedLineBreaks := TLineBreaks(Selected.Tag);
Modified := True;
end;
procedure TfrmTextEditor.SetMaxLength(len: integer);
begin
// Input: Length in number of bytes.
FMaxLength := len;
end;
procedure TfrmTextEditor.SetFont(font: TFont);
begin
MemoText.Font.Name := font.Name;
MemoText.Font.Size := font.Size;
end;
procedure TfrmTextEditor.FormCreate(Sender: TObject);
var
Highlighters: TSynHighlighterList;
i: Integer;
begin
HasSizeGrip := True;
FClosingByApplyButton := False;
// Assign linebreak values to their menu item tags, to write less code later
menuWindowsLB.Tag := Integer(lbsWindows);
menuUnixLB.Tag := Integer(lbsUnix);
menuMacLB.Tag := Integer(lbsMac);
menuWideLB.Tag := Integer(lbsWide);
menuMixedLB.Tag := Integer(lbsMixed);
Highlighters := SynEditHighlighter.GetPlaceableHighlighters;
for i:=0 to Highlighters.Count-1 do begin
comboHighlighter.Items.Add(Highlighters[i].GetLanguageName);
end;
FTableColumn := nil;
// Fix label position:
lblTextLength.Top := tlbStandard.Top + (tlbStandard.Height-lblTextLength.Height) div 2;
// Define highlighters for which we have a reformatter
FHighlighterFormatters := TStringList.Create;
//FHighlighterFormatters.Add(TSynJSONSyn.ClassName);
FHighlighterFormatters.Add(TSynSQLSyn.ClassName);
FHighlighterFormatters.Add(TSynXMLSyn.ClassName);
MemoText.OnMouseWheel := MainForm.AnySynMemoMouseWheel;
//MemoText.OnPaintTransient := MainForm.SynMemoQuery.OnPaintTransient;
if AppSettings.ReadBool(asMemoEditorMaximized) then
WindowState := wsMaximized;
end;
procedure TfrmTextEditor.FormDestroy(Sender: TObject);
begin
if WindowState <> wsMaximized then begin
AppSettings.WriteIntDpiAware(asMemoEditorWidth, Self, Width);
AppSettings.WriteIntDpiAware(asMemoEditorHeight, Self, Height);
end;
AppSettings.WriteBool(asMemoEditorMaximized, WindowState=wsMaximized);
if btnWrap.Enabled then begin
AppSettings.WriteBool(asMemoEditorWrap, btnWrap.Down);
end;
if Assigned(FTableColumn) and (comboHighlighter.Text <> AppSettings.GetDefaultString(asMemoEditorHighlighter)) then begin
AppSettings.SessionPath := MainForm.GetRegKeyTable;
AppSettings.WriteString(asMemoEditorHighlighter, comboHighlighter.Text, FTableColumn.Name);
end;
end;
procedure TfrmTextEditor.FormShow(Sender: TObject);
var
HighlighterName: String;
begin
// Restore form dimensions
if WindowState <> wsMaximized then begin
Width := AppSettings.ReadIntDpiAware(asMemoEditorWidth, Self);
Height := AppSettings.ReadIntDpiAware(asMemoEditorHeight, Self);
end;
if AppSettings.ReadBool(asMemoEditorWrap) and btnWrap.Enabled then begin
btnWrap.Click;
end;
menuAlwaysFormatCode.Checked := AppSettings.ReadBool(asMemoEditorAlwaysFormatCode);
// Select previously used highlighter
HighlighterName := AppSettings.GetDefaultString(asMemoEditorHighlighter);
if Assigned(FTableColumn) then begin
AppSettings.SessionPath := MainForm.GetRegKeyTable;
HighlighterName := AppSettings.ReadString(asMemoEditorHighlighter, FTableColumn.Name, HighlighterName);
end;
if MemoText.ReadOnly then begin
MemoText.Color := clBtnFace;
end;
comboHighlighter.ItemIndex := comboHighlighter.Items.IndexOf(HighlighterName);
comboHighlighter.OnSelect(comboHighlighter);
// Trigger change event, which is not fired when text is empty. See #132.
TimerMemoChangeTimer(Self);
MemoText.SetFocus;
end;
procedure TfrmTextEditor.MemoTextKeyDown(Sender: TObject; var Key: Word; Shift:
TShiftState);
begin
TimerMemoChange.Enabled := False;
TimerMemoChange.Enabled := True;
case Key of
// Cancel active dialog by Escape
VK_ESCAPE: begin
btnCancelClick(Sender);
end;
// Apply changes and end editing by Ctrl + Enter
VK_RETURN: if ssCtrl in Shift then btnApplyClick(Sender);
Ord('a'), Ord('A'): if (ssCtrl in Shift) and (not (ssAlt in Shift)) then Mainform.actSelectAllExecute(Sender);
end;
end;
procedure TfrmTextEditor.MemoTextClick(Sender: TObject);
begin
TimerMemoChange.Enabled := False;
TimerMemoChange.Enabled := True;
end;
procedure TfrmTextEditor.btnWrapClick(Sender: TObject);
var
WasModified: Boolean;
begin
Screen.Cursor := crHourglass;
// Changing the scrollbars invoke the OnChange event. We avoid thinking the text was really modified.
WasModified := Modified;
if MemoText.ScrollBars = ssBoth then begin
MemoText.ScrollBars := ssVertical;
//MemoText.WordWrap := True;
end else begin
MemoText.ScrollBars := ssBoth;
//MemoText.WordWrap := False;
end;
btnWrap.Down := MemoText.ScrollBars = ssVertical;
Modified := WasModified;
Screen.Cursor := crDefault;
end;
procedure TfrmTextEditor.comboHighlighterSelect(Sender: TObject);
var
Highlighters: TSynHighlighterList;
i: Integer;
SelStart, SelLength: Integer;
begin
// Code highlighter selected
SelStart := MemoText.SelStart;
SelLength := MemoText.SelEnd - MemoText.SelStart;
MemoText.Highlighter := nil;
FHighlighter.Free;
Highlighters := SynEditHighlighter.GetPlaceableHighlighters;
for i:=0 to Highlighters.Count-1 do begin
if comboHighlighter.Text = Highlighters[i].GetLanguageName then begin
FHighlighter := Highlighters[i].Create(Self);
MemoText.Highlighter := FHighlighter;
Break;
end;
end;
// In case the combobox is empty:
if MemoText.Highlighter = nil then begin
FHighlighter := TSynSQLSyn.Create(Self);
MemoText.Highlighter := FHighlighter;
end;
menuFormatCodeOnce.Enabled := FHighlighterFormatters.IndexOf(FHighlighter.ClassName) > -1;
if menuAlwaysFormatCode.Checked and menuFormatCodeOnce.Enabled then begin
menuFormatCodeOnce.OnClick(Sender);
SelStart := 0;
SelLength := 0;
end;
// Load custom highlighter settings from ini file, if exists:
MemoText.Highlighter.LoadFromFile(AppSettings.DirnameHighlighters + MemoText.Highlighter.LanguageName + '.ini');
MemoText.SelStart := SelStart;
MemoText.SelEnd := SelStart + SelLength;
end;
procedure TfrmTextEditor.btnLoadTextClick(Sender: TObject);
var
d: TExtFileOpenDialog;
begin
AppSettings.ResetPath;
d := TExtFileOpenDialog.Create(Self);
d.AddFileType('*.txt', _('Text files'));
d.AddFileType('*.*', _('All files'));
d.Encodings.Assign(MainForm.FileEncodings);
d.EncodingIndex := AppSettings.ReadInt(asFileDialogEncoding, Self.Name);
if d.Execute then try
Screen.Cursor := crHourglass;
MemoText.Text := ReadTextFile(d.FileName, MainForm.GetEncodingByName(d.Encodings[d.EncodingIndex]));
if (FMaxLength > 0) and (Length(MemoText.Text) > FMaxLength) then
MemoText.Text := copy(MemoText.Text, 0, FMaxLength);
AppSettings.WriteInt(asFileDialogEncoding, d.EncodingIndex, Self.Name);
finally
Screen.Cursor := crDefault;
end;
d.Free;
end;
procedure TfrmTextEditor.btnCancelClick(Sender: TObject);
begin
FClosingByCancelButton := True;
Close;
end;
procedure TfrmTextEditor.menuAlwaysFormatCodeClick(Sender: TObject);
begin
// Change setting for "always reformat"
AppSettings.WriteBool(asMemoEditorAlwaysFormatCode, menuAlwaysFormatCode.Checked);
if menuAlwaysFormatCode.Checked and menuFormatCodeOnce.Enabled then begin
menuFormatCodeOnce.OnClick(Sender);
end;
end;
procedure TfrmTextEditor.menuFormatCodeOnceClick(Sender: TObject);
//var
//JsonTmp: TJSONValue;
//Xml: TXmlVerySimple;
//XmlTmp: IXMLDocument;
begin
// Reformat code if possible
try
{if FHighlighter is TSynJSONSyn then begin
JsonTmp := TJSONObject.ParseJSONValue(MemoText.Text);
MemoText.Text := JsonTmp.Format;
JsonTmp.Free;
MemoText.SelStart := 0;
MemoText.SelLength := 0;
end
else} if FHighlighter is TSynSQLSyn then begin
// Prefer old internal formatter here, so the user does not run into request limits
frmReformatter := TfrmReformatter.Create(Self);
MemoText.Text := frmReformatter.FormatSqlInternal(MemoText.Text);
MemoText.SelStart := 0;
MemoText.SelEnd := 0;
frmReformatter.Free;
end
{else if FHighlighter is TSynXMLSyn then begin
XmlTmp := TXMLDocument.Create(nil);
XmlTmp.LoadFromXML(MemoText.Text);
MemoText.BeginUpdate;
MemoText.Text := XMLDoc.FormatXMLData(MemoText.Text);
MemoText.EndUpdate;
Xml := TXmlVerySimple.Create;
//Xml.Options := [doNodeAutoIndent, doParseProcessingInstr, doCaseInsensitive, doWriteBOM, doSimplifyTextNodes];
Xml.Clear;
Xml.Text := MemoText.Lines.Text.Trim;
MemoText.BeginUpdate;
MemoText.Lines.Text := Xml.Text;
MemoText.EndUpdate;
Xml.Free;
MemoText.SelStart := 0;
MemoText.SelLength := 0;
end}
else begin
Beep;
end;
except
on E:Exception do begin
Beep;
MainForm.LogSQL(f_('Error in code formatting: %s', [E.Message]));
end;
end;
end;
procedure TfrmTextEditor.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if Modified then begin
if FClosingByCancelButton then
ModalResult := mrCancel
else if FClosingByApplyButton then
ModalResult := mrYes
else
ModalResult := MessageDialog(_('Apply modifications?'), mtConfirmation, [mbYes, mbNo]);
end
else
ModalResult := mrCancel;
end;
procedure TfrmTextEditor.btnApplyClick(Sender: TObject);
begin
FClosingByApplyButton := True;
Close;
end;
procedure TfrmTextEditor.MemoTextChange(Sender: TObject);
begin
Modified := True;
TimerMemoChange.Enabled := False;
TimerMemoChange.Enabled := True;
end;
procedure TfrmTextEditor.SetModified(NewVal: Boolean);
begin
// Enables or disables "apply" button, and resets SynEdit's modification marker in its gutter
if FModified <> NewVal then begin
FModified := NewVal;
if not FModified then
MemoText.Modified := False;
btnApply.Enabled := FModified;
end;
end;
end.