feature<Welcome>: supports to open terminal without repositories

This commit is contained in:
leo 2021-09-13 14:53:24 +08:00
parent f858d32291
commit 26a930aa3a
4 changed files with 507 additions and 474 deletions

View file

@ -297,8 +297,9 @@
<sys:String x:Key="Text.Merge.Mode">Merge Option :</sys:String> <sys:String x:Key="Text.Merge.Mode">Merge Option :</sys:String>
<sys:String x:Key="Text.Welcome.Title">SourceGit</sys:String> <sys:String x:Key="Text.Welcome.Title">SourceGit</sys:String>
<sys:String x:Key="Text.Welcome.OpenOrInit">Open Local Repository</sys:String> <sys:String x:Key="Text.Welcome.OpenOrInit">Open local repository</sys:String>
<sys:String x:Key="Text.Welcome.Clone">Clone Remote Repository</sys:String> <sys:String x:Key="Text.Welcome.OpenTerminal">Open terminal</sys:String>
<sys:String x:Key="Text.Welcome.Clone">Clone remote repository</sys:String>
<sys:String x:Key="Text.Welcome.DragDrop">DRAG-DROP YOUR FOLDER</sys:String> <sys:String x:Key="Text.Welcome.DragDrop">DRAG-DROP YOUR FOLDER</sys:String>
<sys:String x:Key="Text.Welcome.Start">START</sys:String> <sys:String x:Key="Text.Welcome.Start">START</sys:String>
<sys:String x:Key="Text.Welcome.Recent">RECENT OPENED</sys:String> <sys:String x:Key="Text.Welcome.Recent">RECENT OPENED</sys:String>

View file

@ -297,6 +297,7 @@
<sys:String x:Key="Text.Welcome.Title">欢迎使用本软件</sys:String> <sys:String x:Key="Text.Welcome.Title">欢迎使用本软件</sys:String>
<sys:String x:Key="Text.Welcome.OpenOrInit">打开本地仓库</sys:String> <sys:String x:Key="Text.Welcome.OpenOrInit">打开本地仓库</sys:String>
<sys:String x:Key="Text.Welcome.OpenTerminal">打开GIT终端</sys:String>
<sys:String x:Key="Text.Welcome.Clone">克隆远程仓库</sys:String> <sys:String x:Key="Text.Welcome.Clone">克隆远程仓库</sys:String>
<sys:String x:Key="Text.Welcome.DragDrop">支持拖放操作</sys:String> <sys:String x:Key="Text.Welcome.DragDrop">支持拖放操作</sys:String>
<sys:String x:Key="Text.Welcome.Start">开始使用</sys:String> <sys:String x:Key="Text.Welcome.Start">开始使用</sys:String>

View file

