{-----------------------------------------------------------------------------
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/MPL-1.1.html

Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
the specific language governing rights and limitations under the License.

The Original Code is: PageBuilder.pas, released on 2004-03-29.

The Initial Developer of the Original Code is Andreas Hausladen
(Andreas dott Hausladen att gmx dott de)
Portions created by Andreas Hausladen are Copyright (C) 2004 Andreas Hausladen.
All Rights Reserved.

Contributor(s): -

You may retrieve the latest version of this file at the Project JEDI's JVCL
home page, located at http://jvcl.delphi-jedi.org

Known Issues:
-----------------------------------------------------------------------------}
// $Id$

unit PageBuilder;

{$I jvcl.inc}

interface

uses
  Windows, SysUtils, Classes, Controls, Forms, Graphics, StdCtrls, ExtCtrls,
  ComCtrls, ActnList,
  JvWizard,
  Core;


function CreatePage(Page: TJvWizardInteriorPage; Inst: IInstallerPage): TWinControl;
  { returns the active control }
function PreparePage(Page: TJvWizardInteriorPage; Inst: IInstallerPage): TPanel;
procedure DestroyPage(Page: TJvWizardInteriorPage);

implementation

uses
  Math, Main, Utils;

function CreateWelcomePage(Page: TJvWizardInteriorPage; Inst: IWelcomePage): TWinControl; forward;
  { returns the active control }
function CreateMultiChoosePage(Page: TJvWizardInteriorPage; Inst: IMultiChoosePage): TWinControl; forward;
  { returns the active control }
function CreateSingleChoosePage(Page: TJvWizardInteriorPage; Inst: ISingleChoosePage): TWinControl; forward;
  { returns the active control }
function CreateSummaryPage(Page: TJvWizardInteriorPage; Inst: ISummaryPage): TWinControl; forward;
  { returns the active control }

type
  TSingleChooseOptionClick = class(TComponent)
  public
    Index: Integer;
    Page: ISingleChoosePage;
    procedure Click(Sender: TObject);
  end;

  TMultiChooseCheckBoxClick = class(TComponent)
  public
    Index: Integer;
    Page: IMultiChoosePage;
    procedure Click(Sender: TObject);
  end;

procedure TSingleChooseOptionClick.Click(Sender: TObject);
begin
  Page.SetSelectedOption(Index);
end;

procedure TMultiChooseCheckBoxClick.Click(Sender: TObject);
begin
  Page.SetCheckBox(Index, (Sender as TCheckBox).Checked);
end;


function GetDefaultCheckBoxSize: TSize;
begin
  with TBitmap.Create do
  try
    Handle := LoadBitmap(0, PChar(OBM_CHECKBOXES));
    Result.cx := Width div 4;
    Result.cy := Height div 3;
  finally
    Free;
  end;
end;

// -----------------------------------------------------------------------------

function CreateLayoutPanel(Parent: TWinControl; const Name: string;
  const BoundsRect: TRect): TPanel;
begin
  Result := TPanel.Create(Parent);
  Result.Name := Name;
  Result.BevelInner := bvNone;
  Result.BevelOuter := bvNone;
  Result.BoundsRect := BoundsRect;
  Result.Caption := '';
  Result.Parent := Parent;
end;

function PreparePage(Page: TJvWizardInteriorPage; Inst: IInstallerPage): TPanel;
var
  Title, SubTitle: WideString;
begin
 // caption
  Title := '';
  SubTitle := '';
  Inst.Title(Title, SubTitle);
  Page.Title.Text := Title;
  Page.Subtitle.Text := SubTitle;
  Page.Caption := Title;
  Page.Header.Title.Indent := 5;
  Page.Header.Subtitle.Indent := 5;

  Page.Header.Visible := (Title <> '') or (SubTitle <> '');
  Result := nil;
end;

function CreatePage(Page: TJvWizardInteriorPage; Inst: IInstallerPage): TWinControl;
var
  Y: Integer;
  PageClient: TPanel;
  FocusControl: TWinControl;
