2024-03-20 00:36:10 -07:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.IO;
|
|
|
|
using System.Text.Json;
|
|
|
|
using System.Text.Json.Serialization;
|
|
|
|
|
|
|
|
using Avalonia.Collections;
|
2024-03-21 08:19:09 -07:00
|
|
|
using Avalonia.Media;
|
2024-03-20 00:36:10 -07:00
|
|
|
|
|
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
|
|
|
|
|
|
namespace SourceGit.ViewModels
|
|
|
|
{
|
|
|
|
public class Preference : ObservableObject
|
|
|
|
{
|
|
|
|
[JsonIgnore]
|
|
|
|
public static Preference Instance
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
if (_instance == null)
|
|
|
|
{
|
|
|
|
if (!File.Exists(_savePath))
|
|
|
|
{
|
|
|
|
_instance = new Preference();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2024-03-26 07:11:06 -07:00
|
|
|
_instance = JsonSerializer.Deserialize(File.ReadAllText(_savePath), JsonCodeGen.Default.Preference);
|
2024-03-20 00:36:10 -07:00
|
|
|
}
|
|
|
|
catch
|
|
|
|
{
|
|
|
|
_instance = new Preference();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_instance.Repositories.RemoveAll(x => !Directory.Exists(x.FullPath));
|
|
|
|
|
2024-03-21 08:19:09 -07:00
|
|
|
if (_instance.DefaultFont == null)
|
|
|
|
{
|
|
|
|
_instance.DefaultFont = FontManager.Current.DefaultFontFamily;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_instance.MonospaceFont == null)
|
|
|
|
{
|
|
|
|
_instance.MonospaceFont = new FontFamily("fonts:SourceGit#JetBrains Mono");
|
|
|
|
}
|
|
|
|
|
2024-03-20 00:36:10 -07:00
|
|
|
if (!_instance.IsGitConfigured)
|
|
|
|
{
|
|
|
|
_instance.GitInstallPath = Native.OS.FindGitExecutable();
|
|
|
|
}
|
|
|
|
|
|
|
|
return _instance;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public string Locale
|
|
|
|
{
|
|
|
|
get => _locale;
|
|
|
|
set
|
|
|
|
{
|
|
|
|
if (SetProperty(ref _locale, value))
|
|
|
|
{
|
|
|
|
App.SetLocale(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public string Theme
|
|
|
|
{
|
|
|
|
get => _theme;
|
|
|
|
set
|
|
|
|
{
|
|
|
|
if (SetProperty(ref _theme, value))
|
|
|
|
{
|
|
|
|
App.SetTheme(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-21 08:19:09 -07:00
|
|
|
[JsonConverter(typeof(FontFamilyConverter))]
|
|
|
|
public FontFamily DefaultFont
|
2024-03-21 03:02:06 -07:00
|
|
|
{
|
|
|
|
get => _defaultFont;
|
|
|
|
set => SetProperty(ref _defaultFont, value);
|
|
|
|
}
|
|
|
|
|
2024-03-21 08:19:09 -07:00
|
|
|
[JsonConverter(typeof(FontFamilyConverter))]
|
|
|
|
public FontFamily MonospaceFont
|
2024-03-21 03:02:06 -07:00
|
|
|
{
|
|
|
|
get => _monospaceFont;
|
|
|
|
set => SetProperty(ref _monospaceFont, value);
|
|
|
|
}
|
|
|
|
|
2024-03-21 21:03:04 -07:00
|
|
|
public double DefaultFontSize
|
|
|
|
{
|
|
|
|
get => _defaultFontSize;
|
|
|
|
set => SetProperty(ref _defaultFontSize, value);
|
|
|
|
}
|
|
|
|
|
2024-03-20 00:36:10 -07:00
|
|
|
public string AvatarServer
|
|
|
|
{
|
|
|
|
get => Models.AvatarManager.SelectedServer;
|
|
|
|
set
|
|
|
|
{
|
|
|
|
if (Models.AvatarManager.SelectedServer != value)
|
|
|
|
{
|
|
|
|
Models.AvatarManager.SelectedServer = value;
|
|
|
|
OnPropertyChanged(nameof(AvatarServer));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public int MaxHistoryCommits
|
|
|
|
{
|
|
|
|
get => _maxHistoryCommits;
|
|
|
|
set => SetProperty(ref _maxHistoryCommits, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool RestoreTabs
|
|
|
|
{
|
|
|
|
get => _restoreTabs;
|
|
|
|
set => SetProperty(ref _restoreTabs, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool UseFixedTabWidth
|
|
|
|
{
|
|
|
|
get => _useFixedTabWidth;
|
|
|
|
set => SetProperty(ref _useFixedTabWidth, value);
|
|
|
|
}
|
|
|
|
|
2024-03-26 07:11:06 -07:00
|
|
|
public bool Check4UpdatesOnStartup
|
|
|
|
{
|
|
|
|
get => _check4UpdatesOnStartup;
|
|
|
|
set => SetProperty(ref _check4UpdatesOnStartup, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
public string IgnoreUpdateTag
|
|
|
|
{
|
|
|
|
get;
|
|
|
|
set;
|
|
|
|
} = string.Empty;
|
|
|
|
|
2024-03-20 00:36:10 -07:00
|
|
|
public bool UseTwoColumnsLayoutInHistories
|
|
|
|
{
|
|
|
|
get => _useTwoColumnsLayoutInHistories;
|
|
|
|
set => SetProperty(ref _useTwoColumnsLayoutInHistories, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool UseSideBySideDiff
|
|
|
|
{
|
|
|
|
get => _useSideBySideDiff;
|
|
|
|
set => SetProperty(ref _useSideBySideDiff, value);
|
|
|
|
}
|
|
|
|
|
2024-03-20 05:17:20 -07:00
|
|
|
public bool UseSyntaxHighlighting
|
|
|
|
{
|
|
|
|
get => _useSyntaxHighlighting;
|
|
|
|
set => SetProperty(ref _useSyntaxHighlighting, value);
|
|
|
|
}
|
|
|
|
|
2024-03-20 00:36:10 -07:00
|
|
|
public Models.ChangeViewMode UnstagedChangeViewMode
|
|
|
|
{
|
|
|
|
get => _unstagedChangeViewMode;
|
|
|
|
set => SetProperty(ref _unstagedChangeViewMode, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
public Models.ChangeViewMode StagedChangeViewMode
|
|
|
|
{
|
|
|
|
get => _stagedChangeViewMode;
|
|
|
|
set => SetProperty(ref _stagedChangeViewMode, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
public Models.ChangeViewMode CommitChangeViewMode
|
|
|
|
{
|
|
|
|
get => _commitChangeViewMode;
|
|
|
|
set => SetProperty(ref _commitChangeViewMode, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
[JsonIgnore]
|
|
|
|
public bool IsGitConfigured
|
|
|
|
{
|
|
|
|
get => !string.IsNullOrEmpty(GitInstallPath) && File.Exists(GitInstallPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
public string GitInstallPath
|
|
|
|
{
|
|
|
|
get => Native.OS.GitInstallPath;
|
|
|
|
set
|
|
|
|
{
|
|
|
|
if (Native.OS.GitInstallPath != value)
|
|
|
|
{
|
|
|
|
Native.OS.GitInstallPath = value;
|
|
|
|
OnPropertyChanged(nameof(GitInstallPath));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public string GitDefaultCloneDir
|
|
|
|
{
|
|
|
|
get => _gitDefaultCloneDir;
|
|
|
|
set => SetProperty(ref _gitDefaultCloneDir, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool GitAutoFetch
|
|
|
|
{
|
|
|
|
get => Commands.AutoFetch.IsEnabled;
|
|
|
|
set
|
|
|
|
{
|
|
|
|
if (Commands.AutoFetch.IsEnabled != value)
|
|
|
|
{
|
|
|
|
Commands.AutoFetch.IsEnabled = value;
|
|
|
|
OnPropertyChanged(nameof(GitAutoFetch));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public int ExternalMergeToolType
|
|
|
|
{
|
|
|
|
get => _externalMergeToolType;
|
|
|
|
set
|
|
|
|
{
|
|
|
|
var changed = SetProperty(ref _externalMergeToolType, value);
|
|
|
|
if (changed && !OperatingSystem.IsWindows() && value > 0 && value < Models.ExternalMergeTools.Supported.Count)
|
|
|
|
{
|
|
|
|
var tool = Models.ExternalMergeTools.Supported[value];
|
|
|
|
if (File.Exists(tool.Exec)) ExternalMergeToolPath = tool.Exec;
|
|
|
|
else ExternalMergeToolPath = string.Empty;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public string ExternalMergeToolPath
|
|
|
|
{
|
|
|
|
get => _externalMergeToolPath;
|
|
|
|
set => SetProperty(ref _externalMergeToolPath, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
public string ExternalMergeToolCmd
|
|
|
|
{
|
|
|
|
get => _externalMergeToolCmd;
|
|
|
|
set => SetProperty(ref _externalMergeToolCmd, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
public string ExternalMergeToolDiffCmd
|
|
|
|
{
|
|
|
|
get => _externalMergeToolDiffCmd;
|
|
|
|
set => SetProperty(ref _externalMergeToolDiffCmd, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
public List<Repository> Repositories
|
|
|
|
{
|
|
|
|
get;
|
|
|
|
set;
|
|
|
|
} = new List<Repository>();
|
|
|
|
|
|
|
|
public AvaloniaList<RepositoryNode> RepositoryNodes
|
|
|
|
{
|
|
|
|
get => _repositoryNodes;
|
|
|
|
set => SetProperty(ref _repositoryNodes, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
public List<string> OpenedTabs
|
|
|
|
{
|
|
|
|
get;
|
|
|
|
set;
|
|
|
|
} = new List<string>();
|
|
|
|
|
|
|
|
public int LastActiveTabIdx
|
|
|
|
{
|
|
|
|
get;
|
|
|
|
set;
|
|
|
|
} = 0;
|
|
|
|
|
|
|
|
public static void AddNode(RepositoryNode node, RepositoryNode to = null)
|
|
|
|
{
|
|
|
|
var collection = to == null ? _instance._repositoryNodes : to.SubNodes;
|
|
|
|
var list = new List<RepositoryNode>();
|
|
|
|
list.AddRange(collection);
|
|
|
|
list.Add(node);
|
|
|
|
list.Sort((l, r) =>
|
|
|
|
{
|
|
|
|
if (l.IsRepository != r.IsRepository)
|
|
|
|
{
|
|
|
|
return l.IsRepository ? 1 : -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return l.Name.CompareTo(r.Name);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
collection.Clear();
|
|
|
|
foreach (var one in list)
|
|
|
|
{
|
|
|
|
collection.Add(one);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static RepositoryNode FindNode(string id)
|
|
|
|
{
|
|
|
|
return FindNodeRecursive(id, _instance.RepositoryNodes);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void MoveNode(RepositoryNode node, RepositoryNode to = null)
|
|
|
|
{
|
|
|
|
if (to == null && _instance._repositoryNodes.Contains(node)) return;
|
|
|
|
if (to != null && to.SubNodes.Contains(node)) return;
|
|
|
|
|
|
|
|
RemoveNode(node);
|
|
|
|
AddNode(node, to);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void RemoveNode(RepositoryNode node)
|
|
|
|
{
|
|
|
|
RemoveNodeRecursive(node, _instance._repositoryNodes);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Repository FindRepository(string path)
|
|
|
|
{
|
|
|
|
foreach (var repo in _instance.Repositories)
|
|
|
|
{
|
|
|
|
if (repo.FullPath == path) return repo;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Repository AddRepository(string rootDir, string gitDir)
|
|
|
|
{
|
|
|
|
var normalized = rootDir.Replace('\\', '/');
|
|
|
|
var repo = FindRepository(normalized);
|
|
|
|
if (repo != null)
|
|
|
|
{
|
|
|
|
repo.GitDir = gitDir;
|
|
|
|
return repo;
|
|
|
|
}
|
|
|
|
|
|
|
|
repo = new Repository()
|
|
|
|
{
|
|
|
|
FullPath = normalized,
|
|
|
|
GitDir = gitDir
|
|
|
|
};
|
|
|
|
|
|
|
|
_instance.Repositories.Add(repo);
|
|
|
|
return repo;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void Save()
|
|
|
|
{
|
|
|
|
var dir = Path.GetDirectoryName(_savePath);
|
|
|
|
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
|
|
|
|
|
2024-03-26 07:11:06 -07:00
|
|
|
var data = JsonSerializer.Serialize(_instance, JsonCodeGen.Default.Preference);
|
2024-03-20 00:36:10 -07:00
|
|
|
File.WriteAllText(_savePath, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static RepositoryNode FindNodeRecursive(string id, AvaloniaList<RepositoryNode> collection)
|
|
|
|
{
|
|
|
|
foreach (var node in collection)
|
|
|
|
{
|
|
|
|
if (node.Id == id) return node;
|
|
|
|
|
|
|
|
var sub = FindNodeRecursive(id, node.SubNodes);
|
|
|
|
if (sub != null) return sub;
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static bool RemoveNodeRecursive(RepositoryNode node, AvaloniaList<RepositoryNode> collection)
|
|
|
|
{
|
|
|
|
if (collection.Contains(node))
|
|
|
|
{
|
|
|
|
collection.Remove(node);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (RepositoryNode one in collection)
|
|
|
|
{
|
|
|
|
if (RemoveNodeRecursive(node, one.SubNodes)) return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static Preference _instance = null;
|
|
|
|
private static readonly string _savePath = Path.Combine(
|
|
|
|
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
|
|
|
"SourceGit",
|
|
|
|
"preference.json");
|
|
|
|
|
|
|
|
private string _locale = "en_US";
|
|
|
|
private string _theme = "Default";
|
2024-03-21 08:19:09 -07:00
|
|
|
private FontFamily _defaultFont = null;
|
|
|
|
private FontFamily _monospaceFont = null;
|
2024-03-21 21:03:04 -07:00
|
|
|
private double _defaultFontSize = 13;
|
2024-03-21 03:02:06 -07:00
|
|
|
|
2024-03-20 00:36:10 -07:00
|
|
|
private int _maxHistoryCommits = 20000;
|
|
|
|
private bool _restoreTabs = false;
|
|
|
|
private bool _useFixedTabWidth = true;
|
2024-03-26 07:11:06 -07:00
|
|
|
private bool _check4UpdatesOnStartup = true;
|
2024-03-20 00:36:10 -07:00
|
|
|
private bool _useTwoColumnsLayoutInHistories = false;
|
|
|
|
private bool _useSideBySideDiff = false;
|
2024-03-20 05:17:20 -07:00
|
|
|
private bool _useSyntaxHighlighting = false;
|
2024-03-20 00:36:10 -07:00
|
|
|
|
|
|
|
private Models.ChangeViewMode _unstagedChangeViewMode = Models.ChangeViewMode.List;
|
|
|
|
private Models.ChangeViewMode _stagedChangeViewMode = Models.ChangeViewMode.List;
|
|
|
|
private Models.ChangeViewMode _commitChangeViewMode = Models.ChangeViewMode.List;
|
|
|
|
|
|
|
|
private string _gitDefaultCloneDir = string.Empty;
|
|
|
|
|
|
|
|
private int _externalMergeToolType = 0;
|
|
|
|
private string _externalMergeToolPath = string.Empty;
|
|
|
|
private string _externalMergeToolCmd = string.Empty;
|
|
|
|
private string _externalMergeToolDiffCmd = string.Empty;
|
|
|
|
|
|
|
|
private AvaloniaList<RepositoryNode> _repositoryNodes = new AvaloniaList<RepositoryNode>();
|
|
|
|
}
|
|
|
|
|
2024-03-21 08:19:09 -07:00
|
|
|
public class FontFamilyConverter : JsonConverter<FontFamily>
|
|
|
|
{
|
|
|
|
public override FontFamily Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
|
|
|
{
|
|
|
|
var name = reader.GetString();
|
|
|
|
return new FontFamily(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
public override void Write(Utf8JsonWriter writer, FontFamily value, JsonSerializerOptions options)
|
|
|
|
{
|
|
|
|
writer.WriteStringValue(value.ToString());
|
|
|
|
}
|
|
|
|
}
|
2024-02-05 23:08:37 -08:00
|
|
|
}
|