@ -52,16 +52,22 @@
Text="{DynamicResource Text.Welcome.Start}" Text="{DynamicResource Text.Welcome.Start}"
FontSize="13pt"/> FontSize="13pt"/>
<StackPanel Grid.Row="1" Margin="4,12,0,0" Orientation="Vertical"> <StackPanel Grid.Row="1" Margin="4,12,0,0" Orientation="Vertical">
<Button Grid.Column="0" Grid.Row="0" Click="OnOpenClicked" Height="28" Style="{DynamicResource Style.Button.Link}"> <Button Click="OnOpenClicked" Height="28" Style="{DynamicResource Style.Button.Link}">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<Path Width="16" Height="16" Data="{StaticResource Icon.Folder.Open}" Fill="{DynamicResource Brush.Accent1}"/> <Path Width="16" Height="15" Data="{StaticResource Icon.Folder.Open}" Fill="{DynamicResource Brush.Accent1}"/>
<TextBlock Margin="12,0,0,0" Text="{DynamicResource Text.Welcome.OpenOrInit}" Foreground="{DynamicResource Brush.Accent1}"/> <TextBlock Margin="8,0,0,0" Text="{DynamicResource Text.Welcome.OpenOrInit}" Foreground="{DynamicResource Brush.Accent1}"/>
</StackPanel> </StackPanel>
</Button> </Button>
<Button Grid.Column="0" Grid.Row="1" Click="OnCloneClicked" Height="28" Style="{DynamicResource Style.Button.Link}"> <Button Click="OnCloneClicked" Height="28" Style="{DynamicResource Style.Button.Link}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Path Width="16" Height="16" Data="{StaticResource Icon.Pull}" Fill="{DynamicResource Brush.Accent1}"/> <Path Width="16" Height="16" Data="{StaticResource Icon.Pull}" Fill="{DynamicResource Brush.Accent1}"/>
<TextBlock Margin="12,0,0,0" Text="{DynamicResource Text.Welcome.Clone}" Foreground="{DynamicResource Brush.Accent1}"/> <TextBlock Margin="8,0,0,0" Text="{DynamicResource Text.Welcome.Clone}" Foreground="{DynamicResource Brush.Accent1}"/>
</StackPanel>
</Button>
<Button Click="OnOpenTerminalClicked" Height="28" Style="{DynamicResource Style.Button.Link}">
<StackPanel Orientation="Horizontal">
<Path Width="16" Height="14" Data="{StaticResource Icon.Terminal}" Fill="{DynamicResource Brush.Accent1}"/>
<TextBlock Margin="8,0,0,0" Text="{DynamicResource Text.Welcome.OpenTerminal}" Foreground="{DynamicResource Brush.Accent1}"/>
</StackPanel> </StackPanel>
</Button> </Button>
</StackPanel> </StackPanel>

View file

