| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 | using InABox.Core;using NPOI.DDF;using System;using System.Collections.Generic;using System.ComponentModel;using System.Linq;using System.Runtime.CompilerServices;using System.Text;using System.Text.RegularExpressions;using System.Threading.Tasks;using System.Windows;using System.Windows.Controls;using System.Windows.Data;using System.Windows.Documents;using System.Windows.Input;using System.Windows.Media;using System.Windows.Media.Imaging;using System.Windows.Shapes;namespace InABox.DynamicGrid{    /// <summary>    /// Interaction logic for ExpressionEditorWindow.xaml    /// </summary>    public partial class ExpressionEditorWindow : Window    {        public static List<Tuple<string, List<FunctionTemplate>>> FunctionTemplates = new();        public List<string> Variables { get; private set; }        public string Expression        {            get => Editor.Text;            set => Editor.Text = value;        }        private static void RegisterFunctionTemplate(string group, string name, string? description, List<IFunctionParameter> parameters)        {            var groupList = FunctionTemplates.FirstOrDefault(x => x.Item1 == group)?.Item2;            if(groupList is null)            {                groupList = new List<FunctionTemplate>();                FunctionTemplates.Add(new(group, groupList));            }            groupList.Add(new FunctionTemplate            {                Name = name,                Parameters = parameters,                Description = description ?? ""            });        }        private static void RegisterFunctionTemplate(string group, string name, params string[] parameters)            => RegisterFunctionTemplate(group, name, null, parameters.Select(x =>            {                var parts = x.Split(' ');                if (parts.Length == 1)                {                    return new FunctionParameter(parts[0], "");                }                return new FunctionParameter(parts[1], parts[0]);            }).ToList<IFunctionParameter>());        private static void RegisterFunctionTemplateDesc(string group, string name, string description, params string[] parameters)            => RegisterFunctionTemplate(group, name, description, parameters.Select(x =>            {                var parts = x.Split(' ');                if (parts.Length == 1)                {                    return new FunctionParameter(parts[0], "");                }                return new FunctionParameter(parts[1], parts[0]);            }).ToList<IFunctionParameter>());        static ExpressionEditorWindow()        {            RegisterFunctionTemplate("Math", "Abs", "double x");            RegisterFunctionTemplate("Math", "Acos", "double x");            RegisterFunctionTemplate("Math", "Asin", "double x");            RegisterFunctionTemplate("Math", "Atan", "double x");            RegisterFunctionTemplate("Math", "Ceiling", "double x");            RegisterFunctionTemplate("Math", "Cos", "double x");            RegisterFunctionTemplate("Math", "Count", "...");            RegisterFunctionTemplate("Math", "Exp", "double exp");            RegisterFunctionTemplate("Math", "Floor", "double x");            RegisterFunctionTemplate("Math", "IEEERemainder", "double x", "double y");            RegisterFunctionTemplate("Math", "Log10", "double x");            RegisterFunctionTemplate("Math", "Log", "double x", "double base");            RegisterFunctionTemplate("Math", "Max", "double x", "double y");            RegisterFunctionTemplate("Math", "Min", "double x", "double y");            RegisterFunctionTemplateDesc("Math", "Pow", "Returns {base} ^ {exp}.", "double base", "double exp");            RegisterFunctionTemplate("Math", "Random");            RegisterFunctionTemplateDesc("Math", "Round", "Rounds a number to a particular number of decimal places, given by {precision}.", "double x", "double precision");            RegisterFunctionTemplate("Math", "Sign", "double x");            RegisterFunctionTemplate("Math", "Sin", "double x");            RegisterFunctionTemplate("Math", "Sqrt", "double x");            RegisterFunctionTemplate("Math", "Tan", "double x");            RegisterFunctionTemplate("Math", "Truncate", "double x");            RegisterFunctionTemplateDesc("Logical", "If", "If {condition} is true or non-zero, returns {exp1}; otherwise, returns {exp2}.", "bool condition", "exp1", "exp2");            RegisterFunctionTemplateDesc("Logical", "In", "Returns true if {value} is in the list of values.", "value", "...");            RegisterFunctionTemplateDesc("Statistical", "Average", "Takes the average of a list of numbers.", "double ...");            RegisterFunctionTemplateDesc("Statistical", "Mean", "Calculates the mean for a list of numbers.", "double ...");            RegisterFunctionTemplateDesc("Statistical", "Median", "Calculates the median for a list of numbers.", "double ...");            RegisterFunctionTemplateDesc("Statistical", "Mode", "Calculates the mode for a list of numbers.", "double ...");            RegisterFunctionTemplateDesc("Statistical", "Sum", "Calculates the sum of a list of numbers.", "double ...");            RegisterFunctionTemplate("String", "Contains", "string text", "str value");            RegisterFunctionTemplate("String", "EndsWith", "string text", "str value");            RegisterFunctionTemplate("String", "Length", "string value");            RegisterFunctionTemplate("String", "PadLeft", "string value", "int length", "char character");            RegisterFunctionTemplate("String", "PadRight", "string value", "int length", "char character");            RegisterFunctionTemplate("String", "StartsWith", "string text", "string value");            RegisterFunctionTemplate("String", "Substring", "string text", "int startIndex", "int length");            RegisterFunctionTemplate("Date", "AddYears", "date date", "int years");            RegisterFunctionTemplate("Date", "AddMonths", "date date", "int months");            RegisterFunctionTemplate("Date", "AddDays", "date date", "int days");            RegisterFunctionTemplate("Date", "AddHours", "date date", "int hrs");            RegisterFunctionTemplate("Date", "AddMinutes", "date date", "int mins");            RegisterFunctionTemplate("Date", "AddSeconds", "date date", "int secs");            RegisterFunctionTemplate("Date", "AddMilliseconds", "date date", "int ms");            RegisterFunctionTemplate("Date", "YearOf", "date date");            RegisterFunctionTemplate("Date", "MonthOf", "date date");            RegisterFunctionTemplate("Date", "DayOf", "date date");            RegisterFunctionTemplate("Date", "HourOf", "date date");            RegisterFunctionTemplate("Date", "MinuteOf", "date date");            RegisterFunctionTemplate("Date", "SecondOf", "date date");            RegisterFunctionTemplate("Date", "MillisecondOf", "date date");            RegisterFunctionTemplate("Date", "DaysBetween", "date date", "date date");            RegisterFunctionTemplate("Date", "HoursBetween", "date date", "date date");            RegisterFunctionTemplate("Date", "MinutesBetween", "date date", "date date");            RegisterFunctionTemplate("Date", "SecondsBetween", "date date", "date date");            RegisterFunctionTemplate("Date", "MillisecondsBetween", "date date", "date date");            RegisterFunctionTemplate("Conversion", "Date", "value");            RegisterFunctionTemplate("Conversion", "Date", "value", "string format");            RegisterFunctionTemplate("Conversion", "Decimal", "value");            RegisterFunctionTemplate("Conversion", "Double", "value");            RegisterFunctionTemplate("Conversion", "Integer", "value");            RegisterFunctionTemplate("Conversion", "Long", "value");            RegisterFunctionTemplate("Conversion", "String", "value");            foreach(var function in CoreExpression.Functions)            {                RegisterFunctionTemplate(                    function.Group,                    function.Name,                    function.Description,                    function.Parameters.Select(x => new FunctionParameter(x.Name, x.Type)).ToList<IFunctionParameter>());            }        }        public ExpressionEditorWindow(List<string> variables)        {            Variables = variables;            InitializeComponent();        }        private void ExpressionWindow_Loaded(object sender, RoutedEventArgs e)        {            Editor.Focus();        }        private void InsertText(string text)        {            var newCaret = Editor.CaretIndex + text.Length;            Editor.Text = Editor.Text.Insert(Editor.CaretIndex, text);            Editor.CaretIndex = newCaret;        }        private void AppendText(string text)        {            var oldCaret = Editor.CaretIndex;            Editor.Text = Editor.Text.Insert(Editor.CaretIndex, text);            Editor.CaretIndex = oldCaret;        }        private void DeleteText(int start, int length)        {            var oldCaret = Editor.CaretIndex;            Editor.Text = Editor.Text.Remove(start, length);            if(oldCaret >= start && oldCaret < start + length)            {                Editor.CaretIndex = start;            }            else            {                Editor.CaretIndex = oldCaret - length;            }        }        private static Regex tagRegex = new("\\{\\S+\\}");        private static Regex backTagRegex = new("\\{\\S+\\}", RegexOptions.RightToLeft);        private bool DeletePlaceholder()        {            if (Editor.CaretIndex >= Editor.Text.Length) return false;            var tagStart = Editor.Text.LastIndexOf('{', Editor.CaretIndex);            if (tagStart == -1) return false;            var tagMatch = tagRegex.Match(Editor.Text, tagStart);            if (!tagMatch.Success) return false;            var startIndex = tagMatch.Index;            if (startIndex != tagStart) return false;            var length = tagMatch.Length;            if (Editor.CaretIndex >= startIndex + length) return false;            DeleteText(startIndex, length);            return true;        }        private void JumpPlaceholderBackwards()        {            if (Editor.CaretIndex == 0) return;            var tagMatch = backTagRegex.Match(Editor.Text, Editor.CaretIndex - 1);            if (!tagMatch.Success) return;            Editor.CaretIndex = tagMatch.Index;        }        private void JumpPlaceholder(bool allowStart = false)        {            if (Editor.CaretIndex >= Editor.Text.Length) return;            var start = allowStart ? Editor.CaretIndex : Editor.CaretIndex + 1;            var tagMatch = tagRegex.Match(Editor.Text, start);            if (!tagMatch.Success) return;            Editor.CaretIndex = tagMatch.Index;        }        private void FunctionTemplate_Click(object sender, MouseButtonEventArgs e)        {            if ((sender as FrameworkElement)!.Tag is not FunctionTemplate template) return;            var placeholder = DeletePlaceholder();            InsertText($"{template.Name}(");            AppendText(string.Join(", ", template.Parameters.Select(x => $"{{{x.Name}}}")) + ")");            if (placeholder)                JumpPlaceholder(true);        }        private void Variable_Click(object sender, MouseButtonEventArgs e)        {            if ((sender as FrameworkElement)!.Tag is not string str) return;            DeletePlaceholder();            InsertText($"[{str}]");        }        private void Editor_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)        {            e.Handled = true;        }        private void Editor_PreviewTextInput(object sender, TextCompositionEventArgs e)        {            DeletePlaceholder();        }        private void Editor_KeyDown(object sender, KeyEventArgs e)        {            if(e.Key == Key.Tab)            {                if((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)                {                    JumpPlaceholderBackwards();                }                else                {                    JumpPlaceholder();                }                e.Handled = true;            }        }        private void OK_Click(object sender, RoutedEventArgs e)        {            DialogResult = true;        }    }}
 |