2021-04-29 05:05:55 -07:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Globalization;
|
|
|
|
using System.Text;
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
using System.Windows;
|
|
|
|
using System.Windows.Controls;
|
|
|
|
using System.Windows.Data;
|
|
|
|
using System.Windows.Input;
|
|
|
|
using System.Windows.Media;
|
|
|
|
using System.Windows.Shapes;
|
|
|
|
|
|
|
|
namespace SourceGit.Views.Widgets {
|
|
|
|
/// <summary>
|
|
|
|
/// 变更对比视图
|
|
|
|
/// </summary>
|
|
|
|
public partial class DiffViewer : UserControl {
|
|
|
|
|
|
|
|
public class Option {
|
|
|
|
public string[] RevisionRange = new string[] { };
|
|
|
|
public string Path = "";
|
|
|
|
public string OrgPath = null;
|
|
|
|
public string ExtraArgs = "";
|
2021-06-06 23:14:53 -07:00
|
|
|
public bool UseLFS = false;
|
2021-04-29 05:05:55 -07:00
|
|
|
}
|
|
|
|
|
2021-04-29 18:25:52 -07:00
|
|
|
private ulong seq = 0;
|
2021-04-29 05:05:55 -07:00
|
|
|
private string repo = null;
|
|
|
|
private Option opt = null;
|
|
|
|
private List<Models.TextChanges.Line> cachedTextChanges = null;
|
|
|
|
private List<DataGrid> editors = new List<DataGrid>();
|
|
|
|
private List<Rectangle> splitters = new List<Rectangle>();
|
|
|
|
|
|
|
|
public DiffViewer() {
|
|
|
|
InitializeComponent();
|
|
|
|
Reset();
|
|
|
|
}
|
|
|
|
|
2021-04-29 18:25:52 -07:00
|
|
|
public void Reload() {
|
|
|
|
if (repo == null || opt == null) {
|
|
|
|
Reset();
|
|
|
|
} else {
|
|
|
|
Diff(repo, opt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-29 05:05:55 -07:00
|
|
|
public void Reset() {
|
2021-04-29 18:25:52 -07:00
|
|
|
seq++;
|
2021-04-29 05:05:55 -07:00
|
|
|
mask.Visibility = Visibility.Visible;
|
|
|
|
toolbar.Visibility = Visibility.Collapsed;
|
|
|
|
noChange.Visibility = Visibility.Collapsed;
|
|
|
|
sizeChange.Visibility = Visibility.Collapsed;
|
|
|
|
ClearCache();
|
2021-04-30 01:11:19 -07:00
|
|
|
|
|
|
|
foreach (var e in editors) e.ItemsSource = null;
|
|
|
|
foreach (var s in splitters) s.Visibility = Visibility.Hidden;
|
2021-04-29 05:05:55 -07:00
|
|
|
}
|
|
|
|
|
2021-04-29 07:35:02 -07:00
|
|
|
public void Diff(string repo, Option opt) {
|
2021-04-29 18:25:52 -07:00
|
|
|
seq++;
|
|
|
|
|
2021-04-29 05:05:55 -07:00
|
|
|
mask.Visibility = Visibility.Collapsed;
|
|
|
|
noChange.Visibility = Visibility.Collapsed;
|
|
|
|
sizeChange.Visibility = Visibility.Collapsed;
|
|
|
|
toolbar.Visibility = Visibility.Visible;
|
|
|
|
loading.Visibility = Visibility.Visible;
|
|
|
|
loading.IsAnimating = true;
|
|
|
|
|
|
|
|
SetTitle(opt.Path, opt.OrgPath);
|
|
|
|
ClearCache();
|
|
|
|
|
|
|
|
this.repo = repo;
|
|
|
|
this.opt = opt;
|
|
|
|
|
2021-04-29 18:25:52 -07:00
|
|
|
var dummy = seq;
|
2021-04-29 05:05:55 -07:00
|
|
|
Task.Run(() => {
|
|
|
|
var args = $"{opt.ExtraArgs} ";
|
|
|
|
if (opt.RevisionRange.Length > 0) args += $"{opt.RevisionRange[0]} ";
|
|
|
|
if (opt.RevisionRange.Length > 1) args += $"{opt.RevisionRange[1]} ";
|
|
|
|
|
|
|
|
args += "-- ";
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(opt.OrgPath)) args += $"\"{opt.OrgPath}\" ";
|
|
|
|
args += $"\"{opt.Path}\"";
|
|
|
|
|
2021-06-06 23:14:53 -07:00
|
|
|
if (opt.UseLFS) {
|
|
|
|
var isLFSObject = new Commands.LFS(repo).IsFiltered(opt.Path);
|
|
|
|
if (isLFSObject) {
|
|
|
|
var lc = new Commands.QueryLFSObjectChange(repo, args).Result();
|
|
|
|
if (lc.IsValid) {
|
|
|
|
SetLFSChange(lc, dummy);
|
|
|
|
} else {
|
|
|
|
SetSame(dummy);
|
|
|
|
}
|
|
|
|
return;
|
2021-04-29 05:05:55 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var rs = new Commands.Diff(repo, args).Result();
|
|
|
|
if (rs.IsBinary) {
|
|
|
|
var fsc = new Commands.QueryFileSizeChange(repo, opt.RevisionRange, opt.Path, opt.OrgPath).Result();
|
2021-04-29 18:25:52 -07:00
|
|
|
SetSizeChange(fsc, dummy);
|
2021-04-29 05:05:55 -07:00
|
|
|
} else if (rs.Lines.Count > 0) {
|
|
|
|
cachedTextChanges = rs.Lines;
|
2021-04-29 18:25:52 -07:00
|
|
|
SetTextChange(dummy);
|
2021-04-29 05:05:55 -07:00
|
|
|
} else {
|
2021-04-29 18:25:52 -07:00
|
|
|
SetSame(dummy);
|
2021-04-29 05:05:55 -07:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#region LAYOUT_DATA
|
|
|
|
private void SetTitle(string file, string orgFile) {
|
|
|
|
txtFileName.Text = file;
|
|
|
|
if (!string.IsNullOrEmpty(orgFile) && orgFile != "/dev/null") {
|
|
|
|
orgFileNamePanel.Visibility = Visibility.Visible;
|
|
|
|
txtOrgFileName.Text = orgFile;
|
|
|
|
} else {
|
|
|
|
orgFileNamePanel.Visibility = Visibility.Collapsed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-29 18:25:52 -07:00
|
|
|
private void SetTextChange(ulong dummy) {
|
2021-04-29 05:05:55 -07:00
|
|
|
if (cachedTextChanges == null) return;
|
|
|
|
|
|
|
|
if (Models.Preference.Instance.Window.UseCombinedDiff) {
|
2021-04-29 18:25:52 -07:00
|
|
|
MakeCombinedViewer(dummy);
|
2021-04-29 05:05:55 -07:00
|
|
|
} else {
|
2021-04-29 18:25:52 -07:00
|
|
|
MakeSideBySideViewer(dummy);
|
2021-04-29 05:05:55 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-29 18:25:52 -07:00
|
|
|
private void SetSizeChange(Models.FileSizeChange fsc, ulong dummy) {
|
2021-04-29 05:05:55 -07:00
|
|
|
Dispatcher.Invoke(() => {
|
2021-04-29 18:25:52 -07:00
|
|
|
if (dummy != seq) return;
|
|
|
|
|
2021-04-29 05:05:55 -07:00
|
|
|
loading.Visibility = Visibility.Collapsed;
|
|
|
|
mask.Visibility = Visibility.Collapsed;
|
|
|
|
toolbarOptions.Visibility = Visibility.Collapsed;
|
|
|
|
sizeChange.Visibility = Visibility.Visible;
|
|
|
|
|
|
|
|
txtSizeChangeTitle.Text = App.Text("Diff.Binary");
|
|
|
|
iconSizeChange.Data = FindResource("Icon.Binary") as Geometry;
|
|
|
|
txtOldSize.Text = App.Text("Bytes", fsc.OldSize);
|
|
|
|
txtNewSize.Text = App.Text("Bytes", fsc.NewSize);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-04-29 18:25:52 -07:00
|
|
|
private void SetLFSChange(Models.LFSChange lc, ulong dummy) {
|
2021-04-29 05:05:55 -07:00
|
|
|
Dispatcher.Invoke(() => {
|
2021-04-29 18:25:52 -07:00
|
|
|
if (dummy != seq) return;
|
|
|
|
|
2021-04-29 05:05:55 -07:00
|
|
|
var oldSize = lc.Old == null ? 0 : lc.Old.Size;
|
|
|
|
var newSize = lc.New == null ? 0 : lc.New.Size;
|
|
|
|
|
|
|
|
loading.Visibility = Visibility.Collapsed;
|
|
|
|
mask.Visibility = Visibility.Collapsed;
|
|
|
|
toolbarOptions.Visibility = Visibility.Collapsed;
|
|
|
|
sizeChange.Visibility = Visibility.Visible;
|
|
|
|
|
|
|
|
txtSizeChangeTitle.Text = App.Text("Diff.LFS");
|
|
|
|
iconSizeChange.Data = FindResource("Icon.LFS") as Geometry;
|
|
|
|
txtNewSize.Text = App.Text("Bytes", newSize);
|
|
|
|
txtOldSize.Text = App.Text("Bytes", oldSize);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-04-29 18:25:52 -07:00
|
|
|
private void SetSame(ulong dummy) {
|
2021-04-29 05:05:55 -07:00
|
|
|
Dispatcher.Invoke(() => {
|
2021-04-29 18:25:52 -07:00
|
|
|
if (dummy != seq) return;
|
|
|
|
|
2021-04-29 05:05:55 -07:00
|
|
|
loading.Visibility = Visibility.Collapsed;
|
2021-08-05 00:54:00 -07:00
|
|
|
mask.Visibility = Visibility.Collapsed;
|
2021-04-29 05:05:55 -07:00
|
|
|
toolbarOptions.Visibility = Visibility.Collapsed;
|
|
|
|
noChange.Visibility = Visibility.Visible;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-04-29 18:25:52 -07:00
|
|
|
private void MakeCombinedViewer(ulong dummy) {
|
2021-04-29 05:05:55 -07:00
|
|
|
var lastOldLine = "";
|
|
|
|
var lastNewLine = "";
|
2021-08-04 18:29:26 -07:00
|
|
|
var foundOld = false;
|
|
|
|
var foundNew = false;
|
|
|
|
|
|
|
|
for (int i = cachedTextChanges.Count - 1; i >= 0; i--) {
|
|
|
|
var line = cachedTextChanges[i];
|
|
|
|
if (!foundOld && line.OldLine.Length > 0) {
|
|
|
|
lastOldLine = line.OldLine;
|
|
|
|
if (foundNew) break;
|
|
|
|
foundOld = true;
|
|
|
|
}
|
2021-04-29 05:05:55 -07:00
|
|
|
|
2021-08-04 18:29:26 -07:00
|
|
|
if (!foundNew && line.NewLine.Length > 0) {
|
|
|
|
lastNewLine = line.NewLine;
|
|
|
|
if (foundOld) break;
|
|
|
|
foundNew = true;
|
|
|
|
}
|
2021-04-29 05:05:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
Dispatcher.Invoke(() => {
|
2021-04-29 18:25:52 -07:00
|
|
|
if (dummy != seq) return;
|
|
|
|
|
2021-04-29 05:05:55 -07:00
|
|
|
loading.Visibility = Visibility.Collapsed;
|
|
|
|
mask.Visibility = Visibility.Collapsed;
|
|
|
|
toolbarOptions.Visibility = Visibility.Visible;
|
|
|
|
|
|
|
|
var createEditor = editors.Count == 0;
|
|
|
|
var lineNumberWidth = CalcLineNumberColWidth(lastOldLine, lastNewLine);
|
|
|
|
var minWidth = textDiff.ActualWidth - lineNumberWidth * 2;
|
|
|
|
if (textDiff.ActualHeight < cachedTextChanges.Count * 16) minWidth -= 8;
|
|
|
|
|
|
|
|
DataGrid editor;
|
|
|
|
if (createEditor) {
|
|
|
|
editor = CreateTextEditor(new string[] { "OldLine", "NewLine" });
|
|
|
|
editor.SetValue(Grid.ColumnProperty, 0);
|
|
|
|
editor.SetValue(Grid.ColumnSpanProperty, 2);
|
|
|
|
editors.Add(editor);
|
|
|
|
textDiff.Children.Add(editor);
|
|
|
|
|
|
|
|
AddSplitter(0, Math.Floor(lineNumberWidth));
|
|
|
|
AddSplitter(0, Math.Floor(lineNumberWidth) * 2);
|
|
|
|
} else {
|
|
|
|
editor = editors[0];
|
|
|
|
splitters[0].Margin = new Thickness(Math.Floor(lineNumberWidth), 0, 0, 0);
|
|
|
|
splitters[1].Margin = new Thickness(Math.Floor(lineNumberWidth) * 2, 0, 0, 0);
|
|
|
|
}
|
2021-04-30 01:11:19 -07:00
|
|
|
|
|
|
|
foreach (var s in splitters) s.Visibility = Visibility.Visible;
|
2021-08-05 00:54:00 -07:00
|
|
|
|
2021-04-29 05:05:55 -07:00
|
|
|
editor.Columns[0].Width = new DataGridLength(lineNumberWidth, DataGridLengthUnitType.Pixel);
|
|
|
|
editor.Columns[1].Width = new DataGridLength(lineNumberWidth, DataGridLengthUnitType.Pixel);
|
|
|
|
editor.Columns[2].MinWidth = minWidth;
|
2021-08-04 18:29:26 -07:00
|
|
|
editor.SetBinding(DataGrid.ItemsSourceProperty, new Binding() { Source = cachedTextChanges, IsAsync = true });
|
2021-04-29 05:05:55 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-04-29 18:25:52 -07:00
|
|
|
private void MakeSideBySideViewer(ulong dummy) {
|
2021-04-29 05:05:55 -07:00
|
|
|
var lastOldLine = "";
|
|
|
|
var lastNewLine = "";
|
2021-08-04 18:29:26 -07:00
|
|
|
var oldSideBlocks = new List<Models.TextChanges.Line>();
|
|
|
|
var newSideBlocks = new List<Models.TextChanges.Line>();
|
2021-04-29 05:05:55 -07:00
|
|
|
|
|
|
|
foreach (var line in cachedTextChanges) {
|
|
|
|
switch (line.Mode) {
|
|
|
|
case Models.TextChanges.LineMode.Added:
|
2021-08-04 18:29:26 -07:00
|
|
|
newSideBlocks.Add(line);
|
|
|
|
lastNewLine = line.NewLine;
|
2021-04-29 05:05:55 -07:00
|
|
|
break;
|
|
|
|
case Models.TextChanges.LineMode.Deleted:
|
2021-08-04 18:29:26 -07:00
|
|
|
oldSideBlocks.Add(line);
|
|
|
|
lastOldLine = line.OldLine;
|
2021-04-29 05:05:55 -07:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
FillEmptyLines(oldSideBlocks, newSideBlocks);
|
2021-08-04 18:29:26 -07:00
|
|
|
oldSideBlocks.Add(line);
|
|
|
|
newSideBlocks.Add(line);
|
|
|
|
lastNewLine = line.NewLine;
|
|
|
|
lastOldLine = line.OldLine;
|
2021-04-29 05:05:55 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
FillEmptyLines(oldSideBlocks, newSideBlocks);
|
|
|
|
|
|
|
|
Dispatcher.Invoke(() => {
|
2021-04-29 18:25:52 -07:00
|
|
|
if (dummy != seq) return;
|
|
|
|
|
2021-04-29 05:05:55 -07:00
|
|
|
loading.Visibility = Visibility.Collapsed;
|
|
|
|
mask.Visibility = Visibility.Collapsed;
|
|
|
|
toolbarOptions.Visibility = Visibility.Visible;
|
|
|
|
|
|
|
|
var createEditor = editors.Count == 0;
|
|
|
|
var lineNumberWidth = CalcLineNumberColWidth(lastOldLine, lastNewLine);
|
|
|
|
var minWidth = textDiff.ActualWidth / 2 - lineNumberWidth;
|
|
|
|
if (textDiff.ActualHeight < newSideBlocks.Count * 16) minWidth -= 8;
|
|
|
|
|
|
|
|
DataGrid oldEditor, newEditor;
|
|
|
|
if (createEditor) {
|
|
|
|
oldEditor = CreateTextEditor(new string[] { "OldLine" });
|
|
|
|
oldEditor.SetValue(Grid.ColumnProperty, 0);
|
|
|
|
oldEditor.AddHandler(ScrollViewer.ScrollChangedEvent, new ScrollChangedEventHandler(OnTextDiffSyncScroll));
|
2021-05-24 22:50:33 -07:00
|
|
|
oldEditor.SelectionChanged += new SelectionChangedEventHandler(OnTextDiffSyncSelected);
|
2021-04-29 05:05:55 -07:00
|
|
|
|
|
|
|
newEditor = CreateTextEditor(new string[] { "NewLine" });
|
|
|
|
newEditor.SetValue(Grid.ColumnProperty, 1);
|
|
|
|
newEditor.AddHandler(ScrollViewer.ScrollChangedEvent, new ScrollChangedEventHandler(OnTextDiffSyncScroll));
|
2021-05-24 22:50:33 -07:00
|
|
|
newEditor.SelectionChanged += new SelectionChangedEventHandler(OnTextDiffSyncSelected);
|
2021-04-29 05:05:55 -07:00
|
|
|
|
|
|
|
editors.Add(oldEditor);
|
|
|
|
editors.Add(newEditor);
|
|
|
|
textDiff.Children.Add(oldEditor);
|
|
|
|
textDiff.Children.Add(newEditor);
|
|
|
|
|
|
|
|
AddSplitter(0, Math.Floor(lineNumberWidth));
|
|
|
|
AddSplitter(1, 0);
|
|
|
|
AddSplitter(1, Math.Floor(lineNumberWidth));
|
|
|
|
} else {
|
|
|
|
oldEditor = editors[0];
|
|
|
|
newEditor = editors[1];
|
|
|
|
|
|
|
|
splitters[0].Margin = new Thickness(Math.Floor(lineNumberWidth), 0, 0, 0);
|
|
|
|
splitters[2].Margin = new Thickness(Math.Floor(lineNumberWidth), 0, 0, 0);
|
|
|
|
}
|
2021-04-30 01:11:19 -07:00
|
|
|
|
|
|
|
foreach (var s in splitters) s.Visibility = Visibility.Visible;
|
|
|
|
|
2021-04-29 05:05:55 -07:00
|
|
|
oldEditor.Columns[0].Width = new DataGridLength(lineNumberWidth, DataGridLengthUnitType.Pixel);
|
|
|
|
oldEditor.Columns[1].MinWidth = minWidth;
|
2021-04-29 18:41:19 -07:00
|
|
|
oldEditor.SetBinding(DataGrid.ItemsSourceProperty, new Binding() { Source = oldSideBlocks, IsAsync = true });
|
2021-04-29 05:05:55 -07:00
|
|
|
|
|
|
|
newEditor.Columns[0].Width = new DataGridLength(lineNumberWidth, DataGridLengthUnitType.Pixel);
|
|
|
|
newEditor.Columns[1].MinWidth = minWidth;
|
2021-04-29 18:41:19 -07:00
|
|
|
newEditor.SetBinding(DataGrid.ItemsSourceProperty, new Binding() { Source = newSideBlocks, IsAsync = true });
|
2021-04-29 05:05:55 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-08-04 18:29:26 -07:00
|
|
|
private void FillEmptyLines(List<Models.TextChanges.Line> old, List<Models.TextChanges.Line> cur) {
|
2021-04-29 05:05:55 -07:00
|
|
|
if (old.Count < cur.Count) {
|
|
|
|
int diff = cur.Count - old.Count;
|
2021-08-04 18:29:26 -07:00
|
|
|
for (int i = 0; i < diff; i++) old.Add(new Models.TextChanges.Line());
|
2021-04-29 05:05:55 -07:00
|
|
|
} else if (old.Count > cur.Count) {
|
|
|
|
int diff = old.Count - cur.Count;
|
2021-08-04 18:29:26 -07:00
|
|
|
for (int i = 0; i < diff; i++) cur.Add(new Models.TextChanges.Line());
|
2021-04-29 05:05:55 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void AddSplitter(int column, double offset) {
|
|
|
|
var split = new Rectangle();
|
|
|
|
split.Width = 1;
|
|
|
|
split.HorizontalAlignment = HorizontalAlignment.Left;
|
|
|
|
split.Margin = new Thickness(offset, 0, 0, 0);
|
|
|
|
split.SetValue(Grid.ColumnProperty, column);
|
2021-07-20 01:26:10 -07:00
|
|
|
split.SetResourceReference(Rectangle.FillProperty, "Brush.Border2");
|
2021-04-29 05:05:55 -07:00
|
|
|
|
|
|
|
textDiff.Children.Add(split);
|
|
|
|
splitters.Add(split);
|
|
|
|
}
|
|
|
|
|
|
|
|
private DataGrid CreateTextEditor(string[] lineNumbers) {
|
|
|
|
var grid = new DataGrid();
|
2021-04-29 07:35:02 -07:00
|
|
|
grid.EnableRowVirtualization = true;
|
|
|
|
grid.EnableColumnVirtualization = true;
|
2021-04-29 05:05:55 -07:00
|
|
|
grid.RowHeight = 16.0;
|
|
|
|
grid.FrozenColumnCount = lineNumbers.Length;
|
|
|
|
grid.RowStyle = FindResource("Style.DataGridRow.DiffViewer") as Style;
|
2021-05-12 06:01:08 -07:00
|
|
|
grid.ContextMenuOpening += OnTextDiffContextMenuOpening;
|
|
|
|
grid.PreviewMouseWheel += OnTextDiffPreviewMouseWheel;
|
2021-04-29 05:05:55 -07:00
|
|
|
grid.CommandBindings.Add(new CommandBinding(ApplicationCommands.Copy, (o, e) => {
|
|
|
|
var items = (o as DataGrid).SelectedItems;
|
|
|
|
if (items.Count == 0) return;
|
|
|
|
|
|
|
|
var builder = new StringBuilder();
|
|
|
|
foreach (var item in items) {
|
2021-08-04 18:29:26 -07:00
|
|
|
var block = item as Models.TextChanges.Line;
|
2021-04-29 05:05:55 -07:00
|
|
|
if (block == null) continue;
|
2021-08-04 18:29:26 -07:00
|
|
|
if (!block.IsContent) continue;
|
2021-04-29 05:05:55 -07:00
|
|
|
|
2021-08-04 18:29:26 -07:00
|
|
|
builder.Append(block.Content);
|
2021-04-29 05:05:55 -07:00
|
|
|
builder.AppendLine();
|
|
|
|
}
|
|
|
|
|
|
|
|
Clipboard.SetText(builder.ToString());
|
|
|
|
}));
|
|
|
|
|
|
|
|
foreach (var number in lineNumbers) {
|
|
|
|
var colLineNumber = new DataGridTextColumn();
|
|
|
|
colLineNumber.IsReadOnly = true;
|
|
|
|
colLineNumber.Binding = new Binding(number);
|
|
|
|
colLineNumber.ElementStyle = FindResource("Style.TextBlock.LineNumber") as Style;
|
|
|
|
grid.Columns.Add(colLineNumber);
|
|
|
|
}
|
|
|
|
|
2021-07-28 00:02:06 -07:00
|
|
|
var line = new FrameworkElementFactory(typeof(Controls.HighlightableTextBlock));
|
2021-08-04 18:29:26 -07:00
|
|
|
line.SetBinding(Controls.HighlightableTextBlock.DataProperty, new Binding("."));
|
2021-04-29 05:05:55 -07:00
|
|
|
|
|
|
|
var colContent = new DataGridTemplateColumn();
|
|
|
|
colContent.CellTemplate = new DataTemplate();
|
2021-07-28 00:02:06 -07:00
|
|
|
colContent.CellTemplate.VisualTree = line;
|
2021-04-29 05:05:55 -07:00
|
|
|
colContent.Width = DataGridLength.SizeToCells;
|
|
|
|
grid.Columns.Add(colContent);
|
|
|
|
|
|
|
|
return grid;
|
|
|
|
}
|
|
|
|
|
|
|
|
private double CalcLineNumberColWidth(string oldLine, string newLine) {
|
|
|
|
var number = oldLine;
|
|
|
|
if (newLine.Length > oldLine.Length) number = newLine;
|
|
|
|
|
|
|
|
var formatted = new FormattedText(
|
|
|
|
number,
|
|
|
|
CultureInfo.CurrentCulture,
|
|
|
|
FlowDirection.LeftToRight,
|
|
|
|
new Typeface(FontFamily, FontStyles.Normal, FontWeights.Normal, FontStretches.Normal),
|
|
|
|
12.0,
|
|
|
|
Brushes.Black,
|
|
|
|
VisualTreeHelper.GetDpi(this).PixelsPerDip);
|
|
|
|
|
|
|
|
return formatted.Width + 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void ClearCache() {
|
|
|
|
repo = null;
|
|
|
|
opt = null;
|
|
|
|
cachedTextChanges = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
private T GetVisualChild<T>(DependencyObject parent) where T : Visual {
|
|
|
|
T child = null;
|
|
|
|
|
|
|
|
int count = VisualTreeHelper.GetChildrenCount(parent);
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
|
|
|
|
child = v as T;
|
|
|
|
|
|
|
|
if (child == null) {
|
|
|
|
child = GetVisualChild<T>(v);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (child != null) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return child;
|
|
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region EVENTS
|
|
|
|
private void OnDiffViewModeChanged(object sender, RoutedEventArgs e) {
|
|
|
|
if (editors.Count > 0) {
|
2021-04-29 18:25:52 -07:00
|
|
|
editors.Clear();
|
|
|
|
splitters.Clear();
|
|
|
|
textDiff.Children.Clear();
|
|
|
|
|
|
|
|
SetTextChange(seq);
|
2021-04-29 05:05:55 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void OnTextDiffSizeChanged(object sender, SizeChangedEventArgs e) {
|
|
|
|
if (editors.Count == 0) return;
|
|
|
|
|
|
|
|
var total = textDiff.ActualWidth / editors.Count;
|
|
|
|
for (int i = 0; i < editors.Count; i++) {
|
|
|
|
var editor = editors[i];
|
|
|
|
var minWidth = total - editor.NonFrozenColumnsViewportHorizontalOffset;
|
|
|
|
if (editor.Items.Count * 16 > textDiff.ActualHeight) minWidth -= 8;
|
|
|
|
|
|
|
|
var lastColumn = editor.Columns.Count - 1;
|
|
|
|
editor.Columns[lastColumn].MinWidth = minWidth;
|
|
|
|
editor.Columns[lastColumn].Width = DataGridLength.SizeToCells;
|
|
|
|
editor.UpdateLayout();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void OnTextDiffContextMenuOpening(object sender, ContextMenuEventArgs e) {
|
|
|
|
var grid = sender as DataGrid;
|
|
|
|
if (grid == null) return;
|
|
|
|
|
|
|
|
var menu = new ContextMenu();
|
|
|
|
|
|
|
|
var copyIcon = new Path();
|
|
|
|
copyIcon.Data = FindResource("Icon.Copy") as Geometry;
|
|
|
|
copyIcon.Width = 10;
|
|
|
|
|
|
|
|
var copy = new MenuItem();
|
|
|
|
copy.Header = App.Text("Diff.Copy");
|
|
|
|
copy.Icon = copyIcon;
|
|
|
|
copy.Click += (o, ev) => {
|
|
|
|
var items = grid.SelectedItems;
|
|
|
|
if (items.Count == 0) return;
|
|
|
|
|
|
|
|
var builder = new StringBuilder();
|
|
|
|
foreach (var item in items) {
|
2021-08-04 18:29:26 -07:00
|
|
|
var block = item as Models.TextChanges.Line;
|
2021-04-29 05:05:55 -07:00
|
|
|
if (block == null) continue;
|
2021-08-04 18:29:26 -07:00
|
|
|
if (!block.IsContent) continue;
|
2021-04-29 05:05:55 -07:00
|
|
|
|
2021-08-04 18:29:26 -07:00
|
|
|
builder.Append(block.Content);
|
2021-04-29 05:05:55 -07:00
|
|
|
builder.AppendLine();
|
|
|
|
}
|
|
|
|
|
|
|
|
Clipboard.SetText(builder.ToString());
|
|
|
|
};
|
|
|
|
menu.Items.Add(copy);
|
|
|
|
menu.IsOpen = true;
|
|
|
|
e.Handled = true;
|
|
|
|
}
|
|
|
|
|
2021-05-12 06:01:08 -07:00
|
|
|
private void OnTextDiffPreviewMouseWheel(object sender, MouseWheelEventArgs e) {
|
2021-05-12 23:17:53 -07:00
|
|
|
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) {
|
2021-05-12 06:01:08 -07:00
|
|
|
var editor = sender as DataGrid;
|
|
|
|
if (editor == null) return;
|
|
|
|
|
|
|
|
var scroller = GetVisualChild<ScrollViewer>(editor);
|
|
|
|
if (scroller == null || scroller.ComputedHorizontalScrollBarVisibility != Visibility.Visible) return;
|
|
|
|
|
|
|
|
if (e.Delta > 0) scroller.LineLeft();
|
|
|
|
else scroller.LineRight();
|
|
|
|
|
|
|
|
e.Handled = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-29 05:05:55 -07:00
|
|
|
private void OnTextDiffSyncScroll(object sender, ScrollChangedEventArgs e) {
|
|
|
|
foreach (var editor in editors) {
|
|
|
|
var scroller = GetVisualChild<ScrollViewer>(editor);
|
|
|
|
if (scroller == null) continue;
|
|
|
|
|
|
|
|
if (e.VerticalChange != 0 && scroller.VerticalOffset != e.VerticalOffset) {
|
|
|
|
scroller.ScrollToVerticalOffset(e.VerticalOffset);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (e.HorizontalChange != 0 && scroller.HorizontalOffset != e.HorizontalOffset) {
|
|
|
|
scroller.ScrollToHorizontalOffset(e.HorizontalOffset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-24 22:50:33 -07:00
|
|
|
private void OnTextDiffSyncSelected(object sender, SelectionChangedEventArgs e) {
|
|
|
|
DataGrid dG = sender as DataGrid;
|
|
|
|
int Index = dG.SelectedIndex;
|
|
|
|
|
|
|
|
foreach (var editor in editors) {
|
|
|
|
if (editor == sender || editor.SelectedIndex == Index) { /// 对于事件发起者不用处理
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
editor.SelectedIndex = Index; ///* 这里更改后,会导致再次进入改接口
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-29 05:05:55 -07:00
|
|
|
private void OnTextDiffBringIntoView(object sender, RequestBringIntoViewEventArgs e) {
|
|
|
|
e.Handled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void GotoPrevChange(object sender, RoutedEventArgs e) {
|
|
|
|
if (editors.Count == 0) return;
|
|
|
|
|
|
|
|
var grid = editors[0];
|
|
|
|
var scroller = GetVisualChild<ScrollViewer>(grid);
|
|
|
|
if (scroller == null) return;
|
|
|
|
|
|
|
|
var firstVisible = (int)scroller.VerticalOffset;
|
|
|
|
var firstModeEnded = false;
|
2021-08-04 18:29:26 -07:00
|
|
|
var first = grid.Items[firstVisible] as Models.TextChanges.Line;
|
2021-04-29 05:05:55 -07:00
|
|
|
for (int i = firstVisible - 1; i >= 0; i--) {
|
2021-08-04 18:29:26 -07:00
|
|
|
var next = grid.Items[i] as Models.TextChanges.Line;
|
|
|
|
if (next.IsDifference) {
|
|
|
|
if (firstModeEnded || next.Mode != first.Mode) {
|
2021-04-29 05:05:55 -07:00
|
|
|
scroller.ScrollToVerticalOffset(i);
|
2021-05-24 22:53:29 -07:00
|
|
|
grid.SelectedIndex = i;
|
2021-04-29 05:05:55 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
firstModeEnded = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void GotoNextChange(object sender, RoutedEventArgs e) {
|
|
|
|
if (editors.Count == 0) return;
|
|
|
|
|
|
|
|
var grid = editors[0];
|
|
|
|
var scroller = GetVisualChild<ScrollViewer>(grid);
|
|
|
|
if (scroller == null) return;
|
|
|
|
|
|
|
|
var firstVisible = (int)scroller.VerticalOffset;
|
|
|
|
var firstModeEnded = false;
|
2021-08-04 18:29:26 -07:00
|
|
|
var first = grid.Items[firstVisible] as Models.TextChanges.Line;
|
2021-04-29 05:05:55 -07:00
|
|
|
for (int i = firstVisible + 1; i < grid.Items.Count; i++) {
|
2021-08-04 18:29:26 -07:00
|
|
|
var next = grid.Items[i] as Models.TextChanges.Line;
|
|
|
|
if (next.IsDifference) {
|
|
|
|
if (firstModeEnded || next.Mode != first.Mode) {
|
2021-04-29 05:05:55 -07:00
|
|
|
scroller.ScrollToVerticalOffset(i);
|
2021-05-24 22:53:29 -07:00
|
|
|
grid.SelectedIndex = i;
|
2021-04-29 05:05:55 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
firstModeEnded = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-05-30 19:25:48 -07:00
|
|
|
|
|
|
|
private async void OpenWithMerger(object sender, RoutedEventArgs e) {
|
|
|
|
var mergeType = Models.Preference.Instance.MergeTool.Type;
|
|
|
|
var mergeExe = Models.Preference.Instance.MergeTool.Path;
|
|
|
|
|
|
|
|
var merger = Models.MergeTool.Supported.Find(x => x.Type == mergeType);
|
|
|
|
if (merger == null || merger.Type == 0 || !System.IO.File.Exists(mergeExe)) {
|
|
|
|
Models.Exception.Raise("Invalid merge tool in preference setting!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var args = $"{opt.ExtraArgs} ";
|
|
|
|
if (opt.RevisionRange.Length > 0) args += $"{opt.RevisionRange[0]} ";
|
|
|
|
if (opt.RevisionRange.Length > 1) args += $"{opt.RevisionRange[1]} ";
|
|
|
|
|
|
|
|
args += "-- ";
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(opt.OrgPath)) args += $"\"{opt.OrgPath}\" ";
|
|
|
|
args += $"\"{opt.Path}\"";
|
|
|
|
|
|
|
|
var cmd = new Commands.Command();
|
|
|
|
cmd.Cwd = repo;
|
|
|
|
cmd.DontRaiseError = true;
|
|
|
|
cmd.Args = $"-c difftool.sourcegit.cmd=\"\\\"{mergeExe}\\\" {merger.DiffCmd}\" ";
|
|
|
|
cmd.Args += $"difftool --tool=sourcegit --no-prompt {args}";
|
|
|
|
|
|
|
|
await Task.Run(() => cmd.Exec());
|
|
|
|
e.Handled = true;
|
|
|
|
}
|
2021-04-29 05:05:55 -07:00
|
|
|
#endregion
|
|
|
|
}
|
|
|
|
}
|