@ -1,329 +1,354 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
namespace SourceGit.Views.Widgets { namespace SourceGit.Views.Widgets {
/// <summary> /// <summary>
/// 新标签页 /// 新标签页
/// </summary> /// </summary>
public partial class Welcome : UserControl, Controls.IPopupContainer { public partial class Welcome : UserControl, Controls.IPopupContainer {
/// <summary> /// <summary>
/// 树节点数据 /// 树节点数据
/// </summary> /// </summary>
public class Node : Controls.BindableBase { public class Node : Controls.BindableBase {
public string Id { get; set; } public string Id { get; set; }
public string ParentId { get; set; } public string ParentId { get; set; }
private string name; private string name;
public string Name { public string Name {
get => name; get => name;
set => SetProperty(ref name, value); set => SetProperty(ref name, value);
} }
public bool IsGroup { get; set; } public bool IsGroup { get; set; }
private bool isEditing = false; private bool isEditing = false;
public bool IsEditing { public bool IsEditing {
get => isEditing; get => isEditing;
set => SetProperty(ref isEditing, value); set => SetProperty(ref isEditing, value);
} }
public bool IsExpanded { get; set; } public bool IsExpanded { get; set; }
private int bookmark = 0; private int bookmark = 0;
public int Bookmark { public int Bookmark {
get => bookmark; get => bookmark;
set => SetProperty(ref bookmark, value); set => SetProperty(ref bookmark, value);
} }
public List<Node> Children { get; set; } public List<Node> Children { get; set; }
} }
/// <summary> /// <summary>
/// 仓库节点编辑事件参数 /// 仓库节点编辑事件参数
/// </summary> /// </summary>
public event Action<Node> OnNodeEdited; public event Action<Node> OnNodeEdited;
public Welcome() { public Welcome() {
InitializeComponent(); InitializeComponent();
UpdateTree(); UpdateTree();
UpdateRecents(); UpdateRecents();
} }
#region POPUP_CONTAINER #region POPUP_CONTAINER
public void Show(Controls.PopupWidget widget) { public void Show(Controls.PopupWidget widget) {
popup.Show(widget); popup.Show(widget);
} }
public void ShowAndStart(Controls.PopupWidget widget) { public void ShowAndStart(Controls.PopupWidget widget) {
popup.ShowAndStart(widget); popup.ShowAndStart(widget);
} }
public void UpdateProgress(string message) { public void UpdateProgress(string message) {
popup.UpdateProgress(message); popup.UpdateProgress(message);
} }
#endregion #endregion
#region FUNC_EVENTS #region FUNC_EVENTS
private void OnOpenClicked(object sender, RoutedEventArgs e) { private void OnOpenClicked(object sender, RoutedEventArgs e) {
var dialog = new Controls.FolderDialog(); var dialog = new Controls.FolderDialog();
if (dialog.ShowDialog() == true) CheckAndOpen(dialog.SelectedPath); if (dialog.ShowDialog() == true) CheckAndOpen(dialog.SelectedPath);
} }
private void OnCloneClicked(object sender, RoutedEventArgs e) { private void OnOpenTerminalClicked(object sender, RoutedEventArgs e) {
if (MakeSureReady()) new Popups.Clone().Show(); if (MakeSureReady()) {
} var bash = Path.Combine(Models.Preference.Instance.Git.Path, "..", "bash.exe");
if (!File.Exists(bash)) {
private void OnRecentContextMenuOpening(object sender, ContextMenuEventArgs e) { Models.Exception.Raise(App.Text("MissingBash"));
var repo = (sender as DataGridRow).DataContext as Models.Repository; return;
if (repo != null) { }
var remove = new MenuItem();
remove.Header = App.Text("Welcome.Delete"); if (Models.Preference.Instance.General.UseWindowsTerminal) {
remove.Click += (o, ev) => { Process.Start(new ProcessStartInfo {
Models.Preference.Instance.RemoveRecent(repo.Path); FileName = "wt",
UpdateRecents(); Arguments = "\"{bash}\"",
ev.Handled = true; UseShellExecute = false,
}; });
} else {
var menu = new ContextMenu(); Process.Start(new ProcessStartInfo {
menu.Items.Add(remove); FileName = bash,
menu.IsOpen = true; UseShellExecute = true,
});
}
e.Handled = true; e.Handled = true;
} }
} }
private void OnCloneClicked(object sender, RoutedEventArgs e) {
if (MakeSureReady()) new Popups.Clone().Show();
}
private void OnRecentContextMenuOpening(object sender, ContextMenuEventArgs e) {
var repo = (sender as DataGridRow).DataContext as Models.Repository;
if (repo != null) {
var remove = new MenuItem();
remove.Header = App.Text("Welcome.Delete");
remove.Click += (o, ev) => {
Models.Preference.Instance.RemoveRecent(repo.Path);
UpdateRecents();
ev.Handled = true;
};
var menu = new ContextMenu();
menu.Items.Add(remove);
menu.IsOpen = true;
e.Handled = true;
}
}
private void OnRecentDoubleClick(object sender, MouseButtonEventArgs e) { private void OnRecentDoubleClick(object sender, MouseButtonEventArgs e) {
var repo = (sender as DataGridRow).DataContext as Models.Repository; var repo = (sender as DataGridRow).DataContext as Models.Repository;
if (repo != null) CheckAndOpen(repo.Path); if (repo != null) CheckAndOpen(repo.Path);
e.Handled = true; e.Handled = true;
} }
private void OnRecentLostFocus(object sender, RoutedEventArgs e) { private void OnRecentLostFocus(object sender, RoutedEventArgs e) {
list.UnselectAll(); list.UnselectAll();
e.Handled = true; e.Handled = true;
} }
private void OnTreeLostFocus(object sender, RoutedEventArgs e) { private void OnTreeLostFocus(object sender, RoutedEventArgs e) {
var child = FocusManager.GetFocusedElement(body); var child = FocusManager.GetFocusedElement(body);
if (child == null) return; if (child == null) return;
if (!tree.IsAncestorOf(child as UIElement)) tree.UnselectAll(); if (!tree.IsAncestorOf(child as UIElement)) tree.UnselectAll();
e.Handled = true; e.Handled = true;
} }
private void OnTreeNodeStatusChange(object sender, RoutedEventArgs e) { private void OnTreeNodeStatusChange(object sender, RoutedEventArgs e) {
var node = (sender as Controls.TreeItem).DataContext as Node; var node = (sender as Controls.TreeItem).DataContext as Node;
if (node != null) { if (node != null) {
var group = Models.Preference.Instance.FindGroup(node.Id); var group = Models.Preference.Instance.FindGroup(node.Id);
group.IsExpanded = node.IsExpanded; group.IsExpanded = node.IsExpanded;
e.Handled = true; e.Handled = true;
} }
} }
private void OnTreeNodeDoubleClick(object sender, MouseButtonEventArgs e) { private void OnTreeNodeDoubleClick(object sender, MouseButtonEventArgs e) {
var node = (sender as Controls.TreeItem).DataContext as Node; var node = (sender as Controls.TreeItem).DataContext as Node;
if (node != null && !node.IsGroup) { if (node != null && !node.IsGroup) {
CheckAndOpen(node.Id); CheckAndOpen(node.Id);
e.Handled = true; e.Handled = true;
} }
} }
private void OnTreeContextMenuOpening(object sender, ContextMenuEventArgs e) { private void OnTreeContextMenuOpening(object sender, ContextMenuEventArgs e) {
var item = tree.FindItem(e.OriginalSource as DependencyObject); var item = tree.FindItem(e.OriginalSource as DependencyObject);
if (item == null) { if (item == null) {
var addFolder = new MenuItem(); var addFolder = new MenuItem();
addFolder.Header = App.Text("Welcome.NewFolder"); addFolder.Header = App.Text("Welcome.NewFolder");
addFolder.Click += (o, ev) => { addFolder.Click += (o, ev) => {
var group = Models.Preference.Instance.AddGroup("New Group", ""); var group = Models.Preference.Instance.AddGroup("New Group", "");
UpdateTree(group.Id); UpdateTree(group.Id);
ev.Handled = true; ev.Handled = true;
}; };
var menu = new ContextMenu(); var menu = new ContextMenu();
menu.Items.Add(addFolder); menu.Items.Add(addFolder);
menu.IsOpen = true; menu.IsOpen = true;
e.Handled = true; e.Handled = true;
} else { } else {
var node = item.DataContext as Node; var node = item.DataContext as Node;
if (node == null) return; if (node == null) return;
var menu = new ContextMenu(); var menu = new ContextMenu();
if (!node.IsGroup) { if (!node.IsGroup) {
var open = new MenuItem(); var open = new MenuItem();
open.Header = App.Text("RepoCM.Open"); open.Header = App.Text("RepoCM.Open");
open.Click += (o, ev) => { open.Click += (o, ev) => {
CheckAndOpen(node.Id); CheckAndOpen(node.Id);
ev.Handled = true; ev.Handled = true;
}; };
var explore = new MenuItem(); var explore = new MenuItem();
explore.Header = App.Text("RepoCM.Explore"); explore.Header = App.Text("RepoCM.Explore");
explore.Click += (o, ev) => { explore.Click += (o, ev) => {
Process.Start("explorer", node.Id); Process.Start("explorer", node.Id);
ev.Handled = true; ev.Handled = true;
}; };
var iconBookmark = FindResource("Icon.Bookmark") as Geometry; var iconBookmark = FindResource("Icon.Bookmark") as Geometry;
var bookmark = new MenuItem(); var bookmark = new MenuItem();
bookmark.Header = App.Text("RepoCM.Bookmark"); bookmark.Header = App.Text("RepoCM.Bookmark");
for (int i = 0; i < Controls.Bookmark.COLORS.Length; i++) { for (int i = 0; i < Controls.Bookmark.COLORS.Length; i++) {
var icon = new System.Windows.Shapes.Path(); var icon = new System.Windows.Shapes.Path();
icon.Data = iconBookmark; icon.Data = iconBookmark;
icon.Fill = Controls.Bookmark.COLORS[i]; icon.Fill = Controls.Bookmark.COLORS[i];
icon.Width = 8; icon.Width = 8;
var mark = new MenuItem(); var mark = new MenuItem();
mark.Icon = icon; mark.Icon = icon;
mark.Header = $"{i}"; mark.Header = $"{i}";
var refIdx = i; var refIdx = i;
mark.Click += (o, ev) => { mark.Click += (o, ev) => {
var repo = Models.Preference.Instance.FindRepository(node.Id); var repo = Models.Preference.Instance.FindRepository(node.Id);
if (repo != null) { if (repo != null) {
repo.Bookmark = refIdx; repo.Bookmark = refIdx;
node.Bookmark = refIdx; node.Bookmark = refIdx;
UpdateRecents(); UpdateRecents();
OnNodeEdited?.Invoke(node); OnNodeEdited?.Invoke(node);
} }
ev.Handled = true; ev.Handled = true;
}; };
bookmark.Items.Add(mark); bookmark.Items.Add(mark);
} }
menu.Items.Add(open); menu.Items.Add(open);
menu.Items.Add(explore); menu.Items.Add(explore);
menu.Items.Add(bookmark); menu.Items.Add(bookmark);
} else { } else {
var addSubFolder = new MenuItem(); var addSubFolder = new MenuItem();
addSubFolder.Header = App.Text("Welcome.NewSubFolder"); addSubFolder.Header = App.Text("Welcome.NewSubFolder");
addSubFolder.Click += (o, ev) => { addSubFolder.Click += (o, ev) => {
var parent = Models.Preference.Instance.FindGroup(node.Id); var parent = Models.Preference.Instance.FindGroup(node.Id);
if (parent != null) parent.IsExpanded = true; if (parent != null) parent.IsExpanded = true;
var group = Models.Preference.Instance.AddGroup("New Group", node.Id); var group = Models.Preference.Instance.AddGroup("New Group", node.Id);
UpdateTree(group.Id); UpdateTree(group.Id);
ev.Handled = true; ev.Handled = true;
}; };
menu.Items.Add(addSubFolder); menu.Items.Add(addSubFolder);
} }
var rename = new MenuItem(); var rename = new MenuItem();
rename.Header = App.Text("Welcome.Rename"); rename.Header = App.Text("Welcome.Rename");
rename.Click += (o, ev) => { rename.Click += (o, ev) => {
UpdateTree(node.Id); UpdateTree(node.Id);
ev.Handled = true; ev.Handled = true;
}; };
var delete = new MenuItem(); var delete = new MenuItem();
delete.Header = App.Text("Welcome.Delete"); delete.Header = App.Text("Welcome.Delete");
delete.Click += (o, ev) => { delete.Click += (o, ev) => {
DeleteNode(node); DeleteNode(node);
ev.Handled = true; ev.Handled = true;
}; };
menu.Items.Add(rename); menu.Items.Add(rename);
menu.Items.Add(delete); menu.Items.Add(delete);
menu.IsOpen = true; menu.IsOpen = true;
e.Handled = true; e.Handled = true;
} }
} }
#endregion #endregion
#region DRAP_DROP_EVENTS #region DRAP_DROP_EVENTS
private void OnPageDragEnter(object sender, DragEventArgs e) { private void OnPageDragEnter(object sender, DragEventArgs e) {
if (e.Data.GetDataPresent(DataFormats.FileDrop) || e.Data.GetDataPresent(typeof(Node))) { if (e.Data.GetDataPresent(DataFormats.FileDrop) || e.Data.GetDataPresent(typeof(Node))) {
dropArea.Visibility = Visibility.Visible; dropArea.Visibility = Visibility.Visible;
} }
} }
private void OnPageDragLeave(object sender, DragEventArgs e) { private void OnPageDragLeave(object sender, DragEventArgs e) {
dropArea.Visibility = Visibility.Hidden; dropArea.Visibility = Visibility.Hidden;
} }
private void OnPageDrop(object sender, DragEventArgs e) { private void OnPageDrop(object sender, DragEventArgs e) {
dropArea.Visibility = Visibility.Hidden; dropArea.Visibility = Visibility.Hidden;
} }
private void OnTreeMouseMove(object sender, MouseEventArgs e) { private void OnTreeMouseMove(object sender, MouseEventArgs e) {
if (e.LeftButton != MouseButtonState.Pressed) return; if (e.LeftButton != MouseButtonState.Pressed) return;
var item = tree.FindItem(e.OriginalSource as DependencyObject); var item = tree.FindItem(e.OriginalSource as DependencyObject);
if (item == null) return; if (item == null) return;
tree.UnselectAll(); tree.UnselectAll();
var adorner = new Controls.DragDropAdorner(item); var adorner = new Controls.DragDropAdorner(item);
DragDrop.DoDragDrop(item, item.DataContext, DragDropEffects.Move); DragDrop.DoDragDrop(item, item.DataContext, DragDropEffects.Move);
adorner.Remove(); adorner.Remove();
} }
private void OnTreeDragOver(object sender, DragEventArgs e) { private void OnTreeDragOver(object sender, DragEventArgs e) {
if (!e.Data.GetDataPresent(DataFormats.FileDrop) && !e.Data.GetDataPresent(typeof(Node))) return; if (!e.Data.GetDataPresent(DataFormats.FileDrop) && !e.Data.GetDataPresent(typeof(Node))) return;
var item = tree.FindItem(e.OriginalSource as DependencyObject); var item = tree.FindItem(e.OriginalSource as DependencyObject);
if (item == null) return; if (item == null) return;
var node = item.DataContext as Node; var node = item.DataContext as Node;
if (node.IsGroup && !item.IsExpanded) item.IsExpanded = true; if (node.IsGroup && !item.IsExpanded) item.IsExpanded = true;
e.Handled = true; e.Handled = true;
} }
private void OnTreeDrop(object sender, DragEventArgs e) { private void OnTreeDrop(object sender, DragEventArgs e) {
bool rebuild = false; bool rebuild = false;
dropArea.Visibility = Visibility.Hidden; dropArea.Visibility = Visibility.Hidden;
var parent = ""; var parent = "";
var to = tree.FindItem(e.OriginalSource as DependencyObject); var to = tree.FindItem(e.OriginalSource as DependencyObject);
if (to != null) { if (to != null) {
var dst = to.DataContext as Node; var dst = to.DataContext as Node;
parent = dst.IsGroup ? dst.Id : dst.ParentId; parent = dst.IsGroup ? dst.Id : dst.ParentId;
} }
if (e.Data.GetDataPresent(DataFormats.FileDrop)) { if (e.Data.GetDataPresent(DataFormats.FileDrop)) {
if (!MakeSureReady()) return; if (!MakeSureReady()) return;
var paths = e.Data.GetData(DataFormats.FileDrop) as string[]; var paths = e.Data.GetData(DataFormats.FileDrop) as string[];
foreach (var path in paths) { foreach (var path in paths) {
var dir = new Commands.QueryGitDir(path).Result(); var dir = new Commands.QueryGitDir(path).Result();
if (dir != null) { if (dir != null) {
var root = new Commands.GetRepositoryRootPath(path).Result(); var root = new Commands.GetRepositoryRootPath(path).Result();
Models.Preference.Instance.AddRepository(root, dir, parent); Models.Preference.Instance.AddRepository(root, dir, parent);
rebuild = true; rebuild = true;
} }
} }
} else if (e.Data.GetDataPresent(typeof(Node))) { } else if (e.Data.GetDataPresent(typeof(Node))) {
var src = e.Data.GetData(typeof(Node)) as Node; var src = e.Data.GetData(typeof(Node)) as Node;
if (src.IsGroup) { if (src.IsGroup) {
if (!Models.Preference.Instance.IsSubGroup(src.Id, parent)) { if (!Models.Preference.Instance.IsSubGroup(src.Id, parent)) {
Models.Preference.Instance.FindGroup(src.Id).Parent = parent; Models.Preference.Instance.FindGroup(src.Id).Parent = parent;
rebuild = true; rebuild = true;
} }
} else { } else {
Models.Preference.Instance.FindRepository(src.Id).GroupId = parent; Models.Preference.Instance.FindRepository(src.Id).GroupId = parent;
rebuild = true; rebuild = true;
} }
} }
if (rebuild) UpdateTree(); if (rebuild) UpdateTree();
e.Handled = true; e.Handled = true;
} }
#endregion #endregion
#region DATA #region DATA
private void UpdateRecents() { private void UpdateRecents() {
var repos = new List<Models.Repository>(); var repos = new List<Models.Repository>();
var dirty = new List<string>(); var dirty = new List<string>();
foreach (var path in Models.Preference.Instance.Recents) { foreach (var path in Models.Preference.Instance.Recents) {
var repo = Models.Preference.Instance.FindRepository(path); var repo = Models.Preference.Instance.FindRepository(path);
if (repo != null) { if (repo != null) {
@ -331,160 +356,160 @@ namespace SourceGit.Views.Widgets {
} else { } else {
dirty.Add(path); dirty.Add(path);
} }
} }
foreach (var path in dirty) Models.Preference.Instance.RemoveRecent(path); foreach (var path in dirty) Models.Preference.Instance.RemoveRecent(path);
list.ItemsSource = repos; list.ItemsSource = repos;
} }
private void UpdateTree(string editingNodeId = null) { private void UpdateTree(string editingNodeId = null) {
var groupNodes = new Dictionary<string, Node>(); var groupNodes = new Dictionary<string, Node>();
var nodes = new List<Node>(); var nodes = new List<Node>();
foreach (var group in Models.Preference.Instance.Groups) { foreach (var group in Models.Preference.Instance.Groups) {
Node node = new Node() { Node node = new Node() {
Id = group.Id, Id = group.Id,
ParentId = group.Parent, ParentId = group.Parent,
Name = group.Name, Name = group.Name,
IsGroup = true, IsGroup = true,
IsEditing = group.Id == editingNodeId, IsEditing = group.Id == editingNodeId,
IsExpanded = group.IsExpanded, IsExpanded = group.IsExpanded,
Bookmark = 0, Bookmark = 0,
Children = new List<Node>(), Children = new List<Node>(),
}; };
groupNodes.Add(node.Id, node); groupNodes.Add(node.Id, node);
} }
nodes.Clear(); nodes.Clear();
foreach (var kv in groupNodes) { foreach (var kv in groupNodes) {
if (groupNodes.ContainsKey(kv.Value.ParentId)) { if (groupNodes.ContainsKey(kv.Value.ParentId)) {
groupNodes[kv.Value.ParentId].Children.Add(kv.Value); groupNodes[kv.Value.ParentId].Children.Add(kv.Value);
} else { } else {
nodes.Add(kv.Value); nodes.Add(kv.Value);
} }
} }
foreach (var repo in Models.Preference.Instance.Repositories) { foreach (var repo in Models.Preference.Instance.Repositories) {
Node node = new Node() { Node node = new Node() {
Id = repo.Path, Id = repo.Path,
ParentId = repo.GroupId, ParentId = repo.GroupId,
Name = repo.Name, Name = repo.Name,
IsGroup = false, IsGroup = false,
IsEditing = repo.Path == editingNodeId, IsEditing = repo.Path == editingNodeId,
IsExpanded = false, IsExpanded = false,
Bookmark = repo.Bookmark, Bookmark = repo.Bookmark,
Children = new List<Node>(), Children = new List<Node>(),
}; };
if (groupNodes.ContainsKey(repo.GroupId)) { if (groupNodes.ContainsKey(repo.GroupId)) {
groupNodes[repo.GroupId].Children.Add(node); groupNodes[repo.GroupId].Children.Add(node);
} else { } else {
nodes.Add(node); nodes.Add(node);
} }
} }
tree.ItemsSource = nodes; tree.ItemsSource = nodes;
} }
private void DeleteNode(Node node) { private void DeleteNode(Node node) {
if (node.IsGroup) { if (node.IsGroup) {
Models.Preference.Instance.RemoveGroup(node.Id); Models.Preference.Instance.RemoveGroup(node.Id);
} else { } else {
Models.Preference.Instance.RemoveRepository(node.Id); Models.Preference.Instance.RemoveRepository(node.Id);
} }
UpdateTree(); UpdateTree();
UpdateRecents(); UpdateRecents();
} }
private bool MakeSureReady() { private bool MakeSureReady() {
if (!Models.Preference.Instance.IsReady) { if (!Models.Preference.Instance.IsReady) {
Models.Exception.Raise(App.Text("NotConfigured")); Models.Exception.Raise(App.Text("NotConfigured"));
return false; return false;
} }
return true; return true;
} }
private void CheckAndOpen(string path) { private void CheckAndOpen(string path) {
if (!MakeSureReady()) return; if (!MakeSureReady()) return;
if (!Directory.Exists(path)) { if (!Directory.Exists(path)) {
Models.Exception.Raise(App.Text("PathNotFound", path)); Models.Exception.Raise(App.Text("PathNotFound", path));
return; return;
} }
var root = new Commands.GetRepositoryRootPath(path).Result(); var root = new Commands.GetRepositoryRootPath(path).Result();
if (root == null) { if (root == null) {
new Popups.Init(path).Show(); new Popups.Init(path).Show();
return; return;
} }
var gitDir = new Commands.QueryGitDir(root).Result(); var gitDir = new Commands.QueryGitDir(root).Result();
var repo = Models.Preference.Instance.AddRepository(root, gitDir, ""); var repo = Models.Preference.Instance.AddRepository(root, gitDir, "");
Models.Watcher.Open(repo); Models.Watcher.Open(repo);
Models.Preference.Instance.AddRecent(repo.Path); Models.Preference.Instance.AddRecent(repo.Path);
} }
public void UpdateNodes(string id, int bookmark, IEnumerable<Node> nodes = null) { public void UpdateNodes(string id, int bookmark, IEnumerable<Node> nodes = null) {
if (nodes == null) nodes = tree.ItemsSource.OfType<Node>(); if (nodes == null) nodes = tree.ItemsSource.OfType<Node>();
foreach (var node in nodes) { foreach (var node in nodes) {
if (!node.IsGroup) { if (!node.IsGroup) {
if (node.Id == id) { if (node.Id == id) {
node.Bookmark = bookmark; node.Bookmark = bookmark;
break; break;
} }
} else if (node.Children.Count > 0) { } else if (node.Children.Count > 0) {
UpdateNodes(id, bookmark, node.Children); UpdateNodes(id, bookmark, node.Children);
} }
} }
} }
#endregion #endregion
#region RENAME_NODES #region RENAME_NODES
private void RenameStart(object sender, RoutedEventArgs e) { private void RenameStart(object sender, RoutedEventArgs e) {
var edit = sender as Controls.TextEdit; var edit = sender as Controls.TextEdit;
if (edit == null || !edit.IsVisible) return; if (edit == null || !edit.IsVisible) return;
edit.SelectAll(); edit.SelectAll();
edit.Focus(); edit.Focus();
} }
private void RenameKeyDown(object sender, KeyEventArgs e) { private void RenameKeyDown(object sender, KeyEventArgs e) {
if (e.Key == Key.Escape) { if (e.Key == Key.Escape) {
UpdateTree(); UpdateTree();
e.Handled = true; e.Handled = true;
} else if (e.Key == Key.Enter) { } else if (e.Key == Key.Enter) {
RenameEnd(sender, e); RenameEnd(sender, e);
e.Handled = true; e.Handled = true;
} }
} }
private void RenameEnd(object sender, RoutedEventArgs e) { private void RenameEnd(object sender, RoutedEventArgs e) {
var edit = sender as Controls.TextEdit; var edit = sender as Controls.TextEdit;
if (edit == null) return; if (edit == null) return;
if (string.IsNullOrWhiteSpace(edit.Text)) { if (string.IsNullOrWhiteSpace(edit.Text)) {
UpdateTree(); UpdateTree();
e.Handled = false; e.Handled = false;
return; return;
} }
var node = edit.DataContext as Node; var node = edit.DataContext as Node;
if (node != null) { if (node != null) {
node.Name = edit.Text; node.Name = edit.Text;
node.IsEditing = false; node.IsEditing = false;
if (node.IsGroup) { if (node.IsGroup) {
Models.Preference.Instance.RenameGroup(node.Id, edit.Text); Models.Preference.Instance.RenameGroup(node.Id, edit.Text);
} else { } else {
Models.Preference.Instance.RenameRepository(node.Id, node.Name); Models.Preference.Instance.RenameRepository(node.Id, node.Name);
UpdateRecents(); UpdateRecents();
OnNodeEdited?.Invoke(node); OnNodeEdited?.Invoke(node);
} }
e.Handled = false; e.Handled = false;
} }
} }
#endregion #endregion
} }
} }