diff --git a/components/synedit/synguttermarks.pp b/components/synedit/synguttermarks.pp index f8f5b6911b..8f272cd207 100644 --- a/components/synedit/synguttermarks.pp +++ b/components/synedit/synguttermarks.pp @@ -30,13 +30,23 @@ type FDebugMarksImageIndex: Integer; FInternalImage: TSynInternalImage; FNoInternalImage: Boolean; + protected type + TSynEditMarkDrawInfo = record + Mark: TSynEditMark; + Images: TCustomImageList; + IconIdx: integer; + end; + TSynEditMarkDrawInfoArray = array of TSynEditMarkDrawInfo; protected FBookMarkOpt: TSynBookMarkOpt; + FTempDrawInfo: TSynEditMarkDrawInfoArray; procedure Init; override; function PreferedWidth: Integer; override; function LeftMarginAtCurrentPPI: Integer; - function GetImgListRes(const ACanvas: TCanvas; - const AImages: TCustomImageList): TScaledImageListResolution; virtual; + function GetImgListRes(const ACanvas: TCanvas; const AImages: TCustomImageList): TScaledImageListResolution; virtual; + function MarksToDrawInfo(AMLine: TSynEditMarkLine; var ADrawInfo: TSynEditMarkDrawInfoArray; + AMaxEntries: integer; + out aFirstCustomColumnIdx: integer; out AHasNonBookmark: boolean): integer; virtual; // PaintMarks: True, if it has any Mark, that is *not* a bookmark function PaintMarks(aScreenLine: Integer; Canvas : TCanvas; AClip : TRect; var aFirstCustomColumnIdx: integer): Boolean; @@ -99,8 +109,8 @@ begin inherited Destroy; end; -function TSynGutterMarks.GetImgListRes(const ACanvas: TCanvas; - const AImages: TCustomImageList): TScaledImageListResolution; +function TSynGutterMarks.GetImgListRes(const ACanvas: TCanvas; const AImages: TCustomImageList + ): TScaledImageListResolution; var Scale: Double; PPI: Integer; @@ -117,18 +127,123 @@ begin Result := AImages.ResolutionForPPI[0, PPI, Scale]; end; +function TSynGutterMarks.MarksToDrawInfo(AMLine: TSynEditMarkLine; + var ADrawInfo: TSynEditMarkDrawInfoArray; AMaxEntries: integer; out + aFirstCustomColumnIdx: integer; out AHasNonBookmark: boolean): integer; +var + i, CntUniq, CntRep2, CntKeepRep2, CntDel3: Integer; + prev_iidx, pprev_iidx: LongInt; +begin + Result := AMLine.VisibleCount; + if (FOptions * [sgmoDeDuplicateMarks, sgmoDeDuplicateMarksKeepTwo] <> []) or + ((sgmoDeDuplicateMarksOnOverflow in FOptions) and (Result > ColumnCount)) + then begin + CntUniq := 0; + CntRep2 := 0; + prev_iidx := low(integer); + pprev_iidx := low(integer); + for i := 0 to AMLine.Count - 1 do begin + if (not AMLine[i].Visible) or + (AMLine[i].IsBookmark and (not FBookMarkOpt.GlyphsVisible)) + then + continue; + if AMLine[i].ImageIndex <> prev_iidx then + inc(CntUniq) // Uniq + else + if (i=1) or (AMLine[i].ImageIndex <> pprev_iidx) then + inc(CntRep2); // sgmoDeDuplicateMarksKeepTwo + pprev_iidx := prev_iidx; + prev_iidx := AMLine[i].ImageIndex; + end; + + if (sgmoDeDuplicateMarks in FOptions) then begin + Result := Min(cntUniq, AMaxEntries); + CntKeepRep2 := 0; + end + else + if (sgmoDeDuplicateMarksKeepTwo in FOptions) then begin + CntKeepRep2 := Min(CntRep2, Max(0, AMaxEntries- cntUniq)); + Result := Min(cntUniq+CntKeepRep2, AMaxEntries); + end + else begin + // only dedup for MaxExtraMarksColums + CntKeepRep2 := Min(CntRep2, Max(0, AMaxEntries - cntUniq)); + Result := Min(cntUniq+CntKeepRep2, AMaxEntries); + end; + CntDel3 := AMLine.VisibleCount - Result; + end + else begin + Result := Min(AMLine.VisibleCount, AMaxEntries); + CntKeepRep2 := MaxInt; // keep duplicate if exactly 2nd + CntDel3 := 0; // del 3rd or later + end; + + if Length(ADrawInfo) < Result then + SetLength(ADrawInfo, AMaxEntries); // Expand to max needed (for further runs) + + aFirstCustomColumnIdx := 0; + AHasNonBookmark := False; + prev_iidx := low(integer); + pprev_iidx := low(integer); + for i := 0 to AMLine.Count - 1 do begin + if (not AMLine[i].Visible) or + (AMLine[i].IsBookmark and (not FBookMarkOpt.GlyphsVisible)) + then + continue; + + if (i = 0) and FBookMarkOpt.DrawBookmarksFirst and + (Result < ColumnCount) and (not AMLine[i].IsBookmark) + then begin + ADrawInfo[aFirstCustomColumnIdx].Mark := nil; + ADrawInfo[aFirstCustomColumnIdx].IconIdx := 0; + ADrawInfo[aFirstCustomColumnIdx].Images := nil; + inc(Result); + inc(aFirstCustomColumnIdx); + end; + + if AMLine[i].ImageIndex = prev_iidx then begin + if (AMLine[i].ImageIndex = pprev_iidx) and (CntDel3 > 0) then begin + dec(CntDel3); + continue; + end; + if CntKeepRep2 = 0 then + Continue; + dec(CntKeepRep2); + end; + + AHasNonBookmark := AHasNonBookmark or (not AMLine[i].IsBookmark); // Line has a none-bookmark glyph + + ADrawInfo[aFirstCustomColumnIdx].Mark := AMLine[i]; + ADrawInfo[aFirstCustomColumnIdx].IconIdx := 0; //AMLine[i].ImageIndex; + ADrawInfo[aFirstCustomColumnIdx].Images := nil; //AMLine[i].ImageList; + inc(aFirstCustomColumnIdx); + if aFirstCustomColumnIdx >= AMaxEntries then + break; + + pprev_iidx := prev_iidx; + prev_iidx := AMLine[i].ImageIndex; + end; +end; + function TSynGutterMarks.PaintMarks(aScreenLine: Integer; Canvas : TCanvas; AClip : TRect; var aFirstCustomColumnIdx: integer): Boolean; var LineHeight: Integer; + BkMkOptImg: TScaledImageListResolution; + BkMkOptImgDone: Boolean; - procedure DoPaintMark(CurMark: TSynEditMark; aRect: TRect); + procedure DoPaintMark(const CurMarkInfo: TSynEditMarkDrawInfo; aRect: TRect); var img: TScaledImageListResolution; + CurMark: TSynEditMark; + idx: Integer; begin - if CurMark.InternalImage or - ( (not assigned(FBookMarkOpt.BookmarkImages)) and - (not assigned(CurMark.ImageList)) ) + CurMark := CurMarkInfo.Mark; + if (CurMark <> nil) and + ( CurMark.InternalImage or + ( (not assigned(FBookMarkOpt.BookmarkImages)) and + (not assigned(CurMark.ImageList)) ) + ) then begin // draw internal image if CurMark.ImageIndex in [0..9] then @@ -146,26 +261,40 @@ var end else begin // draw from ImageList - if assigned(CurMark.ImageList) then - img := GetImgListRes(Canvas, CurMark.ImageList) + if CurMark = nil then begin + if CurMarkInfo.Images = nil then + exit; + img := GetImgListRes(Canvas, CurMarkInfo.Images); + idx := CurMarkInfo.IconIdx; + end else - img := GetImgListRes(Canvas, FBookMarkOpt.BookmarkImages); + if assigned(CurMark.ImageList) then begin + img := GetImgListRes(Canvas, CurMark.ImageList); + idx := CurMark.ImageIndex; + end + else begin + if not BkMkOptImgDone then begin + BkMkOptImg := GetImgListRes(Canvas, FBookMarkOpt.BookmarkImages); + BkMkOptImgDone := True; + end; + img := BkMkOptImg; + idx := CurMark.ImageIndex; + end; - if (CurMark.ImageIndex <= img.Count) and (CurMark.ImageIndex >= 0) then begin + if (idx <= img.Count) and (idx >= 0) then begin if LineHeight > img.Height then aRect.Top := (aRect.Top + aRect.Bottom - img.Height) div 2; - img.Draw(Canvas, aRect.Left, aRect.Top, CurMark.ImageIndex, True); + img.Draw(Canvas, aRect.Left, aRect.Top, idx, True); end; end end; var - j, lm, StoredColumnWidth, lx, vcnt, VCntU, VCnt2, k2cnt, del3cnt: Integer; + j, lm, StoredColumnWidth, lx, vcnt: Integer; MLine: TSynEditMarkLine; MarkRect: TRect; iRange: TLineRange; - prev_iidx, pprev_iidx: LongInt; begin Result := False; aFirstCustomColumnIdx := 0; @@ -186,59 +315,14 @@ begin else MLine.Sort(smsoBookMarkLast, smsoPriority); - vcnt := MLine.VisibleCount; + vcnt := MarksToDrawInfo(MLine, FTempDrawInfo, ColumnCount + MaxExtraMarksColums, aFirstCustomColumnIdx, Result); - if (FOptions * [sgmoDeDuplicateMarks, sgmoDeDuplicateMarksKeepTwo] <> []) or - ((sgmoDeDuplicateMarksOnOverflow in FOptions) and (vcnt > ColumnCount)) - then begin - VCntU := 0; - VCnt2 := 0; - prev_iidx := low(integer); - pprev_iidx := low(integer); - for j := 0 to MLine.Count - 1 do begin - if (not MLine[j].Visible) or - (MLine[j].IsBookmark and (not FBookMarkOpt.GlyphsVisible)) - then - continue; - if MLine[j].ImageIndex <> prev_iidx then - inc(VCntU) // Uniq - else - if (j=1) or (MLine[j].ImageIndex <> pprev_iidx) then - inc(VCnt2); // sgmoDeDuplicateMarksKeepTwo - pprev_iidx := prev_iidx; - prev_iidx := MLine[j].ImageIndex; - end; - - if (sgmoDeDuplicateMarks in FOptions) then begin - vcnt := Min(vcntU, ColumnCount + MaxExtraMarksColums); - k2cnt := 0; - end - else - if (sgmoDeDuplicateMarksKeepTwo in FOptions) then begin - k2cnt := Min(VCnt2, Max(0, ColumnCount + MaxExtraMarksColums - vcntU)); - vcnt := Min(vcntU+k2cnt, ColumnCount + MaxExtraMarksColums); - end - else begin - // only dedup for MaxExtraMarksColums - k2cnt := Min(VCnt2, Max(0, ColumnCount + MaxExtraMarksColums - vcntU)); - vcnt := Min(vcntU+k2cnt, ColumnCount + MaxExtraMarksColums); - end; - del3cnt := MLine.VisibleCount - vcnt; - end - else begin - vcnt := Min(vcnt, ColumnCount + MaxExtraMarksColums); - k2cnt := MaxInt; // keep duplicate if exactly 2nd - del3cnt := 0; // del 3rd or later - end; - - aFirstCustomColumnIdx := 0; - LineHeight := SynEdit.LineHeight; //Gutter.Paint always supplies AClip.Left = GutterPart.Left - lm := LeftMarginAtCurrentPPI; + BkMkOptImgDone := False; + LineHeight := SynEdit.LineHeight; StoredColumnWidth := FColumnWidth; - prev_iidx := low(integer); - pprev_iidx := low(integer); try + lm := LeftMarginAtCurrentPPI; lx := 0; if vcnt > ColumnCount then begin lx := FColumnWidth; @@ -250,44 +334,11 @@ begin AClip.Left + lm - lx + FColumnWidth, AClip.Top + LineHeight); + for j := 0 to vcnt - 1 do begin + DoPaintMark(FTempDrawInfo[j], MarkRect); - for j := 0 to MLine.Count - 1 do begin - if (not MLine[j].Visible) or - (MLine[j].IsBookmark and (not FBookMarkOpt.GlyphsVisible)) - then - continue; - - if (j = 0) and FBookMarkOpt.DrawBookmarksFirst and - (vcnt < ColumnCount) and (not MLine[j].IsBookmark) - then begin - // leave one column empty - MarkRect.Left := MarkRect.Right; - MarkRect.Right := Min(MarkRect.Right + FColumnWidth, AClip.Right); - inc(aFirstCustomColumnIdx); - end; - - if MLine[j].ImageIndex = prev_iidx then begin - if (MLine[j].ImageIndex = pprev_iidx) and (del3cnt > 0) then begin - dec(del3cnt); - continue; - end; - if k2cnt = 0 then - Continue; - dec(k2cnt); - end; - - DoPaintMark(MLine[j], MarkRect); MarkRect.Left := MarkRect.Right; MarkRect.Right := Min(MarkRect.Right + FColumnWidth, AClip.Right); - - Result := Result or (not MLine[j].IsBookmark); // Line has a none-bookmark glyph - inc(aFirstCustomColumnIdx); - - if aFirstCustomColumnIdx > ColumnCount + MaxExtraMarksColums then - break; - - pprev_iidx := prev_iidx; - prev_iidx := MLine[j].ImageIndex; end; finally FColumnWidth := StoredColumnWidth; diff --git a/ide/sourceeditor.pp b/ide/sourceeditor.pp index 2913a02ce3..4fccb3239c 100644 --- a/ide/sourceeditor.pp +++ b/ide/sourceeditor.pp @@ -3429,7 +3429,7 @@ end; procedure TSourceEditorSharedValues.CreateExecutionMark; begin - FExecutionMark := TSourceMark.Create(SharedEditors[0], nil); + FExecutionMark := TExecutionMark.Create(SharedEditors[0], nil); SourceEditorMarks.Add(FExecutionMark); FExecutionMark.LineColorAttrib := ahaExecutionPoint; FExecutionMark.Priority := 1; diff --git a/ide/sourcemarks.pas b/ide/sourcemarks.pas index a6c6db4e85..be5bd421d7 100644 --- a/ide/sourcemarks.pas +++ b/ide/sourcemarks.pas @@ -185,11 +185,12 @@ type public property IsBreakPoint: boolean read FIsBreakPoint write SetIsBreakPoint; end; - + TSourceMarkClass = class of TSourceMark; PSourceMark = ^TSourceMark; - - + + TExecutionMark = class(TSourceMark) end; + { TSourceMarks } TSourceMarks = class(TComponent) diff --git a/ide/sourcesyneditor.pas b/ide/sourcesyneditor.pas index 80754b0d13..d9b23342a9 100644 --- a/ide/sourcesyneditor.pas +++ b/ide/sourcesyneditor.pas @@ -446,6 +446,7 @@ type private FDebugMarkInfo: TIDESynDebugMarkInfo; FMarkInfoTextBuffer: TSynEditStrings; + FCurLineHasDebugMark: boolean; protected procedure CheckTextBuffer; // Todo: Add a notification, when TextBuffer Changes Procedure PaintLine(aScreenLine: Integer; Canvas : TCanvas; AClip : TRect); override; @@ -453,6 +454,9 @@ type function GetImgListRes(const ACanvas: TCanvas; const AImages: TCustomImageList): TScaledImageListResolution; override; + function MarksToDrawInfo(AMLine: TSynEditMarkLine; var ADrawInfo: TSynEditMarkDrawInfoArray; + AMaxEntries: integer; out aFirstCustomColumnIdx: integer; out AHasNonBookmark: boolean + ): integer; override; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; @@ -2311,8 +2315,7 @@ end; procedure TIDESynGutterMarks.PaintLine(aScreenLine: Integer; Canvas: TCanvas; AClip: TRect); var - aGutterOffs, TxtIdx: Integer; - HasAnyMark: Boolean; + aGutterOffs, TxtIdx, aScreenLine2: Integer; iRange: TLineRange; procedure DrawDebugMark(Line: Integer); @@ -2321,37 +2324,35 @@ var LineHeight: LongInt; img: TScaledImageListResolution; begin - if Line < 0 then Exit; - if Assigned(FBookMarkOpt.BookmarkImages) and - (DebugMarksImageIndex <= FBookMarkOpt.BookmarkImages.Count) and - (DebugMarksImageIndex >= 0) then - begin - LineHeight := TSynEdit(SynEdit).LineHeight; - img := GetImgListRes(Canvas, FBookMarkOpt.BookmarkImages); - iTop := 0; - if LineHeight > img.Height then - iTop := (LineHeight - img.Height) div 2; + LineHeight := TSynEdit(SynEdit).LineHeight; + img := GetImgListRes(Canvas, FBookMarkOpt.BookmarkImages); + iTop := 0; + if LineHeight > img.Height then + iTop := (LineHeight - img.Height) div 2; - img.Draw - (Canvas, AClip.Left + LeftMarginAtCurrentPPI + (ColumnCount-1) * ColumnWidth, - AClip.Top + iTop, DebugMarksImageIndex, True); - end + img.Draw + (Canvas, AClip.Left + LeftMarginAtCurrentPPI + (ColumnCount-1) * ColumnWidth, + AClip.Top + iTop, DebugMarksImageIndex, True); end; begin CheckTextBuffer; + + aScreenLine2 := aScreenLine + ToIdx(GutterArea.TextArea.TopLine); + TxtIdx:= ViewedTextBuffer.DisplayView.ViewToTextIndexEx(aScreenLine2, iRange); + FCurLineHasDebugMark := (aScreenLine2 = iRange.Top) and (aScreenLine2 >= 0) and + (TxtIdx >= 0) and (TxtIdx < TSynEdit(SynEdit).Lines.Count) and + (HasDebugMarks) and (TxtIdx < FDebugMarkInfo.Count) and + (FDebugMarkInfo.SrcLineToMarkLine[TxtIdx] > 0) and + Assigned(FBookMarkOpt.BookmarkImages) and + (DebugMarksImageIndex <= FBookMarkOpt.BookmarkImages.Count) and + (DebugMarksImageIndex >= 0); + aGutterOffs := 0; - HasAnyMark := PaintMarks(aScreenLine, Canvas, AClip, aGutterOffs); - aScreenLine := aScreenLine + ToIdx(GutterArea.TextArea.TopLine); - TxtIdx:= ViewedTextBuffer.DisplayView.ViewToTextIndexEx(aScreenLine, iRange); - if aScreenLine <> iRange.Top then - exit; - if (TxtIdx < 0) or (TxtIdx >= TSynEdit(SynEdit).Lines.Count) then - exit; - if (aGutterOffs < ColumnCount) and (HasDebugMarks) and (TxtIdx < FDebugMarkInfo.Count) and - (FDebugMarkInfo.SrcLineToMarkLine[TxtIdx] > 0) - then - DrawDebugMark(aScreenLine); + PaintMarks(aScreenLine, Canvas, AClip, aGutterOffs); + + if FCurLineHasDebugMark then + DrawDebugMark(aScreenLine2); end; function TIDESynGutterMarks.PreferedWidthAtCurrentPPI: Integer; @@ -2450,6 +2451,71 @@ begin Result := AImages.ResolutionForPPI[ImageHeight, PPI, Scale]; end; +function TIDESynGutterMarks.MarksToDrawInfo(AMLine: TSynEditMarkLine; + var ADrawInfo: TSynEditMarkDrawInfoArray; AMaxEntries: integer; out + aFirstCustomColumnIdx: integer; out AHasNonBookmark: boolean): integer; +var + i, j: Integer; +begin + Result := inherited MarksToDrawInfo(AMLine, ADrawInfo, AMaxEntries, aFirstCustomColumnIdx, + AHasNonBookmark); + + if (Result > 1) and (ADrawInfo[0].Mark = nil) and (ADrawInfo[1].Mark is TETMark) and + ( (ColumnCount <= 2) or + ((Result = ColumnCount) and (ADrawInfo[Result-1].Mark is TETMark)) ) + then begin + dec(Result); + for i := 0 to Result - 1 do + ADrawInfo[i] := ADrawInfo[i+1]; + end; + + if Result <= ColumnCount then begin + i := Result - 1; + while (i >= 0) and (ADrawInfo[i].Mark <> nil) and + ( (ADrawInfo[i].Mark is TExecutionMark) or + ((ADrawInfo[i].Mark is TSourceMark) and (TSourceMark(ADrawInfo[i].Mark).IsBreakPoint)) + ) + do + dec(i); + inc(i); + if i < Result then begin + if Result < ColumnCount then begin + i := Result - i; // columns to move + if Length(ADrawInfo) < ColumnCount then + SetLength(ADrawInfo, Max(ColumnCount, AMaxEntries)); + for j := 1 to i do + ADrawInfo[ColumnCount - j] := ADrawInfo[Result - j]; + for j := Result - i to ColumnCount - 1 - i do begin + ADrawInfo[j].Mark := nil; + ADrawInfo[j].Images := nil; + end; + Result := ColumnCount; + end; + end + else begin + // debug line mark ? + if FCurLineHasDebugMark then begin + if Length(ADrawInfo) < ColumnCount+1 then + SetLength(ADrawInfo, Max(ColumnCount+1, AMaxEntries)); + for i := Result to ColumnCount - 1 do begin + ADrawInfo[i].Mark := nil; + ADrawInfo[i].Images := nil; + end; + + if Result < ColumnCount then + Result := ColumnCount + else + Result := ColumnCount + 1; + ADrawInfo[Result-1].Mark := nil; + ADrawInfo[Result-1].Images := FBookMarkOpt.BookmarkImages; + ADrawInfo[Result-1].IconIdx := DebugMarksImageIndex; + end; + end; + end; + + FCurLineHasDebugMark := False; // done +end; + constructor TIDESynGutterMarks.Create(AOwner: TComponent); begin inherited Create(AOwner);