This commit is contained in:
Michael Becker 2025-01-17 00:03:20 -05:00
commit e5af2f5dec
20 changed files with 1667 additions and 62 deletions

View File

@ -1,22 +1,228 @@
using System.ComponentModel.Design; using System.ComponentModel;
using MBS.Core.Extensibility;
using System.ComponentModel.Design;
namespace MBS.Core; namespace MBS.Core;
public class Application public class Application
{ {
public IList<Command> Commands { get; } = new List<Command>(); public Command.CommandCollection Commands { get; } = new Command.CommandCollection();
public static Application? Instance { get; private set; } public static Application? Instance { get; private set; }
public CommandLine CommandLine { get; } = new CommandLine(); public CommandLine CommandLine { get; } = new CommandLine();
protected virtual void OnStartup(EventArgs e) 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) protected virtual void OnActivated(ApplicationActivatedEventArgs e)
{ {
Activated?.Invoke(this, e);
}
public event ApplicationActivatedEventHandler AfterActivated;
protected virtual void OnAfterActivated(ApplicationActivatedEventArgs e)
{
AfterActivated?.Invoke(this, e);
}
/// <summary>
/// Parses the option.
/// </summary>
/// <returns><c>true</c>, if option was parsed, <c>false</c> otherwise.</returns>
/// <param name="args">Arguments.</param>
/// <param name="index">Index.</param>
/// <param name="list">The list into which to add the option if it has been specified.</param>
/// <param name="optionSet">The set of available options.</param>
private bool ParseOption(string[] args, ref int index, IList<CommandLineOption> 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() 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> --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); OnStartup(EventArgs.Empty);
CommandLine cline = new CommandLine(); CommandLine cline = new CommandLine();
@ -26,11 +232,24 @@ public class Application
OnActivated(e); OnActivated(e);
return e.ExitCode; return e.ExitCode;
*/
}
public event EventHandler<CancelEventArgs> BeforeStartInternal;
protected virtual void OnBeforeStartInternal(CancelEventArgs e)
{
BeforeStartInternal?.Invoke(this, e);
} }
public int Start() public int Start()
{ {
Instance = this; Instance = this;
CancelEventArgs e = new CancelEventArgs();
OnBeforeStartInternal(e);
if (e.Cancel)
return 2;
int exitCode = StartInternal(); int exitCode = StartInternal();
Instance = null; Instance = null;
@ -46,12 +265,251 @@ public class Application
StopInternal(exitCode); StopInternal(exitCode);
} }
private Dictionary<Union<Guid, string>, List<CommandEventHandler>> _commandEventHandlers = new Dictionary<Union<Guid, string>, List<CommandEventHandler>>(); private string _UniqueName = null;
public void AttachCommandEventHandler(Union<Guid, string> commandId, CommandEventHandler handler) 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<string> list = new List<string>();
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("[<global-options...>]");
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(" <command> [<command-options...>]");
Console.WriteLine();
List<CommandLineCommand> commands = new List<CommandLineCommand>(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(" <value>");
}
else if (option.Type == CommandLineOptionValueType.Multiple)
{
Console.Write(" <value1>[,<value2>,...]");
}
Console.Write(" | {0}{1}", longOptionPrefix, option.Name);
if (option.Type == CommandLineOptionValueType.Single)
{
Console.Write("=<value>");
}
else if (option.Type == CommandLineOptionValueType.Multiple)
{
Console.Write("=<value1>[,<value2>,...]");
}
}
else
{
Console.Write(longOptionPrefix ?? "--");
Console.Write("{0}", option.Name);
if (option.Type == CommandLineOptionValueType.Single)
{
Console.Write("=<value>");
}
else if (option.Type == CommandLineOptionValueType.Multiple)
{
Console.Write("=<value1>[,<value2>,...]");
}
}
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<Union<Guid, string>, List<EventHandler<CommandEventArgs>>> _commandEventHandlers = new Dictionary<Union<Guid, string>, List<EventHandler<CommandEventArgs>>>();
public void AttachCommandEventHandler(Union<Guid, string> commandId, EventHandler<CommandEventArgs> handler)
{ {
if (!_commandEventHandlers.ContainsKey(commandId)) if (!_commandEventHandlers.ContainsKey(commandId))
{ {
_commandEventHandlers[commandId] = new List<CommandEventHandler>(); _commandEventHandlers[commandId] = new List<EventHandler<CommandEventArgs>>();
} }
if (!_commandEventHandlers[commandId].Contains(handler)) if (!_commandEventHandlers[commandId].Contains(handler))
{ {
@ -62,7 +520,7 @@ public class Application
{ {
if (_commandEventHandlers.ContainsKey(commandId)) if (_commandEventHandlers.ContainsKey(commandId))
{ {
foreach (CommandEventHandler handler in _commandEventHandlers[commandId]) foreach (EventHandler<CommandEventArgs> handler in _commandEventHandlers[commandId])
{ {
handler(this, new CommandEventArgs(commandId)); handler(this, new CommandEventArgs(commandId));
} }

View File

@ -0,0 +1,31 @@
//
// CardinalDirection.cs
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
using System;
namespace MBS.Core
{
public enum CardinalDirection
{
Top,
Left,
Bottom,
Right
}
}

View File

@ -27,7 +27,7 @@ namespace MBS.Core.Collections
{ {
public static class ExtensionMethods public static class ExtensionMethods
{ {
public static bool ContainsAny<T>(this IEnumerable enumerable, T[] item) public static bool ContainsAny<T>(this IEnumerable enumerable, IEnumerable<T> item)
{ {
foreach (object item1 in enumerable) foreach (object item1 in enumerable)
{ {

View File

@ -55,13 +55,20 @@ namespace MBS.Core.Collections.Generic
return ((List<T>)(cacheOfT[obj][typeof(T)])).ToArray(); return ((List<T>)(cacheOfT[obj][typeof(T)])).ToArray();
} }
public static void AddRange<T>(this IList<T> list, IEnumerable<T> items) public static void AddRange<T>(this ICollection<T> list, IEnumerable<T> items)
{ {
foreach (T item in items) foreach (T item in items)
{ {
list.Add(item); list.Add(item);
} }
} }
public static void AddRange<TKey, TValue>(this ICollection<KeyValuePair<TKey, TValue>> list, IEnumerable<KeyValuePair<TKey, TValue>> items)
{
foreach (KeyValuePair<TKey, TValue> kvp in items)
{
list.Add(kvp);
}
}
public static T[] ToNullTerminatedArray<T>(this IEnumerable<T> enumerable) where T : class public static T[] ToNullTerminatedArray<T>(this IEnumerable<T> enumerable) where T : class
{ {

View File

@ -1,31 +1,149 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com> // Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
// //
// 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (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 // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with editor-dotnet. If not, see <https://www.gnu.org/licenses/>. // along with MBS Framework for .NET Core. If not, see <https://www.gnu.org/licenses/>.
namespace MBS.Core; namespace MBS.Core;
public class Command public class Command
{ {
public Command(Union<string, Guid> id, string title) public class CommandCollection
: System.Collections.ObjectModel.Collection<Command>
{
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; ID = id;
Title = title; Title = title;
if (items != null)
{
for (int i = 0; i < items.Length; i++)
{
Items.Add(items[i]);
}
}
}
/// <summary>
/// Determines whether this command displays as checked.
/// </summary>
public bool Checked { get; set; } = false;
/// <summary>
/// The ID of the command, used to reference it in <see cref="CommandReferenceCommandItem"/>.
/// </summary>
public string ID { get; set; } = String.Empty;
/// <summary>
/// The title of the command (including mnemonic prefix, if applicable).
/// </summary>
public string Title { get; set; } = String.Empty;
private string mvarDefaultCommandID = String.Empty;
public string DefaultCommandID { get { return mvarDefaultCommandID; } set { mvarDefaultCommandID = value; } }
/// <summary>
/// A <see cref="StockType"/> that represents a predefined, platform-themed command.
/// </summary>
public StockType StockType { get; set; } = StockType.None;
private string mvarImageFileName = String.Empty;
/// <summary>
/// The file name of the image to be displayed on the command.
/// </summary>
public string ImageFileName { get { return mvarImageFileName; } set { mvarImageFileName = value; } }
/// <summary>
/// The child <see cref="CommandItem"/>s that are contained within this <see cref="Command"/>.
/// </summary>
public CommandItem.CommandItemCollection Items { get; } = new CommandItem.CommandItemCollection();
/// <summary>
/// The event that is fired when the command is executed.
/// </summary>
public event EventHandler Executed;
/// <summary>
/// Determines whether this <see cref="Command" /> is enabled in all <see cref="CommandBar" />s and <see cref="MenuBar" />s
/// that reference it.
/// </summary>
/// <value><c>true</c> if visible; otherwise, <c>false</c>.</value>
private bool _Enabled = true;
public bool Enabled { get { return _Enabled; } set { _Enabled = value; Application.Instance._EnableDisableCommand(this, value); } }
/// <summary>
/// Determines whether this <see cref="Command" /> is visible in all <see cref="CommandBar" />s and <see cref="MenuBar" />s
/// that reference it.
/// </summary>
/// <value><c>true</c> if visible; otherwise, <c>false</c>.</value>
public bool Visible { get; set; }
/// <summary>
/// Executes this <see cref="Command"/>.
/// </summary>
[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<string, object> _extraData = new Dictionary<string, object>();
public T GetExtraData<T>(string key, T defaultValue = default(T))
{
if (_extraData.ContainsKey(key))
{
if (_extraData[key] is T)
{
return (T)_extraData[key];
}
}
return defaultValue;
}
public void SetExtraData<T>(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<string, Guid> ID { get; }
public string Title { get; set; }
public IEnumerable<CommandItem> Items { get; set; }
} }

View File

@ -1,31 +0,0 @@
/**
* Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
namespace MBS.Core;
public delegate void CommandEventHandler(object sender, CommandEventArgs e);
public class CommandEventArgs : EventArgs
{
public Union<Guid, string> CommandID { get; }
public CommandEventArgs(Union<Guid, string> commandId)
{
CommandID = commandId;
}
}

View File

@ -0,0 +1,56 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
namespace MBS.Core;
public class CommandEventArgs : EventArgs
{
public Command? Command { get; private set; }
private Dictionary<string, object> _NamedParameters = new Dictionary<string, object>();
public T GetNamedParameter<T>(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<Guid, string> CommandID { get; }
public CommandEventArgs(Union<Guid, string> commandId)
{
CommandID = commandId;
}
public CommandEventArgs(Command command, KeyValuePair<string, object>[] namedParameters = null)
{
Command = command;
if (namedParameters != null)
{
for (int i = 0; i < namedParameters.Length; i++)
{
_NamedParameters[namedParameters[i].Key] = namedParameters[i].Value;
}
}
}
}

View File

@ -1,25 +1,79 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com> // Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
// //
// 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (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 // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with editor-dotnet. If not, see <https://www.gnu.org/licenses/>. // along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace MBS.Core; namespace MBS.Core;
/// <summary>
/// The base class from which all command items derive.
/// </summary>
public abstract class CommandItem public abstract class CommandItem
{ {
public string InsertAfterID { get; set; } = null;
public string InsertBeforeID { get; set; } = null;
public class CommandItemCollection
: System.Collections.ObjectModel.Collection<CommandItem>
{
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();
} }

View File

@ -0,0 +1,115 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
namespace MBS.Core;
public class Context
{
public class ContextCollection
: System.Collections.ObjectModel.Collection<Context>
{
private Dictionary<Guid, Context> _ItemsByID = new Dictionary<Guid, Context>();
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<string, List<EventHandler>> _CommandEventHandlers = new Dictionary<string, List<EventHandler>>();
/// <summary>
/// Attachs an event handler for the command with the ID specified by <paramref name="commandID" />.
/// </summary>
/// <returns><c>true</c>, if command event handler was attached, <c>false</c> otherwise.</returns>
/// <param name="commandID">The ID of the <see cref="Command" /> for which to attach an event handler.</param>
/// <param name="handler">The event handler to invoke when the <see cref="Command" /> is executed.</param>
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<EventHandler>());
}
if (!_CommandEventHandlers[commandID].Contains(handler))
{
_CommandEventHandlers[commandID].Add(handler);
return true;
}
return false;
}
/// <summary>
/// Executes the command (i.e., calls all attached <see cref="EventHandler" />s for the <see cref="Command" />) with
/// the given <paramref name="commandID" />, passing in the given parameters in an instance of <see cref="CommandEventArgs" />.
/// </summary>
/// <returns><c>true</c>, if command was executed, <c>false</c> otherwise.</returns>
/// <param name="commandID">Command identifier.</param>
/// <param name="namedParameters">Named parameters.</param>
public bool ExecuteCommand(string commandID, KeyValuePair<string, object>[] 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;
}
}

View File

@ -0,0 +1,61 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
namespace MBS.Core.Extensibility;
public class Feature
{
public class FeatureCollection
: System.Collections.ObjectModel.Collection<Feature>
{
}
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);
}
}

View File

@ -0,0 +1,181 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
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();
/// <summary>
/// Gets a <see cref="PropertyBag" /> containing the plugin-specific
/// settings for this <see cref="Plugin" />.
/// </summary>
/// <value>The settings.</value>
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<TPlugin>(bool resetCache = false) where TPlugin : Plugin
{
Plugin[] plugins = Get(resetCache);
return plugins.OfType<TPlugin>().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<Plugin> plugins = new System.Collections.Generic.List<Plugin>();
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<TPlugin>(Feature[] providedFeatures, bool resetCache = false) where TPlugin : Plugin
{
Plugin[] plugins = Get(providedFeatures, resetCache);
return plugins.OfType<TPlugin>().ToArray();
}
public static Plugin[] Get(Feature[] providedFeatures, bool resetCache = false)
{
System.Collections.Generic.List<Plugin> list = new System.Collections.Generic.List<Plugin>();
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<TPlugin>(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;
}
}

View File

@ -0,0 +1,49 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
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<T>(PropertyValueRequestedEventArgs<T> e)
{
base.OnPropertyValueRequested(e);
}
}

View File

@ -0,0 +1,187 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
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<T> : 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<Guid, object> _settings = new Dictionary<Guid, object>();
private Dictionary<Guid, string> _names = new Dictionary<Guid, string>();
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<Guid, object> _PropertyValues = new Dictionary<Guid, object>();
public bool Contains(Guid id)
{
return _PropertyValues.ContainsKey(id);
}
public bool SetValue<T>(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<PropertyValueChangingEventArgs> PropertyValueChanging;
protected virtual void OnPropertyValueChanging(PropertyValueChangingEventArgs e)
{
PropertyValueChanging?.Invoke(this, e);
}
public event EventHandler<PropertyValueChangedEventArgs> PropertyValueChanged;
protected virtual void OnPropertyValueChanged(PropertyValueChangedEventArgs e)
{
PropertyValueChanged?.Invoke(this, e);
}
public T GetValue<T>(Guid id, T defaultValue = default(T))
{
lock (_PropertyValues)
{
if (_PropertyValues.ContainsKey(id))
{
if (_PropertyValues[id] is T val)
{
return val;
}
}
else
{
PropertyValueRequestedEventArgs<T> e = new PropertyValueRequestedEventArgs<T>(id, defaultValue);
OnPropertyValueRequested(e);
if (e.Handled)
{
if (e.Cache)
{
_PropertyValues[id] = e.Value;
}
return e.Value;
}
}
}
return defaultValue;
}
public event EventHandler<PropertyValueRequestedEventArgs> PropertyValueRequested;
protected virtual void OnPropertyValueRequested<T>(PropertyValueRequestedEventArgs<T> e)
{
PropertyValueRequested?.Invoke(this, e);
}
public IEnumerable<KeyValuePair<Guid, object>> 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;
}
}

View File

@ -0,0 +1,45 @@
//
// FindFileOptions.cs
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
using System;
namespace MBS.Core;
/// <summary>
/// Controls the behavior of relative file resolution.
/// </summary>
[Flags()]
public enum FindFileOptions
{
/// <summary>
/// Returns all matching fully-qualified file paths across all global,
/// application, and user directories.
/// </summary>
All = 1,
/// <summary>
/// Returns only file paths that are writable by the user (i.e., in the
/// user's local or roaming data directory).
/// </summary>
UserWritable = 2,
/// <summary>
/// Allows the user to create a file if it does not exist (i.e., returns
/// a file name even if it does not exist)
/// </summary>
Create = 4
}

View File

@ -0,0 +1,35 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
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<string>(ref result, result.Length + filenames.Length);
Array.Copy(filenames, 0, result, j, filenames.Length);
j += filenames.Length;
}
return result;
}
}

View File

@ -164,6 +164,11 @@ namespace MBS.Core
public const string DefaultAlphabet = "_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; public const string DefaultAlphabet = "_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
public const string DefaultAlphabetNoSpecialChars = "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(); private static readonly CryptoRandom Random = new CryptoRandom();
/// <summary> /// <summary>
/// ///

View File

@ -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;
}
}

View File

@ -129,12 +129,12 @@ public class TypeLoader
} }
private static Type[] mvarAvailableTypes = null; private static Type[] mvarAvailableTypes = null;
public static T[] GetAvailableTypes<T>(Assembly[] additionalAssemblies = null) where T : class public static T[] GetAvailableTypes<T>(Assembly[] additionalAssemblies = null, bool resetCache = false) where T : class
{ {
Type[] ts = null; Type[] ts = null;
try try
{ {
ts = GetAvailableTypes(new Type[] { typeof(T) }, additionalAssemblies); ts = GetAvailableTypes(new Type[] { typeof(T) }, additionalAssemblies, resetCache);
} }
catch (ReflectionTypeLoadException ex) catch (ReflectionTypeLoadException ex)
{ {
@ -156,8 +156,12 @@ public class TypeLoader
} }
return list.ToArray(); 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) if (mvarAvailableTypes == null)
{ {
List<Type> types = new List<Type>(); List<Type> types = new List<Type>();
@ -197,16 +201,30 @@ public class TypeLoader
} }
} }
mvarAvailableTypes = types.ToArray(); mvarAvailableTypes = types.ToArray();
/*
foreach (Type t in mvarAvailableTypes)
{
Console.WriteLine("Type load: {0}", t.FullName);
}
*/
} }
if (inheritsFrom != null) if (inheritsFrom != null)
{ {
List<Type> retval = new List<Type>(); List<Type> retval = new List<Type>();
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(); return retval.ToArray();

View File

@ -0,0 +1,137 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
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
}

View File

@ -19,6 +19,8 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
using System.Net.Http.Headers;
namespace MBS.Core; namespace MBS.Core;
public static class StreamExtensions public static class StreamExtensions
@ -96,4 +98,33 @@ public static class StreamExtensions
} }
_streamPositions[st].Push(st.Position); _streamPositions[st].Push(st.Position);
} }
}
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<byte>(ref buffer, length);
done = true;
}
Array.Resize<byte>(ref output, output.Length + buffer.Length);
Array.Copy(buffer, 0, output, i, buffer.Length);
i += buffer.Length;
}
return output;
}
}