Writing your plugin |
As a start here is a minimal working plugin, whose function is to add a custom template to a newly created DialogueFragment when it was created as part of a Document. This plugin has no visible command and only works in the background.
using Articy.Api; using Articy.Api.Plugins; namespace MyCompany.ArticyPlugin { public class Plugin : MacroPlugin { // the required display name public override string DisplayName { get { return "Minimal Plugin"; } } // called when an object was created public override bool ObjectCreated(ObjectProxy aObject) { // If the new object is writable, a DialogueFragment and is a child of a document, // set a custom Template named "DialogueLine" if ( !aObject.IsReadOnly && aObject.ObjectType == ObjectType.DialogueFragment && aObject.IsInDocumentContext() ) aObject.SetTemplate("DialogueLine"); return base.ObjectCreated(aObject); } } }
This simple command plugin adds one (nested) entry to the context menu of any object which changes all objects with a color to be Red.
using System.Collections.Generic; using System.Windows.Media; using Articy.Api; using Articy.Api.Plugins; // To ease working with Loca-Ids use a namespace alias using Texts = LIds.MyCompany.ArticyPlugin; namespace MyCompany.ArticyPlugin { public partial class Plugin : MacroPlugin { public override string DisplayName { // the namespace alias "Text" makes working with those Ids more convenient get { return LocalizeStringNoFormat(Texts.Plugin.DisplayName); } } public override string ContextName { get { return LocalizeStringNoFormat(Texts.Plugin.ContextName); } } // called when an object was created public override List<MacroCommandDescriptor> GetMenuEntries(List<ObjectProxy> aSelectedObjects, ContextMenuContext aContext ) { var result = new List<MacroCommandDescriptor>(); switch ( aContext ) { case ContextMenuContext.Global: // entries for the "global" commands of the ribbon menu are requested return result; default: // normal context menu when working in the content area, navigator, search if ( aSelectedObjects.Count >= 1) { var setColor = new MacroCommandDescriptor { CaptionLid = "{p:SetColor}Change color\\{p:Red}Set red", ModifiesData = true, Execute = SetColor, UserData = Colors.Red }; result.Add(setColor); } return result; } } private void SetColor(MacroCommandDescriptor aDescriptor, List<ObjectProxy> aSelectedObjects) { var color = (Color)aDescriptor.UserData; foreach (var obj in aSelectedObjects) { if ( obj.HasColor && !obj.IsReadOnly ) obj.SetColor(color); } } public override Brush GetIcon(string aIconName) { switch (aIconName) { case "SetColor": return Session.CreateBrushFromFile(Manifest.ManifestPath + "Resources\\SetColor.png"); case "Red": return new SolidColorBrush(Colors.Red); } return null; } } }
Things of interest for this example:
Each time articy:draft opens a context menu on a single object or object multi-selection,
the GetMenuEntries method is called. The aSelectedObjects argument is null if the "global"
commands icon from the ribbon bar was clicked or non null if the selection was made within
the navigator or content area of articy:draft.
The plugin code can examine the given objects and decide if it wants to add an entry for the context menu.
If you want to create a submenu, please specify the full path to the entry and separate the different levels with a backslash character. The order of the different entries is the same as the entries are added to the result list. Same folders are merged.
You can specify a custom icon for each level of the context menu hierarchy.
To use Icons, prefix the text with a
If you use the
It is up to you how to create the Brush object containing the icon. The example uses
a SingleColorBrush object or created it from an external file. The Session also has
overloads to create Brushes from Streams or from a Bitmap object which you will get if
you use the assembly properties to store images.
You make use the namespace alias feature of C# to work conveniently with Loca-Ids. Adding the following using clause:
using Text = LIds.MyCompany.ArticyPlugin;
allows you to write
return LocalizeStringNoFormat(Texts.Plugin.DisplayName);
instead of
return LocalizeStringNoFormat(LIds.MyCompany.ArticyPlugin.Plugin.DisplayName);
using System; using System.Collections.Generic; using System.Threading; using Articy.Api; using Articy.Api.Plugins; using Texts = LIds.MyCompany.ArticyPlugin; namespace MyCompany.ArticyPlugin { /// <summary> /// public implementation part of plugin code, contains all overrides of the plugin class. /// </summary> public partial class Plugin : MacroPlugin { public override string DisplayName { get { return LocalizeStringNoFormat(Texts.Plugin.DisplayName); } } public override string ContextName { get { return LocalizeStringNoFormat(Texts.Plugin.ContextName); } } public override List<MacroCommandDescriptor> GetMenuEntries(List<ObjectProxy> aSelectedObjects) { var result = new List<MacroCommandDescriptor>(); if ( aSelectedObjects != null ) { // normal context menu when working in the content area or navigator if (aSelectedObjects.Count == 1 && (aSelectedObjects[0].IsFolder || aSelectedObjects[0].IsConnectable)) { var entry = new MacroCommandDescriptor { CaptionLid = Texts.LongRunningTask.Caption, TooltipLid = Texts.LongRunningTask.ToolTip, ModifiesData = true, Execute = ExecuteLongRunning }; result.Add(entry); } } return result; } private void ExecuteLongRunning(MacroCommandDescriptor aDescriptor, List<ObjectProxy> aSelectedObjects) { var task = new AsyncTask(aDescriptor, aSelectedObjects) { Title = Texts.Task.Title, Info = Texts.Task.Info, BeforeAsyncAction = null, AsyncAction = BackgroundTask, AfterAsyncAction = null, ShowProgressBar = true, ShowMessages = true, CloseProgressMode = CloseProgressMode.Never, UserData = aSelectedObjects[0] }; Session.ExecuteTaskWithProgressDialog(task); } private bool BackgroundTask(AsyncTask aTask) { try { const string query = "SELECT * FROM SELF WHERE ObjectType <> Pin"; var resultSet = Session.RunQuery(query, aTask.UserData as ObjectProxy); aTask.SetMaxProgress(resultSet.Rows.Count); aTask.SetProgress(0); for (var i = 0; i < resultSet.Rows.Count; i++) { var obj = resultSet.Rows[i]; aTask.AddMessage( String.Format("{0}: {1}", obj.TypeName, obj.GetDisplayName()), i % 2 == 0 ? EntryType.Info : EntryType.Warning); aTask.SetProgress(i + 1); Thread.Sleep(10); } } catch (Exception ex) { // possibly do some cleanup or logging throw; } return true; } } }
Things of interest for this example:
The ExecuteLongRunning method is called from the context menu command and creates a descriptor that is used to start the execution of a background job. You should check your pre-conditions here before starting the task. For example if all objects that need to be changed are in claimed partitions.
The BackgroundTask method uses the query language to collect objects
recursively from a given starting point object.
Take a look at ArticyDraft - Query Language.pdf for more details.
The task uses SetMaxProgress and SetProgress methods to handle the progress bar.
The AddMessage method is used to add a (pre-localized) string to the log with
alternating types (Info, Warning).