diff --git a/heidisql.lpi b/heidisql.lpi index b6ac62e9..773e4b92 100644 --- a/heidisql.lpi +++ b/heidisql.lpi @@ -250,6 +250,20 @@ + + + + + + + + + + + + + + diff --git a/heidisql.lpr b/heidisql.lpr index d247c413..9e933dc6 100644 --- a/heidisql.lpr +++ b/heidisql.lpr @@ -19,8 +19,8 @@ uses dbstructures.sqlite, change_password, loginform, data_sorting, extra_controls, column_selection, loaddata, csv_detector, createdatabase, editvar, copytable, 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 resources.rc} diff --git a/source/customize_highlighter.lfm b/source/customize_highlighter.lfm new file mode 100644 index 00000000..e27049cb --- /dev/null +++ b/source/customize_highlighter.lfm @@ -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 diff --git a/source/customize_highlighter.pas b/source/customize_highlighter.pas new file mode 100644 index 00000000..adab9c88 --- /dev/null +++ b/source/customize_highlighter.pas @@ -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. diff --git a/source/main.pas b/source/main.pas index eb74cd66..b2d864d0 100644 --- a/source/main.pas +++ b/source/main.pas @@ -1418,7 +1418,7 @@ implementation uses FileInfo, winpeimagereader, elfreader, machoreader, About, data_sorting, column_selection, loaddata, editvar, copytable, csv_detector, exportgrid, usermanager, selectdbobject, reformatter, connections, sqlhelp, updatecheck, - insertfiles; + insertfiles, texteditor; {$R *.lfm} @@ -12995,9 +12995,9 @@ begin end; if (not Assigned(Result)) and QueryTabs.HasActiveTab then Result := QueryTabs.ActiveMemo; - //if (not Assigned(Result)) and (Screen.ActiveForm is TfrmTextEditor) then begin - // Result := TfrmTextEditor(Screen.ActiveForm).MemoText; - //end; + if (not Assigned(Result)) and (Screen.ActiveForm is TfrmTextEditor) then begin + Result := TfrmTextEditor(Screen.ActiveForm).MemoText; + end; end; diff --git a/source/searchreplace.pas b/source/searchreplace.pas index e8158309..7e5e989c 100644 --- a/source/searchreplace.pas +++ b/source/searchreplace.pas @@ -8,7 +8,7 @@ uses SysUtils, Classes, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, SynEdit, SynEditTypes, laz.VirtualTrees, RegExpr, SynEditRegexSearch, SynEditMiscClasses, SynEditSearch, extra_controls, - Menus{, texteditor}; + Menus, texteditor; type TfrmSearchReplace = class(TExtForm) @@ -123,7 +123,7 @@ begin AnySynMemo := MainForm.ActiveSynMemo(True); if Assigned(AnySynMemo) then begin 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 UsedSynMemo := AnySynMemo; end; diff --git a/source/texteditor.lfm b/source/texteditor.lfm new file mode 100644 index 00000000..a360024c --- /dev/null +++ b/source/texteditor.lfm @@ -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 diff --git a/source/texteditor.pas b/source/texteditor.pas new file mode 100644 index 00000000..4718d10f --- /dev/null +++ b/source/texteditor.pas @@ -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.