home *** CD-ROM | disk | FTP | other *** search
/ Chip 2005 May / CMCD0505.ISO / Software / Shareware / Programare / pgedri / Source / Demos / PGEDemo / VCL / Reconcile.pas < prev   
Encoding:
Pascal/Delphi Source File  |  2005-04-01  |  11.8 KB  |  389 lines

  1. {*******************************************************************}
  2. {                                                                   }
  3. {       Vita Voom Software                                          }
  4. {       Reconcile.pas unit - PGEDemo Demo                           }
  5. {                                                                   }
  6. {       Copyright (c) 1998-2003 Vita Voom Software                  }
  7. {       ALL RIGHTS RESERVED                                         }
  8. {*******************************************************************}
  9. {                                                                   }
  10. { Please refer to the Readme.txt file for details.                  }
  11. {                                                                   }
  12.  
  13. {*******************************************************}
  14. {                                                       }
  15. {       Kylix Visual Component Library                  }
  16. {       ClientDataSet Standard Reconcile Error Dialog   }
  17. {                                                       }
  18. {       Copyright (c) 1998 Borland International        }
  19. {                                                       }
  20. {*******************************************************}
  21.  
  22. { Note: To use this dialog you should add a call to HandleReconcileError in
  23.   the OnReconcileError event handler of TClientDataSet (see the Client dataset
  24.   demos for an example).  Also, after adding this unit to your project you must
  25.   go into the Project Options dialog and remove this form from the list of
  26.   Auto-created forms or an error will occur when compiling. }
  27.  
  28. unit Reconcile;
  29.  
  30. interface
  31.  
  32. uses
  33.   {$IFDEF LINUX}
  34.     Libc,
  35.   {$ENDIF}
  36.   SysUtils, Variants, Classes, DB, DBClient, Provider,
  37.   {$IFDEF CLX}
  38.     QGraphics, QControls, QForms, QDialogs, QStdCtrls, QGrids, QExtCtrls;
  39.   {$ELSE}
  40.     Graphics, Controls, Forms, Dialogs, StdCtrls, Grids, ExtCtrls;
  41.   {$ENDIF}
  42.  
  43. const
  44.   ActionStr: array[TReconcileAction] of string = ('Skip', 'Abort', 'Merge',
  45.     'Correct', 'Cancel', 'Refresh');
  46.   UpdateKindStr: array[TUpdateKind] of string = ('Modified', 'Inserted',
  47.     'Deleted');
  48.   SCaption = 'Update Error - %s';
  49.   SUnchanged = '<Unchanged>';
  50.   SBinary = '(Binary)';
  51.   SAdt = '(ADT)';
  52.   SArray = '(Array)';
  53.   SFieldName = 'Field Name';
  54.   SOriginal = 'Original Value';
  55.   SConflict = 'Conflicting Value';
  56.   SValue = ' Value';
  57.   SNoData = '<No Records>';
  58.   SNew = 'New';
  59.  
  60. type
  61.   TReconcileErrorForm = class(TForm)
  62.     UpdateType: TLabel;
  63.     UpdateData: TStringGrid;
  64.     ActionGroup: TRadioGroup;
  65.     CancelBtn: TButton;
  66.     OKBtn: TButton;
  67.     ConflictsOnly: TCheckBox;
  68.     IconImage: TImage;
  69.     ErrorMsg: TMemo;
  70.     ChangedOnly: TCheckBox;
  71.     procedure FormCreate(Sender: TObject);
  72.     procedure FormDestroy(Sender: TObject);
  73.     procedure UpdateDataSetEditText(Sender: TObject; ACol, ARow: Integer;
  74.       const Value: string);
  75.     procedure DisplayFieldValues(Sender: TObject);
  76.     procedure UpdateDataSelectCell(Sender: TObject; Col, Row: Integer;
  77.       var CanSelect: Boolean);
  78.   private
  79.     FDataSet: TDataSet;
  80.     FError: EReconcileError;
  81.     FUpdateKind: TUpdateKind;
  82.     FDataFields: TList;
  83.     FCurColIdx: Integer;
  84.     FNewColIdx: Integer;
  85.     FOldColIdx: Integer;
  86.     procedure AdjustColumnWidths;
  87.     procedure InitDataFields;
  88.     procedure InitUpdateData(HasCurValues: Boolean);
  89.     procedure InitReconcileActions;
  90.     procedure SetFieldValues(DataSet: TDataSet);
  91.   public
  92.     constructor CreateForm(DataSet: TDataSet; UpdateKind: TUpdateKind;
  93.       Error: EReconcileError);
  94.   end;
  95.  
  96. function HandleReconcileError(DataSet: TDataSet;  UpdateKind: TUpdateKind;
  97.   ReconcileError: EReconcileError): TReconcileAction;
  98.  
  99. implementation
  100.  
  101. {$R *.dfm}
  102.  
  103. type
  104.   PFieldData = ^TFieldData;
  105.   TFieldData = record
  106.     Field: TField;
  107.     NewValue: string;
  108.     OldValue: string;
  109.     CurValue: string;
  110.     EditValue: string;
  111.     Edited: Boolean;
  112.   end;
  113.  
  114. { Public and Private Methods }
  115.  
  116. function HandleReconcileError(DataSet: TDataSet; UpdateKind: TUpdateKind;
  117.   ReconcileError: EReconcileError): TReconcileAction;
  118. var
  119.   UpdateForm: TReconcileErrorForm;
  120. begin
  121.   UpdateForm := TReconcileErrorForm.CreateForm(DataSet, UpdateKind, ReconcileError);
  122.   with UpdateForm do
  123.   try
  124.     if ShowModal = mrOK then
  125.     begin
  126.       Result := TReconcileAction(ActionGroup.Items.Objects[ActionGroup.ItemIndex]);
  127.       if Result = raCorrect then SetFieldValues(DataSet);
  128.     end else
  129.       Result := raAbort;
  130.   finally
  131.     Free;
  132.   end;
  133. end;
  134.  
  135. { Routine to convert a variant value into a string.
  136.   Handles binary fields types and "empty" (Unchanged) field values specially }
  137.  
  138. function VarToString(V: Variant; DataType: TFieldType): string;
  139. const
  140.   BinaryDataTypes: set of TFieldType = [ftBytes, ftVarBytes, ftBlob,
  141.     ftGraphic..ftCursor];
  142. begin
  143.   try
  144.     if VarIsClear(V) then
  145.       Result := SUnchanged
  146.     else if DataType in BinaryDataTypes then
  147.       Result := SBinary
  148.     else if DataType in [ftAdt] then
  149.       Result := SAdt
  150.     else if DataType in [ftArray] then
  151.       Result := SArray
  152.     else
  153.       Result := VarToStr(V);
  154.   except
  155.     on E: Exception do
  156.       Result := E.Message;
  157.   end;
  158. end;
  159.  
  160. { TReconcileErrorForm }
  161.  
  162. constructor TReconcileErrorForm.CreateForm(DataSet: TDataSet;
  163.   UpdateKind: TUpdateKind; Error: EReconcileError);
  164. begin
  165.   FDataSet := DataSet;
  166.   FUpdateKind := UpdateKind;
  167.   FError := Error;
  168.   inherited Create(Application);
  169. end;
  170.  
  171. { Create a list of the data fields in the dataset, and store string values
  172.   associated with NewValue, OldValue, and CurValue in string variables
  173.   to make display switching faster }
  174.  
  175. procedure TReconcileErrorForm.InitDataFields;
  176. var
  177.   I: Integer;
  178.   FD: PFieldData;
  179.   V: Variant;
  180.   HasCurValues: Boolean;
  181. begin
  182.   HasCurValues := False;
  183.   for I := 0 to FDataSet.FieldCount - 1 do
  184.   with FDataset.Fields[I] do
  185.   begin
  186.     if (FieldKind <> fkData) then Continue;
  187.     FD := New(PFieldData);
  188.     try
  189.       FD.Field := FDataset.Fields[I];
  190.       FD.Edited := False;
  191.       if FUpdateKind <> ukDelete then
  192.         FD.NewValue := VarToString(NewValue, DataType);
  193.       V := CurValue;
  194.       if not VarIsClear(V) then HasCurValues := True;
  195.       FD.CurValue := VarToString(CurValue, DataType);
  196.       if FUpdateKind <> ukInsert then
  197.         FD.OldValue := VarToString(OldValue, DataType);
  198.       FDataFields.Add(FD);
  199.     except
  200.       Dispose(FD);
  201.       raise;
  202.     end;
  203.   end;
  204.   InitUpdateData(HasCurValues);
  205. end;
  206.  
  207. { Initialize the column indexes and grid titles }
  208.  
  209. procedure TReconcileErrorForm.InitUpdateData(HasCurValues: Boolean);
  210. var
  211.   FColCount: Integer;
  212. begin
  213.   FColCount := 1;
  214.   UpdateData.ColCount := 4;
  215.   UpdateData.Cells[0,0] := SFieldName;
  216.   if FUpdateKind <> ukDelete then
  217.   begin
  218.     FNewColIdx := FColCount;
  219.     Inc(FColCount);
  220.     UpdateData.Cells[FNewColIdx,0] := UpdateKindStr[FUpdateKind] + SValue;
  221.   end else
  222.   begin
  223.     FOldColIdx := FColCount;
  224.     Inc(FColCount);
  225.     UpdateData.Cells[FOldColIdx,0] := SOriginal;
  226.   end;
  227.   if HasCurValues then
  228.   begin
  229.     FCurColIdx := FColCount;
  230.     Inc(FColCount);
  231.     UpdateData.Cells[FCurColIdx,0] := SConflict;
  232.   end;
  233.   if FUpdateKind = ukModify then
  234.   begin
  235.     FOldColIdx := FColCount;
  236.     Inc(FColCount);
  237.     UpdateData.Cells[FOldColIdx,0] := SOriginal;
  238.   end;
  239.   UpdateData.ColCount := FColCount;
  240. end;
  241.  
  242. { Update the reconcile action radio group based on the valid reconcile actions }
  243.  
  244. procedure TReconcileErrorForm.InitReconcileActions;
  245.  
  246.   procedure AddAction(Action: TReconcileAction);
  247.   begin
  248.     ActionGroup.Items.AddObject(ActionStr[Action], TObject(Action));
  249.   end;
  250.  
  251. begin
  252.   AddAction(raSkip);
  253.   AddAction(raCancel);
  254.   AddAction(raCorrect);
  255.   if FCurColIdx > 0 then
  256.   begin
  257.     AddAction(raRefresh);
  258.     AddAction(raMerge);
  259.   end;
  260.   ActionGroup.ItemIndex := 0;
  261. end;
  262.  
  263. { Update the grid based on the current display options }
  264.  
  265. procedure TReconcileErrorForm.DisplayFieldValues(Sender: TObject);
  266. var
  267.   I: Integer;
  268.   CurRow: Integer;
  269.   Action: TReconcileAction;
  270. begin
  271.   if not Visible then Exit;
  272.   Action := TReconcileAction(ActionGroup.Items.Objects[ActionGroup.ItemIndex]);
  273.   UpdateData.Col := 1;
  274.   UpdateData.Row := 1;
  275.   CurRow := 1;
  276.   UpdateData.RowCount := 2;
  277.   UpdateData.Cells[0, CurRow] := SNoData;
  278.   for I := 1 to UpdateData.ColCount - 1 do
  279.     UpdateData.Cells[I, CurRow] := '';
  280.   for I := 0 to FDataFields.Count - 1 do
  281.     with PFieldData(FDataFields[I])^ do
  282.     begin
  283.       if ConflictsOnly.Checked and (CurValue = SUnChanged) then Continue;
  284.       if ChangedOnly.Checked and (NewValue = SUnChanged) then Continue;
  285.       UpdateData.RowCount := CurRow + 1;
  286.       UpdateData.Cells[0, CurRow] := Field.DisplayName;
  287.       if FNewColIdx > 0 then
  288.       begin
  289.         case Action of
  290.           raCancel, raRefresh:
  291.             UpdateData.Cells[FNewColIdx, CurRow] := SUnChanged;
  292.           raCorrect:
  293.             if Edited then
  294.               UpdateData.Cells[FNewColIdx, CurRow] := EditValue else
  295.               UpdateData.Cells[FNewColIdx, CurRow] := NewValue;
  296.           else
  297.             UpdateData.Cells[FNewColIdx, CurRow] := NewValue;
  298.         end;
  299.         UpdateData.Objects[FNewColIdx, CurRow] := FDataFields[I];
  300.       end;
  301.       if FCurColIdx > 0 then
  302.         UpdateData.Cells[FCurColIdx, CurRow] := CurValue;
  303.       if FOldColIdx > 0 then
  304.         if (Action in [raMerge, raRefresh]) and (CurValue <> SUnchanged) then
  305.            UpdateData.Cells[FOldColIdx, CurRow] := CurValue else
  306.            UpdateData.Cells[FOldColIdx, CurRow] := OldValue;
  307.       Inc(CurRow);
  308.     end;
  309.   AdjustColumnWidths;
  310. end;
  311.  
  312. { For fields that the user has edited, copy the changes back into the
  313.   NewValue property of the associated field }
  314.  
  315. procedure TReconcileErrorForm.SetFieldValues(DataSet: TDataSet);
  316. var
  317.   I: Integer;
  318. begin
  319.   for I := 0 to FDataFields.Count - 1 do
  320.     with PFieldData(FDataFields[I])^ do
  321.       if Edited then Field.NewValue := EditValue;
  322. end;
  323.  
  324. procedure TReconcileErrorForm.AdjustColumnWidths;
  325. var
  326.   NewWidth, I: integer;
  327. begin
  328.   with UpdateData do
  329.   begin
  330.     NewWidth := (ClientWidth - ColWidths[0]) div (ColCount - 1);
  331.     for I := 1 to ColCount - 1 do
  332.       ColWidths[I] := NewWidth - 1;
  333.   end;
  334. end;
  335.  
  336. { Event handlers }
  337.  
  338. procedure TReconcileErrorForm.FormCreate(Sender: TObject);
  339. begin
  340.   if FDataSet = nil then Exit;
  341.   FDataFields := TList.Create;
  342.   InitDataFields;
  343.   Caption := Format(SCaption, [FDataSet.Name]);
  344.   UpdateType.Caption := UpdateKindStr[FUpdateKind];
  345.   ErrorMsg.Text := FError.Message;
  346.   if FError.Context <> '' then
  347.     ErrorMsg.Lines.Add(FError.Context);
  348.   ConflictsOnly.Enabled := FCurColIdx > 0;
  349.   ConflictsOnly.Checked := ConflictsOnly.Enabled;
  350.   ChangedOnly.Enabled := FNewColIdx > 0;
  351.   InitReconcileActions;
  352.   UpdateData.DefaultRowHeight := UpdateData.Canvas.TextHeight('SWgjp') + 7; { Do not localize }
  353. end;
  354.  
  355. procedure TReconcileErrorForm.FormDestroy(Sender: TObject);
  356. var
  357.   I: Integer;
  358. begin
  359.   if Assigned(FDataFields) then
  360.   begin
  361.     for I := 0 to FDataFields.Count - 1 do
  362.       Dispose(PFieldData(FDataFields[I]));
  363.     FDataFields.Destroy;
  364.   end;
  365. end;
  366.  
  367. { Set the Edited flag in the DataField list and save the value }
  368.  
  369. procedure TReconcileErrorForm.UpdateDataSetEditText(Sender: TObject; ACol,
  370.   ARow: Integer; const Value: string);
  371. begin
  372.   PFieldData(UpdateData.Objects[ACol, ARow]).EditValue := Value;
  373.   PFieldData(UpdateData.Objects[ACol, ARow]).Edited := True;
  374. end;
  375.  
  376. { Enable the editing in the grid if we are on the NewValue column and the
  377.   current reconcile action is raCorrect }
  378.  
  379. procedure TReconcileErrorForm.UpdateDataSelectCell(Sender: TObject; Col,
  380.   Row: Integer; var CanSelect: Boolean);
  381. begin
  382.   if (Col = FNewColIdx) and
  383.     (TReconcileAction(ActionGroup.Items.Objects[ActionGroup.ItemIndex]) = raCorrect) then
  384.     UpdateData.Options := UpdateData.Options + [goEditing] else
  385.     UpdateData.Options := UpdateData.Options - [goEditing];
  386. end;
  387.  
  388. end.
  389.