{
 /***************************************************************************
                            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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.        *
 *                                                                         *
 ***************************************************************************

  Author: Mattias Gaertner

  Abstract:
    TPkgManager is the class for the global PkgBoss variable, which controls
    the whole package system in the IDE.
}
unit PkgManager;

{$mode objfpc}{$H+}

interface

{$I ide.inc}

uses
  {$IFDEF IDE_MEM_CHECK}
  MemCheck,
  {$ENDIF}
  // FCL, LCL
  TypInfo, Classes, SysUtils, LCLProc, Forms, Controls, Dialogs, Menus,
  contnrs, StringHashList, Translations, LResources,
  // codetools
  CodeToolsConfig, CodeToolManager, CodeCache, CodeToolsStructs, BasicCodeTools,
  FileProcs, Laz2_XMLCfg, lazutf8classes,
  // IDE Interface
  SrcEditorIntf, NewItemIntf, ProjectIntf, PackageIntf, CompOptsIntf,
  MenuIntf, IDEWindowIntf, PropEdits, MacroIntf, LazIDEIntf,
  // IDE
  LazarusIDEStrConsts, IDEProcs, ObjectLists, DialogProcs, IDECommands,
  IDEOptionDefs, EnvironmentOpts, MiscOptions, InputHistory,
  Project, ComponentReg, UComponentManMain, PackageEditor, AddToPackageDlg,
  PackageDefs, PackageLinks, PackageSystem, OpenInstalledPkgDlg,
  PkgGraphExplorer, BrokenDependenciesDlg, CompilerOptions,
  IDETranslations, TransferMacros, BuildLazDialog, NewDialog,
  IDEDialogs, ProjectInspector, ComponentPalette, SourceEditor,
  AddFileToAPackageDlg, LazarusPackageIntf, PublishProjectDlg, PkgLinksDlg,
  InstallPkgSetDlg, ConfirmPkgListDlg, NewPkgComponentDlg,
  // bosses
  BaseBuildManager, BasePkgManager, MainBar, MainIntf, MainBase;

type
  { TPkgManager }

  TPkgManager = class(TBasePkgManager)
    // events - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    // package editor
    function OnPackageEditorAddToProject(Sender: TObject; APackage: TLazPackage;
                                     OnlyTestIfPossible: boolean): TModalResult;
    function OnPackageEditorCompilePackage(Sender: TObject;
                          APackage: TLazPackage;
                          CompileClean, CompileRequired: boolean): TModalResult;
    function OnPackageEditorCreateFile(Sender: TObject;
                                       Params: TAddToPkgResult): TModalResult;
    function OnPackageEditorCreateMakefile(Sender: TObject;
                                           APackage: TLazPackage): TModalResult;
    function OnPackageEditorDeleteAmbiguousFiles(Sender: TObject;
      APackage: TLazPackage; const Filename: string): TModalResult;
    function OnPackageEditorInstallPackage(Sender: TObject;
                                           APackage: TLazPackage): TModalResult;
    function OnPackageEditorOpenPackage(Sender: TObject; APackage: TLazPackage
                                        ): TModalResult;
    function OnPackageEditorOpenPkgFile(Sender: TObject; PkgFile: TPkgFile
                                        ): TModalResult;
    function OnPackageEditorPublishPackage(Sender: TObject;
      APackage: TLazPackage): TModalResult;
    function OnPackageEditorRevertPackage(Sender: TObject; APackage: TLazPackage
      ): TModalResult;
    function OnPackageEditorSavePackage(Sender: TObject; APackage: TLazPackage;
                                        SaveAs: boolean): TModalResult;
    function OnPackageEditorUninstallPackage(Sender: TObject;
                                           APackage: TLazPackage): TModalResult;
    function OnPackageEditorViewPkgSource(Sender: TObject;
                                          APackage: TLazPackage): TModalResult;
    procedure OnAfterWritePackage(Sender: TObject; Restore: boolean);
    procedure OnBeforeReadPackage(Sender: TObject);
    procedure OnPackageEditorFreeEditor(APackage: TLazPackage);
    procedure OnPackageEditorGetUnitRegisterInfo(Sender: TObject;
                              const AFilename: string; var TheUnitName: string;
                              var HasRegisterProc: boolean);

    // package graph
    function PackageGraphExplorerOpenPackage(Sender: TObject;
                                           APackage: TLazPackage): TModalResult;
    function PackageGraphExplorerOpenProject(Sender: TObject;
                                             AProject: TProject): TModalResult;
    function PackageGraphExplorerUninstallPackage(Sender: TObject;
                                           APackage: TLazPackage): TModalResult;
    procedure PackageGraphAddPackage(Pkg: TLazPackage);
    procedure PackageGraphBeginUpdate(Sender: TObject);
    procedure PackageGraphChangePackageName(APackage: TLazPackage;
                                            const OldName: string);
    procedure PackageGraphDeletePackage(APackage: TLazPackage);
    procedure PackageGraphDependencyModified(ADependency: TPkgDependency);
    procedure PackageGraphEndUpdate(Sender: TObject; GraphChanged: boolean);
    procedure PackageGraphFindFPCUnit(const AUnitName, Directory: string;
                                      var Filename: string);

    // menu
    procedure MainIDEitmPkgOpenPackageFileClick(Sender: TObject);
    procedure MainIDEitmPkgPkgGraphClick(Sender: TObject);
    procedure MainIDEitmPkgEditInstallPkgsClick(Sender: TObject);
    procedure MainIDEitmPkgAddCurFileToPkgClick(Sender: TObject);
    procedure MainIDEitmPkgNewComponentClick(Sender: TObject);
    procedure MainIDEitmPkgOpenPackageOfCurUnitClicked(Sender: TObject);
    procedure MainIDEitmConfigCustomCompsClicked(Sender: TObject);
    procedure MainIDEitmOpenRecentPackageClicked(Sender: TObject);
    procedure MainIDEitmPkgOpenPackageClicked(Sender: TObject);
    procedure MainIDEitmPkgNewPackageClick(Sender: TObject);
    procedure MainIDEViewPackageLinksClicked(Sender: TObject);

    // component palette
    procedure IDEComponentPaletteEndUpdate(Sender: TObject;
                                           PaletteChanged: boolean);
    procedure IDEComponentPaletteOpenPackage(Sender: TObject);
    procedure IDEComponentPaletteOpenUnit(Sender: TObject);

    // source editor
    procedure OnOpenPackageForCurrentSrcEditFile(Sender: TObject);

    // LCL
    procedure OnApplicationIdle(Sender: TObject; var Done: Boolean);

    // package links
    function PkgLinksDependencyOwnerGetPkgFilename(PkgLinks: TPackageLinks;
      Dependency: TPkgDependency): boolean;

    // misc
    procedure GetDependencyOwnerDescription(Dependency: TPkgDependency;
                                            out Description: string);
    procedure GetDependencyOwnerDirectory(Dependency: TPkgDependency;
                                          out Directory: string);
    procedure PackageFileLoaded(Sender: TObject);
    procedure OnCheckInstallPackageList(PkgIDList: TObjectList;
                                     RemoveConflicts: boolean; out Ok: boolean);
    function LoadDependencyList(FirstDependency: TPkgDependency;
                                Quiet: boolean): TModalResult;
    procedure CreateIDEWindow(Sender: TObject; aFormName: string;
                          var AForm: TCustomForm; DoDisableAutoSizing: boolean);
  private
    // helper functions
    FLastLazarusSrcDir: string;
    function DoShowSavePackageAsDialog(APackage: TLazPackage): TModalResult;
    function CheckPackageGraphForCompilation(APackage: TLazPackage;
                                 FirstDependency: TPkgDependency;
                                 const Directory: string;
                                 ShowAbort: boolean): TModalResult;
    function DoGetUnitRegisterInfo(const AFilename: string;
                          var TheUnitName: string; var HasRegisterProc: boolean;
                          IgnoreErrors: boolean): TModalResult;
    procedure SaveAutoInstallDependencies;
    procedure LoadStaticCustomPackages;
    function LoadInstalledPackage(const PackageName: string;
                    AddToAutoInstall: boolean; var Quiet: boolean): TLazPackage;
    procedure LoadAutoInstallPackages;
    procedure AddUnitToProjectMainUsesSection(AProject: TProject;
                                    const AnUnitName, AnUnitInFilename: string);
  public
    constructor Create(TheOwner: TComponent); override;
    destructor Destroy; override;

    // initialization and menu
    procedure ConnectMainBarEvents; override;
    procedure ConnectSourceNotebookEvents; override;
    procedure SetupMainBarShortCuts; override;
    procedure SetRecentPackagesMenu; override;
    procedure AddFileToRecentPackages(const Filename: string);
    procedure SaveSettings; override;
    procedure UpdateVisibleComponentPalette; override;
    procedure ProcessCommand(Command: word; var Handled: boolean); override;
    procedure OnSourceEditorPopupMenu(const AddMenuItemProc: TAddMenuItemProc); override;
    procedure TranslateResourceStrings; override;

    // files
    function GetDefaultSaveDirectoryForFile(const Filename: string): string; override;
    function GetPublishPackageDir(APackage: TLazPackage): string;
    function OnRenameFile(const OldFilename, NewFilename: string;
                          IsPartOfProject: boolean): TModalResult; override;
    function FindIncludeFileInProjectDependencies(Project1: TProject;
                          const Filename: string): string; override;
    function AddUnitDependenciesForComponentClasses(const UnitFilename: string;
                         ComponentClassnames: TStrings;
                         Quiet: boolean = false): TModalResult; override;
    function GetMissingDependenciesForUnit(const UnitFilename: string;
                         ComponentClassnames: TStrings;
                         var List: TObjectArray): TModalResult;
    function GetOwnersOfUnit(const UnitFilename: string): TFPList; override;
    procedure ExtendOwnerListWithUsedByOwners(OwnerList: TFPList); override;
    function GetSourceFilesOfOwners(OwnerList: TFPList): TStrings; override;
    function GetPossibleOwnersOfUnit(const UnitFilename: string;
                                     Flags: TPkgIntfOwnerSearchFlags): TFPList; override;
    function GetPackageOfCurrentSourceEditor(out APackage: TIDEPackage): TPkgFile;
    function GetPackageOfSourceEditor(out APackage: TIDEPackage; ASrcEdit: TObject): TLazPackageFile; override;
    function IsOwnerDependingOnPkg(AnOwner: TObject; const PkgName: string;
                                   out DependencyOwner: TObject): boolean; override;
    function AddDependencyToOwners(OwnerList: TFPList; APackage: TIDEPackage;
                   OnlyTestIfPossible: boolean = false): TModalResult; override;
    function DoOpenPkgFile(PkgFile: TPkgFile): TModalResult;
    function FindVirtualUnitSource(PkgFile: TPkgFile): string;
    function SearchFile(const AFilename: string;
                        SearchFlags: TSearchIDEFileFlags;
                        InObject: TObject): TPkgFile; override;
    function SearchUnitInDesigntimePackages(const AnUnitName: string;
                        InObject: TObject): TPkgFile; override;
    function AddDependencyToUnitOwners(const OwnedFilename,
                              RequiredUnitname: string): TModalResult; override;
    procedure GetPackagesChangedOnDisk(var ListOfPackages: TFPList); override;
    function RevertPackages(APackageList: TFPList): TModalResult; override;

    // package graph
    function AddPackageToGraph(APackage: TLazPackage; Replace: boolean): TModalResult;
    procedure DoShowPackageGraph(Show: boolean);
    procedure DoShowPackageGraphPathList(PathList: TFPList); override;
    function ShowBrokenDependenciesReport(Dependencies: TFPList): TModalResult;
    procedure RebuildDefineTemplates; override;
    procedure LazarusSrcDirChanged; override;
    function GetPackageCount: integer; override;
    function GetPackages(Index: integer): TIDEPackage; override;
    function FindPackageWithName(const PkgName: string; IgnorePackage: TIDEPackage = nil): TIDEPackage; override;
    function RedirectPackageDependency(APackage: TIDEPackage): TIDEPackage; override;

    // project
    function OpenProjectDependencies(AProject: TProject;
                                ReportMissing: boolean): TModalResult; override;
    function CheckProjectHasInstalledPackages(AProject: TProject; 
                                  Interactive: boolean): TModalResult; override;
    function CanOpenDesignerForm(AnUnitInfo: TUnitInfo; 
                                 Interactive: boolean): TModalResult; override;
    function AddProjectDependency(AProject: TProject; APackage: TLazPackage;
                                  OnlyTestIfPossible: boolean = false): TModalResult; override;
    function AddProjectDependency(AProject: TProject;
                                  ADependency: TPkgDependency): TModalResult; override;
    procedure AddProjectRegCompDependency(AProject: TProject;
                          ARegisteredComponent: TRegisteredComponent); override;
    procedure AddProjectLCLDependency(AProject: TProject); override;
    function AddProjectDependencies(AProject: TProject; const Packages: string;
                                  OnlyTestIfPossible: boolean = false): TModalResult; override;
    function OnProjectInspectorOpen(Sender: TObject): boolean; override;
    function OnProjectInspectorAddDependency(Sender: TObject;
                           ADependency: TPkgDependency): TModalResult; override;
    function OnProjectInspectorRemoveDependency(Sender: TObject;
                           ADependency: TPkgDependency): TModalResult; override;
    function OnProjectInspectorReAddDependency(Sender: TObject;
                           ADependency: TPkgDependency): TModalResult; override;

    // package editors
    function DoNewPackage: TModalResult; override;
    function DoShowOpenInstalledPckDlg: TModalResult; override;
    function DoOpenPackage(APackage: TLazPackage; Flags: TPkgOpenFlags;
                           ShowAbort: boolean): TModalResult; override;
    function DoOpenPackageWithName(const APackageName: string;
                         Flags: TPkgOpenFlags; ShowAbort: boolean): TModalResult; override;
    function DoOpenPackageFile(AFilename: string;
                         Flags: TPkgOpenFlags;
                         ShowAbort: boolean): TModalResult; override;
    procedure OpenHiddenModifiedPackages; override;
    function DoSavePackage(APackage: TLazPackage;
                           Flags: TPkgSaveFlags): TModalResult; override;
    function DoSaveAllPackages(Flags: TPkgSaveFlags): TModalResult; override;
    function DoClosePackageEditor(APackage: TLazPackage): TModalResult; override;
    function DoCloseAllPackageEditors: TModalResult; override;
    function DoAddActiveUnitToAPackage: TModalResult;
    function DoNewPackageComponent: TModalResult;
    function SavePackageFiles(APackage: TLazPackage): TModalResult;
    function WarnAboutMissingPackageFiles(APackage: TLazPackage): TModalResult;
    function AddPackageDependency(APackage: TLazPackage; const ReqPackage: string;
                                  OnlyTestIfPossible: boolean = false): TModalResult; override;
    function GetPackageOfEditorItem(Sender: TObject): TIDEPackage; override;

    // package compilation
    function DoCompileProjectDependencies(AProject: TProject;
                               Flags: TPkgCompileFlags): TModalResult; override;
    function DoCompilePackage(APackage: TLazPackage; Flags: TPkgCompileFlags;
                              ShowAbort: boolean): TModalResult; override;
    function DoCreatePackageMakefile(APackage: TLazPackage;
                                     ShowAbort: boolean): TModalResult;

    // package installation
    procedure LoadInstalledPackages; override;
    procedure UnloadInstalledPackages;
    function ShowConfigureCustomComponents: TModalResult; override;
    function DoInstallPackage(APackage: TLazPackage): TModalResult;
    function DoUninstallPackage(APackage: TLazPackage;
                   Flags: TPkgUninstallFlags; ShowAbort: boolean): TModalResult;
    function CheckInstallPackageList(PkgIDList: TObjectList;
                          Flags: TPkgInstallInIDEFlags = []
                          ): boolean; override;
    function InstallPackages(PkgIdList: TObjectList;
                             Flags: TPkgInstallInIDEFlags = []): TModalResult; override;
    procedure DoTranslatePackage(APackage: TLazPackage);
    function DoOpenPackageSource(APackage: TLazPackage): TModalResult;
    function DoCompileAutoInstallPackages(Flags: TPkgCompileFlags;
                                          OnlyBase: boolean): TModalResult; override;
    function DoSaveAutoInstallConfig: TModalResult; override;
    function DoPublishPackage(APackage: TLazPackage; Flags: TPkgSaveFlags;
                              ShowDialog: boolean): TModalResult;
                              
    // components
    function GetUsableComponentUnits(CurRoot: TPersistent): TFPList; override; // list of TUnitInfo
    procedure IterateComponentNames(CurRoot: TPersistent; TypeData: PTypeData;
                                    Proc: TGetStrProc); override;
    function FindUsableComponent(CurRoot: TPersistent;
                        const ComponentPath: string): TComponent; override;
    function FindReferencedRootComponent(CurRoot: TPersistent; 
         const ComponentName: string): TComponent; override;
  end;


  { TLazPackageDescriptors }

  TLazPackageDescriptors = class(TPackageDescriptors)
  private
    fDestroying: boolean;
    fItems: TFPList; // list of TProjectDescriptor
  protected
    function GetItems(Index: integer): TPackageDescriptor; override;
  public
    constructor Create;
    destructor Destroy; override;
    function Count: integer; override;
    function GetUniqueName(const Name: string): string; override;
    function IndexOf(const Name: string): integer; override;
    function FindByName(const Name: string): TPackageDescriptor; override;
    procedure RegisterDescriptor(Descriptor: TPackageDescriptor); override;
    procedure UnregisterDescriptor(Descriptor: TPackageDescriptor); override;
    procedure AddDefaultPackageDescriptors;
  public
    property Items[Index: integer]: TPackageDescriptor read GetItems; default;
  end;
  
  
  { TPackageDescriptorStd }
  
  TPackageDescriptorStd = class(TPackageDescriptor)
  public
    constructor Create; override;
    function GetLocalizedName: string; override;
    function GetLocalizedDescription: string; override;
  end;

var
  LazPackageDescriptors: TLazPackageDescriptors;

implementation

{ TPkgManager }

procedure TPkgManager.MainIDEitmPkgOpenPackageFileClick(Sender: TObject);
var
  OpenDialog: TOpenDialog;
  AFilename: string;
  I: Integer;
  OpenFlags: TPkgOpenFlags;
begin
  OpenDialog:=TOpenDialog.Create(nil);
  try
    InputHistories.ApplyFileDialogSettings(OpenDialog);
    OpenDialog.Title:=lisOpenPackageFile;
    OpenDialog.Options:=OpenDialog.Options+[ofAllowMultiSelect];
    OpenDialog.Filter:=lisLazarusPackage+' (*.lpk)|*.lpk'
                     +'|'+dlgAllFiles+' ('+FileMask+')|'+FileMask;
    if OpenDialog.Execute and (OpenDialog.Files.Count>0) then begin
      OpenFlags:=[pofAddToRecent];
      For I := 0 to OpenDialog.Files.Count-1 do
        Begin
          AFilename:=CleanAndExpandFilename(OpenDialog.Files.Strings[i]);
          if i<OpenDialog.Files.Count-1 then
            Include(OpenFlags,pofMultiOpen)
          else
            Exclude(OpenFlags,pofMultiOpen);
          if DoOpenPackageFile(AFilename,OpenFlags,true)=mrAbort then begin
            break;
          end;
        end;
    end;
    InputHistories.StoreFileDialogSettings(OpenDialog);
  finally
    OpenDialog.Free;
  end;
end;

procedure TPkgManager.MainIDEitmPkgPkgGraphClick(Sender: TObject);
begin
  DoShowPackageGraph(true);
end;

procedure TPkgManager.MainIDEitmPkgEditInstallPkgsClick(Sender: TObject);
var
  RebuildIDE: Boolean;
  PkgIDList: TObjectList;
  Flags: TPkgInstallInIDEFlags;
begin
  RebuildIDE:=false;
  PkgIDList:=nil;
  try
    if ShowEditInstallPkgsDialog(PackageGraph.FirstAutoInstallDependency,
      @OnCheckInstallPackageList,PkgIDList,RebuildIDE)<>mrOk
    then exit;

    Flags:=[piiifSkipChecks,piiifClear];
    if RebuildIDE then Include(Flags,piiifRebuildIDE);
    InstallPackages(PkgIDList,Flags);
  finally
    PkgIDList.Free;
  end;
end;

procedure TPkgManager.IDEComponentPaletteEndUpdate(Sender: TObject;
  PaletteChanged: boolean);
begin
  UpdateVisibleComponentPalette;
end;

procedure TPkgManager.IDEComponentPaletteOpenPackage(Sender: TObject);
begin
  if (Sender=nil) or (not (Sender is TLazPackage)) then exit;
  DoOpenPackage(TLazPackage(Sender),[],false);
end;

procedure TPkgManager.IDEComponentPaletteOpenUnit(Sender: TObject);
var
  PkgComponent: TPkgComponent;
  PkgFile: TPkgFile;
  Filename: String;
begin
  if (Sender=nil) then exit;
  if (Sender is TPkgFile) then
    DoOpenPkgFile(TPkgFile(Sender))
  else if (Sender is TPkgComponent) then begin
    PkgComponent:=TPkgComponent(Sender);
    PkgFile:=PkgComponent.PkgFile;
    if PkgFile=nil then exit;
    Filename:='';
    if PkgFile.FileType=pftVirtualUnit then
      Filename:=FindVirtualUnitSource(PkgFile);
    if Filename='' then
      Filename:=PkgFile.GetFullFilename;
    MainIDE.DoOpenFileAndJumpToIdentifier(
      Filename,PkgComponent.ComponentClass.ClassName,
      -1, -1, // open page somewhere
      [ofOnlyIfExists,ofAddToRecent,ofRegularFile,ofConvertMacros]);
  end;
end;

procedure TPkgManager.GetDependencyOwnerDescription(
  Dependency: TPkgDependency; out Description: string);
begin
  GetDescriptionOfDependencyOwner(Dependency,Description);
end;

procedure TPkgManager.GetDependencyOwnerDirectory(Dependency: TPkgDependency;
  out Directory: string);
begin
  GetDirectoryOfDependencyOwner(Dependency,Directory);
end;

procedure TPkgManager.PackageFileLoaded(Sender: TObject);
begin
  DoCallNotifyHandler(pihtPackageFileLoaded,Sender);
end;

procedure TPkgManager.OnCheckInstallPackageList(PkgIDList: TObjectList;
  RemoveConflicts: boolean; out Ok: boolean);
begin
  Ok:=CheckInstallPackageList(PkgIDList);
end;

function TPkgManager.LoadDependencyList(FirstDependency: TPkgDependency;
  Quiet: boolean): TModalResult;
var
  CurDependency: TPkgDependency;
  OpenResult: TLoadPackageResult;
begin
  Result:=mrCancel;
  // load all packages
  CurDependency:=FirstDependency;
  while CurDependency<>nil do begin
    OpenResult:=PackageGraph.OpenDependency(CurDependency,false);
    if OpenResult<>lprSuccess then begin
      if not Quiet then
        IDEMessageDialog(lisCCOErrorCaption,
          Format(lisUnableToLoadPackage, ['"', CurDependency.AsString, '"']),
          mtError,[mbCancel]);
      exit;
    end;
    CurDependency:=CurDependency.NextRequiresDependency;
  end;
  Result:=mrOk;
end;

procedure TPkgManager.OnOpenPackageForCurrentSrcEditFile(Sender: TObject);
var
  APackage: TIDEPackage;
begin
  GetPackageOfCurrentSourceEditor(APackage);
  if APackage is TLazPackage then
    DoOpenPackage(TLazPackage(APackage),[],false);
end;

procedure TPkgManager.CreateIDEWindow(Sender: TObject; aFormName: string; var
  AForm: TCustomForm; DoDisableAutoSizing: boolean);
var
  APackageName: String;
  NewDependency: TPkgDependency;
  APackage: TLazPackage;
  LoadResult: TLoadPackageResult;
begin
  //debugln(['TPkgManager.CreateIDEWindow ',aFormName]);
  if SysUtils.CompareText(aFormName,NonModalIDEWindowNames[nmiwPkgGraphExplorer])=0
  then begin
    DoShowPackageGraph(false);
    AForm:=PackageGraphExplorer;
    if DoDisableAutoSizing then
      AForm.DisableAutoSizing;
  end else if SysUtils.CompareText(PackageEditorWindowPrefix,
    copy(aFormName,1,length(PackageEditorWindowPrefix)))=0
  then begin
    APackageName:=copy(aFormName,length(PackageEditorWindowPrefix)+1,length(aFormName));
    if (APackageName='') or not IsValidUnitName(APackageName) then exit;
    NewDependency:=TPkgDependency.Create;
    try
      NewDependency.PackageName:=APackageName;
      LoadResult:=PackageGraph.OpenDependency(NewDependency,false);
      if LoadResult<>lprSuccess then exit;
    finally
      NewDependency.Free;
    end;
    APackage:=PackageGraph.FindPackageWithName(APackageName,nil);
    if APackage=nil then exit;
    AForm:=PackageEditors.OpenEditor(APackage);
  end;
end;

procedure TPkgManager.MainIDEitmPkgAddCurFileToPkgClick(Sender: TObject);
begin
  DoAddActiveUnitToAPackage;
end;

procedure TPkgManager.MainIDEitmPkgNewComponentClick(Sender: TObject);
begin
  DoNewPackageComponent;
end;

procedure TPkgManager.MainIDEitmPkgOpenPackageOfCurUnitClicked(Sender: TObject);
var
  ActiveSourceEditor: TSourceEditorInterface;
  ActiveUnitInfo: TUnitInfo;
  PkgFile: TPkgFile;
begin
  MainIDE.GetCurrentUnitInfo(ActiveSourceEditor,ActiveUnitInfo);
  if ActiveSourceEditor=nil then exit;
  PkgFile:=PackageGraph.FindFileInAllPackages(ActiveUnitInfo.Filename,true,
                                            not ActiveUnitInfo.IsPartOfProject);
  if PkgFile=nil then
    IDEMessageDialog(lisProjAddPackageNotFound,
        lisPkgThisFileIsNotInAnyLoadedPackage, mtInformation, [mbCancel])
  else
    DoOpenPackageFile(PkgFile.LazPackage.Filename,[pofAddToRecent],false);
end;

procedure TPkgManager.OnAfterWritePackage(Sender: TObject; Restore: boolean);
var
  APackage: TLazPackage absolute Sender;
begin
  //debugln(['TPkgManager.OnAfterWritePackage ',DbgSName(APackage),' Restore=',Restore]);
  if Restore then
    APackage.RestoreOptions;
end;

procedure TPkgManager.OnBeforeReadPackage(Sender: TObject);
var
  APackage: TLazPackage absolute Sender;
begin
  //debugln(['TPkgManager.OnBeforeReadPackage ',DbgSName(APackage)]);
  APackage.BackupOptions;
end;

function TPkgManager.OnPackageEditorCompilePackage(Sender: TObject;
  APackage: TLazPackage; CompileClean, CompileRequired: boolean): TModalResult;
var
  Flags: TPkgCompileFlags;
begin
  Flags:=[];
  if CompileClean then Include(Flags,pcfCleanCompile);
  if CompileRequired then Include(Flags,pcfCompileDependenciesClean);
  //debugln('TPkgManager.OnPackageEditorCompilePackage OS=',Globals.TargetOS);
  Result:=DoCompilePackage(APackage,Flags,false);
end;

function TPkgManager.OnPackageEditorCreateMakefile(Sender: TObject;
  APackage: TLazPackage): TModalResult;
begin
  Result:=DoCreatePackageMakefile(APackage,false);
end;

function TPkgManager.OnPackageEditorCreateFile(Sender: TObject;
  Params: TAddToPkgResult): TModalResult;
var
  LE: String;
  UsesLine: String;
  NewSource: String;
  UnitDirectives: String;
  IconLRSFilename: String;
  BinFileStream: TFileStreamUTF8;
  BinMemStream: TMemoryStream;
  BinExt: String;
  ResType: String;
  ResName: String;
  ResMemStream: TMemoryStream;
  CodeBuf: TCodeBuffer;
begin
  Result:=mrCancel;

  // create icon resource
  IconLRSFilename:='';
  if Params.IconFile<>'' then begin
    IconLRSFilename:=ChangeFileExt(Params.UnitFilename,'')+'_icon.lrs';
    CodeBuf:=CodeToolBoss.CreateFile(IconLRSFilename);
    if CodeBuf=nil then begin
      debugln(['TPkgManager.OnPackageEditorCreateFile file create failed: ',IconLRSFilename]);
      exit;
    end;
    try
      BinFileStream:=TFileStreamUTF8.Create(Params.IconFile,fmOpenRead);
      try
        BinMemStream:=TMemoryStream.Create;
        ResMemStream:=TMemoryStream.Create;
        try
          BinMemStream.CopyFrom(BinFileStream,BinFileStream.Size);
          BinMemStream.Position:=0;
          BinExt:=uppercase(ExtractFileExt(Params.IconFile));
          ResType:=copy(BinExt,2,length(BinExt)-1);
          ResName:=ExtractFileNameOnly(Params.NewClassName);
          BinaryToLazarusResourceCode(BinMemStream,ResMemStream,ResName,ResType);
          ResMemStream.Position:=0;
          CodeBuf.LoadFromStream(ResMemStream);
          Result:=SaveCodeBuffer(CodeBuf);
          if Result<>mrOk then exit;
        finally
          BinMemStream.Free;
          ResMemStream.Free;
        end;
      finally
        BinFileStream.Free;
      end;
    except
      on E: Exception do begin
        MessageDlg(lisCCOErrorCaption,
          Format(lisErrorLoadingFile2, [Params.IconFile, #13, E.Message]), mtError, [
            mbCancel], 0);
      end;
    end;
  end;

  // create sourcecode
  LE:=LineEnding;
  if PackageGraph.FindDependencyRecursively(Params.Pkg.FirstRequiredDependency,
    'LCL')<>nil
  then
    UsesLine:='Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs'
  else
    UsesLine:='Classes, SysUtils';
  if (System.Pos(Params.UsedUnitname,UsesLine)<1)
  and (Params.UsedUnitname<>'') then
    UsesLine:=UsesLine+', '+Params.UsedUnitname;
  UnitDirectives:='{$mode objfpc}{$H+}';
  if Params.Pkg<>nil then
    UnitDirectives:=TFileDescPascalUnit.CompilerOptionsToUnitDirectives(
                                                    Params.Pkg.CompilerOptions);

  NewSource:=
     'unit '+Params.Unit_Name+';'+LE
    +LE
    +UnitDirectives+LE
    +LE
    +'interface'+LE
    +LE
    +'uses'+LE
    +'  '+UsesLine+';'+LE
    +LE
    +'type'+LE
    +'  '+Params.NewClassName+' = class('+Params.AncestorType+')'+LE
    +'  private'+LE
    +'    { Private declarations }'+LE
    +'  protected'+LE
    +'    { Protected declarations }'+LE
    +'  public'+LE
    +'    { Public declarations }'+LE
    +'  published'+LE
    +'    { Published declarations }'+LE
    +'  end;'+LE
    +LE
    +'procedure Register;'+LE
    +LE
    +'implementation'+LE
    +LE
    +'procedure Register;'+LE
    +'begin'+LE;
  if IconLRSFilename<>'' then
    NewSource:=NewSource
      +'  {$I '+ExtractFileName(IconLRSFilename)+'}'+LE;
  NewSource:=NewSource
    +'  RegisterComponents('''+Params.PageName+''',['+Params.NewClassName+']);'+LE
    +'end;'+LE
    +LE
    +'end.'+LE;

  FileDescriptorUnit.Owner:=Params.Pkg;
  try
    Result:=MainIDE.DoNewEditorFile(FileDescriptorUnit,
       Params.UnitFilename,NewSource,
       [nfOpenInEditor,nfIsNotPartOfProject,nfSave,nfAddToRecent]);
  finally
    FileDescriptorUnit.Owner:=nil;
  end;
end;

function TPkgManager.OnPackageEditorDeleteAmbiguousFiles(Sender: TObject;
  APackage: TLazPackage; const Filename: string): TModalResult;
begin
  Result:=BuildBoss.DeleteAmbiguousFiles(Filename);
end;

function TPkgManager.OnPackageEditorAddToProject(Sender: TObject;
  APackage: TLazPackage; OnlyTestIfPossible: boolean): TModalResult;
begin
  Result:=AddProjectDependency(Project1,APackage,OnlyTestIfPossible);
end;

function TPkgManager.OnPackageEditorInstallPackage(Sender: TObject;
  APackage: TLazPackage): TModalResult;
begin
  Result:=DoInstallPackage(APackage);
end;

function TPkgManager.OnPackageEditorPublishPackage(Sender: TObject;
  APackage: TLazPackage): TModalResult;
begin
  Result:=DoPublishPackage(APackage,[],true);
end;

function TPkgManager.OnPackageEditorRevertPackage(Sender: TObject;
  APackage: TLazPackage): TModalResult;
begin
  if APackage.AutoCreated or (not FilenameIsAbsolute(APackage.Filename))
  or (not FileExistsUTF8(APackage.Filename)) then
    exit(mrCancel);
  Result:=DoOpenPackageFile(APackage.Filename,[pofRevert],false);
end;

function TPkgManager.OnPackageEditorUninstallPackage(Sender: TObject;
  APackage: TLazPackage): TModalResult;
begin
  Result:=DoUninstallPackage(APackage,[],false);
end;

function TPkgManager.OnPackageEditorOpenPkgFile(Sender: TObject;
  PkgFile: TPkgFile): TModalResult;
begin
  Result:=DoOpenPkgFile(PkgFile);
end;

procedure TPkgManager.OnPackageEditorFreeEditor(APackage: TLazPackage);
begin
  APackage.Editor:=nil;
  PackageGraph.ClosePackage(APackage);
end;

procedure TPkgManager.OnPackageEditorGetUnitRegisterInfo(Sender: TObject;
  const AFilename: string; var TheUnitName: string; var HasRegisterProc: boolean
  );
begin
  DoGetUnitRegisterInfo(AFilename,TheUnitName,HasRegisterProc,true);
end;

function TPkgManager.OnPackageEditorOpenPackage(Sender: TObject;
  APackage: TLazPackage): TModalResult;
begin
  Result:=DoOpenPackage(APackage,[],false);
end;

function TPkgManager.OnPackageEditorSavePackage(Sender: TObject;
  APackage: TLazPackage; SaveAs: boolean): TModalResult;
begin
  if SaveAs then
    Result:=DoSavePackage(APackage,[psfSaveAs])
  else
    Result:=DoSavePackage(APackage,[]);
end;

function TPkgManager.OnPackageEditorViewPkgSource(Sender: TObject;
  APackage: TLazPackage): TModalResult;
begin
  Result:=DoOpenPackageSource(APackage);
end;

procedure TPkgManager.PackageGraphBeginUpdate(Sender: TObject);
begin
  if PackageGraphExplorer<>nil then PackageGraphExplorer.BeginUpdate;
end;

procedure TPkgManager.PackageGraphChangePackageName(APackage: TLazPackage;
  const OldName: string);
begin
  if PackageGraphExplorer<>nil then
    PackageGraphExplorer.UpdatePackageName(APackage,OldName);
end;

procedure TPkgManager.PackageGraphDeletePackage(APackage: TLazPackage);
begin
  if APackage.Editor<>nil then begin
    APackage.Editor.Hide;
    APackage.Editor.Free;
  end;
end;

procedure TPkgManager.PackageGraphDependencyModified(ADependency: TPkgDependency);
var
  DepOwner: TObject;
begin
  DepOwner:=ADependency.Owner;
  if DepOwner is TLazPackage then
    TLazPackage(DepOwner).Modified:=true
  else if DepOwner is TProject then
    TProject(DepOwner).Modified:=true;
end;

function TPkgManager.PackageGraphExplorerOpenPackage(Sender: TObject;
  APackage: TLazPackage): TModalResult;
begin
  Result:=DoOpenPackage(APackage,[pofAddToRecent],false);
end;

function TPkgManager.PackageGraphExplorerOpenProject(Sender: TObject;
  AProject: TProject): TModalResult;
begin
  if AProject<>Project1 then exit(mrCancel);
  MainIDE.DoShowProjectInspector(true);
  Result:=mrOk;
end;

procedure TPkgManager.PackageGraphAddPackage(Pkg: TLazPackage);
begin
  if FileExistsUTF8(Pkg.FileName) then PkgLinks.AddUserLink(Pkg);
  if PackageGraphExplorer<>nil then
    PackageGraphExplorer.UpdatePackageAdded(Pkg);
end;

procedure TPkgManager.PackageGraphEndUpdate(Sender: TObject;
  GraphChanged: boolean);
begin
  if GraphChanged then IncreaseCompilerParseStamp;
  if PackageGraphExplorer<>nil then begin
    if GraphChanged then PackageGraphExplorer.UpdateAll;
    PackageGraphExplorer.EndUpdate;
  end;
  if GraphChanged then begin
    if PackageEditors<>nil then
      PackageEditors.UpdateAllEditors(false);
    if ProjInspector<>nil then
      ProjInspector.UpdateItems(false);
    DoCallNotifyHandler(pihtGraphChanged,Self);
  end;
end;

procedure TPkgManager.PackageGraphFindFPCUnit(const AUnitName,
  Directory: string; var Filename: string);
begin
  if (Directory<>'') and not FilenameIsAbsolute(Directory) then
    RaiseGDBException(Directory);
  //DebugLn('TPkgManager.PackageGraphFindFPCUnit "',Directory,'"');
  Filename:=CodeToolBoss.DirectoryCachePool.FindUnitInUnitLinks(Directory,
                                                                AUnitName);
end;

function TPkgManager.PackageGraphExplorerUninstallPackage(Sender: TObject;
  APackage: TLazPackage): TModalResult;
begin
  Result:=DoUninstallPackage(APackage,[],false);
end;

function TPkgManager.PkgLinksDependencyOwnerGetPkgFilename(
  PkgLinks: TPackageLinks; Dependency: TPkgDependency): boolean;
begin
  Result:=false;
  // TODO search in Project/Package history list of dependencies
  
end;

procedure TPkgManager.MainIDEitmConfigCustomCompsClicked(Sender: TObject);
begin
  ShowConfigureCustomComponents;
end;

procedure TPkgManager.MainIDEitmPkgNewPackageClick(Sender: TObject);
begin
  DoNewPackage;
end;

procedure TPkgManager.MainIDEitmPkgOpenPackageClicked(Sender: TObject);
begin
  DoShowOpenInstalledPckDlg;
end;

procedure TPkgManager.MainIDEViewPackageLinksClicked(Sender: TObject);
begin
  ShowPackageLinks;
end;

procedure TPkgManager.MainIDEitmOpenRecentPackageClicked(Sender: TObject);

  procedure UpdateEnvironment;
  begin
    SetRecentPackagesMenu;
    MainIDE.SaveEnvironment;
  end;

var
  AFilename: string;
begin
  AFileName:=ExpandFileNameUTF8((Sender as TIDEMenuItem).Caption);
  if DoOpenPackageFile(AFilename,[pofAddToRecent],false)=mrOk then begin
    UpdateEnvironment;
  end else begin
    // open failed
    if not FileExistsUTF8(AFilename) then begin
      // file does not exist -> delete it from recent file list
      RemoveFromRecentList(AFilename,EnvironmentOptions.RecentPackageFiles,rltFile);
      UpdateEnvironment;
    end;
  end;
end;

procedure TPkgManager.OnApplicationIdle(Sender: TObject; var Done: Boolean);
begin
  if PackageGraph = nil then Exit;
  if MainIDE.ToolStatus<>itNone then exit;
  if (Screen.ActiveCustomForm<>nil)
  and (fsModal in Screen.ActiveCustomForm.FormState) then exit;
  PackageGraph.CloseUnneededPackages;
end;

function TPkgManager.DoShowSavePackageAsDialog(
  APackage: TLazPackage): TModalResult;
var
  OldPkgFilename: String;
  SaveDialog: TSaveDialog;
  NewFileName: String;
  NewPkgName: String;
  ConflictPkg: TLazPackage;
  PkgFile: TPkgFile;
  LowerFilename: String;
  BrokenDependencies: TFPList;
  RenameDependencies: Boolean;
  OldPkgName: String;
  NewMainUnitFileName: String;
  
  procedure RenamePackageInProject;
  var
    AProject: TProject;
    OldUnitName: String;
    NewUnitName: String;
  begin
    AProject:=Project1;
    if (pfMainUnitHasUsesSectionForAllUnits in AProject.Flags)
    and (AProject.MainUnitInfo<>nil) then begin
      OldUnitName:=OldPkgName;
      NewUnitName:=APackage.Name;
      if (OldUnitName<>NewUnitName) then begin
        MainIDEInterface.SaveSourceEditorChangesToCodeCache(nil);
        if CodeToolBoss.RenameUsedUnit(
          AProject.MainUnitInfo.Source,OldUnitName,NewUnitName,'')
        then
          AProject.MainUnitInfo.Modified:=true;
      end;
    end;
  end;
  
begin
  OldPkgFilename:=APackage.Filename;
  OldPkgName:=APackage.Name;

  SaveDialog:=TSaveDialog.Create(nil);
  try
    InputHistories.ApplyFileDialogSettings(SaveDialog);
    SaveDialog.Title:=Format(lisPkgMangSavePackageLpk, [APackage.IDAsString]);
    if APackage.HasDirectory then
      SaveDialog.InitialDir:=APackage.Directory;

    // build a nice package filename suggestion
    NewFileName:=APackage.Name+'.lpk';
    SaveDialog.FileName:=NewFileName;

    repeat
      Result:=mrCancel;

      if not SaveDialog.Execute then begin
        // user cancels
        Result:=mrCancel;
        exit;
      end;
      NewFileName:=CleanAndExpandFilename(SaveDialog.Filename);
      NewPkgName:=ExtractFileNameOnly(NewFilename);
      if APackage.MainUnitHasPkgName then
        NewMainUnitFileName:=ChangeFileExt(NewFileName,'.pas')
      else
        NewMainUnitFileName:='';
      
      // check file extension
      if ExtractFileExt(NewFilename)='' then begin
        // append extension
        NewFileName:=NewFileName+'.lpk';
      end else if ExtractFileExt(NewFilename)<>'.lpk' then begin
        Result:=IDEMessageDialog(lisPkgMangInvalidPackageFileExtension,
          lisPkgMangPackagesMustHaveTheExtensionLpk,
          mtInformation,[mbRetry,mbAbort]);
        if Result=mrAbort then exit;
        continue; // try again
      end;

      // check filename
      if (NewPkgName='') or (not IsValidUnitName(NewPkgName)) then begin
        Result:=IDEMessageDialog(lisPkgMangInvalidPackageName,
          Format(lisPkgMangThePackageNameIsNotAValidPackageNamePleaseChooseAn, [
            '"', NewPkgName, '"', #13]),
          mtInformation,[mbRetry,mbAbort]);
        if Result=mrAbort then exit;
        continue; // try again
      end;

      // apply naming conventions
      
      if lowercase(NewPkgName) <> NewPkgName then
      begin
        LowerFilename:=ExtractFilePath(NewFilename)+lowercase(ExtractFileName(NewFilename));
        if EnvironmentOptions.CharcaseFileAction = ccfaAsk then
        begin
          if IDEMessageDialog(lisPkgMangRenameFileLowercase,
            Format(lisPkgMangShouldTheFileRenamedLowercaseTo, [#13, '"',
              LowerFilename, '"']),
            mtConfirmation,[mbYes,mbNo])=mrYes
          then
            NewFileName:=LowerFilename;
        end
        else
        begin
          if EnvironmentOptions.CharcaseFileAction = ccfaAutoRename then NewFileName:=LowerFilename;
        end;
      end;

      // check unit name conflict
      if NewMainUnitFileName<>'' then
      begin
        PkgFile:=APackage.FindUnit(NewPkgName);
        if PkgFile<>nil then begin
          Result:=IDEMessageDialog(lisNameConflict,
            lisThePackageAlreadyContainsAUnitWithThisName,
            mtWarning,[mbRetry,mbAbort]);
          if Result=mrAbort then exit;
          continue; // try again
        end;
      end;

      // check package name conflict
      ConflictPkg:=PackageGraph.FindPackageWithName(NewPkgName,APackage);
      if ConflictPkg<>nil then begin
        Result:=IDEMessageDialog(lisPkgMangPackageNameAlreadyExists,
          Format(lisPkgMangThereIsAlreadyAnotherPackageWithTheName, ['"',
            NewPkgName, '"', #13, '"', ConflictPkg.IDAsString, '"', #13, '"',
            ConflictPkg.Filename, '"']),
          mtInformation,[mbRetry,mbAbort,mbIgnore]);
        if Result=mrAbort then exit;
        if Result<>mrIgnore then continue; // try again
      end;
      
      // check file name conflict with project
      if (NewMainUnitFileName<>'')
      and (Project1.ProjectUnitWithFilename(NewMainUnitFileName)<>nil) then begin
        Result:=IDEMessageDialog(lisPkgMangFilenameIsUsedByProject,
          Format(lisPkgMangTheFileNameIsPartOfTheCurrentProject, ['"',
            NewFilename, '"', #13]),
          mtInformation,[mbRetry,mbAbort]);
        if Result=mrAbort then exit;
        continue; // try again
      end;
      
      // check file name conflicts with files in other packages
      if (NewMainUnitFileName<>'') then
      begin
        PkgFile:=PackageGraph.FindFileInAllPackages(NewMainUnitFileName,true,false);
        if PkgFile<>nil then begin
          Result:=IDEMessageDialog(lisPkgMangFilenameIsUsedByOtherPackage,
            Format(lisPkgMangTheFileNameIsUsedByThePackageInFile, ['"',
              NewFilename, '"', #13, '"', PkgFile.LazPackage.IDAsString, '"',
              #13, '"', PkgFile.LazPackage.Filename, '"']),
            mtWarning,[mbRetry,mbAbort]);
          if Result=mrAbort then exit;
          continue; // try again
        end;
      end;

      // check for broken dependencies
      BrokenDependencies:=PackageGraph.GetBrokenDependenciesWhenChangingPkgID(
        APackage,NewPkgName,APackage.Version);
      RenameDependencies:=false;
      try
        if BrokenDependencies.Count>0 then begin
          Result:=ShowBrokenDependencies(BrokenDependencies,
                                         DefaultBrokenDepButtons);
          if Result=mrAbort then exit;
          if Result=mrRetry then continue;
          if Result=mrYes then RenameDependencies:=true;
        end;
      finally
        BrokenDependencies.Free;
      end;
      
      // check existing file
      if (CompareFilenames(NewFileName,OldPkgFilename)<>0) then
      begin
        if FileExistsUTF8(NewFileName) then begin
          Result:=IDEMessageDialog(lisPkgMangReplaceFile,
            Format(lisPkgMangReplaceExistingFile, ['"', NewFilename, '"']),
            mtConfirmation,[mbOk,mbCancel]);
          if Result<>mrOk then exit;
        end;
        if FileExistsUTF8(NewMainUnitFileName) then
        begin
          Result:=IDEMessageDialog(lisPkgMangReplaceFile,
            Format(lisPkgMangReplaceExistingFile, ['"', NewFilename, '"']),
            mtConfirmation,[mbOk,mbCancel]);
          if Result<>mrOk then exit;
        end;
      end;

      // check if new file is read/writable
      Result:=CheckCreatingFile(NewFileName,true);
      if Result=mrAbort then exit;

    until Result<>mrRetry;
  finally
    InputHistories.StoreFileDialogSettings(SaveDialog);
    SaveDialog.Free;
  end;
  
  // set filename
  APackage.Filename:=NewFilename;
  
  // rename package
  PackageGraph.ChangePackageID(APackage,NewPkgName,APackage.Version,
                               RenameDependencies,true);
  SaveAutoInstallDependencies;
  RenamePackageInProject;

  // clean up old package file to reduce ambiguousities
  if FileExistsUTF8(OldPkgFilename)
  and (CompareFilenames(OldPkgFilename,NewFilename)<>0) then begin
    if IDEMessageDialog(lisPkgMangDeleteOldPackageFile,
      Format(lisPkgMangDeleteOldPackageFile2, ['"', OldPkgFilename, '"']),
      mtConfirmation,[mbYes,mbNo])=mrYes
    then begin
      if DeleteFileUTF8(OldPkgFilename) then begin
        RemoveFromRecentList(OldPkgFilename,
                             EnvironmentOptions.RecentPackageFiles,rltFile);
      end else begin
        IDEMessageDialog(lisPkgMangDeleteFailed,
          Format(lisPkgMangUnableToDeleteFile, ['"', OldPkgFilename, '"']),
            mtError, [mbOk]);
      end;
    end;
  end;

  // success
  Result:=mrOk;
end;

function TPkgManager.CheckPackageGraphForCompilation(APackage: TLazPackage;
  FirstDependency: TPkgDependency; const Directory: string; ShowAbort: boolean
  ): TModalResult;
var
  PathList: TFPList;
  Dependency: TPkgDependency;
  PkgFile1,PkgFile2: TPkgFile;
  ConflictPkg: TLazPackage;
  s: String;
  Btns: TMsgDlgButtons;
begin
  {$IFDEF VerbosePkgCompile}
  debugln('TPkgManager.CheckPackageGraphForCompilation A');
  {$ENDIF}
  PathList:=nil;
  if ShowAbort
  then Btns := [mbCancel] // will be replaced to Ignore
  else Btns := [mbOK];
  try
    // check for unsaved packages
    PathList:=PackageGraph.FindUnsavedDependencyPath(APackage,FirstDependency);
    if PathList<>nil then begin
      DoShowPackageGraphPathList(PathList);
      Result:=IDEMessageDialogAb(lisPkgMangUnsavedPackage,
        lisPkgMangThereIsAnUnsavedPackageInTheRequiredPackages,
        mtError,[mbCancel],ShowAbort);
      exit;
    end;

    // check for broken dependencies
    PathList:=PackageGraph.FindBrokenDependencyPath(APackage,FirstDependency);
    if PathList<>nil then begin
      if (PathList.Count=1) then begin
        Dependency:=TPkgDependency(PathList[0]);
        if Dependency is TPkgDependency then begin
          // check if project
          if Dependency.Owner is TProject then begin
            MainIDE.DoShowProjectInspector(true);
            Result:=IDEMessageDialogAb(lisPkgMangBrokenDependency,
              Format(lisPkgMangTheProjectRequiresThePackageButItWasNotFound, [
                '"', Dependency.AsString, '"', #13]),
              mtError,Btns,ShowAbort);
            if not ShowAbort then
              Result := mrCancel; // User confirmed error, implicitly cancel the action
            exit;
          end;
        end;
      end;
      DoShowPackageGraphPathList(PathList);
      Result:=IDEMessageDialogAb(lisPkgMangBrokenDependency,
        lisPkgMangRequiredPackagesWereNotFound,
        mtError,Btns,ShowAbort);
      if not ShowAbort then
        Result := mrCancel; // User confirmed error, implicitly cancel the action
      exit;
    end;

    // check for cycle dependencies
    PathList:=PackageGraph.FindCycleDependencyPath(APackage,FirstDependency);
    if PathList<>nil then begin
      DoShowPackageGraphPathList(PathList);
      Result:=IDEMessageDialogAb(lisPkgMangCircularDependencies,
        lisPkgMangThereIsACircularDependency,
        mtError,Btns,ShowAbort);
      if not ShowAbort then
        Result := mrCancel; // User confirmed error, implicitly cancel the action
      exit;
    end;

    // check for a package that compiles to the default FPC search path
    PathList:=PackageGraph.FindPkgOutputInFPCSearchPath(APackage,FirstDependency);
    if PathList<>nil then begin
      ConflictPkg:=TObject(PathList[PathList.Count-1]) as TLazPackage;
      DoShowPackageGraphPathList(PathList);
      Result:=IDEMessageDialogAb(lisPkgMangCircularDependencies,
        Format(lisPkgMangThePackageIsCompiledAutomaticallyAndItsOutputDirec, [
          ConflictPkg.Name, ConflictPkg.GetOutputDirectory, #13#13, #13, #13,
          #13]),
        mtError,Btns,ShowAbort);
      if not ShowAbort then
        Result := mrCancel; // User confirmed error, implicitly cancel the action
      exit;
    end;

    // check for ambiguous units between packages
    if PackageGraph.FindAmbiguousUnits(APackage,FirstDependency,
      PkgFile1,PkgFile2,ConflictPkg)
    then begin
      if (PkgFile1<>nil) and (PkgFile2<>nil) then begin
        s:=Format(lisPkgMangThereAreTwoUnitsWithTheSameName1From2From, [#13,
          #13, '"', PkgFile1.Filename, '"', PkgFile1.LazPackage.IDAsString,
          #13, '"', PkgFile2.Filename, '"', PkgFile2.LazPackage.IDAsString,
          #13, #13]);
      end else if (PkgFile1<>nil) and (ConflictPkg<>nil) then begin
        s:=Format(lisPkgMangThereIsAUnitWithTheSameNameAsAPackage1From2, [#13,
          #13, '"', PkgFile1.Filename, '"', PkgFile1.LazPackage.IDAsString,
          #13, '"', ConflictPkg.IDAsString, #13, #13]);
      end else
        s:='Internal inconsistency FindAmbiguousUnits: '
          +'Please report this bug and how you got here.'#13;
      Result:=IDEMessageDialogAb(lisPkgMangAmbiguousUnitsFound, Format(
        lisPkgMangBothPackagesAreConnectedThisMeansEitherOnePackageU, [s]),
          mtError,Btns,ShowAbort);
      if not ShowAbort then
        Result := mrCancel; // User confirmed error, implicitly cancel the action
      exit;
    end;

    // check for ambiguous units between packages and FPC units
    if PackageGraph.FindFPCConflictUnit(APackage,FirstDependency,Directory,
      @PackageGraphFindFPCUnit,PkgFile1,ConflictPkg)
    then begin
      if (ConflictPkg<>nil) then begin
        s:=Format(lisPkgMangThereIsAFPCUnitWithTheSameNameAsAPackage, [#13,
          #13, '"', ConflictPkg.IDAsString, #13, #13]);
      end else if (PkgFile1<>nil) then begin
        s:=Format(lisPkgMangThereIsAFPCUnitWithTheSameNameFrom, [#13, #13, '"',
          PkgFile1.Filename, '"', PkgFile1.LazPackage.IDAsString, #13, #13]);
      end else
        s:='Internal inconsistency FindFPCConflictUnits: '
          +'Please report this bug and how you got here.'#13;
      Result:=IDEMessageDialogAb(lisPkgMangAmbiguousUnitsFound, s,
          mtError,Btns,ShowAbort);
      if not ShowAbort then
        Result := mrCancel; // User confirmed error, implicitly cancel the action
      exit;
    end;

  finally
    PathList.Free;
  end;
  
  {$IFDEF VerbosePkgCompile}
  debugln('TPkgManager.CheckPackageGraphForCompilation END');
  {$ENDIF}
  Result:=mrOk;
end;

function TPkgManager.DoGetUnitRegisterInfo(const AFilename: string;
  var TheUnitName: string; var HasRegisterProc: boolean; IgnoreErrors: boolean
  ): TModalResult;
  
  function ErrorsHandled: boolean;
  begin
    if (CodeToolBoss.ErrorMessage='') or IgnoreErrors then exit(true);
    MainIDE.DoJumpToCodeToolBossError;
    Result:=false;
  end;
  
var
  ExpFilename: String;
  CodeBuffer: TCodeBuffer;
begin
  Result:=mrCancel;
  ExpFilename:=TrimFilename(AFilename);
  // create default values
  TheUnitName:='';
  HasRegisterProc:=false;
  MainIDE.SaveSourceEditorChangesToCodeCache(nil);
  CodeBuffer:=CodeToolBoss.LoadFile(ExpFilename,true,false);
  if CodeBuffer<>nil then begin
    TheUnitName:=CodeToolBoss.GetSourceName(CodeBuffer,false);
    if not ErrorsHandled then exit;
    CodeToolBoss.HasInterfaceRegisterProc(CodeBuffer,HasRegisterProc);
    if not ErrorsHandled then exit;
  end;
  if TheUnitName='' then
    TheUnitName:=ExtractFileNameOnly(ExpFilename);
  Result:=mrOk;
end;

procedure TPkgManager.SaveAutoInstallDependencies;
var
  Dependency: TPkgDependency;
  sl: TStringList;
begin
  sl:=TStringList.Create;
  Dependency:=PackageGraph.FirstAutoInstallDependency;
  while Dependency<>nil do begin
    if (Dependency.LoadPackageResult=lprSuccess)
    and (not Dependency.RequiredPackage.AutoCreated)
    and (not PackageGraph.IsStaticBasePackage(Dependency.PackageName))
    and (not Dependency.RequiredPackage.Missing)
    and (not (Dependency.RequiredPackage.PackageType in [lptRunTime,lptRunTimeOnly]))
    then begin
      if sl.IndexOf(Dependency.PackageName)<0 then begin
        sl.Add(Dependency.PackageName);
        //DebugLn('TPkgManager.SaveAutoInstallDependencies A ',Dependency.PackageName);
      end;
    end;
    Dependency:=Dependency.NextRequiresDependency;
  end;
  MiscellaneousOptions.BuildLazProfiles.StaticAutoInstallPackages.Assign(sl);
  MiscellaneousOptions.Save;
  sl.Free;
end;

procedure TPkgManager.LoadStaticCustomPackages;
var
  StaticPackages: TFPList;
  StaticPackage: PRegisteredPackage;
  i: Integer;
  APackage: TLazPackage;
  Quiet: Boolean;
begin
  StaticPackages:=LazarusPackageIntf.RegisteredPackages;
  if StaticPackages=nil then exit;
  Quiet:=false;
  for i:=0 to StaticPackages.Count-1 do begin
    StaticPackage:=PRegisteredPackage(StaticPackages[i]);

    // check package name
    if (StaticPackage^.Name='') or (not IsValidUnitName(StaticPackage^.Name))
    then begin
      DebugLn('TPkgManager.LoadStaticCustomPackages Invalid Package Name: "',
        BinaryStrToText(StaticPackage^.Name),'"');
      continue;
    end;
    
    // check register procedure
    if (StaticPackage^.RegisterProc=nil) then begin
      DebugLn('TPkgManager.LoadStaticCustomPackages',
        ' Package "',StaticPackage^.Name,'" has no register procedure.');
      continue;
    end;
    
    // load package
    APackage:=LoadInstalledPackage(StaticPackage^.Name,KeepInstalledPackages,
                                   Quiet);
    
    // register
    PackageGraph.RegisterStaticPackage(APackage,StaticPackage^.RegisterProc);
  end;
  PackageGraph.SortAutoInstallDependencies;
  ClearRegisteredPackages;
end;

function TPkgManager.LoadInstalledPackage(const PackageName: string;
  AddToAutoInstall: boolean; var Quiet: boolean): TLazPackage;
var
  NewDependency: TPkgDependency;
  PackageList: TStringList;
begin
  //DebugLn('TPkgManager.LoadInstalledPackage PackageName="',PackageName,'" Quiet=',Quiet);
  NewDependency:=TPkgDependency.Create;
  NewDependency.Owner:=Self;
  NewDependency.PackageName:=PackageName;
  PackageGraph.OpenInstalledDependency(NewDependency,pitStatic,Quiet);
  Result:=NewDependency.RequiredPackage;
  if AddToAutoInstall and (Result<>nil) then begin
    if FindDependencyByNameInList(
           PackageGraph.FirstAutoInstallDependency,pdlRequires,PackageName)=nil
    then begin
      NewDependency.RequiredPackage.AutoInstall:=pitStatic;
      NewDependency.AddToList(PackageGraph.FirstAutoInstallDependency,pdlRequires)
    end else
      NewDependency.Free;
    PackageList:=MiscellaneousOptions.BuildLazProfiles.StaticAutoInstallPackages;
    if PackageList.IndexOf(PackageName)<0 then
      PackageList.Add(PackageName);
  end else begin
    NewDependency.Free;
  end;
end;

procedure TPkgManager.LoadAutoInstallPackages;
begin
  FLastLazarusSrcDir:=EnvironmentOptions.GetParsedLazarusDirectory;
  PackageGraph.LoadAutoInstallPackages(
    MiscellaneousOptions.BuildLazProfiles.StaticAutoInstallPackages);
end;

procedure TPkgManager.AddUnitToProjectMainUsesSection(AProject: TProject;
  const AnUnitName, AnUnitInFilename: string);
begin
  // add unit to project main source file
  if (pfMainUnitHasUsesSectionForAllUnits in AProject.Flags)
  and (AProject.MainUnitInfo<>nil) then begin
    //debugln('TPkgManager.AddUnitToProjectMainUsesSection B ',AnUnitName);
    if (AnUnitName<>'') then begin
      MainIDEInterface.SaveSourceEditorChangesToCodeCache(nil);
      if CodeToolBoss.AddUnitToMainUsesSectionIfNeeded(
        AProject.MainUnitInfo.Source,AnUnitName,AnUnitInFilename)
      then
        AProject.MainUnitInfo.Modified:=true;
    end;
  end;
end;

constructor TPkgManager.Create(TheOwner: TComponent);
var
  CompPalette: TComponentPalette;
begin
  inherited Create(TheOwner);
  OnGetDependencyOwnerDescription:=@GetDependencyOwnerDescription;
  OnGetDependencyOwnerDirectory:=@GetDependencyOwnerDirectory;
  OnPackageFileLoaded:=@PackageFileLoaded;

  // componentpalette
  IDEComponentPalette:=TComponentPalette.Create;
  CompPalette:=TComponentPalette(IDEComponentPalette);
  if CompPalette=nil then ;
  CompPalette.OnEndUpdate:=@IDEComponentPaletteEndUpdate;
  CompPalette.OnOpenPackage:=@IDEComponentPaletteOpenPackage;
  CompPalette.OnOpenUnit:=@IDEComponentPaletteOpenUnit;

  // package links
  PkgLinks:=TPackageLinks.Create;
  PkgLinks.UpdateAll;
  PkgLinks.DependencyOwnerGetPkgFilename:=@PkgLinksDependencyOwnerGetPkgFilename;

  // package graph
  PackageGraph:=TLazPackageGraph.Create;
  PackageGraph.OnChangePackageName:=@PackageGraphChangePackageName;
  PackageGraph.OnAddPackage:=@PackageGraphAddPackage;
  PackageGraph.OnDeletePackage:=@PackageGraphDeletePackage;
  PackageGraph.OnDependencyModified:=@PackageGraphDependencyModified;
  PackageGraph.OnBeginUpdate:=@PackageGraphBeginUpdate;
  PackageGraph.OnEndUpdate:=@PackageGraphEndUpdate;
  PackageGraph.OnDeleteAmbiguousFiles:=@BuildBoss.DeleteAmbiguousFiles;
  PackageGraph.OnUninstallPackage:=@DoUninstallPackage;
  PackageGraph.OnTranslatePackage:=@DoTranslatePackage;

  // package editors
  PackageEditors:=TPackageEditors.Create;
  PackageEditors.OnOpenFile:=@MainIDE.DoOpenMacroFile;
  PackageEditors.OnOpenPkgFile:=@OnPackageEditorOpenPkgFile;
  PackageEditors.OnOpenPackage:=@OnPackageEditorOpenPackage;
  PackageEditors.OnCreateNewFile:=@OnPackageEditorCreateFile;
  PackageEditors.OnGetIDEFileInfo:=@MainIDE.GetIDEFileState;
  PackageEditors.OnGetUnitRegisterInfo:=@OnPackageEditorGetUnitRegisterInfo;
  PackageEditors.OnFreeEditor:=@OnPackageEditorFreeEditor;
  PackageEditors.OnSavePackage:=@OnPackageEditorSavePackage;
  PackageEditors.OnRevertPackage:=@OnPackageEditorRevertPackage;
  PackageEditors.OnPublishPackage:=@OnPackageEditorPublishPackage;
  PackageEditors.OnCompilePackage:=@OnPackageEditorCompilePackage;
  PackageEditors.OnAddToProject:=@OnPackageEditorAddToProject;
  PackageEditors.OnBeforeReadPackage:=@OnBeforeReadPackage;
  PackageEditors.OnAfterWritePackage:=@OnAfterWritePackage;
  PackageEditors.OnInstallPackage:=@OnPackageEditorInstallPackage;
  PackageEditors.OnUninstallPackage:=@OnPackageEditorUninstallPackage;
  PackageEditors.OnViewPackageSource:=@OnPackageEditorViewPkgSource;
  PackageEditors.OnDeleteAmbiguousFiles:=@OnPackageEditorDeleteAmbiguousFiles;
  PackageEditors.OnCreateMakefile:=@OnPackageEditorCreateMakefile;

  // package macros
  CodeToolBoss.DefineTree.MacroFunctions.AddExtended(
    'PKGDIR',nil,@PackageGraph.MacroFunctionCTPkgDir);
  CodeToolBoss.DefineTree.MacroFunctions.AddExtended(
    'PKGSRCPATH',nil,@PackageGraph.MacroFunctionCTPkgSrcPath);
  CodeToolBoss.DefineTree.MacroFunctions.AddExtended(
    'PKGUNITPATH',nil,@PackageGraph.MacroFunctionCTPkgUnitPath);
  CodeToolBoss.DefineTree.MacroFunctions.AddExtended(
    'PKGINCPATH',nil,@PackageGraph.MacroFunctionCTPkgIncPath);
  CodeToolBoss.DefineTree.MacroFunctions.AddExtended(
    'PKGNAME',nil,@PackageGraph.MacroFunctionCTPkgName);

  LazPackageDescriptors:=TLazPackageDescriptors.Create;
  LazPackageDescriptors.AddDefaultPackageDescriptors;

  // idle handler
  Application.AddOnIdleHandler(@OnApplicationIdle,true);
end;

destructor TPkgManager.Destroy;
begin
  if IDEComponentPalette<>nil then
    TComponentPalette(IDEComponentPalette).PageControl:=nil;
  FreeThenNil(LazPackageDescriptors);
  PackageGraph.FreeAutoInstallDependencies;
  FreeThenNil(PackageGraphExplorer);
  FreeThenNil(PackageEditors);
  FreeThenNil(PackageGraph);
  FreeThenNil(PkgLinks);
  FreeThenNil(IDEComponentPalette);
  FreeThenNil(PackageDependencies);
  inherited Destroy;
end;

procedure TPkgManager.ConnectMainBarEvents;
begin
  with MainIDEBar do begin
    itmPkgNewPackage.OnClick :=@MainIDEitmPkgNewPackageClick;
    itmPkgOpenPackage.OnClick :=@MainIDEitmPkgOpenPackageClicked;
    itmPkgOpenPackageFile.OnClick:=@MainIDEitmPkgOpenPackageFileClick;
    itmPkgOpenPackageOfCurUnit.OnClick :=@MainIDEitmPkgOpenPackageOfCurUnitClicked;
    itmPkgAddCurFileToPkg.OnClick:=@MainIDEitmPkgAddCurFileToPkgClick;
    itmPkgAddNewComponentToPkg.OnClick:=@MainIDEitmPkgNewComponentClick;
    itmPkgPkgGraph.OnClick:=@MainIDEitmPkgPkgGraphClick;
    itmPkgEditInstallPkgs.OnClick:=@MainIDEitmPkgEditInstallPkgsClick;
    {$IFDEF CustomIDEComps}
    itmCompsConfigCustomComps.OnClick :=@MainIDEitmConfigCustomCompsClicked;
    {$ENDIF}
    
    itmViewPackageLinks.OnClick := @MainIDEViewPackageLinksClicked;
  end;
  
  SetRecentPackagesMenu;

  IDEWindowCreators.Add(NonModalIDEWindowNames[nmiwPkgGraphExplorer],
                        nil,@CreateIDEWindow,'250','200','','');
  IDEWindowCreators.Add(PackageEditorWindowPrefix,
                        nil,@CreateIDEWindow,'250','200','','');
  RegisterStandardPackageEditorMenuItems;
end;

procedure TPkgManager.ConnectSourceNotebookEvents;
begin

end;

procedure TPkgManager.SetupMainBarShortCuts;
begin

end;

procedure TPkgManager.SetRecentPackagesMenu;
begin
  MainIDE.SetRecentSubMenu(itmPkgOpenRecent,
     EnvironmentOptions.RecentPackageFiles,@MainIDEitmOpenRecentPackageClicked);
end;

procedure TPkgManager.AddFileToRecentPackages(const Filename: string);
begin
  AddToRecentList(Filename,EnvironmentOptions.RecentPackageFiles,
                  EnvironmentOptions.MaxRecentPackageFiles,rltFile);
  SetRecentPackagesMenu;
  MainIDE.SaveEnvironment;
end;

procedure TPkgManager.SaveSettings;
begin

end;

function TPkgManager.GetDefaultSaveDirectoryForFile(const Filename: string
  ): string;
var
  APackage: TLazPackage;
  PkgFile: TPkgFile;
begin
  Result:='';
  if FilenameIsAbsolute(Filename) then begin
    Result:=ExtractFilePath(Filename);
    exit;
  end;
  PkgFile:=PackageGraph.FindFileInAllPackages(Filename,true,true);
  if PkgFile=nil then exit;
  APackage:=PkgFile.LazPackage;
  if APackage.AutoCreated or (not APackage.HasDirectory) then exit;
  Result:=APackage.Directory;
end;

function TPkgManager.GetPublishPackageDir(APackage: TLazPackage): string;
begin
  Result:=APackage.PublishOptions.DestinationDirectory;
  if IDEMacros.SubstituteMacros(Result) then begin
    if FilenameIsAbsolute(Result) then begin
      Result:=AppendPathDelim(TrimFilename(Result));
    end else begin
      Result:='';
    end;
  end else begin
    Result:='';
  end;
end;

procedure TPkgManager.LoadInstalledPackages;
begin
  IDEComponentPalette.BeginUpdate(true);
  try
    PackageGraph.LoadStaticBasePackages;
    LoadStaticCustomPackages;
    LoadAutoInstallPackages;
  finally
    IDEComponentPalette.EndUpdate;
  end;
end;

procedure TPkgManager.UnloadInstalledPackages;
var
  Dependency: TPkgDependency;
begin
  // unbind and free auto installed packages
  while PackageGraph.FirstAutoInstallDependency<>nil do begin
    Dependency:=PackageGraph.FirstAutoInstallDependency;
    Dependency.RequiredPackage:=nil;
    Dependency.RemoveFromList(PackageGraph.FirstAutoInstallDependency,pdlRequires);
    Dependency.Free;
  end;
end;

procedure TPkgManager.UpdateVisibleComponentPalette;
begin
  TComponentPalette(IDEComponentPalette).PageControl:=MainIDEBar.ComponentPageControl;
  TComponentPalette(IDEComponentPalette).UpdateNoteBookButtons;
end;

procedure TPkgManager.ProcessCommand(Command: word; var Handled: boolean);
begin
  Handled:=true;
  case Command of
  ecOpenPackage: MainIDEitmPkgOpenPackageClicked(Self);
  ecOpenPackageFile: MainIDEitmPkgOpenPackageFileClick(Self);
  ecOpenPackageOfCurUnit: MainIDEitmPkgOpenPackageOfCurUnitClicked(Self);
  ecAddCurFileToPkg: MainIDEitmPkgAddCurFileToPkgClick(Self);
  ecPackageGraph: MainIDEitmPkgPkgGraphClick(Self);
  ecEditInstallPkgs: MainIDEitmPkgEditInstallPkgsClick(Self);
  else
    Handled:=false;
  end;
end;

procedure TPkgManager.OnSourceEditorPopupMenu(
  const AddMenuItemProc: TAddMenuItemProc);
var
  APackage: TIDEPackage;
begin
  GetPackageOfCurrentSourceEditor(APackage);
  if APackage<>nil then
    AddMenuItemProc(Format(lisOpenPackage2, [APackage.Name]), true,
                    @OnOpenPackageForCurrentSrcEditFile);
end;

procedure TPkgManager.TranslateResourceStrings;
var
  PkgList: TFPList;
  i: Integer;
begin
  PkgList:=nil;
  PackageGraph.GetAllRequiredPackages(PackageGraph.FirstAutoInstallDependency,PkgList);
  if PkgList=nil then exit;
  for i:=0 to PkgList.Count-1 do
    if TObject(PkgList[i]) is TLazPackage then
      DoTranslatePackage(TLazPackage(PkgList[i]));
  PkgList.Free;
end;

procedure TPkgManager.DoTranslatePackage(APackage: TLazPackage);
var
  TranslatedUnits: TStringHashList;
  
  function UnitTranslated(const AnUnitName: string): boolean;
  begin
    Result:=(TranslatedUnits<>nil) and (TranslatedUnits.Find(AnUnitName)>=0);
  end;
  
  procedure TranslateUnit(const AFilename, AnUnitName: string);
  begin
    //DebugLn(['TranslateUnit AFilename="',AFilename,'" AnUnitName="',AnUnitName,'"']);
    if TranslatedUnits=nil then
      TranslatedUnits:=TStringHashList.Create(false);
    TranslatedUnits.Add(AnUnitName);
    TranslateUnitResourceStrings(AnUnitName,AFilename);
  end;

  function GetPOFilenameParts(const Filename: string;
    var AUnitName, Language: string): boolean;
  var
    UnitNameEnd: Integer;
    LangEnd: Integer;
  begin
    Result:=false;
    UnitNameEnd:=1;
    while (UnitNameEnd<=length(Filename)) and (Filename[UnitNameEnd]<>'.') do
      inc(UnitNameEnd);
    if (UnitNameEnd=1) then exit;
    LangEnd:=UnitNameEnd+1;
    while (LangEnd<=length(Filename)) and (Filename[LangEnd]<>'.') do
      inc(LangEnd);
    if LangEnd<>length(Filename)-2 then exit;
    AUnitName:=copy(Filename,1,UnitNameEnd-1);
    Language:=copy(Filename,UnitNameEnd+1,LangEnd-UnitNameEnd-1);
    Result:=IsValidUnitName(AUnitName) and (Language<>'');
    //DebugLn(['GetPOFilenameParts AUnitName=',AUnitName,' Language=',Language,' Result=',Result]);
  end;
  
  procedure TranslateWithFileMask(APackage: TLazPackage;
    const Directory, Language: string);
  var
    CurUnitName: string;
    CurLang: string;
    Files: TStrings;
    Filename: String;
  begin
    if Language='' then exit;
    Files:=nil;
    try
      CodeToolBoss.DirectoryCachePool.GetListing(Directory,Files,false);
      for Filename in Files do begin
        if CompareFileExt(Filename,'.po',false)<>0 then continue;
        if GetPOFilenameParts(Filename,CurUnitName,CurLang)
        and (CurLang=Language)
        and (APackage.FindUnit(CurUnitName)<>nil)
        and not UnitTranslated(CurUnitName) then begin
          TranslateUnit(AppendPathDelim(Directory)+Filename,CurUnitName);
        end;
      end;
    finally
      Files.Free;
    end;
  end;

var
  Directory: String;
  Lang: String;
  FallbackLang: String;
  Language: String;
begin
  //DebugLn(['TPkgManager.DoTranslatePackage ',APackage.Name,' from ', APackage.POOutputDirectory]);
  if (APackage.POOutputDirectory='') then exit;
  Directory:=AppendPathDelim(APackage.GetPOOutDirectory);

  Language:=EnvironmentOptions.LanguageID;
  if Language='' then begin
    Lang:=SystemLanguageID1;
    FallbackLang:=SystemLanguageID2;
  end else begin
    Lang:=Language;
    FallbackLang:='';
  end;
  
  //DebugLn(['TPkgManager.DoTranslatePackage ', APackage.Name,' from ', APackage.POOutputDirectory,', Translated=',APackage.Translated,' Lang=',Lang]);
  if APackage.Translated=Lang then exit;
  APackage.Translated:=Lang;
  
  TranslatedUnits:=nil;
  try
    //DebugLn(['TPkgManager.DoTranslatePackage ',APackage.Name,' Directory=',Directory,' Lang=',Lang,' FallbackLang=',FallbackLang]);
    TranslateWithFileMask(APackage,Directory,Lang);
    if FallbackLang<>Lang then
      TranslateWithFileMask(APackage,Directory,FallbackLang);
  finally
    TranslatedUnits.Free;
  end;
end;

function TPkgManager.AddPackageToGraph(APackage: TLazPackage;
  Replace: boolean): TModalResult;
var
  ConflictPkg: TLazPackage;
begin
  // check Package Name
  if (APackage.Name='') or (not IsValidUnitName(APackage.Name)) then begin
    Result:=IDEMessageDialog(lisPkgMangInvalidPackageName2,
      Format(lisPkgMangThePackageNameOfTheFileIsInvalid, ['"', APackage.Name,
        '"', #13, '"', APackage.Filename, '"']),
      mtError,[mbCancel,mbAbort]);
    exit;
  end;

  // check if Package with same name is already loaded
  ConflictPkg:=PackageGraph.FindPackageWithName(APackage.Name,nil);
  if ConflictPkg<>nil then begin
    if not PackageGraph.PackageCanBeReplaced(ConflictPkg,APackage) then begin
      Result:=IDEMessageDialog(lisPkgMangPackageConflicts,
        Format(lisPkgMangThereIsAlreadyAPackageLoadedFromFile, ['"',
          ConflictPkg.IDAsString, '"', #13, '"', ConflictPkg.Filename, '"',
          #13, #13]),
        mtError,[mbCancel,mbAbort]);
      exit;
    end;
    
    if ConflictPkg.Modified and (not ConflictPkg.ReadOnly) then begin
      Result:=IDEMessageDialog(lisPkgMangSavePackage,
        Format(lisPkgMangLoadingPackageWillReplacePackage, [
          APackage.IDAsString, ConflictPkg.IDAsString, #13,
          ConflictPkg.Filename, #13, #13, #13, ConflictPkg.Filename]),
        mtConfirmation,[mbYes,mbNo,mbCancel,mbAbort]);
      if Result=mrNo then Result:=mrOk;
      if Result=mrYes then begin
        Result:=DoSavePackage(ConflictPkg,[]);
      end;
      if Result<>mrOk then exit;
    end;
    
    // replace package
    PackageGraph.ReplacePackage(ConflictPkg,APackage);
  end else begin
    // add to graph
    PackageGraph.AddPackage(APackage);
  end;

  // save package file links
  //DebugLn(['TPkgManager.AddPackageToGraph ',APackage.Name]);
  PkgLinks.SaveUserLinks;

  Result:=mrOk;
end;

function TPkgManager.OpenProjectDependencies(AProject: TProject;
  ReportMissing: boolean): TModalResult;
var
  BrokenDependencies: TFPList;
begin
  PackageGraph.OpenRequiredDependencyList(AProject.FirstRequiredDependency);
  if ReportMissing then begin
    BrokenDependencies:=PackageGraph.FindAllBrokenDependencies(nil,
                                             AProject.FirstRequiredDependency);
    if BrokenDependencies<>nil then begin
      Result:=ShowBrokenDependenciesReport(BrokenDependencies);
      BrokenDependencies.Free;
    end;
  end else
    Result:=mrOk;
  PkgLinks.SaveUserLinks;
end;

function TPkgManager.AddProjectDependency(AProject: TProject;
  APackage: TLazPackage; OnlyTestIfPossible: boolean): TModalResult;
var
  NewDependency: TPkgDependency;
  ProvidingAPackage: TLazPackage;
  ConflictDependency: TPkgDependency;
begin
  Result:=mrCancel;

  // check if there is a dependency, that requires another version
  ConflictDependency:=PackageGraph.FindConflictRecursively(
    AProject.FirstRequiredDependency,APackage);
  if ConflictDependency<>nil then begin
    DebugLn(['TPkgManager.AddProjectDependency ',APackage.IDAsString,' conflicts with ',ConflictDependency.AsString]);
    Result:=mrCancel;
    exit;
  end;

  // check if the dependency is already there
  if FindDependencyByNameInList(AProject.FirstRequiredDependency,pdlRequires,
    APackage.Name)<>nil
  then begin
    // package already there
    Result:=mrOk;
    exit;
  end;

  ProvidingAPackage:=PackageGraph.FindPackageProvidingName(
    AProject.FirstRequiredDependency,APackage.Name);
  if ProvidingAPackage<>nil then
  begin
    // package is already provided by another package
    DebugLn(['TPkgManager.AddProjectDependency ',APackage.Name,' is already provided by ',ProvidingAPackage.IDAsString]);
    Result:=mrOk;
    exit;
  end;

  if OnlyTestIfPossible then
    exit(mrOk);
  // add a dependency for the package to the project
  NewDependency:=APackage.CreateDependencyWithOwner(AProject);
  Result:=AddProjectDependency(AProject,NewDependency);
end;

function TPkgManager.AddProjectDependency(AProject: TProject;
  ADependency: TPkgDependency): TModalResult;
begin
  Result:=mrOk;
  AProject.AddRequiredDependency(ADependency);
  PackageGraph.OpenDependency(ADependency,false);
  if (ADependency.RequiredPackage<>nil)
  and (not ADependency.RequiredPackage.AutoCreated)
  and ADependency.RequiredPackage.AddToProjectUsesSection
  and ((ADependency.RequiredPackage.PackageType<>lptDesignTime)
       or (pfUseDesignTimePackages in AProject.Flags))
  then begin
    AddUnitToProjectMainUsesSection(AProject,
      ExtractFileNameOnly(ADependency.RequiredPackage.GetCompileSourceFilename),'');
  end;
end;

procedure TPkgManager.AddProjectRegCompDependency(AProject: TProject;
  ARegisteredComponent: TRegisteredComponent);
var
  PkgFile: TPkgFile;
  APackage: TLazPackage;
begin
  if not (ARegisteredComponent is TPkgComponent) then exit;
  
  PkgFile:=TPkgComponent(ARegisteredComponent).PkgFile;
  if (PkgFile=nil) then exit;

  APackage:=PkgFile.LazPackage;
  APackage:=TLazPackage(RedirectPackageDependency(APackage));

  AddProjectDependency(AProject,APackage);
end;

procedure TPkgManager.AddProjectLCLDependency(AProject: TProject);
begin
  AddProjectDependency(AProject,PackageGraph.LCLPackage);
end;

function TPkgManager.AddProjectDependencies(AProject: TProject;
  const Packages: string; OnlyTestIfPossible: boolean): TModalResult;
var
  RequiredPackages: TStrings;
  i: Integer;
  PkgName: string;
  APackage: TLazPackage;
begin
  RequiredPackages:=SplitString(Packages,';');
  for i:=0 to RequiredPackages.Count-1 do begin
    PkgName:=Trim(RequiredPackages[i]);
    if (PkgName='') or (not IsValidUnitName(PkgName)) then continue;
    APackage:=PackageGraph.FindPackageWithName(PkgName,nil);
    if APackage=nil then begin
      DebugLn(['TPkgManager.AddProjectDependencies package not found: ',PkgName]);
      continue;
    end;
    AddProjectDependency(AProject,APackage);
  end;
  RequiredPackages.Free;
  Result:=mrOk;
end;

function TPkgManager.CheckProjectHasInstalledPackages(AProject: TProject; 
  Interactive: boolean): TModalResult;
var
  MissingUnits: TFPList;
  i: Integer;
  PkgFile: TPkgFile;
  Msg: String;
  PkgList: TObjectList;
begin
  Result:=mrOk;
  MissingUnits:=PackageGraph.FindNotInstalledRegisterUnits(nil,
                                              AProject.FirstRequiredDependency);
  if MissingUnits<>nil then begin
    if Interactive then begin 
      Msg:=Format(lisProbablyYouNeedToInstallSomePackagesForBeforeConti, [#13,
        #13, #13, #13, #13, #13, #13, #13, #13]);
      PkgList:=TObjectList.Create(false);
      try
        for i:=0 to MissingUnits.Count-1 do begin
          PkgFile:=TPkgFile(MissingUnits[i]);
          if PkgList.IndexOf(PkgFile.LazPackage)<0 then
            PkgList.Add(PkgFile.LazPackage);
          Msg:=Format(lisUnitInPackage, [Msg, PkgFile.Unit_Name,
            PkgFile.LazPackage.IDAsString, #13]);
        end;
        Result:=IDEQuestionDialog(lisPackageNeedsInstallation,
          Msg,mtWarning,[mrIgnore,'Continue without install',mrYes,'Install these packages',mrCancel,'Cancel','IsDefault']);
        if Result=mrIgnore then begin
          // continue
        end else if Result=mrYes then
        begin
          // install
          AProject.AutoOpenDesignerFormsDisabled:=true;
          InstallPackages(PkgList,[piiifRebuildIDE]);
          Result:=mrAbort;
        end else begin
          // do not warn again
          AProject.AutoOpenDesignerFormsDisabled:=true;
        end;
      finally
        PkgList.Free;
      end;
    end else
      Result:=mrCancel;    
    MissingUnits.Free;
  end;
end;

function TPkgManager.ShowConfigureCustomComponents: TModalResult;
begin
  Result:=ShowConfigureCustomComponentDlg(EnvironmentOptions.GetParsedLazarusDirectory);
end;

function TPkgManager.DoNewPackage: TModalResult;
var
  NewPackage: TLazPackage;
  CurEditor: TPackageEditorForm;
begin
  Result:=mrCancel;
  // create a new package with standard dependencies
  NewPackage:=PackageGraph.CreateNewPackage(NameToValidIdentifier(lisPkgMangNewPackage));
  PackageGraph.AddDependencyToPackage(NewPackage,
                PackageGraph.FCLPackage.CreateDependencyWithOwner(NewPackage));
  NewPackage.Modified:=false;

  // open a package editor
  CurEditor:=PackageEditors.OpenEditor(NewPackage);
  IDEWindowCreators.ShowForm(CurEditor,true);

  Result:=DoSavePackage(NewPackage,[psfSaveAs]);
end;

function TPkgManager.DoShowOpenInstalledPckDlg: TModalResult;
var
  APackage: TLazPackage;
begin
  Result:=ShowOpenInstalledPkgDlg(APackage);
  if (Result<>mrOk) then exit;
  Result:=DoOpenPackage(APackage,[],false);
end;

function TPkgManager.DoOpenPackage(APackage: TLazPackage;
  Flags: TPkgOpenFlags; ShowAbort: boolean): TModalResult;
var
  CurEditor: TPackageEditorForm;
  AFilename: String;
begin
  AFilename:=APackage.Filename;
  
  // revert: if possible and wanted
  if (pofRevert in Flags) and (FileExistsCached(AFilename)) then begin
    Result:=DoOpenPackageFile(AFilename,Flags,ShowAbort);
    exit;
  end;

  // open a package editor
  CurEditor:=PackageEditors.OpenEditor(APackage);
  IDEWindowCreators.ShowForm(CurEditor,true);

  // add to recent packages
  if (pofAddToRecent in Flags) then begin
    AFilename:=APackage.Filename;
    if FileExistsCached(AFilename) then begin
      AddToRecentList(AFilename,EnvironmentOptions.RecentPackageFiles,
                      EnvironmentOptions.MaxRecentPackageFiles,rltFile);
      SetRecentPackagesMenu;
    end;
  end;

  Result:=mrOk;
end;

function TPkgManager.DoOpenPackageWithName(const APackageName: string;
  Flags: TPkgOpenFlags; ShowAbort: boolean): TModalResult;
var
  APackage: TLazPackage;
  NewDependency: TPkgDependency;
  LoadResult: TLoadPackageResult;
begin
  Result:=mrCancel;
  if (APackageName='') or not IsValidUnitName(APackageName) then exit;
  NewDependency:=TPkgDependency.Create;
  try
    NewDependency.PackageName:=APackageName;
    LoadResult:=PackageGraph.OpenDependency(NewDependency,ShowAbort);
    if LoadResult<>lprSuccess then exit;
  finally
    NewDependency.Free;
  end;
  APackage:=PackageGraph.FindPackageWithName(APackageName,nil);
  if APackage=nil then exit;
  Result:=DoOpenPackage(APackage,Flags,ShowAbort);
end;

function TPkgManager.DoOpenPackageFile(AFilename: string; Flags: TPkgOpenFlags;
  ShowAbort: boolean): TModalResult;
var
  APackage: TLazPackage;
  XMLConfig: TXMLConfig;
  AlternativePkgName: String;
  Code: TCodeBuffer;
  OpenEditor: Boolean;
  
  procedure DoQuestionDlg(const Caption, Message: string);
  begin
    if pofMultiOpen in Flags then
      Result:=IDEQuestionDialog(Caption, Message,
        mtError, [mrIgnore, lisPkgMangSkipThisPackage, mrAbort])
    else
      Result:=IDEQuestionDialog(Caption, Message,
        mtError,[mrAbort])
  end;
begin
  // replace macros
  if pofConvertMacros in Flags then begin
    if not GlobalMacroList.SubstituteStr(AFilename) then exit(mrCancel);
  end;

  AFilename:=CleanAndExpandFilename(AFilename);

  // check file extension
  if (CompareFileExt(AFilename,'.lpk',false)<>0)
  and (not (pofRevert in Flags)) then begin
    DoQuestionDlg(lisPkgMangInvalidFileExtension,
      Format(lisPkgMangTheFileIsNotALazarusPackage, ['"', AFilename, '"']));
    RemoveFromRecentList(AFilename,EnvironmentOptions.RecentPackageFiles,rltFile);
    SetRecentPackagesMenu;
    exit;
  end;
  
  // check filename
  AlternativePkgName:=ExtractFileNameOnly(AFilename);
  if (not (pofRevert in Flags))
  and ((AlternativePkgName='') or (not IsValidUnitName(AlternativePkgName)))
  then begin
    DoQuestionDlg(lisPkgMangInvalidPackageFilename,
      Format(lisPkgMangThePackageFileNameInIsNotAValidLazarusPackageName, ['"',
        AlternativePkgName, '"', #13, '"', AFilename, '"']));
    RemoveFromRecentList(AFilename,EnvironmentOptions.RecentPackageFiles,rltFile);
    SetRecentPackagesMenu;
    exit;
  end;

  // add to recent packages
  if pofAddToRecent in Flags then begin
    AddToRecentList(AFilename,EnvironmentOptions.RecentPackageFiles,
                    EnvironmentOptions.MaxRecentPackageFiles,rltFile);
    SetRecentPackagesMenu;
  end;

  OpenEditor:=not (pofDoNotOpenEditor in Flags);

  // check if package is already loaded
  APackage:=PackageGraph.FindPackageWithFilename(AFilename);
  if (APackage=nil) or (pofRevert in Flags) then begin
    // package not yet loaded or it should be reloaded
    
    if (pofRevert in Flags)
    and ((APackage=nil) or (APackage.Editor=nil)) then
      OpenEditor:=false;
    
    if not FileExistsUTF8(AFilename) then begin
      IDEMessageDialog(lisFileNotFound,
        Format(lisPkgMangFileNotFound, ['"', AFilename, '"']),
        mtError,[mbCancel]);
      RemoveFromRecentList(AFilename,EnvironmentOptions.RecentPackageFiles,rltFile);
      SetRecentPackagesMenu;
      Result:=mrCancel;
      exit;
    end;

    // create a new package
    Result:=mrCancel;
    APackage:=TLazPackage.Create;
    try

      // load the package file
      try
        XMLConfig:=TCodeBufXMLConfig.Create(nil);
        try
          APackage.Filename:=AFilename;
          Result:=LoadXMLConfigFromCodeBuffer(AFilename,XMLConfig,
                               Code,[lbfUpdateFromDisk,lbfRevert],ShowAbort);
          if Result<>mrOk then exit;
          APackage.LPKSource:=Code;
          APackage.LoadFromXMLConfig(XMLConfig,'Package/');
        finally
          XMLConfig.Free;
        end;
      except
        on E: Exception do begin
          DoQuestionDlg(lisPkgMangErrorReadingPackage,
            Format(lisPkgUnableToReadPackageFileError, ['"', AFilename, '"',
              #13, E.Message]));
          exit;
        end;
      end;

      // newly loaded is not modified
      APackage.Modified:=false;

      // check if package name and file name correspond
      if (SysUtils.CompareText(AlternativePkgName,APackage.Name)<>0) then begin
        Result:=IDEMessageDialog(lisPkgMangFilenameDiffersFromPackagename,
          Format(lisPkgMangTheFilenameDoesNotCorrespondToThePackage, ['"',
            ExtractFileName(AFilename), '"', '"', APackage.Name, '"', #13, '"',
            AlternativePkgName, '"']),
          mtConfirmation,[mbYes,mbCancel,mbAbort]);
        if Result<>mrYes then exit;
        APackage.Name:=AlternativePkgName;
      end;
      
      // integrate it into the graph
      Result:=AddPackageToGraph(APackage,pofRevert in Flags);
    finally
      if Result<>mrOk then APackage.Free;
    end;
  end;

  if OpenEditor then
    Result:=DoOpenPackage(APackage,[],ShowAbort)
  else
    Result:=mrOk;

  PkgLinks.SaveUserLinks;

  // the source editor highlighting depends on the compiler mode
  MainIDEInterface.UpdateHighlighters;
end;

procedure TPkgManager.OpenHiddenModifiedPackages;
var
  i: Integer;
  APackage: TLazPackage;
  Editor: TPackageEditorForm;
begin
  for i:=0 to PackageGraph.Count-1 do begin
    APackage:=PackageGraph.Packages[i];
    if (APackage.Editor=nil) and APackage.Modified
    and (APackage.UserIgnoreChangeStamp<>APackage.ChangeStamp) then begin
      Editor:=PackageEditors.OpenEditor(APackage);
      IDEWindowCreators.ShowForm(Editor,false);
    end;
  end;
end;

function TPkgManager.DoSavePackage(APackage: TLazPackage;
  Flags: TPkgSaveFlags): TModalResult;
var
  XMLConfig: TCodeBufXMLConfig;
  PkgLink: TPackageLink;
  Code: TCodeBuffer;
begin
  // do not save during compilation
  if not (MainIDE.ToolStatus in [itNone,itDebugger]) then begin
    Result:=mrAbort;
    exit;
  end;
  
  if APackage.IsVirtual then Include(Flags,psfSaveAs);

  if not ( (psfSaveAs in Flags) or APackage.ReadOnly or APackage.Modified
          or FileExistsCached(APackage.Filename)
          or (APackage.UserIgnoreChangeStamp<>APackage.ChangeStamp )) then
  begin
    Result:=mrOk;
    exit;
  end;

  // save new or changed files
  Result:=SavePackageFiles(APackage);
  if Result<>mrOk then exit;

  // warn about missing files
  Result:=WarnAboutMissingPackageFiles(APackage);
  if Result<>mrOk then exit;

  // save editor files to codetools
  MainIDE.SaveSourceEditorChangesToCodeCache(nil);

  // save package
  if (psfSaveAs in Flags) then begin
    Result:=DoShowSavePackageAsDialog(APackage);
    if Result<>mrOk then exit;
  end;
  
  // backup old file
  Result:=BuildBoss.BackupFile(APackage.Filename);
  if Result=mrAbort then exit;

  // delete ambiguous files
  Result:=BuildBoss.DeleteAmbiguousFiles(APackage.Filename);
  if Result=mrAbort then exit;

  // save
  try
    XMLConfig:=TCodeBufXMLConfig.Create(nil);
    try
      XMLConfig.Clear;
      XMLConfig.KeepFileAttributes:=true;
      APackage.SaveToXMLConfig(XMLConfig,'Package/');
      Code:=nil;
      Result:=SaveXMLConfigToCodeBuffer(APackage.Filename,XMLConfig,Code,true);
      if Result<>mrOk then exit;
      APackage.LPKSource:=Code;
      PkgLink:=PkgLinks.AddUserLink(APackage);
      if PkgLink<>nil then begin
        PkgLink.FileDate:=FileDateToDateTimeDef(FileAgeUTF8(APackage.Filename));
        PkgLink.FileDateValid:=true;
        PkgLinks.SaveUserLinks;
      end;
    finally
      XMLConfig.Free;
    end;
  except
    on E: Exception do begin
      Result:=IDEMessageDialog(lisPkgMangErrorWritingPackage,
        Format(lisPkgMangUnableToWritePackageToFileError, ['"',
          APackage.IDAsString, '"', #13, '"', APackage.Filename, '"', #13,
          E.Message]),
        mtError,[mbAbort,mbCancel]);
      exit;
    end;
  end;

  // success
  APackage.Modified:=false;
  // add to recent
  if (psfSaveAs in Flags) then begin
    AddFileToRecentPackages(APackage.Filename);
  end;

  if APackage.Editor<>nil then
    APackage.Editor.UpdateAll(true);
  Result:=mrOk;
end;

procedure TPkgManager.DoShowPackageGraph(Show: boolean);
begin
  if PackageGraphExplorer=nil then begin
    PackageGraphExplorer:=TPkgGraphExplorerDlg.Create(Application);
    PackageGraphExplorer.OnOpenPackage:=@PackageGraphExplorerOpenPackage;
    PackageGraphExplorer.OnOpenProject:=@PackageGraphExplorerOpenProject;
    PackageGraphExplorer.OnUninstallPackage:=@PackageGraphExplorerUninstallPackage;
  end;
  if Show then
    IDEWindowCreators.ShowForm(PackageGraphExplorer,true);
end;

function TPkgManager.DoCloseAllPackageEditors: TModalResult;
var
  APackage: TLazPackage;
begin
  while PackageEditors.Count>0 do begin
    APackage:=PackageEditors.Editors[PackageEditors.Count-1].LazPackage;
    Result:=DoClosePackageEditor(APackage);
    if Result<>mrOk then exit;
  end;
  Result:=mrOk;
end;

procedure TPkgManager.DoShowPackageGraphPathList(PathList: TFPList);
begin
  DoShowPackageGraph(true);
  PackageGraphExplorer.ShowPath(PathList);
end;

function TPkgManager.ShowBrokenDependenciesReport(Dependencies: TFPList): TModalResult;
var
  Msg: String;
  i: Integer;
  ADependency: TPkgDependency;
begin
  Result:=mrOk;
  if (Dependencies=nil) or (Dependencies.Count=0) then exit;
  if Dependencies.Count=1 then
    Msg:=lisPkgMangTheFollowingPackageFailedToLoad
  else
    Msg:=lisPkgMangTheFollowingPackagesFailedToLoad;
  Msg:=Msg+#13#13;
  for i:=0 to Dependencies.Count-1 do begin
    ADependency:=TPkgDependency(Dependencies[i]);
    Msg:=Msg+ADependency.AsString+#13;
  end;
  
  // give some hints
  ADependency:=TPkgDependency(Dependencies[0]);
  if (ADependency.Owner is TProject) then begin
    // broken dependency used by project -> show project inspector
    if ADependency.Owner=Project1 then begin
      MainIDE.DoShowProjectInspector(true);
      Msg:=Format(lisSeeProjectProjectInspector, [Msg]);
    end;
  end;
  
  Result:=IDEMessageDialog(lisMissingPackages, Msg, mtError, [mbOk]);
end;

procedure TPkgManager.RebuildDefineTemplates;
begin
  PackageGraph.RebuildDefineTemplates;
end;

procedure TPkgManager.LazarusSrcDirChanged;
const
  LazDirMacro = '$(LazarusDir)';
var
  NewLazarusSrcDir: String;
  OldLazarusSrcDir: String;
  VisitedPkgs: TStringToStringTree;
  ReloadPkgs: TStringList;

  function PkgInOldLazarusDir(APackage: TLazPackage): boolean;
  begin
    Result:=FileIsInPath(APackage.Filename,OldLazarusSrcDir)
      or PackageGraph.IsStaticBasePackage(APackage.Name)
      or (SysUtils.CompareText(copy(APackage.Filename,1,length(LazDirMacro)),LazDirMacro)=0)
  end;

  procedure GatherLazarusSrcPackages(APackage: TLazPackage);
  var
    ADependency: TPkgDependency;
  begin
    if APackage=nil then exit;
    if VisitedPkgs.Contains(APackage.Name) then exit;
    VisitedPkgs[APackage.Name]:='1';
    // search the dependencies first
    ADependency:=APackage.FirstRequiredDependency;
    while ADependency<>nil do begin
      GatherLazarusSrcPackages(ADependency.RequiredPackage);
      ADependency:=ADependency.NextRequiresDependency;
    end;
    if PkgInOldLazarusDir(APackage) then begin
      // this package was from the old lazarus source directory
      ReloadPkgs.Add(APackage.Name);
    end;
  end;

  function ReloadPkg(APackage: TLazPackage): boolean;
  var
    Link: TPackageLink;
    MsgResult: TModalResult;
    Filename: String;
  begin
    Result:=true;
    if APackage=nil then exit;
    if not PkgInOldLazarusDir(APackage) then exit;
    // this package was from the old lazarus source directory
    // check if there is a package in the new version
    Link:=PkgLinks.FindLinkWithPkgName(APackage.Name);
    if Link<>nil then begin
      Filename:=TrimFilename(Link.Filename);
      if not FilenameIsAbsolute(Filename) then
        Filename:=AppendPathDelim(NewLazarusSrcDir)+Filename;
      if FileIsInPath(Filename,NewLazarusSrcDir)
      and FileExistsUTF8(Filename) then
      begin
        DebugLn(['TPkgManager.LazarusSrcDirChanged load: ',Filename]);
        // open package in new lazarus source directory
        MsgResult:=DoOpenPackageFile(Filename,[pofDoNotOpenEditor,pofRevert],true);
        if MsgResult=mrAbort then exit(false);
      end;
    end;
  end;

var
  i: Integer;
begin
  if PackageGraph=nil then exit;
  OldLazarusSrcDir:=FLastLazarusSrcDir;
  NewLazarusSrcDir:=EnvironmentOptions.GetParsedLazarusDirectory;
  FLastLazarusSrcDir:=NewLazarusSrcDir;
  if CompareFilenames(OldLazarusSrcDir,NewLazarusSrcDir)=0 then exit;
  debugln(['TPkgManager.LazarusSrcDirChanged loading new lpl files from ',PkgLinks.GetGlobalLinkDirectory]);
  if PkgLinks.IsUpdating then
    debugln(['TPkgManager.LazarusSrcDirChanged inconsistency: pkglinks are locked']);
  PkgLinks.UpdateGlobalLinks;

  VisitedPkgs:=TStringToStringTree.Create(false);
  ReloadPkgs:=TStringList.Create;
  try
    // collect candidates
    for i:=0 to PackageGraph.Count-1 do
      GatherLazarusSrcPackages(PackageGraph.Packages[i]);
    // reload
    for i:=0 to ReloadPkgs.Count-1 do
      ReloadPkg(PackageGraph.FindPackageWithName(ReloadPkgs[i],nil));
  finally
    ReloadPkgs.Free;
    VisitedPkgs.Free;
  end;
end;

function TPkgManager.GetPackageCount: integer;
begin
  Result:=PackageGraph.Count;
end;

function TPkgManager.GetPackages(Index: integer): TIDEPackage;
begin
  Result:=PackageGraph.Packages[Index];
end;

function TPkgManager.FindPackageWithName(const PkgName: string;
  IgnorePackage: TIDEPackage): TIDEPackage;
var
  Pkg: TLazPackage;
begin
  Pkg:=nil;
  if (IgnorePackage<>nil) then
    Pkg:=IgnorePackage as TLazPackage;
  Result:=PackageGraph.FindPackageWithName(PkgName,Pkg);
end;

function TPkgManager.RedirectPackageDependency(APackage: TIDEPackage): TIDEPackage;
begin
  Result:=APackage;
  if Result=PackageGraph.LCLBasePackage then begin
    // Older Lazarus does not have a LCLBase and a component does not work
    // without an LCLBase implementation, so we have to use LCL instead.
    Result:=PackageGraph.LCLPackage;
  end;
end;

function TPkgManager.DoCompileProjectDependencies(AProject: TProject;
  Flags: TPkgCompileFlags): TModalResult;
var
  CompilePolicy: TPackageUpdatePolicy;
begin
  // check graph for circles and broken dependencies
  if not (pcfDoNotCompileDependencies in Flags) then begin
    Result:=CheckPackageGraphForCompilation(nil,
                                            AProject.FirstRequiredDependency,
                                            AProject.ProjectDirectory,false);
    if Result<>mrOk then exit;
  end;
  
  // save all open files
  if not (pcfDoNotSaveEditorFiles in Flags) then begin
    Result:=MainIDE.DoSaveForBuild(crCompile);
    if Result<>mrOk then exit;
  end;

  PackageGraph.BeginUpdate(false);
  try
    // automatically compile required packages
    if not (pcfDoNotCompileDependencies in Flags) then begin
      CompilePolicy:=pupAsNeeded;
      if pcfCompileDependenciesClean in Flags then
        CompilePolicy:=pupOnRebuildingAll;
      Result:=PackageGraph.CompileRequiredPackages(nil,
                                AProject.FirstRequiredDependency,
                                not (pfUseDesignTimePackages in AProject.Flags),
                                CompilePolicy);
      if Result<>mrOk then exit;
    end;
  finally
    PackageGraph.EndUpdate;
  end;
  
  Result:=mrOk;
end;

function TPkgManager.DoCompilePackage(APackage: TLazPackage;
  Flags: TPkgCompileFlags; ShowAbort: boolean): TModalResult;
begin
  Result:=mrCancel;
  
  DebugLn('TPkgManager.DoCompilePackage A ',APackage.IDAsString,' Flags=',PkgCompileFlagsToString(Flags));
  
  if APackage.AutoCreated then exit;

  Result:=MainIDE.PrepareForCompile;
  if Result<>mrOk then exit;
  
  // check graph for circles and broken dependencies
  if not (pcfDoNotCompileDependencies in Flags) then begin
    Result:=CheckPackageGraphForCompilation(APackage,nil,APackage.Directory,ShowAbort);
    if Result<>mrOk then exit;
  end;
  
  // save all open files
  {$IFDEF VerboseSaveForBuild}
  DebugLn('TPkgManager.DoCompilePackage  ',APackage.IDAsString,' Flags=',PkgCompileFlagsToString(Flags));
  {$ENDIF}
  if not (pcfDoNotSaveEditorFiles in Flags) then begin
    Result:=MainIDE.DoSaveForBuild(crCompile);
    if Result<>mrOk then exit;
  end;
  
  Result:=PackageGraph.CompilePackage(APackage,Flags,false);
end;

function TPkgManager.DoCreatePackageMakefile(APackage: TLazPackage;
  ShowAbort: boolean): TModalResult;
begin
  Result:=DoCompilePackage(APackage,[pcfDoNotCompileDependencies,
                       pcfDoNotCompilePackage,pcfCreateMakefile],ShowAbort);
end;

function TPkgManager.OnRenameFile(const OldFilename, NewFilename: string;
  IsPartOfProject: boolean): TModalResult;
var
  OldPackage: TLazPackage;
  OldPkgFile: TPkgFile;
  NewPkgFile: TPkgFile;
begin
  Result:=mrOk;
  if (OldFilename=NewFilename) then
    exit;
  //debugln('TPkgManager.OnRenameFile A OldFilename="',OldFilename,'" New="',NewFilename,'"');
  OldPkgFile:=PackageGraph.FindFileInAllPackages(OldFilename,true,not IsPartOfProject);
  if (OldPkgFile=nil) or (OldPkgFile.LazPackage.ReadOnly) then
    exit;
  OldPackage:=OldPkgFile.LazPackage;
  debugln('TPkgManager.OnRenameFile A OldPackage="',OldPackage.Name);
  NewPkgFile:=PackageGraph.FindFileInAllPackages(NewFilename,true,false);
  if (NewPkgFile<>nil) and (OldPackage<>NewPkgFile.LazPackage) then exit;

  OldPkgFile.Filename:=NewFilename;
  if OldPackage.Editor<>nil then
    OldPackage.Editor.UpdateAll(true);
  OldPackage.Modified:=true;
end;

{------------------------------------------------------------------------------
  function TPkgManager.FindIncludeFileInProjectDependencies(Project1: TProject;
    const Filename: string): string;
    
  Search filename in the include paths of all required packages
------------------------------------------------------------------------------}
function TPkgManager.FindIncludeFileInProjectDependencies(Project1: TProject;
  const Filename: string): string;
var
  APackage: TLazPackage;
  IncPath: String;
  PkgList: TFPList;
  i: Integer;
begin
  Result:='';
  if FilenameIsAbsolute(Filename) then begin
    Result:=Filename;
    exit;
  end;
  PkgList:=nil;
  PackageGraph.GetAllRequiredPackages(Project1.FirstRequiredDependency,PkgList);
  if PkgList=nil then exit;
  try
    for i:=0 to PkgList.Count-1 do begin
      APackage:=TLazPackage(PkgList[i]);
      IncPath:=APackage.CompilerOptions.GetIncludePath(false);
      Result:=SearchFileInPath(Filename,APackage.Directory,IncPath,';',
                               ctsfcDefault);
      if Result<>'' then exit;
    end;
  finally
    PkgList.Free;
  end;
end;

function TPkgManager.AddUnitDependenciesForComponentClasses(
  const UnitFilename: string; ComponentClassnames: TStrings;
  Quiet: boolean): TModalResult;
var
  UnitBuf: TCodeBuffer;
  UnitNames: TStringList;
  Packages: TFPList;
  MissingDependencies: TObjectArray;
  
  function LoadAndParseUnitBuf: TModalResult;
  begin
    if not CodeToolBoss.GatherExternalChanges then begin
      Result:=mrCancel;
      MainIDE.DoJumpToCodeToolBossError;
      exit;
    end;
    UnitBuf:=CodeToolBoss.LoadFile(UnitFilename,false,false);
    if UnitBuf=nil then begin
      Result:=IDEMessageDialog(lisErrorLoadingFile,
        Format(lisLoadingFailed, [UnitFilename]),
        mtError,[mbCancel,mbAbort]);
      exit;
    end;
    Result:=mrOk;
  end;

  function CollectNeededUnitnamesAndPackages: TModalResult;
  var
    i: Integer;
    RegComp: TRegisteredComponent;
    NewUnitName: String;
    PkgFile: TPkgFile;
    ClassUnitInfo: TUnitInfo;
    APackage: TLazPackage;
  begin
    for i:=0 to ComponentClassnames.Count-1 do begin
      //DebugLn(['CollectNeededUnitnamesAndPackages ComponentClassnames[i]=',ComponentClassnames[i]]);
      RegComp:=IDEComponentPalette.FindComponent(ComponentClassnames[i]);
      NewUnitName:='';
      if (RegComp<>nil) then begin
        if RegComp.ComponentClass<>nil then
          NewUnitName:=GetClassUnitName(RegComp.ComponentClass);
        //DebugLn(['CollectNeededUnitnamesAndPackages NewUnitName=',NewUnitName]);
        if NewUnitName='' then
          NewUnitName:=RegComp.GetUnitName;
      end else begin
        ClassUnitInfo:=Project1.UnitWithComponentClassName(ComponentClassnames[i]);
        if ClassUnitInfo<>nil then
          NewUnitName:=ClassUnitInfo.Unit_Name;
      end;
      if (NewUnitName<>'') and (UnitNames.IndexOf(NewUnitName)<0) then begin
        // new needed unit
        UnitNames.Add(NewUnitName);
        // find package
        PkgFile:=PackageGraph.FindUnitInAllPackages(NewUnitName,true);
        //DebugLn(['CollectNeededUnitnamesAndPackages PkgFile=',PkgFile<>nil]);
        if (PkgFile=nil) and (RegComp is TPkgComponent) then
          PkgFile:=TPkgComponent(RegComp).PkgFile;
        if (PkgFile<>nil) then begin
          APackage:=PkgFile.LazPackage;
          APackage:=TLazPackage(RedirectPackageDependency(APackage));
          if (APackage<>nil) and (Packages.IndexOf(APackage)<0) then
            Packages.Add(APackage);
        end;
      end;
    end;
    Result:=mrOk;
  end;

  function RemoveExistingUnitnames: TModalResult;
  var
    ImplementationUsesSection: TStrings;
    MainUsesSection: TStrings;
    j: LongInt;
    i: Integer;
  begin
    Result:=LoadAndParseUnitBuf;
    if Result<>mrOk then exit;
    MainUsesSection:=nil;
    ImplementationUsesSection:=nil;
    try
      if not CodeToolBoss.FindUsedUnitNames(UnitBuf,MainUsesSection,
        ImplementationUsesSection)
      then begin
        MainIDE.DoJumpToCodeToolBossError;
        exit;
      end;
      for i:=0 to MainUsesSection.Count-1 do begin
        j:=UnitNames.IndexOf(MainUsesSection[i]);
        if j>=0 then UnitNames.Delete(j);
      end;
    finally
      MainUsesSection.Free;
      ImplementationUsesSection.Free;
    end;
    Result:=mrOk;
  end;
  
  function AskUser: TModalResult;
  var
    UsesAdditions: String;
    UnitOwner: TObject;
    RequiredPackage: TLazPackage;
    i: Integer;
    PackageAdditions: String;
    Msg: String;
  begin
    UsesAdditions:='';
    for i:=0 to UnitNames.Count-1 do begin
      if UsesAdditions<>'' then UsesAdditions:=UsesAdditions+', ';
      UsesAdditions:=UsesAdditions+UnitNames[i];
    end;
    //DebugLn('TPkgManager.AddUnitDependenciesForComponentClasses UsesAdditions=',UsesAdditions);
    PackageAdditions:='';
    if MissingDependencies<>nil then begin
      for i:=0 to MissingDependencies.Count-1 do begin
        UnitOwner:=TObject(MissingDependencies[i]);
        RequiredPackage:=TLazPackage(MissingDependencies.Objects[i]);
        RequiredPackage:=TLazPackage(RedirectPackageDependency(RequiredPackage));
        if UnitOwner is TProject then begin
          PackageAdditions:=Format(
            lisPkgMangAddingNewDependencyForProjectPackage, [PackageAdditions,
            TProject(UnitOwner).GetTitle, RequiredPackage.Name, #13#13]);
        end else if UnitOwner is TLazPackage then begin
          PackageAdditions:=Format(
            lisPkgMangAddingNewDependencyForPackagePackage, [PackageAdditions,
            TLazPackage(UnitOwner).Name, RequiredPackage.Name, #13#13]);
        end;
      end;
    end;
    //DebugLn('TPkgManager.AddUnitDependenciesForComponentClasses PackageAdditions=',PackageAdditions);
    Msg:='';
    if UsesAdditions<>'' then begin
      Msg:=Format(lisPkgMangTheFollowingUnitsWillBeAddedToTheUsesSectionOf, [
        Msg, #13, UnitFilename, #13, UsesAdditions, #13#13]);
    end;
    if PackageAdditions<>'' then begin
      Msg:=Msg+PackageAdditions;
    end;
    if Msg<>'' then begin
      Result:=IDEMessageDialog(lisConfirmChanges,
        Msg,mtConfirmation,[mbOk,mbAbort]);
      exit;
    end;
    Result:=mrOk;
  end;
  
  function AddDependencies: TModalResult;
  var
    i: Integer;
    UnitOwner: TObject;
    RequiredPackage: TLazPackage;
  begin
    if MissingDependencies<>nil then begin
      for i:=0 to MissingDependencies.Count-1 do begin
        UnitOwner:=TObject(MissingDependencies[i]);
        RequiredPackage:=TLazPackage(MissingDependencies.Objects[i]);
        RequiredPackage:=TLazPackage(RedirectPackageDependency(RequiredPackage));
        if UnitOwner is TProject then begin
          DebugLn('TPkgManager.AddUnitDependenciesForComponentClasses Adding Project Dependency ',TProject(UnitOwner).GetTitle,' -> ',RequiredPackage.Name);
          AddProjectDependency(TProject(UnitOwner),RequiredPackage);
        end else if UnitOwner is TLazPackage then begin
          DebugLn('TPkgManager.AddUnitDependenciesForComponentClasses Adding Package Dependency ',TLazPackage(UnitOwner).Name,' -> ',RequiredPackage.Name);
          AddPackageDependency(TLazPackage(UnitOwner),RequiredPackage.Name);
        end;
      end;
    end;
    Result:=mrOk;
  end;

  function AddUsedUnits: TModalResult;
  var
    i: Integer;
  begin
    Result:=LoadAndParseUnitBuf;
    if Result<>mrOk then exit;
    for i:=0 to UnitNames.Count-1 do begin
      DebugLn('TPkgManager.AddUnitDependenciesForComponentClasses Extending Uses ',UnitBuf.Filename,' ',UnitNames[i]);
      if not CodeToolBoss.AddUnitToMainUsesSection(UnitBuf,UnitNames[i],'') then
        MainIDE.DoJumpToCodeToolBossError;
    end;
    Result:=mrOk;
  end;

begin
  Result:=mrCancel;
  UnitNames:=TStringList.Create;
  Packages:=TFPList.Create;
  MissingDependencies:=nil;
  try
  
    Result:=CollectNeededUnitnamesAndPackages;
    if Result<>mrOk then exit;
    
    Result:=RemoveExistingUnitnames;
    if Result<>mrOk then exit;
    
    Result:=GetMissingDependenciesForUnit(UnitFilename,ComponentClassnames,
                                          MissingDependencies);
    if Result<>mrOk then exit;
    if (UnitNames.Count=0)
    and ((MissingDependencies=nil) or (MissingDependencies.Count=0)) then begin
      // no change needed
      Result:=mrOk;
      exit;
    end;

    if not Quiet then begin
      Result:=AskUser;
      if Result<>mrOk then exit;
    end;
    
    Result:=AddDependencies;
    if Result<>mrOk then exit;

    Result:=AddUsedUnits;
    if Result<>mrOk then exit;

    Result:=mrOk;
  finally
    UnitNames.Free;
    Packages.Free;
    MissingDependencies.Free;
  end;
end;

function TPkgManager.GetMissingDependenciesForUnit(
  const UnitFilename: string; ComponentClassnames: TStrings;
  var List: TObjectArray): TModalResult;
// returns a list of packages needed to use the Component in the unit
var
  UnitOwners: TFPList;
  UnitOwner: TObject;
  FirstDependency: TPkgDependency;
  CurClassID: Integer;
  CurOwnerID: Integer;
  CurCompClass: string;
  CurRegisteredComponent: TRegisteredComponent;
  PkgFile: TPkgFile;
  RequiredPackage: TLazPackage;
  CurUnitName: String;
begin
  Result:=mrCancel;
  List:=nil;
  UnitOwners:=GetOwnersOfUnit(UnitFilename);
  if (UnitOwners<>nil) then begin
    for CurOwnerID:=0 to UnitOwners.Count-1 do begin
      UnitOwner:=TObject(UnitOwners[CurOwnerID]);
      if UnitOwner is TProject then
        FirstDependency:=TProject(UnitOwner).FirstRequiredDependency
      else if UnitOwner is TLazPackage then
        FirstDependency:=TLazPackage(UnitOwner).FirstRequiredDependency
      else
        FirstDependency:=nil;
      for CurClassID:=0 to ComponentClassnames.Count-1 do begin
        CurCompClass:=ComponentClassnames[CurClassID];
        CurRegisteredComponent:=IDEComponentPalette.FindComponent(CurCompClass);
        if CurRegisteredComponent is TPkgComponent then begin
          CurUnitName:='';
          if CurRegisteredComponent.ComponentClass<>nil then
            CurUnitName:=GetClassUnitName(CurRegisteredComponent.ComponentClass);
          //DebugLn(['TPkgManager.GetMissingDependenciesForUnit CurUnitName=',CurUnitName]);
          if CurUnitName='' then
            CurUnitName:=CurRegisteredComponent.GetUnitName;
          PkgFile:=PackageGraph.FindUnitInAllPackages(CurUnitName,true);
          //DebugLn(['TPkgManager.GetMissingDependenciesForUnit PkgFile=',PkgFile<>nil]);
          if PkgFile=nil then
            PkgFile:=TPkgComponent(CurRegisteredComponent).PkgFile;
          if PkgFile<>nil then begin
            RequiredPackage:=PkgFile.LazPackage;
            RequiredPackage:=TLazPackage(RedirectPackageDependency(RequiredPackage));
            if (RequiredPackage<>nil)
            and (RequiredPackage<>UnitOwner)
            and (FindCompatibleDependencyInList(FirstDependency,pdlRequires,
              RequiredPackage)=nil)
            and (PackageGraph.FindPackageProvidingName(FirstDependency,
              RequiredPackage.Name)=nil)
            then begin
              if List=nil then List:=TObjectArray.Create;
              List.AddObject(UnitOwner,RequiredPackage);
              //debugln(['TPkgManager.GetMissingDependenciesForUnit A ',UnitOwner.ClassName,' ',RequiredPackage.Name]);
              //if TObject(List[List.Count-1])<>UnitOwner then RaiseException('A');
              //if TObject(List.Objects[List.Count-1])<>RequiredPackage then RaiseException('B');
            end;
          end;
        end;
      end;
    end;
    UnitOwners.Free;
  end else begin
    DebugLn(['TPkgManager.GetMissingDependenciesForUnit WARNING: unit has no owner: ',UnitFilename]);
  end;
  Result:=mrOk;
end;

function TPkgManager.GetOwnersOfUnit(const UnitFilename: string): TFPList;
begin
  Result:=GetPossibleOwnersOfUnit(UnitFilename,[]);
end;

procedure TPkgManager.ExtendOwnerListWithUsedByOwners(OwnerList: TFPList);
// use items (packages and projects) in OwnerList as leaves and create the
// list of all packages and projects using them.
// The result will be the topologically sorted list of projects and packages
// using the projects/packages in OwnerList, beginning with the top levels.
var
  AddedNonPackages: TFPList;

  procedure AddUsedByOwners(ADependenyOwner: TObject);
  var
    LazPackage: TLazPackage;
    Dependency: TPkgDependency;
  begin
    if ADependenyOwner is TProject then begin
      if AddedNonPackages.IndexOf(ADependenyOwner)>=0 then exit;
      AddedNonPackages.Add(ADependenyOwner);
      OwnerList.Add(ADependenyOwner);
    end else if ADependenyOwner is TLazPackage then begin
      LazPackage:=TLazPackage(ADependenyOwner);
      if lpfVisited in LazPackage.Flags then exit;
      LazPackage.Flags:=LazPackage.Flags+[lpfVisited];
      Dependency:=LazPackage.FirstUsedByDependency;
      while Dependency<>nil do begin
        AddUsedByOwners(Dependency.Owner);
        Dependency:=Dependency.NextUsedByDependency;
      end;
      OwnerList.Add(LazPackage);
    end;
  end;
  
var
  i: Integer;
  OldOwnerList: TFPList;
begin
  OldOwnerList:=TFPList.Create;
  for i:=0 to OwnerList.Count-1 do
    OldOwnerList.Add(OwnerList[i]);
  OwnerList.Clear;
  AddedNonPackages:=TFPList.Create;
  PackageGraph.MarkAllPackagesAsNotVisited;
  for i:=0 to OldOwnerList.Count-1 do
    AddUsedByOwners(TObject(OldOwnerList[i]));
  AddedNonPackages.Free;
  OldOwnerList.Free;
end;

function TPkgManager.GetSourceFilesOfOwners(OwnerList: TFPList): TStrings;

  procedure AddFile(TheOwner: TObject; const Filename: string);
  begin
    if Result=nil then Result:=TStringList.Create;
    Result.AddObject(Filename,TheOwner);
  end;

var
  CurOwner: TObject;
  CurPackage: TLazPackage;
  CurPkgFile: TPkgFile;
  CurProject: TProject;
  CurUnit: TUnitInfo;
  i: Integer;
  j: Integer;
begin
  Result:=nil;
  if OwnerList=nil then exit;
  for i:=0 to OwnerList.Count-1 do begin
    CurOwner:=TObject(OwnerList[i]);
    if CurOwner is TLazPackage then begin
      CurPackage:=TLazPackage(CurOwner);
      for j:=0 to CurPackage.FileCount-1 do begin
        CurPkgFile:=CurPackage.Files[j];
        if CurPkgFile.FileType in PkgFileUnitTypes then
          AddFile(CurOwner,CurPkgFile.GetFullFilename);
      end;
    end else if CurOwner is TProject then begin
      CurProject:=TProject(CurOwner);
      CurUnit:=CurProject.FirstPartOfProject;
      while CurUnit<>nil do begin
        if FilenameIsPascalSource(CurUnit.Filename) then
          AddFile(CurOwner,CurUnit.Filename);
        CurUnit:=CurUnit.NextPartOfProject;
      end;
    end;
  end;
end;

function TPkgManager.GetPossibleOwnersOfUnit(const UnitFilename: string;
  Flags: TPkgIntfOwnerSearchFlags): TFPList;
var
  SrcDir: String;// ExtractFilePath(UnitFilename);

  procedure SearchInProject(AProject: TProject);
  var
    BaseDir: String;
    ProjectDirs: String;
    Add: Boolean;
  begin
    if AProject=nil then exit;
    Add:=false;

    // check if in units
    if not (piosfExcludeOwned in Flags) then begin
      //DebugLn(['SearchInProject ',AProject.ProjectInfoFile,' UnitFilename=',UnitFilename]);
      if (CompareFilenames(UnitFilename,AProject.ProjectInfoFile)=0)
      or (AProject.UnitInfoWithFilename(UnitFilename,[pfsfOnlyProjectFiles])<>nil)
      then
        Add:=true;
    end;

    BaseDir:=ExtractFilePath(AProject.ProjectInfoFile);

    // check if in virtual project
    if (not Add)
    and (piosfIncludeSourceDirectories in Flags)
    and (BaseDir='')
    and (ExtractFilePath(UnitFilename)='') then
      Add:=true;

    if (not Add)
    and (piosfIncludeSourceDirectories in Flags)
    and FilenameIsAbsolute(UnitFilename)
    and (BaseDir<>'') then begin
      // search in project source directories
      ProjectDirs:=AProject.LazCompilerOptions.OtherUnitFiles+';.';
      if IDEMacros.CreateAbsoluteSearchPath(ProjectDirs,BaseDir) then begin
        if FindPathInSearchPath(PChar(SrcDir),length(SrcDir),
          PChar(ProjectDirs),length(ProjectDirs))<>nil
        then
          Add:=true;
      end;
    end;

    if Add then
      Result.Add(AProject);
  end;

var
  PkgFile: TPkgFile;
  CurPackage: TLazPackage;
  i: Integer;
begin
  //DebugLn(['TPkgManager.GetPossibleOwnersOfUnit ',UnitFilename]);
  Result:=TFPList.Create;

  SrcDir:=ExtractFilePath(UnitFilename);

  SearchInProject(Project1);
  
  // find all packages owning file
  if piosfIncludeSourceDirectories in Flags then begin
    PackageGraph.FindPossibleOwnersOfUnit(UnitFilename,Result);
  end else if not (piosfExcludeOwned in Flags) then begin
    PkgFile:=PackageGraph.FindFileInAllPackages(UnitFilename,true,true);
    if (PkgFile<>nil) and (PkgFile.LazPackage<>nil) then
      Result.Add(PkgFile.LazPackage);
    //debugln(['TPkgManager.GetPossibleOwnersOfUnit ',UnitFilename,' ',PkgFile<>nil,' ',(PkgFile<>nil) and (PkgFile.LazPackage<>nil),' Result.Count=',Result.Count]);
    // check package source files (they usually do not have a TPkgFile)
    for i:=0 to PackageGraph.Count-1 do begin
      CurPackage:=PackageGraph.Packages[i];
      if ((CompareFilenames(UnitFilename,CurPackage.GetSrcFilename)=0)
          or (CompareFilenames(UnitFilename,CurPackage.Filename)=0))
      and (Result.IndexOf(CurPackage)<0) then
        Result.Add(CurPackage);
    end;
  end;

  // clean up
  if Result.Count=0 then
    FreeThenNil(Result);
end;

function TPkgManager.GetPackageOfCurrentSourceEditor(out APackage: TIDEPackage
  ): TPkgFile;
var
  SrcEdit: TSourceEditor;
begin
  Result:=nil;
  APackage:=nil;
  SrcEdit:=SourceEditorManager.GetActiveSE;
  if SrcEdit=nil then exit;
  Result := TPkgFile(GetPackageOfSourceEditor(APackage, SrcEdit));
end;

function TPkgManager.GetPackageOfSourceEditor(out APackage: TIDEPackage;
  ASrcEdit: TObject): TLazPackageFile;
var
  Filename: String;
  i: Integer;
begin
  Result:=nil;
  APackage:=nil;
  if ASrcEdit=nil then exit;
  Filename:=TSourceEditor(ASrcEdit).FileName;
  Result:=SearchFile(Filename,[],nil);
  if Result<>nil then begin
    APackage:=Result.LazPackage;
    exit;
  end;
  for i:=0 to PackageGraph.Count-1 do begin
    APackage:=PackageGraph[i];
    if CompareFilenames(TLazPackage(APackage).GetSrcFilename,FileName)=0 then
      exit;
  end;
  APackage:=nil;
end;

function TPkgManager.IsOwnerDependingOnPkg(AnOwner: TObject;
  const PkgName: string; out DependencyOwner: TObject): boolean;
var
  FirstDep: TPkgDependency;
  Dep: TPkgDependency;
begin
  Result:=false;
  DependencyOwner:=nil;
  if (AnOwner=nil) or (PkgName='') then exit;
  if AnOwner is TProject then
    FirstDep:=TProject(AnOwner).FirstRequiredDependency
  else if AnOwner is TLazPackage then begin
    if CompareDottedIdentifiers(PChar(TLazPackage(AnOwner).Name),
      PChar(PkgName))=0
    then begin
      DependencyOwner:=AnOwner;
      exit(true);
    end;
    FirstDep:=TLazPackage(AnOwner).FirstRequiredDependency;
  end else
    exit(false);
  if PackageGraph=nil then exit;
  Dep:=PackageGraph.FindDependencyRecursively(FirstDep,PkgName);
  if Dep=nil then exit;
  DependencyOwner:=Dep.Owner;
  Result:=true;
end;

function TPkgManager.AddDependencyToOwners(OwnerList: TFPList;
  APackage: TIDEPackage; OnlyTestIfPossible: boolean): TModalResult;
var
  i: Integer;
  Item: TObject;
  NewDependency: TPkgDependency;
  ADependency: TPkgDependency;
  r: TModalResult;
  Pkg: TLazPackage;
begin
  Pkg:=APackage as TLazPackage;
  if not OnlyTestIfPossible then begin
    Result:=AddDependencyToOwners(OwnerList,APackage,true);
    if Result<>mrOk then exit;
  end;

  Result:=mrCancel;
  for i:=0 to OwnerList.Count-1 do begin
    Item:=TObject(OwnerList[i]);
    if Item=APackage then continue;
    if Item is TProject then begin
      Result:=AddProjectDependency(TProject(Item),Pkg,OnlyTestIfPossible);
      if Result<>mrOk then exit;
    end else if Item is TLazPackage then begin
      NewDependency:=TPkgDependency.Create;
      try
        NewDependency.PackageName:=APackage.Name;
        r:=CheckAddingDependency(TLazPackage(Item),NewDependency,false,false);
        if r=mrCancel then exit;
        if (not OnlyTestIfPossible) and (r<>mrIgnore) then begin
          ADependency:=NewDependency;
          NewDependency:=nil;
          PackageGraph.AddDependencyToPackage(TLazPackage(Item),ADependency);
        end;
      finally
        NewDependency.Free;
      end;
    end;
  end;
  Result:=mrOk;
end;

function TPkgManager.DoOpenPkgFile(PkgFile: TPkgFile): TModalResult;
var
  Filename: String;
begin
  if (PkgFile.FileType=pftVirtualUnit) then begin
    Filename:=FindVirtualUnitSource(PkgFile);
    if Filename<>'' then begin
      Result:=MainIDE.DoOpenEditorFile(Filename,-1,-1,
                                  [ofOnlyIfExists,ofAddToRecent,ofRegularFile]);
      exit;
    end;
  end;
  Result:=MainIDE.DoOpenEditorFile(PkgFile.GetFullFilename,-1,-1,
                                  [ofOnlyIfExists,ofAddToRecent,ofRegularFile]);
end;

function TPkgManager.FindVirtualUnitSource(PkgFile: TPkgFile): string;
begin
  Result:='';
  if (PkgFile.FileType=pftVirtualUnit)
  and (PkgFile.LazPackage<>nil)
  and (not FileExistsUTF8(PkgFile.Filename)) then begin
    Result:=MainIDE.FindSourceFile(PkgFile.GetShortFilename(false),
                                   PkgFile.LazPackage.Directory,[]);
  end;
end;

function TPkgManager.SearchFile(const AFilename: string;
  SearchFlags: TSearchIDEFileFlags; InObject: TObject): TPkgFile;
var
  i: Integer;
  APackage: TLazPackage;
  CurFilename: String;
begin
  if InObject is TLazPackage then begin
    APackage:=TLazPackage(InObject);
    CurFilename:=AFilename;
    APackage.ShortenFilename(CurFilename,true);
    Result:=APackage.SearchShortFilename(CurFilename,SearchFlags);
    if Result<>nil then exit;
  end;
  if not (siffDoNotCheckAllPackages in SearchFlags) then begin
    for i:=0 to PackageGraph.Count-1 do begin
      APackage:=PackageGraph[i];
      CurFilename:=AFilename;
      APackage.ShortenFilename(CurFilename,true);
      Result:=APackage.SearchShortFilename(CurFilename,SearchFlags);
      //debugln(['TPkgManager.SearchFile Pkg=',APackage.Filename,' CurFilename="',CurFilename,'" Resul=',Result<>nil,' HasDirectory=',APackage.HasDirectory,' ExpFile=',APackage.DirectoryExpanded]);
      if Result<>nil then exit;
    end;
  end;
  Result:=nil;
end;

function TPkgManager.SearchUnitInDesigntimePackages(const AnUnitName: string;
  InObject: TObject): TPkgFile;
var
  i: Integer;
  APackage: TLazPackage;
begin
  if InObject is TLazPackage then begin
    APackage:=TLazPackage(InObject);
    Result:=APackage.FindUnit(AnUnitName);
    if Result<>nil then exit;
  end;
  for i:=0 to PackageGraph.Count-1 do begin
    APackage:=PackageGraph[i];
    if APackage.Installed=pitNope then continue;
    Result:=APackage.FindUnit(AnUnitName);
    if Result<>nil then exit;
  end;
  Result:=nil;
end;

function TPkgManager.AddDependencyToUnitOwners(const OwnedFilename,
  RequiredUnitname: string): TModalResult;
var
  OwnersList: TFPList;
  RequiredPkgFile: TPkgFile;
  RequiredPkg: TLazPackage;
begin
  Result:=mrCancel;
  //DebugLn(['TPkgManager.AddDependencyToUnitOwners RequiredUnitname=',RequiredUnitname,' OwnedFilename=',OwnedFilename]);

  // find needed package
  RequiredPkgFile:=SearchUnitInDesigntimePackages(RequiredUnitName,nil);
  if RequiredPkgFile=nil then begin
    DebugLn(['TPkgManager.AddDependencyToUnitOwners unit not in designtime package: ',RequiredUnitName]);
    exit;
  end;
  RequiredPkg:=RequiredPkgFile.LazPackage;

  // find owners of unit (package or project)
  OwnersList:=GetOwnersOfUnit(OwnedFilename);
  try
    if (OwnersList=nil) or (OwnersList.Count=0) then begin
      DebugLn(['TPkgManager.AddDependencyToUnitOwners Owner not found of unit ',OwnedFilename]);
      exit;
    end;
    // add package dependency
    //DebugLn(['TPkgManager.AddDependencyToUnitOwners ',dbgsName(TObject(OwnersList[0])),' ',RequiredPkg.IDAsString]);
    RequiredPkg:=TLazPackage(RedirectPackageDependency(RequiredPkg));
    Result:=AddDependencyToOwners(OwnersList,RequiredPkg,false);
  finally
    OwnersList.Free;
  end;
end;

procedure TPkgManager.GetPackagesChangedOnDisk(var ListOfPackages: TFPList);
begin
  if PackageGraph=nil then exit;
  PackageGraph.GetPackagesChangedOnDisk(ListOfPackages);
end;

function TPkgManager.RevertPackages(APackageList: TFPList): TModalResult;
var
  i: Integer;
  APackage: TLazPackage;
begin
  if APackageList=nil then exit(mrOk);
  for i:=0 to APackageList.Count-1 do begin
    APackage:=TLazPackage(APackageList[i]);
    if FileExistsCached(APackage.Filename) then
      Result:=DoOpenPackageFile(APackage.Filename,[pofRevert],true)
    else
      APackage.LPKSource:=nil;
    if Result=mrAbort then exit;
  end;
  Result:=mrOk;
end;

function TPkgManager.DoAddActiveUnitToAPackage: TModalResult;
var
  ActiveSourceEditor: TSourceEditorInterface;
  ActiveUnitInfo: TUnitInfo;
  PkgFile: TPkgFile;
  Filename: String;
  TheUnitName: String;
  HasRegisterProc: Boolean;
begin
  MainIDE.GetCurrentUnitInfo(ActiveSourceEditor,ActiveUnitInfo);
  if ActiveSourceEditor=nil then exit;

  Filename:=ActiveUnitInfo.Filename;

  // check if filename is absolute
  if ActiveUnitInfo.IsVirtual or (not FileExistsUTF8(Filename)) then begin
    Result:=IDEMessageDialog(lisPkgMangFileNotSaved,
      lisPkgMangPleaseSaveTheFileBeforeAddingItToAPackage, mtWarning,[mbCancel]);
    exit;
  end;
  
  // check if file is part of project
  if ActiveUnitInfo.IsPartOfProject then begin
    Result:=IDEMessageDialog(lisPkgMangFileIsInProject,
      Format(lisPkgMangWarningTheFileBelongsToTheCurrentProject,
             ['"', Filename, '"', #13])
      ,mtWarning,[mbIgnore,mbCancel]);
    if Result<>mrIgnore then exit;
  end;

  // check if file is already in a package
  PkgFile:=PackageGraph.FindFileInAllPackages(Filename,true,true);
  if PkgFile<>nil then begin
    Result:=IDEMessageDialog(lisPkgMangFileIsAlreadyInPackage,
      Format(lisPkgMangTheFileIsAlreadyInThePackage,
             ['"', Filename, '"', #13, PkgFile.LazPackage.IDAsString]),
      mtWarning,[mbIgnore,mbCancel]);
    if Result<>mrIgnore then exit;
  end;
  
  TheUnitName:='';
  HasRegisterProc:=false;
  if FilenameIsPascalUnit(Filename) then begin
    Result:=DoGetUnitRegisterInfo(Filename,TheUnitName,HasRegisterProc,false);
    if Result<>mrOk then begin
      debugln(['TPkgManager.DoAddActiveUnitToAPackage DoGetUnitRegisterInfo failed']);
      exit;
    end;
  end;
  
  Result:=ShowAddFileToAPackageDlg(Filename,TheUnitName,HasRegisterProc,
                                   @MainIDE.GetIDEFileState);
end;

function TPkgManager.DoNewPackageComponent: TModalResult;
var
  APackage: TLazPackage;
  CurEditor: TPackageEditorForm;
  SaveFlags: TPkgSaveFlags;
  Page: TAddToPkgType;
begin
  Result:=ShowNewPkgComponentDialog(APackage);
  if Result<>mrOk then exit;
  SaveFlags:=[];
  if APackage=nil then begin
    // create new package
    // create a new package with standard dependencies
    APackage:=PackageGraph.CreateNewPackage(NameToValidIdentifier(lisPkgMangNewPackage));
    PackageGraph.AddDependencyToPackage(APackage,
                  PackageGraph.IDEIntfPackage.CreateDependencyWithOwner(APackage));
    APackage.Modified:=false;
    Include(SaveFlags,psfSaveAs);
  end;
  // open a package editor
  CurEditor:=PackageEditors.OpenEditor(APackage);
  IDEWindowCreators.ShowForm(CurEditor,true);
  // save
  Result:=DoSavePackage(APackage,SaveFlags);
  if Result<>mrOk then exit;
  // show new component dialog
  Page:=d2ptNewComponent;
  Result:=CurEditor.ShowAddDialog(Page);
end;

function TPkgManager.SavePackageFiles(APackage: TLazPackage): TModalResult;
var
  i: Integer;
  AFile: TPkgFile;
  AFilename: String;
  SaveFlags: TSaveFlags;
  SrcEdit: TSourceEditor;
begin
  Result:=mrOk;
  for i:=0 to APackage.FileCount-1 do begin
    AFile:=APackage.Files[i];
    if AFile.FileType=pftVirtualUnit then continue;
    AFilename:=AFile.Filename;
    if System.Pos('$(',AFilename)>0 then begin
      // filename contains macros -> skip
      //debugln(['TPkgManager.SavePackageFiles macros ',AFilename]);
      continue;
    end;
    // check if open in editor
    SrcEdit:=SourceEditorManager.SourceEditorIntfWithFilename(AFilename);
    if SrcEdit=nil then
    begin
      // not open in source editor => skip
      //debugln(['TPkgManager.SavePackageFiles no src edit ',AFilename]);
      continue;
    end;
    SaveFlags:=[sfCanAbort];
    if not FilenameIsAbsolute(AFilename) then
      SaveFlags:=[sfSaveAs];
    debugln(['TPkgManager.SavePackageFiles saving ',AFilename]);
    Result:=LazarusIDE.DoSaveEditorFile(SrcEdit,SaveFlags);
    if Result=mrIgnore then Result:=mrOk;
    if Result<>mrOk then exit;
  end;
end;

function TPkgManager.WarnAboutMissingPackageFiles(APackage: TLazPackage
  ): TModalResult;
var
  i: Integer;
  AFile: TPkgFile;
  AFilename: String;
begin
  Result:=mrOk;
  for i:=0 to APackage.FileCount-1 do begin
    AFile:=APackage.Files[i];
    if AFile.FileType=pftVirtualUnit then continue;
    AFilename:=AFile.GetFullFilename;
    if System.Pos('$(',AFilename)>0 then begin
      // filename contains macros -> skip
      continue;
    end;
    if FilenameIsAbsolute(AFilename) and FileExistsCached(AFilename) then
      continue;
    Result:=IDEQuestionDialog(lisPkgSysPackageFileNotFound,
      Format(lisPkgMangTheFileOfPackageWasNotFound, [AFilename, APackage.
        IDAsString]),
      mtWarning,[mrIgnore,mrAbort]);
    if Result<>mrAbort then
      Result:=mrOk;
    // one warning is enough
    exit;
  end;
end;

function TPkgManager.AddPackageDependency(APackage: TLazPackage;
  const ReqPackage: string; OnlyTestIfPossible: boolean): TModalResult;
var
  NewDependency: TPkgDependency;
  ADependency: TPkgDependency;
begin
  NewDependency:=TPkgDependency.Create;
  try
    NewDependency.PackageName:=ReqPackage;
    Result:=CheckAddingDependency(APackage,NewDependency,false,false);
    if Result=mrIgnore then exit(mrOk);
    if Result<>mrOk then exit;
    if not OnlyTestIfPossible then begin
      ADependency:=NewDependency;
      NewDependency:=nil;
      PackageGraph.AddDependencyToPackage(APackage,ADependency);
      Result:=mrOk;
    end;
  finally
    NewDependency.Free;
  end;
end;

function TPkgManager.GetPackageOfEditorItem(Sender: TObject): TIDEPackage;
begin
  Result:=nil;
  while (Sender is TMenuItem) and (TMenuItem(Sender).Parent<>nil) do
    Sender:=TMenuItem(Sender).Parent;
  if (Sender is TMenuItem) and (TMenuItem(Sender).Menu<>nil)
  then
    Sender:=TMenuItem(Sender).Menu;
  if (Sender is TComponent) and (TComponent(Sender).Owner is TCustomForm) then
    Sender:=TCustomForm(TComponent(Sender).Owner);
  if Sender is TPackageEditorForm then
    Result:=TPackageEditorForm(Sender).LazPackage;
end;

function TPkgManager.DoInstallPackage(APackage: TLazPackage): TModalResult;
var
  PkgList: TFPList;
  
  function GetPkgListIndex(APackage: TLazPackage): integer;
  begin
    Result:=PkgList.Count-1;
    while (Result>=0) and (TLazPackage(PkgList[Result])<>APackage) do
      dec(Result);
  end;

  function WarnForSuspiciousPackage(APackage: TLazPackage): TModalResult;
  var
    IgnorePath: String;
    UnitPath: String;
  begin
    if APackage.UsageOptions.IncludePath<>'' then
    begin
      IgnorePath:='InstallPkgAddsIncPath/'+APackage.Filename;
      if InputHistories.Ignores.Find(IgnorePath)=nil then
      begin
        Result:=IDEQuestionDialog(lisSuspiciousIncludePath,
          Format(lisThePackageAddsThePathToTheIncludePathOfTheIDEThisI, [
            APackage.IDAsString, dbgstr(APackage.UsageOptions.IncludePath), #13]
            ),
          mtWarning, [mrYes, lisContinue, mrYesToAll, lisContinueAndDoNotAskAgain, mrCancel]);
        case Result of
        mrYes: ;
        mrYesToAll:
          InputHistories.Ignores.Add(IgnorePath,iiidForever);
        else
          exit(mrCancel);
        end;
      end;
    end;
    UnitPath:=Trim(SetDirSeparators(APackage.UsageOptions.UnitPath));
    while (UnitPath<>'') and (UnitPath[1]=';') do
      UnitPath:=copy(UnitPath,2,Length(UnitPath));
    while (UnitPath<>'') and (RightStr(UnitPath,1)=';') do
      UnitPath:=copy(UnitPath,1,Length(UnitPath)-1);
    UnitPath:=ChompPathDelim(TrimFilename(UnitPath));
    if SysUtils.CompareText(UnitPath,'$(PkgOutDir)')<>0 then
    begin
      IgnorePath:='InstallPkgAddsUnitPath/'+APackage.Filename;
      if InputHistories.Ignores.Find(IgnorePath)=nil then
      begin
        Result:=IDEQuestionDialog(lisSuspiciousUnitPath,
          Format(lisThePackageAddsThePathToTheUnitPathOfTheIDEThisIsPr, [
            APackage.IDAsString, dbgstr(APackage.UsageOptions.UnitPath), #13]),
          mtWarning, [mrYes, lisContinue, mrYesToAll, lisContinueAndDoNotAskAgain, mrCancel]);
        case Result of
        mrYes: ;
        mrYesToAll:
          InputHistories.Ignores.Add(IgnorePath,iiidForever);
        else
          exit(mrCancel);
        end;
      end;
    end;
    Result:=mrOk;
  end;
  
var
  Dependency: TPkgDependency;
  i: Integer;
  s: String;
  NeedSaving: Boolean;
  RequiredPackage: TLazPackage;
  BuildIDEFlags: TBuildLazarusFlags;
  Msg: string;
  Btns: TMsgDlgButtons;
  ConflictDep: TPkgDependency;
begin
  if not MainIDE.DoResetToolStatus([rfInteractive]) then exit(mrCancel);

  PackageGraph.BeginUpdate(true);
  PkgList:=nil;
  try
    // check if package is designtime package
    if APackage.PackageType in [lptRunTime,lptRunTimeOnly] then begin
      Btns:=[mbAbort];
      if APackage.PackageType=lptRunTime then
        Include(Btns,mbIgnore);
      Result:=IDEMessageDialog(lisPkgMangPackageIsNoDesigntimePackage,
        Format(lisPkgMangThePackageIsARuntimeOnlyPackageRuntimeOnlyPackages,
               [APackage.IDAsString, #13]),
        mtError,Btns);
      if Result<>mrIgnore then exit;
    end;
    // check if package requires a runtime only package
    ConflictDep:=PackageGraph.FindRuntimePkgOnlyRecursively(
      APackage.FirstRequiredDependency);
    if ConflictDep<>nil then begin
      IDEQuestionDialog(lisNotADesigntimePackage,
        Format(lisThePackageCanNotBeInstalledBecauseItRequiresWhichI, [
          APackage.Name, ConflictDep.AsString]),
        mtError,
        [mrCancel]
        );
      exit;
    end;

    // save package
    if APackage.IsVirtual or APackage.Modified then begin
      Result:=DoSavePackage(APackage,[]);
      if Result<>mrOk then exit;
    end;

    // check consistency
    Result:=CheckPackageGraphForCompilation(APackage,nil,
                            EnvironmentOptions.GetParsedLazarusDirectory,false);
    if Result<>mrOk then exit;
    
    // get all required packages, which will also be auto installed
    APackage.GetAllRequiredPackages(PkgList,false);
    if PkgList=nil then PkgList:=TFPList.Create;
    
    // remove packages already marked for installation
    for i:=PkgList.Count-1 downto 0 do begin
      RequiredPackage:=TLazPackage(PkgList[i]);
      if (RequiredPackage.AutoInstall<>pitNope) then
        PkgList.Delete(i);
    end;

    // now PkgList contains only the required packages that were added to the
    // list of installation packages
    // => show the user the list
    if PkgList.Count>0 then begin
      s:='';
      for i:=0 to PkgList.Count-1 do begin
        RequiredPackage:=TLazPackage(PkgList[i]);
        s:=s+RequiredPackage.IDAsString+#13;
      end;
      if PkgList.Count=0 then
        Msg:=Format(lisPkgMangInstallingThePackageWillAutomaticallyInstallThePac,
                    [APackage.IDAsString])
      else
        Msg:=Format(lisPkgMangInstallingThePackageWillAutomaticallyInstallThePac2,
                    [APackage.IDAsString]);
      Result:=IDEMessageDialog(lisPkgMangAutomaticallyInstalledPackages,
        Msg+#13+s,mtConfirmation,[mbOk,mbCancel]);
      if Result<>mrOk then exit;
    end;

    // warn for packages with suspicious settings
    Result:=WarnForSuspiciousPackage(APackage);
    if Result<>mrOk then exit;
    for i:=0 to PkgList.Count-1 do begin
      RequiredPackage:=TLazPackage(PkgList[i]);
      Result:=WarnForSuspiciousPackage(RequiredPackage);
      if Result<>mrOk then exit;
    end;

    // add packages to auto installed packages
    if GetPkgListIndex(APackage)<0 then
      PkgList.Add(APackage);
    NeedSaving:=false;
    for i:=0 to PkgList.Count-1 do begin
      RequiredPackage:=TLazPackage(PkgList[i]);
      if RequiredPackage.AutoInstall=pitNope then begin
        RequiredPackage.AutoInstall:=pitStatic;
        Dependency:=RequiredPackage.CreateDependencyWithOwner(Self);
        Dependency.AddToList(PackageGraph.FirstAutoInstallDependency,pdlRequires);
        PackageGraph.OpenDependency(Dependency,false);
        NeedSaving:=true;
      end;
    end;
  finally
    PackageGraph.EndUpdate;
    PkgList.Free;
  end;

  if NeedSaving then begin
    PackageGraph.SortAutoInstallDependencies;
    SaveAutoInstallDependencies;
  end;

  // save IDE build configs, so user can build IDE on command line
  BuildIDEFlags:=[blfDontClean,blfOnlyIDE];
  Result:=MainIDE.DoSaveBuildIDEConfigs(BuildIDEFlags);
  if Result<>mrOk then exit;

  // ask user to rebuild Lazarus now
  Result:=IDEMessageDialog(lisPkgMangRebuildLazarus,
    Format(lisPkgMangThePackageWasMarkedForInstallationCurrentlyLazarus,
           ['"', APackage.IDAsString, '"', #13, #13, #13]),
    mtConfirmation,[mbYes,mbNo]);
  if Result<>mrYes then begin
    Result:=mrOk;
    exit;
  end;
  
  // rebuild Lazarus
  Result:=MainIDE.DoBuildLazarus(BuildIDEFlags);
  if Result<>mrOk then exit;

  Result:=mrOk;
end;

function TPkgManager.DoUninstallPackage(APackage: TLazPackage;
  Flags: TPkgUninstallFlags; ShowAbort: boolean): TModalResult;
var
  DependencyPath: TFPList;
  ParentPackage: TLazPackage;
  Dependency: TPkgDependency;
  BuildIDEFlags: TBuildLazarusFlags;
begin
  if (APackage.Installed=pitNope) and (APackage.AutoInstall=pitNope) then exit;
  
  // check if package is required by auto install package
  DependencyPath:=PackageGraph.FindAutoInstallDependencyPath(APackage);
  if DependencyPath<>nil then begin
    DoShowPackageGraphPathList(DependencyPath);
    ParentPackage:=TLazPackage(DependencyPath[0]);
    Result:=IDEMessageDialogAb(lisPkgMangPackageIsRequired,
      Format(lisPkgMangThePackageIsRequiredByWhichIsMarkedForInstallation,
             [APackage.IDAsString, ParentPackage.IDAsString, #13]),
      mtError,[mbCancel],ShowAbort);
    exit;
  end;

  // check if package is a lazarus base package
  if PackageGraph.IsStaticBasePackage(APackage.Name) then begin
    Result:=IDEMessageDialogAb(lisUninstallImpossible,
      Format(lisThePackageCanNotBeUninstalledBecauseItIsNeededByTh,[APackage.Name]),
      mtError,[mbCancel],ShowAbort);
    exit;
  end;

  // confirm uninstall package
  if not (puifDoNotConfirm in Flags) then begin
    Result:=IDEMessageDialogAb(lisPkgMangUninstallPackage,
      Format(lisPkgMangUninstallPackage2, [APackage.IDAsString]),
      mtConfirmation,[mbYes,mbCancel],ShowAbort);
    if Result<>mrYes then exit;
  end;
  
  PackageGraph.BeginUpdate(true);
  try
    // save package
    if APackage.IsVirtual or APackage.Modified then begin
      Result:=DoSavePackage(APackage,[]);
      if Result<>mrOk then exit;
    end;

    // remove package from auto installed packages
    if APackage.AutoInstall<>pitNope then begin
      APackage.AutoInstall:=pitNope;
      Dependency:=FindCompatibleDependencyInList(PackageGraph.FirstAutoInstallDependency,
                                                 pdlRequires,APackage);
      if Dependency<>nil then begin
        Dependency.RemoveFromList(PackageGraph.FirstAutoInstallDependency,pdlRequires);
        Dependency.Free;
        PackageGraph.SortAutoInstallDependencies;
      end;
      SaveAutoInstallDependencies;
    end;

    // save IDE build configs, so user can build IDE on command line
    BuildIDEFlags:=[blfDontClean,blfOnlyIDE];
    Result:=MainIDE.DoSaveBuildIDEConfigs(BuildIDEFlags);
    if Result<>mrOk then exit;

    if not (puifDoNotBuildIDE in Flags) then begin
      // ask user to rebuilt Lazarus now
      Result:=IDEMessageDialog(lisPkgMangRebuildLazarus,
        Format(lisPkgMangThePackageWasMarkedCurrentlyLazarus,
               ['"', APackage.IDAsString, '"', #13, #13, #13]),
        mtConfirmation,[mbYes,mbNo]);
      if Result=mrNo then begin
        Result:=mrOk;
        exit;
      end;

      // rebuild Lazarus
      Result:=MainIDE.DoBuildLazarus(BuildIDEFlags);
      if Result<>mrOk then exit;
    end;
  finally
    PackageGraph.EndUpdate;
  end;
  Result:=mrOk;
end;

function TPkgManager.CheckInstallPackageList(PkgIDList: TObjectList;
  Flags: TPkgInstallInIDEFlags): boolean;
var
  NewFirstAutoInstallDependency: TPkgDependency;

  procedure DeleteDependency(ADependency: TPkgDependency);
  var
    i: Integer;
    PkgID: TLazPackageID;
  begin
    DeleteDependencyInList(ADependency,NewFirstAutoInstallDependency,pdlRequires);
    if piiifRemoveConflicts in Flags then
      for i:=PkgIDList.Count-1 downto 0 do begin
        PkgID:=TLazPackageID(PkgIDList[i]);
        if SysUtils.CompareText(PkgID.Name,ADependency.PackageName)=0 then begin
          PkgIDList.Delete(i);
          PkgID.Free;
        end;
      end;
  end;

var
  PkgList: TFPList;
  i: Integer;
  APackage: TLazPackage;
  ADependency: TPkgDependency;
  NextDependency: TPkgDependency;
  SaveFlags: TPkgSaveFlags;
  ConflictDep: TPkgDependency;
begin
  Result:=false;
  PkgList:=nil;
  try
    // create new auto install dependency PkgIDList
    ListPkgIDToDependencyList(PkgIDList,NewFirstAutoInstallDependency,
                              pdlRequires,Self,true);

    // load all required packages
    if LoadDependencyList(NewFirstAutoInstallDependency,piiifQuiet in Flags)<>mrOk then exit;

    // remove all top level runtime packages from the list
    // Note: it's ok if a designtime package uses a runtime package
    ADependency:=NewFirstAutoInstallDependency;
    while ADependency<>nil do begin
      NextDependency:=ADependency.NextRequiresDependency;
      if (ADependency.RequiredPackage<>nil) then begin
        if (ADependency.RequiredPackage.PackageType in [lptRunTime,lptRunTimeOnly])
        then begin
          // top level dependency on runtime package => delete
          DeleteDependency(ADependency);
        end else begin
          ConflictDep:=PackageGraph.FindRuntimePkgOnlyRecursively(
            ADependency.RequiredPackage.FirstRequiredDependency);
          //debugln(['TPkgManager.CheckInstallPackageList ',ADependency.RequiredPackage.Name,' ',ConflictDep<>nil]);
          if ConflictDep<>nil then begin
            if piiifRemoveConflicts in Flags then begin
              // can remove conflict
              if not (piiifQuiet in Flags)
              and (IDEQuestionDialog(lisNotADesigntimePackage,
                Format(lisThePackageCanNotBeInstalledBecauseItRequiresWhichI, [
                  ADependency.RequiredPackage.Name, ConflictDep.AsString]),
                mtError,
                [mrYes, Format(lisUninstall, [ADependency.RequiredPackage.Name]), mrCancel]
                )<>mrYes)
              then
                exit;
            end else begin
              // can not remove conflict
              if not (piiifQuiet in Flags) then
                IDEQuestionDialog(lisNotADesigntimePackage,
                  Format(lisThePackageCanNotBeInstalledBecauseItRequiresWhichI, [
                    ADependency.RequiredPackage.Name, ConflictDep.AsString]),
                  mtError,[mrCancel]);
              exit;
            end;
            // dependency needs a runtime only package => delete
            DeleteDependency(ADependency);
          end;
        end;
      end;
      ADependency:=NextDependency;
    end;

    PackageGraph.GetAllRequiredPackages(NewFirstAutoInstallDependency,PkgList);

    // try save all modified packages
    for i:=0 to PkgList.Count-1 do begin
      APackage:=TLazPackage(PkgList[i]);
      if (not APackage.AutoCreated)
      and (APackage.IsVirtual or APackage.Modified) then begin
        SaveFlags:=[];
        if DoSavePackage(APackage,SaveFlags)<>mrOk then exit;
      end;
    end;

    Result:=true;
  finally
    FreeDependencyList(NewFirstAutoInstallDependency,pdlRequires);
    PkgList.Free;
  end;
end;

function TPkgManager.InstallPackages(PkgIdList: TObjectList;
  Flags: TPkgInstallInIDEFlags): TModalResult;

  procedure CreateChangeReport(
    OldDependencyList, NewDependencyList: TPkgDependency; Report: TStrings);
  var
    CurDependency: TPkgDependency;
    OldDependency: TPkgDependency;
    NewDependency: TPkgDependency;
    s: String;
  begin
    // list all packages, that will be installed
    CurDependency:=NewDependencyList;
    while CurDependency<>nil do begin
      s:=CurDependency.AsString;
      OldDependency:=FindDependencyByNameInList(OldDependencyList,pdlRequires,
                                                CurDependency.PackageName);
      if OldDependency=nil then begin
        // newly installed
        s:=s+'|'+lisPkgMgrNew;
        Report.Add(s);
      end;
      CurDependency:=CurDependency.NextRequiresDependency;
    end;

    // list all packages, that will be removed
    CurDependency:=OldDependencyList;
    while CurDependency<>nil do begin
      NewDependency:=FindDependencyByNameInList(NewDependencyList,pdlRequires,
                                                CurDependency.PackageName);
      if NewDependency=nil then
        // this package will be removed
        Report.Add('|'+lisPkgMgrRemove+'|'+CurDependency.AsString);
      CurDependency:=CurDependency.NextRequiresDependency;
    end;

    // list all packages, that are kept
    CurDependency:=NewDependencyList;
    while CurDependency<>nil do begin
      s:=CurDependency.AsString;
      OldDependency:=FindDependencyByNameInList(OldDependencyList,pdlRequires,
                                                CurDependency.PackageName);
      if OldDependency<>nil then begin
        // stay installed
        if CurDependency.AsString<>OldDependency.AsString then
          s:=s+'|'+lisPkgMgrKeep+'|'+OldDependency.AsString;
        Report.Add(s);
      end;
      CurDependency:=CurDependency.NextRequiresDependency;
    end;
  end;

var
  NewFirstAutoInstallDependency: TPkgDependency;
  BuildIDEFlags: TBuildLazarusFlags;
  Report: TStringList;
  PkgList: TFPList;
  RequiredPackage: TLazPackage;
  i: Integer;
  CurDependency: TPkgDependency;
  OldID: TLazPackageID;
begin
  Result:=mrCancel;
  NewFirstAutoInstallDependency:=nil;
  PkgList:=nil;
  try
    if not (piiifClear in Flags) then
    begin
      // add existing install packages to list
      NewFirstAutoInstallDependency:=PackageGraph.FirstAutoInstallDependency;
      while NewFirstAutoInstallDependency<>nil do begin
        if NewFirstAutoInstallDependency.RequiredPackage<>nil then begin
          i:=PkgIdList.Count-1;
          while (i>=0)
          and (TLazPackageID(PkgIdList[i]).Compare(NewFirstAutoInstallDependency.RequiredPackage)<>0)
          do dec(i);
          if i<0 then begin
            OldID:=TLazPackageID.Create;
            OldID.AssignID(NewFirstAutoInstallDependency.RequiredPackage);
            PkgIdList.Add(OldID);
          end;
        end;
        NewFirstAutoInstallDependency:=NewFirstAutoInstallDependency.NextRequiresDependency;
      end;
    end;

    if not (piiifSkipChecks in Flags) then
    begin
      if not CheckInstallPackageList(PkgIDList,Flags*[piiifQuiet,piiifRemoveConflicts]) then
        exit(mrCancel);
    end;

    // create new auto install dependency PkgIDList
    ListPkgIDToDependencyList(PkgIDList,NewFirstAutoInstallDependency,
      pdlRequires,Self,true);

    PackageGraph.SortDependencyListTopologically(NewFirstAutoInstallDependency,
                                                 false);

    if not (piiifQuiet in Flags) then
    begin
      // tell the user, which packages will stay, which will be removed and
      // which will be newly installed
      try
        Report:=TStringList.Create;
        CreateChangeReport(
          PackageGraph.FirstAutoInstallDependency,NewFirstAutoInstallDependency,
          Report);
        if not ConfirmPackageList(Report) then exit(mrCancel);
      finally
        Report.Free;
      end;
    end;

    // try to commit changes -> replace install list
    PackageGraph.BeginUpdate(true);
    try
      // get all required packages
      //debugln('TPkgManager.MainIDEitmPkgEditInstallPkgsClick GetAllRequiredPackages for ',DependencyListAsString(NewFirstAutoInstallDependency,pdlRequires));
      if LoadDependencyList(NewFirstAutoInstallDependency,false)<>mrOk then exit(mrCancel);
      PackageGraph.GetAllRequiredPackages(NewFirstAutoInstallDependency,PkgList);

      // mark packages for installation
      //debugln('TPkgManager.MainIDEitmPkgEditInstallPkgsClick mark packages for installation');
      for i:=0 to PkgList.Count-1 do begin
        RequiredPackage:=TLazPackage(PkgList[i]);
        if RequiredPackage.AutoInstall=pitNope then begin
          RequiredPackage.AutoInstall:=pitStatic;
        end;
      end;

      // mark packages for uninstall
      //debugln('TPkgManager.MainIDEitmPkgEditInstallPkgsClick mark packages for uninstall');
      CurDependency:=PackageGraph.FirstAutoInstallDependency;
      while CurDependency<>nil do begin
        if (CurDependency.RequiredPackage<>nil)
        and (not CurDependency.RequiredPackage.AutoCreated)
        and (not PackageGraph.IsStaticBasePackage(CurDependency.PackageName)) then
          CurDependency.RequiredPackage.AutoInstall:=pitNope;
        CurDependency:=CurDependency.NextRequiresDependency;
      end;

      // replace install list
      //debugln('TPkgManager.MainIDEitmPkgEditInstallPkgsClick replace install list');
      FreeDependencyList(PackageGraph.FirstAutoInstallDependency,pdlRequires);
      PackageGraph.FirstAutoInstallDependency:=NewFirstAutoInstallDependency;
      NewFirstAutoInstallDependency:=nil;
    finally
      PackageGraph.EndUpdate;
    end;

    // save package list
    //debugln('TPkgManager.MainIDEitmPkgEditInstallPkgsClick save package list');
    PackageGraph.SortAutoInstallDependencies;
    SaveAutoInstallDependencies;

    // save IDE build configs, so user can build IDE on command line
    BuildIDEFlags:=[blfDontClean,blfOnlyIDE];
    if MainIDE.DoSaveBuildIDEConfigs(BuildIDEFlags)<>mrOk then exit(mrCancel);

    if piiifRebuildIDE in Flags then
    begin
      // rebuild Lazarus
      if MainIDE.DoBuildLazarus(BuildIDEFlags)<>mrOk then exit(mrCancel);
    end;

  finally
    FreeDependencyList(NewFirstAutoInstallDependency,pdlRequires);
    PkgList.Free;
  end;
  Result:=mrOk;
end;

function TPkgManager.DoOpenPackageSource(APackage: TLazPackage): TModalResult;
var
  Filename: String;
begin
  Result:=mrCancel;
  if APackage.IsVirtual then begin
    IDEMessageDialog(lisCCOErrorCaption,
      lisPkgMangThisIsAVirtualPackageItHasNoSourceYetPleaseSaveThe,
      mtError, [mbCancel]);
    exit;
  end;
  Filename:=APackage.GetSrcFilename;
  if (not FilenameIsAbsolute(Filename)) or (not FileExistsCached(Filename)) then begin
    IDEMessageDialog(lisCCOErrorCaption, lisPkgMangPleaseCompileThePackageFirst,
      mtError,[mbCancel]);
    exit;
  end;
  Result:=MainIDE.DoOpenEditorFile(Filename,-1,-1,[ofRegularFile]);
end;

function TPkgManager.DoCompileAutoInstallPackages(Flags: TPkgCompileFlags;
  OnlyBase: boolean): TModalResult;
var
  Dependency: TPkgDependency;
  OldDependency: TPkgDependency;
  Dependencies: TPkgDependency;
  AutoRemove: Boolean;
  CompilePolicy: TPackageUpdatePolicy;
begin
  PackageGraph.BeginUpdate(false);
  Dependencies:=PackageGraph.FirstAutoInstallDependency;
  try
    if OnlyBase then
    begin
      // create the list of base packages
      OldDependency:=PackageGraph.FirstAutoInstallDependency;
      Dependencies:=nil;
      while OldDependency<>nil do begin
        if (OldDependency.RequiredPackage<>nil)
        and PackageGraph.IsStaticBasePackage(OldDependency.RequiredPackage.Name) then
        begin
          Dependency:=TPkgDependency.Create;
          Dependency.Assign(OldDependency);
          Dependency.AddToEndOfList(Dependencies,pdlRequires);
        end;
        OldDependency:=OldDependency.NextRequiresDependency;
      end;
      Dependencies:=GetFirstDependency(Dependencies,pdlRequires);
      PackageGraph.OpenRequiredDependencyList(Dependencies);
    end;

    // check every installed package if it was loaded correctly
    Dependency:=Dependencies;
    AutoRemove:=false;
    while Dependency<>nil do begin
      OldDependency:=Dependency;
      Dependency:=Dependency.NextRequiresDependency;
      if OldDependency.LoadPackageResult<>lprSuccess then begin
        if not AutoRemove then begin
          Result:=IDEMessageDialog(lisProjAddPackageNotFound,
            Format(lisPkgMangThePackageIsMarkedForInstallationButCanNotBeFound, [
              '"', OldDependency.AsString, '"', #13]),
            mtError,[mbYes,mbYesToAll,mbAbort]);
          case Result of
          mrYes: ;
          mrYesToAll: AutoRemove:=true;
          else
            SaveAutoInstallDependencies;
            exit;
          end;
        end;
        OldDependency.RemoveFromList(PackageGraph.FirstAutoInstallDependency,pdlRequires);
        OldDependency.Free;
      end;
    end;
    SaveAutoInstallDependencies;

    // check consistency
    Result:=CheckPackageGraphForCompilation(nil,Dependencies,
                            EnvironmentOptions.GetParsedLazarusDirectory,false);
    if Result<>mrOk then exit;
    //DebugLn(['TPkgManager.DoCompileAutoInstallPackages LCLUnitPath=',PackageGraph.LCLPackage.CompilerOptions.GetUnitPath(true)]);

    // save all open files
    if not (pcfDoNotSaveEditorFiles in Flags) then begin
      Result:=MainIDE.DoSaveForBuild(crCompile);
      if Result<>mrOk then exit;
    end;
    
    // compile all auto install dependencies
    CompilePolicy:=pupAsNeeded;
    if pcfCompileDependenciesClean in Flags then
      CompilePolicy:=pupOnRebuildingAll;
    Result:=PackageGraph.CompileRequiredPackages(nil,Dependencies,false,
                                                 CompilePolicy);
    if Result<>mrOk then exit;
    
  finally
    if OnlyBase then
      FreeDependencyList(Dependencies,pdlRequires);
    PackageGraph.EndUpdate;
  end;
  Result:=mrOk;
end;

function TPkgManager.DoSaveAutoInstallConfig: TModalResult;
var
  TargetDir: String;
begin
  TargetDir:=MiscellaneousOptions.BuildLazProfiles.Current.TargetDirectory;
  IDEMacros.SubstituteMacros(TargetDir);
  TargetDir:=TrimFilename(TargetDir);
  if not ForceDirectory(TargetDir) then begin
    Result:=IDEMessageDialog(lisPkgMangUnableToCreateDirectory,
      Format(lisPkgMangUnableToCreateTargetDirectoryForLazarus, [#13, '"',
        TargetDir, '"', #13]),
      mtError,[mbCancel,mbAbort]);
    exit;
  end;

  Result:=PackageGraph.SaveAutoInstallConfig;
end;

function TPkgManager.DoPublishPackage(APackage: TLazPackage;
  Flags: TPkgSaveFlags; ShowDialog: boolean): TModalResult;
begin
  // show the publish dialog
  if ShowDialog then begin
    Result:=ShowPublishProjectDialog(APackage.PublishOptions);
    if Result<>mrOk then exit;
  end;

  // save package
  Result:=DoSavePackage(APackage,Flags);
  if Result<>mrOk then exit;

  // publish package
  Result:=MainIDE.DoPublishModule(APackage.PublishOptions,APackage.Directory,
                                  GetPublishPackageDir(APackage));
end;

function TPkgManager.GetUsableComponentUnits(CurRoot: TPersistent): TFPList;
var
  FMainUnitInfo: TUnitInfo;
  FMainUnitInfoValid: boolean;
  FMainOwner: TObject;
  FMainOwnerValid: boolean;

  function MainUnitInfo: TUnitInfo;
  begin
    if not FMainUnitInfoValid then
    begin
      if CurRoot is TComponent then
        FMainUnitInfo := Project1.UnitWithComponent(TComponent(CurRoot));
      FMainUnitInfoValid := True;
    end;
    Result := FMainUnitInfo;
  end;

  function MainOwner: TObject;
  var
    Owners: TFPList;
  begin
    if not FMainOwnerValid then
    begin
      if MainUnitInfo <> nil then
      begin
        if MainUnitInfo.IsPartOfProject then
          FMainOwner := Project1
        else
        begin
          Owners := GetOwnersOfUnit(MainUnitInfo.Filename);
          if (Owners <> nil) and (Owners.Count > 0) then
            FMainOwner := TObject(Owners[0]);
          Owners.Free;
        end;
      end;
      FMainOwnerValid := True;
    end;
    Result := FMainOwner;
  end;

  procedure CheckUnit(AnUnitInfo: TUnitInfo);
  var
    Owners: TFPList;
    OtherOwner: TObject;
    APackage: TLazPackage;
    ConflictDependency: TPkgDependency;
    FirstDependency: TPkgDependency;
  begin
    if (AnUnitInfo.Component=nil)
    or (AnUnitInfo.Component=CurRoot) then
      exit;
    // check if the component can be used
    // A component can only be used, if it has a CreateForm statement in the lpr
    // A unit can not be used, if it has no owner (project/package).
    // And a unit can not be used, if it belongs to a higher level package.
    // For example: Package A uses Package B.
    // A can use units of B, but B can not use units of A.
    if AnUnitInfo.IsPartOfProject and MainUnitInfo.IsPartOfProject then
    begin
      // both units belong to the project => ok
    end else if AnUnitInfo.IsPartOfProject then
    begin
      // AnUnitInfo belongs to Project, but MainUnitInfo does not
      // A project unit can only be used by the project => not allowed
      exit;
    end else
    begin
      Owners:=GetOwnersOfUnit(AnUnitInfo.Filename);
      if (Owners=nil) or (Owners.Count=0) then begin
        // AnUnitInfo does not belong to a project or package
        // => this unit can not be used
        Owners.Free;
        exit;
      end;
      OtherOwner:=TObject(Owners[0]);
      Owners.Free;
      if OtherOwner=MainOwner then begin
        // both units belong to the same owner => ok
      end else if (OtherOwner is TLazPackage) then begin
        // check if MainOwner can use the package
        APackage:=TLazPackage(OtherOwner);
        if MainOwner is TProject then
          FirstDependency:=TProject(MainOwner).FirstRequiredDependency
        else if MainOwner is TLazPackage then
          FirstDependency:=TLazPackage(MainOwner).FirstRequiredDependency
        else
          exit;
        ConflictDependency:=PackageGraph.FindConflictRecursively(
          FirstDependency,APackage);
        if ConflictDependency<>nil then exit;
        if MainOwner is TLazPackage then begin
          // check if package already uses MainOwner
          ConflictDependency:=PackageGraph.FindDependencyRecursively(
            APackage.FirstRequiredDependency,TLazPackage(MainOwner).Name);
          if ConflictDependency<>nil then exit;
        end;
      end else begin
        // AnUnitInfo does not belong to a Package => can not be used
        exit;
      end;
    end;
    // this unit can be used -> add components
    if Result=nil then
      Result:=TFPList.Create;
    Result.Add(AnUnitInfo);
  end;
  
var
  AnUnitInfo: TUnitInfo;
begin
  Result:=nil;
  if not (CurRoot is TComponent) then exit;
  FMainOwner:=nil;
  FMainOwnerValid:=false;
  FMainUnitInfo:=nil;
  FMainUnitInfoValid:=false;
  if (MainOwner=nil) or (MainUnitInfo=nil) then exit;
  // search all open designer forms (can be hidden)
  AnUnitInfo:=Project1.FirstUnitWithComponent;
  while AnUnitInfo<>nil do begin
    CheckUnit(AnUnitInfo);
    AnUnitInfo:=AnUnitInfo.NextUnitWithComponent;
  end;
end;

procedure TPkgManager.IterateComponentNames(CurRoot: TPersistent;
  TypeData: PTypeData; Proc: TGetStrProc);

  procedure CheckComponent(aRoot: TComponent);
  var
    i: integer;
    CurName: String;
  begin
    if aRoot = nil then exit;
    if (aRoot <> CurRoot) and (aRoot is TypeData^.ClassType) then
      Proc(aRoot.Name);
    for i := 0 to aRoot.ComponentCount - 1 do
      if (aRoot.Components[i] is TypeData^.ClassType) then
      begin
        CurName := aRoot.Components[i].Name;
        if aRoot <> CurRoot then
          CurName := aRoot.Name + '.' + CurName;
        Proc(CurName);
      end;
  end;

var
  UnitList: TFPList;
  i: Integer;
begin
  if not (CurRoot is TComponent) then exit;
  CheckComponent(TComponent(CurRoot));
  UnitList := GetUsableComponentUnits(CurRoot);
  if UnitList = nil then exit;
  try
    for i := 0 to UnitList.Count - 1 do
      CheckComponent(TUnitInfo(UnitList[i]).Component);
  finally
    UnitList.Free;
  end;
end;

function TPkgManager.FindReferencedRootComponent(CurRoot: TPersistent;
  const ComponentName: string): TComponent;
var
  UnitList: TFPList;
  ARoot: TComponent;
  i: integer;
begin
  //DebugLn(['search ', ComponentName, ' CurRoot = ', dbgsName(CurRoot)]);
  Result := nil;
  UnitList := GetUsableComponentUnits(CurRoot);
  if UnitList = nil then 
    Exit;
  try
    for i := 0 to UnitList.Count - 1 do 
    begin
      ARoot := TUnitInfo(UnitList[i]).Component;
      DebugLn(['TPkgManager.FindReferencedRootComponent Root=',dbgsName(CurRoot),' Searched="',ComponentName,'" other root=',dbgsName(ARoot)]);
      if (ARoot <> nil) and (SysUtils.CompareText(ComponentName, ARoot.Name) = 0) then
      begin
        Result := ARoot;
        break;
      end;
    end;
  finally
    UnitList.Free;
  end;
  //DebugLn('search end');
end;

function TPkgManager.FindUsableComponent(CurRoot: TPersistent;
  const ComponentPath: string): TComponent;

  procedure CheckComponent(const RootName, SubPath: string; aRoot: TComponent);
  var
    i: integer;
  begin
    if aRoot = nil then exit;
    if (SysUtils.CompareText(RootName, aRoot.Name) <> 0) then exit;

    if SubPath = '' then
    begin
      Result := aRoot;
      Exit;
    end;

    for i := 0 to aRoot.ComponentCount - 1 do
      if SysUtils.CompareText(aRoot.Components[i].Name, SubPath) = 0 then
      begin
        Result := aRoot.Components[i];
        exit;
      end;
  end;

var
  UnitList: TFPList;
  SubPath: String;
  p: LongInt;
  RootName: String;
  i: Integer;
begin
  Result := nil;
  if not (CurRoot is TComponent) then exit;
  SubPath := ComponentPath;
  p := System.Pos('.',SubPath);
  if p < 1 then
    RootName := ''
  else begin
    RootName := copy(ComponentPath, 1, p - 1);
    SubPath := copy(SubPath, p + 1, length(SubPath));
  end;
  if (RootName = '') or (SysUtils.CompareText(RootName, TComponent(CurRoot).Name) = 0) then
    CheckComponent(TComponent(CurRoot).Name, SubPath, TComponent(CurRoot));
  if (p < 1) then
    if Result = nil then
    begin
      RootName := SubPath;
      SubPath := '';
    end
    else
      exit;
  UnitList := GetUsableComponentUnits(CurRoot);
  if UnitList = nil then exit;
  try
    for i := 0 to UnitList.Count-1 do
    begin
      CheckComponent(RootName, SubPath, TUnitInfo(UnitList[i]).Component);
      if Result <> nil then exit;
    end;
  finally
    UnitList.Free;
  end;
end;

function TPkgManager.OnProjectInspectorOpen(Sender: TObject): boolean;
var
  Dependency: TPkgDependency;
begin
  Result:=false;
  if (Sender=nil) or (not (Sender is TProjectInspectorForm)) then exit;
  Dependency:=TProjectInspectorForm(Sender).GetSelectedDependency;
  if Dependency=nil then exit;
  // user has selected a dependency -> open package
  Result:=true;
  if PackageGraph.OpenDependency(Dependency,false)<>lprSuccess then
    exit;
  DoOpenPackage(Dependency.RequiredPackage,[],false);
end;

function TPkgManager.OnProjectInspectorAddDependency(Sender: TObject;
  ADependency: TPkgDependency): TModalResult;
begin
  Result:=AddProjectDependency(Project1,ADependency);
end;

function TPkgManager.OnProjectInspectorRemoveDependency(Sender: TObject;
  ADependency: TPkgDependency): TModalResult;
var
  ShortUnitName: String;
  Dummy: Boolean;
begin
  Result:=mrOk;
  Project1.RemoveRequiredDependency(ADependency);
  //debugln('TPkgManager.OnProjectInspectorRemoveDependency A');
  if (Project1.MainUnitID>=0)
  and (pfMainUnitHasUsesSectionForAllUnits in Project1.Flags)
  then begin
    MainIDEInterface.SaveSourceEditorChangesToCodeCache(nil);
    ShortUnitName:=ADependency.PackageName;
    //debugln('TPkgManager.OnProjectInspectorRemoveDependency B ShortUnitName="',ShortUnitName,'"');
    if (ShortUnitName<>'') then begin
      Dummy:=CodeToolBoss.RemoveUnitFromAllUsesSections(
                                    Project1.MainUnitInfo.Source,ShortUnitName);
      if Dummy then
        Project1.MainUnitInfo.Modified:=true
      else begin
        MainIDEInterface.DoJumpToCodeToolBossError;
        Result:=mrCancel;
        exit;
      end;
    end;
  end;
end;

function TPkgManager.OnProjectInspectorReAddDependency(Sender: TObject;
  ADependency: TPkgDependency): TModalResult;
begin
  Result:=mrOk;
  Project1.ReaddRemovedDependency(ADependency);
  PackageGraph.OpenDependency(ADependency,false);
  if (ADependency.RequiredPackage<>nil)
  and (not ADependency.RequiredPackage.AutoCreated) then begin
    AddUnitToProjectMainUsesSection(Project1,ADependency.PackageName,'');
  end;
end;

function TPkgManager.CanOpenDesignerForm(AnUnitInfo: TUnitInfo;
  Interactive: boolean): TModalResult;
var
  AProject: TProject;
begin
  Result:=mrCancel;
  if AnUnitInfo=nil then exit;
  AProject:=AnUnitInfo.Project;
  if AProject=nil then exit;
  Result:=CheckProjectHasInstalledPackages(AProject,Interactive);
end;

function TPkgManager.DoClosePackageEditor(APackage: TLazPackage): TModalResult;
begin
  if APackage.Editor<>nil then
    APackage.Editor.Free;
  Result:=mrOk;
end;

function TPkgManager.DoSaveAllPackages(Flags: TPkgSaveFlags): TModalResult;
var
  AllSaved: Boolean;
  i: Integer;
  CurPackage: TLazPackage;
begin
  try
    repeat
      AllSaved:=true;
      i:=0;
      while i<PackageGraph.Count do begin
        CurPackage:=PackageGraph[i];
        if CurPackage.Modified and (not CurPackage.ReadOnly)
        and (not (lpfSkipSaving in CurPackage.Flags)) then begin
          Result:=DoSavePackage(CurPackage,Flags);
          if Result=mrIgnore then begin
            CurPackage.Flags:=CurPackage.Flags+[lpfSkipSaving];
            Result:=mrOk;
          end;
          if Result<>mrOk then exit;
          AllSaved:=false;
        end;
        inc(i);
      end;
    until AllSaved;
  finally
    // clear all lpfSkipSaving flags
    for i:=0 to PackageGraph.Count-1 do begin
      CurPackage:=PackageGraph[i];
      CurPackage.Flags:=CurPackage.Flags-[lpfSkipSaving];
    end;
  end;
  Result:=mrOk;
end;

{ TLazPackageDescriptors }

function TLazPackageDescriptors.GetItems(Index: integer): TPackageDescriptor;
begin
  Result:=TPackageDescriptor(FItems[Index]);
end;

constructor TLazPackageDescriptors.Create;
begin
  PackageDescriptors:=Self;
  FItems:=TFPList.Create;
end;

destructor TLazPackageDescriptors.Destroy;
var
  i: Integer;
begin
  fDestroying:=true;
  for i:=Count-1 downto 0 do Items[i].Release;
  FItems.Free;
  FItems:=nil;
  PackageDescriptors:=nil;
  inherited Destroy;
end;

function TLazPackageDescriptors.Count: integer;
begin
  Result:=FItems.Count;
end;

function TLazPackageDescriptors.GetUniqueName(const Name: string): string;
var
  i: Integer;
begin
  Result:=Name;
  if IndexOf(Result)<0 then exit;
  i:=0;
  repeat
    inc(i);
    Result:=Name+IntToStr(i);
  until IndexOf(Result)<0;
end;

function TLazPackageDescriptors.IndexOf(const Name: string): integer;
begin
  Result:=Count-1;
  while (Result>=0) and (AnsiCompareText(Name,Items[Result].Name)<>0) do
    dec(Result);
end;

function TLazPackageDescriptors.FindByName(const Name: string
  ): TPackageDescriptor;
var
  i: LongInt;
begin
  i:=IndexOf(Name);
  if i>=0 then
    Result:=Items[i]
  else
    Result:=nil;
end;

procedure TLazPackageDescriptors.RegisterDescriptor(
  Descriptor: TPackageDescriptor);
begin
  if Descriptor.Name='' then
    raise Exception.Create('TLazPackageDescriptors.RegisterDescriptor Descriptor.Name empty');
  Descriptor.Name:=GetUniqueName(Descriptor.Name);
  FItems.Add(Descriptor);
end;

procedure TLazPackageDescriptors.UnregisterDescriptor(
  Descriptor: TPackageDescriptor);
var
  i: LongInt;
begin
  if fDestroying then exit;
  i:=FItems.IndexOf(Descriptor);
  if i<0 then
    raise Exception.Create('TLazPackageDescriptors.UnregisterDescriptor');
  FItems.Delete(i);
  Descriptor.Release;
end;

procedure TLazPackageDescriptors.AddDefaultPackageDescriptors;
begin
  NewIDEItems.Add(TNewLazIDEItemCategoryPackage.Create(PkgDescGroupName));
  RegisterPackageDescriptor(TPackageDescriptorStd.Create);
end;

{ TPackageDescriptorStd }

constructor TPackageDescriptorStd.Create;
begin
  inherited Create;
  Name:=PkgDescNameStandard;
end;

function TPackageDescriptorStd.GetLocalizedName: string;
begin
  Result:=lisPackage;
end;

function TPackageDescriptorStd.GetLocalizedDescription: string;
begin
  Result:=Format(lisNewDlgCreateANewStandardPackageAPackageIsACollectionOfUn,
                 [#13]);
end;

end.

