1 {
2 Copyright (C) Alexey Torgashin, uvviewsoft.com
3 License: MPL 2.0 or LGPL
4 }
5 unit ATSynEdit_Gaps;
6 
7 {$mode objfpc}{$H+}
8 
9 interface
10 
11 uses
12   SysUtils, Classes, Graphics, Controls, Forms,
13   Math,
14   ATStringProc;
15 
16 type
17   { TATGapItem }
18 
19   TATGapItem = class
20   public
21     LineIndex: integer;
22     Size: integer;
23     Color: TColor;
24     Bitmap: TBitmap;
25     Form: TCustomForm;
26     FormVisible: boolean;
27     Tag: Int64;
28     DeleteOnDelLine: boolean;
29     constructor Create; virtual;
30     destructor Destroy; override;
GetObjectWidthnull31     function GetObjectWidth: integer;
32   end;
33 
34 type
35   TATGapDeleteEvent = procedure(Sender: TObject; ALineIndex: integer) of object;
36 
37 type
38   { TATGaps }
39 
40   TATGaps = class
41   private
42     FList: TFPList;
43     FOnDelete: TATGapDeleteEvent;
44     FSizeOfGapTop: integer;
45     FSizeOfGap0: integer;
GetItemnull46     function GetItem(N: integer): TATGapItem;
47   public
48     constructor Create; virtual;
49     destructor Destroy; override;
50     procedure Clear;
Countnull51     function Count: integer; inline;
IsIndexValidnull52     function IsIndexValid(N: integer): boolean; inline;
53     property Items[N: integer]: TATGapItem read GetItem; default;
54     procedure Delete(N: integer);
Addnull55     function Add(ALineIndex, ASize: integer; ABitmap: TBitmap; AForm: TCustomForm;
56       const ATag: Int64;
57       AColor: TColor=clNone; ADeleteOnDelLine: boolean=true): boolean;
Findnull58     function Find(ALineIndex: integer; ATag: Int64=-1): TATGapItem;
DeleteForLineRangenull59     function DeleteForLineRange(ALineFrom, ALineTo: integer): boolean;
DeleteWithTagnull60     function DeleteWithTag(const ATag: Int64): boolean;
SizeForLineRangenull61     function SizeForLineRange(ALineFrom, ALineTo: integer): integer;
SizeForAllnull62     function SizeForAll: integer;
63     property SizeOfGapTop: integer read FSizeOfGapTop;
64     property SizeOfGap0: integer read FSizeOfGap0;
65     procedure Update(AChange: TATLineChangeKind; ALine, AItemCount: integer);
66     property OnDelete: TATGapDeleteEvent read FOnDelete write FOnDelete;
67   end;
68 
GetGapBitmapPosLeftnull69 function GetGapBitmapPosLeft(const ARect: TRect; AObjectWidth: integer): integer;
70 
71 
72 implementation
73 
GetGapBitmapPosLeftnull74 function GetGapBitmapPosLeft(const ARect: TRect; AObjectWidth: integer): integer;
75 begin
76   Result:= Max(ARect.Left, (ARect.Left+ARect.Right-AObjectWidth) div 2);
77 end;
78 
79 
80 { TATGapItem }
81 
82 constructor TATGapItem.Create;
83 begin
84   Bitmap:= nil;
85   DeleteOnDelLine:= true;
86   Color:= clNone;
87 end;
88 
89 destructor TATGapItem.Destroy;
90 begin
91   if Bitmap<>nil then
92     FreeAndNil(Bitmap);
93   inherited;
94 end;
95 
GetObjectWidthnull96 function TATGapItem.GetObjectWidth: integer;
97 begin
98   if Assigned(Bitmap) then
99     Result:= Bitmap.Width
100   else
101   if Assigned(Form) then
102     Result:= Form.Width
103   else
104     Result:= 0;
105 end;
106 
107 { TATGaps }
108 
109 constructor TATGaps.Create;
110 begin
111   inherited;
112   FList:= TFPList.Create;
113   FSizeOfGapTop:= 0;
114   FSizeOfGap0:= 0;
115 end;
116 
117 destructor TATGaps.Destroy;
118 begin
119   Clear;
120   FreeAndNil(FList);
121   inherited;
122 end;
123 
124 procedure TATGaps.Clear;
125 var
126   i: integer;
127 begin
128   for i:= FList.Count-1 downto 0 do
129   begin
130     if Assigned(FOnDelete) then
131       FOnDelete(Self, Items[i].LineIndex);
132     TObject(FList[i]).Free;
133   end;
134   FList.Clear;
135   FSizeOfGapTop:= 0;
136   FSizeOfGap0:= 0;
137 end;
138 
TATGaps.Countnull139 function TATGaps.Count: integer; inline;
140 begin
141   Result:= FList.Count;
142 end;
143 
TATGaps.IsIndexValidnull144 function TATGaps.IsIndexValid(N: integer): boolean; inline;
145 begin
146   Result:= (N>=0) and (N<FList.Count);
147 end;
148 
GetItemnull149 function TATGaps.GetItem(N: integer): TATGapItem;
150 begin
151   Result:= TATGapItem(FList[N]);
152 end;
153 
154 procedure TATGaps.Delete(N: integer);
155 var
156   Item: TATGapItem;
157 begin
158   Item:= Items[N];
159   if Assigned(FOnDelete) then
160     FOnDelete(Self, Item.LineIndex);
161 
162   if Item.LineIndex=-1 then
163     FSizeOfGapTop:= 0
164   else
165   if Item.LineIndex=0 then
166     FSizeOfGap0:= 0;
167 
168   Item.Free;
169   FList.Delete(N);
170 end;
171 
TATGaps.DeleteForLineRangenull172 function TATGaps.DeleteForLineRange(ALineFrom, ALineTo: integer): boolean;
173 var
174   Item: TATGapItem;
175   i: integer;
176 begin
177   Result:= false;
178   for i:= FList.Count-1 downto 0 do
179   begin
180     Item:= Items[i];
181     if (Item.LineIndex>=ALineFrom) and (Item.LineIndex<=ALineTo) then
182     begin
183       Delete(i);
184       Result:= true;
185     end;
186   end;
187 end;
188 
TATGaps.DeleteWithTagnull189 function TATGaps.DeleteWithTag(const ATag: Int64): boolean;
190 var
191   Item: TATGapItem;
192   i: integer;
193 begin
194   Result:= false;
195   for i:= FList.Count-1 downto 0 do
196   begin
197     Item:= Items[i];
198     if Item.Tag=ATag then
199     begin
200       Delete(i);
201       Result:= true;
202     end;
203   end;
204 end;
205 
Addnull206 function TATGaps.Add(ALineIndex, ASize: integer; ABitmap: TBitmap;
207   AForm: TCustomForm; const ATag: Int64; AColor: TColor;
208   ADeleteOnDelLine: boolean): boolean;
209 var
210   Item: TATGapItem;
211 begin
212   Result:= false;
213   if (ALineIndex<-1) then exit;
214   if Find(ALineIndex)<>nil then exit;
215 
216   Item:= TATGapItem.Create;
217   Item.LineIndex:= ALineIndex;
218   Item.Size:= ASize;
219   Item.Bitmap:= ABitmap;
220   Item.Form:= AForm;
221   Item.Tag:= ATag;
222   Item.Color:= AColor;
223   Item.DeleteOnDelLine:= ADeleteOnDelLine;
224 
225   if ALineIndex=-1 then
226     FSizeOfGapTop:= ASize
227   else
228   if ALineIndex=0 then
229     FSizeOfGap0:= ASize;
230 
231   FList.Add(Item);
232   Result:= true;
233 end;
234 
TATGaps.Findnull235 function TATGaps.Find(ALineIndex: integer; ATag: Int64=-1): TATGapItem;
236 var
237   Item: TATGapItem;
238   i: integer;
239 begin
240   Result:= nil;
241   for i:= 0 to FList.Count-1 do
242   begin
243     Item:= Items[i];
244     if (Item.LineIndex=ALineIndex) and ((ATag<0) or (Item.Tag=ATag)) then exit(Item);
245   end;
246 end;
247 
SizeForLineRangenull248 function TATGaps.SizeForLineRange(ALineFrom, ALineTo: integer): integer;
249 var
250   Item: TATGapItem;
251   i: integer;
252 begin
253   Result:= 0;
254   for i:= 0 to FList.Count-1 do
255   begin
256     Item:= Items[i];
257     if (Item.LineIndex>=ALineFrom) and (Item.LineIndex<=ALineTo) then
258       Inc(Result, Item.Size);
259   end;
260 end;
261 
TATGaps.SizeForAllnull262 function TATGaps.SizeForAll: integer;
263 var
264   Item: TATGapItem;
265   i: integer;
266 begin
267   Result:= 0;
268   for i:= 0 to FList.Count-1 do
269   begin
270     Item:= Items[i];
271     Inc(Result, Item.Size);
272   end;
273 end;
274 
275 procedure TATGaps.Update(AChange: TATLineChangeKind; ALine, AItemCount: integer);
276 var
277   Item: TATGapItem;
278   i: integer;
279 begin
280   case AChange of
281     cLineChangeEdited:
282       begin
283       end;
284     cLineChangeAdded:
285       begin
286         for i:= 0 to Count-1 do
287         begin
288           Item:= Items[i];
289           if Item.LineIndex>=ALine then
290             Item.LineIndex:= Item.LineIndex+AItemCount;
291         end;
292       end;
293     cLineChangeDeletedAll:
294       begin
295         Clear;
296       end;
297     cLineChangeDeleted:
298       begin
299         for i:= Count-1 downto 0 do
300         begin
301           Item:= Items[i];
302           if (Item.LineIndex>=ALine) and (Item.LineIndex<ALine+AItemCount) then
303           begin
304             if Item.DeleteOnDelLine then
305               Delete(i);
306           end
307           else
308           if Item.LineIndex>ALine then
309             Item.LineIndex:= Item.LineIndex-AItemCount;
310         end;
311       end;
312   end;
313 end;
314 
315 
316 end.
317 
318