home *** CD-ROM | disk | FTP | other *** search
/ Chip 1999 January / Chip_1999-01_cd.bin / zkuste / delphi / QDB / QDBV.ZIP / QDBView.pas next >
Encoding:
Pascal/Delphi Source File  |  1998-07-29  |  53.3 KB  |  2,061 lines

  1.  
  2. {*****************************************************************************}
  3. {                                                                             }
  4. {          QDBView v2.11 Visual Components for Delphi 1, 2, & 3               }
  5. {                                                                             }
  6. {       Copyright (c) 1995, 1996, 1997, 1998 Robert R. Marsh, S.J.            }
  7. {             & the British Province of the Society of Jesus                  }
  8. {                                                                             }
  9. {              This source code may *not* be redistributed                    }
  10. {              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                    }
  11. {                                                                             }
  12. {       If you like QDBView and find yourself using it please consider            }
  13. {       making a donation to your favorite charity. I would also be           }
  14. {       pleased if you would acknowledge QDB in any projects that             }
  15. {       make use of it.                                                       }
  16. {                                                                             }
  17. {       QDBView is supplied as is. The author disclaims all warranties,           }
  18. {       expressed or implied, including, without limitation, the              }
  19. {       warranties of merchantability and of fitness for any purpose.         }
  20. {       The author assumes no liability for damages, direct or                }
  21. {       consequential, which may result from the use of QDBView.                  }
  22. {                                                                             }
  23. {                           rrm@sprynet.com                                   }
  24. {                  http://home.sprynet.com/sprynet/rrm                        }
  25. {                                                                             }
  26. {*****************************************************************************}
  27.  
  28.  
  29. {$F+}
  30.  
  31. unit QDBView;
  32.  
  33. interface
  34.  
  35. uses
  36. {$IFDEF WIN32}Windows, {$ELSE}WinTypes, WinProcs, {$ENDIF}
  37.   Messages, SysUtils, Classes, Graphics, Controls, Forms,
  38.   Dialogs, ExtCtrls, StdCtrls, QDB;
  39.  
  40. { The field type describes the format in which a field is }
  41. { stored in the QDB file. QDBItem's AsXXX properties need }
  42. { to know how data is stored so they can interpret it.    }
  43. {                                                         }
  44. {  ftunknown: not for use                                 }
  45. {  ftinteger: a single longint                            }
  46. {  ftintegers: several longints                           }
  47. {  ftreal: a floating point value of type extended        }
  48. {  ftboolean: a boolean value                             }
  49. {  ftdatetime: a Delphi TDateTime value                   }
  50. {  ftdate: TDateTime but just the date                    }
  51. {  fttime: TDateTime but just the time                    }
  52. {  ftstring: a string value (as pchar)                    }
  53. {  ftstrings: string list contents                        }
  54. {  ftrichstrings: richedit contents with formatting       }
  55. {  ftgraphic: a graphic                                   }
  56. {  ftthing: a generic lump of data, say from a stream     }
  57. type
  58.   TQDBFieldType = (ftunknown, ftinteger, ftintegers, ftreal,
  59.     ftboolean, ftdatetime, ftstring, ftstrings,
  60.     ftrichstrings, ftgraphic, ftthing, ftdate, fttime);
  61.  
  62. type
  63.   TKey = QDB.TKey;
  64.   TKeyEvent = procedure(Sender: TObject; var Key: TKey) of object;
  65.  
  66. type
  67.   EQDBItemError = class(EQDBError);
  68.   EQDBConvertError = class(EQDBItemError);
  69.   EQDBFieldError = class(EQDBItemError);
  70.   EQDBKeyError = class(EQDBItemError);
  71.   EQDBViewError = class(EQDBError);
  72.  
  73.   { QDBItem descends from TQDB and handles the parsing of an }
  74.   { item into fields. The field structure is defined by the  }
  75.   { AddField method or by loading the structure from a file  }
  76.   { using FetchStructure.                                    }
  77.   { Clear, Fetch, and Store handle items and individual      }
  78.   { fields can be accessed via the AsXXX[index] properties.  }
  79.   { The format strings govern the conversion of date/time &  }
  80.   { real values. An OnKey event handler must be assigned if  }
  81.   { items are to be stored.                                  }
  82. type
  83.   TQDBItem = class(TQDB)
  84.   private
  85.     CurrentItem: TMemoryStream;
  86.     Fields: TStringList;
  87.     FDateTimeFormatStr: string;
  88.     FRealFormatStr: string;
  89.     FOnKey: TKeyEvent;
  90.   protected
  91.     procedure DoCancel; override;
  92.     procedure DoDelete; override;
  93.     procedure DoEdit; override;
  94.     procedure DoInsert; override;
  95.     procedure DoPost; override;
  96.     function GetAsBoolean(Index: integer): boolean;
  97.     function GetAsDate(Index: integer): TDateTime;
  98.     function GetAsDateTime(Index: integer): TDateTime;
  99.     function GetAsInteger(Index: integer): longint;
  100.     function GetAsReal(Index: integer): extended;
  101.     function GetAsString(Index: integer): string;
  102.     function GetAsTime(Index: integer): TDateTime;
  103.     function GetBoolean(Index: integer): boolean;
  104.     function GetCount: integer;
  105.     function GetDate(Index: integer): TDateTime;
  106.     function GetDateTime(Index: integer): TDateTime;
  107.     function GetInteger(Index: integer): longint;
  108.     function GetName(Index: integer): string;
  109.     function GetReal(Index: integer): extended;
  110.     function GetString(Index: integer): string;
  111.     function GetStrings(Index: integer): string;
  112.     function GetTime(Index: integer): TDateTime;
  113.     function GetType(Index: integer): TQDBFieldType;
  114.     procedure SetAsBoolean(Index: integer; Value: boolean);
  115.     procedure SetAsDate(Index: integer; const Value: TDateTime);
  116.     procedure SetAsDateTime(Index: integer; Value: TDateTime);
  117.     procedure SetAsInteger(Index: integer; Value: longint);
  118.     procedure SetAsReal(Index: integer; Value: extended);
  119.     procedure SetAsString(Index: integer; const Value: string);
  120.     procedure SetAsTime(Index: integer; Value: TDateTime);
  121.     procedure SetBoolean(Index: integer; Value: boolean);
  122.     procedure SetDate(Index: integer; Value: TDateTime);
  123.     procedure SetDateTime(Index: integer; Value: TDateTime);
  124.     procedure SetField(Index: integer; Stream: TMemoryStream);
  125.     procedure SetInteger(Index: integer; Value: longint);
  126.     procedure SetReal(Index: integer; Value: extended);
  127.     procedure SetString(Index: integer; const Value: string);
  128.     procedure SetTime(Index: integer; Value: TDateTime);
  129.   public
  130.     constructor Create(AOwner: TComponent); override;
  131.     destructor Destroy; override;
  132.     procedure AddField(FieldName: string; FieldType: TQDBFieldType);
  133.     procedure Clear; virtual;
  134.     procedure ClearStructure;
  135.     procedure Fetch; virtual;
  136.     procedure ListFileFieldNames(Names: TStrings);
  137.     procedure FetchStructure;
  138.     function FieldIndex(const Name: string): integer;
  139.     procedure FirstItem; override;
  140.     function GetField(Index: integer): TMemoryStream;
  141.     procedure LastItem; override;
  142.     procedure NextItem; override;
  143.     procedure PrevItem; override;
  144.     procedure Refresh; override;
  145.     procedure Store; virtual;
  146.     procedure StoreAs(NewKey: TKey);
  147.     procedure StoreStructure;
  148.     property FieldCount: integer read GetCount;
  149.     property FieldNames[Index: integer]: string read GetName;
  150.     property FieldTypes[Index: integer]: TQDBFieldType read GetType;
  151.     property AsInteger[Index: integer]: longint read GetAsInteger write SetAsInteger;
  152.     property AsReal[Index: integer]: extended read GetAsReal write SetAsReal;
  153.     property AsBoolean[Index: integer]: boolean read GetAsBoolean write SetAsBoolean;
  154.     property AsString[Index: integer]: string read GetAsString write SetAsString;
  155.     property AsDate[Index: integer]: TDateTime read GetAsDate write SetAsDate;
  156.     property AsDateTime[Index: integer]: TDateTime read GetAsDateTime write SetAsDateTime;
  157.     property AsTime[Index: integer]: TDateTime read GetAsTime write SetAsTime;
  158.     property Editing;
  159.     property Inserting;
  160.   published
  161.     property AutoEdit;
  162.     property OnKey: TKeyEvent read FOnKey write FOnKey;
  163.     property DateTimeFormatStr: string read FDateTimeFormatStr write FDateTimeFormatStr;
  164.     property RealFormatStr: string read FRealFormatStr write FRealFormatStr;
  165.   end;
  166.  
  167. type
  168.   TClassProc = procedure(AControl: TControl; Stream: TStream);
  169.  
  170. type
  171.   TRegistrationInfo = record
  172.     AClass: TControlClass;
  173.     AType: TQDBFieldType;
  174.     ClearProc: TClassProc;
  175.     FetchProc: TClassProc;
  176.     StoreProc: TClassProc;
  177.   end;
  178.  
  179. type
  180.   TRegisteredControlList = class(TList)
  181.   public
  182.     constructor Create;
  183.     destructor Destroy; override;
  184.     function GetRegistrationInfo(Instance: TControl; var RegistrationInfo: TRegistrationInfo): boolean;
  185.     procedure RegisterControl(AClass: TControlClass; FieldType: TQDBFieldType; ClearProc, FetchProc, StoreProc: TClassProc);
  186.   end;
  187.  
  188.   { QDBView descends from TQDB via TQBItem and adds the ability  }
  189.   { to automate storage of data in controls on an associated     }
  190.   { panel. The most frequently used controls are registered by   }
  191.   { default but others can be added or existing behavior changed }
  192.   { if desired. The way the panel displays items is governed by  }
  193.   { the AutoEdit, ActiveColor, InactiveColor, and InactiveStyle  }
  194.   { properties.                                                  }
  195. type
  196.   TQDBView = class(TQDBItem)
  197.   private
  198.     RegisteredControls: TRegisteredControlList;
  199.     ControlList: TList;
  200.     FActiveColor: TColor;
  201.     FInactiveColor: TColor;
  202.     FActive: boolean;
  203.     FExcludeTag: longint;
  204.     FPanel: TCustomPanel;
  205.   protected
  206.     procedure BuildFieldList;
  207.     procedure DoCancel; override;
  208.     procedure DoDelete; override;
  209.     procedure DoEdit; override;
  210.     procedure DoInsert; override;
  211.     procedure DoPost; override;
  212.     function FileIsStructured: boolean;
  213.     function FileMatchesPanel: boolean;
  214.     procedure FocusFirstTab;
  215.     procedure Loaded; override;
  216.     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  217.     procedure SetActiveColor(Value: TColor);
  218.     procedure SetActive(Value: boolean);
  219.     procedure SetFileName(Value: TQDBFileName); override;
  220.     procedure SetInactiveColor(Value: TColor);
  221.     procedure SetPanel(Value: TCustomPanel);
  222.   public
  223.     constructor Create(AOwner: TComponent); override;
  224.     destructor Destroy; override;
  225.     function CheckStructure: boolean;
  226.     procedure Clear; override;
  227.     procedure ClearStructure;
  228.     procedure Fetch; override;
  229.     procedure FirstItem; override;
  230.     procedure LastItem; override;
  231.     procedure NextItem; override;
  232.     procedure PrevItem; override;
  233.     procedure RegisterControl(AClass: TControlClass; FieldType: TQDBFieldType; ClearProc, FetchProc, StoreProc: TClassProc);
  234.     procedure RegisterGraphicFormat(const AExtension: string; AGraphicClass: TGraphicClass);
  235.     procedure Refresh; override;
  236.     procedure Store; override;
  237.     procedure StoreStructure;
  238.   published
  239.     property ActiveColor: TColor read FActiveColor write SetActiveColor;
  240.     property ExcludeTag: longint read FExcludeTag write FExcludeTag;
  241.     property InactiveColor: TColor read FInactiveColor write SetInactiveColor;
  242.     property Panel: TCustomPanel read FPanel write SetPanel;
  243.   end;
  244.  
  245. function GetGraphicClass(ext: string): TGraphicClass;
  246.  
  247. implementation
  248.  
  249. uses
  250.   Clipbrd
  251. {$IFDEF WIN32}
  252.   , ComCtrls
  253. {$ENDIF};
  254.  
  255. const
  256.   SUnknownGraphic = 'unknown graphic class';
  257.   SFieldCorrupt = 'problem with fields';
  258.   SBadValue = '%s is not a valid value';
  259.   SInvalidFieldType = 'invalid field type';
  260.   SNoOnKey = 'Cannot store data unless an OnKey handler is assigned';
  261.   SBadMatch = 'File doesn''t match panel';
  262.   SNotStreamable = 'not streamable';
  263.   SBadFieldIndex = 'index doesn''t correspond to a field';
  264.  
  265.   { Default control handlers registered in TQDBView.Create }
  266.  
  267.   { To add your own you must pay attention to their structure. }
  268.   { Clear handlers simply have to put AControl into whatever   }
  269.   { passes as an empty state.                                  }
  270.   { Fetch handlers have to take the data in Stream and put it  }
  271.   { into AControl in an appropriate way. An empty Stream or an }
  272.   { error fetching the data should call the Clear handler.     }
  273.   { Store handlers just put AControl's data into Stream.       )
  274.   { In general the data should be stored as one of the formats }
  275.   { defined by TQDBFieldType.                                     }
  276.  
  277.   { ftstring }
  278.  
  279. procedure ClearCustomEdit(AControl: TControl; Stream: TStream);
  280. begin
  281.   (AControl as TCustomEdit).Text := '';
  282. end;
  283.  
  284. procedure FetchCustomEdit(AControl: TControl; Stream: TStream);
  285. var
  286.   con: TCustomEdit;
  287.   p: pchar;
  288.   Len: longint;
  289. begin
  290.   try
  291.     Len := Stream.size;
  292.     if Len = 0 then
  293.       Abort;
  294.     con := (AControl as TCustomEdit);
  295.     p := StrAlloc(Len);
  296.     try
  297.       Stream.ReadBuffer(p^, Len);
  298.       con.SetTextBuf(p);
  299.     finally
  300.       StrDispose(p);
  301.     end;
  302.   except
  303.     ClearCustomEdit(AControl, Stream);
  304.   end;
  305. end;
  306.  
  307. procedure StoreCustomEdit(AControl: TControl; Stream: TStream);
  308. var
  309.   con: TCustomEdit;
  310.   Len: longint;
  311.   p: pchar;
  312. begin
  313.   con := (AControl as TCustomEdit);
  314.   Len := con.GetTextLen + 1;
  315.   p := StrAlloc(Len);
  316.   try
  317.     con.GetTextBuf(p, Len);
  318.     Stream.Write(p^, Len);
  319.   finally
  320.     StrDispose(p);
  321.   end;
  322. end;
  323.  
  324. {$IFDEF WIN32}
  325. { ftrichstrings }
  326.  
  327. procedure ClearRichEdit(AControl: TControl; Stream: TStream);
  328. begin
  329.   (AControl as TRichEdit).Clear;
  330. end;
  331.  
  332. procedure FetchRichEdit(AControl: TControl; Stream: TStream);
  333. begin
  334.   try
  335.     (AControl as TRichEdit).Lines.LoadFromStream(Stream);
  336.   except
  337.     ClearRichEdit(AControl, Stream);
  338.   end;
  339. end;
  340.  
  341. procedure StoreRichEdit(AControl: TControl; Stream: TStream);
  342. begin
  343.   (AControl as TRichEdit).Lines.SaveToStream(Stream);
  344. end;
  345. {$ENDIF}
  346.  
  347. { ftinteger }
  348.  
  349. procedure ClearCustomRadioGroup(AControl: TControl; Stream: TStream);
  350. begin
  351.   TRadioGroup(AControl).ItemIndex := -1;
  352. end;
  353.  
  354. procedure FetchCustomRadioGroup(AControl: TControl; Stream: TStream);
  355. var
  356.   n: longint;
  357. begin
  358.   try
  359.     Stream.ReadBuffer(n, SizeOf(n));
  360.     TRadioGroup(AControl).ItemIndex := n;
  361.   except
  362.     ClearCustomRadioGroup(AControl, Stream)
  363.   end;
  364. end;
  365.  
  366. procedure StoreCustomRadioGroup(AControl: TControl; Stream: TStream);
  367. var
  368.   n: longint;
  369. begin
  370.   n := TRadioGroup(AControl).ItemIndex;
  371.   Stream.WriteBuffer(n, SizeOf(n));
  372. end;
  373.  
  374. { ftinteger }
  375.  
  376. procedure ClearCustomCheckBox(AControl: TControl; Stream: TStream);
  377. begin
  378.   TCheckBox(AControl).Checked := false;
  379. end;
  380.  
  381. procedure FetchCustomCheckBox(AControl: TControl; Stream: TStream);
  382. var
  383.   n: longint;
  384. begin
  385.   try
  386.     Stream.ReadBuffer(n, SizeOf(n));
  387.     TCheckBox(AControl).State := TCheckBoxState(n);
  388.   except
  389.     ClearCustomCheckBox(AControl, Stream)
  390.   end;
  391. end;
  392.  
  393. procedure StoreCustomCheckBox(AControl: TControl; Stream: TStream);
  394. var
  395.   n: longint;
  396. begin
  397.   n := longint(TCheckBox(AControl).State);
  398.   Stream.WriteBuffer(n, SizeOf(n));
  399. end;
  400.  
  401. { ftstring }
  402.  
  403. procedure ClearCustomComboBox(AControl: TControl; Stream: TStream);
  404. begin
  405.   (AControl as TCustomComboBox).SetTextBuf(nil);
  406. end;
  407.  
  408. procedure FetchCustomComboBox(AControl: TControl; Stream: TStream);
  409. var
  410.   p: pchar;
  411. begin
  412.   try
  413.     if Stream.size = 0 then
  414.       Abort;
  415.     p := StrAlloc(Stream.size);
  416.     try
  417.       Stream.ReadBuffer(p^, Stream.size);
  418.       (Acontrol as TCustomComboBox).SetTextBuf(p);
  419.     finally
  420.       StrDispose(p);
  421.     end;
  422.   except
  423.     ClearCustomComboBox(AControl, Stream)
  424.   end;
  425. end;
  426.  
  427. procedure StoreCustomComboBox(AControl: TControl; Stream: TStream);
  428. var
  429.   con: TCustomComboBox;
  430.   Len: longint;
  431.   p: pchar;
  432. begin
  433.   con := (AControl as TCustomComboBox);
  434.   Len := con.GetTextLen + 1;
  435.   p := StrAlloc(Len);
  436.   try
  437.     con.GetTextBuf(p, Len);
  438.     Stream.Write(p^, Len);
  439.   finally
  440.     StrDispose(p);
  441.   end;
  442. end;
  443.  
  444. type
  445.   THackListBox = class(TCustomListBox);
  446.  
  447.   { ftintegers }
  448.  
  449. procedure ClearCustomListBox(AControl: TControl; Stream: TStream);
  450. var
  451.   con: TCustomListBox;
  452.   n: longint;
  453.   ms: boolean;
  454. begin
  455.   con := (AControl as TCustomListBox);
  456.   ms := THackListBox(con).MultiSelect;
  457.   THackListBox(con).MultiSelect := true;
  458.   for n := 1 to con.Items.Count do
  459.   begin
  460.     con.Selected[n - 1] := false;
  461.   end;
  462.   THackListBox(con).MultiSElect := ms;
  463.   con.ItemIndex := -1;
  464. end;
  465.  
  466. procedure FetchCustomListBox(AControl: TControl; Stream: TStream);
  467. var
  468.   con: TCustomListBox;
  469.   n: longint;
  470.   ms: boolean;
  471. begin
  472.   ClearCustomListBox(AControl, Stream);
  473.   try
  474.     con := (AControl as TCustomListBox);
  475.     ms := THackListBox(con).MultiSelect;
  476.     THackListBox(con).MultiSelect := true;
  477.     while true do
  478.     begin
  479.       Stream.ReadBuffer(n, SizeOf(longint));
  480.       if (n < 0) or (n > con.Items.Count) then
  481.         Break;
  482.       con.Selected[n] := true;
  483.     end;
  484.     if con.SelCount <= 1 then
  485.       THackListBox(con).MultiSelect := ms;
  486.   except
  487.     ClearCustomListBox(AControl, Stream);
  488.   end;
  489. end;
  490.  
  491. procedure StoreCustomListBox(AControl: TControl; Stream: TStream);
  492. var
  493.   con: TCustomListBox;
  494.   n: longint;
  495.   i: longint;
  496. begin
  497.   con := (AControl as TCustomListBox);
  498.   for n := 1 to con.Items.Count do
  499.     if con.Selected[n - 1] then
  500.     begin
  501.       i := n - 1;
  502.       Stream.Write(i, SizeOf(longint));
  503.     end;
  504.   i := -1;
  505.   Stream.Write(i, SizeOf(longint));
  506. end;
  507.  
  508. type
  509.   PFileFormat = ^TFileFormat;
  510.   TFileFormat = record
  511.     GraphicClass: TGraphicClass;
  512.     Extension: string;
  513.     Next: PFileFormat;
  514.   end;
  515.  
  516. const
  517.   WMFMetafileFormat: TFileFormat = (
  518.     GraphicClass: TMetafile;
  519.     Extension: 'wmf';
  520.     Next: nil);
  521.   MetaFileFormat: TFileFormat = (
  522.     GraphicClass: TMetafile;
  523.     Extension: 'emf';
  524.     Next: @WMFMetaFileFormat);
  525.   IconFormat: TFileFormat = (
  526.     GraphicClass: TIcon;
  527.     Extension: 'ico';
  528.     Next: @MetafileFormat);
  529.   BitmapFormat: TFileFormat = (
  530.     GraphicClass: TBitmap;
  531.     Extension: 'bmp';
  532.     Next: @IconFormat);
  533.  
  534. const
  535.   FileFormatList: PFileFormat = @BitmapFormat;
  536.  
  537.   { ftgraphic }
  538.  
  539. procedure ClearImage(AControl: TControl; Stream: TStream);
  540. begin
  541.   (AControl as TImage).Picture.Assign(nil);
  542. end;
  543.  
  544. function GetGraphicClass(ext: string): TGraphicClass;
  545. var
  546.   Graphic: PFileFormat;
  547. begin
  548.   Result:=nil;
  549.   Graphic := FileFormatList;
  550.   while Graphic <> nil do
  551.   begin
  552.     with Graphic^ do
  553.     begin
  554.       if Extension <> Ext then
  555.         Graphic := Next
  556.       else
  557.       begin
  558.         Result:=GraphicClass;
  559.         Exit;
  560.       end;  
  561.     end;
  562.   end;
  563. end;
  564.  
  565. procedure FetchImage(AControl: TControl; Stream: TStream);
  566. var
  567.   con: TImage;
  568.   Ext: array[0..3] of char;
  569.   GraphicClass: TGraphicClass;
  570.   NewGraphic: TGraphic;
  571. begin
  572.   try
  573.     con := (AControl as TImage);
  574.     Ext[3] := #0;
  575.     Stream.ReadBuffer(Ext[0], 3);
  576.     GraphicClass:=GetGraphicClass(StrPas(Ext));
  577.     if GraphicClass <> nil then
  578.     begin
  579.           NewGraphic := GraphicClass.Create;
  580.           try
  581.             try
  582.               NewGraphic.LoadFromStream(Stream);
  583.             except
  584.               NewGraphic.Free;
  585.               raise;
  586.             end;
  587.             con.Picture.Graphic:=NewGraphic;
  588.             exit;
  589.           finally
  590.             NewGraphic.Free;
  591.           end;
  592.         end;
  593.   except
  594.     ClearImage(AControl, Stream);
  595.   end;
  596. end;
  597.  
  598. procedure StoreImage(AControl: TControl; Stream: TStream);
  599. var
  600.   con: TImage;
  601.   Ext: array[0..3] of char;
  602. begin
  603.   con := (AControl as TImage);
  604.   StrPCopy(Ext, LowerCase(GraphicExtension(TGraphicClass(con.Picture.Graphic.ClassType))));
  605.   if StrPas(Ext) <> '' then
  606.   begin
  607.     Stream.WriteBuffer(Ext[0], 3);
  608.     con.Picture.Graphic.SaveToStream(Stream);
  609.   end
  610.   else
  611.     raise Exception.Create(SUnknownGraphic);
  612. end;
  613.  
  614. { TQDBItem }
  615.  
  616. const
  617.   StructureIndicator = 'QF.';
  618.  
  619. const
  620.   {DateInDelphi2/3 := DateInDelphi1 - DateFiddle}
  621.   {All dates are stored in Delphi 2/3 format    }
  622.   DateFiddle: TDateTime = 693594.0;
  623.  
  624. type
  625.   TFieldInfo = class
  626.     TheData: TMemoryStream;
  627.     TheType: TQDBFieldType;
  628.   end;
  629.  
  630. constructor TQDBItem.Create(AOwner: TComponent);
  631. begin
  632.   inherited Create(AOwner);
  633.   CurrentItem := TMemoryStream.Create;
  634.   Fields := TStringList.Create;
  635.   Fields.Sorted := false;
  636.   RealFormatStr := '';
  637.   DateTimeFormatStr := 'c';
  638. end;
  639.  
  640. destructor TQDBItem.Destroy;
  641. var
  642.   n: integer;
  643.   i: integer;
  644. begin
  645.   CurrentItem.Free;
  646.   n := Fields.Count;
  647.   for i := n downto 1 do
  648.   begin
  649.     TFieldInfo(Fields.Objects[i - 1]).TheData.Free;
  650.     TFieldInfo(Fields.Objects[i - 1]).Free;
  651.     Fields.Delete(i - 1);
  652.   end;
  653.   Fields.Free;
  654.   inherited Destroy;
  655. end;
  656.  
  657. { Add a new field definition }
  658.  
  659. procedure TQDBItem.AddField(FieldName: string; FieldType: TQDBFieldType);
  660. var
  661.   i: TFieldInfo;
  662. begin
  663.   i := TFieldInfo.Create;
  664.   i.TheData := TMemoryStream.Create;
  665.   i.TheType := FieldType;
  666.   Fields.AddObject(FieldName, i);
  667. end;
  668.  
  669. { Call TQDB.DoCancel and reload the item }
  670.  
  671. procedure TQDBItem.DoCancel;
  672. begin
  673.   inherited DoCancel;
  674.   if Ready then
  675.   begin
  676.     Fetch;
  677.   end;
  678. end;
  679.  
  680. { Clear each field }
  681.  
  682. procedure TQDBItem.Clear;
  683. var
  684.   n: integer;
  685.   ThisField: TFieldInfo;
  686. begin
  687.   for n := 1 to Fields.Count do
  688.   begin
  689.     ThisField := TFieldInfo(Fields.Objects[n - 1]);
  690.     ThisField.TheData.Clear;
  691.   end;
  692. end;
  693.  
  694. { Remove all record of field structure from the file }
  695.  
  696. procedure TQDBItem.ClearStructure;
  697. begin
  698.   AdminClear(StructureIndicator);
  699. end;
  700.  
  701. { Call TQDB.DoDelete and either load the next item or clear it }
  702.  
  703. procedure TQDBItem.DoDelete;
  704. begin
  705.   if Ready then
  706.   begin
  707.     inherited DoDelete;
  708.     if Count > 0 then
  709.       Fetch
  710.     else
  711.       Clear;
  712.   end;
  713. end;
  714.  
  715. { Call TQDB.DoEdit, i.e., enter edit mode }
  716.  
  717. procedure TQDBItem.DoEdit;
  718. begin
  719.   inherited DoEdit;
  720. end;
  721.  
  722. { Load the current item and parse it into fields }
  723.  
  724. procedure TQDBItem.Fetch;
  725. var
  726.   n: integer;
  727.   ThisField: TFieldInfo;
  728.   Len: longint;
  729. begin
  730.   if Count > 0 then
  731.   begin
  732.     CurrentItem.Clear;
  733.     Get(CurrentItem);
  734.     for n := 1 to Fields.Count do
  735.     begin
  736.       ThisField := TFieldInfo(Fields.Objects[n - 1]);
  737.       ThisField.TheData.Clear;
  738.       CurrentItem.ReadBuffer(Len, SizeOf(Len));
  739.       if Len > 0 then
  740.         ThisField.TheData.CopyFrom(CurrentItem, Len);
  741.     end;
  742.     CurrentItem.Clear;
  743.   end;
  744. end;
  745.  
  746. { Fill a list with the field names as found in the file }
  747.  
  748. procedure TQDBItem.ListFileFieldNames(Names: TStrings);
  749. var
  750.   n: integer;
  751.   T: TStringList;
  752.   TheInfo: string;
  753.   TheName: string;
  754.   p: integer;
  755. begin
  756.   T := TStringList.Create;
  757.   try
  758.     AdminKeys(T, StructureIndicator);
  759.     for n := 1 to T.Count do
  760.     begin
  761.       TheInfo := AdminAsString[T[n - 1]];
  762.       TheName := TheInfo;
  763.       p := Pos('.', TheName);
  764.       if p = 2 then
  765.         System.Delete(TheName, 1, 2)
  766.       else
  767.         raise EQDBFieldError.Create(SFieldCorrupt);
  768.       p := Pos('.', TheName);
  769.       if p >= 1 then
  770.         System.Delete(TheName, p, Length(TheName));
  771.       Names.Add(TheName);
  772.     end;
  773.   finally
  774.     T.Free;
  775.   end;
  776. end;
  777.  
  778. { Load the field structure from the file }
  779.  
  780. procedure TQDBItem.FetchStructure;
  781. var
  782.   n: integer;
  783.   T: TStringList;
  784.   TheInfo: string;
  785.   TheName: string;
  786.   TheType: TQDBFieldType;
  787.   p: integer;
  788. begin
  789.   T := TStringList.Create;
  790.   try
  791.     Fields.Clear;
  792.     AdminKeys(T, StructureIndicator);
  793.     for n := 1 to T.Count do
  794.     begin
  795.       TheInfo := AdminAsString[T[n - 1]];
  796.       TheType := TQDBFieldType(ord(TheInfo[1]));
  797.       TheName := TheInfo;
  798.       p := Pos('.', TheName);
  799.       if p = 2 then
  800.         System.Delete(TheName, 1, 2)
  801.       else
  802.         raise EQDBFieldError.Create(SFieldCorrupt);
  803.       p := Pos('.', TheName);
  804.       if p >= 1 then
  805.         System.Delete(TheName, p, Length(TheName));
  806.       AddField(TheName, TheType);
  807.     end;
  808.   finally
  809.     T.Free;
  810.   end;
  811. end;
  812.  
  813. { Get the place in the field list from a field name }
  814.  
  815. function TQDBItem.FieldIndex(const Name: string): integer;
  816. { -1 indicates the field name doesn't exist }
  817. begin
  818.   Result := Fields.IndexOf(Name);
  819. end;
  820.  
  821. { Call TQDB.FirstItem and load the item }
  822.  
  823. procedure TQDBItem.FirstItem;
  824. begin
  825.   if Ready then
  826.   begin
  827.     inherited FirstItem;
  828.     Fetch;
  829.   end;
  830. end;
  831.  
  832. { The following routines convert a field's data from one }
  833. { format to another.                                     }
  834.  
  835. function StrToBoolean(S: string): boolean;
  836. begin
  837.   try
  838.     if s = '' then
  839.       Result := false
  840.     else
  841.       Result := (S[1] in ['Y', 'y', 'T', 't']);
  842.   except
  843.     raise EQDBConvertError.Create(SBadValue);
  844.   end;
  845. end;
  846.  
  847. function TQDBItem.GetAsBoolean(Index: integer): boolean;
  848. begin
  849.   try
  850.     case GetType(Index) of
  851.       ftinteger: Result := (GetInteger(Index) > 0);
  852.       ftboolean: Result := GetBoolean(Index);
  853.       ftstring: Result := StrToBoolean(GetString(Index));
  854.     else
  855.       raise EQDBFieldError.Create(SInvalidFieldType);
  856.     end;
  857.   except
  858.     on EConvertError do
  859.       raise EQDBConvertError.CreateFmt(SBadValue, [GetString(Index)]);
  860.   else
  861.     raise
  862.   end;
  863. end;
  864.  
  865. function TQDBItem.GetAsDate(Index: integer): TDateTime;
  866. begin
  867.   try
  868.     case GetType(Index) of
  869.       ftreal: Result := GetReal(Index);
  870.       ftstring: Result := StrToDateTime(GetString(Index));
  871.       ftdatetime: Result := int(GetDateTime(Index));
  872.       ftdate: Result:=  GetDate(Index);
  873.     else
  874.       raise EQDBFieldError.Create(SInvalidFieldType);
  875.     end;
  876.   except
  877.     on EConvertError do
  878.       raise EQDBConvertError.CreateFmt(SBadValue, [GetString(Index)]);
  879.   else
  880.     raise
  881.   end;
  882. end;
  883.  
  884. function TQDBItem.GetAsDateTime(Index: integer): TDateTime;
  885. begin
  886.   try
  887.     case GetType(Index) of
  888.       ftreal: Result := GetReal(Index);
  889.       ftstring: Result := StrToDateTime(GetString(Index));
  890.       ftdate: Result:= GetDate(Index);
  891.       ftdatetime: Result := GetDateTime(Index);
  892.       fttime: Result:=GetTime(Index);
  893.     else
  894.       raise EQDBFieldError.Create(SInvalidFieldType);
  895.     end;
  896.   except
  897.     on EConvertError do
  898.       raise EQDBConvertError.CreateFmt(SBadValue, [GetString(Index)]);
  899.   else
  900.     raise
  901.   end;
  902. end;
  903.  
  904. function TQDBItem.GetAsInteger(Index: integer): longint;
  905. begin
  906.   try
  907.     case GetType(Index) of
  908.       ftinteger: Result := GetInteger(Index);
  909.       ftreal: Result := Round(GetReal(Index));
  910.       ftdate: Result := Round(GetDate(Index));
  911.       ftdatetime: Result := Round(GetDateTime(Index));
  912.       ftboolean:
  913.         if GetBoolean(Index) then
  914.           Result := 1
  915.         else
  916.           Result := 0;
  917.       ftstring: Result := StrToInt(GetString(Index));
  918.     else
  919.       raise EQDBFieldError.Create(SInvalidFieldType);
  920.     end;
  921.   except
  922.     on EConvertError do
  923.       raise EQDBConvertError.CreateFmt(SBadValue, [GetString(Index)]);
  924.   else
  925.     raise
  926.   end;
  927. end;
  928.  
  929. function TQDBItem.GetAsReal(Index: integer): extended;
  930. begin
  931.   try
  932.     case GetType(Index) of
  933.       ftinteger: Result := GetInteger(Index);
  934.       ftreal: Result := GetReal(Index);
  935.       ftstring: Result := StrToFloat(GetString(Index));
  936.       ftdatetime: Result := GetDateTime(Index);
  937.       ftdate: Result := GetDate(Index);
  938.       fttime: Result := GetTime(Index);
  939.     else
  940.       raise EQDBFieldError.Create(SInvalidFieldType);
  941.     end;
  942.   except
  943.     on EConvertError do
  944.       raise EQDBConvertError.CreateFmt(SBadValue, [GetString(Index)]);
  945.   else
  946.     raise
  947.   end;
  948. end;
  949.  
  950. function TQDBItem.GetAsString(Index: integer): string;
  951. begin
  952.   try
  953.     Result := '';
  954.     case GetType(Index) of
  955.       ftinteger: Result := IntToStr(GetInteger(Index));
  956.       ftreal: Result := FormatFloat(RealFormatStr, GetReal(Index));
  957.       ftboolean:
  958.         if GetBoolean(Index) then
  959.           Result := 'TRUE'
  960.         else
  961.           Result := 'FALSE';
  962.       ftstring: Result := GetString(Index);
  963.       ftstrings: Result := GetStrings(Index);
  964.       ftdatetime: Result := FormatDateTime(DateTimeFormatStr, GetDateTime(Index));
  965.       ftdate: Result := FormatDateTime(DateTimeFormatStr, GetDate(Index));
  966.       fttime: Result := FormatDateTime(DateTimeFormatStr, GetTime(Index));
  967.     else
  968.       Result := 'N/A';
  969.     end;
  970.   except
  971.     on EConvertError do
  972.       raise EQDBConvertError.CreateFmt(SBadValue, [GetString(Index)]);
  973.   else
  974.     raise
  975.   end;
  976. end;
  977.  
  978. function TQDBItem.GetAsTime(Index: integer): TDateTime;
  979. begin
  980.   try
  981.     case GetType(Index) of
  982.       ftreal: Result := GetReal(Index);
  983.       ftstring: Result := StrToDateTime(GetString(Index));
  984.       ftdatetime: Result := frac(GetDateTime(Index));
  985.       fttime: Result:=  GetTime(Index);
  986.     else
  987.       raise EQDBFieldError.Create(SInvalidFieldType);
  988.     end;
  989.   except
  990.     on EConvertError do
  991.       raise EQDBConvertError.CreateFmt(SBadValue, [GetString(Index)]);
  992.   else
  993.     raise
  994.   end;
  995. end;
  996.  
  997. { The following routines convert a field's data to its native type }
  998.  
  999. function TQDBItem.GetBoolean(Index: integer): boolean;
  1000. var
  1001.   m: TMemoryStream;
  1002. begin
  1003.   Result := false;
  1004.   if GetType(Index) <> ftboolean then
  1005.     raise EQDBFieldError.Create(SFieldCorrupt);
  1006.   m := GetField(Index);
  1007.   if m.size = SizeOf(Result) then
  1008.     m.ReadBuffer(Result, m.size);
  1009.   m.Position := 0;
  1010. end;
  1011.  
  1012. function TQDBItem.GetCount: integer;
  1013. begin
  1014.   Result := Fields.Count;
  1015. end;
  1016.  
  1017. function TQDBItem.GetDate(Index: integer): TDateTime;
  1018. var
  1019.   m: TMemoryStream;
  1020. begin
  1021.   Result := 0;
  1022. {$IFNDEF WIN32}
  1023.   Result := Result + DateFiddle;
  1024. {$ENDIF}
  1025.   if GetType(Index) <> ftdate then
  1026.     raise EQDBFieldError.Create(SFieldCorrupt);
  1027.   m := GetField(Index);
  1028.   if m.size = SizeOf(Result) then
  1029.   begin
  1030.     m.ReadBuffer(Result, m.size);
  1031.     Result:=int(Result);
  1032. {$IFNDEF WIN32}
  1033.     Result := Result + DateFiddle;
  1034. {$ENDIF}
  1035.   end;
  1036.   m.Position := 0;
  1037. end;
  1038.  
  1039. function TQDBItem.GetDateTime(Index: integer): TDateTime;
  1040. var
  1041.   m: TMemoryStream;
  1042. begin
  1043.   Result := 0;
  1044. {$IFNDEF WIN32}
  1045.   Result := Result + DateFiddle;
  1046. {$ENDIF}
  1047.   if GetType(Index) <> ftdatetime then
  1048.     raise EQDBFieldError.Create(SFieldCorrupt);
  1049.   m := GetField(Index);
  1050.   if m.size = SizeOf(Result) then
  1051.   begin
  1052.     m.ReadBuffer(Result, m.size);
  1053. {$IFNDEF WIN32}
  1054.     Result := Result + DateFiddle;
  1055. {$ENDIF}
  1056.   end;
  1057.   m.Position := 0;
  1058. end;
  1059.  
  1060. { This function returns a pointer to the actual data }
  1061. { of a field so use it with care. Don't construct or }
  1062. { destroy the the variable you assign to and only    }
  1063. { modify the returned stream if you really mean to!  }
  1064. { You should also reset the data streams position to }
  1065.  { the beginning when you've finished with it.        }
  1066.  
  1067. function TQDBItem.GetField(Index: integer): TMemoryStream;
  1068. var
  1069.   ThisField: TFieldInfo;
  1070. begin
  1071.   if (Index >= 0) and (Index < Fields.Count) then
  1072.   begin
  1073.     ThisField := TFieldInfo(Fields.Objects[Index]);
  1074.     ThisField.TheData.Position := 0;
  1075.     Result := ThisField.TheData;
  1076.   end
  1077.   else
  1078.     raise EQDBFieldError.Create(SBadFieldIndex);
  1079. end;
  1080.  
  1081. function TQDBItem.GetInteger(Index: integer): longint;
  1082. var
  1083.   m: TMemoryStream;
  1084. begin
  1085.   Result := 0;
  1086.   if GetType(Index) <> ftinteger then
  1087.     raise EQDBFieldError.Create(SFieldCorrupt);
  1088.   m := GetField(Index);
  1089.   if m.size = SizeOf(Result) then
  1090.     m.ReadBuffer(Result, m.size);
  1091.   m.Position := 0;
  1092. end;
  1093.  
  1094. function TQDBItem.GetName(Index: integer): string;
  1095. begin
  1096.   if Index < Fields.Count then
  1097.     Result := Fields[Index]
  1098.   else
  1099.     Result := ''
  1100. end;
  1101.  
  1102. function TQDBItem.GetReal(Index: integer): extended;
  1103. var
  1104.   m: TMemoryStream;
  1105. begin
  1106.   Result := 0;
  1107.   if GetType(Index) <> ftreal then
  1108.     raise EQDBFieldError.Create(SFieldCorrupt);
  1109.   m := GetField(Index);
  1110.   if m.size = SizeOf(Result) then
  1111.     m.ReadBuffer(Result, m.size);
  1112.   m.Position := 0;
  1113. end;
  1114.  
  1115. function TQDBItem.GetString(Index: integer): string;
  1116. var
  1117.   m: TMemoryStream;
  1118.   p: pchar;
  1119. begin
  1120.   Result := '';
  1121.   if GetType(Index) <> ftstring then
  1122.     raise EQDBFieldError.Create(SFieldCorrupt);
  1123.   m := GetField(Index);
  1124.   if m.size > 0 then
  1125.   begin
  1126.     p := StrAlloc(m.size);
  1127.     try
  1128.       m.Read(p^, 80);  {// just get the first 80 chars}
  1129.       Result := StrPas(p);
  1130.     finally
  1131.       StrDispose(p);
  1132.     end;
  1133.   end;
  1134.   m.Position := 0;
  1135. end;
  1136.  
  1137. function TQDBItem.GetStrings(Index: integer): string;
  1138. var
  1139.   m: TMemoryStream;
  1140.   p: pchar;
  1141. begin
  1142.   Result := '';
  1143.   if GetType(Index) <> ftstrings then
  1144.     raise EQDBFieldError.Create(SFieldCorrupt);
  1145.   m := GetField(Index);
  1146.   if m.size > 0 then
  1147.   begin
  1148.     p := StrAlloc(m.size);
  1149.     try
  1150.       m.ReadBuffer(p^, m.size);
  1151.       Result := StrPas(p);
  1152.     finally
  1153.       StrDispose(p);
  1154.     end;
  1155.   end;
  1156.   m.Position := 0;
  1157. end;
  1158.  
  1159. function TQDBItem.GetTime(Index: integer): TDateTime;
  1160. var
  1161.   m: TMemoryStream;
  1162. begin
  1163.   Result := 0;
  1164. {$IFNDEF WIN32}
  1165.   Result := Result + DateFiddle;
  1166. {$ENDIF}
  1167.   if GetType(Index) <> fttime then
  1168.     raise EQDBFieldError.Create(SFieldCorrupt);
  1169.   m := GetField(Index);
  1170.   if m.size = SizeOf(Result) then
  1171.   begin
  1172.     m.ReadBuffer(Result, m.size);
  1173.     Result := frac(Result);
  1174.   end;
  1175.   m.Position := 0;
  1176. end;
  1177.  
  1178. function TQDBItem.GetType(Index: integer): TQDBFieldType;
  1179. begin
  1180.   if Index < Fields.Count then
  1181.     Result := TFieldInfo(Fields.Objects[Index]).TheType
  1182.   else
  1183.     Result := ftinteger;
  1184. end;
  1185.  
  1186. { Enter inserting mode }
  1187.  
  1188. procedure TQDBItem.DoInsert;
  1189. begin
  1190.   inherited DoInsert;
  1191.   Clear;
  1192. end;
  1193.  
  1194. { Call TQDB.LastItem and load the item }
  1195.  
  1196. procedure TQDBItem.LastItem;
  1197. begin
  1198.   if Ready then
  1199.   begin
  1200.     inherited LastItem;
  1201.     Fetch;
  1202.   end;
  1203. end;
  1204.  
  1205. { Call TQDB.NextItem and load the item }
  1206.  
  1207. procedure TQDBItem.NextItem;
  1208. begin
  1209.   if Ready then
  1210.   begin
  1211.     inherited NextItem;
  1212.     Fetch;
  1213.   end;
  1214. end;
  1215.  
  1216. { If we're editing or inserting Store the item }
  1217.  
  1218. procedure TQDBItem.DoPost;
  1219. begin
  1220.   if Ready then
  1221.   begin
  1222.     if Editing or Inserting then
  1223.     begin
  1224.       Store;
  1225.       inherited DoPost;
  1226.     end;
  1227.   end;
  1228. end;
  1229.  
  1230. { Call TQDB.PrevItem and load the item }
  1231.  
  1232. procedure TQDBItem.PrevItem;
  1233. begin
  1234.   if Ready then
  1235.   begin
  1236.     inherited PrevItem;
  1237.     Fetch;
  1238.   end;
  1239. end;
  1240.  
  1241. procedure TQDBItem.Refresh;
  1242. begin
  1243.   if Ready then
  1244.   begin
  1245.     Fetch;
  1246.   end;
  1247. end;
  1248.  
  1249. { The following routines Set a fields value converting as }
  1250. { necesary.                                               }
  1251.  
  1252. procedure TQDBItem.SetAsBoolean(Index: integer; Value: boolean);
  1253. begin
  1254.   case GetType(Index) of
  1255.     ftinteger:
  1256.       if Value then
  1257.         SetInteger(Index, 1)
  1258.       else
  1259.         SetInteger(Index, 0);
  1260.     ftboolean: SetBoolean(Index, Value);
  1261.     ftstring:
  1262.       if Value then
  1263.         SetString(Index, 'TRUE')
  1264.       else
  1265.         SetString(Index, 'FALSE');
  1266.   else
  1267.     raise EQDBFieldError.Create(SInvalidFieldType);
  1268.   end;
  1269. end;
  1270.  
  1271. procedure TQDBItem.SetAsDate(Index: integer; const Value: TDateTime);
  1272. begin
  1273.   case GetType(Index) of
  1274.     ftreal: SetReal(Index, Value);
  1275.     ftstring: SetString(Index, FormatDateTime(DateTimeFormatStr, Value));
  1276.     ftdate: SetDate(Index, value);
  1277.     ftdatetime: SetDateTime(Index, Value);
  1278.   else
  1279.     raise EQDBFieldError.Create(SInvalidFieldType);
  1280.   end;
  1281. end;
  1282.  
  1283. procedure TQDBItem.SetAsDateTime(Index: integer; Value: TDateTime);
  1284. begin
  1285.   case GetType(Index) of
  1286.     ftreal: SetReal(Index, Value);
  1287.     ftstring: SetString(Index, FormatDateTime(DateTimeFormatStr, Value));
  1288.     ftdate: SetDate(Index, value);
  1289.     ftdatetime: SetDateTime(Index, Value);
  1290.     fttime: SetTime(Index, Value);
  1291.   else
  1292.     raise EQDBFieldError.Create(SInvalidFieldType);
  1293.   end;
  1294. end;
  1295.  
  1296. procedure TQDBItem.SetAsInteger(Index: integer; Value: longint);
  1297. begin
  1298.   case GetType(Index) of
  1299.     ftdate: SetDate(Index, 1.0*Value);
  1300.     ftinteger: SetInteger(Index, Value);
  1301.     ftreal: SetReal(Index, Value);
  1302.     ftboolean: SetBoolean(Index, Value > 0);
  1303.     ftstring: SetString(Index, IntToStr(Value));
  1304.   else
  1305.     raise EQDBFieldError.Create(SInvalidFieldType);
  1306.   end;
  1307. end;
  1308.  
  1309. procedure TQDBItem.SetAsReal(Index: integer; Value: extended);
  1310. begin
  1311.   case GetType(Index) of
  1312.     ftinteger: SetInteger(Index, Round(Value));
  1313.     ftreal: SetReal(Index, Value);
  1314.     ftstring: SetString(Index, FormatFloat(RealFormatStr, Value));
  1315.     ftdate: SetDate(Index, Value);
  1316.     ftdatetime: SetDateTime(Index, Value);
  1317.     fttime: SetTime(Index, Value);
  1318.   else
  1319.     raise EQDBFieldError.Create(SInvalidFieldType);
  1320.   end;
  1321. end;
  1322.  
  1323. procedure TQDBItem.SetAsString(Index: integer; const Value: string);
  1324. begin
  1325.   case GetType(Index) of
  1326.     ftinteger: SetInteger(Index, StrToInt(Value));
  1327.     ftreal: SetReal(Index, StrToFloat(Value));
  1328.     ftboolean: SetBoolean(Index, StrToBoolean(Value));
  1329.     ftstring: SetString(Index, Value);
  1330.     ftdate: SetDate(Index, StrToDateTime(Value));
  1331.     ftdatetime: SetDateTime(Index, StrToDateTime(Value));
  1332.     fttime: SetTime(Index, StrToDateTime(Value));
  1333.   else
  1334.     { do nothing
  1335.      raise EQDBFieldError.Create(SInvalidFieldType);}
  1336.   end;
  1337. end;
  1338.  
  1339. procedure TQDBItem.SetAsTime(Index: integer; Value: TDateTime);
  1340. begin
  1341.   case GetType(Index) of
  1342.     ftreal: SetReal(Index, Value);
  1343.     ftstring: SetString(Index, FormatDateTime(DateTimeFormatStr, Value));
  1344.     fttime: SetTime(Index, value);
  1345.     ftdatetime: SetDateTime(Index, Value);
  1346.   else
  1347.     raise EQDBFieldError.Create(SInvalidFieldType);
  1348.   end;
  1349. end;
  1350.  
  1351. { The following routines set a field from its native type }
  1352.  
  1353. procedure TQDBItem.SetBoolean(Index: integer; Value: boolean);
  1354. var
  1355.   m: TMemoryStream;
  1356. begin
  1357.   m := GetField(Index);
  1358.   m.Clear;
  1359.   m.WriteBuffer(Value, SizeOf(Value));
  1360.   m.Position := 0;
  1361. end;
  1362.  
  1363. procedure TQDBItem.SetDate(Index: integer; Value: TDateTime);
  1364. var
  1365.   m: TMemoryStream;
  1366. begin
  1367.   m := GetField(Index);
  1368.   m.Clear;
  1369.   m.WriteBuffer(Value, SizeOf(Value));
  1370.   m.Position := 0;
  1371. end;
  1372.  
  1373. procedure TQDBItem.SetDateTime(Index: integer; Value: TDateTime);
  1374. var
  1375.   m: TMemoryStream;
  1376. begin
  1377.   m := GetField(Index);
  1378.   m.Clear;
  1379.   m.WriteBuffer(Value, SizeOf(Value));
  1380.   m.Position := 0;
  1381. end;
  1382.  
  1383. procedure TQDBItem.SetField(Index: integer; Stream: TMemoryStream);
  1384. var
  1385.   ThisData: TMemoryStream;
  1386. begin
  1387.   if (Index >= 0) and (Index < Fields.Count) then
  1388.   begin
  1389.     ThisData := TFieldInfo(Fields.Objects[Index]).TheData;
  1390.     ThisData.Clear;
  1391.     Stream.Position := 0;
  1392.     ThisData.CopyFrom(Stream, Stream.size);
  1393.     ThisData.Position := 0;
  1394.   end;
  1395. end;
  1396.  
  1397. procedure TQDBItem.SetInteger(Index: integer; Value: longint);
  1398. var
  1399.   m: TMemoryStream;
  1400. begin
  1401.   m := GetField(Index);
  1402.   m.Clear;
  1403.   m.WriteBuffer(Value, SizeOf(Value));
  1404.   m.Position := 0;
  1405. end;
  1406.  
  1407. procedure TQDBItem.SetReal(Index: integer; Value: extended);
  1408. var
  1409.   m: TMemoryStream;
  1410. begin
  1411.   m := GetField(Index);
  1412.   m.Clear;
  1413.   m.WriteBuffer(Value, SizeOf(Value));
  1414.   m.Position := 0;
  1415. end;
  1416.  
  1417. procedure TQDBItem.SetString(Index: integer; const Value: string);
  1418. var
  1419.   m: TMemoryStream;
  1420.   p: pchar;
  1421.   L: integer;
  1422. begin
  1423.   m := GetField(Index);
  1424.   L := Length(Value);
  1425.   p := StrAlloc(L + 1);
  1426.   try
  1427.     p[L] := #0;
  1428.     StrPCopy(p, Value);
  1429.     m.Clear;
  1430.     m.WriteBuffer(p^, L + 1);
  1431.     m.Position := 0;
  1432.   finally
  1433.     StrDispose(p);
  1434.   end;
  1435. end;
  1436.  
  1437. procedure TQDBItem.SetTime(Index: integer; Value: TDateTime);
  1438. var
  1439.   m: TMemoryStream;
  1440. begin
  1441.   m := GetField(Index);
  1442.   m.Clear;
  1443.   m.WriteBuffer(Value, SizeOf(Value));
  1444.   m.Position := 0;
  1445. end;
  1446.  
  1447. { Compile all the fields into an item and store it.     }
  1448. { An OnKey handler must have been assigned. The item is }
  1449. { added or changed as necessary.                        }
  1450.  
  1451. procedure TQDBItem.Store;
  1452. var
  1453.   n: integer;
  1454.   ThisField: TFieldInfo;
  1455.   Len: longint;
  1456.   NewKey: TKey;
  1457. begin
  1458.   CurrentItem.Clear;
  1459.   for n := 1 to Fields.Count do
  1460.   begin
  1461.     ThisField := TFieldInfo(Fields.Objects[n - 1]);
  1462.     ThisField.TheData.Position := 0;
  1463.     Len := ThisField.TheData.size;
  1464.     CurrentItem.WriteBuffer(Len, SizeOf(Len));
  1465.     CurrentItem.CopyFrom(ThisField.TheData, Len);
  1466.     ThisField.TheData.Position := 0;
  1467.   end;
  1468.   if Assigned(FOnKey) then
  1469.     FOnKey(Self, NewKey)
  1470.   else
  1471.     raise EQDBKeyError.Create(SNoOnKey);
  1472.   if ExactMatch(NewKey) then
  1473.     Change(CurrentItem)
  1474.   else
  1475.   begin
  1476.     if not Inserting then
  1477.       inherited DoDelete;
  1478.     Add(CurrentItem, NewKey);
  1479.   end;
  1480. end;
  1481.  
  1482. { Like Store only bypassing the OnKey handler. Will }
  1483. { only store new items.                             }
  1484.  
  1485. procedure TQDBItem.StoreAs(NewKey: TKey);
  1486. var
  1487.   n: integer;
  1488.   ThisField: TFieldInfo;
  1489.   Len: longint;
  1490. begin
  1491.   CurrentItem.Clear;
  1492.   for n := 1 to Fields.Count do
  1493.   begin
  1494.     ThisField := TFieldInfo(Fields.Objects[n - 1]);
  1495.     ThisField.TheData.Position := 0;
  1496.     Len := ThisField.TheData.size;
  1497.     CurrentItem.WriteBuffer(Len, SizeOf(Len));
  1498.     CurrentItem.CopyFrom(ThisField.TheData, Len);
  1499.     ThisField.TheData.Position := 0;
  1500.   end;
  1501.   if NewKey <> '' then
  1502.   begin
  1503.     if not ExactMatch(NewKey) then
  1504.       Add(CurrentItem, NewKey);
  1505.   end;
  1506. end;
  1507.  
  1508. { Stores the current field defintions in the file }
  1509.  
  1510. procedure TQDBItem.StoreStructure;
  1511. var
  1512.   n: integer;
  1513.   StructureInfo: string;
  1514. begin
  1515.   ClearStructure;
  1516.   for n := 1 to FieldCount do
  1517.     with TFieldInfo(Fields.Objects[n - 1]) do
  1518.     begin
  1519.       StructureInfo := chr(Byte(TheType)) + '.' + Fields[n - 1];
  1520.       AdminAsString[Format('%s%.4d', [StructureIndicator, n - 1])] := StructureInfo;
  1521.     end;
  1522. end;
  1523.  
  1524. { TRegisteredControlList }
  1525.  
  1526. { The reference list of what controls have been registered and }
  1527. { how to use them.                                             }
  1528.  
  1529. constructor TRegisteredControlList.Create;
  1530. begin
  1531.   inherited Create;
  1532. end;
  1533.  
  1534. destructor TRegisteredControlList.Destroy;
  1535. var
  1536.   n: integer;
  1537. begin
  1538.   for n := 1 to Count do
  1539.     FreeMem(Items[n - 1], SizeOf(TRegistrationInfo));
  1540.   inherited Destroy;
  1541. end;
  1542.  
  1543. function TRegisteredControlList.GetRegistrationInfo(Instance: TControl; var RegistrationInfo: TRegistrationInfo): boolean;
  1544. var
  1545.   n: integer;
  1546. begin
  1547.   Result := false;
  1548.   for n := Count downto 1 do
  1549.   begin
  1550.     if Instance.InheritsFrom(TRegistrationInfo(Items[n - 1]^).AClass) then
  1551.     begin
  1552.       RegistrationInfo := TRegistrationInfo(Items[n - 1]^);
  1553.       Result := true;
  1554.       exit;
  1555.     end;
  1556.   end;
  1557. end;
  1558.  
  1559. { For a control on a panel to be recognized by QDBView it must  }
  1560. { have been registered via ths method.                           }
  1561. { Registration associates with a class of control the procedures }
  1562. { to clear, fetch, and store it.                                 }
  1563.  
  1564. procedure TRegisteredControlList.RegisterControl(AClass: TControlClass; FieldType: TQDBFieldType;
  1565.   ClearProc, FetchProc, StoreProc: TClassProc);
  1566. var
  1567.   i: ^TRegistrationInfo;
  1568. begin
  1569.   GetMem(i, SizeOf(TRegistrationInfo));
  1570.   i^.AClass := AClass;
  1571.   i^.AType := FieldType;
  1572.   @i^.ClearProc := @ClearProc;
  1573.   @i^.FetchProc := @FetchProc;
  1574.   @i^.StoreProc := @StoreProc;
  1575.   Add(i);
  1576. end;
  1577.  
  1578.  
  1579. { TQDBView }
  1580.  
  1581. type
  1582.   TControlInfo = class
  1583.     TheControl: TControl;
  1584.     ClearProc: TClassProc;
  1585.     FetchProc: TClassProc;
  1586.     StoreProc: TClassProc;
  1587.   end;
  1588.  
  1589. constructor TQDBView.Create(AOwner: TComponent);
  1590. begin
  1591.   inherited Create(AOwner);
  1592.   { the actual control info augmenting the field info in QDBItem }
  1593.   ControlList := TList.Create;
  1594.   { controls with this tag are not counted as fields }
  1595.   ExcludeTag := -999;
  1596.   { register the defualt control classes }
  1597.   RegisteredControls := TRegisteredControlList.Create;
  1598.   RegisterControl(TCustomEdit, ftstring, ClearCustomEdit, FetchCustomEdit, StoreCustomEdit);
  1599. {$IFDEF WIN32}
  1600.   RegisterControl(TRichEdit, ftrichstrings, ClearRichEdit, FetchRichEdit, StoreRichEdit);
  1601. {$ENDIF}
  1602.   RegisterControl(TCustomRadioGroup, ftinteger, ClearCustomRadioGroup, FetchCustomRadioGroup, StoreCustomRadioGroup);
  1603.   RegisterControl(TCustomCheckBox, ftinteger, ClearCustomCheckBox, FetchCustomCheckBox, StoreCustomCheckBox);
  1604.   RegisterControl(TCustomComboBox, ftstring, ClearCustomComboBox, FetchCustomComboBox, StoreCustomComboBox);
  1605.   RegisterControl(TCustomListBox, ftintegers, ClearCustomListBox, FetchCustomListBox, StoreCustomListBox);
  1606.   RegisterControl(TImage, ftgraphic, ClearImage, FetchImage, StoreImage);
  1607.   FActive := false;
  1608.   FActiveColor := clWindow;
  1609.   FInactiveColor := clSilver;
  1610. end;
  1611.  
  1612. destructor TQDBView.Destroy;
  1613. var
  1614.   n: integer;
  1615.   i: integer;
  1616. begin
  1617.   n := ControlList.Count;
  1618.   for i := n downto 1 do
  1619.   begin
  1620.     TControlInfo(ControlList[i - 1]).Free;
  1621.     ControlList.Delete(i - 1);
  1622.   end;
  1623.   ControlList.Free;
  1624.   RegisteredControls.Free;
  1625.   inherited Destroy;
  1626. end;
  1627.  
  1628. { Checks each control on the associated panel and as they are }
  1629. { recognized stores the info in the field and control lists.  }
  1630.  
  1631. procedure TQDBView.BuildFieldList;
  1632. var
  1633.   RegistrationInfo: TRegistrationInfo;
  1634.   NewProcs: TControlInfo;
  1635.  
  1636.   procedure ForEachControl(AWinControl: TWinControl); forward;
  1637.  
  1638.   procedure ProcessControl(ThisControl: TControl);
  1639.   begin
  1640.     { do nothing with a control if the tag is set to exclude them }
  1641.     if (ThisControl.Tag = ExcludeTag) then
  1642.       exit;
  1643.     { if the control owns others  ... }
  1644.     if (ThisControl is TWinControl) and (TWinControl(ThisControl).ControlCount <> 0) then
  1645.       ForEachControl(TWinControl(ThisControl))
  1646.     else
  1647.       if RegisteredControls.GetRegistrationInfo(ThisControl, RegistrationInfo) then
  1648.       begin
  1649.         AddField(ThisControl.Name, RegistrationInfo.AType);
  1650.         NewProcs := TControlInfo.Create;
  1651.         NewProcs.TheControl := ThisControl;
  1652.         @NewProcs.ClearProc := @RegistrationInfo.ClearProc;
  1653.         @NewProcs.FetchProc := @RegistrationInfo.FetchProc;
  1654.         @NewProcs.StoreProc := @RegistrationInfo.StoreProc;
  1655.         ControlList.Add(NewProcs);
  1656.       end;
  1657.   end;
  1658.  
  1659.   procedure ForEachControl(AWinControl: TWinControl);
  1660.   var
  1661.     n: integer;
  1662.   begin
  1663. {$IFDEF WIN32}
  1664.     if (AWinControl is TPageControl) then
  1665.       with (AWinControl as TPageControl) do
  1666.         for n := 1 to PageCount do
  1667.           ProcessControl(Pages[n - 1])
  1668.     else
  1669. {$ENDIF}
  1670.       for n := 1 to AWinControl.ControlCount do
  1671.         ProcessControl(AWinControl.Controls[n - 1]);
  1672.   end;
  1673.  
  1674. begin
  1675.   if FPanel <> nil then
  1676.   begin
  1677.     Fields.Clear;
  1678.     ControlList.Clear;
  1679.     ForEachControl(FPanel);
  1680.   end;
  1681. end;
  1682.  
  1683. procedure TQDBView.DoCancel;
  1684. begin
  1685.   if not AutoEdit then
  1686.     SetActive(false);
  1687.   if Ready then
  1688.   begin
  1689.     inherited DoCancel;
  1690.   end;
  1691. end;
  1692.  
  1693. { Checks the field structure as defined by the associated panel }
  1694. { against the field structure as stored in the associated file. }
  1695.  
  1696. function TQDBView.CheckStructure: boolean;
  1697. var
  1698.   n: integer;
  1699.   StructureInfo: string;
  1700. begin
  1701.   Result := false;
  1702.   for n := 1 to FieldCount do
  1703.   begin
  1704.     Result := true;
  1705.     with TFieldInfo(Fields.Objects[n - 1]) do
  1706.       with TControlInfo(ControlList[n - 1]) do
  1707.       begin
  1708.         StructureInfo := chr(Byte(TheType)) + '.' + Fields[n - 1] + '.' + TheControl.ClassName;
  1709.         if not ((AdminKeyExists(Format('%s%.4d', [StructureIndicator, n - 1])))
  1710.           and (AdminAsString[Format('%s%.4d', [StructureIndicator, n - 1])] = StructureInfo)) then
  1711.         begin
  1712.           Result := false;
  1713.           exit;
  1714.         end;
  1715.       end;
  1716.   end;
  1717. end;
  1718.  
  1719. procedure TQDBView.Clear;
  1720. var
  1721.   n: integer;
  1722. begin
  1723.   inherited Clear;
  1724.   for n := 1 to FieldCount do
  1725.     with TControlInfo(ControlList[n - 1]) do
  1726.       ClearProc(TheControl, nil);
  1727. end;
  1728.  
  1729. { REmoves all record of the field structure from the file }
  1730.  
  1731. procedure TQDBView.ClearStructure;
  1732. begin
  1733.   AdminClear(StructureIndicator);
  1734. end;
  1735.  
  1736. procedure TQDBView.DoDelete;
  1737. begin
  1738.   if Ready then
  1739.   begin
  1740.     if not AutoEdit then
  1741.       SetActive(false);
  1742.     inherited DoDelete;
  1743.   end;
  1744. end;
  1745.  
  1746. procedure TQDBView.DoEdit;
  1747. begin
  1748.   if Ready then
  1749.   begin
  1750.     SetActive(true);
  1751.     Refresh; { ??? }
  1752.     inherited DoEdit;
  1753.   end;
  1754. end;
  1755.  
  1756. { Calls TQDBItem.Fetch to load and parse the item and then }
  1757. { displays the fields in the controls.                     }
  1758.  
  1759. procedure TQDBView.Fetch;
  1760. var
  1761.   n: integer;
  1762. begin
  1763.   inherited Fetch;
  1764.   if Count > 0 then
  1765.   begin
  1766.     for n := 1 to FieldCount do
  1767.       with TControlInfo(ControlList[n - 1]) do
  1768.         with TFieldInfo(Fields.Objects[n - 1]) do
  1769.         begin
  1770.           TheData.Position := 0;
  1771.           FetchProc(TheControl, TheData);
  1772.           TheData.Position := 0;
  1773.         end;
  1774.     FocusFirstTab;
  1775.   end;
  1776. end;
  1777.  
  1778. { Checks if the file contains field definitions }
  1779.  
  1780. function TQDBView.FileIsStructured: boolean;
  1781. begin
  1782.   Result := AdminKeyExists(Format('%s%.4d', [StructureIndicator, 0]));
  1783. end;
  1784.  
  1785. { If the file isn't structured or if it empty the new structure }
  1786. { is stored in the file, otherwise the structrue is checked.    }
  1787.  
  1788. function TQDBView.FileMatchesPanel: boolean;
  1789. begin
  1790.   Result := true;
  1791.   if Ready and (FPanel <> nil) then
  1792.   begin
  1793.     if not FileIsStructured then
  1794.       StoreStructure
  1795.     else
  1796.     begin
  1797.       if not CheckStructure then
  1798.       begin
  1799.         if Count > 0 then
  1800.         begin
  1801.           Result := false;
  1802.         end
  1803.         else
  1804.         begin
  1805.           StoreStructure;
  1806.         end;
  1807.       end;
  1808.     end;
  1809.   end;
  1810. end;
  1811.  
  1812. procedure TQDBView.FirstItem;
  1813. begin
  1814.   if Ready then
  1815.   begin
  1816.     inherited FirstItem;
  1817.   end;
  1818. end;
  1819.  
  1820. { Puts the focus on the first control in the tab order }
  1821. type
  1822.   TWinControlHack = class(TWinControl);
  1823.  
  1824. procedure TQDBView.FocusFirstTab;
  1825. var
  1826.   First: TWinControl;
  1827. begin
  1828.   if FActive and (FPanel <> nil) then
  1829.   begin
  1830.     First := TWinControlHack(FPanel).FindNextControl(nil, true, true, false);
  1831.     if (First <> nil) then
  1832.       First.SetFocus;
  1833.   end;
  1834. end;
  1835.  
  1836. procedure TQDBView.DoInsert;
  1837. begin
  1838.   if Ready then
  1839.   begin
  1840.     SetActive(true);
  1841.     FocusFirstTab;
  1842.     inherited DoInsert;
  1843.   end;
  1844. end;
  1845.  
  1846. procedure TQDBView.LastItem;
  1847. begin
  1848.   if Ready then
  1849.   begin
  1850.     inherited LastItem;
  1851.   end;
  1852. end;
  1853.  
  1854. procedure TQDBView.Loaded;
  1855. var
  1856.   n: integer;
  1857.   c: TWinControl;
  1858. begin
  1859.   if FPanel = nil then
  1860.     exit;
  1861.   for n := 1 to FieldCount do
  1862.   begin
  1863.     c := TWinControl(TControlInfo(ControlList[n - 1]).TheControl);
  1864.     PostMessage(c.Handle, em_setReadOnly, integer(not FActive), 0);
  1865.   end;
  1866. end;
  1867.  
  1868. procedure TQDBView.NextItem;
  1869. begin
  1870.   if Ready then
  1871.   begin
  1872.     inherited NextItem;
  1873.   end;
  1874. end;
  1875.  
  1876. { Makessure that if the associated panel is removed its reference }
  1877. { is also removed.                                                }
  1878.  
  1879. procedure TQDBView.Notification(AComponent: TComponent; Operation: TOperation);
  1880. begin
  1881.   inherited Notification(AComponent, Operation);
  1882.   if (FPanel <> nil) and
  1883.     (AComponent = FPanel) and
  1884.     (Operation = opRemove) then
  1885.   begin
  1886.     FPanel := nil;
  1887.   end;
  1888. end;
  1889.  
  1890. procedure TQDBView.DoPost;
  1891. begin
  1892.   if Ready then
  1893.   begin
  1894.     if FActive then
  1895.       inherited DoPost;
  1896.     if not AutoEdit then
  1897.       SetActive(false);
  1898.   end;
  1899. end;
  1900.  
  1901. procedure TQDBView.PrevItem;
  1902. begin
  1903.   if Ready then
  1904.   begin
  1905.     inherited PrevItem;
  1906.   end;
  1907. end;
  1908.  
  1909. procedure TQDBView.Refresh;
  1910. begin
  1911.   if Ready then
  1912.   begin
  1913.     inherited Refresh;
  1914.   end;
  1915. end;
  1916.  
  1917. procedure TQDBView.RegisterControl(AClass: TControlClass; FieldType: TQDBFieldType;
  1918.   ClearProc, FetchProc, StoreProc: TClassProc);
  1919. begin
  1920.   RegisteredControls.RegisterControl(AClass, FieldType, ClearProc, FetchProc, StoreProc);
  1921. end;
  1922.  
  1923. { By default Delphi recognizes bmp, wmf, and ico formats but }
  1924. { others (like jpg) can be registered via the Graphics unit. }
  1925. { If you want QDBView to also handle them they must be      }
  1926. { registered here too.                                       }
  1927.  
  1928. procedure TQDBView.RegisterGraphicFormat(const AExtension: string; AGraphicClass: TGraphicClass);
  1929. var
  1930.   NewRec: PFileFormat;
  1931. begin
  1932.   New(NewRec);
  1933.   with NewRec^ do
  1934.   begin
  1935.     Extension := LowerCase(AExtension);
  1936.     GraphicClass := AGraphicClass;
  1937.     Next := FileFormatList;
  1938.   end;
  1939.   FileFormatList := NewRec;
  1940. end;
  1941.  
  1942. { The color of a control's background when editing or inserting }
  1943.  
  1944. procedure TQDBView.SetActiveColor(Value: TColor);
  1945. begin
  1946.   if FActiveColor <> Value then
  1947.   begin
  1948.     FActiveColor := Value;
  1949.     SetActive(FActive);
  1950.   end;
  1951. end;
  1952.  
  1953. type
  1954.   THackControl = class(TControl);
  1955.  
  1956.   { Active covers both editing and inserting. }
  1957.  
  1958. procedure TQDBView.SetActive(Value: boolean);
  1959. var
  1960.   n: integer;
  1961.   c: THackControl;
  1962. begin
  1963.   FActive := Value;
  1964.   if FPanel = nil then
  1965.     exit;
  1966.   LockWindowUpdate(FPanel.Handle);
  1967.   try
  1968.     for n := 1 to FieldCount do
  1969.     begin
  1970.       c := THackControl(TControlInfo(ControlList[n - 1]).TheControl);
  1971.       if Value then
  1972.       begin
  1973.         c.Color := ActiveColor;
  1974.         c.Perform(em_setReadOnly, 0, 0);
  1975.       end
  1976.       else
  1977.       begin
  1978.         c.Color := InactiveColor;
  1979.         c.Perform(em_setReadOnly, 1, 0);
  1980.       end;
  1981.     end;
  1982.   finally
  1983.     LockWindowUpdate(0);
  1984.   end;
  1985. end;
  1986.  
  1987. { Note that the structure-check only occurs at run-time }
  1988.  
  1989. procedure TQDBView.SetFileName(Value: TQDBFileName);
  1990. begin
  1991.   inherited SetFileName(Value);
  1992.   if Ready and (Count > 0) then
  1993.     FirstItem;
  1994.   if not FileMatchesPanel then
  1995.   begin
  1996.     SetFileName('');
  1997.     raise EQDBViewError.Create(SBadMatch);
  1998.   end;
  1999. end;
  2000.  
  2001. procedure TQDBView.SetInactiveColor(Value: TColor);
  2002. begin
  2003.   if FInactiveColor <> Value then
  2004.   begin
  2005.     FInactiveColor := Value;
  2006.     SetActive(FActive);
  2007.   end;
  2008. end;
  2009.  
  2010. { Sets the associated panel and rebuilds the field and  }
  2011. { control lists.                                        }
  2012.  
  2013. procedure TQDBView.SetPanel(Value: TCustomPanel);
  2014. begin
  2015.   FPanel := Value;
  2016.   BuildFieldList;
  2017.   if Ready and (Count > 0) then
  2018.     FirstItem;
  2019.   SetActive(AutoEdit);
  2020.   if not FileMatchesPanel then
  2021.   begin
  2022.     SetPanel(nil);
  2023.     raise EQDBViewError.Create(SBadMatch);
  2024.   end;
  2025. end;
  2026.  
  2027. procedure TQDBView.Store;
  2028. var
  2029.   n: integer;
  2030. begin
  2031.   for n := 1 to FieldCount do
  2032.     with TControlInfo(ControlList[n - 1]) do
  2033.       with TFieldInfo(Fields.Objects[n - 1]) do
  2034.       begin
  2035.         TheData.Clear;
  2036.         StoreProc(TheControl, TheData);
  2037.         TheData.Position := 0;
  2038.       end;
  2039.   inherited Store;
  2040. end;
  2041.  
  2042. procedure TQDBView.StoreStructure;
  2043. var
  2044.   n: integer;
  2045.   StructureInfo: string;
  2046. begin
  2047.   if ReadOnly then
  2048.     Exit;
  2049.   ClearStructure;
  2050.   for n := 1 to FieldCount do
  2051.     with TFieldInfo(Fields.Objects[n - 1]) do
  2052.       with TControlInfo(ControlList[n - 1]) do
  2053.       begin
  2054.         StructureInfo := chr(Byte(TheType)) + '.' + Fields[n - 1] + '.' + TheControl.ClassName;
  2055.         AdminAsString[Format('%s%.4d', [StructureIndicator, n - 1])] := StructureInfo;
  2056.       end;
  2057. end;
  2058.  
  2059. end.
  2060.  
  2061.