From d6d2d80d9f18f3f5d77f4d8579b37917638079c0 Mon Sep 17 00:00:00 2001 From: martin Date: Sun, 19 Apr 2009 00:39:59 +0000 Subject: [PATCH] SynEdit Folding: Added Pop-up-Menu to Gutter git-svn-id: trunk@19499 - --- components/synedit/synedit.pp | 2 +- components/synedit/syneditfoldedview.pp | 60 ++++++++++++++++++- components/synedit/syngutter.pp | 8 ++- components/synedit/synguttercodefolding.pp | 68 +++++++++++++++++++++- 4 files changed, 130 insertions(+), 8 deletions(-) diff --git a/components/synedit/synedit.pp b/components/synedit/synedit.pp index 95add36c57..d8dfee7573 100644 --- a/components/synedit/synedit.pp +++ b/components/synedit/synedit.pp @@ -2336,13 +2336,13 @@ begin end; if (X < fGutterWidth) then begin Include(fStateFlags, sfGutterClick); - inherited MouseDown(Button, Shift, X, Y); IncPaintLock; try FGutter.MouseDown(Button, Shift, X, Y); finally DecPaintLock; end; + inherited MouseDown(Button, Shift, X, Y); LCLIntf.SetFocus(Handle); exit; end; diff --git a/components/synedit/syneditfoldedview.pp b/components/synedit/syneditfoldedview.pp index a82336d4ca..6e613503a9 100644 --- a/components/synedit/syneditfoldedview.pp +++ b/components/synedit/syneditfoldedview.pp @@ -170,6 +170,14 @@ const ); type + TFoldViewNodeInfo = record + HNode: TSynFoldNodeInfo; + Text, Keyword: String; + LineNum, ColIndex, OpenIndex: Integer; + OpenCount: Integer; + Folded: boolean; + end; + { TSynTextFoldedView *Line = Line (0-based) on Screen (except TopLine which should be TopViewPos) *ViewPos = Line (1-based) in the array of viewable/visible lines @@ -282,7 +290,11 @@ type procedure UnfoldAll; procedure FoldAll(StartLevel : Integer = 0; IgnoreNested : Boolean = False); procedure FixFoldingAtTextIndex(AStartIndex: Integer; AMinEndLine: Integer = 0); // Real/All lines - + public + function OpenFoldCount(aStartIndex: Integer): Integer; + function OpenFoldInfo(aStartIndex, ColIndex: Integer): TFoldViewNodeInfo; + + public // Find the visible first line of the fold at ALine. Returns -1 if Aline is not folded function CollapsedLineForFoldAtLine(ALine : Integer) : Integer; function ExpandedLineForBlockAtLine(ALine : Integer; HalfExpanded: Boolean = True) : Integer; @@ -2145,6 +2157,52 @@ begin FixFolding(AStartIndex + 1, AMinEndLine, fFoldTree); end; +function TSynEditFoldedView.OpenFoldCount(aStartIndex: Integer): Integer; +var + hl: TSynCustomFoldHighlighter; +begin + if not(assigned(FHighLighter) and (FHighLighter is TSynCustomFoldHighlighter)) + then exit(-1); + hl := TSynCustomFoldHighlighter(FHighLighter); + Result := hl.FoldNestCount(AStartIndex-1) + hl.FoldOpenCount(AStartIndex); +end; + +function TSynEditFoldedView.OpenFoldInfo(aStartIndex, ColIndex: Integer): TFoldViewNodeInfo; +var + hl: TSynCustomFoldHighlighter; + n, o: Integer; + nd: TSynFoldNodeInfo; +begin + if not(assigned(FHighLighter) and (FHighLighter is TSynCustomFoldHighlighter)) + then exit; + hl := TSynCustomFoldHighlighter(FHighLighter); + hl.CurrentLines := fLines; + n := hl.FoldNestCount(AStartIndex-1); + while (ColIndex < n) do begin + dec(aStartIndex); + n := hl.FoldNestCount(AStartIndex-1); + end; + ColIndex := ColIndex - n; + o := hl.FoldOpenCount(AStartIndex); + Result.OpenCount := o; + n := hl.FoldNodeInfoCount[aStartIndex] - 1; + while (o > ColIndex) and (n >= 0) do begin + nd := hl.FoldNodeInfo[aStartIndex, n]; + if sfaInvalid in nd.FoldAction then break; + if sfaClose in nd.FoldAction then inc(o); + if sfaOpen in nd.FoldAction then dec(o); + dec(n); + end; + inc(n); + Result.HNode := nd; + Result.Text := fLines[aStartIndex]; + Result.Keyword := copy(Result.Text, 1 + nd.LogXStart, nd.LogXEnd-nd.LogXStart); + Result.LineNum := aStartIndex + 1; + Result.ColIndex := ColIndex; // for FoldAction + Result.OpenIndex := ColIndex; // for (2/3) + Result.Folded := IsFoldedAtTextIndex(aStartIndex, ColIndex); +end; + function TSynEditFoldedView.ExpandedLineForBlockAtLine(ALine : Integer; HalfExpanded: Boolean = True) : Integer; var diff --git a/components/synedit/syngutter.pp b/components/synedit/syngutter.pp index 5b3a0bdd5a..2fd22061f6 100644 --- a/components/synedit/syngutter.pp +++ b/components/synedit/syngutter.pp @@ -28,6 +28,7 @@ type FVisible: boolean; FAutoSize: boolean; FOnGutterClick: TGutterClickEvent; + FMouseDownPart: Integer; procedure SetAutoSize(const Value: boolean); procedure SetLeftOffset(Value: integer); procedure SetRightOffset(Value: integer); @@ -304,19 +305,20 @@ end; procedure TSynGutter.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin - Parts[PixelToPartIndex(X)].MouseDown(Button, Shift, X, Y); + FMouseDownPart := PixelToPartIndex(X); + Parts[FMouseDownPart].MouseDown(Button, Shift, X, Y); if (Button=mbLeft) then DoOnGutterClick(X, Y); end; procedure TSynGutter.MouseMove(Shift: TShiftState; X, Y: Integer); begin - Parts[PixelToPartIndex(X)].MouseMove(Shift, X, Y); + Parts[FMouseDownPart].MouseMove(Shift, X, Y); end; procedure TSynGutter.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin - Parts[PixelToPartIndex(X)].MouseUp(Button, Shift, X, Y); + Parts[FMouseDownPart].MouseUp(Button, Shift, X, Y); end; function TSynGutter.LineNumberPart(Index: Integer = 0): TSynGutterLineNumber; diff --git a/components/synedit/synguttercodefolding.pp b/components/synedit/synguttercodefolding.pp index 1a49f8dc1e..50c261d9f8 100644 --- a/components/synedit/synguttercodefolding.pp +++ b/components/synedit/synguttercodefolding.pp @@ -5,7 +5,7 @@ unit SynGutterCodeFolding; interface uses - Classes, Controls, Graphics, LCLIntf, SynGutterBase, SynEditMiscProcs, + sysutils, Classes, Controls, Graphics, Menus, LCLIntf, SynGutterBase, SynEditMiscProcs, SynEditFoldedView; type @@ -37,8 +37,11 @@ type FFoldView: TSynEditFoldedView; FExpandedClickConf, FCollapsedClickConf: TSynGutterFoldClickConfList; + FPopUp: TPopupMenu; + FMenuInf: Array of TFoldViewNodeInfo; protected procedure DoChange(Sender: TObject); override; + procedure PopClicked(Sender: TObject); public constructor Create(AOwner: TComponent); override; destructor Destroy; override; @@ -47,6 +50,7 @@ type override; function RealGutterWidth(CharWidth: integer): integer; override; procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override; + procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override; procedure DoOnGutterClick(X, Y: integer); override; property ExpandedClickConf: TSynGutterFoldClickConfList read FExpandedClickConf; property CollapsedClickConf: TSynGutterFoldClickConfList read FCollapsedClickConf; @@ -80,6 +84,7 @@ begin MarkupInfo.FrameColor := clNone; FWidth := 10; + FPopUp := TPopupMenu.Create(nil); for i := low(TSynGutterFoldClickType) to high(TSynGutterFoldClickType) do begin FExpandedClickConf[i].Enabled := False; @@ -124,6 +129,7 @@ end; destructor TSynGutterCodeFolding.Destroy; begin + FreeAndNil(FPopUp); inherited Destroy; end; @@ -137,18 +143,25 @@ end; procedure TSynGutterCodeFolding.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); +var + ClickDone: Boolean; + m: TMenuItem; + line, i, c: Integer; + inf: TFoldViewNodeInfo; + s, s2: String; function isClick(conf : TSynGutterFoldClickConf): Boolean; begin + if ClickDone then exit(False); Result := ( conf.Enabled and (Button = conf.Button) and (Shift * conf.ShiftMask = conf.Shift) ) or ( conf.Enabled2 and (Button = conf.Button2) and (Shift * conf.ShiftMask2 = conf.Shift2) ); + ClickDone := Result; end; -var - line : integer; begin line := TSynEdit(SynEdit).PixelsToRowColumn(Point(X, Y)).Y; if line > SynEdit.Lines.Count then exit; + ClickDone := False;; case FFoldView.FoldType[FFoldView.TextIndexToScreenLine(Line-1)] of cfCollapsed : @@ -170,6 +183,55 @@ begin FFoldView.FoldAtTextIndex(Line-1, -1, 0); end; end; + if (Button = mbRight) then begin + if not ClickDone then begin + FPopUp.Items.Clear; + c := FFoldView.OpenFoldCount(line-1); + SetLength(FMenuInf,c); + for i := c-1 downto 0 do begin + inf := FFoldView.OpenFoldInfo(line-1, i); + FMenuInf[i] := inf; + if (i < c-1) and (FMenuInf[i+1].LineNum = line) and (inf.LineNum <> line) + then begin + m := TMenuItem.Create(FPopUp); + m.Caption := cLineCaption; + m.Tag := -1; + FPopUp.Items.Add(m); + end; + s := copy(inf.Text, 1, inf.HNode.LogXStart-1); + if length(s) > 30 then s := copy(s,1,15) + '...' + copy(s, inf.HNode.LogXStart-11,10); + s := s + copy(inf.Text, inf.HNode.LogXStart, 30 + (30 - length(s))); + s2 := ''; + if inf.OpenCount > 1 then + s2 := format(' (%d/%d)', [inf.OpenIndex+1, inf.OpenCount]); + m := TMenuItem.Create(FPopUp); + m.Caption := format('%4d %-12s %s', [ inf.LineNum, inf.Keyword+s2+':', s]); + m.ShowAlwaysCheckable := true; + m.Checked := inf.Folded; + m.Tag := i; + m.OnClick := {$IFDEF FPC}@{$ENDIF}PopClicked; + FPopUp.Items.Add(m); + end; + FPopUp.PopUp; + end; + end; +end; + +procedure TSynGutterCodeFolding.PopClicked(Sender: TObject); +var + inf: TFoldViewNodeInfo; +begin + inf := FMenuInf[(Sender as TMenuItem).tag]; + if inf.Folded then + FFoldView.UnFoldAtTextIndex(inf.LineNum-1, inf.ColIndex, 1, False) + else + FFoldView.FoldAtTextIndex(inf.LineNum-1, inf.ColIndex, 1, False); +end; + +procedure TSynGutterCodeFolding.MouseUp(Button: TMouseButton; Shift: TShiftState; X, + Y: Integer); +begin + inherited MouseUp(Button, Shift, X, Y); end; procedure TSynGutterCodeFolding.DoOnGutterClick(X, Y : integer);