Technotes, HowTo Series

AddIn System Documentation
Version 0.1

by Daniel Grunwald, daniel@danielgrunwald.de

Contents
1 About the AddIn System Documentation
2 AddIn Tree
2.1 AddIn Definition
2.2 The Runtime Section
2.3 Doozers
2.4 Building Items in the AddIn Tree
3. Conditions
4. List of available Doozers and ConditionEvaluators

1 About the AddIn System Documentation
This document describes the AddIn system used by SharpDevelop. If you plan to write AddIns for SharpDevelop, you should read the “AddInBuildingGuide” to learn how you should structure your project.
This document describes both the AddIn system in general (ICSharpCode.Core) and common extensions points for SharpDevelop.
2 AddIn Tree
Compiled AddIns consist of two (or more) files: the AddIn xml definition, the addin library (.dll) and maybe additional files or libraries.
The xml definitions of all AddIns are read when SharpDevelop is started and combined into a single tree-structure: the AddIn tree.
The AddIn tree is a tree that "binds them all". It is structured like a file system. If we want to access SubNode2 we have to specify the location as /Path1/SubPath1/Node1/SubNode2.
We see that Node1 is like a path but we will see the difference between a path and a node later. For now, we will just say that nodes are paths that contain definitions of behavior.
The most common use of the AddIn-tree is to extend menus and tool bars. When SharpDevelop wants to create a menu or tool bar, it uses a special path in the AddIn tree. The path “/SharpDevelop/Workbench/MainMenu” contains the items of the main menu, the path “/SharpDevelop/ViewContent/Browser/Toolbar” contains the tool bar of the browser (Startpage, help browser etc.).

2.1 AddIn Definition
Every node in the AddIn-tree has a Codon. In the ICSharpCode.Core implementation, the AddInTreeNode class has a Codon property which is null for paths and points to a Codon instance for nodes.
Let's look at the XML way of defining a node with codon:
<MenuItem id       = "Build"
          label    = "${res:XML.MainMenu.BuildMenu.BuildSolution}"
          shortcut = "F8"
          icon     = "Icons.16x16.BuildCombine"
          class    = "ICSharpCode.SharpDevelop.Project.Commands.Build"/>
When the AddIn tree is loaded, an instance of the Codon class is created. Its name property is set to “MenuItem”, its ID property to “Build”. The other attributes are put into a “Properties” container (works like a Hashtable).
The codon contains the information about the menu item: label, shortcut, icon and the fully qualified name of the class being run when the item is clicked.

The important thing about the AddIn tree is that it is constructed by combining the AddIn definitions from all AddIns. For example, the StartPage.addin file from the start page AddIn contains this:
<Path name = "/SharpDevelop/Workbench/MainMenu/View">
	<MenuItem id = "ShowStartPage"
	          insertafter  = "ViewItemsSeparator"
	          insertbefore = "FullScreen"
	          label = "${res:XML.MainMenu.ViewMenu.ShowStartPage}" 
	          icon  = "Icons.16x16.BrowserWindow"
	          class = "ICSharpCode.StartPage.ShowStartPageCommand"/>
</Path>
The path "/SharpDevelop/Workbench/MainMenu/View" is defined in both the main AddIn file in SharpDevelop and in the StartPage's AddIn file. When loading the files, ICSharpCode.Core merges the contents of the paths into the AddIn tree. The attributes insertafter and insertbefore are special, they control at what positions the items are inserted by specifying the IDs of the previous and next item.
2.2 The Runtime Section
Each AddIn definition file has one instance of the AddIn class. That class contains the information found in the header and runtime section of the AddIn definition file. Each Codon class created for nodes in that file also hold a reference to that AddIn class.
The header of the AddIn definition files contains the following attributes: Name, Author, Copyright, URL of the addin homepage,  Description, Version.
The values are stored in properties of the AddIn class.
The runtime sections looks like this:
	<Runtime>
		<Import assembly = "CSharpBinding.dll"/>
		<Import assembly = ":ICSharpCode.SharpDevelop"/>
	</Runtime>
The import elements are stored in the RunTimes property of the AddIn class. The AddIn class has also a method “CreateObject”. CreateObject is used when the class of a menu item should be created. Normally, CreateObject is called for menu items when the item is clicked the first time. CreateObject will go through all imported assemblies and look for the specified class name (in the order the imports are placed in the runtime section).
The imported assemblies are loaded when CreateObject needs them the first time. This leads to the result that AddIns are loaded when they are first used. This is a great improvement for SharpDevelop's startup time.
CreateObject looks for the classes only in the assemblies specified. When you want to use a class from SharpDevelop's main assembly (e.g. a common command like Undo etc.), you have to import that assembly, too. It won't be loaded two times when it is referenced by multiple addins. Since addins can live in any subdirectory, it isn't possible to specify a fixed path back to ICSharpCode.SharpDevelop.dll. There is a special way to reference assemblies in the main directory, though: When the assembly attribute starts with a colon (<Import assembly = ":ICSharpCode.SharpDevelop"/>), SharpDevelop will load the assembly using “Assembly.Load” instead of the normal “Assembly.LoadFrom”. You won't have to specify the path back, but there is another important difference: You do not use the .dll file extension.

