unit framePageSetup;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Graphics, FileUtil, LResources, Forms, ExtCtrls, StdCtrls,
  Printers, OsPrinters, LCLIntf, LCLProc, Controls, CupsLCL;

type
  TPageSetupMode = (psmFull, psmPapers, psmMargins);
  TPageSetupOption = (
    psoMargins,         // margins and preview are visible
    psoPapers,          // papers group visible
    psoOrientation      // orientation group visible
  );
  TPageSetupOptions = set of TPageSetupOption;

  { TframePageSetup }

  TframePageSetup = class(TFrame)
    cbPaper: TComboBox;
    cbSource: TComboBox;
    panMargins: TPanel;
    txtLeft: TEdit;
    txtRight: TEdit;
    txtTop: TEdit;
    txtBottom: TEdit;
    gpPaper: TGroupBox;
    gpOrientation: TGroupBox;
    gpMargins: TGroupBox;
    lblSource: TLabel;
    lblPaper: TLabel;
    lblLeft: TLabel;
    lblRight: TLabel;
    lblTop: TLabel;
    lblBottom: TLabel;
    pbPreview: TPaintBox;
    panSetup: TPanel;
    panPreview: TPanel;
    radLandscape: TRadioButton;
    radPortrait: TRadioButton;
    procedure cbPaperChange(Sender: TObject);
    procedure panPreviewResize(Sender: TObject);
    procedure pbPreviewMouseDown(Sender: TObject; Button: TMouseButton;
      {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
    procedure pbPreviewMouseWheelDown(Sender: TObject; {%H-}Shift: TShiftState;
      {%H-}MousePos: TPoint; var Handled: Boolean);
    procedure pbPreviewMouseWheelUp(Sender: TObject; {%H-}Shift: TShiftState;
      {%H-}MousePos: TPoint; var Handled: Boolean);
    procedure pbPreviewPaint(Sender: TObject);
    procedure radPortraitClick(Sender: TObject);
  private
    { private declarations }
    FHeightTallest: Integer;
    FHardMargins: TRect;
    FKw,FKh,FZoom: Double;
    FOptions: TPageSetupOptions;
  public
    { public declarations }
    procedure Initialize(AMode: TPageSetupMode);
    procedure UpdatePageSize;
  end;

implementation

{$R framepagesetup.lfm}

{ TframePageSetup }

procedure TframePageSetup.pbPreviewPaint(Sender: TObject);
var
  R: TRect;
  procedure DrawMargin(AIndex: Integer; ASize: Integer);
  begin
    with pbPreview do
    case AIndex of
      0: // Left
        begin
          Canvas.MoveTo(ASize, 1);
          Canvas.LineTo(ASize, Height-1);
        end;
      1: //Top
        begin
          Canvas.MoveTo(1,ASize);
          Canvas.LineTo(Width-1, ASize);
        end;
      2: // Right
        begin
          Canvas.MoveTo(Width-1-ASize, 1);
          Canvas.LineTo(Width-1-ASize,Height-1);
        end;
      3: // Bottom
        begin
          Canvas.MoveTo(1,Height-1-Asize);
          Canvas.LineTo(Width-1, Height-1-ASize);
        end;
    end;
  end;
begin

  if Sender=nil then ;

  if not (psoMargins in FOptions) then
    exit;

  with pbPreview do
  begin

    // page frame
    R := Rect(0,0,Width,Height);
    Canvas.Pen.Color := clBlack;
    Canvas.Brush.Color:=clWhite;
    Canvas.Rectangle(R);

    // hard margins
    Canvas.Pen.Color := RGBToColor(255,204,204);
    DrawMargin(0, FHardMargins.Left  );
    DrawMargin(1, FHardMargins.Top   );
    DrawMargin(2, FHardMargins.Right );
    DrawMargin(3, FHardMargins.Bottom);
  end;
end;

procedure TframePageSetup.radPortraitClick(Sender: TObject);
begin
  if sender=nil then ;

  if radPortrait.Checked then
    Printer.Orientation := poPortrait
  else
    Printer.Orientation := poLandsCape;
  UpdatePageSize;
end;

procedure TframePageSetup.cbPaperChange(Sender: TObject);
begin
  if Printer.PaperSize.DefaultPapers then
  begin
    if cbPaper.ItemIndex>=0 then
      Printer.PaperSize.PaperName := cbPaper.Items[cbPaper.ItemIndex];
  end else
    Printer.PaperSize.PaperName := GetCupsComboKeyValue(cbPaper);
  UpdatePageSize;
end;

procedure TframePageSetup.panPreviewResize(Sender: TObject);
var
  TallH: Integer;
begin
  if not (psoMargins in FOptions) then
    exit;

  TallH := Round(FheightTallest * FKh);

  with PanPreview do
  if (Height<>C_BOTHSPACES) and (TallH>(Height-C_BOTHSPACES)) then
    FZoom := (Height-C_BOTHSPACES)/TallH
  else
    FZoom := 1.0;
end;

procedure TframePageSetup.pbPreviewMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if Button=mbMiddle then
  begin
    FZoom := 1;
    UpdatePageSize;
  end;
end;

procedure TframePageSetup.pbPreviewMouseWheelDown(Sender: TObject;
  Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
begin
  FZoom := FZoom - 0.2;
  if FZoom<0.5 then
    FZoom := 0.5;
  UpdatePageSize;
  Handled := true;
end;

procedure TframePageSetup.pbPreviewMouseWheelUp(Sender: TObject;
  Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
begin
  FZoom := FZoom + 0.2;
  UpdatePageSize;
  Handled := true;
end;

procedure TframePageSetup.UpdatePageSize;
begin
  if not (psoMargins in FOptions) then
    exit;

  with Printer.PaperSize.PaperRect.PhysicalRect do
  begin
    PbPreview.Width := Round(Fkw * (Right - Left) * FZoom) + 2;
    PbPreview.Height := Round(FKh * (Bottom - Top) * FZoom) + 2;
  end;

  with Printer.PaperSize.PaperRect do
  begin
    FHardMargins.Left := Round(Fkw * (WorkRect.Left-PhysicalRect.Left) * FZoom);
    FHardMargins.Right := Round(Fkw * (Physicalrect.Right-WorkRect.Right) * FZoom);
    FHardMargins.Top := Round(FkH * (WorkRect.Top-PhysicalRect.Top) * FZoom);
    FHardMargins.Bottom := Round(FkH * (PhysicalRect.Bottom-WorkRect.Bottom) * FZoom);
  end;

  {$IFDEF DebugCUPS}
  with FHardMargins do
  begin
    DebugLn(' Kh=%.2f Kw=%.2f',[FKh,FKw]);
    DebugLn(' BoxLimits L=0 T=0 R=%d B=%d',[PbPreview.Width-1,PbPreview.Height-1]);
    DebugLn('OrgMargins L=%d T=%d R=%d B=%d',[Left,Top,Right,Bottom]);
  end;
  {$ENDIF}
end;

procedure TframePageSetup.Initialize(AMode: TPageSetupMode);
var
  i,j:Integer;
  R: TPaperRect;
begin
  case AMode of
    psmMargins:
      FOptions := [psoMargins];
    psmPapers:
      FOptions := [psoPapers,psoOrientation];
    else
      FOptions := [psoMargins,psoPapers,psoOrientation];
  end;

  if [psoMargins,psoPapers]*FOptions<>[] then
  begin
    SetupCupsCombo(cbPaper, nil, 'PageSize');
    if (cbPaper.Items.Count=0) then
    begin
      // no cups printer papers, use default ones
      cbPaper.Items := Printer.PaperSize.SupportedPapers;
      cbPaper.ItemIndex:= cbPaper.Items.IndexOf(Printer.PaperSize.PaperName);
      cbPaper.Enabled:=true;
    end;
  end;

  if psoPapers in FOptions then
    SetupCupsCOmbo(cbSource, nil, 'InputSlot')
  else
    gpPaper.Visible := false;

  //TODO: support reverse variants too?
  gpOrientation.Visible := (psoOrientation in FOptions);
  case Printer.Orientation of
    poPortrait,poReversePortrait:
      radPortrait.Checked := true;
    poLandscape,poReverseLandscape:
      radLandscape.Checked := true;
  end;

  if psoMargins in FOptions then
  begin
    // assume 100 pix = 8.5 inch (IOW, letter size width = 100 pixels)
    with ScreenInfo do
    begin
      FKw := (100/8.5)/Printer.XDPI;
      FKh := (100/8.5)*(PixelsPerInchY/PixelsPerInchX)/Printer.YDPI;
    end;

    // find the tallest paper
    FHeightTallest := 0;
    j := -1;
    if cbPaper.Enabled then
    for i:=0 to cbPaper.Items.Count-1 do
    begin
      if Printer.PaperSize.DefaultPapers then
        R := Printer.PaperSize.PaperRectOf[cbPaper.Items[i]]
      else
        R := Printer.PaperSize.PaperRectOf[GetCupsComboKeyValue(cbPaper, i)];
      with R.PhysicalRect do
      if FHeightTallest<(Bottom-Top) then
      begin
        FHeightTallest := (Bottom-Top);
        j := i;
      end;
    end;

    if j>=0 then
    begin
      {$IFDEF DebugCUPS}
      DebugLn(' Tallest Paper is: %s Height=%d %.2f Inch',
       [cbPaper.Items[j],FHeightTallest,FHeightTallest/Printer.YDPI]);
      {$ENDIF}
    end;

    // zoom factor
    FZoom := 1.0;
    UpdatePageSize;

  end else
  begin
    panPreview.Visible:=false;
    gpMargins.Visible:=false;
  end;

  if AMode=psmPapers then
  begin
    gpOrientation.Anchors:=[akTop,akRight,akBottom];
    gpOrientation.Align:=alRight;
    gpPaper.Anchors:=[akTop,akLeft];
    gpPaper.Align:=alClient;
    PanSetup.Align:=alClient;
  end else
  if AMode=psmMargins then
    PanSetup.Height:=gpMargins.Height+C_BOTHSPACES;

end;

end.

