{*******************************************************}
{                                                       }
{       RichViewActions                                 }
{       Dialog for paragraph attributes                 }
{                                                       }
{       Copyright (c) Sergey Tkachenko                  }
{       svt@trichview.com                               }
{       http://www.trichview.com                        }
{                                                       }
{*******************************************************}


unit ParaRVFrm;

interface

{$I RV_Defs.inc}
{$I RichViewActions.inc}

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, RVOfficeRadioBtn, ImgList, RVScroll, RVClasses,
  {$IFDEF USERVKSDEVTE}
  te_controls, te_extctrls,
  {$ENDIF}
  {$IFDEF USERVTNT}
  TntComCtrls,
  {$ENDIF}
  RichView, RVStyle, BaseRVFrm, RVSpinEdit, ExtCtrls, RVALocalize, ComCtrls,
  RVTypes;

type
  TfrmRVPara = class(TfrmRVBase)
    btnOk: TButton;
    btnCancel: TButton;
    pc: TPageControl;
    ts1: TTabSheet;
    ts2: TTabSheet;
    ts3: TTabSheet;
    gbAlignment: TRVOfficeRadioGroup;
    gbIndents: TGroupBox;
    lblLeft: TLabel;
    lblRight: TLabel;
    lblFirstLine: TLabel;
    Bevel1: TBevel;
    seLeftIndent: TRVSpinEdit;
    seRightIndent: TRVSpinEdit;
    rbNegative: TRVOfficeRadioButton;
    rbPositive: TRVOfficeRadioButton;
    seFirstLineIndent: TRVSpinEdit;
    gbSpacing: TGroupBox;
    lblBefore: TLabel;
    lblAfter: TLabel;
    lblLineSpacing: TLabel;
    Bevel3: TBevel;
    lblBy: TLabel;
    seSpaceBefore: TRVSpinEdit;
    seSpaceAfter: TRVSpinEdit;
    seLineSpacingValue: TRVSpinEdit;
    cmbLineSpacing: TComboBox;
    gbSample: TGroupBox;
    rv: TRichView;
    ImageList1: TImageList;
    rvs: TRVStyle;
    gbPagination: TGroupBox;
    cbKeepLinesTogether: TCheckBox;
    cbKeepWithNext: TCheckBox;
    ilSmallTabs: TImageList;
    ilTabs: TImageList;
    lstTabs: TListBox;
    btnDelete: TButton;
    btnDeleteAll: TButton;
    rgTabAlign: TRVOfficeRadioGroup;
    rgLeader: TRadioGroup;
    seTabPos: TRVSpinEdit;
    lblTabPos: TLabel;
    btnSet: TButton;
    lblDelTabs: TLabel;
    lblDelTabList: TLabel;
    ilListTabs: TImageList;
    procedure FormCreate(Sender: TObject);
    procedure seFirstLineIndentChange(Sender: TObject);
    procedure cmbLineSpacingClick(Sender: TObject);
    procedure seLineSpacingValueChange(Sender: TObject);
    procedure seChange(Sender: TObject);
    procedure gbAlignmentClick(Sender: TObject);
    procedure gbAlignmentDblClickItem(Sender: TObject);
    procedure rbFLClick(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure seLeftIndentChange(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure lstTabsDrawItem(Control: TWinControl; Index: Integer;
      Rect: TRect; State: TOwnerDrawState);
    procedure lstTabsClick(Sender: TObject);
    procedure seTabPosChange(Sender: TObject);
    procedure btnDeleteAllClick(Sender: TObject);
    procedure btnDeleteClick(Sender: TObject);
    procedure btnSetClick(Sender: TObject);
  private
    { Private declarations }
    _lstTabs, _rgLeader, _lblDelTabList: TControl;
    _btnSet, _btnDelete, _btnDeleteAll: TControl;
    _ts2: TControl;    
    IgnoreChanges: Boolean;
    FDeleteAllTabs: Boolean;
    FTabsToDelete: TRVIntegerList;
    FTabs: TRVTabInfos;
    DoNotResetLeader: Boolean;
    procedure UpdateSample;
    procedure SetTabs(Value: TRVTabInfos);
    procedure UpdateTabList(NewIndex: Integer);
    procedure DrawTabItem(Canvas: TCanvas; Index: Integer; R: TRect; Selected: Boolean);
    procedure UpdateDelTabList;
  protected
    {$IFDEF RVASKINNED}
    procedure OnCreateThemedControl(OldControl, NewControl: TControl); override;
    {$ENDIF}
  public
    { Public declarations }
    _cmbLineSpacing, _cbKeepWithNext, _cbKeepLinesTogether: TControl;
    TabsModified: Boolean;
    procedure Localize; override;
    procedure HideTabsPage;
    property DeleteAllTabs: Boolean read FDeleteAllTabs write FDeleteAllTabs;
    property TabsToDelete: TRVIntegerList read FTabsToDelete;
    property Tabs: TRVTabInfos read FTabs write SetTabs;
  end;

var
  frmRVPara: TfrmRVPara;

implementation
uses RichViewActions;

{$R *.dfm}

procedure TfrmRVPara.FormCreate(Sender: TObject);
var h: Integer;
begin
  Canvas.Font := lstTabs.Font;
  h := Canvas.TextHeight('Wg')+4;
  if h<20 then
    h := 20;
  lstTabs.ItemHeight := h;
  _cmbLineSpacing := cmbLineSpacing;
  _cbKeepWithNext := cbKeepWithNext;
  _cbKeepLinesTogether := cbKeepLinesTogether;
  _lstTabs := lstTabs;
  _rgLeader := rgLeader;
  _btnSet := btnSet;
  _btnDelete := btnDelete;
  _btnDeleteAll := btnDeleteAll;
  _lblDelTabList := lblDelTabList;
  _ts2 := ts2;
  rgLeader.Items[4] := #$B7#$B7#$B7#$B7#$B7#$B7#$B7#$B7#$B7#$B7;
  inherited;
  {$IFDEF RVUNICODESTR}rv.AddNLWTag{$ELSE}rv.AddNLATag{$ENDIF}
    (RVA_GetS(rvam_par_Preview), 0, 0, 0);
  {$IFDEF RVUNICODESTR}rv.AddNLWTag{$ELSE}rv.AddNLATag{$ENDIF}
    (RVA_GetS(rvam_par_Preview), 1, 1, 0);
    {$IFDEF RVUNICODESTR}rv.AddNLWTag{$ELSE}rv.AddNLATag{$ENDIF}
      (RVA_GetS(rvam_par_Preview), 0, 0, 0);
  rv.Format;
  FTabsToDelete := TRVIntegerList.Create;
  FTabs := TRVTabInfos.Create(nil);
  if RVA_UserInterface=rvauiHTML then
    HideTabsPage;
end;

procedure TfrmRVPara.FormDestroy(Sender: TObject);
begin
  FTabsToDelete.Free;
  FTabs.Free;
  inherited;
end;

procedure TfrmRVPara.seFirstLineIndentChange(Sender: TObject);
begin
  if not Visible or IgnoreChanges then
    exit;
  if seFirstLineIndent.Indeterminate or (seFirstLineIndent.AsInteger=0) then begin
    rbNegative.Checked := False;
    rbPositive.Checked := False;
    end
  else if not seFirstLineIndent.Indeterminate and not rbNegative.Checked and not rbPositive.Checked then
    rbPositive.Checked := True
  else if rbNegative.Checked and
    (seLeftIndent.AsInteger-seFirstLineIndent.AsInteger-Round(seLeftIndent.MinValue)<0) then begin
    IgnoreChanges := True;
    seLeftIndent.Value := seFirstLineIndent.AsInteger+Round(seLeftIndent.MinValue);
    IgnoreChanges := False;
  end;
  UpdateSample;
end;

procedure TfrmRVPara.seLeftIndentChange(Sender: TObject);
begin
  if not Visible or IgnoreChanges then
    exit;
  if rbNegative.Checked and (seFirstLineIndent.Value>0) and
    (seLeftIndent.AsInteger-seFirstLineIndent.AsInteger-Round(seLeftIndent.MinValue)<0) then begin
    IgnoreChanges := True;
    seFirstLineIndent.Value := seLeftIndent.AsInteger-Round(seLeftIndent.MinValue);
    IgnoreChanges := False;
  end;
  UpdateSample;
end;

procedure TfrmRVPara.rbFLClick(Sender: TObject);
begin
  if not Visible then
    exit;
  if rbNegative.Checked and
    (seLeftIndent.AsInteger-seFirstLineIndent.AsInteger-Round(seLeftIndent.MinValue)<0) then begin
    IgnoreChanges := True;
    seLeftIndent.Value := seFirstLineIndent.AsInteger+Round(seLeftIndent.MinValue);
    IgnoreChanges := False;
  end;
  if seFirstLineIndent.Indeterminate then
    seFirstLineIndent.Value := 0;
  UpdateSample;
end;


procedure TfrmRVPara.cmbLineSpacingClick(Sender: TObject);
var ItemIndex: Integer;
begin
  if not Visible then
    exit;
  ItemIndex := GetXBoxItemIndex(_cmbLineSpacing);
  case ItemIndex of
    0,1,2:
      begin
        seLineSpacingValue.Indeterminate := True;
        seLineSpacingValue.IntegerValue := False;
        seLineSpacingValue.Increment := 0.5;
        seLineSpacingValue.MinValue := 0.5;
        seLineSpacingValue.MaxValue := 100;
      end;
      3,4:
      begin
        seLineSpacingValue.Indeterminate := True;      
        seLineSpacingValue.IntegerValue := True;
        seLineSpacingValue.Value := 16;
        seLineSpacingValue.Increment := 1;
        seLineSpacingValue.MinValue := 5;
        seLineSpacingValue.MaxValue := 1000;
      end;
      5:
      begin
        seLineSpacingValue.IntegerValue := False;
        seLineSpacingValue.Increment := 0.5;
        seLineSpacingValue.MinValue := 0.5;
        seLineSpacingValue.MaxValue := 100;
        if seLineSpacingValue.Indeterminate then
          seLineSpacingValue.Value := 3;
      end;
  end;
  UpdateSample;
end;

procedure TfrmRVPara.seLineSpacingValueChange(Sender: TObject);
begin
  if not Visible then
    exit;
  if not seLineSpacingValue.Indeterminate and
    (GetXBoxItemIndex(_cmbLineSpacing)<3) then
    SetXBoxItemIndex(_cmbLineSpacing, 5);
  UpdateSample;
end;

procedure TfrmRVPara.gbAlignmentClick(Sender: TObject);
begin
  UpdateSample;
end;

procedure TfrmRVPara.UpdateSample;
begin
  if not Visible then
    exit;
  with rvs.ParaStyles[1] do begin
    if gbAlignment.ItemIndex<0 then
      Alignment := rvaLeft
    else
      Alignment :=  TRVAlignment(gbAlignment.ItemIndex);
    SpaceBefore := seSpaceBefore.AsInteger div 2;
    SpaceAfter := seSpaceAfter.AsInteger  div 2;
    Background.BorderOffsets.Top := SpaceBefore;
    Background.BorderOffsets.Bottom := SpaceAfter;
    LeftIndent := seLeftIndent.AsInteger  div 2;
    RightIndent := seRightIndent.AsInteger  div 2;
    if rbNegative.Checked then
      FirstIndent := -seFirstLineIndent.AsInteger div 2
    else
      FirstIndent := seFirstLineIndent.AsInteger div 2;
    case GetXBoxItemIndex(_cmbLineSpacing) of
      0:
        begin
          LineSpacing := 100;
          LineSpacingType := rvlsPercent;
        end;
      1:
        begin
          LineSpacing := 150;
          LineSpacingType := rvlsPercent;
        end;
      2:
        begin
          LineSpacing := 200;
          LineSpacingType := rvlsPercent;
        end;
      3:
        begin
          LineSpacing := 100;
          LineSpacingType := rvlsPercent;
          if not seLineSpacingValue.Indeterminate then begin
            LineSpacing := Round(seLineSpacingValue.Value);
            LineSpacingType := rvlsLineHeightAtLeast;
          end;
        end;
      4:
        begin
          LineSpacing := 100;
          LineSpacingType := rvlsPercent;        
          if not seLineSpacingValue.Indeterminate then begin
            LineSpacing := Round(seLineSpacingValue.Value);
            LineSpacingType := rvlsLineHeightExact;
          end;
        end;
      5:
        begin
          LineSpacing := Round(seLineSpacingValue.Value * 100);
          if LineSpacing<50 then
            LineSpacing := 50;
          LineSpacingType := rvlsPercent;
        end;

    end;
  end;
  rv.Format;
end;

procedure TfrmRVPara.seChange(Sender: TObject);
begin
  UpdateSample;
end;


procedure TfrmRVPara.gbAlignmentDblClickItem(Sender: TObject);
begin
  ModalResult := mrOk;
end;



procedure TfrmRVPara.FormActivate(Sender: TObject);
begin
  inherited;
  UpdateSample;
end;

procedure TfrmRVPara.SetTabs(Value: TRVTabInfos);
begin
  FTabs.Assign(Value);
  UpdateTabList(-1);
  lstTabsClick(nil);  
end;

procedure TfrmRVPara.UpdateTabList(NewIndex: Integer);
var i: Integer;
    bmp: TBitmap;
begin
  ClearXBoxItems(_lstTabs);
  ilListTabs.Clear;
  ilListTabs.AllocBy := Tabs.Count;
  for i := 0 to Tabs.Count-1 do begin
    XBoxItemsAddObject(_lstTabs, IntToStr(Tabs[i].Position), nil);
    bmp := TBitmap.Create;
    ilSmallTabs.GetBitmap(ord(Tabs[i].Align), bmp);
    ilListTabs.AddMasked(bmp, bmp.Canvas.Pixels[0,0]);
    bmp.Free;
  end;
  _btnDeleteAll.Enabled := (Tabs.Count>0) or not DeleteAllTabs;
  if NewIndex>=0 then
    SetXBoxItemIndex(_lstTabs, NewIndex)
  else if seTabPos.Indeterminate and (Tabs.Count>0) then
    SetXBoxItemIndex(_lstTabs, 0)
  else
    SetXBoxItemIndex(_lstTabs, Tabs.Find(seTabPos.AsInteger));
end;



procedure TfrmRVPara.Localize;
begin
  inherited;
  Caption := {$IFDEF USERVTNT}_GetWideString{$ENDIF}(RVA_GetS(rvam_par_Title));
  btnOk.Caption := RVA_GetS(rvam_btn_OK);
  btnCancel.Caption := RVA_GetS(rvam_btn_Cancel);
  gbAlignment.Caption := RVA_GetSH(rvam_par_Alignment);
  gbAlignment.Items[0].Caption := RVA_GetS(rvam_par_AlLeft);
  gbAlignment.Items[1].Caption := RVA_GetS(rvam_par_AlRight);
  gbAlignment.Items[2].Caption := RVA_GetS(rvam_par_AlCenter);
  gbAlignment.Items[3].Caption := RVA_GetS(rvam_par_AlJustify);
  gbSpacing.Caption := RVA_GetSH(rvam_par_Spacing);
  lblBefore.Caption := RVA_GetS(rvam_par_Before);
  lblAfter.Caption := RVA_GetS(rvam_par_After);
  lblLineSpacing.Caption := RVA_GetS(rvam_par_LineSpacing);
  lblBy.Caption := RVA_GetS(rvam_par_By);
  gbIndents.Caption := RVA_GetSH(rvam_par_Indents);
  lblLeft.Caption := RVA_GetS(rvam_par_Left);
  lblRight.Caption := RVA_GetS(rvam_par_Right);
  lblFirstLine.Caption := RVA_GetS(rvam_par_FirstLine);
  rbNegative.Caption := RVA_GetS(rvam_par_Hanging);
  rbPositive.Caption := RVA_GetS(rvam_par_Indented);
  gbSample.Caption := RVA_GetSH(rvam_par_Sample);
  cmbLineSpacing.Items[0] := RVA_GetS(rvam_par_LS_100);
  cmbLineSpacing.Items[1] := RVA_GetS(rvam_par_150);
  cmbLineSpacing.Items[2] := RVA_GetS(rvam_par_200);
  cmbLineSpacing.Items[3] := RVA_GetS(rvam_par_AtLeast);
  cmbLineSpacing.Items[4] := RVA_GetS(rvam_par_Exactly);
  cmbLineSpacing.Items[5] := RVA_GetS(rvam_par_Multiple);
  rvs.TextStyles[0].Charset := RVA_GetCharset;
  rvs.TextStyles[1].Charset := RVA_GetCharset;
  ts1.Caption := RVA_GetSH(rvam_par_MainTab);
  ts2.Caption := RVA_GetSH(rvam_par_TabsTab);
  ts3.Caption := RVA_GetSH(rvam_par_TextFlowTab);
  lblTabPos.Caption := RVA_GetS(rvam_par_TabStopPos);
  rgTabAlign.Caption := RVA_GetSH(rvam_par_TabAlign);
  rgTabAlign.Items[0].Caption := RVA_GetS(rvam_par_TabAlignLeft);
  rgTabAlign.Items[1].Caption := RVA_GetS(rvam_par_TabAlignRight);
  rgTabAlign.Items[2].Caption := RVA_GetS(rvam_par_TabAlignCenter);
  rgLeader.Caption := RVA_GetSH(rvam_par_Leader);
  rgLeader.Items[0] := RVA_GetS(rvam_par_LeaderNone);
  lblDelTabs.Caption := RVA_GetS(rvam_par_TabsToBeDeleted);
  lblDelTabList.Caption := RVA_GetS(rvam_par_TabDelNone);
  btnDelete.Caption := RVA_GetS(rvam_par_Delete);
  btnDeleteAll.Caption := RVA_GetS(rvam_par_DeleteAll);
  btnSet.Caption := RVA_GetS(rvam_par_btnSet);
  gbPagination.Caption := RVA_GetSH(rvam_par_Pagination);
  cbKeepLinesTogether.Caption := RVA_GetS(rvam_par_KeepLinesTogether);
  cbKeepWithNext.Caption := RVA_GetS(rvam_par_KeepWithNext);  
end;

{$IFDEF RVASKINNED}
procedure TfrmRVPara.OnCreateThemedControl(OldControl,
  NewControl: TControl);
begin
  if OldControl = _cmbLineSpacing then
    _cmbLineSpacing := NewControl
  {$IFDEF USERVTNT}
  else if OldControl = pc then
    _ts2 := TTntPageControl(NewControl).Pages[1]
  {$ENDIF}
  {$IFDEF USERVKSDEVTE}
  else if OldControl = pc then
    _ts2 := TTePageControl(NewControl).Pages[1]
  {$ENDIF}
  else if OldControl = _cbKeepWithNext then
    _cbKeepWithNext := NewControl
  else if OldControl = _cbKeepLinesTogether then
    _cbKeepLinesTogether := NewControl
  else if OldControl = _rgLeader then
    _rgLeader := NewControl
  else if OldControl = _btnSet then
    _btnSet := NewControl
  else if OldControl = _btnDelete then
    _btnDelete := NewControl
  else if OldControl = _btnDeleteAll then
    _btnDeleteAll := NewControl
  else if OldControl = _lblDelTabList then
    _lblDelTabList := NewControl
  else if OldControl = _lstTabs then begin
    _lstTabs := NewControl;
    {$IFDEF USERVKSDEVTE}
    TteListBox(_lstTabs).Images := ilListTabs;
    {$ENDIF}
    end;
end;
{$ENDIF}


procedure TfrmRVPara.DrawTabItem(Canvas: TCanvas; Index: Integer; R: TRect;
  Selected: Boolean);
begin
  if Selected then begin
    Canvas.Font.Color := clHighlightText;
    Canvas.Brush.Color := clHighlight;
    end
  else begin
    Canvas.Font.Color := clWindowText;
    Canvas.Brush.Color := clWindow;
  end;
  Canvas.FillRect(R);
  ilSmallTabs.Draw(Canvas, R.Left+2, (R.Top+R.Bottom-ilSmallTabs.Height) div 2,
    ord(FTabs[Index].Align));
  inc(R.Left, 4+ilSmallTabs.Width);
  DrawText(Canvas.Handle, PChar(IntToStr(FTabs[Index].Position)), -1, R,
    DT_SINGLELINE or DT_VCENTER);
end;

procedure TfrmRVPara.lstTabsDrawItem(Control: TWinControl; Index: Integer;
  Rect: TRect; State: TOwnerDrawState);
begin
  DrawTabItem(lstTabs.Canvas, Index, Rect, odSelected in State);
end;

procedure TfrmRVPara.lstTabsClick(Sender: TObject);
var idx,i,ldr: Integer;
    s: String;
    ss: TStringList;
begin
  idx := GetXBoxItemIndex(_lstTabs);
  if idx<0 then
    exit;
  DoNotResetLeader := True;
  seTabPos.Value := FTabs[idx].Position;
  DoNotResetLeader := False;
  rgTabAlign.ItemIndex := ord(FTabs[idx].Align);
  s := Tabs[idx].Leader;
  if s='' then
    ldr := 0
  else begin
    ldr := -1;
    ss := CreateXBoxItemsCopy(_rgLeader);
    try
      for i := 1 to ss.Count-1 do
        if s=ss[i][5] then begin
          ldr := i;
          break;
        end;
    finally
      ss.Free;
    end;
  end;
  SetXBoxItemIndex(_rgLeader, ldr);
  _btnSet.Enabled := True;
  _btnDelete.Enabled := True;
end;

procedure TfrmRVPara.seTabPosChange(Sender: TObject);
begin
  _btnSet.Enabled := not seTabPos.Indeterminate;
  _btnDelete.Enabled := not seTabPos.Indeterminate;
  if not DoNotResetLeader then
    SetXBoxItemIndex(_rgLeader, 0);
end;

procedure TfrmRVPara.btnDeleteAllClick(Sender: TObject);
begin
  TabsModified := True;
  DeleteAllTabs := True;
  UpdateDelTabList;
  FTabs.Clear;
  UpdateTabList(-1);
end;

procedure TfrmRVPara.UpdateDelTabList;
var i: Integer;
    s: String;
begin
  if DeleteAllTabs then
    s := RVA_GetS(rvam_par_TabDelAll)
  else if FTabsToDelete.Count=0 then
    s := RVA_GetS(rvam_par_TabDelNone)
  else begin
    s := '';
    for i := 0 to FTabsToDelete.Count-2 do
      s := s+IntToStr(FTabsToDelete[i])+'; ';
    s := s+IntToStr(FTabsToDelete[FTabsToDelete.Count-1])+'.';
  end;
  SetControlCaption(_lblDelTabList, s);
end;

procedure TfrmRVPara.btnDeleteClick(Sender: TObject);
var p,idx: Integer;
begin
  if seTabPos.Indeterminate then begin
    Beep;
    exit;
  end;
  TabsModified := True;
  p := seTabPos.AsInteger;
  if not DeleteAllTabs then begin
    FTabsToDelete.AddUnique(p);
    FTabsToDelete.Sort;
    UpdateDelTabList;
  end;
  idx := Tabs.Find(p);
  if idx>=0 then
    Tabs[idx].Free;
  if idx>=Tabs.Count then
    dec(idx);
  UpdateTabList(idx);
  lstTabsClick(nil);
end;

procedure TfrmRVPara.btnSetClick(Sender: TObject);
var p,idx: Integer;
    tab: TRVTabInfo;
    SL: TStringList;
begin
  if seTabPos.Indeterminate then begin
    Beep;
    exit;
  end;
  TabsModified := True;
  p := seTabPos.AsInteger;
  idx := Tabs.Find(p);
  if idx<0 then begin
    Tabs.Add;
    idx := Tabs.Count-1;
  end;
  tab := Tabs[idx];
  if rgTabAlign.ItemIndex>=0 then
    tab.Align := TRVTabAlign(rgTabAlign.ItemIndex);
  if GetXBoxItemIndex(_rgLeader) in [1..4] then begin
    SL := CreateXBoxItemsCopy(_rgLeader);
    tab.Leader := SL[GetXBoxItemIndex(_rgLeader)][5];
    SL.Free;
    end
  else
    tab.Leader := '';
  tab.Position := seTabPos.AsInteger;
  UpdateTabList(tab.Index);
  lstTabsClick(nil);
end;

procedure TfrmRVPara.HideTabsPage;
begin
  HideTabSheet(_ts2);
end;

end.
