using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.IO;
using Mono.Unix;
using Mono.Unix.Native;
using Tomboy.Sync;
namespace Tomboy
{
public class AddinManager
{
readonly string tomboy_conf_dir;
///
/// Key = TypeExtensionNode.Id
///
Dictionary app_addins;
///
///
Dictionary> note_addins;
///
/// Key = TypeExtensionNode.Id
///
Dictionary> note_addin_infos;
public event System.EventHandler ApplicationAddinListChanged;
public AddinManager (string tomboy_conf_dir)
{
this.tomboy_conf_dir = tomboy_conf_dir;
app_addins = new Dictionary ();
note_addins = new Dictionary> ();
note_addin_infos = new Dictionary> ();
InitializeMonoAddins ();
}
void InitializeMonoAddins ()
{
Logger.Info ("Initializing Mono.Addins");
string addins_dir = Path.Combine (tomboy_conf_dir, "addins");
if (!Directory.Exists (addins_dir))
Directory.CreateDirectory (addins_dir);
// Make sure a Tomboy.addins file exists
string addins_file = Path.Combine (addins_dir, "Tomboy.addins");
if (!File.Exists (addins_file)) {
StreamWriter sw = File.CreateText (addins_file);
string addins_file_contents = String.Format (
"\n\n" +
"\n" +
"\t{0}\n" +
"\n",
Defines.SYS_ADDINS_DIR);
sw.Write (addins_file_contents);
sw.Close ();
}
Mono.Addins.AddinManager.AddinLoaded += OnAddinLoaded;
Mono.Addins.AddinManager.AddinUnloaded += OnAddinUnloaded;
Mono.Addins.AddinManager.Initialize (tomboy_conf_dir);
Mono.Addins.AddinManager.Registry.Rebuild (null);
Mono.Addins.AddinManager.AddExtensionNodeHandler ("/Tomboy/ApplicationAddins", OnApplicationAddinExtensionChanged);
// NOTE: A SyncServiceAddin is a specialization of an ApplicationAddin
Mono.Addins.AddinManager.AddExtensionNodeHandler ("/Tomboy/SyncServiceAddins", OnApplicationAddinExtensionChanged);
Mono.Addins.AddinManager.AddExtensionNodeHandler ("/Tomboy/NoteAddins", OnNoteAddinExtensionChanged);
}
void OnAddinLoaded (object sender, Mono.Addins.AddinEventArgs args)
{
Logger.Debug ("AddinManager.OnAddinLoaded: {0}", args.AddinId);
Mono.Addins.Addin addin = Mono.Addins.AddinManager.Registry.GetAddin (args.AddinId);
Logger.Debug ("\t Name: {0}", addin.Name);
Logger.Debug ("\tDescription: {0}", addin.Description.Description);
Logger.Debug ("\t Namespace: {0}", addin.Namespace);
Logger.Debug ("\t Enabled: {0}", addin.Enabled);
Logger.Debug ("\t File: {0}", addin.AddinFile);
}
void OnAddinUnloaded (object sender, Mono.Addins.AddinEventArgs args)
{
Logger.Debug ("AddinManager.OnAddinUnloaded: {0}", args.AddinId);
}
void OnApplicationAddinExtensionChanged (object sender, Mono.Addins.ExtensionNodeEventArgs args)
{
Mono.Addins.TypeExtensionNode type_node =
args.ExtensionNode as Mono.Addins.TypeExtensionNode;
ApplicationAddin addin;
if (args.Change == Mono.Addins.ExtensionChange.Add) {
// Load NoteAddins
if (Tomboy.DefaultNoteManager == null) {
return; // too early -- YUCK! Bad hack
}
addin = type_node.GetInstance (
typeof (ApplicationAddin)) as ApplicationAddin;
if (addin != null) {
if (addin.Initialized == false) {
try {
addin.Initialize ();
app_addins [type_node.Id] = addin;
} catch (Exception e) {
Logger.Debug ("Error initializing app addin {0}: {1}\n{2}",
addin.GetType ().ToString (),
e.Message,
e.StackTrace);
}
}
}
} else {
if (app_addins.ContainsKey (type_node.Id)) {
addin = app_addins [type_node.Id];
try {
addin.Shutdown ();
} catch (Exception e1) {
Logger.Warn ("Error shutting down app addin {0}: {1}\n{2}",
addin.GetType ().ToString (),
e1.Message,
e1.StackTrace);
} finally {
app_addins.Remove (type_node.Id);
}
try {
addin.Dispose ();
} catch (Exception e1) {
Logger.Warn ("Error disposing app addin: {0} - {1}",
addin.GetType ().ToString (), e1.Message);
}
}
}
if (ApplicationAddinListChanged != null)
ApplicationAddinListChanged (sender, args);
}
void OnNoteAddinExtensionChanged (object sender, Mono.Addins.ExtensionNodeEventArgs args)
{
if (args.Change == Mono.Addins.ExtensionChange.Add)
OnNoteAddinEnabled (args);
else
OnNoteAddinDisabled (args);
}
void OnNoteAddinEnabled (Mono.Addins.ExtensionNodeEventArgs args)
{
// Load NoteAddins
if (Tomboy.DefaultNoteManager == null) {
return; // too early -- YUCK! Bad hack
}
foreach (Note note in Tomboy.DefaultNoteManager.Notes) {
// Create a new NoteAddin
Mono.Addins.TypeExtensionNode type_node =
args.ExtensionNode as Mono.Addins.TypeExtensionNode;
try {
NoteAddin n_addin = type_node.CreateInstance () as NoteAddin;
// Keep track of the addins added to each note
AttachAddin (type_node.Id, note, n_addin);
} catch (Exception e) {
Logger.Debug ("Couldn't create a NoteAddin instance: {0}", e.Message);
}
}
}
void OnNoteAddinDisabled (Mono.Addins.ExtensionNodeEventArgs args)
{
Mono.Addins.TypeExtensionNode type_node =
args.ExtensionNode as Mono.Addins.TypeExtensionNode;
OnDisabledAddin (type_node.Id);
}
public void LoadAddinsForNote (Note note)
{
Mono.Addins.ExtensionNodeList list = Mono.Addins.AddinManager.GetExtensionNodes ("/Tomboy/NoteAddins");
foreach (Mono.Addins.ExtensionNode node in list) {
Mono.Addins.TypeExtensionNode type_node =
node as Mono.Addins.TypeExtensionNode;
try {
NoteAddin n_addin = type_node.CreateInstance () as NoteAddin;
// Keep track of the addins added to each note
AttachAddin (type_node.Id, note, n_addin);
} catch (Exception e) {
Logger.Debug ("Couldn't create a NoteAddin instance: {0}", e.Message);
}
}
// Make sure we remove addins when a note is deleted
note.Manager.NoteDeleted += OnNoteDeleted;
}
///
/// Returns an array of ApplicationAddin objects
///
public ApplicationAddin [] GetApplicationAddins ()
{
ApplicationAddin [] app_addins;
try {
app_addins = (ApplicationAddin [])
Mono.Addins.AddinManager.GetExtensionObjects (
"/Tomboy/ApplicationAddins",
typeof (ApplicationAddin),
true);
} catch (Exception e) {
Logger.Debug ("No ApplicationAddins found: {0}", e.Message);
app_addins = new ApplicationAddin [0];
}
return app_addins;
}
///
/// Returns an array of NoteAddin objects that tomboy
/// currently knows about.
///
public NoteAddin [] GetNoteAddins ()
{
NoteAddin [] addins;
try {
addins = (NoteAddin [])
Mono.Addins.AddinManager.GetExtensionObjects (
"/Tomboy/NoteAddins",
typeof (NoteAddin));
} catch (Exception e) {
Logger.Debug ("No NoteAddins found: {0}", e.Message);
addins = new NoteAddin [0];
}
return addins;
}
///
/// Returns an array of SyncServiceAddin objects
///
public SyncServiceAddin [] GetSyncServiceAddins ()
{
SyncServiceAddin [] addins;
try {
addins = (SyncServiceAddin [])
Mono.Addins.AddinManager.GetExtensionObjects (
"/Tomboy/SyncServiceAddins",
typeof (SyncServiceAddin));
} catch (Exception e) {
Logger.Debug ("No SyncServiceAddins found: {0}", e.Message);
addins = new SyncServiceAddin [0];
}
return addins;
}
///
/// Add the addin to the note and save off a reference to the addin that
/// will be used when the note is deleted.
///
public List GetAllAddins ()
{
List addins = new List ();
Mono.Addins.Addin [] addinsArray =
Mono.Addins.AddinManager.Registry.GetAddins ();
if (addinsArray != null) {
// It just so happens that the NoteAddins that are part of
// Tomboy.exe (from Watchers.cs) are not returned by the
// above GetAddins () call, so we don't have to do anything
// to exclude them here.
addins = new List (addinsArray);
}
return addins;
}
///
/// Call NoteAddin.Shutdown () and NoteAddin.Dispose () on every
/// NoteAddin that's attached to the deleted Note.
///
void OnNoteDeleted (object sender, Note deleted)
{
if (note_addins.ContainsKey (deleted) == false)
return;
OnDeletedNote (deleted);
}
void AttachAddin (string ext_node_id, Note note, NoteAddin addin)
{
if (ext_node_id == null || note == null || addin == null)
throw new ArgumentNullException ("Cannot pass in a null parameter to AttachAddin");
// Loading the addin to the note
try {
addin.Initialize (note);
} catch (Exception e) {
Logger.Warn ("Error initializing addin: {0}: {1}",
addin.GetType ().ToString (), e.Message);
// TODO: Would be nice to figure out how to just disable
// the addin altogether if it's failing to initialize so
// it doesn't keep causing problems.
return;
}
NoteAddinInfo info = new NoteAddinInfo (ext_node_id, note, addin);
List note_addin_list;
List ext_node_addin_list;
if (note_addins.ContainsKey (note))
note_addin_list = note_addins [note];
else {
note_addin_list = new List ();
note_addins [note] = note_addin_list;
}
if (note_addin_infos.ContainsKey (ext_node_id))
ext_node_addin_list = note_addin_infos [ext_node_id];
else {
ext_node_addin_list = new List ();
note_addin_infos [ext_node_id] = ext_node_addin_list;
}
note_addin_list.Add (info);
ext_node_addin_list.Add (info);
}
void OnDisabledAddin (string ext_node_id)
{
if (ext_node_id == null)
throw new ArgumentNullException (
"Cannot call OnDisabledAddin with null parameters");
Logger.Debug ("OnDisabledAddin: {0}", ext_node_id);
// Remove and shut down all the addins
if (note_addin_infos.ContainsKey (ext_node_id) == false)
throw new ArgumentException (
"Cannot call OnDisabledAddin with an invalid Mono.Addins.ExtensionNode.Id");
List addin_info_list = note_addin_infos [ext_node_id];
foreach (NoteAddinInfo info in addin_info_list) {
try {
info.Addin.Shutdown ();
} catch (Exception e) {
Logger.Warn ("Error shutting down addin: {0} - {1}",
info.Addin.GetType ().ToString (), e.Message);
}
try {
info.Addin.Dispose ();
} catch (Exception e1) {
Logger.Warn ("Error disposing addin: {0} - {1}",
info.Addin.GetType ().ToString (), e1.Message);
}
// Remove the addin from the Note
if (note_addins.ContainsKey (info.Note)) {
List note_addin_list = note_addins [info.Note];
note_addin_list.Remove (info);
}
}
note_addin_infos.Remove (ext_node_id);
}
void OnDeletedNote (Note note)
{
if (note == null)
throw new ArgumentNullException (
"Cannot call OnDeletedNote with null parameters");
if (note_addins.ContainsKey (note) == false)
throw new ArgumentException (
"Cannot call OnDeletedNote with an invalid Note");
List note_addin_list = note_addins [note];
foreach (NoteAddinInfo info in note_addin_list) {
try {
info.Addin.Shutdown ();
} catch (Exception e) {
Logger.Warn ("Error shutting down addin: {0} - {1}",
info.Addin.GetType ().ToString (), e.Message);
}
try {
info.Addin.Dispose ();
} catch (Exception e1) {
Logger.Warn ("Error disposing addin: {0} - {1}",
info.Addin.GetType ().ToString (), e1.Message);
}
if (note_addin_infos.ContainsKey (info.ExtensionNodeId)) {
List addin_info_list =
note_addin_infos [info.ExtensionNodeId];
addin_info_list.Remove (info);
}
}
note_addins.Remove (note);
}
public bool IsAddinConfigurable (Mono.Addins.Addin addin)
{
object o = GetAddinPrefFactory (addin);
if (o == null)
return false;
return true;
}
public Gtk.Widget CreateAddinPreferenceWidget (Mono.Addins.Addin addin)
{
AddinPreferenceFactory factory = GetAddinPrefFactory (addin);
if (factory == null)
return null;
return factory.CreatePreferenceWidget ();
}
AddinPreferenceFactory GetAddinPrefFactory (Mono.Addins.Addin addin)
{
Mono.Addins.ExtensionNode node =
Mono.Addins.AddinManager.GetExtensionNode ("/Tomboy/AddinPreferences");
if (node != null) {
Mono.Addins.ExtensionNodeList child_nodes = node.ChildNodes;
if (child_nodes != null) {
foreach (Mono.Addins.ExtensionNode child_node in child_nodes) {
if (addin.Id.StartsWith (child_node.Addin.Id)) {
AddinPreferenceFactory factory =
((Mono.Addins.TypeExtensionNode)child_node).GetInstance () as AddinPreferenceFactory;
return factory;
}
}
}
}
return null;
}
}
// TODO: Add this back in so that we can know when Addins leak memory at
// disable/shutdown.
class AddinReference : WeakReference
{
readonly string description;
public AddinReference (object addin, string description) :
base (addin)
{
this.description = description;
}
public string Description
{
get { return description; }
}
}
class NoteAddinInfo
{
readonly string extension_node_id;
readonly Note note;
readonly NoteAddin note_addin;
public NoteAddinInfo (string node_id, Note note, NoteAddin addin)
{
this.extension_node_id = node_id;
this.note = note;
this.note_addin = addin;
}
public string ExtensionNodeId
{
get { return extension_node_id; }
}
public Note Note
{
get { return note; }
}
public NoteAddin Addin
{
get { return note_addin; }
}
}
}