unit ULRecTyp;
{ Generic streamable data records definition:
    - Every TULxxRec record type has its unique ID (RecID)
    - record can by of any length (RecLen)
    - RecID and RecLen are specified in Head field, that is
      present in every record at its beggining
    - Head field is immediately followed by TULRecInfo structure
    - any data bytes can follow - count = Head.RecLen - sizeof(TULRecHead) -
        sizeof(TULRecInfo)
    - record itself can be a header for data that follow after the record
      (mostly other TULxRec records), in that case TULRecInfo.DataLen is
      non zero, if TULRecInfo.Flags have rfNoULRecChild set, then the
      data are not TULxRec records (e.g. acquisition data) and the TULxRec
      usually defines own methods to handle them

  Top owner of all TULxxRec records/objects/forms is ULFObju .TULFObj }

interface
{ see ULREC.LST :
      UliType.pas ... instrument data/record format
      UlgcType.pas ... UlGCID gradient content record
      UlgfType.pas ... ULGFID gradient flow record
      UluType.pas ... users data/record format
      UlmType.pas ... method data/record format
      UldType.pas ... chromatogram data/file format,
      ... }

const
  ULRecMaxDataSize = 256*256*256;
    { Arbitrarily chosen maximal size of the ULxxRec record }
  ULMaxChildRecIDCount = 100;
    { Arbitrarily chosen maximal allowed number of different RecIDs
      allowed for child records }
  ULMaxFldCount = 128;
  ULFExt = '.ULF';
    { Default extension for files }

