diff --git a/src/Helpers/Avatar.cs b/src/Helpers/Avatar.cs
new file mode 100644
index 00000000..2d7026ea
--- /dev/null
+++ b/src/Helpers/Avatar.cs
@@ -0,0 +1,185 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Security.Cryptography;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+
+namespace SourceGit.Helpers {
+
+ ///
+ /// Avatar control
+ ///
+ public class Avatar : Image {
+
+ ///
+ /// Colors used in avatar
+ ///
+ public static Brush[] Colors = new Brush[] {
+ Brushes.DarkBlue,
+ Brushes.DarkCyan,
+ Brushes.DarkGoldenrod,
+ Brushes.DarkGray,
+ Brushes.DarkGreen,
+ Brushes.DarkKhaki,
+ Brushes.DarkMagenta,
+ Brushes.DarkOliveGreen,
+ Brushes.DarkOrange,
+ Brushes.DarkOrchid,
+ Brushes.DarkRed,
+ Brushes.DarkSalmon,
+ Brushes.DarkSeaGreen,
+ Brushes.DarkSlateBlue,
+ Brushes.DarkSlateGray,
+ Brushes.DarkTurquoise,
+ Brushes.DarkViolet
+ };
+
+ ///
+ /// User property definition.
+ ///
+ public static readonly DependencyProperty UserProperty = DependencyProperty.Register(
+ "User",
+ typeof(Git.User),
+ typeof(Avatar),
+ new PropertyMetadata(null, OnUserChanged));
+
+ ///
+ /// User property
+ ///
+ public Git.User User {
+ get { return (Git.User)GetValue(UserProperty); }
+ set { SetValue(UserProperty, value); }
+ }
+
+ ///
+ /// Loading request
+ ///
+ private class Request {
+ public BitmapImage img = null;
+ public List targets = new List();
+ }
+
+ ///
+ /// Path to cache downloaded avatars
+ ///
+ private static readonly string CACHE_PATH = Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
+ "SourceGit",
+ "avatars");
+
+ ///
+ /// Current requests.
+ ///
+ private static Dictionary requesting = new Dictionary();
+
+ ///
+ /// Render implementation.
+ ///
+ ///
+ protected override void OnRender(DrawingContext dc) {
+ base.OnRender(dc);
+
+ if (Source == null && User != null) {
+ var placeholder = User.Name.Length > 0 ? User.Name.Substring(0, 1) : "?";
+ var formatted = new FormattedText(
+ placeholder,
+ CultureInfo.CurrentCulture,
+ FlowDirection.LeftToRight,
+ new Typeface(new FontFamily("Consolas"), FontStyles.Normal, FontWeights.Normal, FontStretches.Normal),
+ Width * 0.75,
+ Brushes.White,
+ VisualTreeHelper.GetDpi(this).PixelsPerDip);
+
+ double offsetX = 0;
+ if (HorizontalAlignment == HorizontalAlignment.Right) {
+ offsetX = -Width * 0.5;
+ }
+
+ var chars = placeholder.ToCharArray();
+ var sum = 0;
+ foreach (var ch in chars) sum += Math.Abs(ch);
+ var brush = Colors[sum % Colors.Length];
+
+ dc.DrawRoundedRectangle(brush, null, new Rect(-Width * 0.5 + offsetX, -Height * 0.5, Width, Height), Width / 16, Height / 16);
+ dc.DrawText(formatted, new Point(formatted.Width * -0.5 + offsetX, formatted.Height * -0.5));
+ }
+ }
+
+ ///
+ /// Reset image.
+ ///
+ private void ReloadImage(Git.User oldUser) {
+ if (oldUser != null && requesting.ContainsKey(oldUser.Email)) {
+ if (requesting[oldUser.Email].targets.Count <= 1) {
+ requesting.Remove(oldUser.Email);
+ } else {
+ requesting[oldUser.Email].targets.Remove(this);
+ }
+ }
+
+ Source = null;
+ InvalidateVisual();
+
+ if (User == null) return;
+
+ var email = User.Email;
+ if (requesting.ContainsKey(email)) {
+ requesting[email].targets.Add(this);
+ return;
+ }
+
+ byte[] hash = MD5.Create().ComputeHash(Encoding.Default.GetBytes(email.ToLower().Trim()));
+ string md5 = "";
+ for (int i = 0; i < hash.Length; i++) md5 += hash[i].ToString("x2");
+ md5 = md5.ToLower();
+
+ string filePath = Path.Combine(CACHE_PATH, md5);
+ if (File.Exists(filePath)) {
+ Source = new BitmapImage(new Uri(filePath));
+ return;
+ }
+
+ requesting.Add(email, new Request());
+ requesting[email].targets.Add(this);
+
+ BitmapImage downloading = new BitmapImage(new Uri("https://www.gravatar.com/avatar/" + md5 + "?d=404"));
+ requesting[email].img = downloading;
+ downloading.DownloadCompleted += (o, e) => {
+ var owner = o as BitmapImage;
+ if (owner != null) {
+ if (!Directory.Exists(CACHE_PATH)) Directory.CreateDirectory(CACHE_PATH);
+
+ var encoder = new PngBitmapEncoder();
+ encoder.Frames.Add(BitmapFrame.Create(owner));
+ using (var fs = new FileStream(filePath, FileMode.Create)) {
+ encoder.Save(fs);
+ }
+
+ if (requesting.ContainsKey(email)) {
+ BitmapImage exists = new BitmapImage(new Uri(filePath));
+ foreach (var one in requesting[email].targets) one.Source = exists;
+ requesting.Remove(email);
+ }
+ }
+ };
+ downloading.DownloadFailed += (o, e) => {
+ requesting.Remove(email);
+ };
+ }
+
+ ///
+ /// Callback on user property changed
+ ///
+ ///
+ ///
+ private static void OnUserChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
+ Avatar a = d as Avatar;
+ if (a != null) a.ReloadImage(e.OldValue as Git.User);
+ }
+ }
+}
diff --git a/src/UI/CommitViewer.xaml b/src/UI/CommitViewer.xaml
index bc4db91a..e5104435 100644
--- a/src/UI/CommitViewer.xaml
+++ b/src/UI/CommitViewer.xaml
@@ -62,11 +62,7 @@
-
-
-
-
-
+
@@ -89,11 +85,7 @@
-
-
-
-
-
+
diff --git a/src/UI/CommitViewer.xaml.cs b/src/UI/CommitViewer.xaml.cs
index 930c65b0..9e72bdc2 100644
--- a/src/UI/CommitViewer.xaml.cs
+++ b/src/UI/CommitViewer.xaml.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
-using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
@@ -20,11 +19,6 @@ namespace SourceGit.UI {
/// Commit detail viewer
///
public partial class CommitViewer : UserControl {
- private static readonly string AVATAR_PATH = Path.Combine(
- Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
- "SourceGit",
- "avatars");
-
private Git.Repository repo = null;
private Git.Commit commit = null;
private List cachedChanges = new List();
@@ -104,31 +98,17 @@ namespace SourceGit.UI {
authorName.Text = commit.Author.Name;
authorEmail.Text = commit.Author.Email;
authorTime.Text = commit.Author.Time;
+ authorAvatar.User = commit.Author;
- if (commit.Committer.Email == commit.Author.Email) {
- if (commit.Committer.Time == commit.Author.Time) {
- committerPanel.Visibility = Visibility.Hidden;
+ committerName.Text = commit.Committer.Name;
+ committerEmail.Text = commit.Committer.Email;
+ committerTime.Text = commit.Committer.Time;
+ committerAvatar.User = commit.Committer;
- SetAvatar(authorAvatar, authorAvatarMask, commit.Author.Email);
- } else {
- committerPanel.Visibility = Visibility.Visible;
-
- committerName.Text = commit.Committer.Name;
- committerEmail.Text = commit.Committer.Email;
- committerTime.Text = commit.Committer.Time;
-
- SetAvatar(authorAvatar, authorAvatarMask, commit.Author.Email);
- SetAvatar(committerAvatar, committerAvatarMask, commit.Committer.Email, false);
- }
+ if (commit.Committer.Email == commit.Author.Email && commit.Committer.Time == commit.Author.Time) {
+ committerPanel.Visibility = Visibility.Hidden;
} else {
committerPanel.Visibility = Visibility.Visible;
-
- committerName.Text = commit.Committer.Name;
- committerEmail.Text = commit.Committer.Email;
- committerTime.Text = commit.Committer.Time;
-
- SetAvatar(authorAvatar, authorAvatarMask, commit.Author.Email);
- SetAvatar(committerAvatar, committerAvatarMask, commit.Committer.Email);
}
if (commit.Decorators.Count == 0) {
@@ -138,41 +118,6 @@ namespace SourceGit.UI {
}
}
- private void SetAvatar(Image img, Border mask, string email, bool save = true) {
- byte[] hash = MD5.Create().ComputeHash(Encoding.Default.GetBytes(email.ToLower().Trim()));
- string md5 = "";
- for (int i = 0; i < hash.Length; i++) md5 += hash[i].ToString("x2");
- md5 = md5.ToLower();
-
- if (!Directory.Exists(AVATAR_PATH)) Directory.CreateDirectory(AVATAR_PATH);
-
- mask.Visibility = Visibility.Visible;
- var sha = commit.SHA;
-
- string filePath = Path.Combine(AVATAR_PATH, md5);
- if (File.Exists(filePath)) {
- img.Source = new BitmapImage(new Uri(filePath));
- mask.Visibility = Visibility.Hidden;
- } else {
- var bitmap = new BitmapImage(new Uri("https://www.gravatar.com/avatar/" + md5 + "?d=404"));
- if (save) {
- bitmap.DownloadCompleted += (o, e) => {
- var owner = o as BitmapImage;
- if (owner != null) {
- var encoder = new PngBitmapEncoder();
- encoder.Frames.Add(BitmapFrame.Create(owner));
- using (var fs = new FileStream(filePath, FileMode.Create)) {
- encoder.Save(fs);
- }
- }
-
- if (commit.SHA == sha) mask.Visibility = Visibility.Hidden;
- };
- }
- img.Source = bitmap;
- }
- }
-
private void NavigateParent(object sender, RequestNavigateEventArgs e) {
repo.OnNavigateCommit?.Invoke(e.Uri.OriginalString);
e.Handled = true;
diff --git a/src/UI/Histories.xaml b/src/UI/Histories.xaml
index 08756556..9ec1781e 100644
--- a/src/UI/Histories.xaml
+++ b/src/UI/Histories.xaml
@@ -136,7 +136,15 @@
-
+
+
+
+
+
+
+
+
+
@@ -154,7 +162,7 @@
-
+