{-----------------------------------------------------------------------------
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: JvaDsgn.PAS, released on 2002-07-04.

The Initial Developers of the Original Code are: Andrei Prygounkov <a dott prygounkov att gmx dott de>
Copyright (c) 1999, 2002 Andrei Prygounkov
All Rights Reserved.

Contributor(s):  Warren Postma (warrenpstma att hotmail dott com)

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

description : TJvCsvDataSet data access component. Design time unit - editor form.

Known Issues:
-----------------------------------------------------------------------------}
// $Id: JvCsvDataForm.pas 10610 2006-05-19 13:35:08Z elahn $

unit JvCsvDataForm;

{$I jvcl.inc}

interface

uses
  SysUtils, Classes,
  Windows, Messages, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, Buttons, StdCtrls,
  JvTypes, JvComponent;

const
  JvCsvAsciiTypeArrayLimit=8; // Array Size constant for TJvCsvColumnFlag to Ascii Character mapping.

type
  TJvCsvDefStrDialog = class(TJvForm)
    EditCsvStr: TEdit;
    Label1: TLabel;
    ButtonOk: TButton;
    ButtonCancel: TButton;
    ListBoxFieldTypes: TListBox;
    Label2: TLabel;
    EditFieldName: TEdit;
    Label3: TLabel;
    SpeedButtonAdd: TSpeedButton;
    SpeedButtonDel: TSpeedButton;
    SpeedButtonMod: TSpeedButton;
    LabelFieldLen: TLabel;
    EditFieldLength: TEdit;
    ListBoxFields: TListBox;
    Label5: TLabel;
    Bevel1: TBevel;
    SpeedButtonMoveFieldUp: TSpeedButton;
    SpeedButtonMoveFieldDown: TSpeedButton;
    LabelKey: TLabel;
    procedure ButtonOkClick(Sender: TObject);
    procedure ButtonCancelClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure ListBoxFieldsClick(Sender: TObject);
    procedure SpeedButtonAddClick(Sender: TObject);
    procedure SpeedButtonModClick(Sender: TObject);
    procedure ListBoxFieldTypesClick(Sender: TObject);
    procedure SpeedButtonMoveFieldUpClick(Sender: TObject);
    procedure SpeedButtonMoveFieldDownClick(Sender: TObject);
    procedure SpeedButtonDelClick(Sender: TObject);
    procedure ListBoxFieldsKeyUp(Sender: TObject; var Key: Word;
      Shift: TShiftState);
  private
    FUpdating: Boolean;
    FOriginalCsvStr: string;
    FSeparator: Char;
    FTypeChars: array [0..JvCsvAsciiTypeArrayLimit] of Char;
    FFieldTypeCh: Char;
    procedure ItemChange;
    function MakeString: string; // take changes, put back into string format
    procedure UpdateCsvStr;
    procedure LengthDisabled;
    procedure LengthEnabled(FieldLength: Integer);
    procedure SetCsvStr(ACsvStr: string);
    function GetCsvStr: string;
  public
    property Separator: Char read FSeparator write FSeparator;
    property CsvStr: string read GetCsvStr write SetCsvStr;
  end;

var
  JvCsvDefStrDialog: TJvCsvDefStrDialog;

implementation

uses
  JvCsvData, JvCsvParse, JvDsgnConsts;

{$R *.dfm}

procedure TJvCsvDefStrDialog.UpdateCsvStr;
var
  I: Integer;
  S: string;
begin
  S := '';
  for I := 0 to ListBoxFields.Items.Count - 1 do
    if S = '' then
      S := ListBoxFields.Items[I]
    else
      S := S + FSeparator + ListBoxFields.Items[I];
  EditCsvStr.Text := S;
end;

procedure TJvCsvDefStrDialog.LengthEnabled(FieldLength: Integer);
begin
  EditFieldLength.Text := IntToStr(FieldLength);
  EditFieldLength.Enabled := True;
  EditFieldLength.Color := clWindow;
  LabelFieldLen.Enabled := True;
end;

procedure TJvCsvDefStrDialog.LengthDisabled;
begin
  EditFieldLength.Text := '';
  EditFieldLength.Color := clBtnFace;
  EditFieldLength.Enabled := False;
  LabelFieldLen.Enabled := False;
end;

type
  TStringArray = array of string;

