Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
ScriptView.xaml.cs
1using System;
2using System.IO;
3using System.Text;
4using System.Threading.Tasks;
5using System.Xml;
6using System.Xml.Schema;
7using System.Xml.Xsl;
8using System.Windows;
9using System.Windows.Controls;
10using System.Windows.Documents;
11using System.Windows.Input;
12using System.Windows.Markup;
13using System.Windows.Media;
14using System.Windows.Media.Imaging;
15using Microsoft.Win32;
16using SkiaSharp;
22using Waher.Events;
23using Waher.Script;
29
31{
35 public partial class ScriptView : UserControl, ITabView
36 {
37 private readonly Variables variables;
38
39 public ScriptView()
40 {
42
43 this.variables = new Variables()
44 {
45 ConsoleOut = new Script.PrintOutput(this)
46 };
47
48 this.Input.Focus();
49 }
50
51 private void Input_PreviewKeyDown(object Sender, KeyEventArgs e)
52 {
53 if (e.Key == Key.Enter)
54 {
55 if (!Keyboard.Modifiers.HasFlag(ModifierKeys.Control) && !Keyboard.Modifiers.HasFlag(ModifierKeys.Shift))
56 {
57 this.ExecuteButton_Click(Sender, e);
58 e.Handled = true;
59 }
60 }
61 }
62
63 private void ExecuteButton_Click(object Sender, RoutedEventArgs e)
64 {
66 TextBlock ScriptBlock;
67 UIElement ResultBlock = null;
68
69 try
70 {
71 Exp = new Waher.Script.Expression(this.Input.Text);
72
73 ScriptBlock = new TextBlock()
74 {
75 Text = this.Input.Text,
76 FontFamily = new FontFamily("Courier New"),
77 TextWrapping = TextWrapping.Wrap,
78 Tag = Exp
79 };
80
81 ScriptBlock.PreviewMouseDown += this.TextBlock_PreviewMouseDown;
82
83 this.HistoryPanel.Children.Add(ScriptBlock);
84 this.HistoryScrollViewer.ScrollToBottom();
85
86 this.Input.Text = string.Empty;
87 this.Input.Focus();
88 }
89 catch (Exception ex)
90 {
91 ex = Log.UnnestException(ex);
92 MessageBox.Show(MainWindow.currentInstance, ex.Message, "Unable to parse script.", MessageBoxButton.OK, MessageBoxImage.Error);
93 return;
94 }
95
96 Task.Run(async () =>
97 {
98 try
99 {
100 IElement Ans;
101
102 Task Preview(object sender2, PreviewEventArgs e2)
103 {
104 this.Dispatcher.Invoke(async () =>
105 ResultBlock = await this.ShowResult(ResultBlock, e2.Preview, ScriptBlock));
106
107 return Task.CompletedTask;
108 };
109
110 this.variables.OnPreview += Preview;
111 try
112 {
113 Ans = await Exp.Root.EvaluateAsync(this.variables);
114 }
116 {
117 Ans = ex.ReturnValue;
118 }
119 catch (Exception ex)
120 {
121 Ans = new ObjectValue(ex);
122 }
123 finally
124 {
125 this.variables.OnPreview -= Preview;
126 }
127
128 this.variables["Ans"] = Ans;
129
130 MainWindow.UpdateGui(async () =>
131 {
132 ResultBlock = await this.ShowResult(ResultBlock, Ans, ScriptBlock);
133 });
134 }
135 catch (Exception ex)
136 {
137 ex = Log.UnnestException(ex);
138 MainWindow.MessageBox(ex.Message, "Unable to parse script.", MessageBoxButton.OK, MessageBoxImage.Error);
139 }
140 });
141 }
142
143 private async Task<UIElement> ShowResult(UIElement ResultBlock, IElement Ans, TextBlock ScriptBlock)
144 {
145 try
146 {
147 if (Ans is Graph G)
148 {
149 PixelInformation Pixels = G.CreatePixels(this.variables, out object[] States);
150 return this.AddImageBlock(ScriptBlock, Pixels, G, States, ResultBlock);
151 }
152 else if (Ans.AssociatedObjectValue is SKImage Img)
153 return this.AddImageBlock(ScriptBlock, PixelInformation.FromImage(Img), null, null, ResultBlock);
154 else if (Ans.AssociatedObjectValue is Exception ex)
155 {
156 UIElement Last = ResultBlock ?? ScriptBlock;
157
158 ex = Log.UnnestException(ex);
159
160 if (ex is AggregateException ex2)
161 {
162 foreach (Exception ex3 in ex2.InnerExceptions)
163 Last = this.AddTextBlock(Last, ex3.Message, Colors.Red, FontWeights.Bold, null, ex3);
164 }
165 else
166 Last = this.AddTextBlock(ScriptBlock, ex.Message, Colors.Red, FontWeights.Bold, ResultBlock, ex);
167
168 return Last;
169 }
170 else if (Ans.AssociatedObjectValue is ObjectMatrix M && !(M.ColumnNames is null))
171 {
172 StringBuilder Markdown = new StringBuilder();
173
174 foreach (string s2 in M.ColumnNames)
175 {
176 Markdown.Append("| ");
177 Markdown.Append(MarkdownDocument.Encode(s2));
178 }
179
180 Markdown.AppendLine(" |");
181
182 foreach (string s2 in M.ColumnNames)
183 Markdown.Append("|---");
184
185 Markdown.AppendLine("|");
186
187 int x, y;
188
189 for (y = 0; y < M.Rows; y++)
190 {
191 for (x = 0; x < M.Columns; x++)
192 {
193 Markdown.Append("| ");
194
195 object Item = M.GetElement(x, y).AssociatedObjectValue;
196 if (!(Item is null))
197 {
198 if (!(Item is string s2))
199 s2 = Waher.Script.Expression.ToString(Item);
200
201 s2 = s2.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", "<br/>");
202 Markdown.Append(MarkdownDocument.Encode(s2));
203 }
204 }
205
206 Markdown.AppendLine(" |");
207 }
208
209 MarkdownDocument Doc = await MarkdownDocument.CreateAsync(Markdown.ToString(), ChatView.GetMarkdownSettings());
210 string XAML = await Doc.GenerateXAML(ChatView.GetXamlSettings());
211
212 if (XamlReader.Parse(XAML) is UIElement Parsed)
213 return this.AddBlock(ScriptBlock, Parsed);
214
215 return null;
216 }
217 else
218 return this.AddTextBlock(ScriptBlock, Ans.ToString(), Colors.Red, FontWeights.Normal, ResultBlock, Ans);
219 }
220 catch (Exception ex)
221 {
222 ex = Log.UnnestException(ex);
223 Ans = new ObjectValue(ex);
224 this.variables["Ans"] = Ans;
225
226 if (ex is AggregateException ex2)
227 {
228 foreach (Exception ex3 in ex2.InnerExceptions)
229 ScriptBlock = this.AddTextBlock(ScriptBlock, ex3.Message, Colors.Red, FontWeights.Bold, null, ex3);
230 }
231 else
232 this.AddTextBlock(ScriptBlock, ex.Message, Colors.Red, FontWeights.Bold, ResultBlock, ex);
233
234 return null;
235 }
236 }
237
238 private TextBlock AddTextBlock(UIElement ScriptBlock, string s, Color cl, FontWeight Weight, UIElement ResultBlock, object Tag)
239 {
240 if (ResultBlock is TextBlock TextBlock)
241 {
242 TextBlock.Text = s;
243 TextBlock.Tag = Tag;
244 }
245 else
246 {
247 TextBlock = new TextBlock()
248 {
249 Text = s,
250 FontFamily = new FontFamily("Courier New"),
251 Foreground = new SolidColorBrush(cl),
252 TextWrapping = TextWrapping.Wrap,
253 FontWeight = Weight,
254 Tag = Tag
255 };
256
257 this.AddBlock(ScriptBlock, TextBlock);
258 }
259
260 return TextBlock;
261 }
262
263 private UIElement AddBlock(UIElement ScriptBlock, UIElement ResultBlock)
264 {
265 if (ScriptBlock is null)
266 this.HistoryPanel.Children.Add(ResultBlock);
267 else
268 this.HistoryPanel.Children.Insert(this.HistoryPanel.Children.IndexOf(ScriptBlock) + 1, ResultBlock);
269
270 return ResultBlock;
271 }
272
273 private void TextBlock_PreviewMouseDown(object Sender, MouseButtonEventArgs e)
274 {
275 this.Input.Text = ((TextBlock)Sender).Text;
276 this.Input.SelectAll();
277 this.Input.Focus();
278 e.Handled = true;
279 }
280
281 private UIElement AddImageBlock(UIElement ScriptBlock, PixelInformation Pixels, Graph Graph, object[] States, UIElement ResultBlock)
282 {
283 BitmapImage BitmapImage;
284 byte[] Bin = Pixels.EncodeAsPng();
285
286 using (MemoryStream ms = new MemoryStream(Bin))
287 {
288 BitmapImage = new BitmapImage();
289 BitmapImage.BeginInit();
290 BitmapImage.CacheOption = BitmapCacheOption.OnLoad;
291 BitmapImage.StreamSource = ms;
292 BitmapImage.EndInit();
293 }
294
295 if (ResultBlock is Image ImageBlock)
296 {
297 ImageBlock.Source = BitmapImage;
298 ImageBlock.Width = Pixels.Width;
299 ImageBlock.Height = Pixels.Height;
300 ImageBlock.Tag = new Tuple<byte[], int, int, Graph, object[]>(Bin, Pixels.Width, Pixels.Height, Graph, States);
301 }
302 else
303 {
304 ImageBlock = new Image()
305 {
306 Source = BitmapImage,
307 Width = Pixels.Width,
308 Height = Pixels.Height,
309 Tag = new Tuple<byte[], int, int, Graph, object[]>(Bin, Pixels.Width, Pixels.Height, Graph, States)
310 };
311
312 ImageBlock.PreviewMouseDown += this.ImageBlock_PreviewMouseDown;
313
314 this.HistoryPanel.Children.Insert(this.HistoryPanel.Children.IndexOf(ScriptBlock) + 1, ImageBlock);
315 }
316
317 return ImageBlock;
318 }
319
320 private void ImageBlock_PreviewMouseDown(object Sender, MouseButtonEventArgs e)
321 {
322 Image ImageBlock = (Image)Sender;
323
324 if (e.ChangedButton == MouseButton.Left)
325 {
326 Point P = e.GetPosition(ImageBlock);
327 string Script;
328
329 if (ImageBlock.Tag is Tuple<byte[], int, int, Graph, object[]> Image && !(Image.Item4 is null) && !(Image.Item5 is null))
330 {
331 double X = ((double)P.X) * Image.Item2 / ImageBlock.ActualWidth;
332 double Y = ((double)P.Y) * Image.Item3 / ImageBlock.ActualHeight;
333
334 Script = Image.Item4.GetBitmapClickScript(X, Y, Image.Item5);
335 }
336 else
337 Script = "[" + P.X.ToString() + "," + P.Y.ToString() + "]";
338
339 this.Input.Text = Script;
340 this.ExecuteButton_Click(this, e);
341 }
342 else if (e.ChangedButton == MouseButton.Right)
343 {
344 BitmapImage Image = (BitmapImage)ImageBlock.Source;
345
346 SaveFileDialog Dialog = new SaveFileDialog()
347 {
348 Title = "Save Image",
349 DefaultExt = ImageCodec.FileExtensionPng,
350 Filter = "PNG files (*.png)|*.png|All Image files (*.bmp, *.gif, *.jpg, *.jpeg, *.png, *.tif, *.tiff)|*.bmp, *.gif, *.jpg, *.jpeg, *.png, *.tif, *.tiff|All files (*.*)|*.*",
351 OverwritePrompt = true
352 };
353
354 bool? Result = Dialog.ShowDialog();
355 if (Result.HasValue && Result.Value)
356 {
357 BitmapEncoder Encoder;
358
359 string Extension = System.IO.Path.GetExtension(Dialog.FileName).ToLower();
360 if (Extension.StartsWith("."))
361 Extension = Extension.Substring(1);
362
363 switch (Extension)
364 {
366 case "jpeg":
367 Encoder = new JpegBitmapEncoder();
368 break;
369
371 Encoder = new BmpBitmapEncoder();
372 break;
373
375 Encoder = new GifBitmapEncoder();
376 break;
377
379 case "tiff":
380 Encoder = new TiffBitmapEncoder();
381 break;
382
384 default:
385 Encoder = new PngBitmapEncoder();
386 break;
387 }
388
389 try
390 {
391 Encoder.Frames.Add(BitmapFrame.Create(Image));
392
393 using (FileStream File = new FileStream(Dialog.FileName, System.IO.FileMode.Create))
394 {
395 Encoder.Save(File);
396 }
397 }
398 catch (Exception ex)
399 {
400 ex = Log.UnnestException(ex);
401 MessageBox.Show(MainWindow.currentInstance, ex.Message, "Unable to save image.", MessageBoxButton.OK, MessageBoxImage.Error);
402 }
403 }
404
405 e.Handled = true;
406 }
407 }
408
409 internal void Print(string Output)
410 {
411 MainWindow.UpdateGui(() =>
412 {
413 this.AddTextBlock(null, Output, Colors.Blue, FontWeights.Normal, null, false);
414 return Task.CompletedTask;
415 });
416 }
417
418 public void NewButton_Click(object Sender, RoutedEventArgs e)
419 {
420 this.HistoryPanel.Children.Clear();
421 }
422
423 public void SaveButton_Click(object Sender, RoutedEventArgs e)
424 {
425 this.SaveAsButton_Click(Sender, e);
426 }
427
428 public void SaveAsButton_Click(object Sender, RoutedEventArgs e)
429 {
430 SaveFileDialog Dialog = new SaveFileDialog()
431 {
432 AddExtension = true,
433 CheckPathExists = true,
434 CreatePrompt = false,
435 DefaultExt = "html",
436 Filter = "Script Files (*.xml)|*.xml|Script Files (*.script)|*.script|HTML Files (*.html,*.htm)|*.html,*.htm|All Files (*.*)|*.*",
437 Title = "Save Script"
438 };
439
440 bool? Result = Dialog.ShowDialog(MainWindow.FindWindow(this));
441
442 if (Result.HasValue && Result.Value)
443 {
444 try
445 {
446 switch (Dialog.FilterIndex)
447 {
448 case 1:
449 using (FileStream f = File.Create(Dialog.FileName))
450 {
451 using (XmlWriter w = XmlWriter.Create(f, XML.WriterSettings(true, false)))
452 {
453 this.SaveAsXml(w);
454 }
455 }
456 break;
457
458 case 3:
459 StringBuilder Xml = new StringBuilder();
460 using (XmlWriter w = XmlWriter.Create(Xml, XML.WriterSettings(true, true)))
461 {
462 this.SaveAsXml(w);
463 }
464
465 string Html = XSL.Transform(Xml.ToString(), scriptToHtml);
466
467 File.WriteAllText(Dialog.FileName, Html, System.Text.Encoding.UTF8);
468 break;
469
470 case 2:
471 default:
472 using (StreamWriter w = File.CreateText(Dialog.FileName))
473 {
474 foreach (Object Obj in this.HistoryPanel.Children)
475 {
476 if (Obj is TextBlock TextBlock && TextBlock.Tag is bool b && b)
477 {
478 string s = TextBlock.Text.TrimEnd();
479 if (!string.IsNullOrEmpty(s) && !s.EndsWith(";"))
480 s += ";";
481
482 w.WriteLine(s);
483 }
484 }
485
486 w.Flush();
487 }
488 break;
489 }
490 }
491 catch (Exception ex)
492 {
493 MessageBox.Show(MainWindow.FindWindow(this), ex.Message, "Unable to save file.", MessageBoxButton.OK, MessageBoxImage.Error);
494 }
495 }
496 }
497
498 private static readonly XslCompiledTransform scriptToHtml = XSL.LoadTransform("Waher.Client.WPF.Transforms.ScriptToHTML.xslt");
499 private static readonly XmlSchema schema = XSL.LoadSchema("Waher.Client.WPF.Schema.Script.xsd");
500 private const string scriptNamespace = "http://waher.se/Schema/Script.xsd";
501 private const string scriptRoot = "Script";
502
503 private void SaveAsXml(XmlWriter w)
504 {
505 w.WriteStartElement(scriptRoot, scriptNamespace);
506
507 foreach (object Object in this.HistoryPanel.Children)
508 {
509 if (Object is TextBlock TextBlock)
510 {
511 if (TextBlock.Tag is Waher.Script.Expression Exp)
512 w.WriteElementString("Expression", Exp.Script);
513 else if (TextBlock.Tag is Exception ex)
514 w.WriteElementString("Error", ex.Message);
515 else if (TextBlock.Tag is bool b)
516 {
517 if (b)
518 w.WriteElementString("Result", TextBlock.Text);
519 else
520 w.WriteElementString("Print", TextBlock.Text);
521 }
522 }
523 else if (Object is Image ImageBlock)
524 {
525 if (ImageBlock.Tag is Tuple<byte[], int, int, Graph, object[]> Image)
526 {
527 w.WriteStartElement("Image");
528 w.WriteAttributeString("width", Image.Item2.ToString());
529 w.WriteAttributeString("height", Image.Item3.ToString());
530 w.WriteValue(Convert.ToBase64String(Image.Item1));
531 w.WriteEndElement();
532 }
533 }
534 }
535
536 w.WriteEndElement();
537 w.Flush();
538 }
539
540 public void OpenButton_Click(object Sender, RoutedEventArgs e)
541 {
542 try
543 {
544 OpenFileDialog Dialog = new OpenFileDialog()
545 {
546 AddExtension = true,
547 CheckFileExists = true,
548 CheckPathExists = true,
549 DefaultExt = "xml",
550 Filter = "Script Files (*.xml)|*.xml|Script Files (*.script)|*.script|All Files (*.*)|*.*",
551 Multiselect = false,
552 ShowReadOnly = true,
553 Title = "Load Script"
554 };
555
556 bool? Result = Dialog.ShowDialog(MainWindow.FindWindow(this));
557
558 if (Result.HasValue && Result.Value)
559 {
560 if (Dialog.FilterIndex == 1)
561 {
562 XmlDocument Xml = new XmlDocument()
563 {
564 PreserveWhitespace = true
565 };
566 Xml.Load(Dialog.FileName);
567
568 this.Load(Xml, Dialog.FileName);
569 }
570 else
571 {
572 string Script = File.ReadAllText(Dialog.FileName);
573
574 this.Input.Text = Script;
575 this.ExecuteButton_Click(this, new RoutedEventArgs());
576 }
577 }
578 }
579 catch (Exception ex)
580 {
581 ex = Log.UnnestException(ex);
582 MessageBox.Show(ex.Message, "Unable to load file.", MessageBoxButton.OK, MessageBoxImage.Error);
583 }
584 }
585
586 public void Load(XmlDocument Xml, string FileName)
587 {
588 XSL.Validate(FileName, Xml, scriptRoot, scriptNamespace, schema);
589
590 this.HistoryPanel.Children.Clear();
591
592 foreach (XmlNode N in Xml.DocumentElement.ChildNodes)
593 {
594 if (N is XmlElement E)
595 {
596 switch (E.LocalName)
597 {
598 case "Expression":
599 Waher.Script.Expression Exp = new Waher.Script.Expression(this.Input.Text);
600
601 TextBlock ScriptBlock = new TextBlock()
602 {
603 Text = E.InnerText,
604 FontFamily = new FontFamily("Courier New"),
605 TextWrapping = TextWrapping.Wrap,
606 Tag = Exp
607 };
608
609 ScriptBlock.PreviewMouseDown += this.TextBlock_PreviewMouseDown;
610
611 this.HistoryPanel.Children.Add(ScriptBlock);
612 break;
613
614 case "Error":
615 this.AddTextBlock(null, E.InnerText, Colors.Red, FontWeights.Bold, null, new Exception(E.InnerText));
616 break;
617
618 case "Result":
619 this.AddTextBlock(null, E.InnerText, Colors.Red, FontWeights.Normal, null, true);
620 break;
621
622 case "Print":
623 this.AddTextBlock(null, E.InnerText, Colors.Blue, FontWeights.Normal, null, false);
624 break;
625
626 case "Image":
627 BitmapImage BitmapImage;
628 byte[] Bin = Convert.FromBase64String(E.InnerText);
629 int Width = XML.Attribute(E, "width", 0);
630 int Height = XML.Attribute(E, "height", 0);
631
632 using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(E.InnerText)))
633 {
634 BitmapImage = new BitmapImage();
635 BitmapImage.BeginInit();
636 BitmapImage.CacheOption = BitmapCacheOption.OnLoad;
637 BitmapImage.StreamSource = ms;
638 BitmapImage.EndInit();
639 }
640
641 Image ImageBlock = new System.Windows.Controls.Image()
642 {
643 Source = BitmapImage,
644 Width = Width,
645 Height = Height,
646 Tag = new Tuple<byte[], int, int, Graph, object[]>(Bin, Width, Height, null, null)
647 };
648
649 ImageBlock.PreviewMouseDown += this.ImageBlock_PreviewMouseDown;
650
651 this.HistoryPanel.Children.Add(ImageBlock);
652 break;
653 }
654 }
655 }
656
657 this.HistoryScrollViewer.ScrollToBottom();
658 }
659
660
661 public void Dispose()
662 {
663 this.HistoryPanel.Children.Clear();
664 }
665
666 private void Hyperlink_Click(object Sender, RoutedEventArgs e)
667 {
668 string Uri = ((Hyperlink)Sender).NavigateUri.ToString();
669 System.Diagnostics.Process.Start(Uri);
670 }
671 }
672}
Interaction logic for ChatView.xaml
Interaction logic for ScriptView.xaml
void InitializeComponent()
InitializeComponent
Definition: ScriptView.g.cs:74
Interaction logic for xaml
Image encoder/decoder.
Definition: ImageCodec.cs:14
const string FileExtensionJpg
jpg
Definition: ImageCodec.cs:90
const string FileExtensionPng
png
Definition: ImageCodec.cs:75
const string FileExtensionGif
gif
Definition: ImageCodec.cs:85
const string FileExtensionBmp
bmp
Definition: ImageCodec.cs:80
const string FileExtensionTiff
tif
Definition: ImageCodec.cs:100
Contains a markdown document. This markdown document class supports original markdown,...
static string Encode(string s)
Encodes all special characters in a string so that it can be included in a markdown document without ...
static Task< MarkdownDocument > CreateAsync(string MarkdownText, params Type[] TransparentExceptionTypes)
Contains a markdown document. This markdown document class supports original markdown,...
Helps with common XML-related tasks.
Definition: XML.cs:19
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
Definition: XML.cs:914
static XmlWriterSettings WriterSettings(bool Indent, bool OmitXmlDeclaration)
Gets an XML writer settings object.
Definition: XML.cs:1177
Static class managing loading of XSL resources stored as embedded resources or in content files.
Definition: XSL.cs:15
static XmlSchema LoadSchema(string ResourceName)
Loads an XML schema from an embedded resource.
Definition: XSL.cs:23
static XslCompiledTransform LoadTransform(string ResourceName)
Loads an XSL transformation from an embedded resource.
Definition: XSL.cs:70
static string Transform(string XML, XslCompiledTransform Transform)
Transforms an XML document using an XSL transform.
Definition: XSL.cs:162
static void Validate(string ObjectID, XmlDocument Xml, params XmlSchema[] Schemas)
Validates an XML document given a set of XML schemas.
Definition: XSL.cs:118
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
static Exception UnnestException(Exception Exception)
Unnests an exception, to extract the relevant inner exception.
Definition: Log.cs:818
Class managing a script expression.
Definition: Expression.cs:39
ScriptNode Root
Root script node.
Definition: Expression.cs:4299
static string ToString(double Value)
Converts a value to a string, that can be parsed as part of an expression.
Definition: Expression.cs:4496
Base class for graphs.
Definition: Graph.cs:79
Contains pixel information
virtual byte[] EncodeAsPng()
Encodes the pixels into a binary PNG image.
static PixelInformation FromImage(SKImage Image)
Gets the pixel information from an SKImage.
Event arguments for preview events.
IElement Preview
Preview of result.
Collection of variables.
Definition: Variables.cs:25
Interface for tab view user controls in the client.
Definition: ITabView.cs:10
Basic interface for all types of elements.
Definition: IElement.cs:20
object AssociatedObjectValue
Associated object value.
Definition: IElement.cs:33
Definition: App.xaml.cs:4