begin
  Result := nil;
  if (Page.FindComponent('piPageClient') <> nil) and not Supports(Inst, ISummaryPage) then // do not localize
    Exit;

  if Supports(Inst, ISummaryPage) then
    Page.FindComponent('piPageClient').Free;

  if Page.Header.Visible then
    Y := Page.Header.Height
  else
    Y := 0;
  PageClient := CreateLayoutPanel(Page, 'piPageClient', // do not localize
    Rect(0, Y, Page.ClientWidth, Page.ClientHeight));

  if Supports(Inst, IWelcomePage) then
    Result := CreateWelcomePage(Page, Inst as IWelcomePage)
  else
  if Supports(Inst, ISingleChoosePage) then
    Result := CreateSingleChoosePage(Page, Inst as ISingleChoosePage)
  else
  if Supports(Inst, IMultiChoosePage) then
    Result := CreateMultiChoosePage(Page, Inst as IMultiChoosePage)
  else
  if Supports(Inst, ISummaryPage) then
    Result := CreateSummaryPage(Page, Inst as ISummaryPage)
  ;

 // maybe the user wants some extra controls
  if Supports(Inst, IUserDefinedPage) then
  begin
    FocusControl := (Inst as IUserDefinedPage).SetupPage(PageClient);
    if FocusControl <> nil then
      Result := FocusControl;
  end;
end;

procedure DestroyPage(Page: TJvWizardInteriorPage);

  procedure RemoveActionLists(Owner: TComponent);
  var
    i: Integer;
  begin
    for i := Owner.ComponentCount - 1 downto 0 do
      RemoveActionLists(Owner.Components[i]);
    if (Owner is TCustomActionList) then
      Owner.Free;
  end;

var
  Panel: TPanel;
begin
  Panel := TPanel(Page.FindComponent('piPageClient')); // do not localize
  if Panel <> nil then
  begin
    RemoveActionLists(Panel); // Bug in VCL does not remove the action list from the frame's actionlist list.
    Panel.Free;
  end;
end;

function CreateSingleChooseControls(Parent: TWinControl; Inst: ISingleChoosePage;
  var LockedHorzOrientation: THorzOrientation): TWinControl;
var
  Options: TStrings;
  i, AbsH, X, Y, ps: Integer;
  RadioButton: TRadioButton;
  ItemIndex: Integer;
  S: string;
  HorzOrientation: THorzOrientation;
  Width, tmpWidth: Integer;
  Canvas: TControlCanvas;
begin
  Result := nil;
  Options := TStringList.Create;
  Canvas := TControlCanvas.Create;
  try
    Canvas.Control := Parent;

    // RadioButtons
    HorzOrientation := hoDefault;
    Inst.Options(Options, HorzOrientation);
    case LockedHorzOrientation of
      hoLeft:
        HorzOrientation := hoRight;
      hoRight:
        HorzOrientation := hoLeft;
      hoCenter:
        Exit; // error
    end;

    if HorzOrientation = hoDefault then
      HorzOrientation := hoLeft;
    LockedHorzOrientation := HorzOrientation;

    if Options.Count > 0 then
    begin
      ItemIndex := Inst.GetSelectedOption;

      // find text with largest width
      Width := 0;
      for i := 0 to Options.Count - 1 do
      begin
        S := Options[i];
        if S <> '' then
        begin
          ps := Pos('|', S);
          if ps = 0 then
            ps := Length(S) + 1;

          tmpWidth := Canvas.TextWidth(Copy(S, 1, ps - 1));
          if tmpWidth > Width then
             Width := tmpWidth;
        end;
      end;
      Inc(Width, GetDefaultCheckBoxSize.cx*Canvas.Font.PixelsPerInch*4 div 2 div 96);  // add checkbox size

      if HorzOrientation = hoLeft then
        X := 8
      else if HorzOrientation = hoCenter then
        X := (Parent.ClientWidth - Width) div 2
      else
        X := Parent.ClientWidth - 8 - Width;

      // create radio buttons
      AbsH := Max((Parent.ClientHeight - 8) div Options.Count, GetDefaultCheckBoxSize.cy + 2 * 8);
      Y := 8;
      for i := 0 to Options.Count - 1 do
      begin
        S := Options[i];
        if S <> '' then
        begin
          ps := Pos('|', S);
          if ps = 0 then
            ps := Length(S) + 1;
          RadioButton := TRadioButton.Create(Parent);
          RadioButton.Name := 'piOption_' + IntToStr(i); // do not localize
          RadioButton.Left := X;
          RadioButton.Top := Y;
          RadioButton.Caption := Copy(S, 1, ps - 1);
          RadioButton.Hint := Copy(S, ps + 1, MaxInt);
          RadioButton.ShowHint := RadioButton.Hint <> '';
          RadioButton.Parent := Parent;
          RadioButton.ClientWidth := Width;

          with TSingleChooseOptionClick.Create(Parent) do
          begin
            Page := Inst;
            Index := i;
            RadioButton.OnClick := Click;
          end;
          if i = ItemIndex then
          begin
            if Result = nil then
              Result := RadioButton;
            RadioButton.Checked := True;
          end;
          Inst.SetupRadioButton(i, RadioButton);
        end;
        Inc(Y, AbsH);
      end;
    end;
  finally
    Options.Free;
    Canvas.Free;
  end;