procedure TJvCsvDefStrDialog.ItemChange;
var
  SubFields: TStringArray;
  I, Count: Integer;
  SelectedText: string;
  FieldLength: Integer;
begin
  SetLength(SubFields, 3);

  if ListBoxFields.ItemIndex >= 0 then
  begin
    SelectedText := ListBoxFields.Items[ListBoxFields.ItemIndex];
    Count := StrSplit(SelectedText, ':', Chr(0), SubFields, 2); // Look for Colon
  end
  else
  begin
    Count := 0;
    SelectedText := '';
  end;

  try
    if Count < 2 then
    begin { no colon! }
      { defaults to string, length DEFAULT_CSV_STR_FIELD}
      EditFieldName.Text := SelectedText;
      FFieldTypeCh := '$';
      FieldLength := DEFAULT_CSV_STR_FIELD;
    end
    else
    begin
      EditFieldName.Text := SubFields[0];
      FFieldTypeCh := SubFields[1][1];
      FieldLength := StrToIntDef(Copy(SubFields[1], 2, Length(SubFields[1])), DEFAULT_CSV_STR_FIELD);
    end;
  except
    { clear it if we have a problem }
    EditFieldName.Text := '';
    FFieldTypeCh := '$';
    FieldLength := DEFAULT_CSV_STR_FIELD;
  end;
  if FFieldTypeCh = '$' then
    LengthEnabled(FieldLength)
  else
    LengthDisabled;

 { given a field type character, match it and then selecting
    the correct index in the field types list }
  ListBoxFieldTypes.ItemIndex := 1;
  for I := Low(FTypeChars) to High(FTypeChars) do
    if FTypeChars[I] = FFieldTypeCh then
    begin
      ListBoxFieldTypes.ItemIndex := I;
      Break;
    end;
end;

function TJvCsvDefStrDialog.GetCsvStr: string;
begin
  Result := EditCsvStr.Text;
end;

procedure TJvCsvDefStrDialog.SetCsvStr(ACsvStr: string);
var
  Fields: array of string;
  I, Count: Integer;
  FieldDefStr: string;

  function StripQuotes(S: string): string;
  begin
    if (Length(S) >= 2) and (S[1] = '"') and (S[Length(S)] = '"') then
      Result := Copy(S, 2, Length(S) - 2)
    else
      Result := S;
  end;

begin
  FOriginalCsvStr := ACsvStr;
  FieldDefStr := UpperCase(StrStrip(ACsvStr));

  SetLength(Fields, MAXCOLUMNS); { MAXCOLUMNS is a constant from CsvDataSource.pas }
  EditCsvStr.Text := ACsvStr;
  if Length(FieldDefStr) > 0 then
    Count := StrSplit(FieldDefStr, FSeparator, Chr(0), Fields, MAXCOLUMNS)
  else
    Count := 0;

  try
    FUpdating := True;
    ListBoxFields.Items.Clear;
    if Count > 0 then
      for I := 0 to Count - 1 do
        ListBoxFields.Items.Add(StripQuotes(Fields[I]));
  finally
    ListBoxFields.ItemIndex := 0;
    ItemChange;
    FUpdating := False;
  end;
  Self.ActiveControl := EditFieldName;
end;

procedure TJvCsvDefStrDialog.ButtonOkClick(Sender: TObject);
begin
  UpdateCsvStr;
  if EditCsvStr.Text = FOriginalCsvStr then
    if MessageBox(Self.Handle, PChar(RsYouHaventActuallyChangedAnythingIfY),
      PChar(RsConfirm), MB_YESNO or MB_ICONWARNING) = IDNO then
      Exit; // quit before we set ModalResult, so we continue on editing.
  ModalResult := mrOk;
end;

procedure TJvCsvDefStrDialog.ButtonCancelClick(Sender: TObject);
begin
  EditCsvStr.Text := FOriginalCsvStr; // cancel all edits.
  ModalResult := mrCancel;
end;

