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