SynEdit: Fix popup for fold gutter (show hides correct)

git-svn-id: trunk@26829 -
This commit is contained in:
martin 2010-07-25 15:39:23 +00:00
parent 2298d6db03
commit b1d560f33b
2 changed files with 241 additions and 130 deletions

View File

@ -222,11 +222,11 @@ type
TFoldChangedEvent = procedure(aLine: Integer) of object;
TFoldViewNodeInfo = record
HNode: TSynFoldNodeInfo;
HNode: TSynFoldNodeInfo; // Highlighter Node
FNode: TSynTextFoldAVLNode; // AvlFoldNode
Text, Keyword: String;
LineNum, ColIndex: Integer;
OpenCount: Integer;
Folded: boolean;
end;
TSynEditFoldLineCapability = (
@ -249,17 +249,16 @@ type
TSynEditFoldProvider = class
private
FHighlighter: TSynCustomFoldHighlighter;
function GetFoldOpenCount(ALineIdx: Integer): Integer;
function GetLineCapabilities(ALineIdx: Integer): TSynEditFoldLineCapabilities;
procedure SetHighLighter(const AValue: TSynCustomFoldHighlighter);
public
property HighLighter: TSynCustomFoldHighlighter read FHighlighter write SetHighLighter;
property FoldOpenCount[ALineIdx: Integer]: Integer read GetFoldOpenCount;
function FoldOpenCount(ALineIdx: Integer; AType: Integer = 0): Integer;
//property FoldOpenInfo[ALineIdx, AColumnIdx: Integer]: Integer read GetFoldOpenInfo;
//property FoldInfoCount[ALineIdx: Integer]: Integer read GetFoldInfoCount;
//property FoldInfo[ALineIdx, AColumnIdx: Integer]: Integer read GetFoldInfo;
property LineCapabilities[ALineIdx: Integer]: TSynEditFoldLineCapabilities
read GetLineCapabilities;
property HighLighter: TSynCustomFoldHighlighter read FHighlighter write SetHighLighter;
end;
{ TSynTextFoldedView
@ -1025,7 +1024,7 @@ procedure TSynEditFoldExportCoder.AddNode(aX, aY, aLen: Integer; aFoldType: TSyn
<Stream> = { <TypeStream> };
<TypeStream> = " T" <TypeId> <TypeData>; (* Stores all folds for the given type (eg cfbtBeginEnd) *)
<TypeStream> = " T" <TypeId> <TypeData>; [* Stores all folds for the given type (eg cfbtBeginEnd) *]
<TypeId> = ord(cfbtBeginEnd) or similar
<TypeData> = [<HideInfo>],
@ -1037,11 +1036,11 @@ procedure TSynEditFoldExportCoder.AddNode(aX, aY, aLen: Integer; aFoldType: TSyn
<FoldList> = [{ <ConsecutiveFoldedCount>, <ConsecutiveUnFoldedCount> }],
<ConsecutiveFoldedCount>,
;
(* NodePos: is the position of a folded node (of the type matching the current stream)
[* NodePos: is the position of a folded node (of the type matching the current stream)
ConsecutiveFoldedCount: more folded nodes of the same type, without any
unfolded node (of this type) inbetween.
ConsecutiveUnFoldedCount: amount of unfolded nodes (of this type) before the next folded node.
*)
*]
<NodePos> = <YOffset> <XPos> <len>;
<YOffset> = <Number>
@ -1051,14 +1050,14 @@ procedure TSynEditFoldExportCoder.AddNode(aX, aY, aLen: Integer; aFoldType: TSyn
<ConsecutiveUnFoldedCount> = <ExNumber>
<FoldListEndCont> = ' p', <SumFoldedLines>;
(* FoldListEndCont is mandotory, if another block of <NodePos>, <FoldList> is coming *)
[* FoldListEndCont is mandotory, if another block of <NodePos>, <FoldList> is coming *]
<FoldListEnd> = ' P' <SumFoldedLines>, <EndY>, <EndX>;
(* FoldListEnd is optional. It is expected if the previous <FoldList> has more than 10 folded lines*)
[* FoldListEnd is optional. It is expected if the previous <FoldList> has more than 10 folded lines*]
<SumFoldedLines> = <Number>
(* The sum of all lines folded by folds in <ConsecutiveFoldedCount>.
[* The sum of all lines folded by folds in <ConsecutiveFoldedCount>.
Not including the fold in <NodePos>, which has it's own len.
*)
*]
<Number> = bigger numbers
<ExNumber> = for numbers expected below 467; specially 0..80
@ -2948,25 +2947,35 @@ begin
if FHighlighter.FoldOpenCount(ALineIdx) > 0 then include(Result, cfFoldStart);
end;
function TSynEditFoldProvider.GetFoldOpenCount(ALineIdx: Integer): Integer;
begin
if (FHighlighter = nil) or (ALineIdx < 0) then exit(0);
//if c > 0 then begin
Result := FHighlighter.FoldNodeInfoCount[ALineIdx, [sfaOpen, sfaFold]];
//Result := Result + FHighlighter.FoldNodeInfoCount[ALineIdx, [sfaOpen, sfaFoldHide]];
//Result := Result + FHighlighter.FoldNodeInfoCount[ALineIdx, [sfaOneLineOpen, sfaFoldHide]];
//end
//else
if Result < 0 then
Result := FHighlighter.FoldOpenCount(ALineIdx);
end;
procedure TSynEditFoldProvider.SetHighLighter(const AValue: TSynCustomFoldHighlighter);
begin
if FHighlighter = AValue then exit;
FHighlighter := AValue;
end;
function TSynEditFoldProvider.FoldOpenCount(ALineIdx: Integer; AType: Integer = 0): Integer;
var
i: Integer;
begin
if (FHighlighter = nil) or (ALineIdx < 0) then exit(0);
Result := FHighlighter.FoldNodeInfoCount[ALineIdx, [sfaOpen, sfaFold]];
if (result > 0) and (AType > 0) then begin
i := Result ;
Result := 0;
while i > 0 do begin
dec(i);
if FHighlighter.FoldNodeInfo[ALineIdx, i, [sfaOpen, sfaFold]].FoldGroup = AType then
inc(Result);
end;
end
//Result := Result + FHighlighter.FoldNodeInfoCount[ALineIdx, [sfaOpen, sfaFoldHide]];
//Result := Result + FHighlighter.FoldNodeInfoCount[ALineIdx, [sfaOneLineOpen, sfaFoldHide]];
else
if Result < 0 then
Result := FHighlighter.FoldOpenCount(ALineIdx, AType);
end;
{ TSynEditFoldedView }
constructor TSynEditFoldedView.Create(aTextView : TSynEditStrings; ACaret: TSynEditCaret);
@ -3689,7 +3698,7 @@ begin
// AStartIndex is 0-based
// FoldTree is 1-based AND first line remains visble
NodeCount := FoldProvider.FoldOpenCount[AStartIndex];
NodeCount := FoldProvider.FoldOpenCount(AStartIndex);
if ColCount = 0 then
ColCount := NodeCount;
@ -3764,7 +3773,7 @@ begin
if not assigned(hl) then
exit;
top := TopTextIndex;
c := FoldProvider.FoldOpenCount[AStartIndex];
c := FoldProvider.FoldOpenCount(AStartIndex);
r := -1;
if ColCount = 0 then begin
@ -3904,7 +3913,7 @@ var
if (FldLine > PrevFldLine) then
AtColumn := 0;
// check the fold-length
MaxCol := FoldProvider.FoldOpenCount[FldIndex] - 1;
MaxCol := FoldProvider.FoldOpenCount(FldIndex) - 1;
FldCol := node.FoldIndex;
IsHide := node.SourceLineOffset = 0;
if (FldCol < AtColumn) then FldCol := AtColumn;
@ -4039,7 +4048,7 @@ begin
hl := TSynCustomFoldHighlighter(HighLighter);
if not assigned(hl) then
exit(-1);
Result := hl.FoldNestCount(AStartIndex-1) + FoldProvider.FoldOpenCount[AStartIndex];
Result := hl.FoldNestCount(AStartIndex-1) + FoldProvider.FoldOpenCount(AStartIndex);
end;
function TSynEditFoldedView.OpenFoldInfo(aStartIndex, ColIndex: Integer): TFoldViewNodeInfo;
@ -4054,7 +4063,7 @@ var
begin
for i := 1 to TypeCnt do begin
EndLvl[i] := hl.FoldNestCount(l-1, i);
EndLvl[i] := EndLvl[i] + hl.FoldOpenCount(l, i);
EndLvl[i] := EndLvl[i] + FoldProvider.FoldOpenCount(l, i);
CurLvl[i] := EndLvl[i];
end
end;
@ -4067,52 +4076,53 @@ begin
Lvl := hl.FoldNestCount(AStartIndex-1);
i := 0;
if ColIndex >= Lvl then begin
// search current line
Lvl := Lvl + hl.FoldOpenCount(aStartIndex);
i := 1;
end;
SetLength(EndLvl, TypeCnt+1);
SetLength(CurLvl, TypeCnt+1);
GetEndLvl(aStartIndex);
GetEndLvl(aStartIndex);
aStartIndex := aStartIndex + i;
while (ColIndex < Lvl) and (aStartIndex > 0) do begin
dec(aStartIndex);
if (hl.FoldOpenCount(aStartIndex) > 0) or
(hl.FoldCloseCount(aStartIndex) > 0) then begin
n := ColIndex - Lvl;
o := hl.FoldNodeInfoCount[aStartIndex, [sfaOpen, sfaFold]];
nd := hl.FoldNodeInfo[aStartIndex, n, [sfaOpen, sfaFold]];
end
else begin
SetLength(EndLvl, TypeCnt+1);
SetLength(CurLvl, TypeCnt+1);
GetEndLvl(aStartIndex);
aStartIndex := aStartIndex + i;
while (ColIndex < Lvl) and (aStartIndex > 0) do begin
dec(aStartIndex);
o := hl.FoldOpenCount(AStartIndex);
n := o;
for i := hl.FoldNodeInfoCount[aStartIndex, []] - 1 downto 0 do begin
nd := hl.FoldNodeInfo[aStartIndex, i, []];
if not(sfaFold in nd.FoldAction) then
continue;
t := nd.FoldGroup;
if sfaOpen in nd.FoldAction then begin
dec(n);
dec(CurLvl[t]);
if CurLvl[t] < EndLvl[t] then begin
dec(EndLvl[t]);
dec(Lvl);
if ColIndex = Lvl then begin
break;
if (o > 0) or (hl.FoldCloseCount(aStartIndex) > 0) then begin
n := o;
for i := hl.FoldNodeInfoCount[aStartIndex, []] - 1 downto 0 do begin
nd := hl.FoldNodeInfo[aStartIndex, i, []];
if not(sfaFold in nd.FoldAction) then
continue;
t := nd.FoldGroup;
if sfaOpen in nd.FoldAction then begin
dec(n);
dec(CurLvl[t]);
if CurLvl[t] < EndLvl[t] then begin
dec(EndLvl[t]);
dec(Lvl);
if ColIndex = Lvl then begin
break;
end;
end;
end else
if sfaClose in nd.FoldAction then begin
inc(CurLvl[t]);
end;
end else
if sfaClose in nd.FoldAction then begin
inc(CurLvl[t]);
end;
end;
end
else
if hl.FoldNestCount(AStartIndex-1) = 0 then break;
end
else
if hl.FoldNestCount(AStartIndex-1) = 0 then break;
end;
end;
Result.HNode := nd;
Result.OpenCount := o;
Result.Text := fLines[aStartIndex];
Result.Keyword := copy(Result.Text, 1 + nd.LogXStart, nd.LogXEnd-nd.LogXStart);
if not(sfaInvalid in nd.FoldAction) then
Result.Keyword := copy(Result.Text, 1 + nd.LogXStart, nd.LogXEnd-nd.LogXStart);
Result.LineNum := aStartIndex + 1;
Result.ColIndex := n;
Result.Folded := IsFoldedAtTextIndex(aStartIndex, n);
Result.FNode := FoldNodeAtTextIndex(aStartIndex, n);
end;
function TSynEditFoldedView.ExpandedLineForBlockAtLine(ALine : Integer;

View File

@ -6,7 +6,7 @@ interface
uses
SysUtils, Classes, Controls, Graphics, Menus, LCLIntf, SynGutterBase, SynEditMiscProcs,
SynEditFoldedView, SynEditMouseCmds, SynEditHighlighterFoldBase, LCLProc;
SynEditFoldedView, SynEditMouseCmds, SynEditHighlighterFoldBase, LCLProc, ImgList;
type
@ -45,11 +45,17 @@ type
FPopUp: TPopupMenu;
FMenuInf: Array of TFoldViewNodeInfo;
FIsFoldHidePreviousLine: Boolean;
FPopUpImageList: TImageList;
FReversePopMenuOrder: Boolean;
procedure SetMouseActionsCollapsed(const AValue: TSynEditMouseActions);
procedure SetMouseActionsExpanded(const AValue: TSynEditMouseActions);
function FoldTypeForLine(AScreenLine: Integer): TSynEditFoldLineCapability;
function IsFoldHidePreviousLine(AScreenLine: Integer): Boolean;
function IsSingleLineHide(AScreenLine: Integer): Boolean;
procedure InitPopUpImageList;
procedure DrawNodeSymbol(Canvas: TCanvas; Rect: TRect;
NodeType: TSynEditFoldLineCapability;
SubType: Integer);
protected
procedure DoChange(Sender: TObject); override;
procedure PopClicked(Sender: TObject);
@ -71,6 +77,8 @@ type
read FMouseActionsExpanded write SetMouseActionsExpanded;
property MouseActionsCollapsed: TSynEditMouseActions
read FMouseActionsCollapsed write SetMouseActionsCollapsed;
property ReversePopMenuOrder: Boolean
read FReversePopMenuOrder write FReversePopMenuOrder default True;
end;
implementation
@ -151,6 +159,44 @@ begin
Result := True;
end;
procedure TSynGutterCodeFolding.InitPopUpImageList;
var
img: TBitmap;
procedure NewImg;
begin
img := TBitmap.Create;
img.SetSize(16, 16);
img.Canvas.Brush.Color := clWhite;
img.Canvas.FillRect(0,0,16,16);
img.TransparentColor := clWhite;
img.Canvas.Pen.Color := clBlack;
img.Canvas.Pen.Width := 1;
end;
begin
FPopUpImageList.DrawingStyle := dsTransparent;
NewImg;
DrawNodeSymbol(img.Canvas, Rect(3,3,14,14), cfFoldStart, 0); // [-]
FPopUpImageList.Add(img, nil);
img.Free;
NewImg;
DrawNodeSymbol(img.Canvas, Rect(3,3,14,14), cfCollapsedFold, 0); // [+]
FPopUpImageList.Add(img, nil);
img.Free;
NewImg;
DrawNodeSymbol(img.Canvas, Rect(3,3,14,14), cfHideStart, 0); // [.]
FPopUpImageList.Add(img, nil);
img.Free;
NewImg;
DrawNodeSymbol(img.Canvas, Rect(3,3,14,14), cfCollapsedHide, 0); // [v]
FPopUpImageList.Add(img, nil);
img.Free;
end;
procedure TSynGutterCodeFolding.DoChange(Sender: TObject);
begin
if AutoSize then
@ -161,6 +207,7 @@ end;
constructor TSynGutterCodeFolding.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FReversePopMenuOrder := true;
FFoldView := Gutter.FoldView;
FMouseActions := TSynEditMouseActionsGutterFold.Create(self);
FMouseActionsExpanded := TSynEditMouseActionsGutterFoldExpanded.Create(self);
@ -174,7 +221,11 @@ begin
MarkupInfo.FrameColor := clNone;
FWidth := 10;
FPopUpImageList := TImageList.Create(nil);
InitPopUpImageList;
FPopUp := TPopupMenu.Create(nil);
FPopUp.Images := FPopUpImageList;
end;
destructor TSynGutterCodeFolding.Destroy;
@ -183,6 +234,7 @@ begin
FreeAndNil(FMouseActionsCollapsed);
FreeAndNil(FMouseActionsExpanded);
FreeAndNil(FPopUp);
FreeAndNil(FPopUpImageList);
inherited Destroy;
end;
@ -199,10 +251,13 @@ 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);
if inf.LineNum < 0 then exit;
case (Sender as TMenuItem).ImageIndex of
0: FFoldView.FoldAtTextIndex(inf.LineNum-1, inf.ColIndex, 1, False);
1: FFoldView.UnFoldAtTextIndex(inf.LineNum-1, inf.ColIndex, 1, False);
2: FFoldView.FoldAtTextIndex(inf.LineNum-1, inf.ColIndex, 1, False, 0);
3: FFoldView.UnFoldAtTextIndex(inf.LineNum-1, inf.ColIndex, 1, False, 0);
end;
end;
procedure TSynGutterCodeFolding.DoOnGutterClick(X, Y : integer);
@ -230,6 +285,16 @@ end;
function TSynGutterCodeFolding.DoHandleMouseAction(AnAction: TSynEditMouseAction;
var AnInfo: TSynEditMouseActionInfo): Boolean;
function AddPopUpItem: TMenuItem;
begin
Result := TMenuItem.Create(FPopUp);
Result.OnClick := {$IFDEF FPC}@{$ENDIF}PopClicked;
if FReversePopMenuOrder then
FPopUp.Items.Add(Result)
else
FPopUp.Items.Insert(0, Result);
end;
var
c, i, line, ScrLine: Integer;
inf: TFoldViewNodeInfo;
@ -314,10 +379,9 @@ begin
FMenuInf[i] := inf;
if (i < c-1) and (FMenuInf[i+1].LineNum = line) and (inf.LineNum <> line)
then begin
m := TMenuItem.Create(FPopUp);
m := AddPopUpItem;
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);
@ -325,13 +389,34 @@ begin
s2 := '';
if inf.OpenCount > 1 then
s2 := format(' (%d/%d)', [inf.ColIndex+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);
if inf.FNode.IsInFold then begin
m := AddPopUpItem;
m.Caption := format('%4d %-12s '#9'%s', [ inf.LineNum, inf.Keyword+s2+':', s]);
m.Tag := i;
if inf.FNode.IsHide then
m.ImageIndex := 3
else
m.ImageIndex := 1;
end
else begin
if sfaFoldFold in inf.HNode.FoldAction then begin
m := AddPopUpItem;
m.Caption := format('%4d %-12s '#9'%s', [ inf.LineNum, inf.Keyword+s2+':', s]);
m.Tag := i;
m.ImageIndex := 0;
end;
if sfaFoldHide in inf.HNode.FoldAction then begin
m := AddPopUpItem;
if sfaFoldFold in inf.HNode.FoldAction then
m.Caption := format('%4d %-12s ', [ inf.LineNum, inf.Keyword+s2 ])
else
m.Caption := format('%4d %-12s '#9'%s', [ inf.LineNum, inf.Keyword+s2+':', s]);
m.Tag := i;
m.ImageIndex := 2;
end;
end;
end;
FPopUp.PopUp;
end;
@ -341,6 +426,68 @@ begin
end;
end;
procedure TSynGutterCodeFolding.DrawNodeSymbol(Canvas: TCanvas; Rect: TRect;
NodeType: TSynEditFoldLineCapability; SubType: Integer);
var
Points: Array [0..3] of TPoint;
X, Y: Integer;
begin
Canvas.Rectangle(Rect);
X := (Rect.Left - 1 + Rect.Right) div 2;
Y := (Rect.Top - 1 + Rect.Bottom) div 2;
case NodeType of
cfFoldStart:
begin
// [-]
Canvas.MoveTo(X - 2, Y);
Canvas.LineTo(X + 3, Y);
end;
cfHideStart:
begin
// [.]
Canvas.MoveTo(X, Y);
Canvas.LineTo(X + 1, Y);
end;
cfCollapsedFold:
begin
// [+]
Canvas.MoveTo(X - 2, Y);
Canvas.LineTo(X + 3, Y);
Canvas.MoveTo(X, Y - 2);
Canvas.LineTo(X, Y + 3);
end;
cfCollapsedHide:
begin
case SubType of
0: begin
// [v]
Points[0].X := X;
Points[0].y := Y + 2;
Points[1].X := X - 2;
Points[1].y := Y;
Points[2].X := X + 2;
Points[2].y := Y;
Points[3].X := X;
Points[3].y := Y + 2;
end;
1: begin
// [v]
Points[0].X := X;
Points[0].y := Y - 2;
Points[1].X := X - 2;
Points[1].y := Y;
Points[2].X := X + 2;
Points[2].y := Y;
Points[3].X := X;
Points[3].y := Y - 2;
end;
end;
Canvas.Polygon(Points);
end;
end;
end;
procedure TSynGutterCodeFolding.Paint(Canvas : TCanvas; AClip : TRect; FirstLine, LastLine : integer);
const cNodeOffset = 1;
var
@ -355,7 +502,7 @@ var
rcNode: TRect;
ptCenter : TPoint;
isPrevLine: Boolean;
Points: Array [0..3] of TPoint;
i: Integer;
begin
isPrevLine := IsFoldHidePreviousLine(iLine);
LineOffset := 0;
@ -399,56 +546,10 @@ var
then
Canvas.Pen.Color := MarkupInfo.FrameColor;
Canvas.Rectangle(rcNode);
i:= 0;
if isPrevLine and (NodeType = cfCollapsedHide) then i := 1;
DrawNodeSymbol(Canvas, rcNode, NodeType, i);
//draw folded sign
case NodeType of
cfFoldStart:
begin
// [-]
Canvas.MoveTo(ptCenter.X - 2, ptCenter.Y);
Canvas.LineTo(ptCenter.X + 3, ptCenter.Y);
end;
cfHideStart:
begin
// [.]
Canvas.MoveTo(ptCenter.X, ptCenter.Y);
Canvas.LineTo(ptCenter.X + 1, ptCenter.Y);
end;
cfCollapsedFold:
begin
// [+]
Canvas.MoveTo(ptCenter.X - 2, ptCenter.Y);
Canvas.LineTo(ptCenter.X + 3, ptCenter.Y);
Canvas.MoveTo(ptCenter.X, ptCenter.Y - 2);
Canvas.LineTo(ptCenter.X, ptCenter.Y + 3);
end;
cfCollapsedHide:
begin
if isPrevLine then begin
// [v]
Points[0].X := ptCenter.X;
Points[0].y := ptCenter.Y - 2;
Points[1].X := ptCenter.X - 2;
Points[1].y := ptCenter.Y;
Points[2].X := ptCenter.X + 2;
Points[2].y := ptCenter.Y;
Points[3].X := ptCenter.X;
Points[3].y := ptCenter.Y - 2;
end else begin
// [v]
Points[0].X := ptCenter.X;
Points[0].y := ptCenter.Y + 2;
Points[1].X := ptCenter.X - 2;
Points[1].y := ptCenter.Y;
Points[2].X := ptCenter.X + 2;
Points[2].y := ptCenter.Y;
Points[3].X := ptCenter.X;
Points[3].y := ptCenter.Y + 2;
end;
Canvas.Polygon(Points);
end;
end;
Canvas.Pen.Color := MarkupInfo.Foreground;
Canvas.Brush.Style := bsSolid;
end;