{**************************************************************************************************}
{  WARNING:  JEDI preprocessor generated unit.  Do not edit.                                       }
{**************************************************************************************************}

{**************************************************************************************************}
{                                                                                                  }
{ 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 JediInstallerMain.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.                                                }
{                                                                                                  }
{ Contributors:                                                                                    }
{   Andreas Hausladen (ahuser)                                                                     }
{   Robert Rossmair (rrossmair) - crossplatform & BCB support, refactoring                         }
{                                                                                                  }
{**************************************************************************************************}

// $Id: JediInstallerMain.pas,v 1.34 2006/02/05 13:26:15 outchy Exp $

unit QJediInstallerMain;

{$I jcl.inc}
{$I crossplatform.inc}

interface

uses
  {$IFDEF MSWINDOWS}
  Windows, Messages,
  {$ENDIF MSWINDOWS}
  SysUtils, Classes,
  Types,
  Qt, QGraphics, QControls, QForms, QDialogs, QStdCtrls, QExtCtrls, QMenus, QButtons, QComCtrls, QImgList,
  QProductFrames,
  JclBorlandTools, JediInstall;

const
  QEventType_UMCheckUpdates = QEventType(Integer(QEventType_ClxUser) + $100);

type
  TMainForm = class(TForm, IJediInstallTool)
    InstallBtn: TBitBtn;
    UninstallBtn: TBitBtn;
    QuitBtn: TBitBtn;
    JediImage: TImage;
    TitlePanel: TPanel;
    Title: TLabel;
    ProductsPageControl: TPageControl;
    StatusBevel: TBevel;
    StatusLabel: TLabel;
    Bevel1: TBevel;
    ProgressBar: TProgressBar;
    ImageList: TImageList;
    ReadmePage: TTabSheet;
    ReadmePane: TTextViewer;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure QuitBtnClick(Sender: TObject);
    procedure InstallBtnClick(Sender: TObject);
    procedure UninstallBtnClick(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure JediImageClick(Sender: TObject);
    procedure TreeViewCollapsing(Sender: TObject; Node: TTreeNode;
      var AllowCollapse: Boolean);
    procedure BplPathEditChange(Sender: TObject);
  private
    FBorRADToolInstallations: TJclBorRADToolInstallations;
    FJclInstall: IJediInstall;
    FSystemPaths: TStringList;
    FFeatureNode: TTreeNode;
    FFeatureChanged: Boolean;
    FHintPos: TPoint;
    function ActiveView: TProductFrame;
    function CheckUpdatePack(Installation: TJclBorRADToolInstallation): Boolean;
    function CreateView(Installation: TJclBorRADToolInstallation): Boolean;
    function ExpandOptionTree(Installation: TJclBorRADToolInstallation): Boolean;
    procedure InstallationStarted(Installation: TJclBorRADToolInstallation);
    procedure InstallationFinished(Installation: TJclBorRADToolInstallation);
    procedure InstallationProgress(Percent: Cardinal);
    procedure ReadSystemPaths;
    function View(Installation: TJclBorRADToolInstallation): TProductFrame;
    procedure TreeViewChange(Sender: TObject; Node: TTreeNode);
    procedure TreeViewEnter(Sender: TObject);
    procedure TreeViewExit(Sender: TObject);
    procedure TreeViewMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure UpdateFeatureInfo(Node: TTreeNode);
  protected
    function EventFilter(Sender: QObjectH; Event: QEventH): Boolean; override;
    function InfoFile(Node: TTreeNode): string;
    function OptionGUI(Installation: TJclBorRADToolInstallation): TObject;
    function GUIAddOption(GUI, Parent: TObject; Option: TJediInstallOption; const Text: string;
      GUIOptions: TJediInstallGUIOptions): TObject;
    procedure HandleException(Sender: TObject; E: Exception);
    property JclDistribution: IJediInstall read FJclInstall;
    // IJediInstallTool
    function GetBPLPath(Installation: TJclBorRADToolInstallation): string;
    function GetDCPPath(Installation: TJclBorRADToolInstallation): string;
    procedure SetBPLPath(Installation: TJclBorRADToolInstallation; const Value: string);
    procedure SetDCPPath(Installation: TJclBorRADToolInstallation; const Value: string);
  public
    procedure ShowFeatureHint(var HintStr: WideString;
      var CanShow: Boolean; var HintInfo: THintInfo);
    function CheckRunningInstances: Boolean;
    procedure Install;
    procedure Uninstall;
    function SystemPathValid(const Path: string): Boolean;
    // IJediInstallTool
    function FeatureChecked(FeatureID: Cardinal; Installation: TJclBorRADToolInstallation): Boolean;
    function GetBorRADToolInstallations: TJclBorRADToolInstallations;
    function Dialog(const Text: string; DialogType: TDialogType = dtInformation;
      Options: TDialogResponses = [drOK]): TDialogResponse;
    procedure SetReadme(const FileName: string);
    procedure UpdateInfo(Installation: TJclBorRADToolInstallation; const InfoText: String);
    procedure UpdateStatus(const Text: string);
    procedure WriteInstallLog(Installation: TJclBorRADToolInstallation; const Text: string);
    property BorRADToolInstallations: TJclBorRADToolInstallations read FBorRADToolInstallations;
    property BPLPath[Installation: TJclBorRADToolInstallation]: string read GetBPLPath write SetBPLPath;
    property DCPPath[Installation: TJclBorRADToolInstallation]: string read GetDCPPath write SetDCPPath;
  end;

var
  MainForm: TMainForm;

implementation

{$R *.xfm}

uses
  {$IFDEF UNIX}
  Libc,
  {$ENDIF UNIX}
  {$IFDEF MSWINDOWS}
  FileCtrl,
  JclDebug, JclShell,
  {$ENDIF MSWINDOWS}
  JclBase, JclFileUtils, JclStrings, JclSysInfo, JclSysUtils,
  JclInstall;

const
  {$IFNDEF RTL140_UP}
  PathSep = ';';
  {$ENDIF RTL140_UP}
  {$IFDEF MSWINDOWS}
  SupportURLs: array[TJclBorRADToolKind] of string = (
                'http://www.borland.com/devsupport/delphi/',
                'http://www.borland.com/devsupport/bcppbuilder/',
                'http://www.borland.com/devsupport/delphi/');
  {$ENDIF MSWINDOWS}
  {$IFDEF KYLIX}
  KylixSupportURL     = 'http://www.borland.com/devsupport/kylix/';
  {$ENDIF KYLIX}
  DelphiJediURL     = 'http://delphi-jedi.org';
  VersionSignature  = 'D%d';
  BCBTag            = $10000;
  VersionMask       = $FFFF;

function FeatureID(Node: TTreeNode): Cardinal;
begin
  Result := Cardinal(Node.Data) and FID_NumberMask;
end;

{ TMainForm }

function TMainForm.ActiveView: TProductFrame;
var
  Page: TTabSheet;
  Control: TControl;
begin
  Result := nil;
  Page := ProductsPageControl.ActivePage;
  Control := Page.Controls[0];
  if Control is TProductFrame then
    Result := TProductFrame(Control);
end;

function TMainForm.InfoFile(Node: TTreeNode): string;
begin
  if Assigned(Node) then
    Result := FJclInstall.FeatureInfoFileName(FeatureID(Node));
end;

function TMainForm.CreateView(Installation: TJclBorRADToolInstallation): Boolean;
var
  Page: TTabSheet;
  ProductFrame: TProductFrame;
begin
  Page := TTabSheet.Create(Self);
  with Installation do
  begin
    Page.Name := Format('%sPage', [VersionNumberStr]);
    Page.Caption := Name;
  end;
  Page.PageControl := ProductsPageControl;
  ProductFrame := TProductFrame.Create(Self);
  ProductFrame.Installation := Installation;
  ProductFrame.TreeView.Images := ImageList;
  ProductFrame.TreeView.OnChange := TreeViewChange;
  ProductFrame.TreeView.OnCollapsing := TreeViewCollapsing;
  ProductFrame.TreeView.OnEnter := TreeViewEnter;
  ProductFrame.TreeView.OnExit := TreeViewExit;
  ProductFrame.TreeView.OnMouseMove := TreeViewMouseMove;
  ProductFrame.Align := alClient;
  ProductFrame.Parent := Page;
  FJclInstall.SetOnWriteLog(Installation, ProductFrame.LogOutputLine);
  Result := True;
end;

function TMainForm.CheckRunningInstances: Boolean;
begin
  Result := FBorRADToolInstallations.AnyInstanceRunning;
  if Result then
    Dialog(RsCloseRADTool, dtWarning);
end;

function TMainForm.CheckUpdatePack(Installation: TJclBorRADToolInstallation): Boolean;
var
  Msg: string;
begin
  Result := True;
  with Installation do
    if UpdateNeeded then
    begin
      Msg := Format(RsUpdateNeeded, [LatestUpdatePack, Name]);
      if Dialog(Msg, dtWarning, [drYes, drNo]) = drYes then
      {$IFDEF MSWINDOWS}
        ShellExecEx(SupportURLs[RadToolKind]);
      {$ENDIF MSWINDOWS}
      {$IFDEF UNIX}
      { TODO : Analoguous function for Linux };
      // Exec(KylixSupportURL);
      {$ENDIF UNIX}
    end;
end;

function TMainForm.GUIAddOption(GUI, Parent: TObject; Option: TJediInstallOption;
  const Text: string; GUIOptions: TJediInstallGUIOptions): TObject;
const
  Icon: array[Boolean] of Integer = (IcoUnchecked, IcoChecked);
  Flag: array[Boolean] of Cardinal = (0, FID_Checked);
var
  FeatureID: Cardinal;
  Nodes: TTreeNodes;
  Node, ParentNode: TTreeNode;
  Checked: Boolean;
begin
  ParentNode := TTreeNode(Parent);
  Checked := goChecked in GUIOptions;
  FeatureID := Cardinal(Ord(Option)) + Flag[goChecked in GUIOptions];
  if goNoAutoCheck in GUIOptions then
    FeatureID := FeatureID + FID_NoAutoCheck;
  if goStandAloneParent in GUIOptions then
    FeatureID := FeatureID + FID_StandAloneParent;
  if goRadioButton in GUIOptions  then
    FeatureID := FeatureID + FID_RadioButton;
  if goExpandable in GUIOptions then
    FeatureID := FeatureID + FID_Expandable;
  Nodes := TTreeNodes(GUI);
  if Parent = nil then
    Node := Nodes.AddObject(nil, Text, Pointer(FeatureID))
  else
    Node := Nodes.AddChildObject(ParentNode, Text, Pointer(FeatureID));
  Node.ImageIndex := Icon[Checked];
  Node.SelectedIndex := Icon[Checked];
  Result := Node;
end;

procedure TMainForm.HandleException(Sender: TObject; E: Exception);
begin
  if E is EJediInstallInitFailure then
  begin
    Dialog(E.Message, dtError);
    Application.ShowMainForm := False;
    Application.Terminate;
  end
  else
    Application.ShowException(E);
end;

procedure TMainForm.Install;
var
  Res: Boolean;
begin
  ProgressBar.Position := 0;
  ProgressBar.Visible := True;
  Screen.Cursor := crHourGlass;
  try
    Res := FJclInstall.Install;
    Screen.Cursor := crDefault;
    if Res then
      Dialog(RsInstallSuccess)
    else
      Dialog(RsInstallFailure);
  finally
    ProgressBar.Visible := False;
    Screen.Cursor := crDefault;
  end;
end;

procedure TMainForm.Uninstall;
begin
  ProgressBar.Position := 0;
  ProgressBar.Visible := True;
  Screen.Cursor := crHourGlass;
  try
    FJclInstall.Uninstall;
    Screen.Cursor := crDefault;
  finally
    ProgressBar.Visible := False;
    Screen.Cursor := crDefault;
  end;
end;

procedure TMainForm.InstallationProgress(Percent: Cardinal);
begin
  ProgressBar.Position := Percent;
end;

function TMainForm.View(Installation: TJclBorRADToolInstallation): TProductFrame;
begin
  if not Assigned(Installation) then
    Result := nil
  else
    with Installation do
      Result := FindComponent(TProductFrame.GetName(Installation)) as TProductFrame;
end;

procedure TMainForm.ReadSystemPaths;
var
  PathVar: string;
  I: Integer;
begin
  if GetEnvironmentVar('PATH', PathVar, False) then
  begin
    StrToStrings(PathVar, PathSep, FSystemPaths, False);
    for I := 0 to FSystemPaths.Count - 1 do
    begin
      PathVar := StrTrimQuotes(FSystemPaths[I]);
      ExpandEnvironmentVar(PathVar);
      {$IFDEF MSWINDOWS}
      PathVar := AnsiUpperCase(PathRemoveSeparator(PathGetLongName(PathVar)));
      {$ENDIF MSWINDOWS}
      FSystemPaths[I] := PathVar;
    end;
    FSystemPaths.Sorted := True;
  end;
end;

function TMainForm.SystemPathValid(const Path: string): Boolean;
begin
  Result := FSystemPaths.IndexOf({$IFDEF MSWINDOWS}AnsiUpperCase{$ENDIF}(Path)) <> -1;
end;

procedure TMainForm.UpdateInfo(Installation: TJclBorRADToolInstallation; const InfoText: String);
var
  P: TProductFrame;
begin
  P := View(Installation);
  if Assigned(P) then
  begin
    P.InfoDisplay.Text := InfoText;
  end;
end;

procedure TMainForm.UpdateStatus(const Text: string);
begin
  if Text = '' then
  begin
    StatusBevel.Visible := False;
    StatusLabel.Visible := False;
  end
  else
  begin
    StatusLabel.Caption := Text;
    StatusBevel.Visible := True;
    StatusLabel.Visible := True;
  end;
  Application.ProcessMessages;  //Update;
end;

procedure TMainForm.WriteInstallLog(Installation: TJclBorRADToolInstallation; const Text: string);
var
  P: TProductFrame;
begin
  P := View(Installation);
  if Assigned(P) then
    P.LogOutputLine(Text);
end;

function TMainForm.GetBPLPath(Installation: TJclBorRADToolInstallation): string;
var
  P: TProductFrame;
  Path: string;
begin
  P := View(Installation);
  if Assigned(P) then
    Path := P.BplPath;
  Result := PathRemoveSeparator(Installation.SubstitutePath(Path));
end;

function TMainForm.GetDCPPath(Installation: TJclBorRADToolInstallation): string;
var
  P: TProductFrame;
  Path: string;
begin
  P := View(Installation);
  if Assigned(P) then
    Path := P.DcpPath;
  Result := PathRemoveSeparator(Installation.SubstitutePath(Path));
end;

procedure TMainForm.BplPathEditChange(Sender: TObject);
begin
  with (Sender as TEdit) do
    if SystemPathValid(Text) then
      Font.Color := clWindowText
    else
      Font.Color := clRed;
end;

function TMainForm.FeatureChecked(FeatureID: Cardinal; Installation: TJclBorRADToolInstallation): Boolean;
var
  P: TProductFrame;
begin
  Result := False;
  P := View(Installation);
  if Assigned(P) then
    Result := P.FeatureChecked(FeatureID);
end;

procedure TMainForm.FormCreate(Sender: TObject);
begin
  Application.OnException := HandleException;
  FBorRADToolInstallations := TJclBorRADToolInstallations.Create;
  FSystemPaths := TStringList.Create;
  JediImage.Hint := DelphiJediURL;
  FJclInstall := CreateJclInstall;
  FJclInstall.SetOnProgress(InstallationProgress);
  FJclInstall.SetOnStarting(InstallationStarted);
  FJclInstall.SetOnEnding(InstallationFinished);
  FJclInstall.SetTool(Self);
  BorRADToolInstallations.Iterate(ExpandOptionTree);

  UpdateStatus('');

  ReadSystemPaths;
  //WindowState := wsMaximized; // wouldn't work in Form resource
  Application.HintPause := 50;
  Application.OnShowHint := ShowFeatureHint;
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
  FreeAndNil(FBorRADToolInstallations);
  FreeAndNil(FSystemPaths);
end;

function TMainForm.EventFilter(Sender: QObjectH; Event: QEventH): Boolean;
begin
  if QEvent_type(Event) = QEventType_UMCheckUpdates then
  begin
    BorRADToolInstallations.Iterate(CheckUpdatePack);
    Result := True;
  end
  else
    Result := inherited EventFilter(Sender, Event);
end;


procedure TMainForm.QuitBtnClick(Sender: TObject);
begin
  Close;
end;

function TMainForm.ExpandOptionTree(
  Installation: TJclBorRADToolInstallation): Boolean;
var
  P: TProductFrame;
begin
  Result := True;
  P := View(Installation);
  if Assigned(P) then
    P.UpdateTree;
end;

procedure TMainForm.InstallBtnClick(Sender: TObject);
begin
  if ({$IFDEF MSWINDOWS} IsDebuggerAttached or {$ENDIF} not CheckRunningInstances) and
    (Dialog(RsConfirmInstall, dtConfirmation, [drYes, drNo]) = drYes) then
  begin
    Install;
    QuitBtn.SetFocus;
  end;
end;

procedure TMainForm.UninstallBtnClick(Sender: TObject);
begin
  if ({$IFDEF MSWINDOWS} IsDebuggerAttached or {$ENDIF} not CheckRunningInstances) then
  begin
    Uninstall;
    QuitBtn.SetFocus;
  end;
end;

procedure TMainForm.FormShow(Sender: TObject);
begin
  QApplication_postEvent(Handle, QCustomEvent_create(QEventType_UMCheckUpdates, Self));
end;

procedure TMainForm.JediImageClick(Sender: TObject);
begin
  { TODO : implement for Unix }
  {$IFDEF MSWINDOWS}
  ShellExecEx(DelphiJediURL);
  {$ENDIF MSWINDOWS}
end;

procedure TMainForm.TreeViewCollapsing(Sender: TObject; Node: TTreeNode;
  var AllowCollapse: Boolean);
begin
  AllowCollapse := Collapsable(Node);
end;

function TMainForm.GetBorRADToolInstallations: TJclBorRADToolInstallations;
begin
  Result := FBorRADToolInstallations;
end;

procedure TMainForm.InstallationStarted(Installation: TJclBorRADToolInstallation);
var
  P: TProductFrame;
begin
  P := View(Installation);
  P.InfoDisplay.Lines.Clear;
  ProductsPageControl.ActivePage := P.Parent as TTabSheet;
  P.StartCompilation(Installation);
end;

procedure TMainForm.InstallationFinished(Installation: TJclBorRADToolInstallation);
var
  P: TProductFrame;
begin
  P := View(Installation);
  P.StopCompilation(Installation);
  P.InfoDisplay.Lines.SaveToFile(JclInstall.LogFileName(Installation));
end;

function TMainForm.Dialog(const Text: string; DialogType: TDialogType = dtInformation;
  Options: TDialogResponses = [drOK]): TDialogResponse;
const
  DlgType: array[TDialogType] of TMsgDlgType = (mtWarning, mtError, mtInformation, mtConfirmation);
  DlgButton: array[TDialogResponse] of TMsgDlgBtn = (mbYes, mbNo, mbOK, mbCancel);
  DlgResult: array[TDialogResponse] of Word = (mrYes, mrNo, mrOK, mrCancel);
var
  Buttons: TMsgDlgButtons;
  Res: Integer;
begin
  Buttons := [];
  for Result := Low(TDialogResponse) to High(TDialogResponse) do
    if Result in Options then
      Include(Buttons, DlgButton[Result]);
  Res := MessageDlg(Text, DlgType[DialogType], Buttons, 0);
  for Result := Low(TDialogResponse) to High(TDialogResponse) do
    if DlgResult[Result] = Res then
      Break;
end;

procedure TMainForm.SetBPLPath(Installation: TJclBorRADToolInstallation; const Value: string);
var
  P: TProductFrame;
begin
  P := View(Installation);
  if Assigned(P) then
    P.BplPath := Value;
end;

procedure TMainForm.SetDCPPath(Installation: TJclBorRADToolInstallation; const Value: string);
var
  P: TProductFrame;
begin
  P := View(Installation);
  if Assigned(P) then
    P.DcpPath := Value;
end;

procedure TMainForm.SetReadme(const FileName: string);
begin
  ReadmePane.LoadFromFile(FileName);
  {$IFDEF MSWINDOWS}
  ShellExecEx('..\docs\Readme.html');
  {$ENDIF MSWINDOWS}
end;

procedure TMainForm.TreeViewChange(Sender: TObject; Node: TTreeNode);
begin
  UpdateFeatureInfo(Node);
end;

procedure TMainForm.TreeViewEnter(Sender: TObject);
begin
  with ActiveView  do
      UpdateFeatureInfo(TreeView.Selected);
end;

procedure TMainForm.TreeViewExit(Sender: TObject);
begin
  //
end;

procedure TMainForm.TreeViewMouseMove(Sender: TObject;
  Shift: TShiftState; X, Y: Integer);
begin
  UpdateFeatureInfo(ActiveView.TreeView.GetNodeAt(X, Y));
end;

procedure TMainForm.UpdateFeatureInfo(Node: TTreeNode);
begin
  if Assigned(Node) and (Node <> FFeatureNode) then
  begin
    FFeatureNode := Node;
    FFeatureChanged := True;
  end;
end;

procedure TMainForm.ShowFeatureHint;
var
  View: TProductFrame;
begin
  View := ActiveView;
  if Assigned(View) and (HintInfo.HintControl = View.TreeView) then
  begin
    if FFeatureChanged then
    begin
      HintInfo.HintStr := FJclInstall.GetHint(TJediInstallOption(FeatureID(FFeatureNode) and $FF));
      FHintPos := HintInfo.HintPos;
      FFeatureChanged := False;
    end                      
    else
      HintInfo.HintPos := FHintPos;
    HintInfo.ReshowTimeout := 500;
  end;
end;

function TMainForm.OptionGUI(
  Installation: TJclBorRADToolInstallation): TObject;
begin
  Result := View(Installation);
  if Result = nil then
    CreateView(Installation);
  Result := View(Installation).TreeView.Items;
end;

end.
