From 81f407e030ffb3ae26144cc066e224eac1ba9e47 Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 21 Jun 2023 00:46:34 +0200 Subject: [PATCH] SynEdit: add TSynPopupMenu with default items for editor. Issue #38792 Patch by Joeny Ang "biloky" --- components/synedit/allsynedit.pas | 3 +- components/synedit/design/syneditlazdsgn.pas | 8 +- components/synedit/design/syneditlazdsgn.res | Bin 76937 -> 82008 bytes components/synedit/design/syneditlazdsgn.txt | 3 + components/synedit/design/tsynpopupmenu.png | Bin 0 -> 1043 bytes .../synedit/design/tsynpopupmenu_150.png | Bin 0 -> 1629 bytes .../synedit/design/tsynpopupmenu_200.png | Bin 0 -> 2208 bytes components/synedit/synedit.lpk | 4 + components/synedit/synpopupmenu.pas | 140 ++++++++++++++++++ 9 files changed, 155 insertions(+), 3 deletions(-) create mode 100644 components/synedit/design/tsynpopupmenu.png create mode 100644 components/synedit/design/tsynpopupmenu_150.png create mode 100644 components/synedit/design/tsynpopupmenu_200.png create mode 100644 components/synedit/synpopupmenu.pas diff --git a/components/synedit/allsynedit.pas b/components/synedit/allsynedit.pas index 03eb2ad055..6b61149258 100644 --- a/components/synedit/allsynedit.pas +++ b/components/synedit/allsynedit.pas @@ -32,10 +32,9 @@ uses SynHighlighterIni, SynEditMarkupSpecialChar, SynEditTextDoubleWidthChars, SynEditTextSystemCharWidth, SynEditMarkupIfDef, SynPluginMultiCaret, synhighlighterpike, SynEditMarkupFoldColoring, SynEditViewedLineMap, - SynEditWrappedView, SynBeautifierPascal, LazSynIMMBase, LazarusPackageIntf; + SynEditWrappedView, SynBeautifierPascal, LazSynIMMBase, SynPopupMenu, LazarusPackageIntf; implementation - procedure Register; begin RegisterUnit('SynEdit', @SynEdit.Register); diff --git a/components/synedit/design/syneditlazdsgn.pas b/components/synedit/design/syneditlazdsgn.pas index ca467c57b9..aab9ba01d8 100644 --- a/components/synedit/design/syneditlazdsgn.pas +++ b/components/synedit/design/syneditlazdsgn.pas @@ -43,7 +43,7 @@ uses SynHighlighterCss, SynHighlighterPHP, SynHighlighterTeX, SynHighlighterSQL, SynHighlighterPython, SynHighlighterVB, SynHighlighterAny, SynHighlighterDiff, SynHighlighterBat, SynHighlighterIni, SynHighlighterPo, - SynPluginSyncroEdit, + SynPluginSyncroEdit, SynPopupMenu, SynPropertyEditObjectList, SynDesignStringConstants, SynHighlighterJScript, LazarusPackageIntf, LResources, PropEdits, ComponentEditors; @@ -88,6 +88,11 @@ begin {$EndIF} end; +procedure RegisterSynPopupMenu; +begin + RegisterComponents('SynEdit',[TSynPopupMenu]); +end; + procedure RegisterSynHighlighterPas; begin RegisterComponents('SynEdit',[TSynPasSyn, TSynFreePascalSyn]); @@ -233,6 +238,7 @@ begin RegisterSynMacroRecorder; RegisterSynExportHTML; RegisterSynSyncroEdit; + RegisterSynPopupMenu; RegisterSynHighlighterPas;; RegisterSynHighlighterCPP; diff --git a/components/synedit/design/syneditlazdsgn.res b/components/synedit/design/syneditlazdsgn.res index b1982c81845522b0e26b83b3c49d608ae127a1c3..93b71adfa7328758e527f8fcb714c68bc5c781c7 100644 GIT binary patch delta 4848 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 N002ovPDHLkV1m|t=e7U< literal 0 HcmV?d00001 diff --git a/components/synedit/design/tsynpopupmenu_150.png b/components/synedit/design/tsynpopupmenu_150.png new file mode 100644 index 0000000000000000000000000000000000000000..dfaee8f685f987cb26d588bedcb6baf010f487df GIT binary patch literal 1629 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 + + + + diff --git a/components/synedit/synpopupmenu.pas b/components/synedit/synpopupmenu.pas new file mode 100644 index 0000000000..d2caf6443d --- /dev/null +++ b/components/synedit/synpopupmenu.pas @@ -0,0 +1,140 @@ +unit SynPopupMenu; + +{$mode ObjFPC}{$H+} + +interface + +uses + Classes, SysUtils, Menus, SynEdit; + +type + TSynDefaultPopupMenu = (dpmDisabled, dpmBefore, dpmAfter); + + TSynPopupMenu = class(TPopupMenu) + private + FDefaultPopupMenu: TSynDefaultPopupMenu; + procedure FillDefaultMenu; + procedure ClearDefaultMenu; + protected + procedure ItemOnClick(Sender: TObject); + public + constructor Create(AOwner: TComponent); override; + procedure PopUp(X, Y: Integer); override; + published + property DefaultPopupMenu: TSynDefaultPopupMenu read + FDefaultPopupMenu write FDefaultPopupMenu default dpmBefore; + end; + +implementation + +type + TMenuEntry = (meNone, meUndo, meRedo, meCut, meCopy, mePaste, + meDelete, meSelectAll); + +resourcestring + SYNS_Undo = '&Undo'; + SYNS_Redo = '&Redo'; + SYNS_Cut = 'C&ut'; + SYNS_Copy = '&Copy'; + SYNS_Paste = '&Paste'; + SYNS_Delete = '&Delete'; + SYNS_SelectAll = 'Select &all'; + +{ TSynPopupMenu } + +constructor TSynPopupMenu.Create(AOwner: TComponent); +begin + inherited; + FDefaultPopupMenu := dpmBefore; +end; + +procedure TSynPopupMenu.FillDefaultMenu; +var + i: Integer; + + procedure AddMenuItem(const ACaption: string; const ATag: TMenuEntry); + var + FItem: TMenuItem; + begin + FItem := TMenuItem.Create(Self); + FItem.Caption := ACaption; + FItem.OnClick := @ItemOnClick; + FItem.Tag := Integer(ATag); + if FDefaultPopupMenu = dpmAfter then + Items.Add(FItem) + else + Items.Insert(i, FItem); + Inc(i); + end; + +var + FEmpty: Boolean; +begin + if FDefaultPopupMenu = dpmDisabled then + Exit; + i := 0; + FEmpty := Items.Count = 0; + if not FEmpty and (FDefaultPopupMenu = dpmAfter) then // separator + AddMenuItem('-', meNone); + AddMenuItem(SYNS_Undo, meUndo); + AddMenuItem(SYNS_Redo, meRedo); + AddMenuItem('-', meNone); + AddMenuItem(SYNS_Cut, meCut); + AddMenuItem(SYNS_Copy, meCopy); + AddMenuItem(SYNS_Paste, mePaste); + AddMenuItem('-', meNone); + AddMenuItem(SYNS_Delete, meDelete); + AddMenuItem(SYNS_SelectAll, meSelectAll); + if not FEmpty and (FDefaultPopupMenu = dpmBefore) then // separator + AddMenuItem('-', meNone); +end; + +procedure TSynPopupMenu.ClearDefaultMenu; +var + i: Integer; +begin + for i := Items.Count - 1 downto 0 do + if Items[i].OnClick = @ItemOnClick then + Items.Delete(i); +end; + +procedure TSynPopupMenu.ItemOnClick(Sender: TObject); +begin + with TCustomSynEdit(PopupComponent) do + case TMenuEntry(TMenuItem(Sender).Tag) of + meUndo: Undo; + meRedo: Redo; + meCut: CutToClipboard; + meCopy: CopyToClipboard; + mePaste: PasteFromClipboard; + meDelete: SelText := ''; + meSelectAll: SelectAll; + end; +end; + +procedure TSynPopupMenu.PopUp(X, Y: Integer); +var + i: Integer; +begin + ClearDefaultMenu; + if PopupComponent is TCustomSynEdit then + begin + FillDefaultMenu; + for i := 0 to Items.Count - 1 do + with TCustomSynEdit(PopupComponent) do + if Items[i].OnClick = @ItemOnClick then // make sure it's ours + case TMenuEntry(Items[i].Tag) of + meUndo: Items[i].Enabled := CanUndo; + meRedo: Items[i].Enabled := CanRedo; + meCut: Items[i].Enabled := SelAvail and not ReadOnly; + meCopy: Items[i].Enabled := SelAvail; + mePaste: Items[i].Enabled := CanPaste; + meDelete: Items[i].Enabled := SelAvail and not ReadOnly; + meSelectAll: Items[i].Enabled := Trim(Lines.Text) <> ''; + end; + end; + inherited; +end; + +end. +