Ignore:
Timestamp:
Feb 22, 2022, 11:07:27 AM (2 years ago)
Author:
chronos
Message:
  • Modified: Improved compare dialog to show text compare of vcard data.
  • Modified: Both compare sides now have scrollbar synced.
  • Modified: Compare panels keep horizontal size ratio during window resize.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Forms/UFormCompare.pas

    r120 r121  
    77uses
    88  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls,
    9   UContact, Diff, LCLType, LCLIntf, ComCtrls, SynEdit;
     9  UContact, Diff, LCLType, LCLIntf, ComCtrls, Buttons, Menus, ActnList, SynEdit,
     10  SynEditMiscClasses, SynHighlighterPosition,
     11  SynEditHighlighter, UCommon, USynEditEx;
    1012
    1113type
     
    1416
    1517  TFormCompare = class(TForm)
     18    ASwitchSides: TAction;
     19    AReloadFiles: TAction;
     20    AFileOpenLeft: TAction;
     21    AFileOpenRight: TAction;
     22    ActionList1: TActionList;
    1623    EditLeftFileName: TEdit;
    1724    EditRightFileName: TEdit;
     25    MainMenu1: TMainMenu;
     26    MenuItem1: TMenuItem;
     27    MenuItem2: TMenuItem;
     28    MenuItem3: TMenuItem;
     29    MenuItem4: TMenuItem;
     30    MenuItem5: TMenuItem;
     31    MenuItemClose: TMenuItem;
     32    OpenDialogSide: TOpenDialog;
    1833    PanelLeft: TPanel;
    1934    PanelRight: TPanel;
     35    SpeedButtonOpenLeft: TSpeedButton;
     36    SpeedButtonOpenRight: TSpeedButton;
    2037    Splitter1: TSplitter;
    21     SynEdit1: TSynEdit;
    22     SynEdit2: TSynEdit;
     38    SynEditLeft: TSynEditEx;
     39    SynEditRight: TSynEditEx;
     40    procedure AFileOpenLeftExecute(Sender: TObject);
     41    procedure AFileOpenRightExecute(Sender: TObject);
     42    procedure AReloadFilesExecute(Sender: TObject);
     43    procedure ASwitchSidesExecute(Sender: TObject);
     44    procedure FormActivate(Sender: TObject);
    2345    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
    2446    procedure FormCreate(Sender: TObject);
    2547    procedure FormDestroy(Sender: TObject);
     48    procedure FormResize(Sender: TObject);
    2649    procedure FormShow(Sender: TObject);
    27     procedure PaintBoxLeftPaint(Sender: TObject);
     50    procedure MenuItemCloseClick(Sender: TObject);
     51    procedure SynEditLeftChange(Sender: TObject);
     52    procedure SynEditLeftScroll(Sender: TObject);
     53    procedure SynEditRightChange(Sender: TObject);
     54    procedure SynEditRightScroll(Sender: TObject);
    2855  private
    29     FLeftSide: TContactsFile;
    30     FRightSide: TContactsFile;
    31     S1, S2: string;
     56    FLeftSide: string;
     57    FRightSide: string;
    3258    Diff: TDiff;
    33     procedure SetLeftSide(AValue: TContactsFile);
    34     procedure SetRightSide(AValue: TContactsFile);
     59    HighlighterLeft: TSynPositionHighlighter;
     60    HighlighterRight: TSynPositionHighlighter;
     61    AttrAdded: TtkTokenKind;
     62    AttrDeleted: TtkTokenKind;
     63    AttrModified: TtkTokenKind;
     64    LastWidth: Integer;
     65    procedure SetLeftSide(AValue: string);
     66    procedure SetRightSide(AValue: string);
    3567    procedure ReloadContent;
    3668    procedure UpdateInterface;
     69    procedure UpdateHighlight;
     70    function LoadFile(AFileName: string): string;
    3771  public
    38     property LeftSide: TContactsFile read FLeftSide write SetLeftSide;
    39     property RightSide: TContactsFile read FRightSide write SetRightSide;
     72    procedure LoadFileLeft(FileName: string);
     73    procedure LoadFileRight(FileName: string);
     74    property LeftSide: string read FLeftSide write SetLeftSide;
     75    property RightSide: string read FRightSide write SetRightSide;
    4076  end;
    4177
     
    5692begin
    5793  Core.PersistentForm1.Save(Self);
     94end;
     95
     96procedure TFormCompare.ASwitchSidesExecute(Sender: TObject);
     97var
     98  TempFileName: string;
     99  TempContent: string;
     100begin
     101  TempContent := SynEditLeft.Text;
     102  SynEditLeft.Text := SynEditRight.Text;
     103  SynEditRight.Text := TempContent;
     104
     105  TempFileName := EditLeftFileName.Text;
     106  EditLeftFileName.Text := EditRightFileName.Text;
     107  EditRightFileName.Text := TempFileName;
     108
     109  UpdateInterface;
     110  UpdateHighlight;
     111end;
     112
     113procedure TFormCompare.FormActivate(Sender: TObject);
     114begin
     115  if LastWidth = -1 then LastWidth := Width;
     116end;
     117
     118procedure TFormCompare.AReloadFilesExecute(Sender: TObject);
     119begin
     120  LoadFileLeft(EditLeftFileName.Text);
     121  LoadFileRight(EditRightFileName.Text);
     122  UpdateHighlight;
     123  UpdateInterface;
     124end;
     125
     126procedure TFormCompare.AFileOpenLeftExecute(Sender: TObject);
     127begin
     128  OpenDialogSide.InitialDir := ExtractFileDir(EditLeftFileName.Text);
     129  OpenDialogSide.FileName := ExtractFileName(EditLeftFileName.Text);
     130  if OpenDialogSide.Execute then begin
     131    EditLeftFileName.Text := OpenDialogSide.FileName;
     132    SynEditLeft.Text := LoadFileToStr(OpenDialogSide.FileName);
     133  end;
     134end;
     135
     136procedure TFormCompare.AFileOpenRightExecute(Sender: TObject);
     137begin
     138  OpenDialogSide.InitialDir := ExtractFileDir(EditRightFileName.Text);
     139  OpenDialogSide.FileName := ExtractFileName(EditRightFileName.Text);
     140  if OpenDialogSide.Execute then begin
     141    EditRightFileName.Text := OpenDialogSide.FileName;
     142    SynEditRight.Text := LoadFileToStr(OpenDialogSide.FileName);
     143  end;
     144  UpdateHighlight;
    58145end;
    59146
     
    63150  Core.ThemeManager1.UseTheme(Self);
    64151  Diff := TDiff.Create(Self);
     152
     153  HighlighterLeft := TSynPositionHighlighter.Create(Self);
     154  with HighlighterLeft do begin
     155    AttrAdded := CreateTokenID('Added', clNone, clLightGreen, []);
     156    AttrDeleted := CreateTokenID('Deleted', clNone, clLightBlue, []);
     157    AttrModified := CreateTokenID('Modified', clNone, clLightRed, []);
     158  end;
     159  SynEditLeft.Highlighter := HighlighterLeft;
     160
     161  HighlighterRight := TSynPositionHighlighter.Create(Self);
     162  with HighlighterRight do begin
     163    AttrAdded := CreateTokenID('Added', clNone, clLightGreen, []);
     164    AttrDeleted := CreateTokenID('Deleted', clNone, clLightBlue, []);
     165    AttrModified := CreateTokenID('Modified', clNone, clLightRed, []);
     166  end;
     167  SynEditRight.Highlighter := HighlighterRight;
     168
     169  LastWidth := -1;
    65170end;
    66171
    67172procedure TFormCompare.FormDestroy(Sender: TObject);
    68173begin
     174  FreeAndNil(HighlighterLeft);
     175  FreeAndNil(HighlighterRight);
    69176  FreeAndNil(Diff);
     177end;
     178
     179procedure TFormCompare.FormResize(Sender: TObject);
     180var
     181  LastHandler: TNotifyEvent;
     182  NewPanelWidth: Integer;
     183const
     184  MaxRatio = 0.8;
     185begin
     186  if LastWidth <> -1 then begin
     187    LastHandler := PanelLeft.OnResize;
     188    try
     189      PanelLeft.OnResize := nil;
     190      NewPanelWidth := Round((PanelLeft.Width / LastWidth) * Width);
     191      if NewPanelWidth > Round(Width * MaxRatio) then NewPanelWidth := Round(Width * MaxRatio);
     192      PanelLeft.Width := NewPanelWidth;
     193    finally
     194      PanelLeft.OnResize := LastHandler;
     195    end;
     196    LastWidth := Width;
     197  end;
    70198end;
    71199
     
    77205end;
    78206
    79 procedure MarkupTextOut(Canvas: TCanvas; X, Y: Integer; Text: string);
    80 var
    81   I: Integer;
    82   Len: Integer;
    83   Clr: Integer;
    84   SavedTextAlign: Cardinal;
    85   SavedBkColor: Cardinal;
    86   SavedTextColor: Cardinal;
    87   SavedPt: TPoint;
    88 begin
    89   I := Pos('<', Text);
    90   if I = 0 then begin
    91     Canvas.TextOut(X, Y, Text);
    92     Exit;
    93   end;
    94 
    95   SavedTextColor := GetTextColor(Canvas.Handle);
    96   SavedBkColor := GetBkColor(Canvas.Handle);
    97   //SavedTextAlign := GetTextAlign(Canvas.Handle);
    98   //SetTextAlign(Canvas.Handle, SavedTextAlign or TA_UPDATECP);
    99   MoveToEx(Canvas.Handle, X, Y, @SavedPt);
    100 
    101   repeat
    102     if I > 1 then TextOut(Canvas.Handle, 0, 0, PChar(Text), I - 1);
    103     Delete(Text, 1, I);
    104     Len := Length(Text);
    105     if Len < 3 then Break
    106     else if (Text[1] = 'F') and (Text[2] = 'C') and (Text[3] = ':') and
    107       (Len > 9) and (Text[10] = '>') then begin
    108       Clr := StrToIntDef('$' + Copy(Text, 4, 6), 0);
    109       SetTextColor(Canvas.Handle, Clr);
    110       Delete(Text, 1, 10);
    111       Dec(Len, 10);
    112     end
    113     else if (Text[1] = 'B') and (Text[2] = 'C') and (Text[3] = ':') and
    114       (Len > 9) and (Text[10] = '>') then
    115     begin
    116       Clr := StrToIntDef('$' + Copy(Text, 4, 6), $1FFFFFF);
    117       if Clr > $FFFFFF then
    118         SetBkColor(Canvas.Handle, SavedBkColor) else
    119         SetBkColor(Canvas.Handle, Clr);
    120       Delete(Text, 1, 10);
    121       Dec(Len, 10);
    122     end
    123     else Break;
    124     I := Pos('<', Text);
    125   until (I = 0);
    126   TextOut(Canvas.Handle, 0, 0, PChar(Text), Len);
    127 
    128   SetTextColor(Canvas.Handle, SavedTextColor);
    129   SetBkColor(Canvas.Handle, SavedBkColor);
    130   //SetTextAlign(Canvas.Handle, SavedTextAlign);
    131   with SavedPt do MoveToEx(Canvas.Handle, X, Y, nil);
    132 end;
    133 
    134 procedure TFormCompare.PaintBoxLeftPaint(Sender: TObject);
    135 begin
    136   with TPaintBox(Sender) do begin
    137     MarkupTextOut(Canvas, 0, 5, S1);
    138     MarkupTextOut(Canvas, 0, 25, S2);
    139     Canvas.TextOut(0, 55, 'Compare Statistics ...');
    140     with Diff.DiffStats do begin
    141       MarkupTextOut(Canvas, 0, 75, '  Matches : ' + IntToStr(Matches));
    142       MarkupTextOut(Canvas, 0, 95, '  <BC:AAFFAA>Modifies:<BC:------> ' + IntToStr(Modifies));
    143       MarkupTextOut(Canvas, 0, 115, '  <BC:FFAAAA>Adds    :<BC:------> ' + IntToStr(Adds));
    144       MarkupTextOut(Canvas, 0, 135, '  <BC:AAAAFF>Deletes :<BC:------> ' + IntToStr(Deletes));
    145     end;
    146   end;
    147 end;
    148 
    149 procedure TFormCompare.SetLeftSide(AValue: TContactsFile);
     207procedure TFormCompare.MenuItemCloseClick(Sender: TObject);
     208begin
     209  Close;
     210end;
     211
     212procedure TFormCompare.SynEditLeftChange(Sender: TObject);
     213begin
     214  UpdateHighlight;
     215end;
     216
     217procedure TFormCompare.SynEditLeftScroll(Sender: TObject);
     218begin
     219  SynEditRight.TopLine := SynEditLeft.TopLine;
     220end;
     221
     222procedure TFormCompare.SynEditRightChange(Sender: TObject);
     223begin
     224  UpdateHighlight;
     225end;
     226
     227procedure TFormCompare.SynEditRightScroll(Sender: TObject);
     228begin
     229  SynEditLeft.TopLine := SynEditRight.TopLine;
     230end;
     231
     232procedure TFormCompare.SetLeftSide(AValue: string);
    150233begin
    151234  if FLeftSide = AValue then Exit;
    152235  FLeftSide := AValue;
    153   ReloadContent;
    154   UpdateInterface;
    155 end;
    156 
    157 procedure TFormCompare.SetRightSide(AValue: TContactsFile);
     236end;
     237
     238procedure TFormCompare.SetRightSide(AValue: string);
    158239begin
    159240  if FRightSide = AValue then Exit;
    160241  FRightSide := AValue;
    161   ReloadContent;
    162   UpdateInterface;
    163242end;
    164243
    165244procedure TFormCompare.ReloadContent;
     245begin
     246  UpdateHighlight;
     247end;
     248
     249procedure TFormCompare.UpdateInterface;
     250begin
     251end;
     252
     253procedure TFormCompare.UpdateHighlight;
    166254var
    167255  LeftText: string;
     
    169257  I: Integer;
    170258  LastKind: TChangeKind;
    171 
    172   //AddCharToStr() adds color markup to strings which will be parsed later by
    173   //my MarkupTextOut() function where diffs (additions, modifications and
    174   //deletions) will be displayed in Paintbox1 with different colors ...
    175   //<BC:------> change background color to original (transparent) color
    176   //<BC:AAFFAA> change background color to pale green
    177   //<BC:AAAAFF> change background color to pale red
    178   //<BC:FFAAAA> change background color to pale blue
    179   procedure AddCharToStr(var s: string; c: char; Kind, LastKind: TChangeKind);
    180   begin
    181     if (Kind = LastKind) then
    182       s := s + c //no need to change colors
    183     else
    184     case Kind of
    185       ckNone: s := s + '<BC:------>' + c;
    186       ckAdd: s := s + '<BC:FFAAAA>' + c;
    187       ckDelete: s := s + '<BC:AAAAFF>' + c;
    188       ckModify: s := s + '<BC:AAFFAA>' + c;
    189     end;
    190   end;
    191 
    192 begin
    193   if Assigned(FLeftSide) then begin
    194     FLeftSide.Sort;
    195     LeftText := FLeftSide.AsString;
    196   end else LeftText := '';
    197   if Assigned(FRightSide) then begin
    198     FRightSide.Sort;
    199     RightText := FRightSide.AsString;
    200   end else RightText := '';
     259  P1: TPoint;
     260  P2: TPoint;
     261  Rec: TCompareRec;
     262  NextToken1: TtkTokenKind;
     263  NextToken2: TtkTokenKind;
     264begin
     265  LeftText := SynEditLeft.Lines.Text;
     266  RightText := SynEditRight.Lines.Text;
    201267
    202268  Diff.Execute(PChar(LeftText), PChar(RightText), Length(LeftText), Length(RightText));
    203269
    204   //now, display the diffs ...
     270  HighlighterLeft.ClearAllTokens;
     271  HighlighterRight.ClearAllTokens;
     272  LeftText := '';
     273  RightText := '';
    205274  LastKind := ckNone;
    206   S1 := '';
    207   S2 := '';
     275  P1 := Point(1, 0);
     276  P2 := Point(1, 0);
     277  NextToken1 := tkText;
     278  NextToken2 := tkText;
    208279  for I := 0 to Diff.Count - 1 do
    209280    with Diff.Compares[I] do begin
    210       //show changes to first string (with spaces for adds to align with second string)
    211       if Kind = ckAdd then AddCharToStr(S1, ' ', Kind, LastKind)
    212       else AddCharToStr(S1, Chr1, Kind, LastKind);
    213 
    214       //show changes to second string (with spaces for deletes to align with first string)
    215       if Kind = ckDelete then AddCharToStr(S2, ' ', Kind, LastKind)
    216       else AddCharToStr(S2, Chr2, Kind, LastKind);
     281      Rec := Diff.Compares[I];
     282      if Rec.Chr1 = LineEnding then begin
     283        if NextToken1 <> tkText then begin
     284          HighlighterLeft.AddToken(P1.Y, 0, NextToken1);
     285          NextToken1 := tkText;
     286        end;
     287        Inc(P1.Y);
     288        P1.X := 0;
     289        LeftText := LeftText + Rec.Chr1;
     290      end else begin
     291        if Kind = ckAdd then LeftText := LeftText + ' '
     292          else LeftText := LeftText + Rec.chr1;
     293        if Kind <> LastKind then begin
     294          HighlighterLeft.AddToken(P1.Y, P1.X, NextToken1);
     295          if Kind = ckNone then NextToken1 := tkText
     296          //else if Kind = ckAdd then NextToken1 := AttrAdded
     297          else if Kind = ckDelete then NextToken1 := AttrDeleted
     298          else if Kind = ckModify then NextToken1 := AttrModified;
     299        end;
     300        Inc(P1.X);
     301      end;
     302
     303      if Rec.Chr2 = LineEnding then begin
     304        if NextToken2 <> tkText then begin
     305          HighlighterRight.AddToken(P2.Y, 0, NextToken2);
     306          NextToken2 := tkText;
     307        end;
     308        Inc(P2.Y);
     309        P2.X := 0;
     310        RightText := RightText + Rec.Chr2;
     311      end else begin
     312        if Kind = ckDelete then RightText := RightText + ' '
     313          else RightText := RightText + Rec.Chr2;
     314        if Kind <> LastKind then begin
     315          HighlighterRight.AddToken(P2.Y, P2.X, NextToken2);
     316          if Kind = ckNone then NextToken2 := tkText
     317          else if Kind = ckAdd then NextToken2 := AttrAdded
     318          //else if Kind = ckDelete then NextToken2 := AttrDeleted
     319          else if Kind = ckModify then NextToken2 := AttrModified;
     320        end;
     321        Inc(P2.X);
     322      end;
    217323
    218324      LastKind := Kind;
    219325    end;
    220 end;
    221 
    222 procedure TFormCompare.UpdateInterface;
    223 begin
    224   if Assigned(FLeftSide) then EditLeftFileName.Text := FLeftSide.FileName
    225     else EditLeftFileName.Text := '';
    226   if Assigned(FRightSide) then EditRightFileName.Text := FRightSide.FileName
    227     else EditRightFileName.Text := '';
     326
     327  //SynEditLeft.Lines.Text := LeftText;
     328  //SynEditRight.Lines.Text := RightText;
     329end;
     330
     331function TFormCompare.LoadFile(AFileName: string): string;
     332var
     333  Ext: string;
     334begin
     335  Ext := ExtractFileExt(AFileName);
     336  if Ext = VCardFileExt then begin
     337    with TContactsFile.Create do
     338    try
     339      LoadFromFile(AFileName);
     340      Result := AsString;
     341    finally
     342      Free;
     343    end;
     344  end else Result := LoadFileToStr(AFileName);
     345end;
     346
     347procedure TFormCompare.LoadFileLeft(FileName: string);
     348begin
     349  EditLeftFileName.Text := FileName;
     350  LeftSide := LoadFile(FileName);
     351  SynEditLeft.Text := LeftSide;
     352end;
     353
     354procedure TFormCompare.LoadFileRight(FileName: string);
     355begin
     356  EditRightFileName.Text := FileName;
     357  RightSide := LoadFile(FileName);
     358  SynEditRight.Text := RightSide;
    228359end;
    229360
Note: See TracChangeset for help on using the changeset viewer.