From dd3fafc3637ddddb4fa31791cb9520ea732aeac8 Mon Sep 17 00:00:00 2001 From: Frederik Jacobsen Date: Sat, 18 Oct 2025 21:55:14 +0200 Subject: [PATCH] Added copy feature --- .../Views/LeftTreeView.axaml | 5 ++ .../Views/LeftTreeView.axaml.cs | 71 +++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/XSDVisualiser.Desktop/Views/LeftTreeView.axaml b/XSDVisualiser.Desktop/Views/LeftTreeView.axaml index df5dd63..95f5a6b 100644 --- a/XSDVisualiser.Desktop/Views/LeftTreeView.axaml +++ b/XSDVisualiser.Desktop/Views/LeftTreeView.axaml @@ -30,6 +30,11 @@ BorderBrush="{Binding Cardinality, Converter={StaticResource OptionalToBrush}}" BorderThickness="{Binding Cardinality, Converter={StaticResource OptionalToThickness}}" Padding="8" Margin="0,0,8,6" Background="{DynamicResource PanelBackgroundBrush}"> + + + + + diff --git a/XSDVisualiser.Desktop/Views/LeftTreeView.axaml.cs b/XSDVisualiser.Desktop/Views/LeftTreeView.axaml.cs index 4541ea3..06e4a03 100644 --- a/XSDVisualiser.Desktop/Views/LeftTreeView.axaml.cs +++ b/XSDVisualiser.Desktop/Views/LeftTreeView.axaml.cs @@ -1,4 +1,10 @@ +using System.Globalization; +using System.Text; +using Avalonia; using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.VisualTree; +using XSDVisualiser.Models; namespace XSDVisualiser.Desktop.Views { @@ -8,5 +14,70 @@ namespace XSDVisualiser.Desktop.Views { InitializeComponent(); } + + private async void OnCopyAsTsvClick(object? sender, RoutedEventArgs e) + { + // Determine the node from the menu item's DataContext + var node = (sender as MenuItem)?.DataContext as SchemaNode; + if (node == null) + node = (sender as Control)?.GetVisualAncestors().OfType().FirstOrDefault()?.DataContext as SchemaNode; + if (node == null) + return; + + var tsv = BuildTsv(node); + + var topLevel = TopLevel.GetTopLevel(this); + if (topLevel?.Clipboard != null) + { + await topLevel.Clipboard.SetTextAsync(tsv); + } + } + + private static string BuildTsv(SchemaNode root) + { + var sb = new StringBuilder(); + // Header + sb.AppendLine("Depth\tName\tNamespace\tTypeName\tBuiltInType\tMinOccurs\tMaxOccurs\tContentModel\tIsNillable"); + AppendNode(sb, root, 0); + return sb.ToString(); + } + + private static void AppendNode(StringBuilder sb, SchemaNode node, int depth) + { + string San(string? s) + { + if (string.IsNullOrEmpty(s)) return string.Empty; + return s.Replace("\t", " ").Replace("\r", " ").Replace("\n", " "); + } + + var min = node.Cardinality?.Min.ToString(CultureInfo.InvariantCulture) ?? string.Empty; + string max; + if (node.Cardinality?.MaxIsUnbounded == true) + max = "unbounded"; + else + max = node.Cardinality != null ? node.Cardinality.Max.ToString(CultureInfo.InvariantCulture) : string.Empty; + + var line = string.Join("\t", new[] + { + depth.ToString(CultureInfo.InvariantCulture), + San(node.Name), + San(node.Namespace), + San(node.TypeName), + San(node.BuiltInType), + min, + max, + San(node.ContentModel), + node.IsNillable ? "true" : "false" + }); + sb.AppendLine(line); + + if (node.Children != null) + { + foreach (var child in node.Children) + { + AppendNode(sb, child, depth + 1); + } + } + } } }