mirror of
https://github.com/doublecmd/doublecmd.git
synced 2026-06-21 09:58:13 +00:00
1522 lines
45 KiB
ObjectPascal
1522 lines
45 KiB
ObjectPascal
{
|
|
Double Commander
|
|
-------------------------------------------------------------------------
|
|
Internal diff and merge tool
|
|
|
|
Copyright (C) 2010-2021 Alexander Koblov (alexx2000@mail.ru)
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
}
|
|
|
|
unit fDiffer;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, FileUtil, Forms, Controls, Dialogs, Menus, ComCtrls,
|
|
ActnList, ExtCtrls, EditBtn, Buttons, SynEdit, uSynDiffControls,
|
|
uPariterControls, uDiffOND, uFormCommands, uHotkeyManager, uOSForms,
|
|
uBinaryDiffViewer, uShowForm, KASStatusBar, Graphics, StdCtrls;
|
|
|
|
type
|
|
|
|
{ TStatusBar }
|
|
|
|
TStatusBar = class(TKASStatusBar);
|
|
|
|
{ TfrmDiffer }
|
|
|
|
TfrmDiffer = class(TAloneForm, IFormCommands)
|
|
actBinaryCompare: TAction;
|
|
actCopyLeftToRight: TAction;
|
|
actCopyRightToLeft: TAction;
|
|
actExit: TAction;
|
|
actEditCut: TAction;
|
|
actEditCopy: TAction;
|
|
actEditDelete: TAction;
|
|
actEditUndo: TAction;
|
|
actEditRedo: TAction;
|
|
actEditSelectAll: TAction;
|
|
actEditPaste: TAction;
|
|
actAbout: TAction;
|
|
actAutoCompare: TAction;
|
|
actLineDifferences: TAction;
|
|
actSaveRightAs: TAction;
|
|
actSaveLeftAs: TAction;
|
|
actOpenRight: TAction;
|
|
actOpenLeft: TAction;
|
|
actReload: TAction;
|
|
actSaveRight: TAction;
|
|
actSaveLeft: TAction;
|
|
actPaintBackground: TAction;
|
|
actStartCompare: TAction;
|
|
actFirstDifference: TAction;
|
|
actIgnoreCase: TAction;
|
|
actIgnoreWhiteSpace: TAction;
|
|
actCancelCompare: TAction;
|
|
actKeepScrolling: TAction;
|
|
actPrevDifference: TAction;
|
|
actLastDifference: TAction;
|
|
actNextDifference: TAction;
|
|
actSaveAs: TAction;
|
|
actSave: TAction;
|
|
ActionList: TActionList;
|
|
edtFileNameLeft: TFileNameEdit;
|
|
edtFileNameRight: TFileNameEdit;
|
|
MainMenu: TMainMenu;
|
|
miAutoCompare: TMenuItem;
|
|
miDivider10: TMenuItem;
|
|
miLineDifferences: TMenuItem;
|
|
miEncodingRight: TMenuItem;
|
|
miEncodingLeft: TMenuItem;
|
|
miAbout: TMenuItem;
|
|
mnuEncoding: TMenuItem;
|
|
miSaveRightAs: TMenuItem;
|
|
miSaveLeftAs: TMenuItem;
|
|
miCopyContext: TMenuItem;
|
|
miCutContext: TMenuItem;
|
|
miDeleteContext: TMenuItem;
|
|
miEditSelectAll: TMenuItem;
|
|
miDivider9: TMenuItem;
|
|
miEditDelete: TMenuItem;
|
|
miEditPaste: TMenuItem;
|
|
miEditCopy: TMenuItem;
|
|
miEditCut: TMenuItem;
|
|
miDivider8: TMenuItem;
|
|
miEditRedo: TMenuItem;
|
|
miEditUndo: TMenuItem;
|
|
miDivider7: TMenuItem;
|
|
miPasteContext: TMenuItem;
|
|
miReload: TMenuItem;
|
|
miDivider6: TMenuItem;
|
|
miExit: TMenuItem;
|
|
miSaveRight: TMenuItem;
|
|
miOpenRight: TMenuItem;
|
|
miOpenLeft: TMenuItem;
|
|
miCopyRightToLeft: TMenuItem;
|
|
miCopyLeftToRight: TMenuItem;
|
|
miDivider5: TMenuItem;
|
|
miPaintBackground: TMenuItem;
|
|
miDivider4: TMenuItem;
|
|
miBinaryCompare: TMenuItem;
|
|
miKeepScrolling: TMenuItem;
|
|
miDivider3: TMenuItem;
|
|
miLastDiff: TMenuItem;
|
|
miFirstDiff: TMenuItem;
|
|
miDivider2: TMenuItem;
|
|
miPrevDiff: TMenuItem;
|
|
miNextDiff: TMenuItem;
|
|
miDivider1: TMenuItem;
|
|
miCancelCompare: TMenuItem;
|
|
miSelectAllContext: TMenuItem;
|
|
miSeparator1: TMenuItem;
|
|
miSeparator2: TMenuItem;
|
|
miStartCompare: TMenuItem;
|
|
miUndoContext: TMenuItem;
|
|
mnuActions: TMenuItem;
|
|
miIgnoreCase: TMenuItem;
|
|
miIgnoreWhiteSpace: TMenuItem;
|
|
mnuOptions: TMenuItem;
|
|
mnuEdit: TMenuItem;
|
|
miSaveLeft: TMenuItem;
|
|
mnuFile: TMenuItem;
|
|
ContextMenu: TPopupMenu;
|
|
pnlLeftBox: TPanel;
|
|
pnlRight: TPanel;
|
|
pnlLeft: TPanel;
|
|
pnlRightBox: TPanel;
|
|
btnLeftEncoding: TSpeedButton;
|
|
btnRightEncoding: TSpeedButton;
|
|
btnLeftSave: TSpeedButton;
|
|
btnLeftSaveAs: TSpeedButton;
|
|
btnRightSave: TSpeedButton;
|
|
btnRightSaveAs: TSpeedButton;
|
|
pmEncodingLeft: TPopupMenu;
|
|
pmEncodingRight: TPopupMenu;
|
|
Splitter: TSplitter;
|
|
StatusBar: TStatusBar;
|
|
tmProgress: TTimer;
|
|
ToolBar: TToolBar;
|
|
btnSave: TToolButton;
|
|
btnSaveAs: TToolButton;
|
|
Divider1: TToolButton;
|
|
btnCompare: TToolButton;
|
|
btnLast: TToolButton;
|
|
btnNext: TToolButton;
|
|
btnPrev: TToolButton;
|
|
btnFirst: TToolButton;
|
|
Divider2: TToolButton;
|
|
Divider3: TToolButton;
|
|
btnCancelCompare: TToolButton;
|
|
Divider4: TToolButton;
|
|
btnReload: TToolButton;
|
|
btnCopyRightToLeft: TToolButton;
|
|
btnCopyLeftToRight: TToolButton;
|
|
Divider5: TToolButton;
|
|
btnEditUndo: TToolButton;
|
|
btnEditRedo: TToolButton;
|
|
procedure actAboutExecute(Sender: TObject);
|
|
procedure actBinaryCompareExecute(Sender: TObject);
|
|
procedure actCancelCompareExecute(Sender: TObject);
|
|
procedure actExecute(Sender: TObject);
|
|
procedure actEditCopyExecute(Sender: TObject);
|
|
procedure actEditCutExecute(Sender: TObject);
|
|
procedure actEditDeleteExecute(Sender: TObject);
|
|
procedure actEditPasteExecute(Sender: TObject);
|
|
procedure actEditRedoExecute(Sender: TObject);
|
|
procedure actEditSelectAllExecute(Sender: TObject);
|
|
procedure actEditUndoExecute(Sender: TObject);
|
|
procedure actIgnoreCaseExecute(Sender: TObject);
|
|
procedure actLineDifferencesExecute(Sender: TObject);
|
|
procedure actOpenLeftExecute(Sender: TObject);
|
|
procedure actOpenRightExecute(Sender: TObject);
|
|
procedure actPaintBackgroundExecute(Sender: TObject);
|
|
procedure actSaveAsExecute(Sender: TObject);
|
|
procedure actSaveExecute(Sender: TObject);
|
|
procedure actSaveLeftAsExecute(Sender: TObject);
|
|
procedure actSaveRightAsExecute(Sender: TObject);
|
|
procedure actStartCompareExecute(Sender: TObject);
|
|
procedure actKeepScrollingExecute(Sender: TObject);
|
|
procedure btnLeftEncodingClick(Sender: TObject);
|
|
procedure btnRightEncodingClick(Sender: TObject);
|
|
procedure edtFileNameLeftAcceptFileName(Sender: TObject; var Value: String);
|
|
procedure edtFileNameRightAcceptFileName(Sender: TObject; var Value: String);
|
|
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
|
|
procedure FormCloseQuery(Sender: TObject; var CanClose: boolean);
|
|
procedure FormCreate(Sender: TObject);
|
|
procedure FormDestroy(Sender: TObject);
|
|
procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
|
|
procedure FormResize(Sender: TObject);
|
|
procedure btnCancelClick(Sender: TObject);
|
|
procedure tmProgressTimer(Sender: TObject);
|
|
private
|
|
BinaryDiffList: TFPList;
|
|
BinaryDiffIndex: Integer;
|
|
BinaryCompare: TBinaryCompare;
|
|
BinaryViewerLeft,
|
|
BinaryViewerRight: TBinaryDiffViewer;
|
|
procedure BinaryCompareFinish;
|
|
private
|
|
Diff: TDiff;
|
|
SynDiffEditActive: TSynDiffEdit;
|
|
SynDiffEditLeft: TSynDiffEdit;
|
|
SynDiffEditRight: TSynDiffEdit;
|
|
SynDiffHighlighterLeft,
|
|
SynDiffHighlighterRight: TSynDiffHighlighter;
|
|
HashListLeft,
|
|
HashListRight: array of Integer;
|
|
EncodingList: TStringList;
|
|
ScrollLock: LongInt;
|
|
FShowIdentical: Boolean;
|
|
FModal: Boolean;
|
|
FCancel: Boolean;
|
|
frmProgress: TForm;
|
|
FWaitData: TWaitData;
|
|
FElevate: TDuplicates;
|
|
FCommands: TFormCommands;
|
|
private
|
|
procedure ShowDialog;
|
|
procedure ShowIdentical;
|
|
procedure ShowProgressDialog;
|
|
procedure CloseProgressDialog;
|
|
procedure Clear(bLeft, bRight: Boolean);
|
|
procedure BuildHashList(bLeft, bRight: Boolean);
|
|
procedure ChooseEncoding(SynDiffEdit: TSynDiffEdit);
|
|
procedure SetColors(cAdded, cDeleted, cModified: TColor);
|
|
procedure ChooseEncoding(MenuItem: TMenuItem; Encoding: String);
|
|
procedure FillEncodingMenu(TheOwner: TMenuItem; MenuHandler: TNotifyEvent; GroupIndex: LongInt);
|
|
procedure LoadFromFile(SynDiffEdit: TSynDiffEdit; const FileName: String);
|
|
procedure SaveToFile(SynDiffEdit: TSynDiffEdit; const FileName: String);
|
|
procedure OpenFileLeft(const FileName: String);
|
|
procedure OpenFileRight(const FileName: String);
|
|
procedure SetEncodingLeft(Sender: TObject);
|
|
procedure SetEncodingRight(Sender: TObject);
|
|
procedure SynDiffEditEnter(Sender: TObject);
|
|
procedure ShowFirstDifference(Data: PtrInt);
|
|
procedure SynDiffEditLeftStatusChange(Sender: TObject; Changes: TSynStatusChanges);
|
|
procedure SynDiffEditRightStatusChange(Sender: TObject; Changes: TSynStatusChanges);
|
|
|
|
property Commands: TFormCommands read FCommands implements IFormCommands;
|
|
public
|
|
constructor Create(TheOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
procedure AfterConstruction; override;
|
|
published
|
|
procedure cm_CopyLeftToRight(const Params: array of string);
|
|
procedure cm_CopyRightToLeft(const Params: array of string);
|
|
procedure cm_Exit(const Params: array of string);
|
|
procedure cm_FirstDifference(const Params: array of string);
|
|
procedure cm_LastDifference(const Params: array of string);
|
|
procedure cm_NextDifference(const Params: array of string);
|
|
procedure cm_PrevDifference(const Params: array of string);
|
|
procedure cm_Reload(const Params: array of string);
|
|
procedure cm_SaveLeft(const Params: array of string);
|
|
procedure cm_SaveRight(const Params: array of string);
|
|
end;
|
|
|
|
procedure ShowDiffer(const FileNameLeft, FileNameRight: String; WaitData: TWaitData = nil; Modal: Boolean = False);
|
|
|
|
implementation
|
|
|
|
{$R *.lfm}
|
|
|
|
uses
|
|
Math, LCLType, LazFileUtils, LConvEncoding, SynEditTypes, uHash, uLng, uGlobs,
|
|
uShowMsg, DCClassesUtf8, dmCommonData, uDCUtils, uConvEncoding, uAdministrator;
|
|
|
|
const
|
|
HotkeysCategory = 'Differ';
|
|
|
|
procedure ShowDiffer(const FileNameLeft, FileNameRight: String; WaitData: TWaitData = nil; Modal: Boolean = False);
|
|
var
|
|
Differ: TfrmDiffer;
|
|
begin
|
|
Differ := TfrmDiffer.Create(Application);
|
|
with Differ do
|
|
begin
|
|
FModal := Modal;
|
|
FWaitData := WaitData;
|
|
FShowIdentical := True;
|
|
edtFileNameLeft.Text:= FileNameLeft;
|
|
edtFileNameRight.Text:= FileNameRight;
|
|
SetColors(gDifferAddedColor, gDifferDeletedColor, gDifferModifiedColor);
|
|
try
|
|
if not (FileIsText(FileNameLeft) and FileIsText(FileNameRight)) then
|
|
actBinaryCompare.Execute
|
|
else begin
|
|
OpenFileLeft(FileNameLeft);
|
|
OpenFileRight(FileNameRight);
|
|
actStartCompare.Execute;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
tmProgress.Enabled:= False;
|
|
msgError(E.Message);
|
|
Free;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{ TfrmDiffer }
|
|
|
|
procedure TfrmDiffer.actStartCompareExecute(Sender: TObject);
|
|
var
|
|
I: Integer;
|
|
LineNumberLeft,
|
|
LineNumberRight: PtrInt;
|
|
begin
|
|
FCancel:= False;
|
|
try
|
|
if actBinaryCompare.Checked then
|
|
begin
|
|
if (BinaryViewerLeft.IsFileOpen and BinaryViewerRight.IsFileOpen) then
|
|
begin
|
|
actStartCompare.Enabled := False;
|
|
actCancelCompare.Enabled := True;
|
|
actBinaryCompare.Enabled := False;
|
|
BinaryCompare:= TBinaryCompare.Create(BinaryViewerLeft.GetDataAdr,
|
|
BinaryViewerRight.GetDataAdr,
|
|
BinaryViewerLeft.FileSize,
|
|
BinaryViewerRight.FileSize,
|
|
BinaryDiffList);
|
|
|
|
BinaryCompare.OnFinish:= @BinaryCompareFinish;
|
|
BinaryCompare.Start;
|
|
end;
|
|
end
|
|
else try
|
|
Inc(ScrollLock);
|
|
Screen.Cursor := crHourGlass;
|
|
|
|
if SynDiffEditLeft.Modified then SynDiffEditLeft.Lines.RemoveFake;
|
|
if SynDiffEditRight.Modified then SynDiffEditRight.Lines.RemoveFake;
|
|
BuildHashList(SynDiffEditLeft.Modified, SynDiffEditRight.Modified);
|
|
|
|
if (Length(HashListLeft) = 0) or (Length(HashListRight) = 0) then
|
|
begin
|
|
FCancel := True;
|
|
Exit;
|
|
end;
|
|
|
|
actStartCompare.Enabled := False;
|
|
actCancelCompare.Enabled := True;
|
|
|
|
Diff.Execute(
|
|
PInteger(@HashListLeft[0]),
|
|
PInteger(@HashListRight[0]),
|
|
Length(HashListLeft),
|
|
Length(HashListRight)
|
|
);
|
|
|
|
tmProgress.Enabled:= False;
|
|
if Diff.Cancelled then Exit;
|
|
|
|
SynDiffEditLeft.StartCompare;
|
|
SynDiffEditRight.StartCompare;
|
|
|
|
for I := 0 to Diff.Count - 1 do
|
|
with Diff.Compares[I] do
|
|
begin
|
|
LineNumberLeft:= oldIndex1 + 1;
|
|
LineNumberRight:= oldIndex2 + 1;
|
|
case Kind of
|
|
ckAdd:
|
|
begin
|
|
SynDiffEditLeft.Lines.InsertFake(I, Kind);
|
|
SynDiffEditRight.Lines.SetKindAndNumber(I, Kind, LineNumberRight);
|
|
end;
|
|
ckDelete:
|
|
begin
|
|
SynDiffEditLeft.Lines.SetKindAndNumber(I, Kind, LineNumberLeft);
|
|
SynDiffEditRight.Lines.InsertFake(I, Kind);
|
|
end;
|
|
else
|
|
begin
|
|
SynDiffEditLeft.Lines.SetKindAndNumber(I, Kind, LineNumberLeft);
|
|
SynDiffEditRight.Lines.SetKindAndNumber(I, Kind, LineNumberRight);
|
|
end;
|
|
end;
|
|
end;
|
|
with Diff.DiffStats do
|
|
begin
|
|
StatusBar.Panels[0].Text := rsDiffMatches + IntToStr(matches);
|
|
StatusBar.Panels[1].Text := rsDiffModifies + IntToStr(modifies);
|
|
StatusBar.Panels[2].Text := rsDiffAdds + IntToStr(adds);
|
|
StatusBar.Panels[3].Text := rsDiffDeletes + IntToStr(deletes);
|
|
if FShowIdentical then
|
|
begin
|
|
CloseProgressDialog;
|
|
FShowIdentical:= (modifies = 0) and (adds = 0) and (deletes = 0);
|
|
if FShowIdentical then
|
|
ShowIdentical
|
|
else begin
|
|
FShowIdentical:= False;
|
|
Application.QueueAsyncCall(@ShowFirstDifference, 0);
|
|
ShowDialog;
|
|
end;
|
|
end;
|
|
end;
|
|
finally
|
|
SynDiffEditLeft.FinishCompare;
|
|
SynDiffEditRight.FinishCompare;
|
|
Screen.Cursor := crDefault;
|
|
actStartCompare.Enabled := True;
|
|
actCancelCompare.Enabled := False;
|
|
Dec(ScrollLock);
|
|
end;
|
|
if actLineDifferences.Checked then
|
|
begin
|
|
SynDiffEditLeft.Highlighter:= SynDiffHighlighterLeft;
|
|
SynDiffEditRight.Highlighter:= SynDiffHighlighterRight;
|
|
end;
|
|
finally
|
|
if FShowIdentical and FCancel then Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmDiffer.actOpenLeftExecute(Sender: TObject);
|
|
begin
|
|
dmComData.OpenDialog.FileName:= edtFileNameLeft.Text;
|
|
dmComData.OpenDialog.Filter:= AllFilesMask;
|
|
if dmComData.OpenDialog.Execute then
|
|
begin
|
|
edtFileNameLeft.Text:= dmComData.OpenDialog.FileName;
|
|
actReload.Execute;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmDiffer.actOpenRightExecute(Sender: TObject);
|
|
begin
|
|
dmComData.OpenDialog.FileName:= edtFileNameRight.Text;
|
|
dmComData.OpenDialog.Filter:= AllFilesMask;
|
|
if dmComData.OpenDialog.Execute then
|
|
begin
|
|
edtFileNameRight.Text:= dmComData.OpenDialog.FileName;
|
|
actReload.Execute;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmDiffer.actPaintBackgroundExecute(Sender: TObject);
|
|
begin
|
|
if actPaintBackground.Checked then
|
|
begin
|
|
SynDiffEditLeft.PaintStyle:= psBackground;
|
|
SynDiffEditRight.PaintStyle:= psBackground;
|
|
end
|
|
else
|
|
begin
|
|
SynDiffEditLeft.PaintStyle:= psForeground;
|
|
SynDiffEditRight.PaintStyle:= psForeground;
|
|
end;
|
|
SynDiffHighlighterLeft.UpdateColors;
|
|
SynDiffHighlighterRight.UpdateColors;
|
|
end;
|
|
|
|
procedure TfrmDiffer.actSaveAsExecute(Sender: TObject);
|
|
begin
|
|
if SynDiffEditActive = SynDiffEditLeft then
|
|
actSaveLeftAs.Execute
|
|
else if SynDiffEditActive = SynDiffEditRight then
|
|
actSaveRightAs.Execute;
|
|
end;
|
|
|
|
procedure TfrmDiffer.actSaveExecute(Sender: TObject);
|
|
begin
|
|
if SynDiffEditActive = SynDiffEditLeft then
|
|
actSaveLeft.Execute
|
|
else if SynDiffEditActive = SynDiffEditRight then
|
|
actSaveRight.Execute;
|
|
end;
|
|
|
|
procedure TfrmDiffer.actSaveLeftAsExecute(Sender: TObject);
|
|
begin
|
|
dmComData.SaveDialog.FileName:= edtFileNameLeft.FileName;
|
|
if dmComData.SaveDialog.Execute then
|
|
begin
|
|
PushPop(FElevate);
|
|
try
|
|
SaveToFile(SynDiffEditLeft, dmComData.SaveDialog.FileName);
|
|
finally
|
|
PushPop(FElevate);
|
|
end;
|
|
edtFileNameLeft.FileName:= dmComData.SaveDialog.FileName;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmDiffer.actSaveRightAsExecute(Sender: TObject);
|
|
begin
|
|
dmComData.SaveDialog.FileName:= edtFileNameRight.FileName;
|
|
if dmComData.SaveDialog.Execute then
|
|
begin
|
|
PushPop(FElevate);
|
|
try
|
|
SaveToFile(SynDiffEditRight, dmComData.SaveDialog.FileName);
|
|
finally
|
|
PushPop(FElevate);
|
|
end;
|
|
edtFileNameRight.FileName:= dmComData.SaveDialog.FileName;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmDiffer.actBinaryCompareExecute(Sender: TObject);
|
|
begin
|
|
mnuEdit.Enabled:= not actBinaryCompare.Checked;
|
|
mnuEncoding.Enabled:= not actBinaryCompare.Checked;
|
|
btnLeftEncoding.Enabled:= not actBinaryCompare.Checked;
|
|
btnRightEncoding.Enabled:= not actBinaryCompare.Checked;
|
|
actCopyLeftToRight.Enabled:= not actBinaryCompare.Checked;
|
|
actCopyRightToLeft.Enabled:= not actBinaryCompare.Checked;
|
|
actEditUndo.Enabled:= not actBinaryCompare.Checked;
|
|
actEditRedo.Enabled:= not actBinaryCompare.Checked;
|
|
actSave.Enabled:= not actBinaryCompare.Checked;
|
|
actSaveAs.Enabled:= not actBinaryCompare.Checked;
|
|
actSaveLeft.Enabled:= not actBinaryCompare.Checked;
|
|
actSaveLeftAs.Enabled:= not actBinaryCompare.Checked;
|
|
actSaveRight.Enabled:= not actBinaryCompare.Checked;
|
|
actSaveRightAs.Enabled:= not actBinaryCompare.Checked;
|
|
actIgnoreCase.Enabled:= not actBinaryCompare.Checked;
|
|
actIgnoreWhiteSpace.Enabled:= not actBinaryCompare.Checked;
|
|
actPaintBackground.Enabled:= not actBinaryCompare.Checked;
|
|
actLineDifferences.Enabled:= not actBinaryCompare.Checked;
|
|
|
|
SynDiffEditLeft.Visible:= not actBinaryCompare.Checked;
|
|
SynDiffEditRight.Visible:= not actBinaryCompare.Checked;
|
|
BinaryViewerLeft.Visible:= actBinaryCompare.Checked;
|
|
BinaryViewerRight.Visible:= actBinaryCompare.Checked;
|
|
|
|
if actBinaryCompare.Checked then
|
|
begin
|
|
PushPop(FElevate);
|
|
try
|
|
BinaryDiffList.Clear;
|
|
BinaryViewerLeft.FileName:= edtFileNameLeft.Text;
|
|
BinaryViewerRight.FileName:= edtFileNameRight.Text;
|
|
finally
|
|
PushPop(FElevate);
|
|
end;
|
|
if FShowIdentical then
|
|
begin
|
|
if not BinaryViewerLeft.IsFileOpen then
|
|
raise EFOpenError.Create(BinaryViewerLeft.LastError + LineEnding + edtFileNameLeft.Text);
|
|
if not BinaryViewerRight.IsFileOpen then
|
|
raise EFOpenError.Create(BinaryViewerRight.LastError + LineEnding + edtFileNameRight.Text);
|
|
end;
|
|
StatusBar.Panels[0].Text := EmptyStr;
|
|
StatusBar.Panels[1].Text := EmptyStr;
|
|
StatusBar.Panels[2].Text := EmptyStr;
|
|
StatusBar.Panels[3].Text := EmptyStr;
|
|
end
|
|
else
|
|
begin
|
|
BinaryViewerLeft.FileName:= EmptyStr;
|
|
BinaryViewerRight.FileName:= EmptyStr;
|
|
OpenFileLeft(edtFileNameLeft.Text);
|
|
OpenFileRight(edtFileNameRight.Text);
|
|
end;
|
|
|
|
actStartCompare.Execute;
|
|
end;
|
|
|
|
procedure TfrmDiffer.actCancelCompareExecute(Sender: TObject);
|
|
begin
|
|
if not actBinaryCompare.Checked then
|
|
Diff.Cancel
|
|
else begin
|
|
if Assigned(BinaryCompare) then
|
|
begin
|
|
BinaryCompare.Terminate;
|
|
BinaryCompare:= nil;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmDiffer.actAboutExecute(Sender: TObject);
|
|
begin
|
|
ShowMessage('Internal Differ tool of Double Commander.' + LineEnding + LineEnding +
|
|
'It is inspired by Flavio Etrusco''s Pariter tool.' + LineEnding +
|
|
'You can find it on: http://sourceforge.net/projects/pariter' + LineEnding +
|
|
'It is based on Angus Johnson''s excellent TDiff component.' + LineEnding +
|
|
'You can find it on: http://www.users.on.net/johnson/delphi');
|
|
end;
|
|
|
|
procedure TfrmDiffer.actEditCopyExecute(Sender: TObject);
|
|
begin
|
|
SynDiffEditActive.CopyToClipboard;
|
|
end;
|
|
|
|
procedure TfrmDiffer.actEditCutExecute(Sender: TObject);
|
|
begin
|
|
SynDiffEditActive.CutToClipboard;
|
|
end;
|
|
|
|
procedure TfrmDiffer.actEditDeleteExecute(Sender: TObject);
|
|
begin
|
|
SynDiffEditActive.ClearSelection;
|
|
end;
|
|
|
|
procedure TfrmDiffer.actEditPasteExecute(Sender: TObject);
|
|
begin
|
|
SynDiffEditActive.PasteFromClipboard;
|
|
end;
|
|
|
|
procedure TfrmDiffer.actEditRedoExecute(Sender: TObject);
|
|
begin
|
|
SynDiffEditActive.Redo;
|
|
end;
|
|
|
|
procedure TfrmDiffer.actEditSelectAllExecute(Sender: TObject);
|
|
begin
|
|
SynDiffEditActive.SelectAll;
|
|
end;
|
|
|
|
procedure TfrmDiffer.actEditUndoExecute(Sender: TObject);
|
|
begin
|
|
SynDiffEditActive.Undo;
|
|
end;
|
|
|
|
procedure TfrmDiffer.actExecute(Sender: TObject);
|
|
var
|
|
cmd: string;
|
|
begin
|
|
cmd := (Sender as TAction).Name;
|
|
cmd := 'cm_' + Copy(cmd, 4, Length(cmd) - 3);
|
|
Commands.ExecuteCommand(cmd, []);
|
|
end;
|
|
|
|
procedure TfrmDiffer.actIgnoreCaseExecute(Sender: TObject);
|
|
begin
|
|
if actAutoCompare.Checked then actStartCompare.Execute;
|
|
end;
|
|
|
|
procedure TfrmDiffer.actLineDifferencesExecute(Sender: TObject);
|
|
begin
|
|
if actLineDifferences.Checked and (Diff.Count <> 0) then
|
|
begin
|
|
SynDiffEditLeft.Highlighter:= SynDiffHighlighterLeft;
|
|
SynDiffEditRight.Highlighter:= SynDiffHighlighterRight;
|
|
end
|
|
else
|
|
begin
|
|
SynDiffEditLeft.Highlighter:= nil;
|
|
SynDiffEditRight.Highlighter:= nil;
|
|
end;
|
|
SynDiffEditLeft.Repaint;
|
|
SynDiffEditRight.Repaint;
|
|
end;
|
|
|
|
procedure TfrmDiffer.actKeepScrollingExecute(Sender: TObject);
|
|
begin
|
|
BinaryViewerLeft.KeepScrolling:= actKeepScrolling.Checked;
|
|
BinaryViewerRight.KeepScrolling:= actKeepScrolling.Checked;
|
|
end;
|
|
|
|
procedure TfrmDiffer.btnLeftEncodingClick(Sender: TObject);
|
|
begin
|
|
pmEncodingLeft.PopUp(Mouse.CursorPos.X, Mouse.CursorPos.Y);
|
|
end;
|
|
|
|
procedure TfrmDiffer.btnRightEncodingClick(Sender: TObject);
|
|
begin
|
|
pmEncodingRight.PopUp(Mouse.CursorPos.X, Mouse.CursorPos.Y);
|
|
end;
|
|
|
|
procedure TfrmDiffer.edtFileNameLeftAcceptFileName(Sender: TObject;
|
|
var Value: String);
|
|
begin
|
|
OpenFileLeft(Value);
|
|
if actAutoCompare.Checked then actStartCompare.Execute;
|
|
end;
|
|
|
|
procedure TfrmDiffer.edtFileNameRightAcceptFileName(Sender: TObject;
|
|
var Value: String);
|
|
begin
|
|
OpenFileRight(Value);
|
|
if actAutoCompare.Checked then actStartCompare.Execute;
|
|
end;
|
|
|
|
procedure TfrmDiffer.FormCreate(Sender: TObject);
|
|
begin
|
|
ScrollLock:= 0;
|
|
Diff:= TDiff.Create(Self);
|
|
SynDiffEditLeft:= TSynDiffEdit.Create(Self);
|
|
SynDiffEditRight:= TSynDiffEdit.Create(Self);
|
|
SynDiffHighlighterLeft:= TSynDiffHighlighter.Create(SynDiffEditLeft);
|
|
SynDiffHighlighterRight:= TSynDiffHighlighter.Create(SynDiffEditRight);
|
|
|
|
SynDiffEditLeft.Parent:= pnlLeft;
|
|
SynDiffEditRight.Parent:= pnlRight;
|
|
SynDiffEditLeft.Align:= alClient;
|
|
SynDiffEditRight.Align:= alClient;
|
|
SynDiffEditLeft.PopupMenu:= ContextMenu;
|
|
SynDiffEditRight.PopupMenu:= ContextMenu;
|
|
|
|
SynDiffEditLeft.ModifiedFile:= SynDiffEditRight;
|
|
SynDiffEditRight.OriginalFile:= SynDiffEditLeft;
|
|
|
|
SynDiffEditLeft.OnEnter:= @SynDiffEditEnter;
|
|
SynDiffEditRight.OnEnter:= @SynDiffEditEnter;
|
|
SynDiffEditLeft.OnStatusChange:= @SynDiffEditLeftStatusChange;
|
|
SynDiffEditRight.OnStatusChange:= @SynDiffEditRightStatusChange;
|
|
// Set active editor
|
|
SynDiffEditActive:= SynDiffEditLeft;
|
|
|
|
BinaryDiffList:= TFPList.Create;
|
|
BinaryViewerLeft:= TBinaryDiffViewer.Create(Self);
|
|
BinaryViewerRight:= TBinaryDiffViewer.Create(Self);
|
|
|
|
BinaryViewerLeft.OnFileOpen:= @FileOpenUAC;
|
|
BinaryViewerRight.OnFileOpen:= @FileOpenUAC;
|
|
|
|
BinaryViewerLeft.Visible:= False;
|
|
BinaryViewerRight.Visible:= False;
|
|
BinaryViewerLeft.Parent:= pnlLeft;
|
|
BinaryViewerRight.Parent:= pnlRight;
|
|
BinaryViewerLeft.Align:= alClient;
|
|
BinaryViewerRight.Align:= alClient;
|
|
|
|
BinaryViewerLeft.SecondViewer:= BinaryViewerRight;
|
|
BinaryViewerRight.SecondViewer:= BinaryViewerLeft;
|
|
|
|
BinaryViewerLeft.Modified:= gDifferModifiedBinaryColor;
|
|
BinaryViewerRight.Modified:= gDifferModifiedBinaryColor;
|
|
|
|
FontOptionsToFont(gFonts[dcfEditor], SynDiffEditLeft.Font);
|
|
FontOptionsToFont(gFonts[dcfEditor], SynDiffEditRight.Font);
|
|
FontOptionsToFont(gFonts[dcfViewer], BinaryViewerLeft.Font);
|
|
FontOptionsToFont(gFonts[dcfViewer], BinaryViewerRight.Font);
|
|
|
|
// Load settings
|
|
actIgnoreCase.Checked := gDifferIgnoreCase;
|
|
actKeepScrolling.Checked := gDifferKeepScrolling;
|
|
actLineDifferences.Checked := gDifferLineDifferences;
|
|
actPaintBackground.Checked := gDifferPaintBackground;
|
|
actIgnoreWhiteSpace.Checked := gDifferIgnoreWhiteSpace;
|
|
|
|
// Initialize mode
|
|
actKeepScrollingExecute(actKeepScrolling);
|
|
actPaintBackgroundExecute(actPaintBackground);
|
|
|
|
// Initialize property storage
|
|
InitPropStorage(Self);
|
|
|
|
// Fill encoding menu
|
|
EncodingList:= TStringList.Create;
|
|
GetSupportedEncodings(EncodingList);
|
|
FillEncodingMenu(miEncodingLeft, @SetEncodingLeft, 1);
|
|
FillEncodingMenu(miEncodingRight, @SetEncodingRight, 2);
|
|
FillEncodingMenu(pmEncodingLeft.Items, @SetEncodingLeft, 1);
|
|
FillEncodingMenu(pmEncodingRight.Items, @SetEncodingRight, 2);
|
|
EncodingList.Free;
|
|
end;
|
|
|
|
procedure TfrmDiffer.FormDestroy(Sender: TObject);
|
|
begin
|
|
FreeAndNil(Diff);
|
|
FreeAndNil(BinaryDiffList);
|
|
end;
|
|
|
|
procedure TfrmDiffer.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
|
|
begin
|
|
if Key = VK_ESCAPE then
|
|
begin
|
|
Key:= 0;
|
|
Close;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmDiffer.FormResize(Sender: TObject);
|
|
begin
|
|
pnlLeft.Width:= (ClientWidth div 2) - (Splitter.Width div 2);
|
|
end;
|
|
|
|
procedure TfrmDiffer.btnCancelClick(Sender: TObject);
|
|
begin
|
|
FCancel:= True;
|
|
if actBinaryCompare.Checked and Assigned(BinaryCompare) then
|
|
BinaryCompare.Terminate
|
|
else begin
|
|
Diff.Cancel;
|
|
end;
|
|
CloseProgressDialog;
|
|
end;
|
|
|
|
procedure TfrmDiffer.tmProgressTimer(Sender: TObject);
|
|
begin
|
|
tmProgress.Enabled:= False;
|
|
ShowProgressDialog;
|
|
end;
|
|
|
|
procedure TfrmDiffer.BinaryCompareFinish;
|
|
begin
|
|
BinaryCompare:= nil;
|
|
if FCancel then begin
|
|
if FShowIdentical then Free;
|
|
Exit;
|
|
end;
|
|
BinaryDiffIndex:= -1;
|
|
tmProgress.Enabled:= False;
|
|
StatusBar.Panels[0].Text := EmptyStr;
|
|
StatusBar.Panels[1].Text := rsDiffModifies + IntToStr(BinaryDiffList.Count);
|
|
StatusBar.Panels[2].Text := EmptyStr;
|
|
StatusBar.Panels[3].Text := EmptyStr;
|
|
actStartCompare.Enabled := True;
|
|
actCancelCompare.Enabled := False;
|
|
actBinaryCompare.Enabled := True;
|
|
if FShowIdentical then
|
|
begin
|
|
CloseProgressDialog;
|
|
FShowIdentical:= (BinaryDiffList.Count = 0);
|
|
if FShowIdentical then
|
|
ShowIdentical
|
|
else begin
|
|
FShowIdentical:= False;
|
|
Application.QueueAsyncCall(@ShowFirstDifference, 0);
|
|
ShowDialog;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmDiffer.ShowDialog;
|
|
begin
|
|
if FModal then
|
|
ShowModal
|
|
else if (FWaitData = nil) then
|
|
ShowOnTop
|
|
else
|
|
FWaitData.ShowOnTop(Self);
|
|
end;
|
|
|
|
procedure TfrmDiffer.ShowIdentical;
|
|
var
|
|
Message: String;
|
|
begin
|
|
Message:= rsDiffFilesIdentical + LineEnding + LineEnding;
|
|
Message+= edtFileNameLeft.Text + LineEnding + edtFileNameRight.Text;
|
|
if MessageDlg(rsToolDiffer, Message, mtWarning, [mbIgnore, mbCancel], 0, mbIgnore) = mrCancel then
|
|
Close
|
|
else begin
|
|
FShowIdentical:= False;
|
|
ShowDialog;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmDiffer.ShowProgressDialog;
|
|
var
|
|
lblPrompt : TLabel;
|
|
btnCancel : TBitBtn;
|
|
pbProgress: TProgressBar;
|
|
begin
|
|
frmProgress := TModalDialog.CreateNew(nil, 0);
|
|
with frmProgress do
|
|
begin
|
|
BorderStyle := bsDialog;
|
|
Position := poOwnerFormCenter;
|
|
AutoSize := True;
|
|
Height := 120;
|
|
ChildSizing.TopBottomSpacing := 8;
|
|
ChildSizing.LeftRightSpacing := 8;
|
|
Caption := Self.Caption;
|
|
lblPrompt := TLabel.Create(frmProgress);
|
|
with lblPrompt do
|
|
begin
|
|
Parent := frmProgress;
|
|
Caption := rsDiffComparing;
|
|
Top := 6;
|
|
Left := 6;
|
|
end;
|
|
pbProgress:= TProgressBar.Create(frmProgress);
|
|
with pbProgress do
|
|
begin
|
|
Parent := frmProgress;
|
|
Style:= pbstMarquee;
|
|
Left := 6;
|
|
AnchorToNeighbour(akTop, 6, lblPrompt);
|
|
Constraints.MinWidth := Math.Max(280, Screen.Width div 4);
|
|
end;
|
|
btnCancel := TBitBtn.Create(frmProgress);
|
|
with btnCancel do
|
|
begin
|
|
AutoSize := True;
|
|
Parent := frmProgress;
|
|
Kind := bkCancel;
|
|
Cancel := True;
|
|
OnClick:= @btnCancelClick;
|
|
Anchors := [akTop, akRight];
|
|
AnchorToNeighbour(akTop, 18, pbProgress);
|
|
AnchorSide[akRight].Control := pbProgress;
|
|
AnchorSide[akRight].Side := asrCenter;
|
|
end;
|
|
if FModal then
|
|
ShowModal
|
|
else if (FWaitData = nil) then
|
|
ShowOnTop
|
|
else
|
|
FWaitData.ShowOnTop(frmProgress);
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmDiffer.CloseProgressDialog;
|
|
begin
|
|
if Assigned(frmProgress) then
|
|
begin
|
|
frmProgress.Close;
|
|
FreeAndNil(frmProgress);
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmDiffer.FormClose(Sender: TObject; var CloseAction: TCloseAction);
|
|
begin
|
|
CloseAction:= caFree;
|
|
// Save settings
|
|
gDifferIgnoreCase := actIgnoreCase.Checked;
|
|
gDifferKeepScrolling := actKeepScrolling.Checked;
|
|
gDifferLineDifferences := actLineDifferences.Checked;
|
|
gDifferPaintBackground := actPaintBackground.Checked;
|
|
gDifferIgnoreWhiteSpace := actIgnoreWhiteSpace.Checked;
|
|
end;
|
|
|
|
procedure TfrmDiffer.FormCloseQuery(Sender: TObject; var CanClose: boolean);
|
|
var
|
|
Result: TMyMsgResult;
|
|
begin
|
|
if SynDiffEditLeft.Modified then
|
|
begin
|
|
Result:= msgYesNoCancel(Format(rsMsgFileChangedSave, [edtFileNameLeft.FileName]));
|
|
CanClose:= Result <> mmrCancel;
|
|
if Result = mmrYes then
|
|
actSaveLeft.Execute
|
|
else if Result = mmrCancel then
|
|
Exit;
|
|
end;
|
|
if SynDiffEditRight.Modified then
|
|
begin
|
|
Result:= msgYesNoCancel(Format(rsMsgFileChangedSave, [edtFileNameRight.FileName]));
|
|
CanClose:= Result <> mmrCancel;
|
|
if Result = mmrYes then
|
|
actSaveRight.Execute
|
|
else if Result = mmrCancel then
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmDiffer.Clear(bLeft, bRight: Boolean);
|
|
begin
|
|
if bLeft then
|
|
begin
|
|
SynDiffEditLeft.Lines.Clear;
|
|
SetLength(HashListLeft, 0);
|
|
end;
|
|
if bRight then
|
|
begin
|
|
SynDiffEditRight.Lines.Clear;
|
|
SetLength(HashListRight, 0);
|
|
end;
|
|
Diff.Clear;
|
|
StatusBar.Panels[0].Text := EmptyStr;
|
|
StatusBar.Panels[1].Text := EmptyStr;
|
|
StatusBar.Panels[2].Text := EmptyStr;
|
|
StatusBar.Panels[3].Text := EmptyStr;
|
|
actStartCompare.Enabled := True;
|
|
end;
|
|
|
|
procedure TfrmDiffer.cm_CopyLeftToRight(const Params: array of string);
|
|
var
|
|
I, iStart,
|
|
iFinish: Integer;
|
|
begin
|
|
I := SynDiffEditLeft.CaretY - 1;
|
|
iStart:= SynDiffEditLeft.DiffBegin(I);
|
|
iFinish:= SynDiffEditLeft.DiffEnd(I);
|
|
if SynDiffEditLeft.Lines.Kind[iStart] <> ckAdd then
|
|
begin
|
|
for I:= iStart to iFinish do
|
|
begin
|
|
SynDiffEditRight.Lines[I]:= SynDiffEditLeft.Lines[I];
|
|
if SynDiffEditLeft.Lines.Kind[I] = ckDelete then
|
|
begin
|
|
SynDiffEditLeft.Lines.Kind[I]:= ckNone;
|
|
SynDiffEditRight.Lines.SetKindAndNumber(I, ckNone, 0);
|
|
end;
|
|
end;
|
|
end
|
|
else begin
|
|
for I:= iStart to iFinish do
|
|
begin
|
|
if SynDiffEditLeft.Lines[iStart] <> EmptyStr then
|
|
begin
|
|
SynDiffEditRight.Lines[iStart]:= SynDiffEditLeft.Lines[iStart];
|
|
Inc(iStart);
|
|
end
|
|
else begin
|
|
SynDiffEditLeft.Lines.Delete(iStart);
|
|
SynDiffEditRight.Lines.Delete(iStart);
|
|
end;
|
|
end;
|
|
end;
|
|
SynDiffEditLeft.Renumber;
|
|
SynDiffEditRight.Renumber;
|
|
end;
|
|
|
|
procedure TfrmDiffer.cm_CopyRightToLeft(const Params: array of string);
|
|
var
|
|
I, iStart,
|
|
iFinish: Integer;
|
|
begin
|
|
I := SynDiffEditRight.CaretY - 1;
|
|
iStart:= SynDiffEditRight.DiffBegin(I);
|
|
iFinish:= SynDiffEditRight.DiffEnd(I);
|
|
if SynDiffEditLeft.Lines.Kind[iStart] <> ckDelete then
|
|
begin
|
|
for I:= iStart to iFinish do
|
|
begin
|
|
SynDiffEditLeft.Lines[I]:= SynDiffEditRight.Lines[I];
|
|
if SynDiffEditRight.Lines.Kind[I] = ckAdd then
|
|
begin
|
|
SynDiffEditRight.Lines.Kind[I]:= ckNone;
|
|
SynDiffEditLeft.Lines.SetKindAndNumber(I, ckNone, 0);
|
|
end;
|
|
end;
|
|
end
|
|
else begin
|
|
for I:= iStart to iFinish do
|
|
begin
|
|
if SynDiffEditRight.Lines[iStart] <> EmptyStr then
|
|
begin
|
|
SynDiffEditLeft.Lines[iStart]:= SynDiffEditRight.Lines[iStart];
|
|
Inc(iStart);
|
|
end
|
|
else begin
|
|
SynDiffEditLeft.Lines.Delete(iStart);
|
|
SynDiffEditRight.Lines.Delete(iStart);
|
|
end;
|
|
end;
|
|
end;
|
|
SynDiffEditLeft.Renumber;
|
|
SynDiffEditRight.Renumber;
|
|
end;
|
|
|
|
procedure TfrmDiffer.cm_Exit(const Params: array of string);
|
|
begin
|
|
Close;
|
|
end;
|
|
|
|
procedure TfrmDiffer.cm_FirstDifference(const Params: array of string);
|
|
var
|
|
Line: Integer;
|
|
Kind: TChangeKind;
|
|
begin
|
|
if actBinaryCompare.Checked then
|
|
begin
|
|
if BinaryDiffList.Count > 0 then
|
|
begin
|
|
BinaryDiffIndex:= 0;
|
|
BinaryViewerLeft.Position:= PtrInt(BinaryDiffList[BinaryDiffIndex]);
|
|
if not actKeepScrolling.Checked then
|
|
BinaryViewerRight.Position:= PtrInt(BinaryDiffList[BinaryDiffIndex]);
|
|
end;
|
|
end
|
|
else begin
|
|
// Start at first line
|
|
Line := 0;
|
|
if Line = SynDiffEditLeft.Lines.Count then Exit;
|
|
// Skip unmodified lines
|
|
Kind := ckNone;
|
|
while (Line < SynDiffEditLeft.Lines.Count - 1) and
|
|
(SynDiffEditLeft.Lines.Kind[Line] = Kind) do Inc(Line);
|
|
Inc(Line);
|
|
SynDiffEditLeft.CaretY := Line;
|
|
SynDiffEditLeft.TopLine := Line;
|
|
SynDiffEditRight.CaretY := Line;
|
|
if not actKeepScrolling.Checked then begin
|
|
SynDiffEditRight.TopLine := Line;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmDiffer.cm_LastDifference(const Params: array of string);
|
|
var
|
|
Line: Integer;
|
|
Kind: TChangeKind;
|
|
begin
|
|
if actBinaryCompare.Checked then
|
|
begin
|
|
if BinaryDiffList.Count > 0 then
|
|
begin
|
|
BinaryDiffIndex:= BinaryDiffList.Count - 1;
|
|
BinaryViewerLeft.Position:= PtrInt(BinaryDiffList[BinaryDiffIndex]);
|
|
if not actKeepScrolling.Checked then
|
|
BinaryViewerRight.Position:= PtrInt(BinaryDiffList[BinaryDiffIndex]);
|
|
end;
|
|
end
|
|
else begin
|
|
Line := SynDiffEditLeft.Lines.Count - 1;
|
|
if Line = 0 then Exit;
|
|
// Skip unmodified lines
|
|
Kind := ckNone;
|
|
while (Line > 0) and (SynDiffEditLeft.Lines.Kind[Line] = Kind) do Dec(Line);
|
|
// Find top line of previous difference
|
|
Kind:= SynDiffEditLeft.Lines.Kind[Line];
|
|
while (Line > 0) and (SynDiffEditLeft.Lines.Kind[Line] = Kind) do Dec(Line);
|
|
if (Line <> 0) then Inc(Line, 2);
|
|
SynDiffEditLeft.CaretY := Line;
|
|
SynDiffEditLeft.TopLine := Line;
|
|
SynDiffEditRight.CaretY := Line;
|
|
if not actKeepScrolling.Checked then begin
|
|
SynDiffEditRight.TopLine := Line;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmDiffer.cm_NextDifference(const Params: array of string);
|
|
var
|
|
Line: Integer;
|
|
Kind: TChangeKind;
|
|
begin
|
|
if actBinaryCompare.Checked then
|
|
begin
|
|
if BinaryDiffIndex < BinaryDiffList.Count - 1 then
|
|
begin
|
|
BinaryDiffIndex:= BinaryDiffIndex + 1;
|
|
BinaryViewerLeft.Position:= PtrInt(BinaryDiffList[BinaryDiffIndex]);
|
|
if not actKeepScrolling.Checked then
|
|
BinaryViewerRight.Position:= PtrInt(BinaryDiffList[BinaryDiffIndex]);
|
|
end;
|
|
end
|
|
else begin
|
|
Line := SynDiffEditLeft.CaretY - 1;
|
|
if Line = SynDiffEditLeft.Lines.Count - 1 then Exit;
|
|
// Skip lines with current difference type
|
|
Kind := SynDiffEditLeft.Lines.Kind[Line];
|
|
while (Line < SynDiffEditLeft.Lines.Count - 1) and
|
|
(SynDiffEditLeft.Lines.Kind[Line] = Kind) do Inc(Line);
|
|
if SynDiffEditLeft.Lines.Kind[Line] = ckNone then
|
|
begin
|
|
// Skip unmodified lines
|
|
Kind := ckNone;
|
|
while (Line < SynDiffEditLeft.Lines.Count - 1) and
|
|
(SynDiffEditLeft.Lines.Kind[Line] = Kind) do Inc(Line);
|
|
end;
|
|
Inc(Line);
|
|
SynDiffEditLeft.CaretY := Line;
|
|
SynDiffEditLeft.TopLine := Line;
|
|
SynDiffEditRight.CaretY := Line;
|
|
if not actKeepScrolling.Checked then begin
|
|
SynDiffEditRight.TopLine := Line;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmDiffer.cm_PrevDifference(const Params: array of string);
|
|
var
|
|
Line: Integer;
|
|
Kind: TChangeKind;
|
|
begin
|
|
if actBinaryCompare.Checked then
|
|
begin
|
|
if BinaryDiffIndex > 0 then
|
|
begin
|
|
BinaryDiffIndex:= BinaryDiffIndex - 1;
|
|
BinaryViewerLeft.Position:= PtrInt(BinaryDiffList[BinaryDiffIndex]);
|
|
if not actKeepScrolling.Checked then
|
|
BinaryViewerRight.Position:= PtrInt(BinaryDiffList[BinaryDiffIndex]);
|
|
end;
|
|
end
|
|
else begin
|
|
Line := SynDiffEditLeft.CaretY - 1;
|
|
if Line = 0 then Exit;
|
|
// Skip lines with current difference type
|
|
Kind := SynDiffEditLeft.Lines.Kind[Line];
|
|
while (Line > 0) and (SynDiffEditLeft.Lines.Kind[Line] = Kind) do Dec(Line);
|
|
if SynDiffEditLeft.Lines.Kind[Line] = ckNone then
|
|
begin
|
|
// Skip unmodified lines
|
|
Kind := ckNone;
|
|
while (Line > 0) and (SynDiffEditLeft.Lines.Kind[Line] = Kind) do Dec(Line);
|
|
end;
|
|
// Find top line of previous difference
|
|
Kind:= SynDiffEditLeft.Lines.Kind[Line];
|
|
while (Line > 0) and (SynDiffEditLeft.Lines.Kind[Line] = Kind) do Dec(Line);
|
|
if (Line <> 0) then Inc(Line, 2);
|
|
SynDiffEditLeft.CaretY := Line;
|
|
SynDiffEditLeft.TopLine := Line;
|
|
SynDiffEditRight.CaretY := Line;
|
|
if not actKeepScrolling.Checked then begin
|
|
SynDiffEditRight.TopLine := Line;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmDiffer.cm_Reload(const Params: array of string);
|
|
begin
|
|
OpenFileLeft(edtFileNameLeft.FileName);
|
|
OpenFileRight(edtFileNameRight.FileName);
|
|
if actAutoCompare.Checked then actStartCompare.Execute;
|
|
end;
|
|
|
|
procedure TfrmDiffer.cm_SaveLeft(const Params: array of string);
|
|
begin
|
|
PushPop(FElevate);
|
|
try
|
|
SaveToFile(SynDiffEditLeft, edtFileNameLeft.FileName);
|
|
finally
|
|
PushPop(FElevate);
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmDiffer.cm_SaveRight(const Params: array of string);
|
|
begin
|
|
PushPop(FElevate);
|
|
try
|
|
SaveToFile(SynDiffEditRight, edtFileNameRight.FileName);
|
|
finally
|
|
PushPop(FElevate);
|
|
end;
|
|
end;
|
|
|
|
constructor TfrmDiffer.Create(TheOwner: TComponent);
|
|
var
|
|
HMForm: THMForm;
|
|
begin
|
|
inherited Create(TheOwner);
|
|
FCommands := TFormCommands.Create(Self, actionList);
|
|
HMForm := HotMan.Register(Self, HotkeysCategory);
|
|
HMForm.RegisterActionList(actionList);
|
|
end;
|
|
|
|
destructor TfrmDiffer.Destroy;
|
|
begin
|
|
BinaryViewerLeft.SecondViewer:= nil;
|
|
BinaryViewerRight.SecondViewer:= nil;
|
|
HotMan.UnRegister(Self);
|
|
inherited Destroy;
|
|
if Assigned(FWaitData) then FWaitData.Done;
|
|
end;
|
|
|
|
procedure TfrmDiffer.AfterConstruction;
|
|
begin
|
|
inherited AfterConstruction;
|
|
ToolBar.ImagesWidth:= gToolIconsSize;
|
|
ToolBar.SetButtonSize(gToolIconsSize + ScaleX(6, 96),
|
|
gToolIconsSize + ScaleY(6, 96));
|
|
end;
|
|
|
|
procedure TfrmDiffer.BuildHashList(bLeft, bRight: Boolean);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if bLeft then
|
|
begin
|
|
SetLength(HashListLeft, SynDiffEditLeft.Lines.Count);
|
|
for I := 0 to SynDiffEditLeft.Lines.Count - 1 do
|
|
HashListLeft[I]:= Integer(HashString(SynDiffEditLeft.Lines[I],
|
|
actIgnoreCase.Checked, actIgnoreWhiteSpace.Checked));
|
|
end;
|
|
if bRight then
|
|
begin
|
|
SetLength(HashListRight, SynDiffEditRight.Lines.Count);
|
|
for I := 0 to SynDiffEditRight.Lines.Count - 1 do
|
|
HashListRight[I]:= Integer(HashString(SynDiffEditRight.Lines[I],
|
|
actIgnoreCase.Checked, actIgnoreWhiteSpace.Checked));
|
|
end;
|
|
|
|
actStartCompare.Enabled := (Length(HashListLeft) > 0) and (Length(HashListRight) > 0);
|
|
actCopyLeftToRight.Enabled := actStartCompare.Enabled;
|
|
actCopyRightToLeft.Enabled := actStartCompare.Enabled;
|
|
end;
|
|
|
|
procedure TfrmDiffer.SetColors(cAdded, cDeleted, cModified: TColor);
|
|
begin
|
|
with SynDiffEditLeft do
|
|
begin
|
|
Colors.Added:= cAdded;
|
|
Colors.Deleted:= cDeleted;
|
|
Colors.Modified:= cModified;
|
|
end;
|
|
with SynDiffEditRight do
|
|
begin
|
|
Colors.Added:= cAdded;
|
|
Colors.Deleted:= cDeleted;
|
|
Colors.Modified:= cModified;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmDiffer.ChooseEncoding(SynDiffEdit: TSynDiffEdit);
|
|
begin
|
|
if SynDiffEdit = SynDiffEditLeft then
|
|
begin
|
|
ChooseEncoding(miEncodingLeft, SynDiffEdit.Encoding);
|
|
ChooseEncoding(pmEncodingLeft.Items, SynDiffEdit.Encoding);
|
|
end
|
|
else
|
|
begin
|
|
ChooseEncoding(miEncodingRight, SynDiffEdit.Encoding);
|
|
ChooseEncoding(pmEncodingRight.Items, SynDiffEdit.Encoding);
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmDiffer.ChooseEncoding(MenuItem: TMenuItem; Encoding: String);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Encoding:= NormalizeEncoding(Encoding);
|
|
for I:= 0 to MenuItem.Count - 1 do
|
|
if SameText(NormalizeEncoding(MenuItem.Items[I].Caption), Encoding) then
|
|
MenuItem.Items[I].Checked:= True;
|
|
end;
|
|
|
|
procedure TfrmDiffer.FillEncodingMenu(TheOwner: TMenuItem;
|
|
MenuHandler: TNotifyEvent; GroupIndex: LongInt);
|
|
var
|
|
I: Integer;
|
|
mi: TMenuItem;
|
|
begin
|
|
for I:= 0 to EncodingList.Count - 1 do
|
|
begin
|
|
mi:= TMenuItem.Create(TheOwner);
|
|
mi.Caption:= EncodingList[I];
|
|
mi.RadioItem:= True;
|
|
mi.GroupIndex:= GroupIndex;
|
|
mi.OnClick:= MenuHandler;
|
|
TheOwner.Add(mi);
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmDiffer.LoadFromFile(SynDiffEdit: TSynDiffEdit; const FileName: String);
|
|
var
|
|
AText: String;
|
|
fsFileStream: TFileStreamUAC;
|
|
begin
|
|
try
|
|
fsFileStream:= TFileStreamUAC.Create(FileName, fmOpenRead or fmShareDenyNone);
|
|
try
|
|
SetLength(AText, fsFileStream.Size);
|
|
fsFileStream.Read(Pointer(AText)^, Length(AText));
|
|
if Length(SynDiffEdit.Encoding) = 0 then
|
|
begin
|
|
SynDiffEdit.Encoding:= DetectEncoding(AText);
|
|
ChooseEncoding(SynDiffEdit);
|
|
end;
|
|
with SynDiffEdit do
|
|
begin
|
|
if (Encoding = EncodingUTF16LE) or (Encoding = EncodingUTF16BE) then
|
|
begin
|
|
AText:= Copy(AText, 3, MaxInt); // Skip BOM
|
|
end;
|
|
AText:= ConvertEncoding(AText, Encoding, EncodingUTF8);
|
|
end;
|
|
SynDiffEdit.Lines.Text:= AText;
|
|
// Determine line break style
|
|
SynDiffEdit.Lines.TextLineBreakStyle := GuessLineBreakStyle(AText);
|
|
finally
|
|
FreeAndNil(fsFileStream);
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
E.Message:= E.Message + LineEnding + FileName;
|
|
if FShowIdentical then raise;
|
|
msgError(E.Message);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmDiffer.SaveToFile(SynDiffEdit: TSynDiffEdit; const FileName: String);
|
|
var
|
|
AText: String;
|
|
AMode: LongWord;
|
|
begin
|
|
AText := EmptyStr;
|
|
if (SynDiffEdit.Encoding = EncodingUTF16LE) then
|
|
AText := UTF16LEBOM
|
|
else if (SynDiffEdit.Encoding = EncodingUTF16BE) then begin
|
|
AText := UTF16BEBOM
|
|
end;
|
|
with TStringListEx.Create do
|
|
try
|
|
Assign(SynDiffEdit.Lines);
|
|
// remove fake lines
|
|
RemoveFake;
|
|
// restore encoding
|
|
AText+= ConvertEncoding(Text, EncodingUTF8, SynDiffEdit.Encoding);
|
|
finally
|
|
Free;
|
|
end;
|
|
// save to file
|
|
try
|
|
if not FileExistsUAC(FileName) then
|
|
AMode:= fmCreate
|
|
else begin
|
|
AMode:= fmOpenWrite or fmShareDenyWrite;
|
|
end;
|
|
with TFileStreamUAC.Create(FileName, AMode) do
|
|
try
|
|
WriteBuffer(Pointer(AText)^, Length(AText));
|
|
if (AMode <> fmCreate) then Size:= Position;
|
|
finally
|
|
Free;
|
|
end;
|
|
SynDiffEdit.Modified:= False; // needed for the undo stack
|
|
except
|
|
on E: Exception do
|
|
msgError(rsMsgErrSaveFile + ' ' + FileName + LineEnding + E.Message);
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmDiffer.OpenFileLeft(const FileName: String);
|
|
begin
|
|
PushPop(FElevate);
|
|
try
|
|
if not FileExistsUAC(FileName) then Exit;
|
|
if actBinaryCompare.Checked then
|
|
begin
|
|
BinaryDiffList.Clear;
|
|
BinaryViewerLeft.FileName:= FileName
|
|
end
|
|
else begin
|
|
Clear(True, False);
|
|
LoadFromFile(SynDiffEditLeft, FileName);
|
|
BuildHashList(True, False);
|
|
SynDiffEditLeft.Repaint;
|
|
end;
|
|
finally
|
|
PushPop(FElevate);
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmDiffer.OpenFileRight(const FileName: String);
|
|
begin
|
|
PushPop(FElevate);
|
|
try
|
|
if not FileExistsUAC(FileName) then Exit;
|
|
if actBinaryCompare.Checked then
|
|
begin
|
|
BinaryDiffList.Clear;
|
|
BinaryViewerRight.FileName:= FileName
|
|
end
|
|
else begin
|
|
Clear(False, True);
|
|
LoadFromFile(SynDiffEditRight, FileName);
|
|
BuildHashList(False, True);
|
|
SynDiffEditRight.Repaint;
|
|
end;
|
|
finally
|
|
PushPop(FElevate);
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmDiffer.SetEncodingLeft(Sender: TObject);
|
|
begin
|
|
SynDiffEditLeft.Encoding:= (Sender as TMenuItem).Caption;
|
|
ChooseEncoding(miEncodingLeft, SynDiffEditLeft.Encoding);
|
|
ChooseEncoding(pmEncodingLeft.Items, SynDiffEditLeft.Encoding);
|
|
actReload.Execute;
|
|
end;
|
|
|
|
procedure TfrmDiffer.SetEncodingRight(Sender: TObject);
|
|
begin
|
|
SynDiffEditRight.Encoding:= (Sender as TMenuItem).Caption;
|
|
ChooseEncoding(miEncodingRight, SynDiffEditRight.Encoding);
|
|
ChooseEncoding(pmEncodingRight.Items, SynDiffEditRight.Encoding);
|
|
actReload.Execute;
|
|
end;
|
|
|
|
procedure TfrmDiffer.SynDiffEditEnter(Sender: TObject);
|
|
begin
|
|
SynDiffEditActive:= (Sender as TSynDiffEdit);
|
|
end;
|
|
|
|
procedure TfrmDiffer.ShowFirstDifference(Data: PtrInt);
|
|
begin
|
|
cm_FirstDifference([]);
|
|
end;
|
|
|
|
procedure TfrmDiffer.SynDiffEditLeftStatusChange(Sender: TObject;
|
|
Changes: TSynStatusChanges);
|
|
begin
|
|
if (actKeepScrolling.Checked) and (ScrollLock = 0) and
|
|
((scTopLine in Changes) or (scLeftChar in Changes)) then
|
|
try
|
|
Inc(ScrollLock);
|
|
while (SynDiffEditRight.PaintLock <> 0) do Sleep(1);
|
|
SynDiffEditRight.TopLine:= SynDiffEditLeft.TopLine;
|
|
SynDiffEditRight.LeftChar:= SynDiffEditLeft.LeftChar;
|
|
finally
|
|
Dec(ScrollLock);
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmDiffer.SynDiffEditRightStatusChange(Sender: TObject;
|
|
Changes: TSynStatusChanges);
|
|
begin
|
|
if (actKeepScrolling.Checked) and (ScrollLock = 0) and
|
|
((scTopLine in Changes) or (scLeftChar in Changes)) then
|
|
try
|
|
Inc(ScrollLock);
|
|
while (SynDiffEditLeft.PaintLock <> 0) do Sleep(1);
|
|
SynDiffEditLeft.TopLine:= SynDiffEditRight.TopLine;
|
|
SynDiffEditLeft.LeftChar:= SynDiffEditRight.LeftChar;
|
|
finally
|
|
Dec(ScrollLock);
|
|
end;
|
|
end;
|
|
|
|
initialization
|
|
TFormCommands.RegisterCommandsForm(TfrmDiffer, HotkeysCategory, @rsHotkeyCategoryDiffer);
|
|
|
|
end.
|
|
|