lazarus/packager/projpackfilepropgui.pas

552 lines
19 KiB
ObjectPascal

{
***************************************************************************
* *
* 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: Juha Manninen
Abstract:
TProjPackFilePropGui creates and sets layout for controls that are used for
properties of files and dependencies in a package or in a project.
Used by the package editor and the project inspector.
}
unit ProjPackFilePropGui;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
// LCL
LResources, Forms, Controls, StdCtrls, ComCtrls, Dialogs,
// LazControls
TreeFilterEdit,
// LazUtils
LazLoggerBase,
// BuildIntf
PackageDependencyIntf, PackageLinkIntf, PackageIntf,
// IdeIntf
FormEditingIntf, IDEImagesIntf,
// IDE
LazarusIDEStrConsts, ProjPackEditing, PackageLinks, MainIntf;
type
TMultiBool = (mubNone, mubAllTrue, mubAllFalse, mubMixed);
TGetPkgDepEvent = function(Immediately: boolean): TPkgDependencyID of object;
{ TProjPackFilePropGui }
TProjPackFilePropGui = class
private
fOwner: TWinControl;
FNodeDataList: array[TPENodeType] of TPENodeData;
fOnGetPkgDep: TGetPkgDepEvent;
procedure MinMaxVersionEditChange(Sender: TObject);
procedure UseMinVersionCheckBoxChange(Sender: TObject);
procedure UseMaxVersionCheckBoxChange(Sender: TObject);
public
// ImageIndexes
ImageIndexRequired: integer;
ImageIndexRemovedRequired: integer;
ImageIndexConflict: integer;
ImageIndexAvailableOnline: integer;
// file properties
AddToUsesPkgSectionCheckBox: TCheckBox;
CallRegisterProcCheckBox: TCheckBox;
RegisteredPluginsGroupBox: TGroupBox;
RegisteredListBox: TListBox;
DisableI18NForLFMCheckBox: TCheckBox; // I18N
// dependency properties
UseMinVersionCheckBox: TCheckBox;
MinVersionEdit: TEdit;
UseMaxVersionCheckBox: TCheckBox;
MaxVersionEdit: TEdit;
ApplyDependencyButton: TButton;
// Values used when setting controls' visibility and Enabled state.
ControlVisible, ControlEnabled: Boolean;
constructor Create(aOwner: TWinControl; aPackageGui: Boolean);
destructor Destroy; override;
function CreateNodeData(Typ: TPENodeType; aName: string; aRemoved: boolean): TPENodeData;
procedure FreeNodeData(Typ: TPENodeType);
function GetDependencyImageIndex(aDep: TPkgDependencyID): Integer;
procedure SetAddToUsesCB(State: TMultiBool);
procedure SetCallRegisterProcCB(State: TMultiBool);
procedure SetRegisteredPluginsGB(aPlugins: TStringList);
procedure SetMinMaxVisibility;
procedure SetMinMaxValues(aDep: TPkgDependencyID);
procedure SetDisableI18NCB(State: TMultiBool);
function CheckApplyDependency(aDep: TPkgDependencyID): Boolean;
procedure UpdateApplyDependencyButton(Immediately: boolean = False);
property OnGetPkgDep: TGetPkgDepEvent read fOnGetPkgDep write fOnGetPkgDep;
end;
function GetNodeData(TVNode: TTreeNode): TPENodeData;
function NodeTreeIsIn(xIterNode, xParentNode: TTreeNode): Boolean;
function FindOPLink(const ADependency: TPkgDependencyID): TPackageLink;
function OPNote(ADep: TPkgDependencyID): string;
function OpmAddOrOpenDependency(ADep: TPkgDependencyID): Boolean;
procedure OpmInstallPendingDependencies;
implementation
function GetNodeData(TVNode: TTreeNode): TPENodeData;
var
o: TObject;
begin
Result:=nil;
if TVNode=nil then exit;
o:=TObject(TVNode.Data);
if o is TFileNameItem then
o:=TObject(TFileNameItem(o).Data);
if o is TPENodeData then
Result:=TPENodeData(o);
end;
function NodeTreeIsIn(xIterNode, xParentNode: TTreeNode): Boolean;
// Is xIterNode under xParentNode, or the xParentNode itself?
begin
Result := (xIterNode = xParentNode);
if not Result and Assigned(xIterNode) then
Result := NodeTreeIsIn(xIterNode.Parent, xParentNode);
end;
function FindOPLink(const ADependency: TPkgDependencyID): TPackageLink;
var
PackageLink: TPackageLink;
begin
Result := nil;
if OPMInterface = Nil then Exit;
PackageLink := LazPackageLinks.FindLinkWithPkgName(ADependency.PackageName);
if Assigned(PackageLink) and (PackageLink.Origin = ploOnline)
and ADependency.IsCompatible(PackageLink.Version) then
begin
ADependency.LoadPackageResult := lprAvailableOnline;
Result := PackageLink;
end;
end;
function OPNote(ADep: TPkgDependencyID): string;
// Returns a note about online package's availability, if there is one.
begin
if (ADep.LoadPackageResult<>lprSuccess) and (FindOPLink(ADep)<>nil) then
Result:=' '+lisPckEditAvailableOnline
else
Result:='';
end;
// The following 2 functions are a pair.
// The first may add online package names to a list and the second downloads them.
var
OpmPkgLinks: TList = Nil;
function OpmAddOrOpenDependency(ADep: TPkgDependencyID): Boolean;
var
PackageLink: TPackageLink;
begin
Result := True;
if aDep.DependencyType <> pdtLazarus then Exit;
case aDep.LoadPackageResult of
lprSuccess:
if PackageEditingInterface.DoOpenPackageWithName(aDep.PackageName,[],false)<>mrOk then
Exit(False);
lprAvailableOnline:
begin // Install
PackageLink := FindOPLink(ADep);
if Assigned(PackageLink) then
begin
if OpmPkgLinks = Nil then
OpmPkgLinks := TList.Create;
OpmPkgLinks.Add(PackageLink);
end;
end;
else begin // lprNotFound
if Assigned(OPMInterface) and not OPMInterface.IsPackageListLoaded then
OPMInterface.GetPackageList;
PackageLink := FindOPLink(ADep); // Sets lprAvailableOnline flag if package found.
end;
end;
end;
procedure OpmInstallPendingDependencies;
begin
if Assigned(OpmPkgLinks) then
try
if OPMInterface.InstallPackages(OpmPkgLinks) = mrRetry then
MainIDEInterface.DoBuildLazarus([]); // mrRetry means IDE must be rebuilt.
finally
FreeAndNil(OpmPkgLinks);
end;
end;
{ TProjPackFilePropGui }
constructor TProjPackFilePropGui.Create(aOwner: TWinControl; aPackageGui: Boolean);
begin
fOwner := aOwner;
// ImageIndexes to be used later.
ImageIndexRequired := IDEImages.LoadImage('pkg_required');
ImageIndexRemovedRequired := IDEImages.LoadImage('pkg_removedrequired');
ImageIndexConflict := IDEImages.LoadImage('pkg_conflict');
ImageIndexAvailableOnline := IDEImages.LoadImage('pkg_install');
if aPackageGui then
begin
// file properties (not used for project files)
// ---------------
CallRegisterProcCheckBox := TCheckBox.Create(fOwner);
CallRegisterProcCheckBox.Parent := fOwner;
CallRegisterProcCheckBox.Left := 6;
CallRegisterProcCheckBox.Top := 0;
CallRegisterProcCheckBox.Width := 185;
CallRegisterProcCheckBox.ShowHint := True;
CallRegisterProcCheckBox.TabOrder := 0;
CallRegisterProcCheckBox.Caption := lisPckEditRegisterUnit;
CallRegisterProcCheckBox.Hint := Format(lisPckEditCallRegisterProcedureOfSelectedUnit, ['"', '"']);
AddToUsesPkgSectionCheckBox := TCheckBox.Create(fOwner);
AddToUsesPkgSectionCheckBox.Parent := fOwner;
AddToUsesPkgSectionCheckBox.AnchorSideLeft.Control := CallRegisterProcCheckBox;
AddToUsesPkgSectionCheckBox.AnchorSideLeft.Side := asrBottom;
AddToUsesPkgSectionCheckBox.Left := 195;
AddToUsesPkgSectionCheckBox.Top := 0;
AddToUsesPkgSectionCheckBox.Width := 222;
AddToUsesPkgSectionCheckBox.BorderSpacing.Left := 10;
AddToUsesPkgSectionCheckBox.ShowHint := True;
AddToUsesPkgSectionCheckBox.TabOrder := 1;
AddToUsesPkgSectionCheckBox.Caption := lisPkgMangUseUnit;
AddToUsesPkgSectionCheckBox.Hint := lisPkgMangAddUnitToUsesClause;
RegisteredPluginsGroupBox := TGroupBox.Create(fOwner);
RegisteredPluginsGroupBox.Parent := fOwner;
RegisteredPluginsGroupBox.Left := 3;
RegisteredPluginsGroupBox.Height := 165;
RegisteredPluginsGroupBox.Top := 27; // Too early to set final top according
// to screen PPI
RegisteredPluginsGroupBox.Width := 452;
RegisteredPluginsGroupBox.Align := alBottom;
RegisteredPluginsGroupBox.Anchors := [akTop, akLeft, akRight, akBottom];
RegisteredPluginsGroupBox.BorderSpacing.Top := 6;
RegisteredPluginsGroupBox.TabOrder := 7;
RegisteredPluginsGroupBox.Caption := lisPckEditRegisteredPlugins;
RegisteredListBox := TListBox.Create(fOwner);
RegisteredListBox.Parent := RegisteredPluginsGroupBox;
RegisteredListBox.Align := alClient;
RegisteredListBox.ScrollWidth := 448;
RegisteredListBox.Style := lbOwnerDrawFixed;
RegisteredListBox.TabOrder := 0;
RegisteredListBox.ItemHeight := ComponentPaletteImageHeight;
// I18N
DisableI18NForLFMCheckBox := TCheckBox.Create(fOwner);
DisableI18NForLFMCheckBox.Parent := fOwner;
DisableI18NForLFMCheckBox.AnchorSideLeft.Control := AddToUsesPkgSectionCheckBox;
DisableI18NForLFMCheckBox.AnchorSideLeft.Side := asrBottom;
DisableI18NForLFMCheckBox.AnchorSideTop.Control := AddToUsesPkgSectionCheckBox;
DisableI18NForLFMCheckBox.Left := 423;
DisableI18NForLFMCheckBox.Top := 0;
DisableI18NForLFMCheckBox.Width := 208;
DisableI18NForLFMCheckBox.BorderSpacing.Left := 10;
DisableI18NForLFMCheckBox.ShowHint := True;
DisableI18NForLFMCheckBox.TabOrder := 8;
DisableI18NForLFMCheckBox.Caption := lisPckDisableI18NOfLfm;
DisableI18NForLFMCheckBox.Hint := lisPckWhenTheFormIsSavedTheIDECanStoreAllTTranslateString;
end;
// dependency properties
// ---------------------
MinVersionEdit := TEdit.Create(fOwner);
MinVersionEdit.Parent := fOwner;
MinVersionEdit.Left := 200;
MinVersionEdit.Top := 4;
MinVersionEdit.Width := 100;
MinVersionEdit.BorderSpacing.Left := 6;
MinVersionEdit.TabOrder := 3;
MinVersionEdit.OnChange := @MinMaxVersionEditChange;
UseMinVersionCheckBox := TCheckBox.Create(fOwner);
UseMinVersionCheckBox.Parent := fOwner;
UseMinVersionCheckBox.Left := 6;
UseMinVersionCheckBox.AnchorSideTop.Control := MinVersionEdit;
UseMinVersionCheckBox.AnchorSideTop.Side := asrCenter;
UseMinVersionCheckBox.TabOrder := 2;
UseMinVersionCheckBox.Caption := lisPckEditMinimumVersion;
UseMinVersionCheckBox.OnChange := @UseMinVersionCheckBoxChange;
MaxVersionEdit := TEdit.Create(fOwner);
MaxVersionEdit.Parent := fOwner;
MaxVersionEdit.Width := 100;
MaxVersionEdit.BorderSpacing.Top := 4;
MaxVersionEdit.AnchorSideTop.Side := asrBottom;
MaxVersionEdit.AnchorSideTop.Control := MinVersionEdit;
MaxVersionEdit.AnchorSideLeft.Side := asrLeft;
MaxVersionEdit.AnchorSideLeft.Control := MinVersionEdit;
MaxVersionEdit.TabOrder := 5;
MaxVersionEdit.OnChange := @MinMaxVersionEditChange;
UseMaxVersionCheckBox := TCheckBox.Create(fOwner);
UseMaxVersionCheckBox.Parent := fOwner;
UseMaxVersionCheckBox.Left := 6;
UseMaxVersionCheckBox.AnchorSideTop.Control := MaxVersionEdit;
UseMaxVersionCheckBox.AnchorSideTop.Side := asrCenter;
UseMaxVersionCheckBox.TabOrder := 4;
UseMaxVersionCheckBox.Caption := lisPckEditMaximumVersion;
UseMaxVersionCheckBox.OnChange := @UseMaxVersionCheckBoxChange;
// anchor edit controls to the right edge of the longest check box caption
if Length(UseMinVersionCheckBox.Caption) > Length(UseMaxVersionCheckBox.Caption) then
MinVersionEdit.AnchorSideLeft.Control := UseMinVersionCheckBox
else
MinVersionEdit.AnchorSideLeft.Control := UseMaxVersionCheckBox;
MinVersionEdit.AnchorSideLeft.Side := asrRight;
ApplyDependencyButton := TButton.Create(fOwner);
ApplyDependencyButton.Parent := fOwner;
ApplyDependencyButton.Left := 6;
ApplyDependencyButton.AutoSize := True;
ApplyDependencyButton.BorderSpacing.Top := 6;
ApplyDependencyButton.AnchorSideTop.Control := MaxVersionEdit;
ApplyDependencyButton.AnchorSideTop.Side := asrBottom;
ApplyDependencyButton.TabOrder := 6;
ApplyDependencyButton.Caption := lisPckEditApplyChanges;
end;
destructor TProjPackFilePropGui.Destroy;
var
nt: TPENodeType;
begin
for nt:=Low(TPENodeType) to High(TPENodeType) do
FreeNodeData(nt);
inherited Destroy;
end;
function TProjPackFilePropGui.CreateNodeData(Typ: TPENodeType; aName: string;
aRemoved: boolean): TPENodeData;
begin
Result:=TPENodeData.Create(Typ,aName,aRemoved);
Result.Next:=FNodeDataList[Typ];
FNodeDataList[Typ]:=Result;
end;
procedure TProjPackFilePropGui.FreeNodeData(Typ: TPENodeType);
var
NodeData, n: TPENodeData;
begin
NodeData:=FNodeDataList[Typ];
while NodeData<>nil do begin
n:=NodeData;
NodeData:=NodeData.Next;
if Assigned(n.Branch) Then
n.Branch.FreeNodeData(n.Node);
n.Free;
end;
FNodeDataList[Typ]:=nil;
end;
function TProjPackFilePropGui.GetDependencyImageIndex(aDep: TPkgDependencyID): Integer;
begin
if aDep.Removed then
Result := ImageIndexRemovedRequired
else if aDep.LoadPackageResult=lprSuccess then
Result := ImageIndexRequired
else if aDep.LoadPackageResult=lprAvailableOnline {Assigned(FindOPLink(aDep))} then
Result := ImageIndexAvailableOnline
else
Result := ImageIndexConflict;
end;
procedure SetCheckBox(Box: TCheckBox; aVisible: boolean; State: TMultiBool);
begin
Box.Visible:=aVisible;
case State of
mubAllTrue:
begin
Box.State:=cbChecked;
Box.AllowGrayed:=false;
end;
mubAllFalse:
begin
Box.State:=cbUnchecked;
Box.AllowGrayed:=false;
end;
mubMixed:
begin
Box.State:=cbGrayed;
Box.AllowGrayed:=true;
end;
end;
end;
procedure TProjPackFilePropGui.SetAddToUsesCB(State: TMultiBool);
begin
SetCheckBox(AddToUsesPkgSectionCheckBox, ControlVisible, State);
AddToUsesPkgSectionCheckBox.Enabled := ControlEnabled;
end;
procedure TProjPackFilePropGui.SetCallRegisterProcCB(State: TMultiBool);
begin
SetCheckBox(CallRegisterProcCheckBox, ControlVisible, State);
CallRegisterProcCheckBox.Enabled := ControlEnabled;
end;
procedure TProjPackFilePropGui.SetRegisteredPluginsGB(aPlugins: TStringList);
begin
RegisteredPluginsGroupBox.Visible := ControlVisible;
RegisteredPluginsGroupBox.Enabled := ControlEnabled;
if not ControlVisible then
aPlugins.Clear;
RegisteredListBox.Items.Assign(aPlugins);
end;
procedure TProjPackFilePropGui.SetMinMaxVisibility;
begin
UseMinVersionCheckBox.Visible := ControlVisible;
MinVersionEdit.Visible := ControlVisible;
UseMaxVersionCheckBox.Visible := ControlVisible;
MaxVersionEdit.Visible := ControlVisible;
ApplyDependencyButton.Visible := ControlVisible;
end;
procedure TProjPackFilePropGui.SetMinMaxValues(aDep: TPkgDependencyID);
begin
UseMinVersionCheckBox.Checked := pdfMinVersion in aDep.Flags;
MinVersionEdit.Text := aDep.MinVersion.AsString;
MinVersionEdit.Enabled := pdfMinVersion in aDep.Flags;
UseMaxVersionCheckBox.Checked := pdfMaxVersion in aDep.Flags;
MaxVersionEdit.Text := aDep.MaxVersion.AsString;
MaxVersionEdit.Enabled := pdfMaxVersion in aDep.Flags;
end;
procedure TProjPackFilePropGui.SetDisableI18NCB(State: TMultiBool);
begin
SetCheckBox(DisableI18NForLFMCheckBox, ControlVisible, State);
DisableI18NForLFMCheckBox.Enabled := ControlEnabled;
end;
function TProjPackFilePropGui.CheckApplyDependency(aDep: TPkgDependencyID): Boolean;
var
Flags: TPkgDependencyFlags;
MinVers, MaxVers: TPkgVersion;
begin
Result := False;
MinVers:=TPkgVersion.Create;
MaxVers:=TPkgVersion.Create;
try
// Assign relevant data to temp variables
Flags:=aDep.Flags;
MinVers.Assign(aDep.MinVersion);
MaxVers.Assign(aDep.MaxVersion);
// read minimum version
if UseMinVersionCheckBox.Checked then begin
Include(Flags, pdfMinVersion);
if not MinVers.ReadString(MinVersionEdit.Text) then begin
MessageDlg(lisPckEditInvalidMinimumVersion,
Format(lisPckEditTheMinimumVersionIsNotAValidPackageVersion,
[MinVersionEdit.Text, LineEnding]),
mtError,[mbCancel],0);
exit;
end;
end
else
Exclude(Flags, pdfMinVersion);
// read maximum version
if UseMaxVersionCheckBox.Checked then begin
Include(Flags, pdfMaxVersion);
if not MaxVers.ReadString(MaxVersionEdit.Text) then begin
MessageDlg(lisPckEditInvalidMaximumVersion,
Format(lisPckEditTheMaximumVersionIsNotAValidPackageVersion,
[MaxVersionEdit.Text, LineEnding]),
mtError,[mbCancel],0);
exit;
end;
end
else
Exclude(Flags, pdfMaxVersion);
// Assign changes back to the dependency
aDep.Flags := Flags;
aDep.MinVersion.Assign(MinVers);
aDep.MaxVersion.Assign(MaxVers);
Result := True;
finally
MaxVers.Free;
MinVers.Free;
end;
end;
procedure TProjPackFilePropGui.UpdateApplyDependencyButton(Immediately: boolean);
var
DependencyChanged: Boolean;
AVersion: TPkgVersion;
CurDependency: TPkgDependencyID;
begin
Assert(Assigned(OnGetPkgDep), 'UpdateApplyDependencyButton: OnPkgDepToUpdate = Nil.');
CurDependency := OnGetPkgDep(Immediately);
DependencyChanged := false;
if Assigned(CurDependency) then
begin
// check min version
if UseMinVersionCheckBox.Checked <> (pdfMinVersion in CurDependency.Flags) then
DependencyChanged := true;
if UseMinVersionCheckBox.Checked then begin
AVersion := TPkgVersion.Create;
if AVersion.ReadString(MinVersionEdit.Text)
and (AVersion.Compare(CurDependency.MinVersion)<>0) then
DependencyChanged := true;
AVersion.Free;
end;
// check max version
if UseMaxVersionCheckBox.Checked <> (pdfMaxVersion in CurDependency.Flags) then
DependencyChanged := true;
if UseMaxVersionCheckBox.Checked then begin
AVersion := TPkgVersion.Create;
if AVersion.ReadString(MaxVersionEdit.Text)
and (AVersion.Compare(CurDependency.MaxVersion)<>0) then
DependencyChanged := true;
AVersion.Free;
end;
end;
ApplyDependencyButton.Enabled := DependencyChanged;
end;
procedure TProjPackFilePropGui.MinMaxVersionEditChange(Sender: TObject);
begin
UpdateApplyDependencyButton;
end;
procedure TProjPackFilePropGui.UseMinVersionCheckBoxChange(Sender: TObject);
begin
MinVersionEdit.Enabled := UseMinVersionCheckBox.Checked;
UpdateApplyDependencyButton;
end;
procedure TProjPackFilePropGui.UseMaxVersionCheckBoxChange(Sender: TObject);
begin
MaxVersionEdit.Enabled := UseMaxVersionCheckBox.Checked;
UpdateApplyDependencyButton;
end;
end.