diff --git a/framework-dotnet/src/lib/MBS.Core/Application.cs b/framework-dotnet/src/lib/MBS.Core/Application.cs index d00de12..77483e2 100644 --- a/framework-dotnet/src/lib/MBS.Core/Application.cs +++ b/framework-dotnet/src/lib/MBS.Core/Application.cs @@ -1,22 +1,228 @@ -using System.ComponentModel.Design; +using System.ComponentModel; +using MBS.Core.Extensibility; + +using System.ComponentModel.Design; namespace MBS.Core; public class Application { - public IList Commands { get; } = new List(); + public Command.CommandCollection Commands { get; } = new Command.CommandCollection(); public static Application? Instance { get; private set; } public CommandLine CommandLine { get; } = new CommandLine(); protected virtual void OnStartup(EventArgs e) { } + public event ApplicationActivatedEventHandler BeforeActivated; + protected virtual void OnBeforeActivated(ApplicationActivatedEventArgs e) + { + BeforeActivated?.Invoke(this, e); + } + + public event ApplicationActivatedEventHandler Activated; protected virtual void OnActivated(ApplicationActivatedEventArgs e) { + Activated?.Invoke(this, e); + } + + + public event ApplicationActivatedEventHandler AfterActivated; + protected virtual void OnAfterActivated(ApplicationActivatedEventArgs e) + { + AfterActivated?.Invoke(this, e); + } + + /// + /// Parses the option. + /// + /// true, if option was parsed, false otherwise. + /// Arguments. + /// Index. + /// The list into which to add the option if it has been specified. + /// The set of available options. + private bool ParseOption(string[] args, ref int index, IList list, CommandLineOption.CommandLineOptionCollection optionSet) + { + string longOptionPrefix = "--", shortOptionPrefix = "-"; + + bool breakout = false; + if (args[index].StartsWith(shortOptionPrefix) && args[index].Length == (shortOptionPrefix.Length + 1)) + { + char shortOptionChar = args[index][args[index].Length - 1]; + CommandLineOption option = optionSet[shortOptionChar]; + if (option != null) + { + if (option.Abbreviation == shortOptionChar) + { + if (option.Type != CommandLineOptionValueType.None) + { + index++; + option.Value = args[index]; + } + else + { + option.Value = true; + } + + if (list != null) + list.Add(option); + } + } + else + { + list.Add(new CommandLineOption() { Abbreviation = shortOptionChar }); + } + } + else if (args[index].StartsWith(longOptionPrefix)) + { + // long option format is --name[=value] + string name = args[index].Substring(longOptionPrefix.Length); + string value = null; + if (name.Contains("=")) + { + int idx = name.IndexOf('='); + value = name.Substring(idx + 1); + name = name.Substring(0, idx); + } + CommandLineOption option = optionSet[name]; + if (option != null) + { + if (option.Type != CommandLineOptionValueType.None) + { + // index++; + // option.Value = args[index]; + if (!name.Contains("=")) + { + // already taken care of the true case above + if (index + 1 < args.Length) + { + index++; + value = args[index]; + } + } + option.Value = value; + } + else + { + option.Value = true; + } + list.Add(option); + } + else + { + list.Add(new CommandLineOption() { Name = name }); + } + } + else + { + // we have reached a non-option + return true; + } + return false; } protected virtual int StartInternal() { + CommandLine cline = new CommandLine(); + string[] args = CommandLine.Arguments; + + if (args.Length > 0) + { + int i = 0; + for (i = 0; i < args.Length; i++) + { + if (ParseOption(args, ref i, cline.Options, CommandLine.Options)) + break; + } + + // we have finished parsing the first set of options ("global" options) + // now we see if we have commands + if (CommandLine.Commands.Count > 0 && i < args.Length) + { + // we support commands like git and apt, "appname --app-global-options --command-options" + CommandLineCommand cmd = CommandLine.Commands[args[i]]; + if (cmd != null) + { + for (i++; i < args.Length; i++) + { + if (ParseOption(args, ref i, cline.Options, cmd.Options)) + break; + } + + cline.Command = cmd; + } + else + { + // assume filename + } + } + + for (/* intentionally left blank */; i < args.Length; i++) + { + cline.FileNames.Add(args[i]); + } + } + + ApplicationActivatedEventArgs e = new ApplicationActivatedEventArgs(true, ApplicationActivationType.CommandLineLaunch, cline); + + if (CommandLine.Options["help"]?.Value is bool && ((bool)CommandLine.Options["help"]?.Value) == true) + { + if (ShowCommandLineHelp(out int resultCode)) + { + return resultCode; + } + } + + OnStartup(EventArgs.Empty); + if (cline.Command != null && cline.Command.ActivationDelegate != null) + { + OnBeforeActivated(e); + if (!e.Success) + { + Console.WriteLine(String.Format("Try '{0} --help' for more information.", ShortName)); + return e.ExitCode; + } + + // use the activation delegate instead of calling OnActivated + cline.Command.ActivationDelegate(e); + if (!e.Success) + { + Console.WriteLine(String.Format("Try '{0} --help' for more information.", ShortName)); + return e.ExitCode; + } + + OnAfterActivated(e); + if (!e.Success) + { + Console.WriteLine(String.Format("Try '{0} --help' for more information.", ShortName)); + return e.ExitCode; + } + } + else + { + OnBeforeActivated(e); + if (!e.Success) + { + Console.WriteLine(String.Format("Try '{0} --help' for more information.", ShortName)); + return e.ExitCode; + } + + OnActivated(e); + if (!e.Success) + { + Console.WriteLine(String.Format("Try '{0} --help' for more information.", ShortName)); + return e.ExitCode; + } + + OnAfterActivated(e); + if (!e.Success) + { + Console.WriteLine(String.Format("Try '{0} --help' for more information.", ShortName)); + return e.ExitCode; + } + } + return e.ExitCode; + /* OnStartup(EventArgs.Empty); CommandLine cline = new CommandLine(); @@ -26,11 +232,24 @@ public class Application OnActivated(e); return e.ExitCode; + */ + } + + public event EventHandler BeforeStartInternal; + protected virtual void OnBeforeStartInternal(CancelEventArgs e) + { + BeforeStartInternal?.Invoke(this, e); } public int Start() { Instance = this; + + CancelEventArgs e = new CancelEventArgs(); + OnBeforeStartInternal(e); + if (e.Cancel) + return 2; + int exitCode = StartInternal(); Instance = null; @@ -46,12 +265,251 @@ public class Application StopInternal(exitCode); } - private Dictionary, List> _commandEventHandlers = new Dictionary, List>(); - public void AttachCommandEventHandler(Union commandId, CommandEventHandler handler) + private string _UniqueName = null; + public string UniqueName + { + get + { + if (_UniqueName == null) + { + return ShortName; + } + return _UniqueName; + } + set + { + _UniqueName = value; + } + } + + private string mvarBasePath = null; + public string BasePath + { + get + { + if (mvarBasePath == null) + { + // Set up the base path for the current application. Should this be able to be + // overridden with a switch (/basepath:...) ? + mvarBasePath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); + } + return mvarBasePath; + } + } + + protected virtual string DataDirectoryName => ShortName; + public string[] EnumerateDataPaths() => EnumerateDataPaths(FindFileOptions.All); + public string[] EnumerateDataPaths(FindFileOptions options) + { + List list = new List(); + + string[] dataDirectoryNames = null; + if (AdditionalShortNames != null) + { + dataDirectoryNames = new string[AdditionalShortNames.Length + 1]; + dataDirectoryNames[0] = DataDirectoryName; + for (int i = 0; i < AdditionalShortNames.Length; i++) + { + dataDirectoryNames[i + 1] = AdditionalShortNames[i]; + } + } + else + { + dataDirectoryNames = new string[] { DataDirectoryName }; + } + + if ((options & FindFileOptions.All) == FindFileOptions.All) + { + // first look in the application root directory since this will override everything else + list.Add(BasePath); + + foreach (string dataDirName in dataDirectoryNames) + { + if (Environment.OSVersion.Platform == PlatformID.Unix) + { + // if we are on Unix or Mac OS X, look in /etc/... + list.Add(String.Join(System.IO.Path.DirectorySeparatorChar.ToString(), new string[] + { + String.Empty, // *nix root directory + "etc", + dataDirName + })); + } + + // then look in /usr/share/universal-editor or C:\ProgramData\Mike Becker's Software\Universal Editor + list.Add(String.Join(System.IO.Path.DirectorySeparatorChar.ToString(), new string[] + { + System.Environment.GetFolderPath(System.Environment.SpecialFolder.CommonApplicationData), + dataDirName + })); + + // then look in ~/.local/share/universal-editor or C:\Users\USERNAME\AppData\Local\Mike Becker's Software\Universal Editor + list.Add(String.Join(System.IO.Path.DirectorySeparatorChar.ToString(), new string[] + { + System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData), + dataDirName + })); + } + } + + + + //fixme: addd finddfileoption.userconfig, localdata, etc. + // now for the user-writable locations... + + foreach (string dataDirName in dataDirectoryNames) + { + // then look in ~/.universal-editor or C:\Users\USERNAME\AppData\Roaming\Mike Becker's Software\Universal Editor + list.Add(String.Join(System.IO.Path.DirectorySeparatorChar.ToString(), new string[] + { + System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData), + dataDirName + })); + } + return list.ToArray(); + } + + public string ShortName { get; set; } + public virtual string[] AdditionalShortNames => null; + + protected void PrintUsageStatement(CommandLineCommand command = null) + { + string shortOptionPrefix = CommandLine.ShortOptionPrefix ?? "-"; + string longOptionPrefix = CommandLine.LongOptionPrefix ?? "--"; + + Console.Write("usage: {0} ", ShortName); + foreach (CommandLineOption option in CommandLine.Options) + { + PrintUsageStatementOption(option); + } + + if (CommandLine.HelpTextPrefix != null) + { + Console.WriteLine(); + Console.WriteLine(); + Console.WriteLine(CommandLine.HelpTextPrefix); + } + + Console.WriteLine(); + + bool printDescriptions = true; + if (printDescriptions) + { + foreach (CommandLineOption option in CommandLine.Options) + { + Console.WriteLine(" {0}{1}", option.Abbreviation != '\0' ? String.Format("{0}{1}, {2}{3}", shortOptionPrefix, option.Abbreviation, longOptionPrefix, option.Name) : String.Format("{0}{1}", longOptionPrefix, option.Name), option.Description == null ? null : String.Format(" - {0}", option.Description)); + } + } + // Console.Write("[]"); + + if (command != null) + { + Console.WriteLine(" {0}{1}", command.Name, command.Description == null ? null : String.Format(" - {0}", command.Description)); + foreach (CommandLineOption option in command.Options) + { + PrintUsageStatementOption(option); + } + } + else + { + if (CommandLine.Commands.Count > 0) + { + Console.WriteLine(" []"); + Console.WriteLine(); + + List commands = new List(CommandLine.Commands); + commands.Sort((x, y) => + { + return x.Name.CompareTo(y.Name); + }); + foreach (CommandLineCommand command1 in commands) + { + Console.WriteLine(" {0}{1}", command1.Name, command1.Description == null ? null : String.Format(" - {0}", command1.Description)); + } + } + } + + if (CommandLine.HelpTextSuffix != null) + { + Console.WriteLine(); + Console.WriteLine(CommandLine.HelpTextSuffix); + } + } + + private void PrintUsageStatementOption(CommandLineOption option) + { + Console.Write(" "); + if (option.Optional) + { + Console.Write('['); + } + + string shortOptionPrefix = CommandLine.ShortOptionPrefix ?? "-"; + string longOptionPrefix = CommandLine.LongOptionPrefix ?? "--"; + + if (option.Abbreviation != '\0') + { + Console.Write("{0}{1}", shortOptionPrefix, option.Abbreviation); + if (option.Type == CommandLineOptionValueType.Single) + { + Console.Write(" "); + } + else if (option.Type == CommandLineOptionValueType.Multiple) + { + Console.Write(" [,,...]"); + } + + Console.Write(" | {0}{1}", longOptionPrefix, option.Name); + if (option.Type == CommandLineOptionValueType.Single) + { + Console.Write("="); + } + else if (option.Type == CommandLineOptionValueType.Multiple) + { + Console.Write("=[,,...]"); + } + } + else + { + Console.Write(longOptionPrefix ?? "--"); + Console.Write("{0}", option.Name); + if (option.Type == CommandLineOptionValueType.Single) + { + Console.Write("="); + } + else if (option.Type == CommandLineOptionValueType.Multiple) + { + Console.Write("=[,,...]"); + } + } + if (option.Optional) + { + Console.Write(']'); + } + // Console.WriteLine(); + } + protected virtual bool ShowCommandLineHelp(out int resultCode) + { + // bash: cd returns 2 if --help is specified OR invalid option selected, 1 if file not found + PrintUsageStatement(); + resultCode = 2; + return true; + } + + internal void _EnableDisableCommand(Command command, bool enable) + { + } + + protected internal virtual Plugin[] GetAdditionalPlugins() + { + return new Plugin[0]; + } + private Dictionary, List>> _commandEventHandlers = new Dictionary, List>>(); + public void AttachCommandEventHandler(Union commandId, EventHandler handler) { if (!_commandEventHandlers.ContainsKey(commandId)) { - _commandEventHandlers[commandId] = new List(); + _commandEventHandlers[commandId] = new List>(); } if (!_commandEventHandlers[commandId].Contains(handler)) { @@ -62,7 +520,7 @@ public class Application { if (_commandEventHandlers.ContainsKey(commandId)) { - foreach (CommandEventHandler handler in _commandEventHandlers[commandId]) + foreach (EventHandler handler in _commandEventHandlers[commandId]) { handler(this, new CommandEventArgs(commandId)); } diff --git a/framework-dotnet/src/lib/MBS.Core/CardinalDirection.cs b/framework-dotnet/src/lib/MBS.Core/CardinalDirection.cs new file mode 100755 index 0000000..ed24932 --- /dev/null +++ b/framework-dotnet/src/lib/MBS.Core/CardinalDirection.cs @@ -0,0 +1,31 @@ +// +// CardinalDirection.cs +// +// Author: +// Michael Becker +// +// Copyright (c) 2021 Mike Becker's Software +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +using System; +namespace MBS.Core +{ + public enum CardinalDirection + { + Top, + Left, + Bottom, + Right + } +} diff --git a/framework-dotnet/src/lib/MBS.Core/Collections/ExtensionMethods.cs b/framework-dotnet/src/lib/MBS.Core/Collections/ExtensionMethods.cs index fdd29c9..e76176e 100755 --- a/framework-dotnet/src/lib/MBS.Core/Collections/ExtensionMethods.cs +++ b/framework-dotnet/src/lib/MBS.Core/Collections/ExtensionMethods.cs @@ -27,7 +27,7 @@ namespace MBS.Core.Collections { public static class ExtensionMethods { - public static bool ContainsAny(this IEnumerable enumerable, T[] item) + public static bool ContainsAny(this IEnumerable enumerable, IEnumerable item) { foreach (object item1 in enumerable) { diff --git a/framework-dotnet/src/lib/MBS.Core/Collections/Generic/ExtensionMethods.cs b/framework-dotnet/src/lib/MBS.Core/Collections/Generic/ExtensionMethods.cs index db03cbf..fbe1f43 100755 --- a/framework-dotnet/src/lib/MBS.Core/Collections/Generic/ExtensionMethods.cs +++ b/framework-dotnet/src/lib/MBS.Core/Collections/Generic/ExtensionMethods.cs @@ -55,13 +55,20 @@ namespace MBS.Core.Collections.Generic return ((List)(cacheOfT[obj][typeof(T)])).ToArray(); } - public static void AddRange(this IList list, IEnumerable items) + public static void AddRange(this ICollection list, IEnumerable items) { foreach (T item in items) { list.Add(item); } } + public static void AddRange(this ICollection> list, IEnumerable> items) + { + foreach (KeyValuePair kvp in items) + { + list.Add(kvp); + } + } public static T[] ToNullTerminatedArray(this IEnumerable enumerable) where T : class { diff --git a/framework-dotnet/src/lib/MBS.Core/Command.cs b/framework-dotnet/src/lib/MBS.Core/Command.cs index 8cf1c8e..c838887 100644 --- a/framework-dotnet/src/lib/MBS.Core/Command.cs +++ b/framework-dotnet/src/lib/MBS.Core/Command.cs @@ -1,31 +1,149 @@ // Copyright (C) 2024 Michael Becker // -// This file is part of editor-dotnet. +// This file is part of MBS Framework for .NET Core. // -// editor-dotnet is free software: you can redistribute it and/or modify +// MBS Framework for .NET Core is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // -// editor-dotnet is distributed in the hope that it will be useful, +// MBS Framework for .NET Core is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License -// along with editor-dotnet. If not, see . +// along with MBS Framework for .NET Core. If not, see . namespace MBS.Core; public class Command { - public Command(Union id, string title) + public class CommandCollection + : System.Collections.ObjectModel.Collection + { + public Command this[string ID] + { + get + { + foreach (Command command in this) + { + if (command.ID == ID) return command; + } + return null; + } + } + } + + public Command() + { + } + public Command(string id, string title, CommandItem[] items = null) { ID = id; Title = title; + if (items != null) + { + for (int i = 0; i < items.Length; i++) + { + Items.Add(items[i]); + } + } + } + /// + /// Determines whether this command displays as checked. + /// + public bool Checked { get; set; } = false; + /// + /// The ID of the command, used to reference it in . + /// + public string ID { get; set; } = String.Empty; + /// + /// The title of the command (including mnemonic prefix, if applicable). + /// + public string Title { get; set; } = String.Empty; + + private string mvarDefaultCommandID = String.Empty; + public string DefaultCommandID { get { return mvarDefaultCommandID; } set { mvarDefaultCommandID = value; } } + + /// + /// A that represents a predefined, platform-themed command. + /// + public StockType StockType { get; set; } = StockType.None; + + private string mvarImageFileName = String.Empty; + /// + /// The file name of the image to be displayed on the command. + /// + public string ImageFileName { get { return mvarImageFileName; } set { mvarImageFileName = value; } } + + + /// + /// The child s that are contained within this . + /// + public CommandItem.CommandItemCollection Items { get; } = new CommandItem.CommandItemCollection(); + + /// + /// The event that is fired when the command is executed. + /// + public event EventHandler Executed; + + /// + /// Determines whether this is enabled in all s and s + /// that reference it. + /// + /// true if visible; otherwise, false. + private bool _Enabled = true; + public bool Enabled { get { return _Enabled; } set { _Enabled = value; Application.Instance._EnableDisableCommand(this, value); } } + + /// + /// Determines whether this is visible in all s and s + /// that reference it. + /// + /// true if visible; otherwise, false. + public bool Visible { get; set; } + + /// + /// Executes this . + /// + [Obsolete("Please use Application.ExecuteCommand. Command.Execute does not always work and will be removed in a future release.")] + public void Execute() + { + if (Executed != null) Executed(this, EventArgs.Empty); + } + + public override string ToString() + { + return String.Format("{0} [{1}]", ID, Title); + } + + private Dictionary _extraData = new Dictionary(); + public T GetExtraData(string key, T defaultValue = default(T)) + { + if (_extraData.ContainsKey(key)) + { + if (_extraData[key] is T) + { + return (T)_extraData[key]; + } + } + return defaultValue; + } + + public void SetExtraData(string key, T value) + { + _extraData[key] = value; + } + + public object GetExtraData(string key, object defaultValue = null) + { + if (_extraData.ContainsKey(key)) + return _extraData[key]; + return defaultValue; + } + + public void SetExtraData(string key, object value) + { + _extraData[key] = value; } - - public Union ID { get; } - public string Title { get; set; } - public IEnumerable Items { get; set; } } \ No newline at end of file diff --git a/framework-dotnet/src/lib/MBS.Core/CommandEvent.cs b/framework-dotnet/src/lib/MBS.Core/CommandEvent.cs deleted file mode 100644 index dee385c..0000000 --- a/framework-dotnet/src/lib/MBS.Core/CommandEvent.cs +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (C) 2024 Michael Becker - * - * This file is part of editor-dotnet. - * - * editor-dotnet is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * editor-dotnet is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with editor-dotnet. If not, see . - */ - -namespace MBS.Core; - -public delegate void CommandEventHandler(object sender, CommandEventArgs e); -public class CommandEventArgs : EventArgs -{ - public Union CommandID { get; } - - public CommandEventArgs(Union commandId) - { - CommandID = commandId; - } -} \ No newline at end of file diff --git a/framework-dotnet/src/lib/MBS.Core/CommandEventArgs.cs b/framework-dotnet/src/lib/MBS.Core/CommandEventArgs.cs new file mode 100644 index 0000000..8f34213 --- /dev/null +++ b/framework-dotnet/src/lib/MBS.Core/CommandEventArgs.cs @@ -0,0 +1,56 @@ +// Copyright (C) 2024 Michael Becker +// +// This file is part of MBS Framework for .NET Core. +// +// MBS Framework for .NET Core is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// MBS Framework for .NET Core is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with MBS Framework for .NET Core. If not, see . + +namespace MBS.Core; + +public class CommandEventArgs : EventArgs +{ + public Command? Command { get; private set; } + + private Dictionary _NamedParameters = new Dictionary(); + public T GetNamedParameter(string key, T defaultValue = default(T)) + { + if (_NamedParameters.ContainsKey(key)) + return (T)_NamedParameters[key]; + return defaultValue; + } + public object GetNamedParameter(string key, object defaultValue = null) + { + if (_NamedParameters.ContainsKey(key)) + return _NamedParameters[key]; + return defaultValue; + } + + public Union CommandID { get; } + + public CommandEventArgs(Union commandId) + { + CommandID = commandId; + } + + public CommandEventArgs(Command command, KeyValuePair[] namedParameters = null) + { + Command = command; + if (namedParameters != null) + { + for (int i = 0; i < namedParameters.Length; i++) + { + _NamedParameters[namedParameters[i].Key] = namedParameters[i].Value; + } + } + } +} \ No newline at end of file diff --git a/framework-dotnet/src/lib/MBS.Core/CommandItem.cs b/framework-dotnet/src/lib/MBS.Core/CommandItem.cs index 763050a..4a9994b 100644 --- a/framework-dotnet/src/lib/MBS.Core/CommandItem.cs +++ b/framework-dotnet/src/lib/MBS.Core/CommandItem.cs @@ -1,25 +1,79 @@ // Copyright (C) 2024 Michael Becker // -// This file is part of editor-dotnet. +// This file is part of Mocha.NET. // -// editor-dotnet is free software: you can redistribute it and/or modify +// Mocha.NET is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // -// editor-dotnet is distributed in the hope that it will be useful, +// Mocha.NET is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License -// along with editor-dotnet. If not, see . +// along with Mocha.NET. If not, see . namespace MBS.Core; -/// -/// The base class from which all command items derive. -/// public abstract class CommandItem { + public string InsertAfterID { get; set; } = null; + public string InsertBeforeID { get; set; } = null; + + public class CommandItemCollection + : System.Collections.ObjectModel.Collection + { + public int IndexOf(string value) + { + for (int i = 0; i < Count; i++) + { + if (this[i] is CommandReferenceCommandItem crci) + { + if (crci.CommandID.Equals(value)) + return i; + } + } + return -1; + } + } +} +public class ActionCommandItem : CommandItem +{ + public string ID { get; } + public string Title { get; } + + public event EventHandler Executed; + + public void Execute() + { + Executed?.Invoke(this, EventArgs.Empty); + } + + public ActionCommandItem(string id, string title, EventHandler execute = null) + { + ID = id; + Title = title; + if (execute != null) + { + Executed += execute; + } + } +} + +public class CommandPlaceholderCommandItem : CommandItem +{ + private string mvarPlaceholderID = String.Empty; + public string PlaceholderID { get { return mvarPlaceholderID; } set { mvarPlaceholderID = value; } } + + public CommandPlaceholderCommandItem(string placeholderID) + { + mvarPlaceholderID = placeholderID; + } +} + +public class GroupCommandItem : CommandItem +{ + public CommandItemCollection Items { get; } = new CommandItemCollection(); } \ No newline at end of file diff --git a/framework-dotnet/src/lib/MBS.Core/Context.cs b/framework-dotnet/src/lib/MBS.Core/Context.cs new file mode 100644 index 0000000..4394f02 --- /dev/null +++ b/framework-dotnet/src/lib/MBS.Core/Context.cs @@ -0,0 +1,115 @@ +// Copyright (C) 2024 Michael Becker +// +// This file is part of Mocha.NET. +// +// Mocha.NET is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Mocha.NET is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Mocha.NET. If not, see . + +namespace MBS.Core; + +public class Context +{ + public class ContextCollection + : System.Collections.ObjectModel.Collection + { + private Dictionary _ItemsByID = new Dictionary(); + + public bool Contains(Guid contextID) + { + return _ItemsByID.ContainsKey(contextID); + } + + public Context this[Guid id] + { + get + { + if (_ItemsByID.ContainsKey(id)) + return _ItemsByID[id]; + return null; + } + } + + protected override void ClearItems() + { + base.ClearItems(); + _ItemsByID.Clear(); + } + protected override void InsertItem(int index, Context item) + { + base.InsertItem(index, item); + _ItemsByID[item.ID] = item; + } + protected override void RemoveItem(int index) + { + _ItemsByID.Remove(this[index].ID); + base.RemoveItem(index); + } + } + + public Guid ID { get; private set; } = Guid.Empty; + public string Name { get; private set; } = String.Empty; + + // public MenuBar MenuBar { get; } = new MenuBar(); + public Command.CommandCollection Commands { get; } = new Command.CommandCollection(); + + public Context(Guid id, string name) + { + ID = id; + Name = name; + } + + public override string ToString() + { + return String.Format("{0} {1}", Name, ID); + } + + private Dictionary> _CommandEventHandlers = new Dictionary>(); + /// + /// Attachs an event handler for the command with the ID specified by . + /// + /// true, if command event handler was attached, false otherwise. + /// The ID of the for which to attach an event handler. + /// The event handler to invoke when the is executed. + public bool AttachCommandEventHandler(string commandID, EventHandler handler) + { + // handle command event handlers attached without a Command instance + if (!_CommandEventHandlers.ContainsKey(commandID)) + { + _CommandEventHandlers.Add(commandID, new List()); + } + if (!_CommandEventHandlers[commandID].Contains(handler)) + { + _CommandEventHandlers[commandID].Add(handler); + return true; + } + return false; + } + /// + /// Executes the command (i.e., calls all attached s for the ) with + /// the given , passing in the given parameters in an instance of . + /// + /// true, if command was executed, false otherwise. + /// Command identifier. + /// Named parameters. + public bool ExecuteCommand(string commandID, KeyValuePair[] namedParameters = null) + { + if (_CommandEventHandlers.ContainsKey(commandID)) + { + for (int i = 0; i < _CommandEventHandlers[commandID].Count; i++) + { + _CommandEventHandlers[commandID][i](this, new CommandEventArgs(Commands[commandID], namedParameters)); + } + } + return false; + } +} \ No newline at end of file diff --git a/framework-dotnet/src/lib/MBS.Core/Extensibility/Feature.cs b/framework-dotnet/src/lib/MBS.Core/Extensibility/Feature.cs new file mode 100644 index 0000000..50c802b --- /dev/null +++ b/framework-dotnet/src/lib/MBS.Core/Extensibility/Feature.cs @@ -0,0 +1,61 @@ +// Copyright (C) 2024 Michael Becker +// +// This file is part of MBS Framework for .NET Core. +// +// MBS Framework for .NET Core is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// MBS Framework for .NET Core is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with MBS Framework for .NET Core. If not, see . + +namespace MBS.Core.Extensibility; + +public class Feature +{ + public class FeatureCollection + : System.Collections.ObjectModel.Collection + { + } + + public Guid ID { get; private set; } = Guid.Empty; + public string Title { get; private set; } = null; + + public Feature(Guid id, string title) + { + ID = id; + Title = title; + } + + public override bool Equals(object obj) + { + if (!(this is null) && (obj is null)) return false; + if (this is null && !(obj is null)) return false; + + if (obj is Feature) + { + Feature feat = (obj as Feature); + return feat.ID.Equals(ID); + } + return base.Equals(obj); + } + public override int GetHashCode() + { + return base.GetHashCode(); + } + + public static bool operator ==(Feature left, Feature right) + { + return (left.ID == right.ID); + } + public static bool operator !=(Feature left, Feature right) + { + return (left.ID != right.ID); + } +} \ No newline at end of file diff --git a/framework-dotnet/src/lib/MBS.Core/Extensibility/Plugin.cs b/framework-dotnet/src/lib/MBS.Core/Extensibility/Plugin.cs new file mode 100644 index 0000000..1bc062b --- /dev/null +++ b/framework-dotnet/src/lib/MBS.Core/Extensibility/Plugin.cs @@ -0,0 +1,181 @@ +// Copyright (C) 2024 Michael Becker +// +// This file is part of MBS Framework for .NET Core. +// +// MBS Framework for .NET Core is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// MBS Framework for .NET Core is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with MBS Framework for .NET Core. If not, see . + +namespace MBS.Core.Extensibility; + +public class Plugin +{ + public Context Context { get; protected set; } + public virtual string Title { get; set; } = null; + public Feature.FeatureCollection ProvidedFeatures { get; } = new Feature.FeatureCollection(); + + /// + /// Gets a containing the plugin-specific + /// settings for this . + /// + /// The settings. + public PropertyBag Settings { get; } + + public Plugin() + { + Settings = new PluginPropertyBag(this); + } + + public bool Initialized { get; private set; } = false; + public void Initialize() + { + if (Initialized) + return; + + InitializeInternal(); + + Settings.Initialize(); + Initialized = true; + } + + public Guid ID { get; set; } = Guid.Empty; + + protected virtual void InitializeInternal() + { + // this method intentionally left blank + } + + protected virtual bool AutoRegister => true; + + protected virtual bool IsSupportedInternal() + { + return true; + } + public bool IsSupported() + { + return IsSupportedInternal(); + } + private static Plugin[] _plugins = null; + public static TPlugin[] Get(bool resetCache = false) where TPlugin : Plugin + { + Plugin[] plugins = Get(resetCache); + return plugins.OfType().ToArray(); + } + public static Plugin[] Get(bool resetCache = false) + { + if (resetCache) + { + _plugins = null; // should not be cached? // actually, yes it should... + + // 2020-12-12 20:54 by beckermj + // ACTUALLY, it depends on whether the configuration needs to be persisted across calls to Get() + // [it does] and whether the list of plugins needs to be reloaded when CustomPlugins is modified + // [it shouldn't, but it does] . + // + // The safest way we can handle this RIGHT NOW is to prevent any plugin from being loaded until after CustomPlugins + // is initialized by UIApplication. + // + // We call Plugins.Get(new Feature[] { KnownFeatures.UWTPlatform }) to retrieve the available User Interface plugins + // that supply the UWT Platform implementation (e.g. GTK, Windows Forms, etc.) - and this causes CustomPlugins to not + // load properly since it loads AFTER this initial call to Plugins.Get(). + // + // So I add ed a resetCache parameter that when specified TRUE will clear the cache and then return the appropriate + // plugin. This way we can continue caching future calls to Get() without missing out on CustomPlugins. + } + + if (_plugins == null) + { + Type[] types = Reflection.TypeLoader.GetAvailableTypes(new Type[] { typeof(Plugin) }); + System.Collections.Generic.List plugins = new System.Collections.Generic.List(); + for (int i = 0; i < types.Length; i++) + { + if (types[i].IsAbstract) + continue; + + try + { + /* + if (types[i].IsSubclassOf(typeof(ICustomPlugin))) + { + continue; + } + */ + + Plugin plg = (Plugin)types[i].Assembly.CreateInstance(types[i].FullName); + plugins.Add(plg); + } + catch (Exception ex) + { + } + } + + if (Application.Instance != null) + { + Plugin[] plugins2 = Application.Instance.GetAdditionalPlugins(); + for (int i = 0; i < plugins2.Length; i++) + { + plugins.Add(plugins2[i]); + } + } + _plugins = plugins.ToArray(); + + if (resetCache) + { + _plugins = null; + return plugins.ToArray(); + } + } + return _plugins; + } + + public static TPlugin[] Get(Feature[] providedFeatures, bool resetCache = false) where TPlugin : Plugin + { + Plugin[] plugins = Get(providedFeatures, resetCache); + return plugins.OfType().ToArray(); + } + public static Plugin[] Get(Feature[] providedFeatures, bool resetCache = false) + { + System.Collections.Generic.List list = new System.Collections.Generic.List(); + Plugin[] plugins = Get(resetCache); + for (int i = 0; i < plugins.Length; i++) + { + if (!plugins[i].AutoRegister) + continue; + + if (!plugins[i].IsSupported()) + continue; + + for (int j = 0; j < providedFeatures.Length; j++) + { + if (plugins[i].ProvidedFeatures.Contains(providedFeatures[j])) + list.Add(plugins[i]); + } + } + return list.ToArray(); + } + + public static TPlugin Get(Guid id) where TPlugin : Plugin + { + Plugin plugin = Get(id); + return (TPlugin)plugin; + } + public static Plugin Get(Guid id) + { + Plugin[] plugins = Get(); + for (int i = 0; i < plugins.Length; i++) + { + if (plugins[i].ID == id) + return plugins[i]; + } + return null; + } +} \ No newline at end of file diff --git a/framework-dotnet/src/lib/MBS.Core/Extensibility/PluginPropertyBag.cs b/framework-dotnet/src/lib/MBS.Core/Extensibility/PluginPropertyBag.cs new file mode 100644 index 0000000..c4a8b11 --- /dev/null +++ b/framework-dotnet/src/lib/MBS.Core/Extensibility/PluginPropertyBag.cs @@ -0,0 +1,49 @@ +// Copyright (C) 2024 Michael Becker +// +// This file is part of MBS Framework for .NET Core. +// +// MBS Framework for .NET Core is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// MBS Framework for .NET Core is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with MBS Framework for .NET Core. If not, see . + +namespace MBS.Core.Extensibility; + + +public class PluginPropertyBag : PropertyBag +{ + private Plugin Plugin { get; } + + protected override void OnInitialized(EventArgs e) + { + base.OnInitialized(e); + + string[] paths = Application.Instance.EnumerateDataPaths(); + foreach (string datapath in paths) + { + string path = String.Format("{0}/plugins/{1}/config.xml", datapath, this.Plugin.ID.ToString("b")); + if (System.IO.File.Exists(path)) + { + Console.WriteLine("found config in {0}", path); + } + } + } + + internal PluginPropertyBag(Plugin plugin) + { + Plugin = plugin; + } + + protected override void OnPropertyValueRequested(PropertyValueRequestedEventArgs e) + { + base.OnPropertyValueRequested(e); + } +} \ No newline at end of file diff --git a/framework-dotnet/src/lib/MBS.Core/Extensibility/PropertyBag.cs b/framework-dotnet/src/lib/MBS.Core/Extensibility/PropertyBag.cs new file mode 100644 index 0000000..db7ae3e --- /dev/null +++ b/framework-dotnet/src/lib/MBS.Core/Extensibility/PropertyBag.cs @@ -0,0 +1,187 @@ +// Copyright (C) 2024 Michael Becker +// +// This file is part of MBS Framework for .NET Core. +// +// MBS Framework for .NET Core is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// MBS Framework for .NET Core is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with MBS Framework for .NET Core. If not, see . + +namespace MBS.Core.Extensibility; + +public class PropertyValueRequestedEventArgs : EventArgs +{ + public Guid ID { get; } + public object Value { get; set; } + + public bool Cache { get; set; } = true; + public bool Handled { get; set; } = false; + + public PropertyValueRequestedEventArgs(Guid id, object value) + { + ID = id; + Value = value; + } +} +public class PropertyValueRequestedEventArgs : PropertyValueRequestedEventArgs +{ + public new T Value { get { return (T)base.Value; } set { base.Value = value; } } + + public PropertyValueRequestedEventArgs(Guid id, T value) : base(id, value) + { + } +} + +public class PropertyValueChangingEventArgs : System.ComponentModel.CancelEventArgs +{ + public Guid ID { get; } + public object OldValue { get; } + public object NewValue { get; set; } + + public PropertyValueChangingEventArgs(Guid id, object oldValue, object newValue) + { + ID = id; + OldValue = oldValue; + NewValue = newValue; + } +} +public class PropertyValueChangedEventArgs : EventArgs +{ + public Guid ID { get; } + public object OldValue { get; } + public object NewValue { get; } + + public PropertyValueChangedEventArgs(Guid id, object oldValue, object newValue) + { + ID = id; + OldValue = oldValue; + NewValue = newValue; + } +} + +public class PropertyBag +{ + private Dictionary _settings = new Dictionary(); + private Dictionary _names = new Dictionary(); + public string GetName(Guid id) + { + if (_names.ContainsKey(id)) + return _names[id]; + return null; + } + public void SetName(Guid id, string name) + { + _names[id] = name; + } + + private Dictionary _PropertyValues = new Dictionary(); + public bool Contains(Guid id) + { + return _PropertyValues.ContainsKey(id); + } + public bool SetValue(Guid id, T value) + { + bool changed = false; + lock (_PropertyValues) + { + object oldValue = null; + if (!_PropertyValues.ContainsKey(id) || (!( + (_PropertyValues[id] == null && (value as object) == null) || + (_PropertyValues[id] != null && _PropertyValues[id].Equals(value))))) + { + changed = true; + } + if (_PropertyValues.ContainsKey(id)) + { + oldValue = _PropertyValues[id]; + } + + if (changed) + { + PropertyValueChangingEventArgs e = new PropertyValueChangingEventArgs(id, oldValue, value); + OnPropertyValueChanging(e); + if (e.Cancel) + { + return false; + } + } + _PropertyValues[id] = value; + if (changed) + { + OnPropertyValueChanged(new PropertyValueChangedEventArgs(id, oldValue, value)); + } + } + return changed; + } + + public event EventHandler PropertyValueChanging; + protected virtual void OnPropertyValueChanging(PropertyValueChangingEventArgs e) + { + PropertyValueChanging?.Invoke(this, e); + } + public event EventHandler PropertyValueChanged; + protected virtual void OnPropertyValueChanged(PropertyValueChangedEventArgs e) + { + PropertyValueChanged?.Invoke(this, e); + } + + public T GetValue(Guid id, T defaultValue = default(T)) + { + lock (_PropertyValues) + { + if (_PropertyValues.ContainsKey(id)) + { + if (_PropertyValues[id] is T val) + { + return val; + } + } + else + { + PropertyValueRequestedEventArgs e = new PropertyValueRequestedEventArgs(id, defaultValue); + OnPropertyValueRequested(e); + if (e.Handled) + { + if (e.Cache) + { + _PropertyValues[id] = e.Value; + } + return e.Value; + } + } + } + return defaultValue; + } + + public event EventHandler PropertyValueRequested; + protected virtual void OnPropertyValueRequested(PropertyValueRequestedEventArgs e) + { + PropertyValueRequested?.Invoke(this, e); + } + + public IEnumerable> GetAll() + { + return _PropertyValues; + } + + public bool Initialized { get; private set; } = false; + protected virtual void OnInitialized(EventArgs e) + { + } + public void Initialize() + { + if (Initialized) + return; + + OnInitialized(EventArgs.Empty); + Initialized = true; + } +} \ No newline at end of file diff --git a/framework-dotnet/src/lib/MBS.Core/FindFileOptions.cs b/framework-dotnet/src/lib/MBS.Core/FindFileOptions.cs new file mode 100644 index 0000000..473678d --- /dev/null +++ b/framework-dotnet/src/lib/MBS.Core/FindFileOptions.cs @@ -0,0 +1,45 @@ +// +// FindFileOptions.cs +// +// Author: +// Michael Becker +// +// Copyright (c) 2022 Mike Becker's Software +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +using System; +namespace MBS.Core; + +/// +/// Controls the behavior of relative file resolution. +/// +[Flags()] +public enum FindFileOptions +{ + /// + /// Returns all matching fully-qualified file paths across all global, + /// application, and user directories. + /// + All = 1, + /// + /// Returns only file paths that are writable by the user (i.e., in the + /// user's local or roaming data directory). + /// + UserWritable = 2, + /// + /// Allows the user to create a file if it does not exist (i.e., returns + /// a file name even if it does not exist) + /// + Create = 4 +} diff --git a/framework-dotnet/src/lib/MBS.Core/IO/DirectoryExtensions.cs b/framework-dotnet/src/lib/MBS.Core/IO/DirectoryExtensions.cs new file mode 100644 index 0000000..fdc496b --- /dev/null +++ b/framework-dotnet/src/lib/MBS.Core/IO/DirectoryExtensions.cs @@ -0,0 +1,35 @@ +// Copyright (C) 2024 Michael Becker +// +// This file is part of Mocha.NET. +// +// Mocha.NET is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Mocha.NET is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Mocha.NET. If not, see . + +namespace MBS.Core.IO; + +public static class DirectoryExtensions +{ + public static string[] GetFiles(string path, string[] searchPatterns, SearchOption options) + { + string[] result = new string[0]; + int j = 0; + foreach (string ext in searchPatterns) + { + string[] filenames = Directory.GetFiles(path, ext, options); + Array.Resize(ref result, result.Length + filenames.Length); + Array.Copy(filenames, 0, result, j, filenames.Length); + j += filenames.Length; + } + return result; + } +} \ No newline at end of file diff --git a/framework-dotnet/src/lib/MBS.Core/NanoId.cs b/framework-dotnet/src/lib/MBS.Core/NanoId.cs index 6259def..967d4f8 100755 --- a/framework-dotnet/src/lib/MBS.Core/NanoId.cs +++ b/framework-dotnet/src/lib/MBS.Core/NanoId.cs @@ -164,6 +164,11 @@ namespace MBS.Core public const string DefaultAlphabet = "_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; public const string DefaultAlphabetNoSpecialChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + public const string CapitalAlphabetNoSpecialChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + public const string CapitalAlphanumeric = "_-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + public const string CapitalAlphanumericNoSpecialChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + private static readonly CryptoRandom Random = new CryptoRandom(); /// /// diff --git a/framework-dotnet/src/lib/MBS.Core/Reflection/TypeExtensions.cs b/framework-dotnet/src/lib/MBS.Core/Reflection/TypeExtensions.cs new file mode 100644 index 0000000..5275b44 --- /dev/null +++ b/framework-dotnet/src/lib/MBS.Core/Reflection/TypeExtensions.cs @@ -0,0 +1,48 @@ +using System.Reflection; + +namespace MBS.Core.Reflection; + +public static class TypeExtensions +{ + public static bool IsSubclassOfGeneric(this Type toCheck, Type generic) + { + // thanks https://stackoverflow.com/a/457708 + + while (toCheck != null && toCheck != typeof(object)) + { + var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck; + if (generic == cur) + { + return true; + } + + Type[] intfs = toCheck.GetInterfaces(); + foreach (Type intf in intfs) + { + // !!! HACK HACK HACK !!! + bool hack = intf.Namespace.Equals(generic.Namespace) && intf.Name.Equals(generic.Name); + if (hack) + { + return true; + } + } + toCheck = toCheck.BaseType; + } + return false; + } + + public static PropertyInfo? GetProperty(this MethodBase methodBase) + { + Type declType = methodBase.DeclaringType; + foreach (PropertyInfo pi in declType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) + { + if (pi.GetMethod == methodBase + || pi.SetMethod == methodBase) + { + return pi; + } + } + return null; + } + +} \ No newline at end of file diff --git a/framework-dotnet/src/lib/MBS.Core/Reflection/TypeLoader.cs b/framework-dotnet/src/lib/MBS.Core/Reflection/TypeLoader.cs index 29e999e..acdfefa 100644 --- a/framework-dotnet/src/lib/MBS.Core/Reflection/TypeLoader.cs +++ b/framework-dotnet/src/lib/MBS.Core/Reflection/TypeLoader.cs @@ -129,12 +129,12 @@ public class TypeLoader } private static Type[] mvarAvailableTypes = null; - public static T[] GetAvailableTypes(Assembly[] additionalAssemblies = null) where T : class + public static T[] GetAvailableTypes(Assembly[] additionalAssemblies = null, bool resetCache = false) where T : class { Type[] ts = null; try { - ts = GetAvailableTypes(new Type[] { typeof(T) }, additionalAssemblies); + ts = GetAvailableTypes(new Type[] { typeof(T) }, additionalAssemblies, resetCache); } catch (ReflectionTypeLoadException ex) { @@ -156,8 +156,12 @@ public class TypeLoader } return list.ToArray(); } - public static Type[] GetAvailableTypes(Type[] inheritsFrom = null, Assembly[] additionalAssemblies = null) + public static Type[] GetAvailableTypes(Type[] inheritsFrom = null, Assembly[] additionalAssemblies = null, bool resetCache = false) { + if (resetCache) + { + mvarAvailableTypes = null; + } if (mvarAvailableTypes == null) { List types = new List(); @@ -197,16 +201,30 @@ public class TypeLoader } } mvarAvailableTypes = types.ToArray(); + /* + foreach (Type t in mvarAvailableTypes) + { + Console.WriteLine("Type load: {0}", t.FullName); + } + */ } if (inheritsFrom != null) { List retval = new List(); - for (int iTyp = 0; iTyp < mvarAvailableTypes.Length; iTyp++) + foreach (Type t in mvarAvailableTypes) { - for (int jInh = 0; jInh < inheritsFrom.Length; jInh++) + string typeFullName = t.FullName; + foreach (Type inheritsFromType in inheritsFrom) { - if (mvarAvailableTypes[iTyp].IsSubclassOf(inheritsFrom[jInh])) retval.Add(mvarAvailableTypes[iTyp]); + if (t.FullName.Contains("Mini.")) + { + // Console.WriteLine(inheritsFromType.FullName + " ? inherits ? " + t.FullName); + } + if (t.IsSubclassOf(inheritsFromType)) + { + retval.Add(t); + } } } return retval.ToArray(); diff --git a/framework-dotnet/src/lib/MBS.Core/StockType.cs b/framework-dotnet/src/lib/MBS.Core/StockType.cs new file mode 100644 index 0000000..9f36272 --- /dev/null +++ b/framework-dotnet/src/lib/MBS.Core/StockType.cs @@ -0,0 +1,137 @@ +// Copyright (C) 2024 Michael Becker +// +// This file is part of MBS Framework for .NET Core. +// +// MBS Framework for .NET Core is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// MBS Framework for .NET Core is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with MBS Framework for .NET Core. If not, see . + +namespace MBS.Core; + +public enum StockType +{ + None, + About, + Add, + Apply, + + // these four do not have gtk-stock equivalents + ArrowDown, + ArrowLeft, + ArrowRight, + ArrowUp, + + Bold, + Bookmarks, + Cancel, + CapsLockWarning, // not for buttons + CDROM, + Clear, + Close, + ColorPicker, // not for buttons + Connect, + Convert, + Copy, + Cut, + Delete, + DialogAuthentication, // not for buttons + DialogInfo, + DialogWarning, + DialogError, + DialogQuestion, + Directory, // not for buttons + Discard, + Disconnect, + DragAndDrop, // not for buttons + DragAndDropMultiple, // not for buttons + Edit, + Execute, + File, + Find, + FindAndReplace, + Floppy, + Folder, + Fullscreen, + GotoBottom, + GotoFirst, + GotoLast, + GotoTop, + GoBack, + GoDown, + GoForward, + GoUp, + HardDisk, + Help, + Home, + Index, + Indent, + Info, + Italic, + JumpTo, + JustifyCenter, + JustifyFill, + JustifyLeft, + JustifyRight, + LeaveFullscreen, + MissingImage, // not for buttons + MediaForward, + MediaNext, + MediaPause, + MediaPlay, + MediaPrevious, + MediaRecord, + MediaRewind, + MediaStop, + Network, + New, + No, + OK, + Open, + OrientationPortrait, + OrientationLandscape, + OrientationReverseLandscape, + OrientationReversePortrait, + PageSetup, + Paste, + Preferences, + Print, + PrintError, // not for buttons + PrintPaused, // not for buttons + PrintPreview, + PrintReport, // not for buttons + PrintWarning, // not for buttons + Properties, + Quit, + Redo, + Refresh, + Remove, + RevertToSaved, + Save, + SaveAs, + SelectAll, + SelectColor, + SelectFont, + SortAscending, + SortDescending, + SpellCheck, + Stop, + Strikethrough, + Undelete, + Underline, + Undo, + Unindent, + Yes, + Zoom100, + ZoomFit, + ZoomIn, + ZoomOut +} \ No newline at end of file diff --git a/framework-dotnet/src/lib/MBS.Core/StreamExtensions.cs b/framework-dotnet/src/lib/MBS.Core/StreamExtensions.cs index b1a5d14..9b3f51f 100644 --- a/framework-dotnet/src/lib/MBS.Core/StreamExtensions.cs +++ b/framework-dotnet/src/lib/MBS.Core/StreamExtensions.cs @@ -19,6 +19,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +using System.Net.Http.Headers; + namespace MBS.Core; public static class StreamExtensions @@ -96,4 +98,33 @@ public static class StreamExtensions } _streamPositions[st].Push(st.Position); } -} \ No newline at end of file + + private const long BUFFER_SIZE=4096; + public static byte[] ReadToEnd(this Stream st) + { + byte[] buffer = new byte[BUFFER_SIZE]; + byte[] output = new byte[0]; + int i = 0; + bool done = false; + while (!done) + { + int length = st.Read(buffer, 0, buffer.Length); + if (length == 0) + { + done = true; + break; + } + + if (length < BUFFER_SIZE) + { + Array.Resize(ref buffer, length); + done = true; + } + + Array.Resize(ref output, output.Length + buffer.Length); + Array.Copy(buffer, 0, output, i, buffer.Length); + i += buffer.Length; + } + return output; + } +}