1 {
2  ***************************************************************************
3  *                                                                         *
4  *   This source is free software; you can redistribute it and/or modify   *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This code is distributed in the hope that it will be useful, but      *
10  *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
12  *   General Public License for more details.                              *
13  *                                                                         *
14  *   A copy of the GNU General Public License is available on the World    *
15  *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
16  *   obtain it by writing to the Free Software Foundation,                 *
17  *   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.   *
18  *                                                                         *
19  ***************************************************************************
20 
21   Author: Mattias Gaertner
22 
23   Abstract:
24     Dialog used by the fpdoc editor to create a link.
25 }
26 unit FPDocSelectLink;
27 
28 {$mode objfpc}{$H+}
29 
30 interface
31 
32 uses
33   Classes, SysUtils, Laz_AVL_Tree,
34   // LCL
35   LCLProc, LCLType, Forms, Controls, Graphics, ExtCtrls, StdCtrls, ButtonPanel,
36   // LazUtils
37   FileUtil, LazFileUtils, Laz2_DOM,
38   // IdeIntf
39   PackageIntf, ProjectIntf,
40   // IDE
41   CodeHelp, LazarusIDEStrConsts, PackageSystem, PackageDefs;
42 
43 type
44 
45   { TFPDocLinkCompletionItem }
46 
47   TFPDocLinkCompletionItem = class
48   public
49     Text: string;
50     Description: string;
51     constructor Create(const AText, ADescription: string);
52   end;
53 
54   { TFPDocLinkCompletionList }
55 
56   TFPDocLinkCompletionList = class
57   private
58     FBGColor: TColor;
59     FItemHeight: integer;
60     FItems: TFPList; // list of TFPDocLinkCompletionItem
61     FPrefix: string;
62     FSelected: integer;
63     FSelectedBGColor: TColor;
64     FSelectedTextColor: TColor;
65     FSorted: Boolean;
66     FTextColor: TColor;
67     FTop: integer;
68     FVisibleItems: integer;
69     FTree: TAvlTree; // tree of TFPDocLinkCompletionItem
GetCountnull70     function GetCount: integer;
GetItemsnull71     function GetItems(Index: integer): TFPDocLinkCompletionItem;
72     procedure SetSorted(const AValue: Boolean);
73     procedure SetTop(const AValue: integer);
74   public
75     constructor Create;
76     destructor Destroy; override;
77     procedure Clear;
78     procedure Sort;
79     procedure AddPackage(Pkg: TLazPackage);
80     procedure Add(Identifier, Description: string);
81     procedure AddIdentifier(Identifier: string);
82     procedure Draw(Canvas: TCanvas; Width, Height: integer);
83     property Count: integer read GetCount;
84     property Sorted: Boolean read FSorted write SetSorted;
85     property Items[Index: integer]: TFPDocLinkCompletionItem read GetItems;
86     property ItemHeight: integer read FItemHeight write FItemHeight;// pixel per item
87     property VisibleItems: integer read FVisibleItems write FVisibleItems;// visible lines
88     property Top: integer read FTop write SetTop;
89     property Selected: integer read FSelected write FSelected;
90     property Prefix: string read FPrefix write FPrefix;
91     property BGColor: TColor read FBGColor write FBGColor;
92     property TextColor: TColor read FTextColor write FTextColor;
93     property SelectedBGColor: TColor read FSelectedBGColor write FSelectedBGColor;
94     property SelectedTextColor: TColor read FSelectedTextColor write FSelectedTextColor;
95   end;
96 
97   { TFPDocLinkEditorDlg }
98 
99   TFPDocLinkEditorDlg = class(TForm)
100     ButtonPanel1: TButtonPanel;
101     CompletionBox: TPaintBox;
102     TitleEdit: TEdit;
103     TitleLabel: TLabel;
104     LinkEdit: TEdit;
105     LinkLabel: TLabel;
106     procedure CompletionBoxMouseDown(Sender: TObject; {%H-}Button: TMouseButton;
107       {%H-}Shift: TShiftState; {%H-}X, Y: Integer);
108     procedure CompletionBoxPaint(Sender: TObject);
109     procedure FormCreate(Sender: TObject);
110     procedure FormDestroy(Sender: TObject);
111     procedure LinkEditChange(Sender: TObject);
112     procedure LinkEditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
113   private
114     FStartFPDocFile: TLazFPDocFile;
115     fItems: TFPDocLinkCompletionList;
116     FSourceFilename: string;
117     FStartModuleOwner: TObject;
118     fUpdatingItems: boolean;
GetLinknull119     function GetLink: string;
GetLinkTitlenull120     function GetLinkTitle: string;
121     procedure SetStartFPDocFile(const AValue: TLazFPDocFile);
122     procedure SetLink(const AValue: string);
123     procedure SetLinkTitle(const AValue: string);
124     procedure SetSourceFilename(const AValue: string);
125     procedure SetStartModuleOwner(const AValue: TObject);
126     procedure UpdateCompletionBox;
127     procedure AddPackagesToCompletion(Prefix: string);
128     procedure AddModuleUnits(ModuleOwner: TObject; Prefix: string);
129     procedure AddProjectUnits(AProject: TLazProject; Prefix: string);
130     procedure AddPackageUnits(APackage: TLazPackage; Prefix: string);
131     procedure AddIdentifiers(ModuleOwner: TObject; FPDocFile: TLazFPDocFile;
132                              Prefix: string);
133     procedure AddSubIdentifiers(Path: string);
134   public
135     procedure SetLinkAndContext(const ASrcFilename, ATitle, ALink: string;
136                       ADocFile: TLazFPDocFile);
137     property SourceFilename: string read FSourceFilename write SetSourceFilename;
138     property LinkTitle: string read GetLinkTitle write SetLinkTitle;
139     property Link: string read GetLink write SetLink;
140     property StartFPDocFile: TLazFPDocFile read FStartFPDocFile write SetStartFPDocFile;
141     property StartModuleOwner: TObject read FStartModuleOwner write SetStartModuleOwner;
142   end;
143 
ShowFPDocLinkEditorDialognull144 function ShowFPDocLinkEditorDialog(SrcFilename: string;
145   StartFPDocFile: TLazFPDocFile; out Link, LinkTitle: string): TModalResult;
146 
CompareFPDocLinkCompletionItemnull147 function CompareFPDocLinkCompletionItem(Data1, Data2: Pointer): integer;
ComparePathWithFPDocLinkCompletionItemnull148 function ComparePathWithFPDocLinkCompletionItem(AnsiString1, Data2: Pointer): integer;
149 
150 implementation
151 
152 {$R *.lfm}
153 
ShowFPDocLinkEditorDialognull154 function ShowFPDocLinkEditorDialog(SrcFilename: string;
155   StartFPDocFile: TLazFPDocFile; out Link, LinkTitle: string): TModalResult;
156 var
157   FPDocLinkEditorDlg: TFPDocLinkEditorDlg;
158 begin
159   Link:='';
160   LinkTitle:='';
161   FPDocLinkEditorDlg:=TFPDocLinkEditorDlg.Create(nil);
162   try
163     FPDocLinkEditorDlg.SetLinkAndContext(SrcFilename,LinkTitle,Link,StartFPDocFile);
164     Result:=FPDocLinkEditorDlg.ShowModal;
165     if Result=mrOk then begin
166       Link:=FPDocLinkEditorDlg.Link;
167       LinkTitle:=FPDocLinkEditorDlg.LinkTitle;
168     end;
169   finally
170     FPDocLinkEditorDlg.Release;
171   end;
172 end;
173 
CompareFPDocLinkCompletionItemnull174 function CompareFPDocLinkCompletionItem(Data1, Data2: Pointer): integer;
175 var
176   Item1: TFPDocLinkCompletionItem absolute Data1;
177   Item2: TFPDocLinkCompletionItem absolute Data2;
178 begin
179   Result:=SysUtils.CompareText(Item1.Text,Item2.Text);
180 end;
181 
ComparePathWithFPDocLinkCompletionItemnull182 function ComparePathWithFPDocLinkCompletionItem(AnsiString1, Data2: Pointer
183   ): integer;
184 var
185   s: String;
186   Item: TFPDocLinkCompletionItem absolute Data2;
187 begin
188   s:=AnsiString(AnsiString1);
189   Result:=SysUtils.CompareText(s,Item.Text);
190 end;
191 
192 { TFPDocLinkEditorDlg }
193 
194 procedure TFPDocLinkEditorDlg.FormCreate(Sender: TObject);
195 begin
196   Caption:=lisChooseAFPDocLink;
197   LinkLabel.Caption:=lisLinkTarget;
198   LinkLabel.Hint:=Format(lisExamplesIdentifierTMyEnumEnumUnitnameIdentifierPac,
199                          [LineEnding, LineEnding, LineEnding, LineEnding]);
200   TitleLabel.Caption:=lisTitleLeaveEmptyForDefault;
201 
202   LinkEdit.Text:='';
203   TitleEdit.Text:='';
204 
205   // disable return key
206   ButtonPanel1.OKButton.Default:=false;
207 
208   FItems:=TFPDocLinkCompletionList.Create;
209 
210   ActiveControl:=LinkEdit;
211 end;
212 
213 procedure TFPDocLinkEditorDlg.FormDestroy(Sender: TObject);
214 begin
215   FreeAndNil(fItems);
216 end;
217 
218 procedure TFPDocLinkEditorDlg.CompletionBoxPaint(Sender: TObject);
219 begin
220   fItems.BGColor:=clWindow;
221   fItems.TextColor:=clWindowText;
222   fItems.SelectedBGColor:=clHighlight;
223   fItems.SelectedTextColor:=clHighlightText;
224   fItems.ItemHeight:=CompletionBox.Canvas.TextHeight('ABCTWSMgqp')+4;
225   fItems.VisibleItems:=CompletionBox.ClientHeight div fItems.ItemHeight;
226   fItems.Draw(CompletionBox.Canvas,
227               CompletionBox.ClientWidth,CompletionBox.ClientHeight);
228 end;
229 
230 procedure TFPDocLinkEditorDlg.CompletionBoxMouseDown(Sender: TObject;
231   Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
232 var
233   Line: Integer;
234 begin
235   if fItems.ItemHeight<=0 then exit;
236   Line:=fItems.Top+(Y div fItems.ItemHeight);
237   if Line>=fItems.Count then exit;
238   Link:=FItems.Items[Line].Text;
239   LinkEdit.SelStart:=length(LinkEdit.Text);
240 end;
241 
242 procedure TFPDocLinkEditorDlg.LinkEditChange(Sender: TObject);
243 begin
244   //DebugLn(['TFPDocLinkEditorDlg.LinkEditChange "',LinkEdit.Text,'"']);
245   Link:=LinkEdit.Text;
246 end;
247 
248 procedure TFPDocLinkEditorDlg.LinkEditKeyDown(Sender: TObject; var Key: Word;
249   Shift: TShiftState);
250 var
251   Handled: Boolean;
252 begin
253   if Shift=[] then begin
254     Handled:=true;
255     case Key of
256     VK_UP:
257       if FItems.Selected>0 then begin
258         FItems.Selected:=FItems.Selected-1;
259         if FItems.Top>fItems.Selected then
260           FItems.Top:=fItems.Selected;
261         CompletionBox.Invalidate;
262       end;
263     VK_DOWN:
264       if FItems.Selected<fItems.Count-1 then begin
265         FItems.Selected:=FItems.Selected+1;
266         if FItems.Selected>=fItems.Top+fItems.VisibleItems then
267           FItems.Top:=FItems.Top+1;
268         CompletionBox.Invalidate;
269       end;
270     VK_RETURN:
271       if (FItems.Selected>=0) and (FItems.Selected<fItems.Count) then begin
272         Link:=FItems.Items[fItems.Selected].Text;
273         LinkEdit.SelStart:=length(LinkEdit.Text);
274       end;
275     else
276       Handled:=false;
277     end;
278     if Handled then Key:=VK_UNKNOWN;
279   end;
280 end;
281 
282 procedure TFPDocLinkEditorDlg.SetSourceFilename(const AValue: string);
283 var
284   Owners: TFPList;
285   i: Integer;
286 begin
287   if FSourceFilename=AValue then exit;
288   FSourceFilename:=AValue;
289   FStartModuleOwner:=nil;
290   Owners:=PackageEditingInterface.GetPossibleOwnersOfUnit(FSourceFilename,
291     [piosfIncludeSourceDirectories]);
292   if Owners=nil then exit;
293   try
294     for i:=0 to Owners.Count-1 do begin
295       if TObject(Owners[i]) is TLazProject then begin
296         FStartModuleOwner:=TLazProject(Owners[i]);
297       end else if TObject(Owners[i]) is TLazPackage then begin
298         if FStartModuleOwner=nil then
299           FStartModuleOwner:=TLazPackage(Owners[i]);
300       end;
301     end;
302   finally
303     Owners.Free;
304   end;
305 end;
306 
307 procedure TFPDocLinkEditorDlg.SetStartModuleOwner(const AValue: TObject);
308 begin
309   if FStartModuleOwner=AValue then exit;
310   FStartModuleOwner:=AValue;
311 end;
312 
313 procedure TFPDocLinkEditorDlg.UpdateCompletionBox;
314 {
315   ToDo:
316   empty  : show all packages, all units of current project/package and all identifiers of unit
317   #l     : show all packages beginning with the letter l
318   #lcl.  : show all units of package lcl
319   f      : show all units and all identifiers beginning with the letter f
320   forms. : show all identifiers of unit forms and all sub identifiers of identifier forms
321 
322   forms.tcontrol.        : show all sub identifiers of identifier tcontrol
323   #lcl.forms.            : same as above
324   #lcl.forms.tcontrol.   : same as above
325 }
326 var
327   l: String;
328 begin
329   if FItems=nil then exit;
330   if fUpdatingItems then exit;
331   fUpdatingItems:=true;
332   try
333     fItems.Clear;
334     l:=FItems.Prefix;
335     //DebugLn(['TFPDocLinkEditorDlg.UpdateCompletionBox Prefix="',l,'"']);
336     AddSubIdentifiers(l);
337     CompletionBox.Invalidate;
338   finally
339     fUpdatingItems:=false;
340   end;
341 end;
342 
343 procedure TFPDocLinkEditorDlg.AddPackagesToCompletion(Prefix: string);
344 var
345   i: Integer;
346   Pkg: TLazPackage;
347 begin
348   for i:=0 to PackageGraph.Count-1 do begin
349     Pkg:=PackageGraph.Packages[i];
350     if Pkg.FPDocPaths='' then continue;
351     if (SysUtils.CompareText(Prefix,copy(Pkg.Name,1,length(Prefix)))=0) then
352       fItems.AddPackage(Pkg);
353   end;
354 end;
355 
356 procedure TFPDocLinkEditorDlg.AddModuleUnits(ModuleOwner: TObject;
357   Prefix: string);
358 var
359   AProject: TLazProject;
360   APackage: TLazPackage;
361 begin
362   DebugLn(['TFPDocLinkEditorDlg.AddModuleUnits ',DbgSName(ModuleOwner)]);
363   if ModuleOwner=nil then exit;
364   if ModuleOwner is TLazProject then begin
365     AProject:=TLazProject(ModuleOwner);
366     AddProjectUnits(AProject,Prefix);
367   end else if ModuleOwner is TLazPackage then begin
368     APackage:=TLazPackage(ModuleOwner);
369     AddPackageUnits(APackage,Prefix);
370   end;
371 end;
372 
373 procedure TFPDocLinkEditorDlg.AddProjectUnits(AProject: TLazProject;
374   Prefix: string);
375 var
376   i: Integer;
377   Filename: String;
378   ProjFile: TLazProjectFile;
379   Identifier: String;
380 begin
381   DebugLn(['TFPDocLinkEditorDlg.AddProjectUnits ']);
382   for i:=0 to AProject.FileCount-1 do begin
383     ProjFile:=AProject.Files[i];
384     if ProjFile.IsPartOfProject then begin
385       Filename:=ProjFile.Filename;
386       if FilenameHasPascalExt(Filename) then begin
387         Filename:=ExtractFileNameOnly(Filename);
388         DebugLn(['TFPDocLinkEditorDlg.AddProjectUnits ',Prefix,' ',Filename]);
389         if (CompareFilenames(Prefix,copy(Filename,1,length(Prefix)))=0) then
390         begin
391           Identifier:=ExtractFileNameOnly(ProjFile.Filename);
392           if AProject<>StartModuleOwner then
393             Identifier:='#'+ExtractFileNameOnly(AProject.ProjectInfoFile)+'.'+Identifier;
394           fItems.Add(Identifier, lisProjectUnit);
395         end;
396       end;
397     end;
398   end;
399 end;
400 
401 procedure TFPDocLinkEditorDlg.AddPackageUnits(APackage: TLazPackage;
402   Prefix: string);
403 var
404   i: Integer;
405   PkgFile: TPkgFile;
406   Filename: String;
407   Identifier: String;
408 begin
409   for i:=0 to APackage.FileCount-1 do begin
410     PkgFile:=APackage.Files[i];
411     Filename:=PkgFile.GetFullFilename;
412     if FilenameHasPascalExt(Filename) then begin
413       Filename:=ExtractFileNameOnly(Filename);
414       if (CompareFilenames(Prefix,copy(Filename,1,length(Prefix)))=0) then
415       begin
416         Identifier:=ExtractFileNameOnly(Filename);
417         if APackage<>StartModuleOwner then
418           Identifier:='#'+APackage.Name+'.'+Identifier;
419         fItems.Add(Identifier, lisPackageUnit);
420       end;
421     end;
422   end;
423 end;
424 
425 procedure TFPDocLinkEditorDlg.AddIdentifiers(ModuleOwner: TObject;
426   FPDocFile: TLazFPDocFile; Prefix: string);
427 var
428   DOMNode: TDOMNode;
429   ElementName: String;
430   ModuleName: String;
431 begin
432   if FPDocFile=nil then exit;
433   //DebugLn(['TFPDocLinkEditorDlg.AddIdentifiers ',FPDocFile.Filename,' Prefix=',Prefix]);
434   DOMNode:=FPDocFile.GetFirstElement;
435   while DOMNode<>nil do begin
436     if (DOMNode is TDomElement) then begin
437       //DebugLn(['TFPDocLinkEditorDlg.AddIdentifiers ',DbgSName(DOMNode)]);
438       ElementName:=TDomElement(DOMNode).GetAttribute('name');
439       if (SysUtils.CompareText(Prefix,copy(ElementName,1,length(Prefix)))=0)
440       then begin
441         // same prefix
442         if (FPDocFile<>nil) and (FPDocFile<>StartFPDocFile) then begin
443           // different unit
444           ElementName:=ExtractFileNameOnly(FPDocFile.Filename)+'.'+ElementName;
445         end;
446         if (ModuleOwner<>nil) and (ModuleOwner<>StartModuleOwner) then begin
447           // different module
448           if ModuleOwner is TLazProject then
449             ModuleName:=lowercase(ExtractFileNameOnly(TLazProject(ModuleOwner).ProjectInfoFile))
450           else if ModuleOwner is TLazPackage then
451             ModuleName:=TLazPackage(ModuleOwner).Name
452           else
453             ModuleName:='';
454           if ModuleName<>'' then
455             ElementName:='#'+ModuleName+'.'+ElementName
456           else
457             ElementName:='';
458         end;
459         if ElementName<>'' then
460           FItems.AddIdentifier(ElementName);
461       end;
462     end;
463     DOMNode:=DOMNode.NextSibling;
464   end;
465 end;
466 
467 procedure TFPDocLinkEditorDlg.AddSubIdentifiers(Path: string);
468 var
469   p: LongInt;
470   Prefix: String;
471   ModuleOwner: TObject;
472   FPDocFile: TLazFPDocFile;
473   DOMNode: TDOMNode;
474   InvalidPath: integer;
475   CacheWasUsed: boolean;
476   DOMElement: TDOMElement;
477 begin
478   p:=length(Path);
479   while (p>0) and (Path[p]<>'.') do dec(p);
480   if p<1 then begin
481     // empty  : show all packages, all units of current project/package and all identifiers of unit
482     // #l     : show all packages beginning with the letter l
483     // f      : show all units and all identifiers beginning with the letter f
484     if (Path='') or (Path[1]='#') then
485       AddPackagesToCompletion(copy(Path,2,length(Path)));
486     if (Path='') or (Path[1]<>'#') then begin
487       AddModuleUnits(StartModuleOwner,Path);
488       AddIdentifiers(StartModuleOwner,StartFPDocFile,Path);
489     end;
490   end else begin
491     // sub identifier
492     //DebugLn(['TFPDocLinkEditorDlg.AddSubIdentifiers searching context ..']);
493     CodeHelpBoss.GetLinkedFPDocNode(StartFPDocFile,nil,Path,
494       [chofUpdateFromDisk,chofQuiet],ModuleOwner,FPDocFile,DOMNode,InvalidPath,
495       CacheWasUsed);
496     // get rest path as filter
497     Prefix:=copy(Path,InvalidPath,length(Path));
498     if (Prefix<>'') and (Prefix[1]='.') then System.Delete(Prefix,1,1);
499     //DebugLn(['TFPDocLinkEditorDlg.AddSubIdentifiers context found: ModuleOwner=',DbgSName(ModuleOwner),' FPDocFile=',FPDocFile<>nil,' DOMNode=',DOMNode<>nil,' invalid path="',Prefix,'"']);
500     if DOMNode is TDomElement then begin
501       // show elements of unit, beginning with prefix
502       DOMElement:=TDomElement(DOMNode);
503       Prefix:=DOMElement.GetAttribute('name')+'.'+Prefix;
504       AddIdentifiers(ModuleOwner,FPDocFile,Prefix);
505     end else if FPDocFile<>nil then begin
506       // show elements of unit, beginning with prefix
507       AddIdentifiers(ModuleOwner,FPDocFile,Prefix);
508     end else if ModuleOwner<>nil then begin
509       // show units of module, beginning with first part of prefix
510       p:=1;
511       while (p<=length(Prefix)) and (Prefix[p]<>'.') do inc(p);
512       Prefix:=copy(Prefix,1,p-1);
513       if ModuleOwner is TLazPackage then
514         AddPackageUnits(TLazPackage(ModuleOwner),Prefix)
515       else if ModuleOwner is TLazProject then
516         AddProjectUnits(TLazProject(ModuleOwner),Prefix);
517     end;
518   end;
519 end;
520 
521 procedure TFPDocLinkEditorDlg.SetLinkAndContext(const ASrcFilename, ATitle,
522   ALink: string; ADocFile: TLazFPDocFile);
523 begin
524   StartFPDocFile:=ADocFile;
525   fSourceFilename:=ASrcFilename;
526   LinkTitle:=ATitle;
527   Link:=ALink;
528   UpdateCompletionBox;
529 end;
530 
GetLinkTitlenull531 function TFPDocLinkEditorDlg.GetLinkTitle: string;
532 begin
533   Result:=TitleEdit.Text;
534 end;
535 
536 procedure TFPDocLinkEditorDlg.SetStartFPDocFile(const AValue: TLazFPDocFile);
537 begin
538   if FStartFPDocFile=AValue then exit;
539   FStartFPDocFile:=AValue;
540   FStartModuleOwner:=CodeHelpBoss.FindModuleOwner(FStartFPDocFile);
541 end;
542 
GetLinknull543 function TFPDocLinkEditorDlg.GetLink: string;
544 begin
545   Result:=LinkEdit.Text;
546 end;
547 
548 procedure TFPDocLinkEditorDlg.SetLink(const AValue: string);
549 begin
550   if FItems=nil then exit;
551   if AValue=fItems.Prefix then exit;
552   fItems.Prefix:=AValue;
553   LinkEdit.Text:=AValue;
554   UpdateCompletionBox;
555 end;
556 
557 procedure TFPDocLinkEditorDlg.SetLinkTitle(const AValue: string);
558 begin
559   TitleEdit.Text:=AValue;
560 end;
561 
562 { TFPDocLinkCompletionList }
563 
GetCountnull564 function TFPDocLinkCompletionList.GetCount: integer;
565 begin
566   Result:=FItems.Count;
567 end;
568 
TFPDocLinkCompletionList.GetItemsnull569 function TFPDocLinkCompletionList.GetItems(Index: integer
570   ): TFPDocLinkCompletionItem;
571 begin
572   Result:=TFPDocLinkCompletionItem(FItems[Index]);
573 end;
574 
575 procedure TFPDocLinkCompletionList.SetSorted(const AValue: Boolean);
576 begin
577   if FSorted=AValue then exit;
578   if AValue then
579     Sort
580   else
581     FSorted:=false;
582 end;
583 
584 procedure TFPDocLinkCompletionList.SetTop(const AValue: integer);
585 begin
586   if FTop=AValue then exit;
587   FTop:=AValue;
588 end;
589 
590 constructor TFPDocLinkCompletionList.Create;
591 begin
592   FItems:=TFPList.Create;
593   FTree:=TAvlTree.Create(@CompareFPDocLinkCompletionItem);
594 end;
595 
596 destructor TFPDocLinkCompletionList.Destroy;
597 begin
598   Clear;
599   FreeAndNil(FItems);
600   FreeAndNil(FTree);
601   inherited Destroy;
602 end;
603 
604 procedure TFPDocLinkCompletionList.Clear;
605 var
606   i: Integer;
607 begin
608   FTree.Clear;
609   for i:=0 to FItems.Count-1 do TObject(FItems[i]).Free;
610   FItems.Clear;
611   FSelected:=0;
612   FTop:=0;
613   FSorted:=true;
614 end;
615 
616 procedure TFPDocLinkCompletionList.Sort;
617 var
618   Node: TAvlTreeNode;
619   i: Integer;
620 begin
621   if FSorted then exit;
622   Node:=FTree.FindLowest;
623   i:=0;
624   while Node<>nil do begin
625     FItems[i]:=Node.Data;
626     inc(i);
627     Node:=FTree.FindSuccessor(Node);
628   end;
629   FSorted:=true;
630 end;
631 
632 procedure TFPDocLinkCompletionList.AddPackage(Pkg: TLazPackage);
633 begin
634   Add('#'+Pkg.Name, Format(lisPackage2, [Pkg.IDAsString]));
635 end;
636 
637 procedure TFPDocLinkCompletionList.Add(Identifier, Description: string);
638 var
639   Item: TFPDocLinkCompletionItem;
640 begin
641   if FTree.FindKey(Pointer(Identifier),
642                    @ComparePathWithFPDocLinkCompletionItem)<>nil
643   then exit;
644   Item:=TFPDocLinkCompletionItem.Create(Identifier,Description);
645   FItems.Add(Item);
646   FTree.Add(Item);
647   FSorted:=false;
648 end;
649 
650 procedure TFPDocLinkCompletionList.AddIdentifier(Identifier: string);
651 begin
652   Add(Identifier, lisIdentifier);
653 end;
654 
655 procedure TFPDocLinkCompletionList.Draw(Canvas: TCanvas; Width, Height: integer);
656 var
657   i: LongInt;
658   y: Integer;
659   dy: LongInt;
660   Item: TFPDocLinkCompletionItem;
661   s: String;
662 begin
663   //DebugLn(['TFPDocLinkCompletionList.Draw ',Width,' ',Height,' Count=',Count]);
664   Sorted:=true;
665   i:=Top;
666   y:=0;
667   dy:=ItemHeight;
668   while (y<Height) and (i<Count) do begin
669     Item:=Items[i];
670     Canvas.Brush.Style:=bsSolid;
671     Canvas.Font.Style:=[];
672     if i=Selected then begin
673       Canvas.Brush.Color:=SelectedBGColor;
674       Canvas.Font.Color:=SelectedTextColor;
675     end else begin
676       Canvas.Brush.Color:=BGColor;
677       Canvas.Font.Color:=TextColor;
678     end;
679     Canvas.FillRect(0,y,Width,y+dy);
680     s:=Item.Text;
681     Canvas.TextOut(2,y+2,s);
682     inc(y,dy);
683     inc(i);
684   end;
685   if y<Height then begin
686     Canvas.Brush.Color:=BGColor;
687     Canvas.FillRect(0,y,Width,Height);
688   end;
689 end;
690 
691 { TFPDocLinkCompletionItem }
692 
693 constructor TFPDocLinkCompletionItem.Create(const AText, ADescription: string);
694 begin
695   Text:=AText;
696   Description:=ADescription;
697 end;
698 
699 end.
700 
701