添加 MX-PD-盘古 项目文件

将 MX-PD-盘古 - new 目录下的所有文件添加到主仓库
This commit is contained in:
Shi.Ji
2026-05-18 11:43:09 +08:00
parent 03632a379d
commit e31d3560bb
739 changed files with 99783 additions and 0 deletions

View File

@@ -0,0 +1,442 @@
using MainShell.Common;
using MainShell.Models;
using MainShell.Recipe.Models;
using Newtonsoft.Json.Linq;
using Stylet;
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
namespace MainShell.Recipe.ViewModel
{
public abstract class RecipeViewModelBase<TRecipe> : BaseScreen, IRecipeViewModel
where TRecipe : RecipeBase, new()
{
private string _lastSavedSnapshot = string.Empty;
protected string LastSavedSnapshot => _lastSavedSnapshot;
private bool _skipLeaveCheck;
private RecipeWrap _selectedRecipeWrap;
public ObservableCollection<RecipeWrap> RecipeWraps { get; protected set; } = new ObservableCollection<RecipeWrap>();
public RecipeWrap SelectedRecipeWrap
{
get { return _selectedRecipeWrap; }
set
{
if (_selectedRecipeWrap == value)
return;
if (!_skipLeaveCheck && _selectedRecipeWrap != null && value != null && !TryLeaveCurrentRecipeContext())
{
NotifyOfPropertyChange(() => SelectedRecipeWrap);
return;
}
if (SetAndNotify(ref _selectedRecipeWrap, value))
{
LoadRecipeFromWrap(value);
}
}
}
public RecipeButtonGroupViewModel RecipeButtonGroupViewModel { get; protected set; }
public virtual bool CanCopyRecipe => true;
public virtual bool CanImportRecipe => false;
public virtual bool CanExportRecipe => false;
public bool HasUnsavedChanges => IsRecipeDirty();
public string CurrentRecipeDisplayName => CurrentRecipe != null ? CurrentRecipe.RecipeName : EmptyRecipeDisplayName;
public RecipeManager RecipeManager { get; }
protected RecipeViewModelBase(RecipeManager recipeManager)
{
RecipeManager = recipeManager;
}
protected abstract TRecipe CurrentRecipe { get; set; }
protected abstract string RecipeFolderPath { get; }
protected abstract string EmptyRecipeDisplayName { get; }
protected abstract string GetActiveRecipeName();
protected abstract void SwitchActiveRecipe(string recipeName);
protected abstract void ClearActiveRecipe();
protected virtual void OnRecipeLoaded(TRecipe recipe, RecipeWrap recipeWrap) { }
protected virtual string GetUnsavedChangesMessage() => $"<22><EFBFBD><E4B7BD>{CurrentRecipeDisplayName}<7D><><EFBFBD><EFBFBD>δ<EFBFBD><CEB4><EFBFBD><EFBFBD><EFBFBD>޸ģ<DEB8><C4A3>Ƿ<EFBFBD><C7B7>ȱ<EFBFBD><C8B1>棿";
protected virtual void AfterDeleteCurrentRecipe(string deletedRecipeName) { }
protected virtual void AfterRecipeRenamed(string oldRecipeName, string newRecipeName) { }
public virtual bool TryLeaveCurrentRecipeContext()
{
if (!HasUnsavedChanges)
return true;
var result = MwMessageBox.Show(GetUnsavedChangesMessage(), <><CEB4><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʾ", System.Windows.MessageBoxButton.YesNoCancel, System.Windows.MessageBoxImage.Warning);
if (result == System.Windows.MessageBoxResult.Cancel)
return false;
if (result == System.Windows.MessageBoxResult.Yes)
SaveCurrentRecipe(false);
return true;
}
public void SaveCurrentRecipeSilently()
{
SaveCurrentRecipe(false);
}
protected void RegisterButtonGroupEvents()
{
RecipeButtonGroupViewModel = new RecipeButtonGroupViewModel();
RecipeButtonGroupViewModel.CreateNewRequested -= OnCreateNewRecipe;
RecipeButtonGroupViewModel.ApplyRequested -= OnApplyRecipe;
RecipeButtonGroupViewModel.DeleteRequested -= OnDeleteRecipe;
RecipeButtonGroupViewModel.CopyRequested -= OnCopyRecipe;
RecipeButtonGroupViewModel.ImportRequested -= OnImportRecipe;
RecipeButtonGroupViewModel.SaveRequested -= OnSaveRecipe;
RecipeButtonGroupViewModel.ExportRequested -= OnExportRecipe;
RecipeButtonGroupViewModel.ReNameRequested -= OnReNameRecipe;
RecipeButtonGroupViewModel.ClearRequested -= OnClearRecipe;
RecipeButtonGroupViewModel.CreateNewRequested += OnCreateNewRecipe;
RecipeButtonGroupViewModel.ApplyRequested += OnApplyRecipe;
RecipeButtonGroupViewModel.DeleteRequested += OnDeleteRecipe;
RecipeButtonGroupViewModel.CopyRequested += OnCopyRecipe;
RecipeButtonGroupViewModel.ImportRequested += OnImportRecipe;
RecipeButtonGroupViewModel.SaveRequested += OnSaveRecipe;
RecipeButtonGroupViewModel.ExportRequested += OnExportRecipe;
RecipeButtonGroupViewModel.ReNameRequested += OnReNameRecipe;
RecipeButtonGroupViewModel.ClearRequested += OnClearRecipe;
}
public virtual void OnCreateNewRecipe(object sender, EventArgs eventArgs)
{
if (eventArgs is RecipeEventArgs args)
{
if (!TryLeaveCurrentRecipeContext())
return;
var recipeWrap = new RecipeWrap
{
RecipeName = args.RecipeName,
CreateTime = DateTime.UtcNow,
ModifiedTime = DateTime.UtcNow,
};
RecipeWraps.Add(recipeWrap);
SelectRecipeWrapInternal(recipeWrap);
}
}
public virtual void OnDeleteRecipe(object sender, EventArgs eventArgs)
{
if (SelectedRecipeWrap == null)
return;
if (!TryLeaveCurrentRecipeContext())
return;
var deletedRecipeName = SelectedRecipeWrap.RecipeName;
if (!RecipeManager.DeleteRecipeFolder(RecipeFolderPath, deletedRecipeName, out var error))
{
MwMessageBox.Show(error);
return;
}
RecipeWraps.Remove(SelectedRecipeWrap);
AfterDeleteCurrentRecipe(deletedRecipeName);
if (RecipeWraps.Count > 0)
{
SelectRecipeWrapInternal(RecipeWraps[0]);
}
else
{
SelectedRecipeWrap = null;
CurrentRecipe = null;
ClearActiveRecipe();
CaptureSavedSnapshot();
}
}
public virtual void OnCopyRecipe(object sender, EventArgs eventArgs)
{
if (!(eventArgs is RecipeEventArgs args) || SelectedRecipeWrap == null)
return;
var sourceRecipeName = SelectedRecipeWrap.RecipeName;
if (string.IsNullOrWhiteSpace(sourceRecipeName) || string.IsNullOrWhiteSpace(args.RecipeName))
return;
if (CurrentRecipe != null &&
string.Equals(CurrentRecipe.RecipeName, sourceRecipeName, StringComparison.Ordinal) &&
HasUnsavedChanges)
{
SaveCurrentRecipe(false);
}
if (!RecipeManager.CopyRecipeFolder(RecipeFolderPath, sourceRecipeName, args.RecipeName, out var error))
{
MwMessageBox.Show(error);
return;
}
var copiedRecipeWrap = new RecipeWrap
{
RecipeName = args.RecipeName,
CreateTime = DateTime.UtcNow,
ModifiedTime = DateTime.UtcNow,
};
RecipeWraps.Add(copiedRecipeWrap);
SelectRecipeWrapInternal(copiedRecipeWrap);
}
public virtual void OnApplyRecipe(object sender, EventArgs eventArgs)
{
if (eventArgs is RecipeEventArgs args)
{
var recipeWrap = RecipeWraps.FirstOrDefault(p => p.RecipeName == args.RecipeName);
if (recipeWrap == null)
return;
if (!ReferenceEquals(SelectedRecipeWrap, recipeWrap))
{
if (!TryLeaveCurrentRecipeContext())
return;
SelectRecipeWrapInternal(recipeWrap);
}
if (SelectedRecipeWrap == null)
return;
if (CurrentRecipe != null && IsRecipeDirty())
{
SaveCurrentRecipe(false);
}
SwitchActiveRecipe(SelectedRecipeWrap.RecipeName);
UpdateInUseState(SelectedRecipeWrap);
}
}
public abstract void OnImportRecipe(object sender, EventArgs eventArgs);
public virtual void OnSaveRecipe(object sender, EventArgs eventArgs)
{
CommonUti.RunOnUi(() =>
{
if (eventArgs is RecipeEventArgs)
{
SaveCurrentRecipe(true);
}
});
}
public abstract void OnExportRecipe(object sender, EventArgs eventArgs);
public abstract void OnReNameRecipe(object sender, EventArgs eventArgs);
public virtual void OnClearRecipe(object sender, EventArgs eventArgs)
{
if (!TryLeaveCurrentRecipeContext())
return;
if (SelectedRecipeWrap == null)
return;
var clearedRecipe = new TRecipe
{
RecipeName = SelectedRecipeWrap.RecipeName
};
CurrentRecipe = clearedRecipe;
OnRecipeLoaded(clearedRecipe, SelectedRecipeWrap);
NotifyOfPropertyChange(() => HasUnsavedChanges);
}
protected bool IsRecipeDirty()
{
if (CurrentRecipe == null)
return false;
var path = Path.Combine(CurrentRecipe.Dir ?? string.Empty, CurrentRecipe.FileName ?? string.Empty);
if (!File.Exists(path))
return true;
return !AreSnapshotsEquivalent(_lastSavedSnapshot, CurrentRecipe.CreateSnapshot());
}
protected void CaptureSavedSnapshot()
{
_lastSavedSnapshot = CurrentRecipe != null ? CurrentRecipe.CreateSnapshot() : string.Empty;
NotifyOfPropertyChange(() => HasUnsavedChanges);
NotifyOfPropertyChange(() => CurrentRecipeDisplayName);
}
protected bool TryRenameRecipe(RecipeRenameEventArgs args)
{
if (args == null)
return false;
var recipeWrap = RecipeWraps.FirstOrDefault(p => p.RecipeName == args.OldRecipeName);
if (recipeWrap == null)
return false;
if (!RecipeManager.RenameFolder(RecipeFolderPath, args.OldRecipeName, args.NewRecipeName, out var error))
{
MwMessageBox.Show(error);
return false;
}
recipeWrap.RecipeName = args.NewRecipeName;
recipeWrap.ModifiedTime = DateTime.UtcNow;
if (CurrentRecipe != null && string.Equals(CurrentRecipe.RecipeName, args.OldRecipeName, StringComparison.Ordinal))
{
CurrentRecipe.RecipeName = args.NewRecipeName;
}
if (ReferenceEquals(recipeWrap, SelectedRecipeWrap))
{
SwitchActiveRecipe(args.NewRecipeName);
}
CaptureSavedSnapshot();
AfterRecipeRenamed(args.OldRecipeName, args.NewRecipeName);
return true;
}
protected void SaveCurrentRecipe(bool showSuccessMessage)
{
if (CurrentRecipe == null)
return;
CurrentRecipe.Write();
CaptureSavedSnapshot();
if (SelectedRecipeWrap != null)
{
SelectedRecipeWrap.ModifiedTime = DateTime.UtcNow;
}
if (showSuccessMessage)
{
MwMessageBox.Show("<22><><EFBFBD><EFBFBD><EFBFBD>ɹ<EFBFBD>");
}
}
private static bool AreSnapshotsEquivalent(string leftSnapshot, string rightSnapshot)
{
var left = string.IsNullOrWhiteSpace(leftSnapshot) ? null : ParseSnapshotToken(leftSnapshot);
var right = string.IsNullOrWhiteSpace(rightSnapshot) ? null : ParseSnapshotToken(rightSnapshot);
if (left == null || right == null)
return false;
return JToken.DeepEquals(left, right);
}
private static JToken ParseSnapshotToken(string snapshot)
{
try
{
return JToken.Parse(snapshot);
}
catch
{
return null;
}
}
protected void SelectRecipeWrapInternal(RecipeWrap recipeWrap)
{
try
{
_skipLeaveCheck = true;
SelectedRecipeWrap = recipeWrap;
}
finally
{
_skipLeaveCheck = false;
}
}
protected void InitializeSelection(string activeRecipeName)
{
SyncSelection(activeRecipeName, true);
}
protected virtual void UpdateInUseState(RecipeWrap activeRecipeWrap)
{
foreach (var wrap in RecipeWraps)
{
wrap.IsInUse = ReferenceEquals(wrap, activeRecipeWrap);
}
}
public void SyncSelectionFromActiveRecipe()
{
SyncSelection(GetActiveRecipeName(), false);
}
private void SyncSelection(string recipeName, bool fallbackToFirst)
{
if (RecipeWraps == null || RecipeWraps.Count == 0)
{
UpdateInUseState(null);
return;
}
var target = string.IsNullOrWhiteSpace(recipeName)
? null
: RecipeWraps.FirstOrDefault(p => string.Equals(p.RecipeName, recipeName, StringComparison.Ordinal));
if (target == null)
{
UpdateInUseState(null);
if (!fallbackToFirst)
return;
target = RecipeWraps[0];
}
if (ReferenceEquals(SelectedRecipeWrap, target))
{
UpdateInUseState(target);
return;
}
SelectRecipeWrapInternal(target);
}
private void LoadRecipeFromWrap(RecipeWrap recipeWrap)
{
if (recipeWrap == null)
{
UpdateInUseStateByActiveRecipe();
return;
}
var newRecipe = new TRecipe
{
RecipeName = recipeWrap.RecipeName
};
newRecipe.Read();
CurrentRecipe = newRecipe;
CaptureSavedSnapshot();
UpdateInUseStateByActiveRecipe();
OnRecipeLoaded(newRecipe, recipeWrap);
}
private void UpdateInUseStateByActiveRecipe()
{
var activeRecipeName = GetActiveRecipeName();
var activeRecipeWrap = RecipeWraps == null || string.IsNullOrWhiteSpace(activeRecipeName)
? null
: RecipeWraps.FirstOrDefault(p => string.Equals(p.RecipeName, activeRecipeName, StringComparison.Ordinal));
UpdateInUseState(activeRecipeWrap);
}
}
}