lazarus/packager/pkglinksdlg.pas

575 lines
17 KiB
ObjectPascal

{
/***************************************************************************
pkgmanager.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 showing the package links of the IDE package systems.
}
unit PkgLinksDlg;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, AVL_Tree,
// LCL
Forms, Controls, StdCtrls, Buttons, Grids, ExtCtrls, ComCtrls, Menus, Dialogs,
// Codetools
FileProcs,
// LazUtils
LazUTF8, LazFileUtils, LazFileCache,
// IdeIntf
IdeIntfStrConsts, PackageLinkIntf, PackageIntf, IDEDialogs,
// IDE
LazarusIDEStrConsts, PackageLinks, LPKCache;
type
{ TPkgLinkInfo }
TPkgLinkInfo = class(TLazPackageLink)
private
FEffectiveFilename: string;
FIsValid: boolean;
FLPKInfo: TLPKInfo;
FVisible: boolean;
public
constructor Create; override;
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
property Origin;
property LPKUrl;
property LPKInfo: TLPKInfo read FLPKInfo;
property Visible: boolean read FVisible write FVisible;
property IsValid: boolean read FIsValid write FIsValid;
property EffectiveFilename: string read FEffectiveFilename write FEffectiveFilename;
property OPMFileDate;
end;
{ TPackageLinksDialog }
TPackageLinksDialog = class(TForm)
BtnPanel: TPanel;
CloseBitBtn: TBitBtn;
DeleteSelectedButton: TButton;
FilterEdit: TEdit;
LPKFileValidCheckBox: TCheckBox;
LPKFileInvalidCheckBox: TCheckBox;
LPKParsingTimer: TTimer;
CopyCellToClipboardMenuItem: TMenuItem;
GridPopupMenu: TPopupMenu;
ShowGlobalLinksCheckBox: TCheckBox;
ShowOnlineLinksCheckBox: TCheckBox;
ShowUserLinksCheckBox: TCheckBox;
ScopeGroupBox: TGroupBox;
PkgStringGrid: TStringGrid;
ProgressBar1: TProgressBar;
UpdateGlobalLinksButton: TButton;
procedure CopyCellToClipboardMenuItemClick(Sender: TObject);
procedure DeleteSelectedButtonClick(Sender: TObject);
procedure FilterEditChange(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure LPKFileValidCheckBoxChange(Sender: TObject);
procedure LPKFileInvalidCheckBoxChange(Sender: TObject);
procedure LPKParsingTimerTimer(Sender: TObject);
procedure OnAllLPKParsed(Sender: TObject);
procedure ShowLinksCheckBoxChange(Sender: TObject);
procedure ShowOnlineLinksCheckBoxChange(Sender: TObject);
procedure UpdateGlobalLinksButtonClick(Sender: TObject);
private
FCountGlobalLinks: integer;
FCountLPKValid: integer;
FCountLPKInvalid: integer;
FCountOnlineLinks: Integer;
FCountUserLinks: Integer;
FLinks: TAVLTree; // tree of TPkgLinkInfo sorted for name and version
FCollectingOrigin: TPkgLinkOrigin;
procedure RescanGlobalLinks;
procedure UpdateFacets;
procedure UpdatePackageList;
procedure ClearLinks;
procedure IteratePackages(APackage: TLazPackageID);
function GetLinkAtRow(Row: integer): TPkgLinkInfo;
function GetLinkWithEffectiveFilename(Filename: string;
Origins: TPkgLinkOrigins): TPkgLinkInfo;
procedure PackageListAvailable(Sender: TObject);
public
property CountLPKValid: integer read FCountLPKValid;
property CountLPKInvalid: integer read FCountLPKInvalid;
property CountOnlineLinks: Integer read FCountOnlineLinks;
property CountUserLinks: Integer read FCountUserLinks;
property CountGlobalLinks: integer read FCountGlobalLinks;
end;
function ShowPackageLinks: TModalResult;
implementation
{$R *.lfm}
function ShowPackageLinks: TModalResult;
var
PackageLinksDialog: TPackageLinksDialog;
begin
PackageLinksDialog:=TPackageLinksDialog.Create(nil);
try
Result:=PackageLinksDialog.ShowModal;
finally
PackageLinksDialog.Free;
end;
end;
{ TPackageLinksDialog }
procedure TPackageLinksDialog.FormCreate(Sender: TObject);
begin
Caption:=lisPLDPackageLinks;
ScopeGroupBox.Caption:=dlgSearchScope;
CopyCellToClipboardMenuItem.Caption:=srkmecCopy;
DeleteSelectedButton.Caption:=lrsPLDDeleteSelected;
UpdateGlobalLinksButton.Caption:=lrsRescanLplFiles;
CloseBitBtn.Caption:=lisClose;
FilterEdit.TextHint:=lisCEFilter;
ProgressBar1.Style:=pbstMarquee;
ProgressBar1.Visible:=true;
LPKInfoCache.AddOnQueueEmpty(@OnAllLPKParsed);
LPKInfoCache.StartLPKReaderWithAllAvailable;
if Assigned(OPMInterface) then
ShowOnlineLinksCheckBox.Checked := OPMInterface.IsPackageListLoaded
else
ShowOnlineLinksCheckBox.Enabled := False;
// Set handler after setting Checked.
ShowOnlineLinksCheckBox.OnChange := @ShowOnlineLinksCheckBoxChange;
UpdatePackageList;
if OPMInterface <> nil then
OPMInterface.AddPackageListNotification(@PackageListAvailable);
end;
procedure TPackageLinksDialog.FormDestroy(Sender: TObject);
begin
if OPMInterface <> nil then
OPMInterface.RemovePackageListNotification(@PackageListAvailable);
LPKInfoCache.EndLPKReader;
LPKInfoCache.RemoveOnQueueEmpty(@OnAllLPKParsed);
ClearLinks;
end;
procedure TPackageLinksDialog.FilterEditChange(Sender: TObject);
begin
UpdatePackageList;
end;
procedure TPackageLinksDialog.CopyCellToClipboardMenuItemClick(Sender: TObject);
begin
PkgStringGrid.CopyToClipboard(true);
end;
procedure TPackageLinksDialog.DeleteSelectedButtonClick(Sender: TObject);
var
i: Integer;
Link: TPkgLinkInfo;
ErrMsg: String;
HasOnline: Boolean;
begin
ErrMsg:='';
HasOnline:=false;
for i:=1 to PkgStringGrid.RowCount-1 do
begin
if PkgStringGrid.Cells[0,i]=PkgStringGrid.Columns[0].ValueChecked then
begin
Link:=GetLinkAtRow(i);
if Link=nil then begin
HasOnline:=true;
continue;
end;
case Link.Origin of
ploGlobal: begin
// delete lpl file
if FileExistsCached(Link.LPLFilename) then begin
if not DeleteFileUTF8(Link.LPLFilename) then
ErrMsg+=Format(lrsPLDUnableToDeleteFile, [Link.LPLFilename])+LineEnding;
end;
end;
ploOnline: HasOnline:=true;
ploUser: LazPackageLinks.RemoveUserLinks(Link); // delete user link
end;
end;
end;
RescanGlobalLinks;
UpdatePackageList;
LazPackageLinks.SaveUserLinks;
if ErrMsg<>'' then
begin
IDEMessageDialog(dlgMsgWinColorUrgentError,
lisPLDSomePackagesCannotBeDeleted+':'+LineEnding
+ErrMsg,mtError,[mbOk]);
end else if HasOnline then begin
IDEMessageDialog(dlgMsgWinColorUrgentError,
lisPLDOnlinePackagesCannotBeDeleted, mtError, [
mbOk]);
end;
if HasOnline then;
end;
procedure TPackageLinksDialog.LPKFileValidCheckBoxChange(Sender: TObject);
begin
UpdatePackageList;
end;
procedure TPackageLinksDialog.LPKFileInvalidCheckBoxChange(Sender: TObject);
begin
UpdatePackageList;
end;
procedure TPackageLinksDialog.LPKParsingTimerTimer(Sender: TObject);
begin
UpdatePackageList;
end;
procedure TPackageLinksDialog.OnAllLPKParsed(Sender: TObject);
begin
LPKParsingTimer.Enabled:=false;
ProgressBar1.Visible:=false;
ProgressBar1.Style:=pbstNormal;
UpdatePackageList;
end;
procedure TPackageLinksDialog.ShowLinksCheckBoxChange(Sender: TObject);
begin
UpdatePackageList;
end;
procedure TPackageLinksDialog.ShowOnlineLinksCheckBoxChange(Sender: TObject);
begin
Assert(Assigned(OPMInterface), 'TPackageLinksDialog: OPMInterface=Nil.');
if (Sender as TCheckBox).Checked and not OPMInterface.IsPackageListLoaded then
OPMInterface.GetPackageList // ListBox will be updated later by an event.
else
UpdatePackageList;
end;
procedure TPackageLinksDialog.UpdateGlobalLinksButtonClick(Sender: TObject);
begin
RescanGlobalLinks;
UpdatePackageList;
end;
procedure TPackageLinksDialog.UpdatePackageList;
var
FilterCase: TCaption;
FilterLo: String;
function HasFilterText(Link: TPkgLinkInfo): boolean;
begin
Result:=true;
if Pos(FilterLo,UTF8LowerCase(Link.Name))>0 then
exit;
if Pos(FilterLo,UTF8LowerCase(Link.Version.AsString))>0 then
exit;
if FilenamesCaseSensitive then {%H-}begin
if Pos(FilterCase,Link.EffectiveFilename)>0 then
exit;
end else {%H-}begin
if Pos(FilterLo,UTF8LowerCase(Link.EffectiveFilename))>0 then
exit;
end;
Result:=false;
end;
var
Node: TAvlTreeNode;
Link: TPkgLinkInfo;
i: Integer;
OriginStr: String;
Info: TLPKInfo;
NextNode: TAvlTreeNode;
s: String;
begin
// collect links
ClearLinks;
if FLinks=nil then
FLinks:=TAVLTree.Create(@ComparePackageLinks);
if ShowGlobalLinksCheckBox.Checked then begin
FCollectingOrigin:=ploGlobal;
LazPackageLinks.IteratePackages(false,@IteratePackages,[ploGlobal]);
end;
if ShowOnlineLinksCheckBox.Checked then begin
FCollectingOrigin:=ploOnline;
LazPackageLinks.IteratePackages(false,@IteratePackages,[ploOnline]);
end;
if ShowUserLinksCheckBox.Checked then begin
FCollectingOrigin:=ploUser;
LazPackageLinks.IteratePackages(false,@IteratePackages,[ploUser]);
end;
// query additional information from lpk files
LPKInfoCache.EnterCritSection;
try
FCountLPKValid:=0;
FCountLPKInvalid:=0;
FCountGlobalLinks:=0;
FCountUserLinks:=0;
FCountOnlineLinks:=0;
Node:=FLinks.FindLowest;
FilterCase:=FilterEdit.Text;
FilterLo:=UTF8LowerCase(FilterCase);
while Node<>nil do begin
Link:=TPkgLinkInfo(Node.Data);
Link.Visible:=true;
NextNode:=Node.Successor;
Link.EffectiveFilename:=Link.GetEffectiveFilename;
Info:=LPKInfoCache.FindPkgInfoWithFilename(Link.EffectiveFilename);
// filter for Validity
if Link.Visible then begin
Link.IsValid:=true;
if Info<>nil then begin
Link.LPKInfo.Assign(Info);
if Link.LPKInfo.LPKParsed=lpkiParsedError then
Link.IsValid:=false;
end else
Link.IsValid:=false;
if Link.IsValid then begin
if not LPKFileValidCheckBox.Checked then Link.Visible:=false;
end else begin
if not LPKFileInvalidCheckBox.Checked then Link.Visible:=false;
end;
end;
if Link.Visible and (FilterCase<>'') then begin
// filter for text
Link.Visible:=HasFilterText(Link);
end;
if Link.Visible then begin
// this link is shown => increase facet counters
if Link.IsValid then
inc(FCountLPKValid)
else
inc(FCountLPKInvalid);
case Link.Origin of
ploGlobal: inc(FCountGlobalLinks);
ploOnline: inc(FCountOnlineLinks);
ploUser : inc(FCountUserLinks);
end;
end else begin
// delete link
Link.Free;
FLinks.Delete(Node);
end;
Node:=NextNode;
end;
finally
LPKInfoCache.LeaveCritSection;
end;
// fill/update grid
PkgStringGrid.RowCount:=FLinks.Count+1;
PkgStringGrid.Columns[0].Title.Caption:=lisMenuSelect;
PkgStringGrid.Columns[1].Title.Caption:=lisName;
PkgStringGrid.Columns[2].Title.Caption:=lisVersion;
PkgStringGrid.Columns[3].Title.Caption:=lisGroup;
PkgStringGrid.Columns[4].Title.Caption:=lisOIPState;
PkgStringGrid.Columns[5].Title.Caption:=lisA2PFilename2;
PkgStringGrid.Columns[6].Title.Caption:=lisLastOpened;
i:=1;
Node:=FLinks.FindLowest;
while Node<>nil do begin
Link:=TPkgLinkInfo(Node.Data);
Node:=Node.Successor;
PkgStringGrid.Cells[0,i]:=PkgStringGrid.Columns[0].ValueUnchecked;
PkgStringGrid.Cells[1,i]:=Link.Name;
PkgStringGrid.Cells[2,i]:=Link.Version.AsString;
case Link.Origin of
ploGlobal: OriginStr:=lisPLDGlobal;
ploOnline: OriginStr:=lisPLDOnline;
ploUser : OriginStr:=lisPLDUser;
end;
PkgStringGrid.Cells[3,i]:=OriginStr;
if Link.Origin = ploOnline then
s:=lrsPLDValid
else
begin
if Link.IsValid then
s:=lrsPLDValid
else if (Info<>nil) and (Info.LPKError<>'') then
s:=Info.LPKError
else
s:=lrsPLDInvalid;
end;
PkgStringGrid.Cells[4,i]:=s;
if Link.Origin = ploOnline then
PkgStringGrid.Cells[5,i]:=Link.LPKUrl
else
PkgStringGrid.Cells[5,i]:=Link.EffectiveFilename;
if Link.Origin = ploOnline then
PkgStringGrid.Cells[6,i]:= FormatDateTime('YYYY/MM/DD hh:mm:ss', Link.OPMFileDate)
else
begin
if Link.LastUsed=0 then
PkgStringGrid.Cells[6,i]:= lisNever
else
PkgStringGrid.Cells[6,i]:= FormatDateTime('YYYY/MM/DD hh:mm:ss', Link.LastUsed);
end;
inc(i);
end;
PkgStringGrid.AutoAdjustColumns;
if PkgStringGrid.SortColumn>=0 then
PkgStringGrid.SortColRow(true,PkgStringGrid.SortColumn);
UpdateFacets;
end;
procedure TPackageLinksDialog.UpdateFacets;
begin
ShowGlobalLinksCheckBox.Caption:=lisPLDShowGlobalLinksIn
+LazPackageLinks.GetGlobalLinkDirectory+'*.lpl'
+' ('+IntToStr(CountGlobalLinks)+')';
ShowOnlineLinksCheckBox.Caption:=lisPLDShowOnlineLinks
+' ('+IntToStr(CountOnlineLinks)+')';
ShowUserLinksCheckBox.Caption:=lisPLDShowUserLinksIn
+LazPackageLinks.GetUserLinkFile
+' ('+IntToStr(CountUserLinks)+')';
LPKFileValidCheckBox.Caption:=Format(lrsPLDLpkFileValid, [IntToStr(CountLPKValid)]);
LPKFileInvalidCheckBox.Caption:=Format(lrsPLDLpkFileInvalid, [IntToStr(CountLPKInvalid)]);
end;
procedure TPackageLinksDialog.RescanGlobalLinks;
begin
LazPackageLinks.UpdateGlobalLinks;
end;
procedure TPackageLinksDialog.ClearLinks;
begin
if FLinks<>nil then begin
FLinks.FreeAndClear;
FreeAndNil(FLinks);
end;
end;
procedure TPackageLinksDialog.IteratePackages(APackage: TLazPackageID);
var
NewLink: TPkgLinkInfo;
begin
NewLink:=TPkgLinkInfo.Create;
NewLink.Assign(APackage);
NewLink.Origin:=FCollectingOrigin;
FLinks.Add(NewLink);
end;
function TPackageLinksDialog.GetLinkAtRow(Row: integer): TPkgLinkInfo;
var
Origin: TPkgLinkOrigin;
EffectiveFilename, S: String;
begin
Result:=nil;
if (Row<1) or (Row>=PkgStringGrid.RowCount) then exit;
EffectiveFilename:=PkgStringGrid.Cells[5,Row];
S := PkgStringGrid.Cells[3,Row];
if S = lisPLDGlobal then
Origin:=ploGlobal
else if S = lisPLDOnline then
Origin:=ploOnline
else // lisPLDUser
Origin:=ploUser;
Result:=GetLinkWithEffectiveFilename(EffectiveFilename,[Origin]);
end;
function TPackageLinksDialog.GetLinkWithEffectiveFilename(Filename: string;
Origins: TPkgLinkOrigins): TPkgLinkInfo;
var
Node: TAvlTreeNode;
begin
Node:=FLinks.FindLowest;
while Node<>nil do begin
Result:=TPkgLinkInfo(Node.Data);
if (Result.Origin in Origins) and (Result.EffectiveFilename=Filename) then
exit;
Node:=Node.Successor;
end;
Result:=nil;
end;
procedure TPackageLinksDialog.PackageListAvailable(Sender: TObject);
begin
DebugLn('TPackageLinksDialog.PackageListAvailable.');
UpdatePackageList;
end;
{ TPkgLinkInfo }
constructor TPkgLinkInfo.Create;
begin
inherited Create;
FLPKInfo:=TLPKInfo.Create(TLazPackageID.Create);
end;
destructor TPkgLinkInfo.Destroy;
begin
FreeAndNil(FLPKInfo);
inherited Destroy;
end;
procedure TPkgLinkInfo.Assign(Source: TPersistent);
var
Link: TLazPackageLink;
begin
if Source is TLazPackageID then begin
AssignID(TLazPackageID(Source));
LPKInfo.Assign(Source);
if Source is TLazPackageLink then begin
Link:=TLazPackageLink(Source);
Origin:=Link.Origin;
LPKUrl := Link.LPKUrl;
LPKFilename:=Link.LPKFilename;
LPLFilename:=Link.LPLFilename;
AutoCheckExists:=Link.AutoCheckExists;
LPKFileDateValid:=Link.LPKFileDateValid;
LPKFileDate:=Link.LPKFileDate;
LastUsed:=Link.LastUsed;
OPMFileDate:=Link.OPMFileDate;
end;
end else
inherited Assign(Source);
end;
end.