type
  TULRecID = longint;
    { Unique record ID - every data type record has it's own ID }
  TULRecIDStr = shortstring;

  TULRecLen = longint;
    { Length of the record data, including sizeof(TULRecHead), i.e. = sizeof(TULxRec) }
  TULDataLen = longint;
    { Length of the additional data that are not part of the record itself,
      the data are stored in streams immediately after the record,
      it can be array of another records (loaded into Components array)
      or data of any structure }
  TULRecFlags = longint;
    { Field that has every ULxRec data record in Info field,
      meaning of bits depends on RecID, for common ones see rfXXXX  }
  TULRecOptions = longint;
    { For user defined flags, not used by the system. }
  TULRecCount = longint;
    { Number of owned TULxxRec records. }
  TULRecName = shortstring;
    { Name used for discrimination of more records of the same type stored
      in the same file. If the record.Info.Flags has rfHasRecName set,
      then the first field after Info field is shortstring that holds
      name of the record. (see TULFileName) }

  TULRecHead = packed record
    RecID: TULRecID;
    RecLen: TULRecLen;
  end;
    { Every TULxRec starts with this structure and is followed by TULRecInfo
      structure }

  TULRecInfo = packed record
    Flags: TULRecFlags; { see rfXXXX }
    CreateTime: TDateTime;
      { date/time of creation of this record }
    ChangeTime: TDateTime;
      { date/time of last modification (last common published property,
        see ULRecLastCommonProp ) }
    DataLen: TULDataLen;
      { sum of sizes of child records, that follow in the source stream }
    FieldsLen: integer;
      { Equals mostly = sizeof(TULxxRec), only if the record contains longstring
        fields, then it includes also lenghts of the strings. Whole record
        including its fields and data needs FieldsLen+DataLen bytes in the stream. }
    Options: TULRecOptions;
      { Flags reserved just for user convenience, not used in any way }
  end;
    { Every TULxRec contains this structure immediately after TULRecHead
      structure, that is at the beginning of the TULxRec }

  TULRec = packed record
    Head: TULRecHead;
    Info: TULRecInfo;
    Fields: record end;
  end;

  PULRec = ^TULRec;
  TULRecMax = packed record
    Head: TULRecHead;
    Info: TULRecInfo;
    case integer of
      0: (Fields: array[0..ULRecMaxDataSize - 1]of byte);
      1: (RecName: TULRecName);
      2: (Data: array[0..ULRecMaxDataSize - 1]of byte);
  end;
    { just for typecasting }
  PULRecMax = ^TULRecMax;

{v0.09}
type
  TUserCoef = single;
{/v0.09}

  TULFldDesc = record
    Caption: string;
      { Name of the field that should be appear in edit and browse window }
    Hint: string;
      { Short description of the field purpose. }
    EditWidth: integer;
      { Width of the field value in editwindow. If = 0, default used. }
    BrowseWidth: integer;
      { Width of the field value in browsewindow. If = 0, default used. }
    Flags: longint;
      { See ffXXXX }
    {v0.09}
    UserCoef: TUserCoef;
      { Coeficient by which the value of the field will be divided
        if UserValue will be requested (taken in acount if <> 0) }
    NumDec: integer;
      { number of decimal points if converting number to string,
        number of digits taken from BrowseWidth }
    {/v0.09}
    {v0.14}
    ValuesSourceRecID: TULRecID;
    {/v0.14}
  end;
  PULFldDesc = ^TULFldDesc;

  TULFldDescs = array[0..ULMaxFldCount-1] of TULFldDesc;
  PULFldDescs = ^TULFldDescs;

  TULChildRecIDs = array [0..ULMaxChildRecIDCount - 1] of TULRecID;
  PULChildRecIDs = ^TULChildRecIDs;
  TULRecDesc = record
    { Info common for all instances of given TULxxRec record. }
    Caption: string;
      { Name of the record that is shown to the user }
    ChildRecIDsStr: string;
      { List of child RecIDs, in ASCII, comma separated. Used in MAKECOMP.  }
    ChildRecIDs: PULChildRecIDs;
      { pointer to array containing list of RecIDs that are allowed
        for TULxxObj childs. }
    ChildRecIDCount: integer;
      { How many RecIDs are in the ChildRecIDs^ array. }
    FldCount: integer;
      { Number of ULxxRec fields (exluded Head and Info fields) }
    Flds: PULFldDescs;
      { Description of fields (common info for all instances of the record) }
    Flags: TULRecFlags;
    SortExp: string;
      { Pascal expression that returns sort value of the record; of rfSortedByNumber
        is set, than the result should be number otherwise string. }
    EditFieldList: string;
      { List of names of the fields (comma separated, no spaces) that should
        appear in the edit window (in the given order). If not specified,
        all fields in normal order will be used. }
    BrowseFieldList: string;
      { List of names of the fields (comma separated, no spaces) that should
        appear in browser (in the given order). If not specified all fields
        will be used. }
  end;
  PULRecDesc = ^TULRecDesc;

  TULFileName = shortstring;
    { Name of the files used for storing ulan data, records. If
      the name contains "#" (= ULFNDelim) then before "#" is the usual filename
      part, after "#" follows name of the record stored in that file - used for
      reading just that record from the file. }
  TULRecFn = function(AObj: pointer):integer;
    { Exported function that are looked up before calling standard TULObj
      methods have this type }

const
  ULFNDelim = '#';
    { See TULFileName. }
  UlfID =  ord('U') + 256*ord('L') + 256*256*ord('F');
    { ID of master record that holds all records read from file. }
  NoULRecID = 0;
    { Means no data assigned }
  UlxID = ord('U') + 256*ord('L') + 256*256*ord('X');
    { generic ULxRec record with default behavior - just read/write data of
      any size - not supported now. }
  ULRecHeadSize = sizeof(TULRecHead);
    { lenght of header of ULxRec data record(s) (files)
      (includes record type unique ID (TRecID = longint) and (TRecLen = longint)
      length of the record }
  ULRecInfoSize = sizeof(TULRecInfo);
  ULRecSize = sizeof(TULRec);
  ULRecMaxSize = ULRecSize + ULRecMaxDataSize;
  ULRecLastCommonProp = 'ChangeTime';
    { Name of the last published property that is present in every ULObj
      object descendant }

{rfXXXX common ULxRecFlags bits}
const
  {v0.15}
  rfWriteLocked = $00000080;
    { Records is currently locked for writing, i.e. can not be changed by user
      (in edit form or browser). Can be changed runtime (see rfEnabled, rfVisible) }
  {/v0.15}
  rfJustCreated = $00000100;
    { if set, then the object was created in browser and is beeing edited,
      should be destroyed if editing canceled (by pressing Cancel btn) }
  rfCantAddChild =  $00000800;
    { Child can not be added from browser (by user) }
  rfSelected = $00001000;
    { The objects is currently selected (highligthed) }
  rfSortedByNumber = $00002000;
    { If set, then if rfChildSorted set, then Sort method uses childs'
      GetSortNum method insted of GetSortStr (must be set to owner and childs) }
  rfBrowseOnEdit    = $00004000;
    { If set, then if Edit is called from browse window, then TULObj.Browse
      method is called insted of TULObj.Edit (used for objects, that are
      just no info having headers for array of records, e.g. peaks or baseline) }
  rfCantDelete      = $00008000;
    { The object can not be deleted by user during browsing if flag set.
      Especially useful, if pointer to some childs used, prevents dereferencing
      already invalid pointer. }
  rfFileDataStream = $00010000;
    { FData field in TULObj is implemented as FileStream; rfHasRecName must
      be also on and the RecName property is used for FileStream's name. }
  rfVisible        = $00020000;
    { Record visible during manual editing (permanent property, i.e.
      should not be changed runtime) }
  rfEnabled        = $00040000;
    { Record enabled for user manual editing (permanent property, i.e.
      should not be changed runtime) }
  rfTemporary      = $00080000;
    { The record/object is created just at runtime, not stored to file
      (SaveToStream methods of given object does nothing) }
  rfRootChild      = $00100000;
    { The record/object can be direct child of root TULFObj object
      (used to offer this record as possible new record if New Record requested) }
  rfHasRecName     = $00200000;
    { If set, then the record contains field, that holds name of the record -
      it is ALWAYS the first field after standard Head and Info fields;
      used for discrimination if more records of the same type included
      in the same file. (see TULFileName) }
  rfChildSorted    = $00400000;
    { If set, child records are sorted, i.e. child objects must implement
      (override) method GetSortStr. }
  rfChildAllowed   = $00800000;
    { if set, the record can have child data - if rfNoULRecChild set, then
      just generic data, otherwise childs are ULxRec records in Components array}
  rfDefault        = $01000000;
    { if set, then the record contains program compile time default values }
  rfFromTemplate   = $02000000;
    { if set, then the record contains data retrieved from ini or template file }
  rfManual         = $04000000;
    { if set, then the record contains data that were set by the user manually
      during the program session }
  rfAcquired       = $08000000;
    { if set, then the record contains data received from some device }
  rfIsTemplate     = $10000000;
    { if set, then the record is serving as a template record (read only) }
  rfEncrypted      = $20000000;
    { if set, then all record fields after Head and Info fields are encrypted }
  rfDataChild   = $40000000;
    { if set, then data that follow after the record are not TULxRec records
      and will be loaded to Data memory stream associated with TULObj,
      instead into TULObj.Components array }
{/rfXXXX}

{/ffXXXX ULRec fields/properties flags }
const
  ffEnabled = 1;
    { Field can be modified by user }
  {ffVisible = 2; not used, use EditFieldList and BrowseFieldList instead }
  {v0.13}
  ffFileName = 4;
    { The field value is name of a file }
  ffFileDateTime = 8;
    { The field value (integer) is File-date/time value }
  {/v0.13}
  {v0.14}
  ffULEnum = $10;
    { The field is of type ULEnum (i.e. values can be assigned only
      from childs of ValuesSource ULObj (its ID can be specified in ValuesSourceRecID) }
  ffReadOnly = $20;
    { The field (must be edited in TEdit) is readonly, i.e. if Enabled,
      user can put cursor in it but can not modify it }
  {/v0.14}
{/ffXXXX}
{ULRecFindOptions}
type
  TULRecFindOptions = longint;
    { options for TULObj.FindObj(ARecID:TULRecID; fo:TULRecFindOptions;
      var AObj:TULObj) method }
{foXXXX}
const
  foDefault = 0;
    { starts looking from self, then childs, recursively }
  foNotSelf = 1;
    { look only to childs (not self), recursively }
  foNotRecursive = 2;
    { don't look in childs' childs }
  foFromRoot = 4;
    { start looking from top owner }
  foNext = 8;
    { look for next matching AObj (i.e. AObj start value is last found value) }
  foFromOwner = $10;
    { start looking from self.owner }
{/foXXXX}

{/FindULRecOptions}
type
  TStreamOptions = record
    Flags: longint;
    IndentLevel: shortstring;
    Line: longint;
    Head: shortstring;
    IsInData: boolean;
  end;
{sfXXXX}
const
  sfAscii = 1;
    { TULRec.SaveToStream, LoadFromStream, LoadRecFromStream will deal with
      ascii file/conversion. This flag set if FileName has .ASC extension
      instead of .ULF }
  sfOnlySelectedChilds = 2;
    { SaveToStream will save only selected childs }
{/sfXXXX}

type
  TULRecFnID = integer;
{ufXXXX supported ULRec exported functions; parameter to  ULFObju
  .TULFObj.ULFindObjFN method }
const
  ufEdit = 0;
    { function called when Obj.Edit method called }

{/ufXXXX}

const
  AscBeginTag = ' begin';
    { When exporting TULObj to .ASC file, every record begining is marked
      with line: "ULxx begin" (eventuall indent spaces at the beginning) }
  AscEndTag = 'end';
    { When exporting TULObj to .ASC file, every record end with line: "end"
      (eventuall indent spaces at the beginning) }
  AscValDelimiter = '=';
    { When exporting TULObj to .ASC file, value of each published property
      of the record is written on the separate line prepended with property
      name - separated with "=", e.g. "Name=Neco" record end with line: "end"


{ULRecChildType}
type
  TULRecChildType = byte;
{ctXXXX}
const
  ctNone = 0;
    { no childs allowed for the ULxRec record }
  ctData = 1;
    { generic data (non ULxRec) childs allowed (= rfChildAllowed and rfNoULRecChild) }
  ctULRec = 2;
    { childs are ULxRec record (= rfChildAllowed and (not rfNoULRecChild) }
{/ctXXXX}

{/ULRecChildType}

{pnXXXX record/fields attribute/property names (specify in ULxxType.PAS files,
  used by makecomp.exe to generate source files), see ulobju }
const
  {common ULRec properties}
  pnRecID = 'RecID';
  pnRecLen = 'RecLen';
  pnFlags = 'Flags';
  pnCreateTime = 'CreateTime';
  pnChangeTime = 'ChangeTime';
  pnRecSize = 'RecSize';
  pnFileName = 'FileName';
  pnDataLen = 'DataLen';
  pnChildType = 'ChildType';
  {/common ULRec properties}

  {ULxxType.PAS properties used to define additional properties of TULxxRec
    specified in TULRecDesc }
  pnType = 'Type';
    { Property of fields - type of the field for Form creation:
      for pvEnum - TComboBox
      for pvULEnum - TComboBox (assigning value from the field of the
        same name of on of the child records of ValuesSource ULObject assigned
        runtime to FieldDesc accorging to ValuesSourceRecID (= RecID of
        the owner of the childs with the fields of the same name (can be 0 for any))
      for pvFileName - TEdit (for now; assigns RelativeFileName))
      for pvFileDateTime - TEdit (converting to human readable value)
      else TEdit (for boolean TCheckBox) }
  pnCaption = 'Caption';
    { Property for: record - name of the record type shown to the user;
                    field - name of the field shown to the user }
  pnChildRecIDs = 'ChildRecIDs';
    { Property for record. Holds comma separated list of ULyyID constants,
      that are allowed for the childs of this record (i.e. what kind of records
      are allowed in TULxxObj.Childs array) }
  pnChildSorted = 'ChildSorted';
    { Property for record. If = '1', then child records will be sorted.
      Makes sense only if ChildRecIDs defined. (= rfChildSorted) }
  pnDataChild = 'DataChild';
    { Property of record. If = '1', then the record has just generic data stream,
      not TULxxRec childs (= rfDataChild) }
  pnUses = 'Uses';
    { Property of record. List of units (comma separated, no spaces) that
      should be in uses clause of units created by MAKECOMP }
  pnRootChild = 'RootChild';
    { Property of record. If specified (e.g. RootChild=1), than the record is
      direct child of TULFObj }
  pnHasRecName = 'HasRecName';
    { Property of record (= rfHasRecName). }
  pnSortExp = 'SortExp';
    { Property of record - pascal expression that is returned by GetSortStr
      method }
  pnTemporary = 'Temporary';
    { Property of record (= rfTemporary) }
  pnEnabled = 'Enabled';
    { Property of record (= rfEnabled), or field (ffEnabled) }
  pnReadOnly = 'ReadOnly';
    { property if field (= ffReadOnly) }
  pnVisible = 'Visible';
    { Property of record (= rfVisible) }
  pnFileDataStream = 'FileDataStream';
    { Property of record (= rfFileDataStream) }
  pnBrowseOnEdit = 'BrowseOnEdit';
    { Property of record (= rfBrowseOnEdit) }
  pnSortedByNumber = 'SortedByNumber';
    { Property of record (owner and child) = (rfSortedByNumber) }
  pnEditFieldList = 'EditFieldList';
    { Property of record, list of fields (comma separated, no spaces)
      that should be included in edit window. }
  pnBrowseFieldList = 'BrowseFieldList';
    { Property of record, list of fields (comma separated, no spaces) that
      should be included in browser (in less details mode). }
  pnHint = 'Hint';
    { Property of fields. }
  pnEditWidth = 'EditWidth';
    { Property of fields }
  pnBrowseWidth = 'BrowseWidth';
    { Property of fields. }
  {v0.09}
  pnUserCoef = 'UserCoef';
  pnNumDec = 'NumDec';
  {/0.09}
  {v0.14}
  pnValuesSourceRecID = 'ValuesSourceRecID';
    { Property of fields, is specified only if pnType=ULEnum, then it's value
      is RecID of the ULObject (e.g. 'ULDID'), which childs have the field with
      the same name, and only one of the values specified in the childs can
      be assigned to the field that has this property defined. What is the
      source object must be specified runtime by assigning FieldDesc.ValuesSource := ..,
      before invoking edit dialog. }
  {/ULxxType.PAS properties ...}
{/pnXXXX}

{pvXXXX record/fields attribute/property values }
  pvEnum = 'Enum';
  {v0.13}
  pvFileName = 'FileName';
  pvFileDateTime = 'FileDateTime';
  {/v0.13}
  {v0.14}
  pvULEnum = 'ULEnum';
  {/v0.14}
{/pvXXXX}
type
  TULObjResult = integer;
{orXXXX ULObject result codes}

  (* were exceptions:
        EEmptyFileName = class(Exception);
        { Trying to save to file, but no file name specified. }
      EEmptyRec = class(Exception);
        { Trying to access data in empty record. }
      EInvalidRecID = class(Exception);
        { Invalid RecID encountered. Raised from ULFObju CreateObj }
      EInvalidRecSize = class(Exception);
        { Trying to access field which offset is beyond allocated memory. }
      ENegativeRecSize = class(Exception);
        { Trying to set negative size for the record. }
      ENoChildAllowed = class(Exception);
        { Trying to create child in ULObj for which it is not allowed. }
      EInvalidChildType = class(Exception);
        { Trying to access Data field for in non ctData childtype ULObj. }
      {EOnlyULFCanLoadFromFile = class(Exception);
        { Trying to call LoadFromFile method by object with other RecID then
          ULFID. }
      EULFCantLoadRecFromStream = class(Exception);
        { LoadRecFromStream method can not be called by ULFID object to load itself
          (ULF object is never stored in the stream). }
      EInvalidULRecDataLen = class(Exception);
        { There was not enough data in the stream as was specified by DataLen field }
      EULFTopOwnerNotFound = class(Exception);
        { Every ULObj should have as the top owner ULFID record, but it was
          not found for this one. }
      ENonULObjOwner = class(Exception);
        { Every non ULFID TULxxObj descendants must have always Owner of type
          TULxxObj }
      EAbstractCreateObj = class(Exception);
        { Abstract method TULObj.CreateObj was called. }
      EMustHaveOwner = class(Exception);
        { Every object with other ID than ULFID must have Owner. }
      EAbstractBrowseObj = class(Exception);
      EAbstractEditObj = class(Exception);
      EFileDataStreamMustHasRecName = class(Exception);
        { Object has rfFileDataStream set, but not rfHasRecName }
      EInvalidAscFormat = class(Exception);
        { While reading from .ASC file something went wrong. }
      EUnregisteredUser = class(Exception);
        { Trying to unregister unregistered user of TULObj }
   *)

const
  orOK = 0;
  or0 = 4000;
  orEmptyFileName = or0 + 1;
    { Trying to save to file, but no file name specified. }
  orEmptyRec = or0 + 2;
    { Trying to access data in empty record. }
  orInvalidRecID = or0 + 3;
    { Invalid RecID encountered. Raised from ULFObju CreateObj }
  orInvalidRecSize = or0 + 4;
    { Trying to access field which offset is beyond allocated memory. }
  orNegativeRecSize = or0 + 5;
    { Trying to set negative size for the record. }
  orNoChildAllowed = or0 + 6;
    { Trying to create child in ULObj for which it is not allowed. }
  orInvalidChildType = or0 + 7;
    { Trying to access Data field for in non ctData childtype ULObj. }
  {EOnlyULFCanLoadFromFile = class(Exception);
    { Trying to call LoadFromFile method by object with other RecID then
      ULFID. }
  orULFCantLoadRecFromStream = or0 + 8;
    { LoadRecFromStream method can not be called by ULFID object to load itself
      (ULF object is never stored in the stream). }
  orInvalidULRecDataLen = or0 + 9;
    { There was not enough data in the stream as was specified by DataLen field }
  orULFTopOwnerNotFound = or0 + 10;
    { Every ULObj should have as the top owner ULFID record, but it was
      not found for this one. }
  orNonULObjOwner = or0 + 11;
    { Every non ULFID TULxxObj descendants must have always Owner of type
      TULxxObj }
  orAbstractCreateObj = or0 + 11;
    { Abstract method TULObj.CreateObj was called. }
  orMustHaveOwner = or0 + 12;
    { Every object with other ID than ULFID must have Owner. }
  orEmptyCurULObj = or0 + 13;
    { CurULObj variable was not set to valid ULObj e.g. before calling
      TBrowseForm.Create. }
  orAbstractBrowseObj = or0 + 14;
  orAbstractEditObj = or0 + 15;
  orFileDataStreamMustHasRecName = or0 + 16;
    { Object has rfFileDataStream set, but not rfHasRecName }
  orInvalidAscFormat = or0 + 17;
    { While reading from .ASC file something went wrong. }
  orUnregisteredUser = or0 + 18;
    { Trying to unregister unregistered user of TULObj }
  orFieldNotFound = or0 + 19;
    { Property with specified name was not found in RTTI of the object }
  orEmptyPropInfo = or0 + 20;
    { Parameter APropInfo in some method (ULObjDes) was nil }
  orUpdateUnlockFailed = or0 + 21;
    { UpdateUnlock was called more times than UpdateLock }
  {v0.14}
  orNotULObject = or0 + 22;
    { Assigning value to field that should be pointer to ULObj, that is
      not pointer to such object  }
  orInvalidValuesSourceRecID = or0 + 23;
    { Setting ValuesSource value to pointer to other ULObj then
      specified in field definition }
  orNotULEnumField = or0 + 24;
  orValueSourceNil = or0 + 25;
  {/v0.14}
{/orXXXX}

{bmXXXX browse modes for ULBrowu browsers, specified in ULObjDes . TULObjDesc }
type
  TULBrowseMode = (
    bmShort,
    bmFull
  );
{/bmXXXX}
const
  MaxULBrowseCols = 40;
type
  TULBrowseColWidths = array[0..MaxULBrowseCols-1] of integer;

{v0.09}
type
  TULObjStateFlag = integer;
{osXXXX ULObj State flags}
const
  osModified = 1;
    { set if some property of the object modified since load or save }
  osReadOnly = 2;
    { object is opened readonly, changes won't be saved (useful for ULF object) }

{/v0.09}

implementation

end.
