(*
                              udlgSelectPrinter.pp
                                ------------
 *****************************************************************************
 *                                                                           *
 *  See the file COPYING.modifiedLGPL, included in this distribution,        *
 *  for details about the copyright.                                         *
 *                                                                           *
 *  This program 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.                     *
 *                                                                           *
 *****************************************************************************

 Author: Olivier Guilbaud (OG)

 Abstract:
   Printer select and configure dialog. This dialog box allows to choose
   a printer and to modify some options to print a file.
   
 history
   oct 24 2003 OG - Job hold and priority options.
                  - Add few function for convert Date time Local <-> GMT
                    for job-hold-until option with time specification
   nov 04 2003 OG - First release
   apr 19 2004 OG - Implemented More and Less button with Lazarus #212 bug
                    Fixed (thanks)
   sep 12 2004 OG - Fix bug num copies by replace IntToStr(Trunc(edCopies.Value)))
                    with edCopies.Text. Idem for priority of job
   sep 29 2004 OG - Modify for use new CUPSPrinters unit
   dec 20 2004 OG - TPrintRange and PrintRange property from Darek
   mar 08 2005 OG - Dynamique CUPS link
                  - Some bug compile fix
   mar 08 2005 OG - Modifications for Printer4Lazarus pakage
------------------------------------------------------------------------------*)
unit uDlgSelectPrinter;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls,
  Buttons, ExtCtrls, Spin, ComCtrls, LCLType, InterfaceBase,
  Printers, OsPrinters, CUPSDyn;

type

  { TdlgSelectPrinter }

  TdlgSelectPrinter = class(TForm)
    Bevel1: TBEVEL;
    btnProp: TBUTTON;
    btnCancel: TBUTTON;
    btnPrint: TBUTTON;
    btnReduc: TBUTTON;
    btnPreview: TBUTTON;
    cbPrinters: TCOMBOBOX;
    cbCollate: TCHECKBOX;
    cbReverse: TCHECKBOX;
    cbPrintToFile: TCheckBox;
    edPageSet: TCOMBOBOX;
    cbTasktime: TCOMBOBOX;
    edRange: TEDIT;
    Groupbox1: TGROUPBOX;
    gbPages: TGROUPBOX;
    gbCopies: TGROUPBOX;
    ImgPrn: TIMAGE;
    imgCollate: TIMAGE;
    Label1: TLABEL;
    Label2: TLABEL;
    Label3: TLABEL;
    Label4: TLABEL;
    labComment: TLABEL;
    labCUPS: TLABEL;
    Label5: TLABEL;
    labCUPSServer: TLABEL;
    labTask: TLABEL;
    lanNumCopies: TLABEL;
    labPage: TLABEL;
    labLocation: TLABEL;
    labState: TLABEL;
    edTimeTask: TEDIT;
    NbOpts: TNOTEBOOK;
    pgAdvance: TPAGE;
    pgCopies: TPAGE;
    Panel1: TPANEL;
    rbSelection: TRadioButton;
    rbRange: TRADIOBUTTON;
    rbCurrentPage: TRADIOBUTTON;
    rbAllPage: TRADIOBUTTON;
    edCopies: TSPINEDIT;
    edPriority: TSPINEDIT;
    tkbPriority: TTRACKBAR;
    procedure btnPrintCLICK(Sender: TObject);
    procedure btnPropCLICK(Sender: TObject);
    procedure btnReducCLICK(Sender: TObject);
    procedure cbPrintersCHANGE(Sender: TObject);
    procedure cbPrintersKEYPRESS(Sender: TObject; var Key: Char);
    procedure cbReverseCLICK(Sender: TObject);
    procedure cbTasktimeCHANGE(Sender: TObject);
    procedure dlgSelectPrinterCREATE(Sender: TObject);
    procedure dlgSelectPrinterSHOW(Sender: TObject);
    procedure tkbPriorityCHANGE(Sender: TObject);
  private
    { private declarations }
    fPropertiesSetting : Boolean;
    FOptions: TPrintDialogOptions;
    
    function GetPrintRange: TPrintRange;
    procedure RefreshInfos;
    procedure InitPrinterOptions;
    procedure SetPrintRange(const AValue: TPrintRange);
  public
    { public declaration}
    constructor Create(aOwner : TComponent); override;
    
    property PrintRange: TPrintRange read GetPrintRange write SetPrintRange;
    property Options: TPrintDialogOptions read FOptions write FOptions;
  end; 

