2024-03-17 18:37:06 -07:00
|
|
|
|
using System;
|
2024-08-22 03:11:25 -07:00
|
|
|
|
using System.Collections.Generic;
|
2024-08-30 01:14:10 -07:00
|
|
|
|
using System.IO;
|
2024-07-22 00:34:31 -07:00
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
using Avalonia.Collections;
|
2024-04-27 22:51:44 -07:00
|
|
|
|
using Avalonia.Controls;
|
2024-03-17 18:37:06 -07:00
|
|
|
|
|
2024-02-05 23:08:37 -08:00
|
|
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
namespace SourceGit.ViewModels
|
|
|
|
|
{
|
|
|
|
|
public class Welcome : ObservableObject
|
|
|
|
|
{
|
2024-05-06 23:12:33 -07:00
|
|
|
|
public static Welcome Instance => _instance;
|
2024-02-05 23:08:37 -08:00
|
|
|
|
|
2024-08-22 06:10:23 -07:00
|
|
|
|
public AvaloniaList<RepositoryNode> Rows
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-08-22 06:10:23 -07:00
|
|
|
|
get;
|
|
|
|
|
private set;
|
|
|
|
|
} = [];
|
2024-02-05 23:08:37 -08:00
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public string SearchFilter
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
get => _searchFilter;
|
2024-03-17 18:37:06 -07:00
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (SetProperty(ref _searchFilter, value))
|
2024-07-22 00:34:31 -07:00
|
|
|
|
Refresh();
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-22 06:10:23 -07:00
|
|
|
|
public Welcome()
|
|
|
|
|
{
|
|
|
|
|
Refresh();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Refresh()
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrWhiteSpace(_searchFilter))
|
|
|
|
|
{
|
|
|
|
|
foreach (var node in Preference.Instance.RepositoryNodes)
|
|
|
|
|
ResetVisibility(node);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
foreach (var node in Preference.Instance.RepositoryNodes)
|
|
|
|
|
SetVisibilityBySearch(node);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var rows = new List<RepositoryNode>();
|
|
|
|
|
MakeTreeRows(rows, Preference.Instance.RepositoryNodes);
|
|
|
|
|
Rows.Clear();
|
|
|
|
|
Rows.AddRange(rows);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ToggleNodeIsExpanded(RepositoryNode node)
|
|
|
|
|
{
|
|
|
|
|
node.IsExpanded = !node.IsExpanded;
|
|
|
|
|
|
|
|
|
|
var depth = node.Depth;
|
|
|
|
|
var idx = Rows.IndexOf(node);
|
|
|
|
|
if (idx == -1)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (node.IsExpanded)
|
|
|
|
|
{
|
|
|
|
|
var subrows = new List<RepositoryNode>();
|
|
|
|
|
MakeTreeRows(subrows, node.SubNodes, depth + 1);
|
|
|
|
|
Rows.InsertRange(idx + 1, subrows);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var removeCount = 0;
|
|
|
|
|
for (int i = idx + 1; i < Rows.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
var row = Rows[i];
|
|
|
|
|
if (row.Depth <= depth)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
removeCount++;
|
|
|
|
|
}
|
|
|
|
|
Rows.RemoveRange(idx + 1, removeCount);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-27 00:35:10 -07:00
|
|
|
|
public void InitRepository(string path, RepositoryNode parent, string reason)
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-07-22 00:34:31 -07:00
|
|
|
|
if (!Preference.Instance.IsGitConfigured())
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
App.RaiseException(PopupHost.Active.GetId(), App.Text("NotConfigured"));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
if (PopupHost.CanCreatePopup())
|
2024-08-27 00:35:10 -07:00
|
|
|
|
PopupHost.ShowPopup(new Init(path, parent, reason));
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-30 00:13:59 -07:00
|
|
|
|
public void Clone()
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-07-22 00:34:31 -07:00
|
|
|
|
if (!Preference.Instance.IsGitConfigured())
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-05-30 00:13:59 -07:00
|
|
|
|
App.RaiseException(string.Empty, App.Text("NotConfigured"));
|
2024-02-05 23:08:37 -08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-23 23:32:27 -07:00
|
|
|
|
if (PopupHost.CanCreatePopup())
|
|
|
|
|
PopupHost.ShowPopup(new Clone());
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public void OpenTerminal()
|
|
|
|
|
{
|
2024-07-22 00:34:31 -07:00
|
|
|
|
if (!Preference.Instance.IsGitConfigured())
|
2024-02-05 23:08:37 -08:00
|
|
|
|
App.RaiseException(PopupHost.Active.GetId(), App.Text("NotConfigured"));
|
2024-03-17 18:37:06 -07:00
|
|
|
|
else
|
2024-02-05 23:08:37 -08:00
|
|
|
|
Native.OS.OpenTerminal(null);
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-30 01:14:10 -07:00
|
|
|
|
public void ScanDefaultCloneDir()
|
|
|
|
|
{
|
|
|
|
|
var defaultCloneDir = Preference.Instance.GitDefaultCloneDir;
|
|
|
|
|
if (string.IsNullOrEmpty(defaultCloneDir))
|
|
|
|
|
App.RaiseException(PopupHost.Active.GetId(), "The default clone dir haven't been configured!");
|
|
|
|
|
else if (!Directory.Exists(defaultCloneDir))
|
|
|
|
|
App.RaiseException(PopupHost.Active.GetId(), $"The default clone dir '{defaultCloneDir}' is not exists!");
|
|
|
|
|
else if (PopupHost.CanCreatePopup())
|
|
|
|
|
PopupHost.ShowAndStartPopup(new ScanRepositories(defaultCloneDir));
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public void ClearSearchFilter()
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
SearchFilter = string.Empty;
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-27 22:51:44 -07:00
|
|
|
|
public void AddRootNode()
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-03-31 01:54:29 -07:00
|
|
|
|
if (PopupHost.CanCreatePopup())
|
|
|
|
|
PopupHost.ShowPopup(new CreateGroup(null));
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public void MoveNode(RepositoryNode from, RepositoryNode to)
|
|
|
|
|
{
|
2024-09-01 23:14:52 -07:00
|
|
|
|
Preference.Instance.MoveNode(from, to, true);
|
2024-08-22 06:10:23 -07:00
|
|
|
|
Refresh();
|
2024-08-22 03:11:25 -07:00
|
|
|
|
}
|
|
|
|
|
|
2024-04-27 22:51:44 -07:00
|
|
|
|
public ContextMenu CreateContextMenu(RepositoryNode node)
|
|
|
|
|
{
|
|
|
|
|
var menu = new ContextMenu();
|
|
|
|
|
|
2024-06-17 20:07:48 -07:00
|
|
|
|
if (!node.IsRepository && node.SubNodes.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
var openAll = new MenuItem();
|
|
|
|
|
openAll.Header = App.Text("Welcome.OpenAllInNode");
|
|
|
|
|
openAll.Icon = App.CreateMenuIcon("Icons.Folder.Open");
|
|
|
|
|
openAll.Click += (_, e) =>
|
|
|
|
|
{
|
2024-07-23 23:32:27 -07:00
|
|
|
|
OpenAllInNode(App.GetLauncer(), node);
|
2024-06-17 20:07:48 -07:00
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
menu.Items.Add(openAll);
|
|
|
|
|
menu.Items.Add(new MenuItem() { Header = "-" });
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-27 22:51:44 -07:00
|
|
|
|
if (node.IsRepository)
|
|
|
|
|
{
|
2024-09-02 02:44:02 -07:00
|
|
|
|
var open = new MenuItem();
|
|
|
|
|
open.Header = App.Text("Welcome.OpenOrInit");
|
|
|
|
|
open.Icon = App.CreateMenuIcon("Icons.Folder.Open");
|
|
|
|
|
open.Click += (_, e) =>
|
|
|
|
|
{
|
|
|
|
|
App.GetLauncer()?.OpenRepositoryInTab(node, null);
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
2024-04-27 22:51:44 -07:00
|
|
|
|
var explore = new MenuItem();
|
|
|
|
|
explore.Header = App.Text("Repository.Explore");
|
2024-07-15 00:40:15 -07:00
|
|
|
|
explore.Icon = App.CreateMenuIcon("Icons.Explore");
|
2024-04-27 22:51:44 -07:00
|
|
|
|
explore.Click += (_, e) =>
|
|
|
|
|
{
|
|
|
|
|
node.OpenInFileManager();
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var terminal = new MenuItem();
|
|
|
|
|
terminal.Header = App.Text("Repository.Terminal");
|
|
|
|
|
terminal.Icon = App.CreateMenuIcon("Icons.Terminal");
|
|
|
|
|
terminal.Click += (_, e) =>
|
|
|
|
|
{
|
|
|
|
|
node.OpenTerminal();
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
2024-09-02 02:44:02 -07:00
|
|
|
|
|
|
|
|
|
menu.Items.Add(open);
|
|
|
|
|
menu.Items.Add(new MenuItem() { Header = "-" });
|
|
|
|
|
menu.Items.Add(explore);
|
2024-04-27 22:51:44 -07:00
|
|
|
|
menu.Items.Add(terminal);
|
2024-09-02 02:44:02 -07:00
|
|
|
|
menu.Items.Add(new MenuItem() { Header = "-" });
|
2024-04-27 22:51:44 -07:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var addSubFolder = new MenuItem();
|
|
|
|
|
addSubFolder.Header = App.Text("Welcome.AddSubFolder");
|
|
|
|
|
addSubFolder.Icon = App.CreateMenuIcon("Icons.Folder.Add");
|
|
|
|
|
addSubFolder.Click += (_, e) =>
|
|
|
|
|
{
|
|
|
|
|
node.AddSubFolder();
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
menu.Items.Add(addSubFolder);
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-02 02:44:02 -07:00
|
|
|
|
var edit = new MenuItem();
|
|
|
|
|
edit.Header = App.Text("Welcome.Edit");
|
|
|
|
|
edit.Icon = App.CreateMenuIcon("Icons.Edit");
|
|
|
|
|
edit.Click += (_, e) =>
|
|
|
|
|
{
|
|
|
|
|
node.Edit();
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var move = new MenuItem();
|
|
|
|
|
move.Header = App.Text("Welcome.Move");
|
2024-10-01 08:16:25 -07:00
|
|
|
|
move.Icon = App.CreateMenuIcon("Icons.MoveToAnotherGroup");
|
2024-09-02 02:44:02 -07:00
|
|
|
|
move.Click += (_, e) =>
|
|
|
|
|
{
|
|
|
|
|
if (PopupHost.CanCreatePopup())
|
|
|
|
|
PopupHost.ShowPopup(new MoveRepositoryNode(node));
|
|
|
|
|
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
2024-04-27 22:51:44 -07:00
|
|
|
|
var delete = new MenuItem();
|
|
|
|
|
delete.Header = App.Text("Welcome.Delete");
|
|
|
|
|
delete.Icon = App.CreateMenuIcon("Icons.Clear");
|
|
|
|
|
delete.Click += (_, e) =>
|
|
|
|
|
{
|
|
|
|
|
node.Delete();
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
2024-09-02 02:44:02 -07:00
|
|
|
|
|
|
|
|
|
menu.Items.Add(edit);
|
|
|
|
|
menu.Items.Add(move);
|
|
|
|
|
menu.Items.Add(new MenuItem() { Header = "-" });
|
2024-04-27 22:51:44 -07:00
|
|
|
|
menu.Items.Add(delete);
|
|
|
|
|
|
|
|
|
|
return menu;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
private void ResetVisibility(RepositoryNode node)
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
node.IsVisible = true;
|
2024-03-31 01:54:29 -07:00
|
|
|
|
foreach (var subNode in node.SubNodes)
|
|
|
|
|
ResetVisibility(subNode);
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
private void SetVisibilityBySearch(RepositoryNode node)
|
|
|
|
|
{
|
|
|
|
|
if (!node.IsRepository)
|
|
|
|
|
{
|
|
|
|
|
if (node.Name.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
node.IsVisible = true;
|
2024-03-31 01:54:29 -07:00
|
|
|
|
foreach (var subNode in node.SubNodes)
|
|
|
|
|
ResetVisibility(subNode);
|
2024-03-17 18:37:06 -07:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
bool hasVisibleSubNode = false;
|
2024-03-17 18:37:06 -07:00
|
|
|
|
foreach (var subNode in node.SubNodes)
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
SetVisibilityBySearch(subNode);
|
|
|
|
|
hasVisibleSubNode |= subNode.IsVisible;
|
|
|
|
|
}
|
|
|
|
|
node.IsVisible = hasVisibleSubNode;
|
|
|
|
|
}
|
2024-03-17 18:37:06 -07:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-08-23 02:25:21 -07:00
|
|
|
|
node.IsVisible = node.Name.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase) ||
|
|
|
|
|
node.Id.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase);
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-22 20:24:31 -07:00
|
|
|
|
private void MakeTreeRows(List<RepositoryNode> rows, List<RepositoryNode> nodes, int depth = 0)
|
2024-08-22 06:10:23 -07:00
|
|
|
|
{
|
|
|
|
|
foreach (var node in nodes)
|
|
|
|
|
{
|
|
|
|
|
if (!node.IsVisible)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
node.Depth = depth;
|
|
|
|
|
rows.Add(node);
|
|
|
|
|
|
|
|
|
|
if (node.IsRepository || !node.IsExpanded)
|
|
|
|
|
continue;
|
|
|
|
|
|
2024-08-22 19:28:16 -07:00
|
|
|
|
MakeTreeRows(rows, node.SubNodes, depth + 1);
|
2024-08-22 06:10:23 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-17 20:07:48 -07:00
|
|
|
|
private void OpenAllInNode(Launcher launcher, RepositoryNode node)
|
|
|
|
|
{
|
|
|
|
|
foreach (var subNode in node.SubNodes)
|
|
|
|
|
{
|
|
|
|
|
if (subNode.IsRepository)
|
|
|
|
|
launcher.OpenRepositoryInTab(subNode, null);
|
|
|
|
|
else if (subNode.SubNodes.Count > 0)
|
|
|
|
|
OpenAllInNode(launcher, subNode);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-06 23:12:33 -07:00
|
|
|
|
private static Welcome _instance = new Welcome();
|
2024-02-05 23:08:37 -08:00
|
|
|
|
private string _searchFilter = string.Empty;
|
|
|
|
|
}
|
2024-03-31 01:54:29 -07:00
|
|
|
|
}
|