1 {
2 /***************************************************************************
3 installpkgsetdlg.pas
4 --------------------
5
6
7 ***************************************************************************/
8
9 ***************************************************************************
10 * *
11 * This source is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This code is distributed in the hope that it will be useful, but *
17 * WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
19 * General Public License for more details. *
20 * *
21 * A copy of the GNU General Public License is available on the World *
22 * Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
23 * obtain it by writing to the Free Software Foundation, *
24 * Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
25 * *
26 ***************************************************************************
27
28 Author: Mattias Gaertner
29
30 Abstract:
31 Dialog to edit the package set installed in the IDE.
32 }
33 unit InstallPkgSetDlg;
34
35 {$mode objfpc}{$H+}
36
37 interface
38
39 uses
40 Classes, SysUtils, contnrs, Laz_AVL_Tree,
41 // LCL
42 LCLType, LCLProc, Forms, Controls, Graphics, Dialogs, StdCtrls, Buttons,
43 ExtCtrls, ComCtrls, ImgList,
44 // LazControls
45 TreeFilterEdit,
46 // Codetools
47 BasicCodeTools,
48 // LazUtils
49 LazFileUtils, Laz2_XMLCfg, LazUTF8,
50 // IdeIntf
51 PackageDependencyIntf, PackageIntf, IDEImagesIntf, IDEHelpIntf, IDEDialogs, IDEWindowIntf, PackageLinkIntf,
52 // IDE
53 LazarusIDEStrConsts, InputHistory, LazConf, PackageDefs, PackageSystem, LPKCache, PackageLinks;
54
55 type
56 TOnCheckInstallPackageList =
57 procedure(PkgIDs: TObjectList; RemoveConflicts: boolean; out Ok: boolean) of object;
58
59 { TInstallPkgSetDialog }
60
61 TInstallPkgSetDialog = class(TForm)
62 AddToInstallButton: TBitBtn;
63 AvailableTreeView: TTreeView;
64 AvailablePkgGroupBox: TGroupBox;
65 MiddleBevel: TBevel;
66 HelpButton: TBitBtn;
67 CancelButton: TBitBtn;
68 ExportButton: TButton;
69 BtnPanel: TPanel;
70 InstallTreeView: TTreeView;
71 AvailableFilterEdit: TTreeFilterEdit;
72 LPKParsingTimer: TTimer;
73 NoteLabel: TLabel;
74 Panel1: TPanel;
75 Panel2: TPanel;
76 PkgInfoMemo: TMemo;
77 PkgInfoGroupBox: TGroupBox;
78 ImportButton: TButton;
79 PkgInfoMemoLicense: TMemo;
80 SaveAndExitButton: TBitBtn;
81 InstallPkgGroupBox: TGroupBox;
82 SaveAndRebuildButton: TBitBtn;
83 InstalledFilterEdit: TTreeFilterEdit;
84 Splitter1: TSplitter;
85 Splitter2: TSplitter;
86 UninstallButton: TBitBtn;
87 procedure AddToInstallButtonClick(Sender: TObject);
FilterEditGetImageIndexnull88 function FilterEditGetImageIndex({%H-}Str: String; {%H-}Data: TObject;
89 var {%H-}AIsEnabled: Boolean): Integer;
90 procedure FormClose(Sender: TObject; var {%H-}CloseAction: TCloseAction);
91 procedure InstallTreeViewKeyPress(Sender: TObject; var Key: char);
92 procedure LPKParsingTimerTimer(Sender: TObject);
93 procedure OnAllLPKParsed(Sender: TObject);
94 procedure OnIdle(Sender: TObject; var {%H-}Done: Boolean);
95 procedure TreeViewAdvancedCustomDrawItem(Sender: TCustomTreeView;
96 Node: TTreeNode; {%H-}State: TCustomDrawState; Stage: TCustomDrawStage;
97 var PaintImages, {%H-}DefaultDraw: Boolean);
98 procedure AvailableTreeViewDblClick(Sender: TObject);
99 procedure AvailableTreeViewKeyPress(Sender: TObject; var Key: char);
100 procedure AvailableTreeViewSelectionChanged(Sender: TObject);
101 procedure ExportButtonClick(Sender: TObject);
102 procedure HelpButtonClick(Sender: TObject);
103 procedure ImportButtonClick(Sender: TObject);
104 procedure SaveAndRebuildButtonClick(Sender: TObject);
105 procedure InstallTreeViewDblClick(Sender: TObject);
106 procedure InstallPkgSetDialogCreate(Sender: TObject);
107 procedure InstallPkgSetDialogDestroy(Sender: TObject);
108 procedure InstallPkgSetDialogShow(Sender: TObject);
109 procedure InstallPkgSetDialogResize(Sender: TObject);
110 procedure InstallTreeViewSelectionChanged(Sender: TObject);
111 procedure SaveAndExitButtonClick(Sender: TObject);
112 procedure UninstallButtonClick(Sender: TObject);
113 private
114 FIdleConnected: boolean;
115 FNewInstalledPackages: TObjectList; // list of TLazPackageID (not TLazPackage)
116 FOldInstalledPackages: TPkgDependency;
117 FOnCheckInstallPackageList: TOnCheckInstallPackageList;
118 FRebuildIDE: boolean;
119 FSelectedPkgState: TLPKInfoState;
120 FSelectedPkgID: string;
121 fAvailablePkgsNeedUpdate: boolean;
122 ImgIndexPackage: integer;
123 ImgIndexInstallPackage: integer;
124 ImgIndexInstalledPackage: integer;
125 ImgIndexUninstallPackage: integer;
126 ImgIndexCirclePackage: integer;
127 ImgIndexMissingPackage: integer;
128 ImgIndexAvailableOnline: integer;
129 ImgIndexOverlayUnknown: integer;
130 ImgIndexOverlayBasePackage: integer;
131 ImgIndexOverlayFPCPackage: integer;
132 ImgIndexOverlayLazarusPackage: integer;
133 ImgIndexOverlayDesigntimePackage: integer;
134 ImgIndexOverlayRuntimePackage: integer;
135 procedure SetIdleConnected(AValue: boolean);
136 procedure SetOldInstalledPackages(const AValue: TPkgDependency);
137 procedure AssignOldInstalledPackagesToList;
PackageInInstallListnull138 function PackageInInstallList(PkgName: string): boolean;
GetPkgImgIndexnull139 function GetPkgImgIndex(Installed: TPackageInstallType; InInstallList,
140 IsOnline: boolean): integer;
141 procedure UpdateAvailablePackages(Immediately: boolean = false);
142 procedure UpdateNewInstalledPackages;
DependencyToStrnull143 function DependencyToStr(Dependency: TPkgDependency): string;
144 procedure ClearNewInstalledPackages;
CheckSelectionnull145 function CheckSelection: boolean;
146 procedure UpdateButtonStates;
147 procedure UpdatePackageInfo(Tree: TTreeView);
NewInstalledPackagesContainsnull148 function NewInstalledPackagesContains(APackageID: TLazPackageID): boolean;
IndexOfNewInstalledPackageIDnull149 function IndexOfNewInstalledPackageID(APackageID: TLazPackageID): integer;
IndexOfNewInstalledPkgByNamenull150 function IndexOfNewInstalledPkgByName(const APackageName: string): integer;
151 procedure SavePackageListToFile(const AFilename: string);
152 procedure LoadPackageListFromFile(const AFilename: string);
ExtractNameFromPkgIDnull153 function ExtractNameFromPkgID(ID: string): string;
154 procedure AddToInstall;
155 procedure AddToUninstall;
156 procedure PkgInfosChanged;
157 procedure ChangePkgVersion(PkgInfo: TLPKInfo; NewVersion: TPkgVersion);
FindOnlinePackageLinknull158 function FindOnlinePackageLink(const AName: String): TPackageLink;
159 public
GetNewInstalledPackagesnull160 function GetNewInstalledPackages: TObjectList;
161 property OldInstalledPackages: TPkgDependency read FOldInstalledPackages
162 write SetOldInstalledPackages;
163 property NewInstalledPackages: TObjectList read FNewInstalledPackages; // list of TLazPackageID
164 property RebuildIDE: boolean read FRebuildIDE write FRebuildIDE;
165 property OnCheckInstallPackageList: TOnCheckInstallPackageList
166 read FOnCheckInstallPackageList write FOnCheckInstallPackageList;
167 property IdleConnected: boolean read FIdleConnected write SetIdleConnected;
168 end;
169
ShowEditInstallPkgsDialognull170 function ShowEditInstallPkgsDialog(OldInstalledPackages: TPkgDependency;
171 CheckInstallPackageList: TOnCheckInstallPackageList;
172 var NewInstalledPackages: TObjectList; // list of TLazPackageID (must be freed)
173 var RebuildIDE: boolean): TModalResult;
174
175 implementation
176
177 {$R *.lfm}
178
179 procedure SetControlsWidthOnMax(AControls: array of TControl);
180 var
181 i, MaxWidth: Integer;
182 begin
183 MaxWidth:=0;
184 for i:=Low(AControls) to High(AControls) do
185 if AControls[i].Width>MaxWidth then
186 MaxWidth:=AControls[i].Width;
187 for i:=Low(AControls) to High(AControls) do
188 AControls[i].Constraints.MinWidth:=MaxWidth; // AutoSize=True
189 end;
190
ShowEditInstallPkgsDialognull191 function ShowEditInstallPkgsDialog(OldInstalledPackages: TPkgDependency;
192 CheckInstallPackageList: TOnCheckInstallPackageList;
193 var NewInstalledPackages: TObjectList; // list of TLazPackageID
194 var RebuildIDE: boolean): TModalResult;
195 var
196 InstallPkgSetDialog: TInstallPkgSetDialog;
197 begin
198 NewInstalledPackages:=nil;
199 InstallPkgSetDialog:=TInstallPkgSetDialog.Create(nil);
200 try
201 InstallPkgSetDialog.OldInstalledPackages:=OldInstalledPackages;
202 InstallPkgSetDialog.UpdateButtonStates;
203 InstallPkgSetDialog.OnCheckInstallPackageList:=CheckInstallPackageList;
204 Result:=InstallPkgSetDialog.ShowModal;
205 NewInstalledPackages:=InstallPkgSetDialog.GetNewInstalledPackages;
206 RebuildIDE:=InstallPkgSetDialog.RebuildIDE;
207 finally
208 InstallPkgSetDialog.Free;
209 end;
210 end;
211
212 { TInstallPkgSetDialog }
213
214 procedure TInstallPkgSetDialog.InstallPkgSetDialogCreate(Sender: TObject);
215 begin
216 IDEDialogLayoutList.ApplyLayout(Self);
217
218 InstallTreeView.Images := IDEImages.Images_16;
219 AvailableTreeView.Images := IDEImages.Images_16;
220 ImgIndexPackage := IDEImages.LoadImage('item_package');
221 ImgIndexInstalledPackage := IDEImages.LoadImage('pkg_installed');
222 ImgIndexInstallPackage := IDEImages.LoadImage('pkg_package_autoinstall');
223 ImgIndexUninstallPackage := IDEImages.LoadImage('pkg_package_uninstall');
224 ImgIndexCirclePackage := IDEImages.LoadImage('pkg_package_circle');
225 ImgIndexMissingPackage := IDEImages.LoadImage('pkg_conflict');
226 ImgIndexAvailableOnline := IDEImages.LoadImage('pkg_install');
227 ImgIndexOverlayUnknown := IDEImages.LoadImage('state_unknown');
228 ImgIndexOverlayBasePackage := IDEImages.LoadImage('pkg_core_overlay');
229 ImgIndexOverlayFPCPackage := IDEImages.LoadImage('pkg_fpc_overlay');
230 ImgIndexOverlayLazarusPackage := IDEImages.LoadImage('pkg_lazarus_overlay');
231 ImgIndexOverlayDesignTimePackage := IDEImages.LoadImage('pkg_design_overlay');
232 ImgIndexOverlayRunTimePackage := IDEImages.LoadImage('pkg_runtime_overlay');
233
234 Caption:=lisInstallUninstallPackages;
235 NoteLabel.Caption:=lisIDECompileAndRestart;
236
237 AvailablePkgGroupBox.Caption:=lisAvailableForInstallation;
238
239 ExportButton.Caption:=lisExportList;
240 ImportButton.Caption:=lisImportList;
241 UninstallButton.Caption:=lisUninstallSelection;
242 IDEImages.AssignImage(UninstallButton, 'arrow__darkred_right');
243 InstallPkgGroupBox.Caption:=lisPckEditInstall;
244 AddToInstallButton.Caption:=lisInstallSelection;
245 IDEImages.AssignImage(AddToInstallButton, 'arrow__darkgreen_left');
246 PkgInfoGroupBox.Caption := lisPackageInfo;
247 SaveAndRebuildButton.Caption:=lisSaveAndRebuildIDE;
248 SaveAndExitButton.Caption:=lisSaveAndExitDialog;
249 HelpButton.Caption:=lisMenuHelp;
250 CancelButton.Caption:=lisCancel;
251
252 FNewInstalledPackages:=TObjectList.Create(true);
253 PkgInfoMemo.Clear;
254 PkgInfoMemoLicense.Clear;
255 LPKInfoCache.AddOnQueueEmpty(@OnAllLPKParsed);
256 LPKInfoCache.StartLPKReaderWithAllAvailable;
257
258 UpdateButtonStates;
259 end;
260
261 procedure TInstallPkgSetDialog.InstallPkgSetDialogDestroy(Sender: TObject);
262 begin
263 LPKInfoCache.EndLPKReader;
264 LPKInfoCache.RemoveOnQueueEmpty(@OnAllLPKParsed);
265 ClearNewInstalledPackages;
266 FreeAndNil(FNewInstalledPackages);
267 IdleConnected:=false;
268 end;
269
270 procedure TInstallPkgSetDialog.InstallPkgSetDialogShow(Sender: TObject);
271 begin
272 InstalledFilterEdit.Filter:=''; // (filter) - text is shown after this.
273 AvailableFilterEdit.Filter:='';
274 SetControlsWidthOnMax([UninstallButton, AddToInstallButton]);
275 SetControlsWidthOnMax([ImportButton, ExportButton]);
276 end;
277
278 procedure TInstallPkgSetDialog.SaveAndRebuildButtonClick(Sender: TObject);
279 begin
280 if not CheckSelection then exit;
281 RebuildIDE:=true;
282 ModalResult:=mrOk;
283 end;
284
285 procedure TInstallPkgSetDialog.InstallTreeViewDblClick(Sender: TObject);
286 begin
287 AddToUninstall;
288 end;
289
290 procedure TInstallPkgSetDialog.AvailableTreeViewSelectionChanged(Sender: TObject);
291 begin
292 UpdateButtonStates;
293 UpdatePackageInfo(AvailableTreeView);
294 end;
295
296 procedure TInstallPkgSetDialog.ExportButtonClick(Sender: TObject);
297 var
298 SaveDialog: TSaveDialog;
299 AFilename: string;
300 begin
301 SaveDialog:=TSaveDialog.Create(nil);
302 try
303 InputHistories.ApplyFileDialogSettings(SaveDialog);
304 SaveDialog.InitialDir:=GetPrimaryConfigPath;
305 SaveDialog.Title:=lisExportPackageListXml;
306 SaveDialog.Options:=SaveDialog.Options+[ofPathMustExist];
307 if SaveDialog.Execute then begin
308 AFilename:=CleanAndExpandFilename(SaveDialog.Filename);
309 if ExtractFileExt(AFilename)='' then
310 AFilename:=AFilename+'.xml';
311 SavePackageListToFile(AFilename);
312 end;
313 InputHistories.StoreFileDialogSettings(SaveDialog);
314 finally
315 SaveDialog.Free;
316 end;
317 end;
318
319 procedure TInstallPkgSetDialog.HelpButtonClick(Sender: TObject);
320 begin
321 LazarusHelp.ShowHelpForIDEControl(Self);
322 end;
323
324 procedure TInstallPkgSetDialog.ImportButtonClick(Sender: TObject);
325 var
326 OpenDialog: TOpenDialog;
327 AFilename: string;
328 begin
329 OpenDialog:=TOpenDialog.Create(nil);
330 try
331 InputHistories.ApplyFileDialogSettings(OpenDialog);
332 OpenDialog.InitialDir:=GetPrimaryConfigPath;
333 OpenDialog.Title:=lisImportPackageListXml;
334 OpenDialog.Options:=OpenDialog.Options+[ofPathMustExist,ofFileMustExist];
335 if OpenDialog.Execute then begin
336 AFilename:=CleanAndExpandFilename(OpenDialog.Filename);
337 LoadPackageListFromFile(AFilename);
338 end;
339 InputHistories.StoreFileDialogSettings(OpenDialog);
340 finally
341 OpenDialog.Free;
342 end;
343 end;
344
345 procedure TInstallPkgSetDialog.AddToInstallButtonClick(Sender: TObject);
346 begin
347 AddToInstall;
348 end;
349
TInstallPkgSetDialog.FilterEditGetImageIndexnull350 function TInstallPkgSetDialog.FilterEditGetImageIndex(Str: String;
351 Data: TObject; var AIsEnabled: Boolean): Integer;
352 begin
353 Result:=0;
354 end;
355
356 procedure TInstallPkgSetDialog.FormClose(Sender: TObject;
357 var CloseAction: TCloseAction);
358 begin
359 IDEDialogLayoutList.SaveLayout(Self);
360 end;
361
362 procedure TInstallPkgSetDialog.InstallTreeViewKeyPress(Sender: TObject; var Key: char);
363 begin
364 if Key = char(VK_RETURN) then
365 AddToUninstall;
366 end;
367
368 procedure TInstallPkgSetDialog.LPKParsingTimerTimer(Sender: TObject);
369 begin
370 UpdateNewInstalledPackages;
371 UpdateAvailablePackages;
372 end;
373
374 procedure TInstallPkgSetDialog.OnAllLPKParsed(Sender: TObject);
375 begin
376 LPKParsingTimer.Enabled:=false;
377 UpdateNewInstalledPackages;
378 UpdateAvailablePackages;
379 end;
380
381 procedure TInstallPkgSetDialog.OnIdle(Sender: TObject; var Done: Boolean);
382 begin
383 if fAvailablePkgsNeedUpdate then
384 UpdateAvailablePackages(true);
385 IdleConnected:=false;
386 end;
387
388 procedure TInstallPkgSetDialog.TreeViewAdvancedCustomDrawItem(
389 Sender: TCustomTreeView; Node: TTreeNode; State: TCustomDrawState;
390 Stage: TCustomDrawStage; var PaintImages, DefaultDraw: Boolean);
391 var
392 Info: TLPKInfo;
393 NodeRect: TRect;
394 x: Integer;
395 Images: TCustomImageList;
396 CurCanvas: TCanvas;
397 y: Integer;
398 Tree: TTreeView;
399 InLazSrc: Boolean;
400 IsBase: Boolean;
401 PkgType: TLazPackageType;
402 Installed: TPackageInstallType;
403 PkgName: String;
404 ImgIndex: Integer;
405 Unknown: Boolean;
406 PackageLink: TPackageLink;
407 ImagesRes: TScaledImageListResolution;
408 begin
409 Tree:=Sender as TTreeView;
410 if Stage=cdPostPaint then begin
411 LPKInfoCache.EnterCritSection;
412 try
413 Info:=LPKInfoCache.FindPkgInfoWithIDAsString(Node.Text);
414 if Info=nil then exit;
415 PkgName:=Info.ID.Name;
416 Unknown:=not (Info.LPKParsed in [lpkiParsed,lpkiParsedError]);
417 InLazSrc:=Info.InLazSrc;
418 IsBase:=Info.Base;
419 PkgType:=Info.PkgType;
420 Installed:=Info.Installed;
421 finally
422 LPKInfoCache.LeaveCritSection;
423 end;
424 if Sender = InstallTreeView then
425 PackageLink := nil
426 else
427 PackageLink := FindOnlinePackageLink(Info.ID.Name);
428 Images:=Tree.Images;
429 if Images = nil then exit;
430 ImagesRes := Images.ResolutionForPPI[Tree.ImagesWidth, Font.PixelsPerInch, GetCanvasScaleFactor];
431 CurCanvas:=Tree.Canvas;
432
433 NodeRect:=Node.DisplayRect(False);
434 x:=Node.DisplayIconLeft+1;
435 y:=(NodeRect.Top+NodeRect.Bottom-ImagesRes.Height) div 2;
436 // draw image
437 ImgIndex:=GetPkgImgIndex(Installed,PackageInInstallList(PkgName), PackageLink <> nil);
438 ImagesRes.Draw(CurCanvas,x,y,ImgIndex);
439 // draw overlays
440 if InLazSrc then
441 ImagesRes.Draw(CurCanvas,x,y,ImgIndexOverlayLazarusPackage);
442 if IsBase then
443 ImagesRes.Draw(CurCanvas,x,y,ImgIndexOverlayBasePackage);
444 if PkgType=lptRunTimeOnly then
445 ImagesRes.Draw(CurCanvas,x,y,ImgIndexOverlayRuntimePackage);
446 if PkgType=lptDesignTime then
447 ImagesRes.Draw(CurCanvas,x,y,ImgIndexOverlayDesigntimePackage);
448 if Unknown then
449 ImagesRes.Draw(CurCanvas,x,y,ImgIndexOverlayUnknown);
450 end;
451 PaintImages:=false;
452 end;
453
454 procedure TInstallPkgSetDialog.AvailableTreeViewDblClick(Sender: TObject);
455 begin
456 AddToInstall;
457 end;
458
459 procedure TInstallPkgSetDialog.AvailableTreeViewKeyPress(Sender: TObject; var Key: char);
460 begin
461 if Key = char(VK_RETURN) then
462 AddToInstall;
463 end;
464
465 procedure TInstallPkgSetDialog.InstallPkgSetDialogResize(Sender: TObject);
466 var
467 w: Integer;
468 begin
469 w:=ClientWidth div 2-InstallPkgGroupBox.BorderSpacing.Left*3;
470 if w<1 then w:=1;
471 InstallPkgGroupBox.Width:=w;
472 end;
473
474 procedure TInstallPkgSetDialog.InstallTreeViewSelectionChanged(Sender: TObject);
475 begin
476 UpdateButtonStates;
477 UpdatePackageInfo(InstallTreeView);
478 end;
479
480 procedure TInstallPkgSetDialog.SaveAndExitButtonClick(Sender: TObject);
481 begin
482 if not CheckSelection then exit;
483 RebuildIDE:=false;
484 ModalResult:=mrOk;
485 end;
486
487 procedure TInstallPkgSetDialog.UninstallButtonClick(Sender: TObject);
488 begin
489 AddToUninstall;
490 end;
491
492 procedure TInstallPkgSetDialog.SetOldInstalledPackages(const AValue: TPkgDependency);
493 begin
494 if FOldInstalledPackages=AValue then exit;
495 FOldInstalledPackages:=AValue;
496 AssignOldInstalledPackagesToList;
497 end;
498
499 procedure TInstallPkgSetDialog.SetIdleConnected(AValue: boolean);
500 begin
501 if FIdleConnected=AValue then Exit;
502 FIdleConnected:=AValue;
503 if IdleConnected then
504 Application.AddOnIdleHandler(@OnIdle)
505 else
506 Application.RemoveOnIdleHandler(@OnIdle);
507 end;
508
509 procedure TInstallPkgSetDialog.AssignOldInstalledPackagesToList;
510 var
511 Dependency: TPkgDependency;
512 Cnt: Integer;
513 NewPackageID: TLazPackageID;
514 begin
515 ClearNewInstalledPackages;
516 Cnt:=0;
517 Dependency:=OldInstalledPackages;
518 while Dependency<>nil do begin
519 NewPackageID:=TLazPackageID.Create;
520 if (Dependency.LoadPackageResult=lprSuccess)
521 and (Dependency.RequiredPackage<>nil) then begin
522 // packages can be freed while the dialog runs => use packageid instead
523 NewPackageID.AssignID(Dependency.RequiredPackage);
524 end else begin
525 NewPackageID.Name:=Dependency.PackageName;
526 end;
527 FNewInstalledPackages.Add(NewPackageID);
528 Dependency:=Dependency.NextRequiresDependency;
529 inc(Cnt);
530 end;
531 UpdateNewInstalledPackages;
532 UpdateAvailablePackages;
533 end;
534
TInstallPkgSetDialog.PackageInInstallListnull535 function TInstallPkgSetDialog.PackageInInstallList(PkgName: string): boolean;
536 var
537 i: Integer;
538 begin
539 for i:=0 to NewInstalledPackages.Count-1 do
540 if SysUtils.CompareText(TLazPackageID(NewInstalledPackages[i]).Name,PkgName)=0 then
541 exit(true);
542 Result:=false;
543 end;
544
TInstallPkgSetDialog.GetPkgImgIndexnull545 function TInstallPkgSetDialog.GetPkgImgIndex(Installed: TPackageInstallType;
546 InInstallList, IsOnline: boolean): integer;
547 begin
548 if Installed<>pitNope then begin
549 // is not currently installed
550 if InInstallList then begin
551 // is installed and will be installed
552 Result:=ImgIndexPackage;
553 end
554 else begin
555 // is installed and will be uninstalled
556 Result:=ImgIndexUninstallPackage;
557 end;
558 end else begin
559 // is currently installed
560 if InInstallList then begin
561 // is not installed and will be installed
562 Result:=ImgIndexInstallPackage;
563 end
564 else begin
565 // is not installed and will be not be installed
566 if IsOnline then
567 Result := ImgIndexAvailableOnline
568 else
569 Result:=ImgIndexPackage;
570 end;
571 end;
572 end;
573
FindOnlinePackageLinknull574 function TInstallPkgSetDialog.FindOnlinePackageLink(const AName: String): TPackageLink;
575 var
576 PackageLink: TPackageLink;
577 PkgName: String;
578 P: Integer;
579 begin
580 Result := nil;
581 Exit;
582 if OPMInterface = nil then
583 Exit;
584 PkgName := Trim(AName);
585 P := Pos(' ', PkgName);
586 if P > 0 then
587 PkgName := Copy(PkgName, 1, P - 1);
588 PackageLink := OPMInterface.FindOnlineLink(PkgName);
589 if PackageLink <> nil then
590 Result := PackageLink;
591 end;
592
593
594 procedure TInstallPkgSetDialog.UpdateAvailablePackages(Immediately: boolean);
595 var
596 ANode: TAvlTreeNode;
597 FilteredBranch: TTreeFilterBranch;
598 Info: TLPKInfo;
599 List: TStringList;
600 i: Integer;
601 PackageLink: TPackageLink;
602 begin
603 if not Immediately then begin
604 fAvailablePkgsNeedUpdate:=true;
605 IdleConnected:=true;
606 exit;
607 end;
608 fAvailablePkgsNeedUpdate:=false;
609 List:=TStringList.Create;
610 try
611 // collect available packages, not yet installed
612 LPKInfoCache.EnterCritSection;
613 try
614 ANode:=LPKInfoCache.LPKByID.FindLowest;
615 while ANode<>nil do begin
616 Info:=TLPKInfo(ANode.Data);
617 ANode:=LPKInfoCache.LPKByID.FindSuccessor(ANode);
618 PackageLink := FindOnlinePackageLink(Info.ID.Name);
619 if (PackageLink <> nil) and (PackageLink.PackageType in [lptDesignTime,lptRunAndDesignTime]) then begin
620 if (not PackageInInstallList(Info.ID.Name)) then begin
621 Info.PkgType := PackageLink.PackageType;
622 Info.ID.Version.Assign(PackageLink.Version);
623 List.Add(Info.ID.IDAsString);
624 Continue;
625 end;
626 end;
627 if Info.LPKParsed=lpkiParsedError then continue;
628 if (Info.LPKParsed in [lpkiNotParsed,lpkiParsing])
629 or (Info.PkgType in [lptDesignTime,lptRunAndDesignTime])
630 then begin
631 if (not PackageInInstallList(Info.ID.Name)) then
632 List.Add(Info.ID.IDAsString);
633 end;
634 end;
635 finally
636 LPKInfoCache.LeaveCritSection;
637 end;
638 // fill tree view through FilterEdit
639 FilteredBranch := AvailableFilterEdit.GetCleanBranch(Nil); // All items are top level.
640 for i:=0 to List.Count-1 do
641 FilteredBranch.AddNodeData(List[i],nil);
642 finally
643 List.Free;
644 end;
645 AvailableFilterEdit.InvalidateFilter;
646 end;
647
648 procedure TInstallPkgSetDialog.UpdateNewInstalledPackages;
649 var
650 NewPackageID: TLazPackageID;
651 APackage: TLazPackage;
652 FilteredBranch: TTreeFilterBranch;
653 List: TStringListUTF8Fast;
654 i: Integer;
655 begin
656 List:=TStringListUTF8Fast.Create;
657 try
658 for i:=0 to FNewInstalledPackages.Count-1 do begin
659 NewPackageID:=TLazPackageID(FNewInstalledPackages[i]);
660 APackage:=PackageGraph.FindPackageWithName(NewPackageID.Name,nil);
661 if APackage<>nil then
662 NewPackageID:=APackage;
663 List.Add(NewPackageID.IDAsString);
664 end;
665 List.Sort;
666 // fill tree view through FilterEdit
667 FilteredBranch := InstalledFilterEdit.GetCleanBranch(Nil); // All items are top level.
668 for i:=0 to List.Count-1 do
669 FilteredBranch.AddNodeData(List[i],nil);
670 finally
671 List.Free;
672 end;
673 InstalledFilterEdit.InvalidateFilter;
674 end;
675
676 procedure TInstallPkgSetDialog.PkgInfosChanged;
677 // called in mainthread after package parser helper thread finished
678 begin
679 UpdateAvailablePackages;
680 end;
681
682 procedure TInstallPkgSetDialog.ChangePkgVersion(PkgInfo: TLPKInfo;
683 NewVersion: TPkgVersion);
684 // called by LPKInfoCache when a lpk has a different version than the IDE list
685 var
686 OldID, NewID: String;
687
688 procedure ChangeTV(TV: TTreeView);
689 var
690 i: Integer;
691 Node: TTreeNode;
692 begin
693 for i:=0 to TV.Items.TopLvlCount-1 do begin
694 Node:=TV.Items.TopLvlItems[i];
695 if Node.Text=OldID then
696 Node.Text:=NewID;
697 end;
698 end;
699
700 begin
701 OldID:=PkgInfo.ID.IDAsString;
702 NewID:=PkgInfo.ID.Name+' '+NewVersion.AsString;
703 ChangeTV(AvailableTreeView);
704 ChangeTV(InstallTreeView);
705 end;
706
TInstallPkgSetDialog.DependencyToStrnull707 function TInstallPkgSetDialog.DependencyToStr(Dependency: TPkgDependency): string;
708 begin
709 Result:='';
710 if Dependency=nil then exit;
711 if (Dependency.LoadPackageResult=lprSuccess)
712 and (Dependency.RequiredPackage<>nil) then
713 Result:=Dependency.RequiredPackage.IDAsString
714 else
715 Result:=Dependency.PackageName;
716 end;
717
718 procedure TInstallPkgSetDialog.ClearNewInstalledPackages;
719 begin
720 FNewInstalledPackages.Clear;
721 end;
722
TInstallPkgSetDialog.CheckSelectionnull723 function TInstallPkgSetDialog.CheckSelection: boolean;
724 begin
725 OnCheckInstallPackageList(FNewInstalledPackages,true,Result);
726 end;
727
728 procedure TInstallPkgSetDialog.UpdateButtonStates;
729 var
730 Cnt: Integer;
731 Dependency: TPkgDependency;
732 s: String;
733 ListChanged: Boolean;
734 begin
735 UninstallButton.Enabled:=InstallTreeView.Selected<>nil;
736 AddToInstallButton.Enabled:=AvailableTreeView.Selected<>nil;
737 // check for changes
738 ListChanged:=false;
739 Cnt:=0;
740 Dependency:=OldInstalledPackages;
741 while Dependency<>nil do begin
742 s:=Dependency.PackageName;
743 if not PackageInInstallList(s) then begin
744 ListChanged:=true;
745 break;
746 end;
747 Dependency:=Dependency.NextRequiresDependency;
748 inc(Cnt);
749 end;
750 if InstallTreeView.Items.TopLvlCount<>Cnt then
751 ListChanged:=true;
752 SaveAndExitButton.Enabled:=ListChanged;
753 SaveAndRebuildButton.Enabled:=ListChanged;
754 end;
755
756 procedure TInstallPkgSetDialog.UpdatePackageInfo(Tree: TTreeView);
757 var
758 InfoStr: string;
759
760 procedure AddState(const NewState: string);
761 begin
762 if (InfoStr<>'') and (InfoStr[length(InfoStr)]<>' ') then
763 InfoStr:=InfoStr+', ';
764 InfoStr:=InfoStr+NewState;
765 end;
766
767 var
768 PkgID: String;
769 Info: TLPKInfo;
770 PackageLink: TPackageLink;
771 Author, Description, License: String;
772 begin
773 if Tree = nil then Exit;
774 PkgID := '';
775 if Tree.Selected <> nil then
776 PkgID := Tree.Selected.Text;
777 if PkgID = '' then Exit;
778 if Tree = InstallTreeView then
779 PackageLink := nil
780 else
781 PackageLink := FindOnlinePackageLink(PkgID);
782 LPKInfoCache.EnterCritSection;
783 try
784 Info:=LPKInfoCache.FindPkgInfoWithIDAsString(PkgID);
785 if ((Info=nil) and (FSelectedPkgID=''))
786 or ((Info<>nil) and (Info.ID.IDAsString=FSelectedPkgID)
787 and (Info.LPKParsed=FSelectedPkgState))
788 then
789 exit; // no change
790 PkgInfoMemo.Clear;
791 PkgInfoMemoLicense.Clear;
792 if (Info=nil) then begin
793 FSelectedPkgID:='';
794 exit;
795 end;
796 FSelectedPkgID:=PkgID;
797
798 if Info.LPKParsed=lpkiNotParsed then begin
799 LPKInfoCache.ParseLPKInfoInMainThread(Info);
800 if FSelectedPkgID='' then begin
801 // version has changed
802 // => has already triggered an update
803 exit;
804 end;
805 end;
806
807 if PackageLink = nil then
808 begin
809 Author := Info.Author;
810 Description := Trim(Info.Description);
811 License := Info.License;
812 end
813 else
814 begin
815 Author := PackageLink.Author;
816 Description := Trim(PackageLink.Description);
817 License := PackageLink.License;
818 end;
819
820 if Description<>'' then // Description is the most interesting piece.
821 PkgInfoMemo.Lines.Add(Description); // Put it first.
822 PkgInfoMemo.Lines.Add('');
823 if Author<>'' then
824 PkgInfoMemo.Lines.Add(lisPckOptsAuthor + ': ' + Author); // Author
825 PkgInfoMemo.Lines.Add(Format(lisOIPFilename, [Info.LPKFilename])); // Pkg name
826
827 if License<>'' then // License has its own memo.
828 PkgInfoMemoLicense.Lines.Add(lisPckOptsLicense + ': ' + License);
829
830 InfoStr:=lisCurrentState;
831 if Info.Installed<>pitNope then
832 begin
833 if PackageInInstallList(Info.ID.Name)=false then
834 AddState(lisSelectedForUninstallation);
835 AddState(lisInstalled);
836 end
837 else
838 begin
839 if PackageInInstallList(Info.ID.Name)=true then
840 AddState(lisSelectedForInstallation);
841 AddState(lisNotInstalled);
842 if PackageLink <> nil then
843 AddState(lisOnlinePackage);
844 end;
845 if Info.Base then
846 AddState(lisPckExplBase);
847 AddState(LazPackageTypeIdents[Info.PkgType]);
848 PkgInfoMemo.Lines.Add(InfoStr);
849 PkgInfoMemo.SelStart := 1;
850 PkgInfoMemoLicense.SelStart := 1;
851 finally
852 LPKInfoCache.LeaveCritSection;
853 end;
854 end;
855
TInstallPkgSetDialog.NewInstalledPackagesContainsnull856 function TInstallPkgSetDialog.NewInstalledPackagesContains(
857 APackageID: TLazPackageID): boolean;
858 begin
859 Result:=IndexOfNewInstalledPackageID(APackageID)>=0;
860 end;
861
IndexOfNewInstalledPackageIDnull862 function TInstallPkgSetDialog.IndexOfNewInstalledPackageID(
863 APackageID: TLazPackageID): integer;
864 begin
865 Result:=FNewInstalledPackages.Count-1;
866 while (Result>=0)
867 and (TLazPackageID(FNewInstalledPackages[Result]).Compare(APackageID)<>0) do
868 dec(Result);
869 end;
870
IndexOfNewInstalledPkgByNamenull871 function TInstallPkgSetDialog.IndexOfNewInstalledPkgByName(
872 const APackageName: string): integer;
873 begin
874 Result:=FNewInstalledPackages.Count-1;
875 while (Result>=0)
876 and (CompareText(TLazPackageID(FNewInstalledPackages[Result]).Name,
877 APackageName)<>0)
878 do
879 dec(Result);
880 end;
881
882 procedure TInstallPkgSetDialog.SavePackageListToFile(const AFilename: string);
883 var
884 XMLConfig: TXMLConfig;
885 i: Integer;
886 LazPackageID: TLazPackageID;
887 begin
888 try
889 XMLConfig:=TXMLConfig.CreateClean(AFilename);
890 try
891 XMLConfig.SetDeleteValue('Packages/Count',FNewInstalledPackages.Count,0);
892 for i:=0 to FNewInstalledPackages.Count-1 do begin
893 LazPackageID:=TLazPackageID(FNewInstalledPackages[i]);
894 XMLConfig.SetDeleteValue('Packages/Item'+IntToStr(i)+'/ID',
895 LazPackageID.IDAsString,'');
896 end;
897 InvalidateFileStateCache;
898 XMLConfig.Flush;
899 finally
900 XMLConfig.Free;
901 end;
902 except
903 on E: Exception do begin
904 MessageDlg(lisCodeToolsDefsWriteError,
905 Format(lisErrorWritingPackageListToFile,[LineEnding,AFilename,LineEnding,E.Message]),
906 mtError, [mbCancel], 0);
907 end;
908 end;
909 end;
910
911 procedure TInstallPkgSetDialog.LoadPackageListFromFile(const AFilename: string);
912
PkgNameExistsnull913 function PkgNameExists(List: TObjectList; ID: TLazPackageID): boolean;
914 var
915 i: Integer;
916 LazPackageID: TLazPackageID;
917 begin
918 if List<>nil then
919 for i:=0 to List.Count-1 do begin
920 LazPackageID:=TLazPackageID(List[i]);
921 if CompareText(LazPackageID.Name,ID.Name)=0 then begin
922 Result:=true;
923 exit;
924 end;
925 end;
926 Result:=false;
927 end;
928
929 var
930 XMLConfig: TXMLConfig;
931 i: Integer;
932 LazPackageID: TLazPackageID;
933 NewCount: LongInt;
934 NewList: TObjectList;
935 ID: String;
936 begin
937 NewList:=nil;
938 LazPackageID:=nil;
939 try
940 XMLConfig:=TXMLConfig.Create(AFilename);
941 try
942 NewCount:=XMLConfig.GetValue('Packages/Count',0);
943 LazPackageID:=TLazPackageID.Create;
944 for i:=0 to NewCount-1 do begin
945 // get ID
946 ID:=XMLConfig.GetValue('Packages/Item'+IntToStr(i)+'/ID','');
947 if ID='' then continue;
948 // parse ID
949 if not LazPackageID.StringToID(ID) then continue;
950 // ignore doubles
951 if PkgNameExists(NewList,LazPackageID) then continue;
952 // add
953 if NewList=nil then NewList:=TObjectList.Create(true);
954 NewList.Add(LazPackageID);
955 LazPackageID:=TLazPackageID.Create;
956 end;
957 // clean up old list
958 ClearNewInstalledPackages;
959 FNewInstalledPackages.Free;
960 // assign new list
961 FNewInstalledPackages:=NewList;
962 NewList:=nil;
963 UpdateNewInstalledPackages;
964 UpdateAvailablePackages;
965 UpdateButtonStates;
966 finally
967 XMLConfig.Free;
968 LazPackageID.Free;
969 NewList.Free;
970 end;
971 except
972 on E: Exception do begin
973 MessageDlg(lisCodeToolsDefsReadError,
974 Format(lisErrorReadingPackageListFromFile,[LineEnding,AFilename,LineEnding,E.Message]),
975 mtError, [mbCancel], 0);
976 end;
977 end;
978 end;
979
ExtractNameFromPkgIDnull980 function TInstallPkgSetDialog.ExtractNameFromPkgID(ID: string): string;
981 begin
982 if ID='' then
983 Result:=''
984 else
985 Result:=GetIdentifier(PChar(ID));
986 end;
987
988 procedure TInstallPkgSetDialog.AddToInstall;
989
SelectionOknull990 function SelectionOk(aPackageID: TLazPackageID): Boolean;
991 var
992 APackage: TLazPackage;
993 ConflictDep: TPkgDependency;
994 i: Integer;
995 begin
996 // check if already in list
997 if NewInstalledPackagesContains(aPackageID) then begin
998 MessageDlg(lisDuplicate,
999 Format(lisThePackageIsAlreadyInTheList, [aPackageID.Name]),
1000 mtError, [mbCancel],0);
1001 exit(false);
1002 end;
1003 // check if a package with same name is already in the list
1004 i:=IndexOfNewInstalledPkgByName(aPackageID.Name);
1005 if i>=0 then begin
1006 MessageDlg(lisConflict,
1007 Format(lisThereIsAlreadyAPackageInTheList, [aPackageID.Name]),
1008 mtError,[mbCancel],0);
1009 exit(false);
1010 end;
1011 // check if package is loaded and has some attributes that prevents
1012 // installation in the IDE
1013 APackage:=PackageGraph.FindPackageWithID(aPackageID);
1014 if APackage<>nil then begin
1015 if APackage.PackageType in [lptRunTime,lptRunTimeOnly] then begin
1016 IDEMessageDialog(lisNotADesigntimePackage,
1017 Format(lisThePackageIsNotADesignTimePackageItCanNotBeInstall,
1018 [APackage.IDAsString]),
1019 mtError, [mbCancel]);
1020 exit(false);
1021 end;
1022 ConflictDep:=PackageGraph.FindRuntimePkgOnlyRecursively(
1023 APackage.FirstRequiredDependency);
1024 if ConflictDep<>nil then begin
1025 IDEMessageDialog(lisNotADesigntimePackage,
1026 Format(lisThePackageCanNotBeInstalledBecauseItRequiresWhichI,
1027 [APackage.Name, ConflictDep.AsString]),
1028 mtError, [mbCancel]);
1029 exit(false);
1030 end;
1031 end;
1032 Result:=true;
1033 end;
1034
1035 var
1036 i, j: Integer;
1037 NewSelectedIndex, LastNonSelectedIndex: Integer;
1038 NewPackageID: TLazPackageID;
1039 Additions: TObjectList;
1040 AddedPkgNames: TStringList;
1041 TVNode: TTreeNode;
1042 PkgName: String;
1043 FilteredBranch: TTreeFilterBranch;
1044 PkgLinks: TList;
1045 PkgLinksStr: String;
1046 PkgLink: TPackageLink;
1047 begin
1048 NewSelectedIndex:=-1;
1049 LastNonSelectedIndex:=-1;
1050 Additions:=TObjectList.Create(false);
1051 AddedPkgNames:=TStringList.Create;
1052 PkgLinks := TList.Create;
1053 NewPackageID:=TLazPackageID.Create;
1054 FilteredBranch := AvailableFilterEdit.GetExistingBranch(Nil); // All items are top level.
1055 try
1056 for i:=0 to AvailableTreeView.Items.TopLvlCount-1 do
1057 begin
1058 TVNode:=AvailableTreeView.Items.TopLvlItems[i];
1059 if not TVNode.MultiSelected then begin
1060 LastNonSelectedIndex:=i;
1061 continue;
1062 end;
1063 NewSelectedIndex:=i+1; // Will have the next index after the selected one.
1064 PkgName:=TVNode.Text;
1065 // Convert package name to ID and check it
1066 if not NewPackageID.StringToID(PkgName) then begin
1067 TVNode.Selected:=false;
1068 DebugLn('TInstallPkgSetDialog.AddToInstall invalid ID: ', PkgName);
1069 continue;
1070 end;
1071 if not SelectionOk(NewPackageID) then begin
1072 TVNode.Selected:=false;
1073 exit;
1074 end;
1075 // ok => add to list
1076 PkgLink := FindOnlinePackageLink(NewPackageID.Name);
1077 if PkgLink <> nil then begin
1078 if not FileExists(PkgLink.OPMFileName) then
1079 PkgLinks.Add(PkgLink)
1080 else
1081 begin
1082 PkgLink := LazPackageLinks.AddUserLink(PkgLink.OPMFileName, PkgLink.Name);
1083 if PkgLink <> nil then
1084 LazPackageLinks.SaveUserLinks;
1085 Additions.Add(NewPackageID);
1086 NewPackageID:=TLazPackageID.Create;
1087 AddedPkgNames.Add(PkgName);
1088 end;
1089 end else begin
1090 Additions.Add(NewPackageID);
1091 NewPackageID:=TLazPackageID.Create;
1092 AddedPkgNames.Add(PkgName);
1093 end;
1094 end;
1095 //download online packages
1096 if (OPMInterface <> nil) and (PkgLinks.Count > 0) then
1097 begin
1098 PkgLinksStr := '';
1099 for I := 0 to PkgLinks.Count - 1 do begin
1100 if PkgLinksStr = '' then
1101 PkgLinksStr := '"' + TPackageLink(PkgLinks.Items[I]).Name + '"'
1102 else
1103 PkgLinksStr := PkgLinksStr + ', ' + '"' + TPackageLink(PkgLinks.Items[I]).Name + '"';
1104 end;
1105 if IDEMessageDialog(lisDownload, Format(lisDonwloadOnlinePackages, [PkgLinksStr]), mtConfirmation, [mbYes, mbNo]) = mrYes then begin
1106 if OPMInterface.DownloadPackages(PkgLinks) = mrOK then begin
1107 for I := PkgLinks.Count - 1 downto 0 do begin
1108 if OPMInterface.IsPackageAvailable(TPackageLink(PkgLinks.Items[I]), 1) then begin
1109 Additions.Add(NewPackageID);
1110 NewPackageID:=TLazPackageID.Create;
1111 AddedPkgNames.Add(PkgName);
1112 PkgLink := LazPackageLinks.AddUserLink(TPackageLink(PkgLinks.Items[I]).OPMFileName, TPackageLink(PkgLinks.Items[I]).Name);
1113 if PkgLink <> nil then
1114 LazPackageLinks.SaveUserLinks;
1115 end;
1116 end;
1117 end
1118 else
1119 Dec(NewSelectedIndex);
1120 end
1121 else
1122 Dec(NewSelectedIndex);
1123 end;
1124 // all ok => add to installed packages
1125 for i:=0 to Additions.Count-1 do
1126 FNewInstalledPackages.Add(Additions[i]);
1127 for i:=0 to AddedPkgNames.Count-1 do begin
1128 j:=FilteredBranch.Items.IndexOf(AddedPkgNames[i]);
1129 Assert(j<>-1, 'TInstallPkgSetDialog.AddToInstall: '+AddedPkgNames[i]+' not found in Filter items.');
1130 FilteredBranch.Items.Delete(j);
1131 end;
1132 // Don't call UpdateAvailablePackages here, only the selected nodes were removed.
1133 UpdateNewInstalledPackages;
1134 UpdateButtonStates;
1135 if ((NewSelectedIndex=-1) or (NewSelectedIndex=AvailableTreeView.Items.TopLvlCount)) then
1136 NewSelectedIndex:=LastNonSelectedIndex;
1137 if NewSelectedIndex<>-1 then
1138 AvailableTreeView.Items.TopLvlItems[NewSelectedIndex].Selected:=True;
1139 AvailableFilterEdit.InvalidateFilter;
1140 finally
1141 NewPackageID.Free;
1142 AddedPkgNames.Free;
1143 Additions.Free;
1144 PkgLinks.Free;
1145 end;
1146 end;
1147
1148 procedure TInstallPkgSetDialog.AddToUninstall;
1149
1150 function SelectionOk(aPackageID: TLazPackageID): Boolean;
1151 var
1152 APackage: TLazPackage;
1153 begin
1154 APackage:=PackageGraph.FindPackageWithID(aPackageID);
1155 if APackage<>nil then begin
1156 // check if package is a base package
1157 if PackageGraph.IsStaticBasePackage(APackage.Name) then begin
1158 MessageDlg(lisUninstallImpossible,
1159 Format(lisThePackageCanNotBeUninstalledBecauseItIsNeededByTh, [
1160 APackage.Name]), mtError, [mbCancel], 0);
1161 exit(false);
1162 end;
1163 end;
1164 Result:=true;
1165 end;
1166
1167 var
1168 i, j: Integer;
1169 NewSelectedIndex, LastNonSelectedIndex: Integer;
1170 DelPackageID, PackID: TLazPackageID;
1171 Deletions: TObjectList;
1172 DeletedPkgNames: TStringList;
1173 TVNode: TTreeNode;
1174 PkgName: String;
1175 FilteredBranch: TTreeFilterBranch;
1176 begin
1177 NewSelectedIndex:=-1;
1178 LastNonSelectedIndex:=-1;
1179 Deletions:=TObjectList.Create(true);
1180 DeletedPkgNames:=TStringList.Create;
1181 DelPackageID:=TLazPackageID.Create;
1182 FilteredBranch := InstalledFilterEdit.GetExistingBranch(Nil); // All items are top level.
1183 try
1184 for i:=0 to InstallTreeView.Items.TopLvlCount-1 do begin
1185 TVNode:=InstallTreeView.Items.TopLvlItems[i];
1186 if not TVNode.MultiSelected then begin
1187 LastNonSelectedIndex:=i;
1188 continue;
1189 end;
1190 NewSelectedIndex:=i+1; // Will have the next index after the selected one.
1191 PkgName:=TVNode.Text;
1192 if not DelPackageID.StringToID(PkgName) then begin
1193 TVNode.Selected:=false;
1194 debugln('TInstallPkgSetDialog.AddToUninstall invalid ID: ', PkgName);
1195 continue;
1196 end;
1197 if not SelectionOk(DelPackageID) then begin
1198 TVNode.Selected:=false;
1199 exit;
1200 end;
1201 // ok => add to deletions
1202 Deletions.Add(DelPackageID);
1203 DelPackageID:=TLazPackageID.Create;
1204 DeletedPkgNames.Add(PkgName);
1205 end;
1206
1207 // ok => remove from installed packages
1208 InstallTreeView.Selected:=nil;
1209 for i:=0 to Deletions.Count-1 do begin
1210 PackID:=TLazPackageID(Deletions[i]);
1211 j:=IndexOfNewInstalledPackageID(PackID);
1212 FNewInstalledPackages.Delete(j);
1213 end;
1214 for i:=0 to DeletedPkgNames.Count-1 do begin
1215 j:=FilteredBranch.Items.IndexOf(DeletedPkgNames[i]);
1216 Assert(j<>-1, 'TInstallPkgSetDialog.AddToUninstall: '+DeletedPkgNames[i]+' not found in Filter items.');
1217 FilteredBranch.Items.Delete(j);
1218 end;
1219
1220 // Don't call UpdateNewInstalledPackages here, only the selected nodes were removed.
1221 UpdateAvailablePackages;
1222 UpdateButtonStates;
1223 if ((NewSelectedIndex=-1) or (NewSelectedIndex=InstallTreeView.Items.TopLvlCount)) then
1224 NewSelectedIndex:=LastNonSelectedIndex;
1225 if NewSelectedIndex<>-1 then
1226 InstallTreeView.Items.TopLvlItems[NewSelectedIndex].Selected:=True;
1227 InstalledFilterEdit.InvalidateFilter;
1228 finally
1229 DelPackageID.Free;
1230 DeletedPkgNames.Free;
1231 Deletions.Free;
1232 end;
1233 end;
1234
GetNewInstalledPackagesnull1235 function TInstallPkgSetDialog.GetNewInstalledPackages: TObjectList;
1236 var
1237 i: Integer;
1238 NewPackageID: TLazPackageID;
1239 begin
1240 Result:=TObjectList.Create(true);
1241 for i:=0 to FNewInstalledPackages.Count-1 do begin
1242 NewPackageID:=TLazPackageID.Create;
1243 NewPackageID.AssignID(TLazPackageID(FNewInstalledPackages[i]));
1244 Result.Add(NewPackageID);
1245 end;
1246 end;
1247
1248 end.
1249
1250