{**************************************************************************************************} { } { Project JEDI Code Library (JCL) extension } { } { The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); } { you may not use this file except in compliance with the License. You may obtain a copy of the } { License at http://www.mozilla.org/MPL/ } { } { Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF } { ANY KIND, either express or implied. See the License for the specific language governing rights } { and limitations under the License. } { } { The Original Code is JediInstallIntf.pas. } { } { The Initial Developer of the Original Code is Petr Vones. Portions created by Petr Vones are } { Copyright (C) of Petr Vones. All Rights Reserved. } { } { Contributor(s): Robert Rossmair (crossplatform & BCB support) } { Florent Ouchet (new core for more than one target) } { } { Last modified: $Date: 2006-12-30 20:56:40 +0100 (sam., 30 déc. 2006) $ } { } {**************************************************************************************************} unit JediInstall; {$I jcl.inc} {$I crossplatform.inc} interface uses SysUtils, Classes, JclContainerIntf; type TJediInstallGUIOption = ( goExpandable, goRadioButton, goNoAutoCheck, // do not auto-check when the parent node gets checked goStandaloneParent, // do not auto-uncheck when all child nodes are unchecked goChecked ); TJediInstallGUIOptions = set of TJediInstallGUIOption; type TDialogType = (dtWarning, dtError, dtInformation, dtConfirmation); TDialogResponse = (drYes, drNo, drOK, drCancel); TDialogResponses = set of TDialogResponse; EJediInstallInitFailure = class(Exception); IJediPage = interface ['{5669B427-F46D-4737-9D1D-680C52CDE3DF}'] function GetCaption: string; procedure SetCaption(const Value: string); function GetHintAtPos(ScreenX, ScreenY: Integer): string; procedure Show; property Caption: string read GetCaption write SetCaption; end; IJediReadmePage = interface(IJediPage) ['{5DA5C5C9-649F-47CF-B64A-55E983CA88EC}'] procedure SetReadmeFileName(const Value: string); function GetReadmeFileName: string; property ReadmeFileName: string read GetReadmeFileName write SetReadmeFileName; end; IJediInstallPage = interface(IJediPage) ['{91C3A26F-0258-410A-9EAF-06F86C5748CF}'] procedure AddInstallOption(Id: Integer; Options: TJediInstallGUIOptions; const Caption: string = ''; const Hint: string = ''; Parent: Integer = -1); procedure InitDisplay; function GetOptionChecked(Id: Integer): Boolean; procedure SetOptionChecked(Id: Integer; Value: Boolean); function GetDirectoryCount: Integer; function GetDirectory(Index: Integer): string; procedure SetDirectory(Index: Integer; const Value: string); function AddDirectory(Caption: string): Integer; function GetProgress: Integer; procedure SetProgress(Value: Integer); procedure BeginInstall; procedure MarkOptionBegin(Id: Integer); procedure MarkOptionEnd(Id: Integer; Failed: Boolean); procedure EndInstall; procedure CompilationStart(const ProjectName: string); procedure AddHint(const Line: string); procedure AddWarning(const Line: string); procedure AddError(const Line: string); procedure AddFatal(const Line: string); procedure AddText(const Line: string); procedure CompilationProgress(const FileName: string; LineNumber: Integer); procedure SetIcon(const FileName: string); property OptionChecked[Id: Integer]: Boolean read GetOptionChecked write SetOptionChecked; property DirectoryCount: Integer read GetDirectoryCount; property Directories[Index: Integer]: string read GetDirectory write SetDirectory; property Progress: Integer read GetProgress write SetProgress; end; TOptionRec = record Name: string; Value: string; end; TOptionArray = array of TOptionRec; TStringArray = array of string; IJediConfiguration = interface ['{4E96C8E8-ABA7-475D-BDF9-88B158F2CED3}'] function GetSections: TStringArray; function GetOptions(const Section: string): TOptionArray; function GetOptionAsBool(const Section: string; Id: Integer): Boolean; procedure SetOptionAsBool(const Section: string; Id: Integer; Value: Boolean); function GetOptionAsBoolByName(const Section: string; const Name: string): Boolean; procedure SetOptionAsBoolByName(const Section: string; const Name: string; Value: Boolean); function GetOptionAsString(const Section: string; Id: Integer): string; procedure SetOptionAsString(const Section: string; Id: Integer; const Value: string); function GetOptionAsStringByName(const Section: string; const Name: string): string; procedure SetOptionAsStringByName(const Section: string; const Name: string; const Value: string); procedure Clear; procedure DeleteSection(const Section: string); procedure DeleteOption(const Section: string; Id: Integer); function SectionExists(const Section: string): Boolean; function ValueExists(const Section: string; Id: Integer): Boolean; overload; function ValueExists(const Section: string; const Name: string): Boolean; overload; property Sections: TStringArray read GetSections; property Options[const Section: string]: TOptionArray read GetOptions; property OptionAsBool[const Section: string; Id: Integer]: Boolean read GetOptionAsBool write SetOptionAsBool; property OptionAsBoolByName[const Section: string; const Name: string]: Boolean read GetOptionAsBoolByName write SetOptionAsBoolByName; property OptionAsString[const Section: string; Id: Integer]: string read GetOptionAsString write SetOptionAsString; property OptionAsStringByName[const Section: string; const Name: string]: string read GetOptionAsStringByName write SetOptionAsStringByName; end; IJediDistribution = interface ['{90E201C9-EA6B-446A-9251-D2516867874D}'] end; TInstallEvent = procedure of Object; // GUI abstraction layer IJediInstallGUI = interface ['{3471A535-51D7-4FBB-B6AE-20D136E38E34}'] function Dialog(const Text: string; DialogType: TDialogType = dtInformation; Options: TDialogResponses = [drOK]): TDialogResponse; function CreateReadmePage: IJediReadmePage; function CreateInstallPage: IJediInstallPage; function GetPageCount: Integer; function GetPage(Index: Integer): IJediPage; function GetStatus: string; procedure SetStatus(const Value: string); function GetCaption: string; procedure SetCaption(const Value: string); function GetProgress: Integer; procedure SetProgress(Value: Integer); procedure Execute; property PageCount: Integer read GetPageCount; property Pages[Index: Integer]: IJediPage read GetPage; property Status: string read GetStatus write SetStatus; property Caption: string read GetCaption write SetCaption; property Progress: Integer read GetProgress write SetProgress; end; IJediProduct = interface ['{CF5BE67A-4A49-43FB-8F6E-217A51023DA4}'] procedure Init; procedure Install; procedure Uninstall; procedure Close; end; TJediInstallGUICreator = function: IJediInstallGUI; TJediConfigurationCreator = function: IJediConfiguration; TCompileLineType = (clText, clFileProgress, clHint, clWarning, clError, clFatal); TJediInstallCore = class(TComponent) private FInstallGUI: IJediInstallGUI; {$IFDEF VisualCLX} FGUIComponent: TComponent; {$ENDIF VisualCLX} FProducts: IJclIntfList; FClosing: Boolean; FOptions: TStrings; FInstallGUICreator: TJediInstallGUICreator; FConfiguration: IJediConfiguration; FConfigurationCreator: TJediConfigurationCreator; function GetProductCount: Integer; function GetProduct(Index: Integer): IJediProduct; function GetInstallGUI: IJediInstallGUI; function GetConfiguration: IJediConfiguration; {$IFDEF VisualCLX} protected procedure Notification(AComponent: TComponent; Operation: TOperation); override; {$ENDIF VisualCLX} public constructor Create; reintroduce; destructor Destroy; override; function AddProduct(AProduct: IJediProduct): Integer; procedure Execute; procedure Install; procedure Uninstall; procedure Close; function AddInstallOption(const Name: string): Integer; function GetInstallOptionName(Id: Integer): string; function GetOptionCount: Integer; function ProcessLogLine(const Line: string; var LineType: TCompileLineType; Page: IJediInstallPage): string; property ProductCount: Integer read GetProductCount; property Products[Index: Integer]: IJediProduct read GetProduct; property Closing: Boolean read FClosing; property InstallOptionName[Id: Integer]: string read GetInstallOptionName; property OptionCount: Integer read GetOptionCount; property InstallGUI: IJediInstallGUI read GetInstallGUI; property InstallGUICreator: TJediInstallGUICreator read FInstallGUICreator write FInstallGUICreator; property Configuration: IJediConfiguration read GetConfiguration; property ConfigurationCreator: TJediConfigurationCreator read FConfigurationCreator write FConfigurationCreator; end; var JediTargetOption: Integer = -1; function InstallCore: TJediInstallCore; resourcestring RsCantFindFiles = 'Can not find installation files, check your installation.'; RsCloseRADTool = 'Please close all running instances of Delphi/C++Builder IDE before the installation.'; RsConfirmInstall = 'Are you sure to install all selected features?'; RsConfirmUninstall = 'Do you really want to uninstall the JCL?'; RsInstallSuccess = 'Installation finished'; RsInstallFailure = 'Installation failed.'#10'Check compiler output for details.'; RsNoInstall = 'There is no Delphi/C++Builder installation on this machine. Installer will close.'; RsUpdateNeeded = 'You should install latest Update Pack #%d for %s.'#13#10 + 'Would you like to open Borland support web page?'; RsHintTarget = 'Installation target'; implementation uses JclArrayLists; var InternalInstallCore: TJediInstallCore = nil; function InstallCore: TJediInstallCore; begin if not Assigned(InternalInstallCore) then InternalInstallCore := TJediInstallCore.Create; Result := InternalInstallCore; end; //=== { TJediInstallCore } =================================================== function TJediInstallCore.AddInstallOption(const Name: string): Integer; begin Result := FOptions.IndexOf(Name); if Result = -1 then Result := FOptions.Add(Name); end; function TJediInstallCore.AddProduct(AProduct: IJediProduct): Integer; begin Result := FProducts.Size; FProducts.Add(AProduct); end; procedure TJediInstallCore.Close; var Index: Integer; begin if Closing then Exit; FClosing := True; for Index := FProducts.Size - 1 downto 0 do (FProducts.GetObject(Index) as IJediProduct).Close; FProducts.Clear; FProducts := nil; FInstallGUI := nil; FConfiguration := nil; end; constructor TJediInstallCore.Create; begin inherited Create(nil); FOptions := TStringList.Create; FProducts := TJclIntfArrayList.Create; FClosing := False; JediTargetOption := AddInstallOption('joTarget'); end; destructor TJediInstallCore.Destroy; begin Close; FConfigurationCreator := nil; FInstallGUICreator := nil; FProducts := nil; FInstallGUI := nil; FConfiguration := nil; FOptions.Free; inherited Destroy; end; procedure TJediInstallCore.Execute; var Index: Integer; AInstallGUI: IJediInstallGUI; begin AInstallGUI := InstallGUI; for Index := FProducts.Size - 1 downto 0 do (FProducts.GetObject(Index) as IJediProduct).Init; if Assigned(AInstallGUI) then AInstallGUI.Execute; end; function TJediInstallCore.GetConfiguration: IJediConfiguration; begin if Assigned(FConfigurationCreator) and not Assigned(FConfiguration) then FConfiguration := ConfigurationCreator; Result := FConfiguration; end; function TJediInstallCore.GetInstallGUI: IJediInstallGUI; {$IFDEF VisualCLX} var CompRef: IInterfaceComponentReference; {$ENDIF VisualCLX} begin if Assigned(FInstallGUICreator) and not Assigned(FInstallGUI) then FInstallGUI := InstallGUICreator; Result := FInstallGUI; {$IFDEF VisualCLX} Result.QueryInterface(IInterfaceComponentReference, CompRef); if Assigned(CompRef) then begin FGUIComponent := CompRef.GetComponent; FGuiComponent.FreeNotification(Self); end; {$ENDIF VisualCLX} end; function TJediInstallCore.GetInstallOptionName(Id: Integer): string; begin Result := FOptions.Strings[Id]; end; function TJediInstallCore.GetOptionCount: Integer; begin Result := FOptions.Count; end; function TJediInstallCore.GetProduct(Index: Integer): IJediProduct; begin Result := FProducts.GetObject(Index) as IJediProduct; end; function TJediInstallCore.GetProductCount: Integer; begin Result := FProducts.Size; end; procedure TJediInstallCore.Install; var Index: Integer; begin for Index := FProducts.Size - 1 downto 0 do (FProducts.GetObject(Index) as IJediProduct).Install; end; {$IFDEF VisualCLX} procedure TJediInstallCore.Notification(AComponent: TComponent; Operation: TOperation); begin inherited Notification(AComponent, Operation); if (Operation = opRemove) and (AComponent = FGUIComponent) then begin FGUIComponent := nil; FInstallGUI := nil; end; end; {$ENDIF VisualCLX} function TJediInstallCore.ProcessLogLine(const Line: string; var LineType: TCompileLineType; Page: IJediInstallPage): string; function HasText(Text: string; const Values: array of string): Boolean; var i: Integer; begin Result := True; Text := AnsiLowerCase(Text); for i := Low(Values) to High(Values) do if Pos(Values[i], Text) > 0 then Exit; Result := False; end; function IsCompileFileLine(const Line: string): Boolean; function PosLast(Ch: Char; const S: string): Integer; begin for Result := Length(S) downto 1 do if S[Result] = Ch then Exit; Result := 0; end; var ps, psEnd, LineNum, Err: Integer; Filename: string; begin Result := False; ps := PosLast('(', Line); if (ps > 0) and (Pos(': ', Line) = 0) and (Pos('.', Line) > 0) then begin psEnd := PosLast(')', Line); if psEnd < ps then Exit; Filename := Copy(Line, 1, ps - 1); if (Filename <> '') and (Filename[Length(Filename)] > #32) then begin Val(Copy(Line, ps + 1, psEnd - ps - 1), LineNum, Err); if Err = 0 then begin if Assigned(Page) then Page.CompilationProgress(FileName, LineNum); Result := True; end; end; end; end; begin LineType := clText; Result := Line; if Line = '' then Exit; if IsCompileFileLine(Line) then begin LineType:= clFileProgress; Result := ''; end else if HasText(Line, ['hint: ', 'hinweis: ', 'suggestion: ', 'conseil: ']) then // do not localize begin // hide hint about getter/setter names if (Pos(' H2369 ', Line) = 0) then begin LineType := clHint; if Assigned(Page) then Page.AddHint(Line); end else Result := ''; end else if HasText(Line, ['warning: ', 'warnung: ', 'avertissement: ']) then // do not localize begin // hide platform warnings if (Pos(' W1002 ', Line) = 0) then begin LineType := clWarning; if Assigned(Page) then Page.AddWarning(Line); end else Result := ''; end else if HasText(Line, ['error: ', 'fehler: ', 'erreur: ']) then // do not localize begin LineType := clError; if Assigned(Page) then Page.AddError(Line); end else if HasText(Line, ['fatal: ', 'schwerwiegend: ', 'fatale: ']) then // do not localize begin LineType := clFatal; if Assigned(Page) then Page.AddFatal(Line); end else if Assigned(Page) then Page.AddText(Line); end; procedure TJediInstallCore.Uninstall; var Index: Integer; begin for Index := FProducts.Size - 1 downto 0 do (FProducts.GetObject(Index) as IJediProduct).Uninstall; end; initialization finalization InternalInstallCore.Free; end.