end;

function CreateMultiChooseControls(Parent: TWinControl; Inst: IMultiChoosePage;
  var LockedHorzOrientation: THorzOrientation): TWinControl;
var
  CheckBoxes: TStrings;
  i, AbsH, X, Y, ps: Integer;
  CheckBox: TCheckBox;
  S: string;
  HorzOrientation: THorzOrientation;
  Width, tmpWidth: Integer;
  Canvas: TControlCanvas;
begin
  Result := nil;
  CheckBoxes := TStringList.Create;
  Canvas := TControlCanvas.Create;
  try
    Canvas.Control := Parent;

    HorzOrientation := hoDefault;
    Inst.CheckBoxes(CheckBoxes, HorzOrientation);
    case LockedHorzOrientation of
      hoLeft:
        HorzOrientation := hoRight;
      hoRight:
        HorzOrientation := hoLeft;
      hoCenter:
        Exit; // error
    end;

    if HorzOrientation = hoDefault then
      HorzOrientation := hoLeft;
    LockedHorzOrientation := HorzOrientation;

    if CheckBoxes.Count > 0 then
    begin
      // find text with largest width
      Width := 0;
      for i := 0 to CheckBoxes.Count - 1 do
      begin
        S := CheckBoxes[i];
        if S <> '' then
        begin
          ps := Pos('|', S);
          if ps = 0 then
            ps := Length(S) + 1;

          tmpWidth := Canvas.TextWidth(Copy(S, 1, ps - 1));
          if tmpWidth > Width then
             Width := tmpWidth;
        end;
      end;
      Inc(Width, GetDefaultCheckBoxSize.cx*4 div 2);  // add checkbox size


      if HorzOrientation = hoLeft then
        X := 8
      else if HorzOrientation = hoCenter then
        X := (Parent.ClientWidth - Width) div 2
      else
        X := Parent.ClientWidth - 8 - Width;

      // create check boxes
      AbsH := Max((Parent.ClientHeight - 8) div CheckBoxes.Count, GetDefaultCheckBoxSize.cy + 2 * 8);
      Y := 8;
      for i := 0 to CheckBoxes.Count - 1 do
      begin
        S := CheckBoxes[i];
        if S <> '' then
        begin
          ps := Pos('|', S);
          if ps = 0 then
            ps := Length(S) + 1;
          CheckBox := TCheckBox.Create(Parent);
          CheckBox.Name := 'piCheckBox_' + IntToStr(i);
          CheckBox.Left := X;
          CheckBox.Top := Y;
          CheckBox.Caption := Copy(S, 1, ps - 1);
          CheckBox.Hint := Copy(S, ps + 1, MaxInt);
          CheckBox.ShowHint := CheckBox.Hint <> '';
          CheckBox.Parent := Parent;
          CheckBox.ClientWidth := Width;

          with TMultiChooseCheckBoxClick.Create(Parent) do
          begin
            Page := Inst;
            Index := i;
            CheckBox.OnClick := Click;
          end;
          if Result = nil then
            Result := CheckBox;
          CheckBox.Checked := Inst.GetCheckBox(i);
          Inst.SetupCheckBox(i, CheckBox);
        end;
        Inc(Y, AbsH);
      end;
    end;
  finally
    CheckBoxes.Free;
    Canvas.Free;
  end;
end;

function CreateWelcomePage(Page: TJvWizardInteriorPage; Inst: IWelcomePage): TWinControl;
var
  Text: WideString;

  Memo: TMemo;
  PageClient, ControlsPanel: TPanel;
  Y: Integer;
  HorzOrientation: THorzOrientation;
  MultiChoosePage: IMultiChoosePage;