The runtime section can also contain doozer and condition evaluator definitions, read the doozer and conditions sections for more information on that.

2.3 Doozers
Now to the question: How does a Codon become a System.Windows.Forms.MenuStripCommand?
Here the doozers come into play: Doozers are helper classes that build items (any object is possible) from codons.
Here a simplified version of the MenuItemDoozer:
public class MenuItemDoozer : IDoozer
{
  // More on HandleConditions in the conditions section.
  public bool HandleConditions { get { return true; } }

  public object BuildItem(object caller, Codon codon, ArrayList subItems)
  {
     if (codon.Properties.Contains("type"))
	type = codon.Properties["type"];
     else
	type = "Command";
     switch (type) {
	case "Separator":
	    return new MenuSeparator(codon, caller);
	case "CheckBox":
	    return new MenuCheckBox(codon, caller);
	case "Item":
	    return new MenuCommand(codon, caller);
	case "Command":
	    return new MenuCommand(codon, caller);
	case "Menu":
	    return new Menu(codon, caller, subItems);
	case "Builder":
	    return codon.AddIn.CreateObject(codon.Properties["class"]);
	default:
	    throw new NotSupportedException(type);
      }
  }
}
The classes “MenuCommand”, “MenuCheckBox” are SharpDevelop classes overriding “MenuStripCommand” etc. They take their label, icon and shortcut information when the codon's properties.
When a command is clicked, MenuCommand calls “codon.AddIn.CreateObject()”, casts the result to the interface ICommand and calls the Run method.

How to add custom doozers
The main doozers are all added directly by SharpDevelopMain.cs. But what if you want to create your own doozer inside your AddIn?
To do that, you can simply include your doozer in the <Runtime> section of the XML file:
  <Import assembly = 'MyAddIn.dll'>
    <Doozer name='MyDoozer' class = 'MyAddIn.MyDoozer'/>
  </Import>
As usual, the doozer (and the imported assembly) is loaded when it is first used.
2.4 Building Items in the AddIn Tree
This section talks about how your AddIn can create it's own extension paths so your AddIn can be extended by other AddIns.
Context menus:
The static method MenuService.CreateContextMenu can be used to create a ContextMenuStrip.
listView.ContextMenuStrip = MenuService.CreateContextMenu(this, "/MyAddIn/SomePath/ContextMenu");
Passing “this” as parameters sets the caller/owner on the created items and commands. The Run method of a command can use the Owner property to get a reference to the object passed as parameter to CreateContextMenu. In most of the cases it will cast the owner to some interface or the type of the class which executed the “CreateContextMenu” line. Then it can access the public methods and properties on the owner.
Toolbars:
Toolbars are similar to context menus. Use ToolbarService.CreateToolStrip and add the tool strip being returned to your user control.
toolStrip = ToolbarService.CreateToolStrip(this, "/MyAddIn/SomePath/Toolbar");
toolStrip.GripStyle = ToolStripGripStyle.Hidden;
Controls.Add(toolStrip);
The caller/owner works just like in context menus. Here is the “back” command of the browser toolbar:
public class GoBack : AbstractCommand {
    public override void Run() {
	((HtmlViewPane)Owner).WebBrowser.GoBack();
    }
}
Your own objects:
It is just as simple to use your own objects. First, you need a common interface for the objects which will be stored in the AddIn tree. Let's say your AddIn executes some action and wants to give other AddIns to possibility to react on that action, so we could use this interface:
public interface IActionNameListener
{
        void DoAction(MyDataClass data);
}
If you can find an interface in the .NET Framework or the SharpDevelop base, you should prefer using that instead of creating your own.
If you do create your own interface, AddIns extending your AddIn will need to reference it, which can cause versioning problems when you recompile your AddIn.
You can access the items like this:
ArrayList list = AddInTree.BuildItems("/MyAddIn/ActionName", this, false);
foreach (IActionNameListener obj in list) {
	obj.DoAction(data);
}
You will get all classes defined in the path, in the order specified by their insertbefore/insertafter attributes. The second parameter is the owner/caller, just like for context menus and tool bars.
The owner is given to the doozer, and the doozer could pass it on to the created object.
When you use your own objects, you can create your own doozer, but you don't have to.
ICSharpCode.Core already contains a doozer that can create instances of arbitrary objects by calling their parameterless constructor via reflection.

3. Conditions
TODO: Write this section
4. List of available Doozers and ConditionEvaluators
The lists are automatically generated from the source code and saved as ConditionList.html and DoozerList.html in this directory.