var
  dlgSelectPrinter: TdlgSelectPrinter;

function GMTToLocalDateTime(aDate : TDateTime) : TDateTime;
function LocalToGMTDateTime(aDate : TDateTime) : TDateTime;
function LapseLocalToGMT : integer;

implementation

uses
  {$IF DEFINED(darwin) OR DEFINED(freebsd)}
  miniCupsLibc,
  {$ELSE}
    {$IFDEF freebsd}
    miniCupsLibc,
    {$ELSE}
     Libc,
    {$ENDIF}
  {$ENDIF}
  uDlgPropertiesPrinter;

Type
  THackCUPSPrinter = Class(TCUPSPrinter);
  
{----------- Utile date/time convertion ------------}
//Return the lapse time in second
//beetween localtime and gmt time with daylight
function LapseLocalToGMT : integer;
var TT : ttime_t;
    LOC: PTm;
begin
  TT :=__time(nil);
  LOC:=localtime(TT);
  if Assigned(LOC) then
    Result:=Loc^.tm_gmtoff
  else
    Result:=0;
end;

//Convert an local date & time to a GMT(UTC) Date & Time
function LocalToGMTDateTime(aDate : TDateTime) : TDateTime;
begin
  Result:=aDate-((TDateTime(LapseLocalToGMT)/3600)/24);
end;

//Convert an GMT(UTC) Date & Time to local date & time
function GMTToLocalDateTime(aDate : TDateTime) : TDateTime;
begin
  Result:=aDate+((TDateTime(LapseLocalToGMT)/3600)/24);
end;

{---------------------------------------------------}

{ TdlgSelectPrinter }

constructor TdlgSelectPrinter.Create(aOwner : TComponent);
begin
  Inherited Create(aOwner);

  //Set Height of form
  btnReduc.Tag:=1;
  btnReducCLICK(nil);
  if WidgetSet.LCLPlatform = lpCarbon then
    begin  //Can't hide tabs with button on Carbon, so just expand dialog.
    btnReduc.Tag:=0;
    btnReducCLICK(nil);
    btnReduc.Visible:=False;
    end;
end;


procedure TdlgSelectPrinter.tkbPriorityCHANGE(Sender: TObject);
begin
  if Sender=nil then ;
  edPriority.Value:=tkbPriority.Position;
end;

//Initialization
procedure TdlgSelectPrinter.RefreshInfos;
var St  : string;
    Stp : string;
    n   : Integer;

  //Convert an GMT hour to Local hour
  function GetTimeHold : string;
  Var Dt       : TDateTime;
  begin
    try
      Dt:=Date+StrToTime(St);
      DT:=GMTToLocalDatetime(Dt);
      Result:=FormatDateTime('HH:NN:SS',Dt);
    Except
      Result:='00:00:00';
    end;
  end;