procedure TJvCsvDefStrDialog.FormCreate(Sender: TObject);
begin
  // FTypeChars:  Map of Ascii character symbols to TJvCsvColumnFlag enumerated
  //              types.
  //
  // IF YOU ADD ANYTHING TO THIS LIST, CHANGE CONSTANT
  // JvCsvAsciiTypeArrayLimit at top of this unit!
  //
  // The order of this list must match the order of the
  // enumerated TJvCsvColumnFlag type in JvCsvData.pas!
  //
  //
  FTypeChars[0] := '!'; // Boolean
  FTypeChars[1] := '$'; // String
  FTypeChars[2] := '%'; // Integer
  FTypeChars[3] := '&'; // Float
  FTypeChars[4] := '@'; // Ascii DateTime
  FTypeChars[5] := '#'; // Hex Timestamp
  FTypeChars[6] := '^'; // Hex LocalTime
  FTypeChars[7] := '/'; // Ascii Date
  FTypeChars[8] := '*';
  {
   $ = string (ftString) - also used if no character is given.
   % = whole Integer value (ftInteger)
   & = floating point value (ftFloat)
   @ = Ascii datetime value (ftDateTime) as YYYY/MM/DD HH:MM:SS (Component Specific)
   # = Hex-Ascii Timestamp (A93F38C9) seconds since Jan 1, 1970 GMT (Component Specific)
   ^ = Hex-Ascii Timestamp (A93F38CP) corrected to local timezone (Component Specific)
   ! = Boolean Field (0 in csv file=False, not 0 = True, blank = NULL)

  }
end;

procedure TJvCsvDefStrDialog.ListBoxFieldsClick(Sender: TObject);
begin
  if not FUpdating then
    try
      FUpdating := True;
      ItemChange;
    finally
      FUpdating := False;
    end;
end;

function TJvCsvDefStrDialog.MakeString: string;
var
  S: string;
  FieldLength: Integer;
begin
  if ListBoxFieldTypes.ItemIndex < 0 then
    Exit;

  S := StrStrip(UpperCase(EditFieldName.Text));
  if S <> EditFieldName.Text then
    EditFieldName.Text := S;

  if not ValidIdentifier(PChar(S)) then
    Exit;

  FFieldTypeCh := FTypeChars[ListBoxFieldTypes.ItemIndex];
  if FFieldTypeCh = '$' then
  begin
    FieldLength := StrToIntDef(EditFieldLength.Text, DEFAULT_CSV_STR_FIELD);
    if FieldLength <> DEFAULT_CSV_STR_FIELD then
      S := S + ':$' + IntToStr(FieldLength);
  end
  else
    S := S + ':' + FFieldTypeCh;
  Result := S;
end;

function FieldNameOnly(CsvFieldDef: string): string;
var
  XPos: Integer;
begin
  XPos := Pos(':', CsvFieldDef);
  if XPos = 0 then
  begin
    Result := CsvFieldDef;
    Exit;
  end;
  Result := Copy(CsvFieldDef, 1, XPos - 1);
end;

procedure TJvCsvDefStrDialog.SpeedButtonAddClick(Sender: TObject);
var
  F, S, Unique: string;
  I: Integer;
