|
Home Screenshots Writing Plugins Download |
Writing PluginsPlugin TypesThere are 5 basic types of plugins, each serving a different purpose in the message cycle. To write custom plugins, you should simply inherit from the appropriate base class and optionally add some attributes to specify additional metadata. The following table lists the different types of plugins and the base class they should inherit from (all in the JelleDruyts.Emc.Common namespace of the JelleDruyts.Emc.Common.dll assembly). They all inherit from the EmcPlugin base class, which already provides a number of services common to all plugins.
Plugin AttributesThe following table lists the attributes that can optionally be applied to plugin types.
Plugin ServicesThe plugin model is explicitly designed to be as simple as possible, so the following services are provided out-of-the-box, with little to no code required from the individual plugins:
A Simple PluginA very simple plugin is shown below, in this case a trigger plugin that will cause a message cycle when it is activated. using System;
using System.Collections.Generic;
using System.Text;
using JelleDruyts.Emc.Common;
namespace JelleDruyts.Emc.Plugins.Triggers.Startup
{
[EmcPlugin(Name = "Startup", Description = "This plugin will trigger when the runtime is started up.", Author = "Jelle Druyts", Url = "http://jelle.druyts.net")]
[EmcPluginIcon(typeof(StartupTrigger))]
public class StartupTrigger : TriggerPlugin
{
public override void Activate()
{
// Signal a trigger once as the plugin is activated.
OnTriggered(EventArgs.Empty);
}
}
}
Note that:
Each type of plugin has different members that can be overridden, so check the documentation of the specific base class for all the details. Plugin BasicsThe EmcPlugin ClassAll plugin types ultimately inherit from the EmcPlugin base class, which already provides a number of services. The following table lists the most important members of the EmcPlugin class and their purpose.
The EmcMessage ClassMost plugins will also handle E = m c² messages, which are the basis for all communication between plugins. The following table lists the most important members of the EmcMessage class and their purpose.
Note that the class and most of its members are virtual, so it is possible to subclass it in order to propagate more information between plugins. Plugin SettingsSpecifying And Using SettingsMost plugins will typically require settings that need to be configured by the user and persisted by the runtime. This is possible by providing a single type that contains all settings for that plugin, and specifying this type in the EmcPluginSettingsType attribute. A simple plugin that uses settings is shown below, in this case a publisher plugin that will show the number of new messages in a message box. [EmcPlugin(Name = "MessageBox", Description = "This plugin will show a simple message box.")]
[EmcPluginSettingsType(typeof(PluginSettings))]
[EmcPluginIcon(typeof(MessageBoxPublisher))]
public class MessageBoxPublisher : ThreadSafePublisherPlugin
{
public override void PublishCore(EmcMessage[] messages)
{
PluginSettings settings = (PluginSettings)this.Settings;
MessageBox.Show(string.Format("There are {0} new messages.", messages.Length), settings.Title);
}
}
Note that:
Defining SettingsThere is one important rule for settings objects, and that is that they must be serializable for the runtime to be able to persist them. This means they should at least declare the [Serializable] attribute, or implement the System.Runtime.Serialization.ISerializable interface. In its most basic form, the PluginSettings class could look as follows: [Serializable]
public class PluginSettings
{
private string title = "New messages...";
public string Title
{
get { return this.title; }
set { this.title = value; }
}
}
However, to make it easier for the user to configure the settings, settings objects can support all design-time enhancements that are available in the .NET Framework, such as default values, descriptions, categories, type editors, type converters, ... A more user-friendly settings object would look as follows: [Serializable]
public class PluginSettings
{
private string title = "New messages...";
[Description("The title of the message box.")]
[DefaultValue("New messages...")]
[Category("Options")]
public string Title
{
get { return this.title; }
set { this.title = value; }
}
}
With these extra attributes, the Title setting will be placed in the "Options" category, the user will be shown a description of the setting, and it becomes possible to reset the setting to its default value. Commonly Used AttributesThe following table lists a number of commonly used attributes in settings classes with their purpose.
Advanced TopicsEncrypting SettingsCertain settings contain sensitive information and should be encrypted. The host application contains options for the user to specify encryption mechanisms, and all the settings class itself should do is indicate to the host which fields should be encrypted. This can be done by applying the SerializeEncrypted attribute (from the JelleDruyts.Tools.Security namespace) to the field (not the property) storing the sensitive information. To allow the user to securely enter the sensitive data (so that it doesn't get displayed on the screen), the MaskedDialogEditor and MaskedTypeConverter attributes (both from the JelleDruyts.Tools.Design namespace) can be applied. An example for a Password setting can look as follows: [SerializeEncrypted]
private string password = string.Empty;
[Description("The password.")]
[DefaultValue("")]
[Editor(typeof(MaskedDialogEditor), typeof(UITypeEditor))]
[TypeConverter(typeof(MaskedTypeConverter))]
[Category("General")]
public string Password
{
get { return this.password; }
set { this.password = value; }
}
Note that:
Text TemplatingSome plugins can benefit from a text templating engine that replaces certain parts of strings with dynamic data, e.g. $(Sender) can get replaced by the sender of an E = m c² message. The JelleDruyts.Tools library provides a basic text templating engine with design-time support that can be used easily from E = m c² plugins. If we extend our message box plugin from above to support templates, it becomes possible for the user to define a template format that will be processed to include values from messages. For example, the template "[$(Sender)] $(Subject)" could be expanded into "[Jelle Druyts] Here's a new message for you!". The plugin implementation could then become as follows: public override void PublishCore(EmcMessage[] messages)
{
PluginSettings settings = (PluginSettings)this.Settings;
TemplateProcessor messageProcessor = new TemplateProcessor(settings.Format);
StringBuilder summary = new StringBuilder();
foreach (EmcMessage message in messages)
{
IDictionary<string, object> messageTokenValues = EmcMessageTemplateHelper.GetTokenValues(messages.Length, message);
string processedText = messageProcessor.Process(messageTokenValues);
summary.AppendLine(processedText);
}
MessageBox.Show(summary.ToString(), settings.Title);
}
Note that:
The new Format property of the PluginSettings class would then look as follows: private string format = "[$(Sender)] $(Subject)";
[Description("The format of each message that is displayed.")]
[DefaultValue("[$(Sender)] $(Subject)")]
[Editor(typeof(TemplateEditor), typeof(UITypeEditor))]
[TemplateInfoProvider(typeof(EmcMessageTemplateHelper))]
[Category("Options")]
public string Format
{
get { return this.format; }
set { this.format = value; }
}
Note that:
Plugin CommandsPlugins can provide commands to the runtime, which are pieces of functionality that are exposed by the plugin. They are made available to the user by the host, e.g. in the Windows application through the menu in the system's notification area. To provide commands to the runtime, override the GetCommands method to return a number of PluginCommand instances. The following table lists the most important members of the PluginCommand class and their purpose.
A simple example of a plugin that supports a "Say Hello" command (that is always enabled and shows a message box when executed) looks as follows: public override PluginCommand[] GetCommands()
{
PluginCommand command = new PluginCommand("Say Hello", new ExecuteCommandCallback(SayHello), new QueryCommandStateCallback(CanSayHello));
return new PluginCommand[] { command };
}
private void SayHello()
{
MessageBox.Show("Hello");
}
private CommandState CanSayHello()
{
return CommandState.Enabled;
}
|