mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-05 22:58:12 +02:00
1370 lines
43 KiB
ObjectPascal
1370 lines
43 KiB
ObjectPascal
{
|
|
/***************************************************************************
|
|
installpkgsetdlg.pas
|
|
--------------------
|
|
|
|
|
|
***************************************************************************/
|
|
|
|
***************************************************************************
|
|
* *
|
|
* This source is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This code is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
* General Public License for more details. *
|
|
* *
|
|
* A copy of the GNU General Public License is available on the World *
|
|
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
|
|
* obtain it by writing to the Free Software Foundation, *
|
|
* Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
|
|
* *
|
|
***************************************************************************
|
|
|
|
Author: Mattias Gaertner
|
|
|
|
Abstract:
|
|
Dialog to edit the package set installed in the IDE.
|
|
}
|
|
unit InstallPkgSetDlg;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, Contnrs, AVL_Tree,
|
|
// LCL
|
|
LCLType, Forms, Controls, Graphics, Dialogs, StdCtrls, Buttons,
|
|
ExtCtrls, ComCtrls, ImgList,
|
|
// LazControls
|
|
TreeFilterEdit,
|
|
// Codetools
|
|
BasicCodeTools,
|
|
// LazUtils
|
|
LazFileUtils, Laz2_XMLCfg, LazUTF8, LazLoggerBase,
|
|
// BuildIntf
|
|
PackageIntf, PackageLinkIntf, PackageDependencyIntf,
|
|
// IdeIntf
|
|
IDEImagesIntf, IdeIntfStrConsts, IDEHelpIntf, IDEDialogs, IDEWindowIntf, InputHistory,
|
|
// IdeConfig
|
|
LazConf,
|
|
// IDE
|
|
LazarusIDEStrConsts, PackageDefs, PackageSystem, LPKCache, PackageLinks;
|
|
|
|
type
|
|
|
|
{ TInstallPkgSetDialog }
|
|
|
|
TInstallPkgSetDialog = class(TForm)
|
|
AddToInstallButton: TBitBtn;
|
|
AvailableTreeView: TTreeView;
|
|
AvailablePkgGroupBox: TGroupBox;
|
|
MiddleBevel: TBevel;
|
|
HelpButton: TBitBtn;
|
|
CancelButton: TBitBtn;
|
|
ExportButton: TButton;
|
|
BtnPanel: TPanel;
|
|
InstallTreeView: TTreeView;
|
|
AvailableFilterEdit: TTreeFilterEdit;
|
|
LPKParsingTimer: TTimer;
|
|
NoteLabel: TLabel;
|
|
Panel1: TPanel;
|
|
Panel2: TPanel;
|
|
PkgInfoMemo: TMemo;
|
|
PkgInfoGroupBox: TGroupBox;
|
|
ImportButton: TButton;
|
|
PkgInfoMemoLicense: TMemo;
|
|
SaveAndExitButton: TBitBtn;
|
|
InstallPkgGroupBox: TGroupBox;
|
|
SaveAndRebuildButton: TBitBtn;
|
|
InstalledFilterEdit: TTreeFilterEdit;
|
|
Splitter1: TSplitter;
|
|
Splitter2: TSplitter;
|
|
UninstallButton: TBitBtn;
|
|
procedure AvailableTreeViewKeyDown(Sender: TObject; var Key: Word;
|
|
Shift: TShiftState);
|
|
function FilterEditGetImageIndex({%H-}Str: String; {%H-}Data: TObject;
|
|
var {%H-}AIsEnabled: Boolean): Integer;
|
|
procedure FormClose(Sender: TObject; var {%H-}CloseAction: TCloseAction);
|
|
procedure FormDropFiles(Sender: TObject; const FileNames: array of string);
|
|
procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
|
|
procedure InstallTreeViewKeyDown(Sender: TObject; var Key: Word;
|
|
Shift: TShiftState);
|
|
procedure LPKParsingTimerTimer(Sender: TObject);
|
|
procedure OnAllLPKParsed(Sender: TObject);
|
|
procedure OnIdle(Sender: TObject; var {%H-}Done: Boolean);
|
|
procedure TreeViewAdvancedCustomDrawItem(Sender: TCustomTreeView;
|
|
Node: TTreeNode; {%H-}State: TCustomDrawState; Stage: TCustomDrawStage;
|
|
var PaintImages, {%H-}DefaultDraw: Boolean);
|
|
procedure AvailableTreeViewSelectionChanged(Sender: TObject);
|
|
procedure ExportButtonClick(Sender: TObject);
|
|
procedure HelpButtonClick(Sender: TObject);
|
|
procedure ImportButtonClick(Sender: TObject);
|
|
procedure SaveAndRebuildButtonClick(Sender: TObject);
|
|
procedure InstallPkgSetDialogCreate(Sender: TObject);
|
|
procedure InstallPkgSetDialogDestroy(Sender: TObject);
|
|
procedure InstallPkgSetDialogShow(Sender: TObject);
|
|
procedure InstallPkgSetDialogResize(Sender: TObject);
|
|
procedure InstallTreeViewSelectionChanged(Sender: TObject);
|
|
procedure SaveAndExitButtonClick(Sender: TObject);
|
|
procedure AddToInstall(Sender: TObject);
|
|
procedure AddToUninstall(Sender: TObject);
|
|
private
|
|
FIdleConnected: boolean;
|
|
FNewInstalledPackages: TObjectList; // list of TLazPackageID (not TLazPackage)
|
|
FOldInstalledPackages: TPkgDependency;
|
|
FRebuildIDE: boolean;
|
|
FSelectedPkgState: TLPKInfoState;
|
|
FSelectedPkgID: string;
|
|
fAvailablePkgsNeedUpdate: boolean;
|
|
ImgIndexPackage: integer;
|
|
ImgIndexInstallPackage: integer;
|
|
ImgIndexInstalledPackage: integer;
|
|
ImgIndexUninstallPackage: integer;
|
|
ImgIndexCirclePackage: integer;
|
|
ImgIndexMissingPackage: integer;
|
|
ImgIndexAvailableOnline: integer;
|
|
ImgIndexOverlayUnknown: integer;
|
|
ImgIndexOverlayBasePackage: integer;
|
|
ImgIndexOverlayFPCPackage: integer;
|
|
ImgIndexOverlayLazarusPackage: integer;
|
|
ImgIndexOverlayDesigntimePackage: integer;
|
|
ImgIndexOverlayRuntimePackage: integer;
|
|
procedure SetIdleConnected(AValue: boolean);
|
|
procedure SetOldInstalledPackages(const AValue: TPkgDependency);
|
|
procedure AssignOldInstalledPackagesToList;
|
|
function PackageInInstallList(PkgName: string): boolean;
|
|
function GetPkgImgIndex(Installed: TPackageInstallType; InInstallList,
|
|
IsOnline: boolean): integer;
|
|
procedure UpdateAvailablePackages(Immediately: boolean = false);
|
|
procedure UpdateNewInstalledPackages;
|
|
function DependencyToStr(Dependency: TPkgDependency): string;
|
|
procedure ClearNewInstalledPackages;
|
|
function CheckSelection: boolean;
|
|
procedure UpdateButtonStates;
|
|
procedure UpdatePackageInfo(Tree: TTreeView);
|
|
function NewInstalledPackagesContains(APackageID: TLazPackageID): boolean;
|
|
function IndexOfNewInstalledPackageID(APackageID: TLazPackageID): integer;
|
|
function IndexOfNewInstalledPkgByName(const APackageName: string): integer;
|
|
procedure SavePackageListToFile(const AFilename: string);
|
|
procedure LoadPackageListFromFile(const AFilename: string);
|
|
function ExtractNameFromPkgID(ID: string): string;
|
|
procedure PkgInfosChanged;
|
|
procedure ChangePkgVersion(PkgInfo: TLPKInfo; NewVersion: TPkgVersion);
|
|
function FindOnlinePackageLink(const AName: String): TPackageLink;
|
|
function IsBasePkg(aPkgID: TLazPackageID): boolean;
|
|
function IsBasePkg(aPkgName: string): boolean;
|
|
public
|
|
function GetNewInstalledPackages: TObjectList;
|
|
property OldInstalledPackages: TPkgDependency read FOldInstalledPackages
|
|
write SetOldInstalledPackages;
|
|
property NewInstalledPackages: TObjectList read FNewInstalledPackages; // list of TLazPackageID
|
|
property RebuildIDE: boolean read FRebuildIDE write FRebuildIDE;
|
|
property IdleConnected: boolean read FIdleConnected write SetIdleConnected;
|
|
end;
|
|
|
|
function ShowEditInstallPkgsDialog(
|
|
OldInstalledPackages: TPkgDependency;
|
|
out NewInstalledPackages: TObjectList; // list of TLazPackageID (must be freed, may be null)
|
|
out RebuildIDE: boolean
|
|
): boolean;
|
|
|
|
implementation
|
|
|
|
{$R *.lfm}
|
|
|
|
procedure SetControlsWidthOnMax(AControls: array of TControl);
|
|
var
|
|
i, MaxWidth: Integer;
|
|
begin
|
|
MaxWidth:=0;
|
|
for i:=Low(AControls) to High(AControls) do
|
|
if AControls[i].Width>MaxWidth then
|
|
MaxWidth:=AControls[i].Width;
|
|
for i:=Low(AControls) to High(AControls) do
|
|
AControls[i].Constraints.MinWidth:=MaxWidth; // AutoSize=True
|
|
end;
|
|
|
|
function ShowEditInstallPkgsDialog(
|
|
OldInstalledPackages: TPkgDependency;
|
|
out NewInstalledPackages: TObjectList; // list of TLazPackageID (must be freed, may be null)
|
|
out RebuildIDE: boolean
|
|
): boolean;
|
|
var
|
|
InstallPkgSetDialog: TInstallPkgSetDialog;
|
|
begin
|
|
result := false;
|
|
NewInstalledPackages := nil;
|
|
RebuildIDE := false;
|
|
InstallPkgSetDialog := TInstallPkgSetDialog.Create(nil);
|
|
try
|
|
InstallPkgSetDialog.OldInstalledPackages := OldInstalledPackages;
|
|
result := InstallPkgSetDialog.ShowModal = mrOK;
|
|
if result then
|
|
begin
|
|
NewInstalledPackages := InstallPkgSetDialog.GetNewInstalledPackages;
|
|
RebuildIDE := InstallPkgSetDialog.RebuildIDE;
|
|
end;
|
|
finally
|
|
InstallPkgSetDialog.Free;
|
|
end;
|
|
end;
|
|
|
|
{ TInstallPkgSetDialog }
|
|
|
|
procedure TInstallPkgSetDialog.InstallPkgSetDialogCreate(Sender: TObject);
|
|
begin
|
|
IDEDialogLayoutList.ApplyLayout(Self);
|
|
|
|
{ Captions }
|
|
|
|
Self .Caption := lisInstallUninstallPackages;
|
|
NoteLabel .Caption := lisIDECompileAndRestart;
|
|
AvailablePkgGroupBox.Caption := lisAvailableForInstallation;
|
|
ExportButton .Caption := lisExport;
|
|
ImportButton .Caption := lisImport;
|
|
UninstallButton .Caption := lisUninstallSelection;
|
|
InstallPkgGroupBox .Caption := lisPckEditInstall;
|
|
AddToInstallButton .Caption := lisInstallSelection;
|
|
PkgInfoGroupBox .Caption := lisPackageInfo;
|
|
SaveAndRebuildButton.Caption := lisSaveAndRebuildIDE;
|
|
SaveAndExitButton .Caption := lisSaveAndExitDialog;
|
|
CancelButton .Caption := lisCancel;
|
|
HelpButton .Caption := lisMenuHelp;
|
|
|
|
{ Images }
|
|
|
|
InstallTreeView .Images := IDEImages.Images_16;
|
|
AvailableTreeView.Images := IDEImages.Images_16;
|
|
|
|
IDEImages.AssignImage(AddToInstallButton, 'arrow__darkgreen_left');
|
|
IDEImages.AssignImage(UninstallButton , 'arrow__darkred_right');
|
|
|
|
ImgIndexPackage := IDEImages.LoadImage('item_package');
|
|
ImgIndexInstalledPackage := IDEImages.LoadImage('pkg_installed');
|
|
ImgIndexInstallPackage := IDEImages.LoadImage('pkg_package_autoinstall');
|
|
ImgIndexUninstallPackage := IDEImages.LoadImage('pkg_package_uninstall');
|
|
ImgIndexCirclePackage := IDEImages.LoadImage('pkg_package_circle');
|
|
ImgIndexMissingPackage := IDEImages.LoadImage('pkg_conflict');
|
|
ImgIndexAvailableOnline := IDEImages.LoadImage('pkg_install');
|
|
ImgIndexOverlayUnknown := IDEImages.LoadImage('state_unknown');
|
|
ImgIndexOverlayBasePackage := IDEImages.LoadImage('pkg_core_overlay');
|
|
ImgIndexOverlayFPCPackage := IDEImages.LoadImage('pkg_fpc_overlay');
|
|
ImgIndexOverlayLazarusPackage := IDEImages.LoadImage('pkg_lazarus_overlay');
|
|
ImgIndexOverlayDesignTimePackage := IDEImages.LoadImage('pkg_design_overlay');
|
|
ImgIndexOverlayRunTimePackage := IDEImages.LoadImage('pkg_runtime_overlay');
|
|
|
|
{}
|
|
|
|
FNewInstalledPackages := TObjectList.Create(true);
|
|
|
|
LPKInfoCache.AddOnQueueEmpty(@OnAllLPKParsed);
|
|
LPKInfoCache.StartLPKReaderWithAllAvailable;
|
|
|
|
UpdateButtonStates;
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.InstallPkgSetDialogDestroy(Sender: TObject);
|
|
begin
|
|
LPKInfoCache.EndLPKReader;
|
|
LPKInfoCache.RemoveOnQueueEmpty(@OnAllLPKParsed);
|
|
ClearNewInstalledPackages;
|
|
FreeAndNil(FNewInstalledPackages);
|
|
IdleConnected:=false;
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.InstallPkgSetDialogShow(Sender: TObject);
|
|
begin
|
|
InstalledFilterEdit.ResetFilter; // (filter) - TextHint is shown after this.
|
|
AvailableFilterEdit.ResetFilter;
|
|
SetControlsWidthOnMax([UninstallButton, AddToInstallButton]);
|
|
SetControlsWidthOnMax([ImportButton, ExportButton]);
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.SaveAndRebuildButtonClick(Sender: TObject);
|
|
begin
|
|
if SaveAndRebuildButton.Enabled and CheckSelection then
|
|
begin
|
|
RebuildIDE := true;
|
|
ModalResult := mrOk;
|
|
end;
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.AvailableTreeViewSelectionChanged(Sender: TObject);
|
|
begin
|
|
UpdateButtonStates;
|
|
UpdatePackageInfo(AvailableTreeView);
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.ExportButtonClick(Sender: TObject);
|
|
var
|
|
SaveDialog: TSaveDialog;
|
|
AFilename: string;
|
|
begin
|
|
SaveDialog:=TSaveDialog.Create(nil);
|
|
try
|
|
InputHistories.ApplyFileDialogSettings(SaveDialog);
|
|
SaveDialog.InitialDir:=GetPrimaryConfigPath;
|
|
SaveDialog.Title:=lisExportPackageListXml;
|
|
SaveDialog.Filter:=dlgFilterPackageListFiles+'|*.xml;*.txt|'+dlgFilterAll+'|*.*';
|
|
SaveDialog.Options:=SaveDialog.Options+[ofPathMustExist];
|
|
if SaveDialog.Execute then begin
|
|
AFilename:=CleanAndExpandFilename(SaveDialog.Filename);
|
|
if ExtractFileExt(AFilename)='' then
|
|
AFilename:=AFilename+'.xml';
|
|
SavePackageListToFile(AFilename);
|
|
end;
|
|
InputHistories.StoreFileDialogSettings(SaveDialog);
|
|
finally
|
|
SaveDialog.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.HelpButtonClick(Sender: TObject);
|
|
begin
|
|
LazarusHelp.ShowHelpForIDEControl(Self);
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.ImportButtonClick(Sender: TObject);
|
|
var
|
|
OpenDialog: TOpenDialog;
|
|
AFilename: string;
|
|
begin
|
|
OpenDialog:=TOpenDialog.Create(nil);
|
|
try
|
|
InputHistories.ApplyFileDialogSettings(OpenDialog);
|
|
OpenDialog.InitialDir:=GetPrimaryConfigPath;
|
|
OpenDialog.Title:=lisImportPackageListXml;
|
|
OpenDialog.Filter:=dlgFilterPackageListFiles+'|*.xml;*.txt|'+dlgFilterAll+'|*.*';
|
|
OpenDialog.Options:=OpenDialog.Options+[ofPathMustExist,ofFileMustExist];
|
|
if OpenDialog.Execute then begin
|
|
AFilename:=CleanAndExpandFilename(OpenDialog.Filename);
|
|
LoadPackageListFromFile(AFilename);
|
|
end;
|
|
InputHistories.StoreFileDialogSettings(OpenDialog);
|
|
finally
|
|
OpenDialog.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.AvailableTreeViewKeyDown(Sender: TObject;
|
|
var Key: Word; Shift: TShiftState);
|
|
begin
|
|
if (Key = VK_RETURN) and (Shift = []) then
|
|
begin
|
|
AddToInstall(Sender);
|
|
Key := 0;
|
|
end;
|
|
end;
|
|
|
|
function TInstallPkgSetDialog.FilterEditGetImageIndex(Str: String;
|
|
Data: TObject; var AIsEnabled: Boolean): Integer;
|
|
begin
|
|
Result:=0;
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.FormClose(Sender: TObject;
|
|
var CloseAction: TCloseAction);
|
|
begin
|
|
// don't close until parsing is complete
|
|
if LPKParsingTimer.Enabled then
|
|
begin
|
|
CloseAction := caNone;
|
|
exit;
|
|
end;
|
|
|
|
IDEDialogLayoutList.SaveLayout(Self);
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.FormDropFiles(Sender: TObject;
|
|
const FileNames: array of string);
|
|
var
|
|
lFilename: String;
|
|
begin
|
|
if length(FileNames) = 1 then // only one file
|
|
begin
|
|
lFilename := CleanAndExpandFilename(FileNames[0]);
|
|
LoadPackageListFromFile(lFilename);
|
|
end;
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.FormKeyDown(Sender: TObject; var Key: Word;
|
|
Shift: TShiftState);
|
|
begin
|
|
if (Key = VK_ESCAPE) and (Shift = []) then
|
|
begin
|
|
Close;
|
|
Key := 0;
|
|
end
|
|
else if (Key = VK_RETURN) and (Shift = [ssCtrl]) then
|
|
begin
|
|
SaveAndRebuildButtonClick(Sender);
|
|
Key := 0;
|
|
end
|
|
else if (Key = VK_S) and (Shift = [ssCtrl]) then
|
|
begin
|
|
SaveAndExitButtonClick(Sender);
|
|
Key := 0;
|
|
end
|
|
// import and export
|
|
else if (Key = VK_E) and (Shift = [ssCtrl]) then
|
|
begin
|
|
ExportButtonClick(Sender);
|
|
Key := 0;
|
|
end
|
|
else if (Key = VK_I) and (Shift = [ssCtrl]) then
|
|
begin
|
|
ImportButtonClick(Sender);
|
|
Key := 0;
|
|
end
|
|
// scroll description
|
|
else if (Key = VK_DOWN) and (Shift = [ssCtrl]) then
|
|
begin
|
|
PkgInfoMemo.VertScrollBar.Position := PkgInfoMemo.VertScrollBar.Position + 1;
|
|
Key := 0;
|
|
end
|
|
else if (Key = VK_UP) and (Shift = [ssCtrl]) then
|
|
begin
|
|
PkgInfoMemo.VertScrollBar.Position := PkgInfoMemo.VertScrollBar.Position - 1;
|
|
Key := 0;
|
|
end
|
|
// switch focus
|
|
else if (Key = VK_TAB) and (Shift = [ssCtrl]) then
|
|
begin
|
|
if AvailableFilterEdit.Focused then
|
|
InstalledFilterEdit.SetFocus
|
|
else
|
|
AvailableFilterEdit.SetFocus;
|
|
Key := 0;
|
|
end
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.InstallTreeViewKeyDown(Sender: TObject;
|
|
var Key: Word; Shift: TShiftState);
|
|
begin
|
|
if (Key = VK_RETURN) and (Shift = []) then
|
|
begin
|
|
AddToUninstall(Sender);
|
|
Key := 0;
|
|
end;
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.LPKParsingTimerTimer(Sender: TObject);
|
|
begin
|
|
UpdateNewInstalledPackages;
|
|
UpdateAvailablePackages;
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.OnAllLPKParsed(Sender: TObject);
|
|
begin
|
|
LPKParsingTimer.Enabled := false;
|
|
LPKParsingTimerTimer(Sender);
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.OnIdle(Sender: TObject; var Done: Boolean);
|
|
begin
|
|
if fAvailablePkgsNeedUpdate then
|
|
UpdateAvailablePackages(true);
|
|
IdleConnected:=false;
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.TreeViewAdvancedCustomDrawItem(
|
|
Sender: TCustomTreeView; Node: TTreeNode; State: TCustomDrawState;
|
|
Stage: TCustomDrawStage; var PaintImages, DefaultDraw: Boolean);
|
|
var
|
|
Info: TLPKInfo;
|
|
NodeRect: TRect;
|
|
x: Integer;
|
|
Images: TCustomImageList;
|
|
CurCanvas: TCanvas;
|
|
y: Integer;
|
|
Tree: TTreeView;
|
|
InLazSrc: Boolean;
|
|
IsBase: Boolean;
|
|
PkgType: TLazPackageType;
|
|
Installed: TPackageInstallType;
|
|
PkgName: String;
|
|
ImgIndex: Integer;
|
|
Unknown: Boolean;
|
|
PackageLink: TPackageLink;
|
|
ImagesRes: TScaledImageListResolution;
|
|
begin
|
|
Tree:=Sender as TTreeView;
|
|
if Stage=cdPostPaint then begin
|
|
LPKInfoCache.EnterCritSection;
|
|
try
|
|
Info:=LPKInfoCache.FindPkgInfoWithIDAsString(Node.Text);
|
|
if Info=nil then exit;
|
|
PkgName:=Info.ID.Name;
|
|
Unknown:=not (Info.LPKParsed in [lpkiParsed,lpkiParsedError]);
|
|
InLazSrc:=Info.InLazSrc;
|
|
IsBase:=Info.Base;
|
|
PkgType:=Info.PkgType;
|
|
Installed:=Info.Installed;
|
|
finally
|
|
LPKInfoCache.LeaveCritSection;
|
|
end;
|
|
if Sender = InstallTreeView then
|
|
PackageLink := nil
|
|
else
|
|
PackageLink := FindOnlinePackageLink(Info.ID.Name);
|
|
Images:=Tree.Images;
|
|
if Images = nil then exit;
|
|
ImagesRes := Images.ResolutionForPPI[Tree.ImagesWidth, Font.PixelsPerInch, GetCanvasScaleFactor];
|
|
CurCanvas:=Tree.Canvas;
|
|
|
|
NodeRect:=Node.DisplayRect(False);
|
|
x:=Node.DisplayIconLeft+1;
|
|
y:=(NodeRect.Top+NodeRect.Bottom-ImagesRes.Height) div 2;
|
|
// draw image
|
|
ImgIndex:=GetPkgImgIndex(Installed,PackageInInstallList(PkgName), PackageLink <> nil);
|
|
ImagesRes.Draw(CurCanvas,x,y,ImgIndex);
|
|
// draw overlays
|
|
if InLazSrc then
|
|
ImagesRes.Draw(CurCanvas,x,y,ImgIndexOverlayLazarusPackage);
|
|
if IsBase then
|
|
ImagesRes.Draw(CurCanvas,x,y,ImgIndexOverlayBasePackage);
|
|
if PkgType=lptRunTimeOnly then
|
|
ImagesRes.Draw(CurCanvas,x,y,ImgIndexOverlayRuntimePackage);
|
|
if PkgType=lptDesignTime then
|
|
ImagesRes.Draw(CurCanvas,x,y,ImgIndexOverlayDesigntimePackage);
|
|
if Unknown then
|
|
ImagesRes.Draw(CurCanvas,x,y,ImgIndexOverlayUnknown);
|
|
end;
|
|
PaintImages:=false;
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.InstallPkgSetDialogResize(Sender: TObject);
|
|
var
|
|
w: Integer;
|
|
begin
|
|
w:=ClientWidth div 2-InstallPkgGroupBox.BorderSpacing.Left*3;
|
|
if w<1 then w:=1;
|
|
InstallPkgGroupBox.Width:=w;
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.InstallTreeViewSelectionChanged(Sender: TObject);
|
|
begin
|
|
UpdateButtonStates;
|
|
UpdatePackageInfo(InstallTreeView);
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.SaveAndExitButtonClick(Sender: TObject);
|
|
begin
|
|
if SaveAndExitButton.Enabled and CheckSelection then
|
|
begin
|
|
RebuildIDE := false;
|
|
ModalResult := mrOk;
|
|
end;
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.SetOldInstalledPackages(const AValue: TPkgDependency);
|
|
begin
|
|
if FOldInstalledPackages=AValue then exit;
|
|
FOldInstalledPackages:=AValue;
|
|
AssignOldInstalledPackagesToList;
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.SetIdleConnected(AValue: boolean);
|
|
begin
|
|
if FIdleConnected=AValue then Exit;
|
|
FIdleConnected:=AValue;
|
|
if IdleConnected then
|
|
Application.AddOnIdleHandler(@OnIdle)
|
|
else
|
|
Application.RemoveOnIdleHandler(@OnIdle);
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.AssignOldInstalledPackagesToList;
|
|
var
|
|
Dependency: TPkgDependency;
|
|
Cnt: Integer;
|
|
NewPackageID: TLazPackageID;
|
|
begin
|
|
ClearNewInstalledPackages;
|
|
Cnt:=0;
|
|
Dependency:=OldInstalledPackages;
|
|
while Dependency<>nil do begin
|
|
NewPackageID:=TLazPackageID.Create;
|
|
if (Dependency.LoadPackageResult=lprSuccess)
|
|
and (Dependency.RequiredPackage<>nil) then begin
|
|
// packages can be freed while the dialog runs => use packageid instead
|
|
NewPackageID.AssignID(Dependency.RequiredPackage);
|
|
end else begin
|
|
NewPackageID.Name:=Dependency.PackageName;
|
|
end;
|
|
FNewInstalledPackages.Add(NewPackageID);
|
|
Dependency:=Dependency.NextRequiresDependency;
|
|
inc(Cnt);
|
|
end;
|
|
UpdateNewInstalledPackages;
|
|
UpdateAvailablePackages;
|
|
end;
|
|
|
|
function TInstallPkgSetDialog.PackageInInstallList(PkgName: string): boolean;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i:=0 to NewInstalledPackages.Count-1 do
|
|
if CompareText(TLazPackageID(NewInstalledPackages[i]).Name,PkgName)=0 then
|
|
exit(true);
|
|
Result:=false;
|
|
end;
|
|
|
|
function TInstallPkgSetDialog.GetPkgImgIndex(Installed: TPackageInstallType;
|
|
InInstallList, IsOnline: boolean): integer;
|
|
begin
|
|
if Installed<>pitNope then begin
|
|
// is not currently installed
|
|
if InInstallList then begin
|
|
// is installed and will be installed
|
|
Result:=ImgIndexPackage;
|
|
end
|
|
else begin
|
|
// is installed and will be uninstalled
|
|
Result:=ImgIndexUninstallPackage;
|
|
end;
|
|
end else begin
|
|
// is currently installed
|
|
if InInstallList then begin
|
|
// is not installed and will be installed
|
|
Result:=ImgIndexInstallPackage;
|
|
end
|
|
else begin
|
|
// is not installed and will be not be installed
|
|
if IsOnline then
|
|
Result := ImgIndexAvailableOnline
|
|
else
|
|
Result:=ImgIndexPackage;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TInstallPkgSetDialog.FindOnlinePackageLink(const AName: String): TPackageLink;
|
|
var
|
|
PackageLink: TPackageLink;
|
|
PkgName: String;
|
|
P: Integer;
|
|
begin
|
|
Result := nil;
|
|
Exit;
|
|
if OPMInterface = nil then
|
|
Exit;
|
|
PkgName := Trim(AName);
|
|
P := Pos(' ', PkgName);
|
|
if P > 0 then
|
|
PkgName := Copy(PkgName, 1, P - 1);
|
|
PackageLink := OPMInterface.FindOnlineLink(PkgName);
|
|
if PackageLink <> nil then
|
|
Result := PackageLink;
|
|
end;
|
|
|
|
// returns false on error or if the package is not base
|
|
function TInstallPkgSetDialog.IsBasePkg(aPkgID: TLazPackageID): boolean;
|
|
var
|
|
lPkg: TLazPackage;
|
|
begin
|
|
lPkg := PackageGraph.FindPackageWithID(aPkgID); // no need to free
|
|
result := (lPkg <> nil) and PackageGraph.IsCompiledInBasePackage(lPkg.Name);
|
|
end;
|
|
|
|
// returns false on error or if the package is not base
|
|
function TInstallPkgSetDialog.IsBasePkg(aPkgName: string): boolean;
|
|
var
|
|
lPkgID: TLazPackageID;
|
|
begin
|
|
result := false;
|
|
lPkgID := TLazPackageID.Create;
|
|
try
|
|
result := lPkgID.StringToID(aPkgName) and IsBasePkg(lPkgID);
|
|
finally
|
|
lPkgID.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.UpdateAvailablePackages(Immediately: boolean);
|
|
var
|
|
ANode: TAvlTreeNode;
|
|
FilteredBranch: TTreeFilterBranch;
|
|
Info: TLPKInfo;
|
|
List: TStringList;
|
|
i: Integer;
|
|
PackageLink: TPackageLink;
|
|
begin
|
|
if not Immediately then begin
|
|
fAvailablePkgsNeedUpdate:=true;
|
|
IdleConnected:=true;
|
|
exit;
|
|
end;
|
|
fAvailablePkgsNeedUpdate:=false;
|
|
List:=TStringList.Create;
|
|
try
|
|
// collect available packages, not yet installed
|
|
LPKInfoCache.EnterCritSection;
|
|
try
|
|
ANode:=LPKInfoCache.LPKByID.FindLowest;
|
|
while ANode<>nil do begin
|
|
Info:=TLPKInfo(ANode.Data);
|
|
ANode:=LPKInfoCache.LPKByID.FindSuccessor(ANode);
|
|
PackageLink := FindOnlinePackageLink(Info.ID.Name);
|
|
if (PackageLink <> nil) and (PackageLink.PackageType in [lptDesignTime,lptRunAndDesignTime]) then begin
|
|
if (not PackageInInstallList(Info.ID.Name)) then begin
|
|
Info.PkgType := PackageLink.PackageType;
|
|
Info.ID.Version.Assign(PackageLink.Version);
|
|
List.Add(Info.ID.IDAsString);
|
|
Continue;
|
|
end;
|
|
end;
|
|
if Info.LPKParsed=lpkiParsedError then continue;
|
|
if (Info.LPKParsed in [lpkiNotParsed,lpkiParsing])
|
|
or (Info.PkgType in [lptDesignTime,lptRunAndDesignTime])
|
|
then begin
|
|
if (not PackageInInstallList(Info.ID.Name)) then
|
|
List.Add(Info.ID.IDAsString);
|
|
end;
|
|
end;
|
|
finally
|
|
LPKInfoCache.LeaveCritSection;
|
|
end;
|
|
// fill tree view through FilterEdit
|
|
FilteredBranch := AvailableFilterEdit.GetCleanBranch(Nil); // All items are top level.
|
|
for i:=0 to List.Count-1 do
|
|
FilteredBranch.AddNodeData(List[i],nil);
|
|
finally
|
|
List.Free;
|
|
end;
|
|
AvailableFilterEdit.InvalidateFilter;
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.UpdateNewInstalledPackages;
|
|
var
|
|
NewPackageID: TLazPackageID;
|
|
APackage: TLazPackage;
|
|
FilteredBranch: TTreeFilterBranch;
|
|
List: TStringListUTF8Fast;
|
|
i: Integer;
|
|
begin
|
|
List:=TStringListUTF8Fast.Create;
|
|
try
|
|
for i:=0 to FNewInstalledPackages.Count-1 do begin
|
|
NewPackageID:=TLazPackageID(FNewInstalledPackages[i]);
|
|
APackage:=PackageGraph.FindPackageWithName(NewPackageID.Name,nil);
|
|
if APackage<>nil then
|
|
NewPackageID:=APackage;
|
|
List.Add(NewPackageID.IDAsString);
|
|
end;
|
|
List.Sort;
|
|
// fill tree view through FilterEdit
|
|
FilteredBranch := InstalledFilterEdit.GetCleanBranch(Nil); // All items are top level.
|
|
for i:=0 to List.Count-1 do
|
|
FilteredBranch.AddNodeData(List[i],nil);
|
|
finally
|
|
List.Free;
|
|
end;
|
|
InstalledFilterEdit.InvalidateFilter;
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.PkgInfosChanged;
|
|
// called in mainthread after package parser helper thread finished
|
|
begin
|
|
UpdateAvailablePackages;
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.ChangePkgVersion(PkgInfo: TLPKInfo;
|
|
NewVersion: TPkgVersion);
|
|
// called by LPKInfoCache when a lpk has a different version than the IDE list
|
|
var
|
|
OldID, NewID: String;
|
|
|
|
procedure ChangeTV(TV: TTreeView);
|
|
var
|
|
i: Integer;
|
|
Node: TTreeNode;
|
|
begin
|
|
for i:=0 to TV.Items.TopLvlCount-1 do begin
|
|
Node:=TV.Items.TopLvlItems[i];
|
|
if Node.Text=OldID then
|
|
Node.Text:=NewID;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
OldID:=PkgInfo.ID.IDAsString;
|
|
NewID:=PkgInfo.ID.Name+' '+NewVersion.AsString;
|
|
ChangeTV(AvailableTreeView);
|
|
ChangeTV(InstallTreeView);
|
|
end;
|
|
|
|
function TInstallPkgSetDialog.DependencyToStr(Dependency: TPkgDependency): string;
|
|
begin
|
|
Result:='';
|
|
if Dependency=nil then exit;
|
|
if (Dependency.LoadPackageResult=lprSuccess)
|
|
and (Dependency.RequiredPackage<>nil) then
|
|
Result:=Dependency.RequiredPackage.IDAsString
|
|
else
|
|
Result:=Dependency.PackageName;
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.ClearNewInstalledPackages;
|
|
begin
|
|
FNewInstalledPackages.Clear;
|
|
end;
|
|
|
|
function TInstallPkgSetDialog.CheckSelection: boolean;
|
|
var
|
|
UninstallPkgs: TObjectList; // list of TLazPackageID
|
|
Dependency: TPkgDependency;
|
|
OldPackageID: TLazPackageID;
|
|
begin
|
|
UninstallPkgs:=TObjectList.Create(true);
|
|
try
|
|
Dependency:=OldInstalledPackages;
|
|
while Dependency<>nil do begin
|
|
OldPackageID:=TLazPackageID.Create;
|
|
if (Dependency.LoadPackageResult=lprSuccess)
|
|
and (Dependency.RequiredPackage<>nil) then begin
|
|
OldPackageID.AssignID(Dependency.RequiredPackage);
|
|
end else begin
|
|
OldPackageID.Name:=Dependency.PackageName;
|
|
end;
|
|
|
|
if NewInstalledPackagesContains(OldPackageID) then
|
|
OldPackageID.Free
|
|
else
|
|
UninstallPkgs.Add(OldPackageID);
|
|
|
|
Dependency:=Dependency.NextRequiresDependency;
|
|
end;
|
|
|
|
Result:=PackageEditingInterface.CheckInstallPackageList(
|
|
FNewInstalledPackages,UninstallPkgs,[piiifRemoveConflicts]);
|
|
if not Result then begin
|
|
AssignOldInstalledPackagesToList; // Restore the old list.
|
|
UpdateNewInstalledPackages;
|
|
UpdateAvailablePackages(True);
|
|
UpdateButtonStates;
|
|
end;
|
|
finally
|
|
UninstallPkgs.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.UpdateButtonStates;
|
|
//
|
|
function ChangesFound: boolean;
|
|
var
|
|
Cnt: Integer;
|
|
Dependency: TPkgDependency;
|
|
FilteredBranch: TTreeFilterBranch;
|
|
begin
|
|
Cnt := 0;
|
|
Dependency := OldInstalledPackages;
|
|
while Dependency <> nil do
|
|
begin
|
|
if not PackageInInstallList(Dependency.PackageName) then
|
|
exit(true);
|
|
Dependency := Dependency.NextRequiresDependency;
|
|
inc(Cnt);
|
|
end;
|
|
FilteredBranch := InstalledFilterEdit.GetExistingBranch(nil);
|
|
result := assigned(FilteredBranch) and (FilteredBranch.Items.Count <> Cnt);
|
|
end;
|
|
//
|
|
begin
|
|
SaveAndRebuildButton.Enabled := ChangesFound;
|
|
SaveAndExitButton .Enabled := SaveAndRebuildButton.Enabled;
|
|
UninstallButton .Enabled := (InstallTreeView.Selected <> nil) and
|
|
not IsBasePkg(InstallTreeView.Selected.Text);
|
|
AddToInstallButton .Enabled := AvailableTreeView.Selected <> nil;
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.UpdatePackageInfo(Tree: TTreeView);
|
|
var
|
|
InfoStr: string;
|
|
|
|
procedure AddState(const NewState: string);
|
|
begin
|
|
if (InfoStr<>'') and (InfoStr[length(InfoStr)]<>' ') then
|
|
InfoStr:=InfoStr+', ';
|
|
InfoStr:=InfoStr+NewState;
|
|
end;
|
|
|
|
var
|
|
PkgID: String;
|
|
Info: TLPKInfo;
|
|
PackageLink: TPackageLink;
|
|
Author, Description, License: String;
|
|
begin
|
|
if Tree = nil then Exit;
|
|
PkgID := '';
|
|
if Tree.Selected <> nil then
|
|
PkgID := Tree.Selected.Text;
|
|
if PkgID = '' then Exit;
|
|
if Tree = InstallTreeView then
|
|
PackageLink := nil
|
|
else
|
|
PackageLink := FindOnlinePackageLink(PkgID);
|
|
LPKInfoCache.EnterCritSection;
|
|
try
|
|
Info:=LPKInfoCache.FindPkgInfoWithIDAsString(PkgID);
|
|
if ((Info=nil) and (FSelectedPkgID=''))
|
|
or ((Info<>nil) and (Info.ID.IDAsString=FSelectedPkgID)
|
|
and (Info.LPKParsed=FSelectedPkgState))
|
|
then
|
|
exit; // no change
|
|
PkgInfoMemo.Clear;
|
|
PkgInfoMemoLicense.Clear;
|
|
if (Info=nil) then begin
|
|
FSelectedPkgID:='';
|
|
exit;
|
|
end;
|
|
FSelectedPkgID:=PkgID;
|
|
|
|
if Info.LPKParsed=lpkiNotParsed then begin
|
|
LPKInfoCache.ParseLPKInfoInMainThread(Info);
|
|
if FSelectedPkgID='' then begin
|
|
// version has changed
|
|
// => has already triggered an update
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
if PackageLink = nil then
|
|
begin
|
|
Author := Info.Author;
|
|
Description := Trim(Info.Description);
|
|
License := Info.License;
|
|
end
|
|
else
|
|
begin
|
|
Author := PackageLink.Author;
|
|
Description := Trim(PackageLink.Description);
|
|
License := PackageLink.License;
|
|
end;
|
|
|
|
if Description<>'' then // Description is the most interesting piece.
|
|
PkgInfoMemo.Lines.Add(Description); // Put it first.
|
|
PkgInfoMemo.Lines.Add('');
|
|
if Author<>'' then
|
|
PkgInfoMemo.Lines.Add(lisPckOptsAuthor + ': ' + Author); // Author
|
|
PkgInfoMemo.Lines.Add(Format(lisOIPFilename, [Info.LPKFilename])); // Pkg name
|
|
|
|
if License<>'' then // License has its own memo.
|
|
PkgInfoMemoLicense.Lines.Add(lisPckOptsLicense + ': ' + License);
|
|
|
|
InfoStr:=lisCurrentState;
|
|
if Info.Installed<>pitNope then
|
|
begin
|
|
if PackageInInstallList(Info.ID.Name)=false then
|
|
AddState(lisSelectedForUninstallation);
|
|
AddState(lisInstalled);
|
|
end
|
|
else
|
|
begin
|
|
if PackageInInstallList(Info.ID.Name)=true then
|
|
AddState(lisSelectedForInstallation);
|
|
AddState(lisNotInstalled);
|
|
if PackageLink <> nil then
|
|
AddState(lisOnlinePackage);
|
|
end;
|
|
if Info.Base then
|
|
AddState(lisPckExplBase);
|
|
AddState(LazPackageTypeIdents[Info.PkgType]);
|
|
PkgInfoMemo.Lines.Add(InfoStr);
|
|
PkgInfoMemo.SelStart := 1;
|
|
PkgInfoMemoLicense.SelStart := 1;
|
|
finally
|
|
LPKInfoCache.LeaveCritSection;
|
|
end;
|
|
end;
|
|
|
|
function TInstallPkgSetDialog.NewInstalledPackagesContains(
|
|
APackageID: TLazPackageID): boolean;
|
|
begin
|
|
Result:=IndexOfNewInstalledPackageID(APackageID)>=0;
|
|
end;
|
|
|
|
function TInstallPkgSetDialog.IndexOfNewInstalledPackageID(
|
|
APackageID: TLazPackageID): integer;
|
|
begin
|
|
Result:=FNewInstalledPackages.Count-1;
|
|
while (Result>=0)
|
|
and (TLazPackageID(FNewInstalledPackages[Result]).Compare(APackageID)<>0) do
|
|
dec(Result);
|
|
end;
|
|
|
|
function TInstallPkgSetDialog.IndexOfNewInstalledPkgByName(
|
|
const APackageName: string): integer;
|
|
begin
|
|
Result:=FNewInstalledPackages.Count-1;
|
|
while (Result>=0)
|
|
and (CompareText(TLazPackageID(FNewInstalledPackages[Result]).Name,
|
|
APackageName)<>0)
|
|
do
|
|
dec(Result);
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.SavePackageListToFile(const AFilename: string);
|
|
var
|
|
XMLConfig: TXMLConfig;
|
|
i: Integer;
|
|
LazPackageID: TLazPackageID;
|
|
begin
|
|
try
|
|
XMLConfig:=TXMLConfig.CreateClean(AFilename);
|
|
try
|
|
XMLConfig.SetDeleteValue('Packages/Count',FNewInstalledPackages.Count,0);
|
|
for i:=0 to FNewInstalledPackages.Count-1 do begin
|
|
LazPackageID:=TLazPackageID(FNewInstalledPackages[i]);
|
|
XMLConfig.SetDeleteValue('Packages/Item'+IntToStr(i)+'/ID',
|
|
LazPackageID.IDAsString,'');
|
|
end;
|
|
InvalidateFileStateCache;
|
|
XMLConfig.Flush;
|
|
finally
|
|
XMLConfig.Free;
|
|
end;
|
|
except
|
|
on E: Exception do begin
|
|
MessageDlg(lisCodeToolsDefsWriteError,
|
|
Format(lisErrorWritingPackageListToFile,[LineEnding,AFilename,LineEnding,E.Message]),
|
|
mtError, [mbCancel], 0);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.LoadPackageListFromFile(const AFilename: string);
|
|
|
|
function PkgNameExists(List: TObjectList; ID: TLazPackageID): boolean;
|
|
var
|
|
i: Integer;
|
|
LazPackageID: TLazPackageID;
|
|
begin
|
|
if List<>nil then
|
|
for i:=0 to List.Count-1 do begin
|
|
LazPackageID:=TLazPackageID(List[i]);
|
|
if CompareText(LazPackageID.Name,ID.Name)=0 then begin
|
|
Result:=true;
|
|
exit;
|
|
end;
|
|
end;
|
|
Result:=false;
|
|
end;
|
|
|
|
var
|
|
XMLConfig: TXMLConfig;
|
|
i: Integer;
|
|
LazPackageID: TLazPackageID;
|
|
NewCount: LongInt;
|
|
NewList: TObjectList;
|
|
ID: String;
|
|
begin
|
|
NewList:=nil;
|
|
LazPackageID:=nil;
|
|
try
|
|
XMLConfig:=TXMLConfig.Create(AFilename);
|
|
try
|
|
NewCount:=XMLConfig.GetValue('Packages/Count',0);
|
|
LazPackageID:=TLazPackageID.Create;
|
|
for i:=0 to NewCount-1 do begin
|
|
// get ID
|
|
ID:=XMLConfig.GetValue('Packages/Item'+IntToStr(i)+'/ID','');
|
|
if ID='' then continue;
|
|
// parse ID
|
|
if not LazPackageID.StringToID(ID) then continue;
|
|
// ignore doubles
|
|
if PkgNameExists(NewList,LazPackageID) then continue;
|
|
// add
|
|
if NewList=nil then NewList:=TObjectList.Create(true);
|
|
NewList.Add(LazPackageID);
|
|
LazPackageID:=TLazPackageID.Create;
|
|
end;
|
|
// clean up old list
|
|
ClearNewInstalledPackages;
|
|
FNewInstalledPackages.Free;
|
|
// assign new list
|
|
FNewInstalledPackages:=NewList;
|
|
NewList:=nil;
|
|
UpdateNewInstalledPackages;
|
|
UpdateAvailablePackages;
|
|
UpdateButtonStates;
|
|
finally
|
|
XMLConfig.Free;
|
|
LazPackageID.Free;
|
|
NewList.Free;
|
|
end;
|
|
except
|
|
on E: Exception do begin
|
|
MessageDlg(lisCodeToolsDefsReadError,
|
|
Format(lisErrorReadingPackageListFromFile,[LineEnding,AFilename,LineEnding,E.Message]),
|
|
mtError, [mbCancel], 0);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TInstallPkgSetDialog.ExtractNameFromPkgID(ID: string): string;
|
|
begin
|
|
if ID='' then
|
|
Result:=''
|
|
else
|
|
Result:=GetIdentifier(PChar(ID));
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.AddToInstall(Sender: TObject);
|
|
|
|
function SelectionOk(aPackageID: TLazPackageID): Boolean;
|
|
var
|
|
APackage: TLazPackage;
|
|
ConflictDep: TPkgDependency;
|
|
i: Integer;
|
|
begin
|
|
// check if already in list
|
|
if NewInstalledPackagesContains(aPackageID) then begin
|
|
MessageDlg(lisDuplicate,
|
|
Format(lisThePackageIsAlreadyInTheList, [aPackageID.Name]),
|
|
mtError, [mbCancel],0);
|
|
exit(false);
|
|
end;
|
|
// check if a package with same name is already in the list
|
|
i:=IndexOfNewInstalledPkgByName(aPackageID.Name);
|
|
if i>=0 then begin
|
|
MessageDlg(lisConflict,
|
|
Format(lisThereIsAlreadyAPackageInTheList, [aPackageID.Name]),
|
|
mtError,[mbCancel],0);
|
|
exit(false);
|
|
end;
|
|
// check if package is loaded and has some attributes that prevents
|
|
// installation in the IDE
|
|
APackage:=PackageGraph.FindPackageWithID(aPackageID);
|
|
if APackage<>nil then begin
|
|
if APackage.PackageType in [lptRunTime,lptRunTimeOnly] then begin
|
|
IDEMessageDialog(lisNotADesigntimePackage,
|
|
Format(lisThePackageIsNotADesignTimePackageItCanNotBeInstall,
|
|
[APackage.IDAsString]),
|
|
mtError, [mbCancel]);
|
|
exit(false);
|
|
end;
|
|
ConflictDep:=PackageGraph.FindRuntimePkgOnlyRecursively(
|
|
APackage.FirstRequiredDependency);
|
|
if ConflictDep<>nil then begin
|
|
IDEMessageDialog(lisNotADesigntimePackage,
|
|
Format(lisThePackageCanNotBeInstalledBecauseItRequiresWhichI,
|
|
[APackage.Name, ConflictDep.AsString]),
|
|
mtError, [mbCancel]);
|
|
exit(false);
|
|
end;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
var
|
|
i, j: Integer;
|
|
NewSelectedIndex, LastNonSelectedIndex: Integer;
|
|
NewPackageID: TLazPackageID;
|
|
Additions: TObjectList;
|
|
AddedPkgNames: TStringList;
|
|
TVNode: TTreeNode;
|
|
PkgName: String;
|
|
FilteredBranch: TTreeFilterBranch;
|
|
PkgLinks: TList;
|
|
PkgLinksStr: String;
|
|
PkgLink: TPackageLink;
|
|
begin
|
|
NewSelectedIndex:=-1;
|
|
LastNonSelectedIndex:=-1;
|
|
FilteredBranch := AvailableFilterEdit.GetExistingBranch(Nil); // All items are top level
|
|
|
|
Additions:=TObjectList.Create(false);
|
|
AddedPkgNames:=TStringList.Create;
|
|
PkgLinks := TList.Create;
|
|
NewPackageID:=TLazPackageID.Create;
|
|
try
|
|
for i:=0 to AvailableTreeView.Items.TopLvlCount-1 do
|
|
begin
|
|
TVNode:=AvailableTreeView.Items.TopLvlItems[i];
|
|
if not TVNode.MultiSelected then begin
|
|
LastNonSelectedIndex:=i;
|
|
continue;
|
|
end;
|
|
NewSelectedIndex:=i+1; // Will have the next index after the selected one.
|
|
PkgName:=TVNode.Text;
|
|
// Convert package name to ID and check it
|
|
if not NewPackageID.StringToID(PkgName) then begin
|
|
TVNode.Selected:=false;
|
|
DebugLn('TInstallPkgSetDialog.AddToInstall invalid ID: ', PkgName);
|
|
continue;
|
|
end;
|
|
if not SelectionOk(NewPackageID) then begin
|
|
TVNode.Selected:=false;
|
|
exit;
|
|
end;
|
|
// ok => add to list
|
|
PkgLink := FindOnlinePackageLink(NewPackageID.Name);
|
|
if PkgLink <> nil then begin
|
|
if not FileExists(PkgLink.OPMFileName) then
|
|
PkgLinks.Add(PkgLink)
|
|
else
|
|
begin
|
|
PkgLink := LazPackageLinks.AddUserLink(PkgLink.OPMFileName, PkgLink.Name);
|
|
if PkgLink <> nil then
|
|
LazPackageLinks.SaveUserLinks;
|
|
Additions.Add(NewPackageID);
|
|
NewPackageID:=TLazPackageID.Create;
|
|
AddedPkgNames.Add(PkgName);
|
|
end;
|
|
end else begin
|
|
Additions.Add(NewPackageID);
|
|
NewPackageID:=TLazPackageID.Create;
|
|
AddedPkgNames.Add(PkgName);
|
|
end;
|
|
end;
|
|
//download online packages
|
|
if (OPMInterface <> nil) and (PkgLinks.Count > 0) then
|
|
begin
|
|
PkgLinksStr := '';
|
|
for I := 0 to PkgLinks.Count - 1 do begin
|
|
if PkgLinksStr = '' then
|
|
PkgLinksStr := '"' + TPackageLink(PkgLinks.Items[I]).Name + '"'
|
|
else
|
|
PkgLinksStr := PkgLinksStr + ', ' + '"' + TPackageLink(PkgLinks.Items[I]).Name + '"';
|
|
end;
|
|
if IDEMessageDialog(lisDownload, Format(lisDonwloadOnlinePackages, [PkgLinksStr]), mtConfirmation, [mbYes, mbNo]) = mrYes then begin
|
|
if OPMInterface.DownloadPackages(PkgLinks) = mrOK then begin
|
|
for I := PkgLinks.Count - 1 downto 0 do begin
|
|
if OPMInterface.IsPackageAvailable(TPackageLink(PkgLinks.Items[I]), 1) then begin
|
|
Additions.Add(NewPackageID);
|
|
NewPackageID:=TLazPackageID.Create;
|
|
AddedPkgNames.Add(PkgName);
|
|
PkgLink := LazPackageLinks.AddUserLink(TPackageLink(PkgLinks.Items[I]).OPMFileName, TPackageLink(PkgLinks.Items[I]).Name);
|
|
if PkgLink <> nil then
|
|
LazPackageLinks.SaveUserLinks;
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
Dec(NewSelectedIndex);
|
|
end
|
|
else
|
|
Dec(NewSelectedIndex);
|
|
end;
|
|
// all ok => add to installed packages
|
|
for i:=0 to Additions.Count-1 do
|
|
FNewInstalledPackages.Add(Additions[i]);
|
|
for i:=0 to AddedPkgNames.Count-1 do begin
|
|
j:=FilteredBranch.Items.IndexOf(AddedPkgNames[i]);
|
|
Assert(j<>-1, 'TInstallPkgSetDialog.AddToInstall: '+AddedPkgNames[i]+' not found in Filter items.');
|
|
FilteredBranch.Items.Delete(j);
|
|
end;
|
|
// Don't call UpdateAvailablePackages here, only the selected nodes were removed.
|
|
UpdateNewInstalledPackages;
|
|
UpdateButtonStates;
|
|
if ((NewSelectedIndex=-1) or (NewSelectedIndex=AvailableTreeView.Items.TopLvlCount)) then
|
|
NewSelectedIndex:=LastNonSelectedIndex;
|
|
if NewSelectedIndex<>-1 then
|
|
AvailableTreeView.Items.TopLvlItems[NewSelectedIndex].Selected:=True;
|
|
AvailableFilterEdit.InvalidateFilter;
|
|
finally
|
|
NewPackageID.Free;
|
|
AddedPkgNames.Free;
|
|
Additions.Free;
|
|
PkgLinks.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TInstallPkgSetDialog.AddToUninstall(Sender: TObject);
|
|
var
|
|
i, j: Integer;
|
|
NewSelectedIndex, LastNonSelectedIndex: Integer;
|
|
DelPackageID, PackID: TLazPackageID;
|
|
Deletions: TObjectList; // list of TLazPackageID
|
|
DeletedPkgNames: TStringList;
|
|
TVNode: TTreeNode;
|
|
PkgName: String;
|
|
FilteredBranch: TTreeFilterBranch;
|
|
begin
|
|
NewSelectedIndex:=-1;
|
|
LastNonSelectedIndex:=-1;
|
|
FilteredBranch := InstalledFilterEdit.GetExistingBranch(Nil); // All items are top level
|
|
|
|
Deletions:=TObjectList.Create(true);
|
|
DeletedPkgNames:=TStringList.Create;
|
|
DelPackageID:=TLazPackageID.Create;
|
|
try
|
|
for i:=0 to InstallTreeView.Items.TopLvlCount-1 do begin
|
|
TVNode:=InstallTreeView.Items.TopLvlItems[i];
|
|
if not TVNode.MultiSelected then begin
|
|
LastNonSelectedIndex:=i;
|
|
continue;
|
|
end;
|
|
NewSelectedIndex:=i+1; // Will have the next index after the selected one.
|
|
PkgName:=TVNode.Text;
|
|
if not DelPackageID.StringToID(PkgName) then begin
|
|
debugln('TInstallPkgSetDialog.AddToUninstall invalid ID: ', PkgName);
|
|
continue;
|
|
end;
|
|
if IsBasePkg(DelPackageID) then begin
|
|
MessageDlg(
|
|
lisUninstallImpossible,
|
|
Format(lisThePackageCanNotBeUninstalledBecauseItIsNeededByTh, [PkgName]),
|
|
mtError,
|
|
[mbCancel],
|
|
0
|
|
);
|
|
exit;
|
|
end;
|
|
// ok => add to deletions
|
|
Deletions.Add(DelPackageID);
|
|
DelPackageID:=TLazPackageID.Create;
|
|
DeletedPkgNames.Add(PkgName);
|
|
end;
|
|
|
|
// ok => remove from installed packages
|
|
InstallTreeView.Selected:=nil;
|
|
for i:=0 to Deletions.Count-1 do begin
|
|
PackID:=TLazPackageID(Deletions[i]);
|
|
j:=IndexOfNewInstalledPackageID(PackID);
|
|
FNewInstalledPackages.Delete(j);
|
|
end;
|
|
for i:=0 to DeletedPkgNames.Count-1 do begin
|
|
j:=FilteredBranch.Items.IndexOf(DeletedPkgNames[i]);
|
|
Assert(j<>-1, 'TInstallPkgSetDialog.AddToUninstall: '+DeletedPkgNames[i]+' not found in Filter items.');
|
|
FilteredBranch.Items.Delete(j);
|
|
end;
|
|
|
|
// Don't call UpdateNewInstalledPackages here, only the selected nodes were removed.
|
|
UpdateAvailablePackages;
|
|
UpdateButtonStates;
|
|
if ((NewSelectedIndex<0) or (NewSelectedIndex=InstallTreeView.Items.TopLvlCount)) then
|
|
NewSelectedIndex:=LastNonSelectedIndex;
|
|
if NewSelectedIndex>=0 then
|
|
InstallTreeView.Items.TopLvlItems[NewSelectedIndex].Selected:=True;
|
|
InstalledFilterEdit.InvalidateFilter;
|
|
finally
|
|
DelPackageID.Free;
|
|
DeletedPkgNames.Free;
|
|
Deletions.Free;
|
|
end;
|
|
end;
|
|
|
|
function TInstallPkgSetDialog.GetNewInstalledPackages: TObjectList;
|
|
var
|
|
i: Integer;
|
|
NewPackageID: TLazPackageID;
|
|
begin
|
|
Result:=TObjectList.Create(true);
|
|
for i:=0 to FNewInstalledPackages.Count-1 do begin
|
|
NewPackageID:=TLazPackageID.Create;
|
|
NewPackageID.AssignID(TLazPackageID(FNewInstalledPackages[i]));
|
|
Result.Add(NewPackageID);
|
|
end;
|
|
end;
|
|
|
|
end.
|
|
|