begin
  if FUpdating then
    Exit;
  S := MakeString;
  if S = '' then
  begin
    MessageBox(Self.Handle, PChar(RsMustTypeAValidFieldNameAndSelectAFi),
      PChar(RsAddFailed), MB_OK or MB_ICONERROR);
    Exit; { not valid, can't add }
  end;
   // XXX Check Validity and Uniqueness before adding.
  F := FieldNameOnly(S);
  if not ValidIdentifier(PChar(F)) then
  begin
    MessageBox(Self.Handle, PChar(Format(RsFieldNameIsNotAValidIdentifier, [S])),
      PChar(RsAddFailed), MB_OK or MB_ICONERROR);
    Exit;
  end;
  for I := 0 to ListBoxFields.Items.Count - 1 do
  begin
    Unique := FieldNameOnly(ListBoxFields.Items[I]);
    if Unique = F then
    begin
      MessageBox(Self.Handle, PChar(RsCantAddTwoFieldsWithTheSameNameSele),
        PChar(RsAddFailed), MB_OK or MB_ICONERROR);
      Exit;
    end;
  end;

  try
    FUpdating := True;
    ListBoxFields.Items.Add(S);
    ListBoxFields.ItemIndex := -1; // make sure no new item is selected, so we can add another.
    UpdateCsvStr;
    EditFieldName.Text := '';
    ActiveControl := EditFieldName;
  finally
    FUpdating := False;
  end;
end;

procedure TJvCsvDefStrDialog.SpeedButtonModClick(Sender: TObject);
var
  F, S, Unique: string;
  Selected, I: Integer;
begin
  if FUpdating then
    Exit;
  S := MakeString;
  if S = '' then
  begin
    MessageBox(Self.Handle, PChar(RsMustTypeAValidFieldNameAndSelectAFi),
      PChar(RsUpdateFailed), MB_OK or MB_ICONERROR);
    Exit; { not valid, can't add }
  end;
   // XXX Check Validity and Uniqueness before adding.
  F := FieldNameOnly(S);
  if not ValidIdentifier(PChar(F)) then
  begin
    MessageBox(Self.Handle, PChar(Format(RsFieldNameIsNotAValidIdentifier, [S])),
      PChar(RsUpdateFailed), MB_OK or MB_ICONERROR);
    Exit;
  end;
  Selected := ListBoxFields.ItemIndex;
  if Selected < 0 then
  begin
    MessageBox(Self.Handle, PChar(RsNoItemIsSelectedInTheFieldsListYouC),
      PChar(RsUpdateFailed), MB_OK or MB_ICONERROR);
    Exit; // can't do that!
  end;
  for I := 0 to ListBoxFields.Items.Count - 1 do
  begin
    Unique := FieldNameOnly(ListBoxFields.Items[I]);
    if (I <> Selected) and (Unique = F) then
    begin
      MessageBox(Self.Handle, PChar(RsModifyingTheCurrentlySelectedItemWo),
        PChar(RsUpdateFailed), MB_OK or MB_ICONERROR);
      Exit;
    end;
  end;
  try
    FUpdating := True;
    ListBoxFields.Items[Selected] := S; // changes current item.
    UpdateCsvStr;
  finally
    FUpdating := False;
  end;
end;

procedure TJvCsvDefStrDialog.ListBoxFieldTypesClick(Sender: TObject);
begin
  if not FUpdating then
    try
      FUpdating := True;
      if ListBoxFieldTypes.ItemIndex = 1 then
        LengthEnabled(DEFAULT_CSV_STR_FIELD)
      else
        LengthDisabled;
      ActiveControl := EditFieldName;
    finally
      FUpdating := False;
    end;
end;

procedure TJvCsvDefStrDialog.SpeedButtonMoveFieldUpClick(Sender: TObject);
var
  Selected: Integer;
  TempStr: string;
begin
  Selected := ListBoxFields.ItemIndex;
  if Selected <= 0 then
    Exit; // can't move an invalid item up, or item 0
  // swap selected, with selected-1 (moves selected item up)
  FUpdating := True;
  TempStr := ListBoxFields.Items[Selected - 1];
  ListBoxFields.Items[Selected - 1] := ListBoxFields.Items[Selected];
  ListBoxFields.Items[Selected] := TempStr;
  ListBoxFields.ItemIndex := ListBoxFields.ItemIndex - 1;
  UpdateCsvStr;
  FUpdating := False;
end;

procedure TJvCsvDefStrDialog.SpeedButtonMoveFieldDownClick(Sender: TObject);
var
  Selected: Integer;
  TempStr: string;
begin
  Selected := ListBoxFields.ItemIndex;
  if Selected < 0 then
    Exit; // can't move an invalid item down
  if Selected >= ListBoxFields.Items.Count - 1 then
    Exit; // can't move last item down

  // swap selected, with selected+1 (moves selected item down)
  FUpdating := True;
  TempStr := ListBoxFields.Items[Selected + 1];
  ListBoxFields.Items[Selected + 1] := ListBoxFields.Items[Selected];
  ListBoxFields.Items[Selected] := TempStr;
  ListBoxFields.ItemIndex := ListBoxFields.ItemIndex + 1;
  UpdateCsvStr;
  FUpdating := False;
end;

procedure TJvCsvDefStrDialog.SpeedButtonDelClick(Sender: TObject);
var
  Item: Integer;
begin
  Item := ListBoxFields.ItemIndex;
  if Item < 0 then
    Exit; // can't delete, nothing selected.
  FUpdating := True;
  ListBoxFields.Items.Delete(Item);
  ListBoxFields.ItemIndex := -1;
  UpdateCsvStr;
  FUpdating := False;
end;

procedure TJvCsvDefStrDialog.ListBoxFieldsKeyUp(Sender: TObject;
  var Key: Word; Shift: TShiftState);
begin
// LabelKey.Caption := IntToStr(Key);
  if Key = VK_DELETE then
    SpeedButtonDelClick(Sender);
end;

end.