begin
  try

    cbPrintToFile.Visible := (poPrintToFile in FOptions);
    cbPrintToFile.Enabled := not (poDisablePrintToFile in FOptions);
    rbSelection.Enabled := (poSelection in FOptions);
    rbCurrentPage.Enabled := (poPageNums in FOptions);

    //State
    St:='';
    StP:='printer';
    if Printer.PrinterType=ptNetWork then
      StP:=StP+'_remote';

    BtnPrint.Enabled:=True;
    Case Printer.PrinterState of
      psReady     : St:='Ready';
      psPrinting  : St:='Printing';
      psStopped   : begin
                      St:='Stopped';
                      StP:=StP+'_stopped';
                      BtnPrint.Enabled:=False;
                    end;
    end;

    if Printer.CanPrint then
      St:=St+' (Accepting jobs)'
    else
    begin
      St:=St+' (Rejetting jobs)';
      BtnPrint.Enabled:=False;
    end;

    labState.Caption:=St;

    //DRw image printer
    imgPrn.Picture.PixMap.TransparentColor:=clNone;
    imgPrn.Picture.PixMap.LoadFromLazarusResource(Stp);
    imgPrn.Picture.PixMap.Transparent:=True;

    //cups server
    labCUPSServer.Caption:=cupsServer()+':'+IntToStr(ippPort());
    //
    labLocation.Caption:=THackCUPSPrinter(Printer).GetAttributeString('printer-location','');
    labComment.Caption :=THackCUPSPrinter(Printer).GetAttributeString('printer-info','');

    //Copies
    edCopies.Value:=Printer.Copies;
    cbCollate.Checked:=True; //For update image

    //Range setting
    edRange.Enabled:=
      (poPageNums in FOptions) and
      THackCUPSPrinter(Printer).GetAttributeBoolean('page-ranges-supported',False);
    rbRange.Enabled:=edRange.Enabled;

    //Job priority
    n:=THackCUPSPrinter(Printer).GetAttributeInteger('job-priority-supported',0);
    edPriority.MaxValue:=n;
    tkbPriority.Max:=n;
    n:=THackCUPSPrinter(Printer).GetAttributeInteger('job-priority-default',0);
    edPriority.Value:=n;
    edPriority.Tag  :=n; //Save default priority
    tkbPriority.Position:=n;
  
    //Job-Hold
    edTimeTask.Enabled:=False;
    edTimeTask.Text:=FormatDateTime('hh:nn:ss',Now);
    St:=THackCUPSPrinter(Printer).cupsGetOption('job-hold-until');
    n:=0;
    if St='indefinite' then n:=1;
    if St='day-time'   then n:=2;
    if st='evening'    then n:=3;
    if st='night'      then n:=4;
    if st='weekend'    then n:=5;
    if St='second-shift' then n:=6;
    if St='third-shift'  then n:=7;
    if Pos(':',St)<>0 then
    begin
      n:=8;
      edTimeTask.Enabled:=True;
      edTimeTask.Text:=St;
    end;

    cbTasktime.ItemIndex:=n;
  Except
  end;
end;

function TdlgSelectPrinter.GetPrintRange: TPrintRange;
begin
  Result:=prAllPages;

  if rbCurrentPage.checked then
     Result:=prCurrentPage
  else
    if rbRange.checked then
       Result:=prPageNums;
end;

//Initialization of selected Printer options
procedure TdlgSelectPrinter.InitPrinterOptions;
Var St    : string;

  //Convert an Local hour to GMT hour
  function GetTimeHold : string;
  Var Dt       : TDateTime;
  begin
    try
      Dt:=Date+StrToTime(edTimeTask.Text);
      DT:=LocalToGMTDateTime(Dt);
      Result:=FormatDateTime('HH:NN:SS',Dt);
    Except
      Result:='indefinite';
    end;
  end;

begin
  if not fPropertiesSetting then
  begin
    //Free current options if exists
    THackCUPSPrinter(Printer).FreeOptions;
    //Initialize default Options
    THackCUPSPrinter(Printer).SetOptionsOfPrinter;
  end;
  
  //Copies
  THackCUPSPrinter(Printer).cupsAddOption('copies',edCopies.Text);
  if rbRange.Checked then
    THackCUPSPrinter(Printer).cupsAddOption('page-ranges',edRange.Text);
  if edPageSet.ItemIndex>0 then
  begin
    if edPageSet.ItemIndex=1 then
      St:='Odd'
    else
      St:='Even';
    THackCUPSPrinter(Printer).cupsAddOption('page-set',St);
  end;
  if cbCollate.Checked then
    st:='separate-documents-collated-copies'
  else
    St:='separate-documents-uncollated-copies';
  THackCUPSPrinter(Printer).cupsAddOption('multiple-document-handling',St);
  if cbReverse.Checked then
    THackCUPSPrinter(Printer).cupsAddOption('OutputOrder','Reverse');

  //Priority job
  if edPriority.Tag<>edPriority.Value then
    THackCUPSPrinter(Printer).cupsAddOption('job-priority',edPriority.Text);

  //Job-Hold
  Case cbTasktime.ItemIndex of
    1 : St:='indefinite';
    2 : St:='day-time';
    3 : st:='evening';
    4 : st:='night';
    5 : st:='weekend';
    6 : St:='second-shift';
    7 : St:='third-shift';
    8 : St:=GetTimeHold;
    else St:='no-hold';
  end;
  THackCUPSPrinter(Printer).cupsAddOption('job-hold-until',St);