begin
  PageClient := TPanel(Page.FindComponent('piPageClient')); // do not localize

  Text := Inst.Text;
  if Text <> '' then
  begin
    Memo := TMemo.Create(PageClient);
    Memo.Parent := PageClient;
    Memo.Name := 'piPageMemo'; // do not localize
    Memo.Left := 8;
    Memo.Top := 8;
    Memo.Width := PageClient.ClientWidth - Memo.Left * 2;
    Memo.Height := (PageClient.ClientHeight - Memo.Top * 2) * 2 div 3;
    Memo.ReadOnly := True;
    Memo.Font.Name := 'Arial';
    Memo.Lines.Text := Text;
    Memo.ScrollBars := ssVertical;
    Memo.Visible := True;
    Y := Memo.Top + Memo.Height;
  end
  else
    Y := 0;
  ControlsPanel := CreateLayoutPanel(PageClient, 'piControlsPanel', // do not localize
    Rect(0, Y, PageClient.ClientWidth, PageClient.ClientHeight));

  HorzOrientation := hoDefault;
  Result := CreateSingleChooseControls(ControlsPanel, Inst, HorzOrientation);
  if Supports(Inst, IMultiChoosePage, MultiChoosePage) then
    CreateMultiChooseControls(ControlsPanel, MultiChoosePage, HorzOrientation);
end;

function CreateSingleChoosePage(Page: TJvWizardInteriorPage; Inst: ISingleChoosePage): TWinControl;
var
  PageClient: TPanel;
  ScrollBox: TScrollBox;
  HortOrientation: THorzOrientation;
begin
  PageClient := TPanel(Page.FindComponent('piPageClient')); // do not localize
  ScrollBox := TScrollBox.Create(PageClient);
  ScrollBox.Align := alClient;
  ScrollBox.BorderStyle := bsNone;
  ScrollBox.Parent := PageClient;

  HortOrientation := hoDefault;
  Result := CreateSingleChooseControls(ScrollBox, Inst, HortOrientation);
end;

function CreateMultiChoosePage(Page: TJvWizardInteriorPage; Inst: IMultiChoosePage): TWinControl;
var
  PageClient: TPanel;
  ScrollBox: TScrollBox;
  HortOrientation: THorzOrientation;
begin
  PageClient := TPanel(Page.FindComponent('piPageClient')); // do not localize
  ScrollBox := TScrollBox.Create(PageClient);
  ScrollBox.Align := alClient;
  ScrollBox.BorderStyle := bsNone;
  ScrollBox.Parent := PageClient;

  HortOrientation := hoDefault;
  Result := CreateMultiChooseControls(ScrollBox, Inst, HortOrientation);
end;

function CreateSummaryPage(Page: TJvWizardInteriorPage; Inst: ISummaryPage): TWinControl;
var
  PageClient: TPanel;
  ListView: TListView;
  Actions, Comments: TStrings;
  I, MaxWidth, TextWidth, Count, ActionsWidth: Integer;
  ListItem: TListItem;
begin
  PageClient := TPanel(Page.FindComponent('piPageClient')); // do not localize

  ListView := TListView.Create(PageClient);
  ListView.Parent := PageClient;
  ListView.SetBounds(8, 8, PageClient.ClientWidth - 8 * 2, PageClient.ClientHeight - 8 * 2);
  ListView.ReadOnly := True;
  ListView.ViewStyle := vsReport;
  ListView.RowSelect := True;
  ListView.ShowColumnHeaders := False;

  ListView.SmallImages := FormMain.ImageList;

  MaxWidth := 150;
  ActionsWidth := 0;
  Actions := TStringList.Create;
  Comments := TStringList.Create;
  ListView.Items.BeginUpdate;
  try
    Inst.GetSummary(Actions, Comments);
    Count := Actions.Count;
    if Count < Comments.Count then
      Count := Comments.Count;

    for I := 0 to Count - 1 do
    begin
      ListItem := ListView.Items.Add;
      if I >= Actions.Count then
      begin
        ListItem.Caption := '';
      end
      else
      begin
        ListItem.Caption := Actions[I];
        TextWidth := ListView.Canvas.TextWidth(Actions[I]);
        if TextWidth > ActionsWidth then
          ActionsWidth := TextWidth;
      end;

      if ListItem.Caption = '' then
        ListItem.ImageIndex := -1
      else
        ListItem.ImageIndex := 0;


      if I >= Comments.Count then
        ListItem.SubItems.Add('')
      else
      begin
        ListItem.SubItems.Add(Comments[I]);
        TextWidth := ListView.Canvas.TextWidth(Comments[I]);
        if MaxWidth < TextWidth then
          MaxWidth := TextWidth;
      end;
    end;

    ListView.Columns.Add.Width := ActionsWidth+30; // +30 to cope for the bullet width
    if ListView.ClientWidth < MaxWidth + 50 then
      ListView.Columns.Add.Width := MaxWidth + 50
    else
      ListView.Columns.Add.AutoSize := True;
    ListView.Width := ListView.Width + 1; // force AutoSize
  finally
    ListView.Items.EndUpdate;
    Actions.Free;
    Comments.Free;
  end;

  Result := ListView;
end;


end.