Files
2024-02-18 16:40:33 +08:00

823 lines
23 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.Design;
using System.Linq;
using System.Security.Policy;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Input;
using System.Windows.Media;
using AIStudio.Wpf.DiagramDesigner;
using AIStudio.Wpf.DiagramDesigner.Geometrys;
using AIStudio.Wpf.DiagramDesigner.Helpers;
using AIStudio.Wpf.DiagramDesigner.Models;
using AIStudio.Wpf.DiagramDesigner.Serializable;
using AIStudio.Wpf.DiagramDesigner.Serializable.ViewModels;
using AIStudio.Wpf.Mind.Controls;
using AIStudio.Wpf.Mind.Helpers;
using AIStudio.Wpf.Mind.Models;
namespace AIStudio.Wpf.Mind.ViewModels
{
public class MindNode : DiagramItemViewModel
{
public MindNode() : this(null)
{
}
public MindNode(IDiagramViewModel root) : base(root)
{
}
public MindNode(IDiagramViewModel root, SelectableItemBase designer) : base(root, designer)
{
InitLayout(false);
}
public MindNode(IDiagramViewModel root, SerializableItem serializableItem, string serializableType) : base(root, serializableItem, serializableType)
{
InitLayout(false);
}
public override SelectableItemBase GetSerializableObject()
{
return new MindNodeDesignerItem(this);
}
public override DiagramNode ToDiagram()
{
var mindNodeModel = new MindNodeModel();
mindNodeModel.MindType = MindType;
mindNodeModel.MindTheme = MindTheme;
mindNodeModel.Spacing = Spacing;
mindNodeModel.Offset = Offset;
mindNodeModel.IsExpanded = IsExpanded;
mindNodeModel.LinkInfoModel = new LinkInfoModel(LinkInfo);
mindNodeModel.ImageInfoModel = new ImageInfoModel(ImageInfo);
mindNodeModel.Remark = Remark;
mindNodeModel.Priority = Priority;
mindNodeModel.Rate = Rate;
if (Tags != null)
{
mindNodeModel.Tags = new List<string>(Tags);
}
return mindNodeModel;
}
public override void Init(IDiagramViewModel root, bool initNew)
{
base.Init(root, initNew);
EnabledForConnection = false;
AlwayForResized = true;
CustomText = true;
IsInnerConnector = true;
AddChildCommand = (Root as IMindDiagramViewModel)?.AddChildCommand;
AddParentCommand = (Root as IMindDiagramViewModel)?.AddParentCommand;
AddPearCommand = (Root as IMindDiagramViewModel)?.AddPearCommand;
DeleteCommand = (Root as IMindDiagramViewModel)?.DeleteCommand;
MoveForwardCommand = (Root as IMindDiagramViewModel)?.MoveForwardCommand;
MoveBackCommand = (Root as IMindDiagramViewModel)?.MoveBackCommand;
ExportCommand = (Root as IMindDiagramViewModel)?.ExportCommand;
ImportCommand = (Root as IMindDiagramViewModel)?.ImportCommand;
BuildMenuOptions();
Tags = new ObservableCollection<string>();
}
protected override void InitNew()
{
this.ClearConnectors();
}
public void InitLayout(bool initAppearance)
{
IsLoaded = false;
var layout = TypeHelper.GetType(this.MindType.ToString() + "Layout");
MindLayout = layout != null ? (System.Activator.CreateInstance(layout) as IMindLayout) : new MindLayout();
MindLayout.Appearance(this, MindTheme, initAppearance);
IsLoaded = true;
}
public void InitConnectionLayout()
{
var connector = Root?.Items.OfType<ConnectionViewModel>().Where(p => p.IsFullConnection).FirstOrDefault(p => p.SinkConnectorInfoFully.DataItem == this);
if (connector != null)
{
MindLayout?.GetOrSetConnectionViewModel(connector.SourceConnectorInfoFully.DataItem as MindNode, connector.SinkConnectorInfoFully.DataItem as MindNode, connector);
}
else if (ParentNode != null)
{
connector = MindLayout?.GetOrSetConnectionViewModel(ParentNode, this, null);
Root?.Add(new SelectableDesignerItemViewModelBase[] { connector });
connector.ZIndex = -1;
connector.IsSelected = false;
}
}
public void UpdatedLayout()
{
MindLayout?.UpdatedLayout(GetLevel0Node());
}
public void ThemeChange()
{
MindThemeHelper.ThemeChange(this, MindTheme);
}
protected override void LoadDesignerItemViewModel(SelectableItemBase designerbase)
{
base.LoadDesignerItemViewModel(designerbase);
if (designerbase is MindNodeDesignerItem designer)
{
MindType = designer.MindType;
MindTheme = designer.MindTheme;
Spacing = designer.Spacing;
Offset = designer.Offset;
IsExpanded = designer.IsExpanded;
if (string.IsNullOrEmpty(designer.LinkInfoItem?.Url) && string.IsNullOrEmpty(designer.LinkInfoItem?.Text))
{
LinkInfo = null;
}
else
{
LinkInfo = new LinkInfo(designer.LinkInfoItem?.Url, designer.LinkInfoItem?.Text);
}
if (string.IsNullOrEmpty(designer.ImageInfoItem?.Url) && string.IsNullOrEmpty(designer.ImageInfoItem?.Text))
{
ImageInfo = null;
}
else
{
ImageInfo = new ImageInfo(designer.ImageInfoItem?.Url, designer.ImageInfoItem?.Text);
}
Remark = designer.Remark;
if (double.TryParse(designer.Priority ?? "", out var priority))
{
Priority = priority;
}
if (double.TryParse(designer.Rate ?? "", out var rate))
{
Rate = rate;
}
if (designer.Tags != null)
{
Tags = new ObservableCollection<string>(designer.Tags);
}
}
}
#region
private MindType _mindType = MindType.Mind;
public MindType MindType
{
get
{
return GetLevel0Node()._mindType;
}
set
{
GetLevel0Node()._mindType = value;
}
}
private MindTheme _mindTheme;
public MindTheme MindTheme
{
get
{
return GetLevel0Node()._mindTheme;
}
set
{
GetLevel0Node()._mindTheme = value;
}
}
public IMindLayout MindLayout
{
get; set;
}
public MindNode ParentNode
{
get
{
return Parent as MindNode;
}
}
public MindNode RootNode
{
get
{
return GetLevel0Node();
}
}
public int NodeLevel
{
get
{
if (ParentNode == null)
{
return 0;
}
else
{
return ParentNode.NodeLevel + 1;
}
}
}
private bool _isExpanded = true;
[CanDo]
public bool IsExpanded
{
get
{
return _isExpanded;
}
set
{
SetProperty(ref _isExpanded, value);
}
}
public SizeBase Spacing
{
get; set;
} = new SizeBase(15, 15);
private ObservableCollection<MindNode> _children = new ObservableCollection<MindNode>();
public ObservableCollection<MindNode> Children
{
get
{
return _children;
}
set
{
SetProperty(ref _children, value);
}
}
public SizeBase SizeWithSpacing
{
get
{
return this.Size.Add(Spacing.Width * 2, Spacing.Height * 2);
}
}
public SizeBase DesiredSize
{
get; set;
}
public PointBase DesiredPosition
{
get; set;
}
public PointBase DesiredMiddlePosition
{
get
{
return new PointBase(this.DesiredPosition.X + ItemWidth / 2, this.DesiredPosition.Y + ItemHeight / 2);
}
}
public PointBase Offset
{
get; set;
}
private ConnectorOrientation _connectorOrientation = ConnectorOrientation.Left;
public ConnectorOrientation ConnectorOrientation
{
get
{
return _connectorOrientation;
}
set
{
SetProperty(ref _connectorOrientation, value);
}
}
public bool LayoutUpdating
{
get; set;
}
#endregion
#region
private LinkInfo _linkInfo;
[CanDo]
public LinkInfo LinkInfo
{
get
{
return _linkInfo;
}
set
{
SetProperty(ref _linkInfo, value);
}
}
private ImageInfo _imageInfo;
[CanDo]
public ImageInfo ImageInfo
{
get
{
return _imageInfo;
}
set
{
SetProperty(ref _imageInfo, value);
}
}
private string _remark;
[CanDo]
[Browsable(true)]
public string Remark
{
get
{
return _remark;
}
set
{
SetProperty(ref _remark, value);
}
}
private double? _priority;
[CanDo]
public double? Priority
{
get
{
return _priority;
}
set
{
SetProperty(ref _priority, value);
}
}
private double? _rate;
[CanDo]
public double? Rate
{
get
{
return _rate;
}
set
{
SetProperty(ref _rate, value);
}
}
private ObservableCollection<string> _tags;
[CanDo]
public ObservableCollection<string> Tags
{
get
{
return _tags;
}
set
{
if (_tags != null)
{
_tags.CollectionChanged -= _tags_CollectionChanged;
}
SetProperty(ref _tags, value);
if (_tags != null)
{
_tags.CollectionChanged += _tags_CollectionChanged;
}
}
}
private void _tags_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (var item in e.NewItems.OfType<string>())
{
var width = GetTextDisplayWidthHelper.GetTextDisplayWidth(item, new FontFamily(FontViewModel.FontFamily), FontViewModel.FontStyle, FontViewModel.FontWeight, FontViewModel.FontStretch, 12) + 6;
ItemWidth += width;
}
}
if (e.OldItems != null)
{
foreach (var item in e.OldItems.OfType<string>())
{
var width = GetTextDisplayWidthHelper.GetTextDisplayWidth(item, new FontFamily(FontViewModel.FontFamily), FontViewModel.FontStyle, FontViewModel.FontWeight, FontViewModel.FontStretch, 12) + 6;
ItemWidth -= width;
}
}
}
#endregion
#region
public ICommand AddParentCommand
{
get; private set;
}
public ICommand AddChildCommand
{
get; private set;
}
public ICommand AddPearCommand
{
get; private set;
}
public ICommand DeleteCommand
{
get; private set;
}
public ICommand MoveForwardCommand
{
get; private set;
}
public ICommand MoveBackCommand
{
get; private set;
}
public ICommand ExportCommand
{
get; private set;
}
public ICommand ImportCommand
{
get; private set;
}
#endregion
#region
private void BuildMenuOptions()
{
menuOptions = new ObservableCollection<CinchMenuItem>();
CinchMenuItem menuItem = new CinchMenuItem();
menuItem.Text = "下级";
menuItem.Command = AddChildCommand;
menuItem.CommandParameter = this;
menuOptions.Add(menuItem);
menuItem = new CinchMenuItem();
menuItem.Text = "同级";
menuItem.Command = AddPearCommand;
menuItem.CommandParameter = this;
menuOptions.Add(menuItem);
menuItem = new CinchMenuItem();
menuItem.Text = "上级";
menuItem.Command = AddParentCommand;
menuItem.CommandParameter = this;
menuOptions.Add(menuItem);
menuItem = new CinchMenuItem();
menuItem.Text = "前移";
menuItem.Command = MoveForwardCommand;
menuItem.CommandParameter = this;
menuOptions.Add(menuItem);
menuItem = new CinchMenuItem();
menuItem.Text = "后移";
menuItem.Command = MoveBackCommand;
menuItem.CommandParameter = this;
menuOptions.Add(menuItem);
menuItem = new CinchMenuItem();
menuItem.Text = "删除";
menuItem.Command = DeleteCommand;
menuItem.CommandParameter = this;
menuOptions.Add(menuItem);
menuItem = new CinchMenuItem();
menuItem.Text = "导出节点";
menuItem.Command = ExportCommand;
menuItem.CommandParameter = this;
menuOptions.Add(menuItem);
menuItem = new CinchMenuItem();
menuItem.Text = "导入节点";
menuItem.Command = ImportCommand;
menuItem.CommandParameter = this;
menuOptions.Add(menuItem);
}
#endregion
#region
public void AddTo(MindNode parent, int index = -1, bool isSelected = true)
{
//parent为空为根节点
if (index >= 0)
{
parent?.Children.Insert(index, this);
}
else
{
parent?.Children.Add(this);
}
this.Parent = parent;
this.ParentId = parent?.Id ?? Guid.Empty;
this.Offset = parent?.Offset ?? new PointBase();
this.InitLayout(true);//因为节点的层级不同的样式所以需要Parent确定后才能初始化
this.InitConnectionLayout();
Root?.Add(this);
if (isSelected)
{
if (parent != null)
{
parent.IsSelected = false;
}
this.IsSelected = true;
}
}
public void RemoveFrom()
{
this.IsLoaded = false;
if (this.ParentNode != null)
{
this.ParentNode.Children.Remove(this);
}
var connectors = Root?.Items.OfType<ConnectionViewModel>().Where(p => p.SinkConnectorInfoFully?.DataItem == this).ToList();
Root?.Delete(this);
Root?.Delete(connectors);
}
#endregion
#region
protected override void Item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (IsLoaded == false) return;
if (GetLevel0Node()?.LayoutUpdating == true) return;
Interlocked.Increment(ref Root.DoCommandManager.BeginDo);
switch (e.PropertyName)
{
case nameof(Left):
{
if (e is ValuePropertyChangedEventArgs valuePropertyChangedEventArgs)
{
UpdateOffsetX((double)valuePropertyChangedEventArgs.OldValue, (double)valuePropertyChangedEventArgs.NewValue);
}
break;
}
case nameof(Top):
{
if (e is ValuePropertyChangedEventArgs valuePropertyChangedEventArgs)
{
UpdateOffsetY((double)valuePropertyChangedEventArgs.OldValue, (double)valuePropertyChangedEventArgs.NewValue);
}
break;
}
case nameof(IsExpanded):
case nameof(ItemWidth):
case nameof(ItemHeight):
{
UpdatedLayout();
break;
}
case nameof(NodeLevel):
MindLayout?.Appearance(this);
break;
case nameof(Text):
case nameof(Rate):
case nameof(Priority):
case nameof(Remark):
case nameof(LinkInfo):
case nameof(ImageInfo):
{
SetItemWidthHeight();
break;
}
case nameof(FontViewModel.FontSize):
SetItemWidthHeight();
break;
}
Interlocked.Decrement(ref Root.DoCommandManager.BeginDo);
}
#endregion
#region
public MindNode GetLevel0Node()
{
var node = this;
while (node.Parent is MindNode mindNode)
{
node = mindNode;
}
return node;
}
public MindNode GetLevel1Node()
{
var node = this;
while (node.Parent is MindNode mindNode)
{
if (node.NodeLevel == 1)
{
break;
}
node = mindNode;
}
return node;
}
public List<MindNode> GetParent()
{
var node = this;
List<MindNode> mindnode = new List<MindNode>();
while (node.ParentNode != null)
{
mindnode.Add(node.ParentNode);
node = node.ParentNode;
}
return mindnode;
}
public List<MindNode> GetChildren(bool self = false)
{
List<MindNode> mindnode = new List<MindNode>();
if (self)
{
mindnode.Add(this);
}
if (this.Children != null)
{
foreach (var child in this.Children)
{
mindnode.Add(child);
mindnode.AddRange(child.GetChildren());
}
}
return mindnode;
}
public string GetChildrenText(bool self = false)
{
StringBuilder sb = new StringBuilder();
List<MindNode> mindnode = new List<MindNode>();
if (self)
{
sb.AppendLine(this.Text);
}
if (this.Children != null)
{
foreach (var child in this.Children)
{
sb.AppendLine($"{new String('\t', child.NodeLevel)}{child.Text}");
sb.Append(child.GetChildrenText());
}
}
return sb.ToString();
}
protected void UpdateOffsetX(double oldvalue, double newvalue)
{
Offset += new VectorBase(newvalue - oldvalue, 0);
}
protected void UpdateOffsetY(double oldvalue, double newvalue)
{
Offset += new VectorBase(0, newvalue - oldvalue);
}
private void SetItemWidthHeight()
{
double width = 0;
double height = 0;
width += GetTextDisplayWidthHelper.GetTextDisplayWidth(Text, new FontFamily(FontViewModel.FontFamily), FontViewModel.FontStyle, FontViewModel.FontWeight, FontViewModel.FontStretch, FontViewModel.FontSize) + 30;
width += Rate == null ? 0 : 24;
width += Priority == null ? 0 : 24;
width += Remark == null ? 0 : 24;
width += LinkInfo == null ? 0 : 24;
var defaultTheme = MindThemeHelper.GetNodeDefaultTheme(this);
if (ImageInfo != null)
{
width = Math.Max(width, 160);
height = defaultTheme.ItemHeight / defaultTheme.FontSize * FontViewModel.FontSize + 135;
}
else
{
height = defaultTheme.ItemHeight / defaultTheme.FontSize * FontViewModel.FontSize;
}
ItemWidth = width;
ItemHeight = height;
}
public override void AddToSelection(bool selected, bool clearother)
{
if (clearother)
{
foreach (SelectableDesignerItemViewModelBase item in Root.SelectedItems.ToList())
{
if (item != this)
{
item.RemoveFromSelection();
}
}
}
IsSelected = selected;
}
protected override void ExecuteExitEditCommand(object param)
{
Root.Next();
}
#endregion
}
public class LinkInfo
{
public string Url
{
get; set;
}
public string Text
{
get; set;
}
public LinkInfo()
{
}
public LinkInfo(string url, string text)
{
Url = url;
Text = text;
}
public LinkInfo(LinkInfo info)
{
Url = info?.Url;
Text = info?.Text;
}
}
public class ImageInfo
{
public string Url
{
get; set;
}
public string Text
{
get; set;
}
public ImageInfo()
{
}
public ImageInfo(string url, string text)
{
Url = url;
Text = text;
}
public ImageInfo(ImageInfo info)
{
Url = info?.Url;
Text = info?.Text;
}
}
}