end;

procedure TdlgSelectPrinter.SetPrintRange(const AValue: TPrintRange);
begin
  case aValue of
    prAllPages    : rbAllPage.checked:=True;
    prCurrentPage : rbCurrentPage.checked:=True;
    prPageNums    : rbRange.checked:=True;
  end;
end;


//Initialization of screen
procedure TdlgSelectPrinter.dlgSelectPrinterSHOW(Sender: TObject);
begin
  if Sender=nil then ;
  NbOpts.PageIndex:=0;
  
  Printer.Refresh;
  cbPrinters.Items.Assign(Printer.Printers);
  
  if cbPrinters.Items.Count>0 then
    cbPrinters.ItemIndex:= Printer.PrinterIndex;
    
  RefreshInfos;
end;

procedure TdlgSelectPrinter.cbTasktimeCHANGE(Sender: TObject);
begin
  if Sender=nil then ;
  //Time is active if last item is selected
  edTimeTask.Enabled:=(cbTaskTime.ItemIndex=cbTaskTime.Items.Count-1);
  edTimeTask.Text:=FormatDateTime('hh:nn:ss',Now);
end;

procedure TdlgSelectPrinter.dlgSelectPrinterCREATE(Sender: TObject);
begin
  if Sender=nil then ;
  fPropertiesSetting:=False;
end;

//Show corresponding image
procedure TdlgSelectPrinter.cbReverseCLICK(Sender: TObject);
Var St : string;
begin
  if Sender=nil then ;
  St:='collate';
  If not cbCollate.Checked then
   St:='un'+St;
  if cbReverse.Checked then
    St:=St+'_rev';

  imgCollate.Picture.PixMap.TransparentColor:=clNone;
  imgCollate.Picture.PixMap.LoadFromLazarusResource(St);
  imgCollate.Picture.BitMap.Transparent:=True;
end;

procedure TdlgSelectPrinter.cbPrintersKEYPRESS(Sender: TObject; var Key: Char);
begin
  if Sender=nil then ;
  Key:=#0;
end;

//If tag of btnReduc is 0 then the caption is "More ..." and
//if it's 1 then "Less ..."
procedure TdlgSelectPrinter.btnReducCLICK(Sender: TObject);
begin
  if Sender=nil then ;
  if btnReduc.Tag=1 then
  begin
    btnReduc.Tag:=0;
    btnReduc.Caption:='More ...';
    Height:=Height-nbOpts.Height;
  end
  else
  begin
    btnReduc.Tag:=1;
    btnReduc.Caption:='Less ...';
    Height:=Height+nbOpts.Height;
  end;
end;

procedure TdlgSelectPrinter.btnPrintCLICK(Sender: TObject);
begin
  if Sender=nil then ;
  InitPrinterOptions;
end;

//Show the printer properties dialog
procedure TdlgSelectPrinter.btnPropCLICK(Sender: TObject);
var Dlg : Tdlgpropertiesprinter;
begin
  if Sender=nil then ;
  //Set default printer
  THackCUPSPrinter(Printer).SelectCurrentPrinterOrDefault;

  Dlg:=Tdlgpropertiesprinter.Create(self);
  try
    if Dlg.ShowModal=mrOk then
    begin
      Dlg.InitProperties;
      fPropertiesSetting:=True;
    end;
  finally
    Dlg.free;
  end;
end;

procedure TdlgSelectPrinter.cbPrintersCHANGE(Sender: TObject);
begin
  if Sender=nil then ;
  Printer.SetPrinter(cbPrinters.Text);
  fPropertiesSetting:=False;
  RefreshInfos;
end;


initialization
  {$I udlgselectprinter.lrs}
  {$I selectprinter.lrs}
end.
