using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using ICSharpCode.AvalonEdit.Highlighting;
using InABox.Core;
using InABox.Scripting;
using InABox.WPF;
using InABox.Wpf;
using Microsoft.CodeAnalysis;
using RoslynPad.Editor;
using RoslynPad.Roslyn;
using Syncfusion.Data.Extensions;
using Image = System.Windows.Controls.Image;
namespace InABox.DynamicGrid
{
///
/// Interaction logic for ScriptEditor.xaml
///
public partial class ScriptEditor : ThemableWindow
{
public static RoutedCommand SaveCommand = new();
private bool _bChanged;
private string _editorTitle = "ScriptEditor";
public string EditorTitle
{
get => _editorTitle;
set {
_editorTitle = value;
UpdateTitle();
}
}
private RoslynHost _host;
private readonly SyntaxLanguage _language = SyntaxLanguage.CSharp;
private readonly string _script = "";
private Dictionary _snippets = new();
private bool bCompiled;
private readonly Dictionary compile = SetupImage(Properties.Resources.tick);
private readonly Dictionary copy = SetupImage(Properties.Resources.copy);
private readonly Dictionary cut = SetupImage(Properties.Resources.cut);
private readonly Dictionary paste = SetupImage(Properties.Resources.paste);
private readonly Dictionary print = SetupImage(Properties.Resources.print);
private readonly Dictionary redo = SetupImage(Properties.Resources.redo);
private readonly Dictionary run = SetupImage(Properties.Resources.run);
private readonly Dictionary save = SetupImage(Properties.Resources.disk);
private readonly Dictionary undo = SetupImage(Properties.Resources.undo);
static ScriptEditor()
{
SaveCommand.InputGestures.Add(new KeyGesture(Key.S, ModifierKeys.Control));
}
public ScriptEditor(string script, SyntaxLanguage language = SyntaxLanguage.CSharp, string scriptTitle = "ScriptEditor")
{
_language = language;
_script = script;
EditorTitle = scriptTitle;
InitializeComponent();
// Not Sure if we need Roslyn for XAML - need to research this
if (language == SyntaxLanguage.CSharp || language == SyntaxLanguage.XAML)
try
{
if (ScriptDocument.Host == null)
ScriptDocument.Initialize();
}
catch (Exception e)
{
MessageBox.Show("Unable to initialize Script Editor!\nPlease try again.\n\n" + e.Message);
}
Roslyn.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition(
language == SyntaxLanguage.HTML
? "HTML"
: language == SyntaxLanguage.XAML
? "XML"
: language == SyntaxLanguage.CSS
? "CSS"
: "C#"
);
Roslyn.TextArea.TextEntering += TextArea_TextEntering;
CheckButton(SaveButton, true, save);
CheckButton(CopyButton, false, copy);
CheckButton(CutButton, false, cut);
CheckButton(PasteButton, false, paste);
CheckButton(UndoButton, false, undo);
CheckButton(RedoButton, false, redo);
CheckButton(PrintButton, true, print);
CheckButton(CompileButton, language == SyntaxLanguage.CSharp || language == SyntaxLanguage.XAML, compile);
CheckButton(RunButton, false, run);
var timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(500);
timer.Tick += Timer_Tick;
timer.Start();
bChanged = false;
}
public string Script => Roslyn.Text;
public Dictionary Snippets
{
get => _snippets;
set
{
_snippets = value;
if (_snippets != null && _snippets.Any())
{
SnippetPanel.Visibility = Visibility.Visible;
SnippetSection.ItemsSource = _snippets.Keys;
SnippetSection.SelectedValue = _snippets.Keys.First();
}
else
{
SnippetPanel.Visibility = Visibility.Collapsed;
}
}
}
private bool bChanged
{
get => _bChanged;
set
{
_bChanged = value;
UpdateTitle();
}
}
private static Dictionary SetupImage(Bitmap bitmap)
{
return new Dictionary
{
{ true, new Image { Source = bitmap.AsBitmapImage(), MaxHeight = 32, MaxWidth = 32 } },
{ false, new Image { Source = bitmap.AsGrayScale().AsBitmapImage(), MaxHeight = 32, MaxWidth = 32 } }
};
}
private event EventHandler _onSave;
public event EventHandler OnSave
{
add
{
_onSave += value;
SaveButton.ToolTip = "Save";
}
remove
{
_onSave -= value;
if (_onSave == null) SaveButton.ToolTip = "Save & Close";
}
}
private event EventHandler _onCompile;
public event EventHandler OnCompile
{
add
{
_onCompile += value;
CheckButton(CompileButton, true, compile);
}
remove => _onCompile -= value;
}
private void CheckButton(Button button, bool condition, Dictionary images)
{
if (button.Visibility != Visibility.Visible)
button.Visibility = Visibility.Visible;
if (condition != button.IsEnabled)
{
button.IsEnabled = condition;
button.Content = images[condition];
}
}
private void UpdateTitle()
{
if (bChanged)
{
Title = _editorTitle + "*";
}
else
{
Title = _editorTitle;
}
}
#region Public Interface
public ScriptEditor ClearErrors()
{
Errors.Items.Clear();
return this;
}
public ScriptEditor AddError(string error)
{
Errors.Items.Add(error);
return this;
}
public void Save()
{
bChanged = false;
if (_onSave != null)
{
_onSave?.Invoke(this, EventArgs.Empty);
}
else
{
DialogResult = true;
Close();
}
}
#endregion
#region Event Handlers
private void TextArea_TextEntering(object sender, TextCompositionEventArgs e)
{
if (string.Equals(e.Text, "\r"))
e.Handled = true;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
if (_language == SyntaxLanguage.CSharp || _language == SyntaxLanguage.XAML)
{
Roslyn.DataContext = new ScriptDocument(_script);
}
else
{
Roslyn.Text = _script;
bChanged = false;
}
//Roslyn.Text = _script;
//Changed = DateTime.MinValue;
}
private void Roslyn_Loaded(object sender, RoutedEventArgs e)
{
var editor = (RoslynCodeEditor)sender;
editor.Focus();
if (_language == SyntaxLanguage.CSharp || _language == SyntaxLanguage.XAML)
{
var changed = bChanged;
var script = (ScriptDocument)editor.DataContext;
script.Id = editor.Initialize(
ScriptDocument.Host,
new ClassificationHighlightColors(),
Directory.GetCurrentDirectory(),
_script,
SourceCodeKind.Regular
);
bChanged = changed;
}
}
private void Roslyn_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (bCompiled)
{
bCompiled = false;
CheckButton(RunButton, false, run);
}
}
private void Roslyn_TextChanged(object sender, EventArgs e)
{
bChanged = true;
if (bCompiled)
{
bCompiled = false;
CheckButton(RunButton, false, run);
}
}
private void Timer_Tick(object sender, EventArgs e)
{
CheckButton(CopyButton, !string.IsNullOrEmpty(Roslyn.SelectedText), copy);
CheckButton(CutButton, !string.IsNullOrEmpty(Roslyn.SelectedText), cut);
CheckButton(PasteButton, Clipboard.ContainsText(), paste);
CheckButton(UndoButton, Roslyn.CanUndo, undo);
CheckButton(RedoButton, Roslyn.CanRedo, redo);
}
private void OKButton_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
Close();
}
private void CancelButton_Click(object sender, RoutedEventArgs e)
{
DialogResult = false;
Close();
}
private void CompileButton_Click(object sender, RoutedEventArgs e)
{
CheckButton(RunButton, false, run);
if (_onCompile != null)
{
_onCompile.Invoke(this, EventArgs.Empty);
}
else
{
var viewModel = (ScriptDocument)Roslyn.DataContext;
viewModel.Text = Roslyn.Text;
Errors.Items.Clear();
Errors.Items.Add("Compiling Script... ");
Task.Run(() =>
{
bCompiled = viewModel.Compile();
Dispatcher.Invoke(() =>
{
Errors.Items.Clear();
if (bCompiled)
{
Errors.Items.Add("Script Compiled Successfully!");
}
else
{
var errors = viewModel.Result.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
errors.ForEach(x => Errors.Items.Add(x));
}
CheckButton(RunButton, bCompiled, run);
});
});
}
}
private void RunButton_Click(object sender, RoutedEventArgs e)
{
var viewModel = (ScriptDocument)Roslyn.DataContext;
try
{
var result = viewModel.Execute();
}
catch (Exception err)
{
MessageBox.Show(CoreUtils.FormatException(err));
}
}
private void GotoLine(string line)
{
if (line.StartsWith("("))
{
var sLine = line.Substring(1).Split(',')[0];
var iLine = int.Parse(sLine);
Roslyn.ScrollToLine(iLine);
}
}
private void Errors_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count == 1) GotoLine(e.AddedItems[0].ToString());
}
private void Errors_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (Errors.SelectedItems.Count == 1) GotoLine(Errors.SelectedItems[0].ToString());
}
private void Window_Closing(object sender, CancelEventArgs e)
{
if (bChanged)
{
var bRes = MessageBox.Show("Script has changed. Do you wish to save this script before closing?", "Save Changes",
MessageBoxButton.YesNoCancel);
if (bRes == MessageBoxResult.Cancel)
{
e.Cancel = true;
return;
}
if (bRes == MessageBoxResult.Yes) _onSave?.Invoke(this, EventArgs.Empty);
try
{
DialogResult = bRes == MessageBoxResult.Yes;
}
catch (InvalidOperationException)
{
// Do nothing, just avoids a crash when closing a script editor which was opened by Show();
}
}
}
private void SaveButton_Click(object sender, RoutedEventArgs e)
{
Save();
}
private void CopyButton_Click(object sender, RoutedEventArgs e)
{
Roslyn.Copy();
}
private void CutButton_Click(object sender, RoutedEventArgs e)
{
Roslyn.Cut();
}
private void PasteButton_Click(object sender, RoutedEventArgs e)
{
Roslyn.Paste();
}
private void UndoButton_Click(object sender, RoutedEventArgs e)
{
Roslyn.Undo();
}
private void RedoButton_Click(object sender, RoutedEventArgs e)
{
Roslyn.Redo();
}
private void PrintButton_Click(object sender, RoutedEventArgs e)
{
//Syncfusion.Windows.Edit.EditCommands.PrintPreview.Execute(null, Edit1);
}
private void Snippets_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
Roslyn.SelectedText = SnippetList.SelectedValue.ToString();
}
private void SnippetSection_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count == 1)
SnippetList.ItemsSource = _snippets[e.AddedItems[0].ToString()];
else
SnippetList.ItemsSource = null;
}
private void SaveCommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
if (_onSave != null)
{
bChanged = false;
_onSave?.Invoke(this, EventArgs.Empty);
}
}
private void SaveCommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = _onSave != null;
}
#endregion
}
}