home *** CD-ROM | disk | FTP | other *** search
/ Delphi Anthology / aDELPHI.iso / Runimage / Delphi50 / Objrepos / recerror.pas < prev    next >
Pascal/Delphi Source File  |  1999-08-11  |  11KB  |  365 lines

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