~ted/ubuntu/lucid/tomboy/with-patch

« back to all changes in this revision

Viewing changes to Tomboy/PluginManager.cs

Tags: upstream-0.7.2
Import upstream version 0.7.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
 
2
 
using System;
3
 
using System.Collections.Generic;
4
 
using System.Diagnostics;
5
 
using System.Reflection;
6
 
using System.IO;
7
 
 
8
 
using Mono.Unix;
9
 
using Mono.Unix.Native;
10
 
 
11
 
namespace Tomboy
12
 
{
13
 
        using PluginTable = IDictionary<Type, NotePlugin>;
14
 
 
15
 
        class PluginReference : WeakReference
16
 
        {
17
 
                readonly string description;
18
 
 
19
 
                public PluginReference (object plugin, string description) : 
20
 
                        base (plugin)
21
 
                {
22
 
                        this.description = description;
23
 
                }
24
 
 
25
 
                public string Description
26
 
                {
27
 
                        get { return description; }
28
 
                }
29
 
        }
30
 
 
31
 
        [AttributeUsage(
32
 
                AttributeTargets.Class,
33
 
                AllowMultiple = false, Inherited = false)]
34
 
        public class PluginInfoAttribute : Attribute
35
 
        {
36
 
                string name;
37
 
                string version;
38
 
                string description;
39
 
                string website;
40
 
                string author;
41
 
 
42
 
                Type preferencesWidget;
43
 
 
44
 
                public const string OFFICIAL_AUTHOR = "official";
45
 
                
46
 
                // The default constructor is, for some reason, needed or Tomboy will
47
 
                // crash when attempting to read the plugin attributes.
48
 
                public PluginInfoAttribute ()
49
 
                {
50
 
                        // Intentionally blank
51
 
                }
52
 
 
53
 
                public PluginInfoAttribute (string name, string version,
54
 
                                            string author, string description)
55
 
                {
56
 
                        this.name = Catalog.GetString (name);
57
 
                        this.description = Catalog.GetString (description);
58
 
                        this.version = version;
59
 
                        if (author == OFFICIAL_AUTHOR)
60
 
                                this.author = Catalog.GetString ("Tomboy Project");
61
 
                        else
62
 
                                this.author = author;
63
 
                }
64
 
 
65
 
                public string Name
66
 
                {
67
 
                        get { return name; }
68
 
                        set { name = value; }
69
 
                }
70
 
 
71
 
                public string Version
72
 
                {
73
 
                        get { return version; }
74
 
                        set { version = value; }
75
 
                }
76
 
 
77
 
                public string Description
78
 
                {
79
 
                        get { return description; }
80
 
                        set { description = value; }
81
 
                }
82
 
 
83
 
                public string WebSite
84
 
                {
85
 
                        get { return website; }
86
 
                        set { website = value; }
87
 
                }
88
 
 
89
 
                public string Author
90
 
                {
91
 
                        get { return author; }
92
 
                        set { author = value; }
93
 
                }
94
 
 
95
 
                public Type PreferencesWidget
96
 
                {
97
 
                        get { return preferencesWidget; }
98
 
                        set { preferencesWidget = value; }
99
 
                }
100
 
        }
101
 
 
102
 
        [AttributeUsage(
103
 
                AttributeTargets.Class,
104
 
                AllowMultiple = false, Inherited = true)]
105
 
        public class RequiredPlugins: Attribute
106
 
        {
107
 
                readonly string[] pluginNames;
108
 
 
109
 
                public RequiredPlugins(params string[] pluginNames)
110
 
                {
111
 
                        this.pluginNames = pluginNames;
112
 
                }
113
 
 
114
 
                public string[] PluginNames
115
 
                {
116
 
                        get { return pluginNames; }
117
 
                }
118
 
        }
119
 
 
120
 
        [AttributeUsage(
121
 
                AttributeTargets.Class,
122
 
                AllowMultiple = false, Inherited = true)]
123
 
        public class SuggestedPlugins: Attribute
124
 
        {
125
 
                readonly string[] pluginNames;
126
 
 
127
 
                public SuggestedPlugins(params string[] pluginNames)
128
 
                {
129
 
                        this.pluginNames = pluginNames;
130
 
                }
131
 
 
132
 
                public string[] PluginNames
133
 
                {
134
 
                        get { return pluginNames; }
135
 
                }
136
 
        }
137
 
 
138
 
        public interface IPlugin : IDisposable
139
 
        {
140
 
        }
141
 
 
142
 
        public abstract class AbstractPlugin : IPlugin
143
 
        {
144
 
                bool disposing = false;
145
 
 
146
 
                ~AbstractPlugin ()
147
 
                {
148
 
                        Dispose (false);
149
 
                }
150
 
 
151
 
                public void Dispose ()
152
 
                {
153
 
                        disposing = true;
154
 
                        Dispose (true);
155
 
 
156
 
                        GC.SuppressFinalize (this);
157
 
                }
158
 
 
159
 
                protected virtual void Dispose (bool disposing)
160
 
                {
161
 
                }
162
 
 
163
 
                public bool IsDisposing
164
 
                {
165
 
                        get { return disposing; }
166
 
                }
167
 
        }
168
 
 
169
 
        public abstract class NotePlugin : AbstractPlugin
170
 
        {
171
 
                Note note;
172
 
 
173
 
                List<Gtk.MenuItem> plugin_menu_items;
174
 
                List<Gtk.MenuItem> text_menu_items;
175
 
 
176
 
                public void Initialize (Note note)
177
 
                {
178
 
                        this.note = note;
179
 
                        this.note.Opened += OnNoteOpenedEvent;
180
 
 
181
 
                        Initialize ();
182
 
 
183
 
                        if (note.IsOpened)
184
 
                                OnNoteOpened ();
185
 
                }
186
 
 
187
 
                protected override void Dispose (bool disposing)
188
 
                {
189
 
                        if (disposing) {
190
 
                                if (plugin_menu_items != null) {
191
 
                                        foreach (Gtk.Widget item in plugin_menu_items)
192
 
                                                item.Destroy ();
193
 
                                }
194
 
 
195
 
                                if (text_menu_items != null) {
196
 
                                        foreach (Gtk.Widget item in text_menu_items)
197
 
                                                item.Destroy ();
198
 
                                }
199
 
 
200
 
                                Shutdown ();
201
 
                        }
202
 
 
203
 
                        note.Opened -= OnNoteOpenedEvent;
204
 
                }
205
 
 
206
 
                protected abstract void Initialize ();
207
 
                protected abstract void Shutdown ();
208
 
                protected abstract void OnNoteOpened ();
209
 
 
210
 
                public Note Note
211
 
                {
212
 
                        get { return note; }
213
 
                }
214
 
 
215
 
                public bool HasBuffer
216
 
                {
217
 
                        get { return note.HasBuffer; }
218
 
                }
219
 
 
220
 
                public NoteBuffer Buffer
221
 
                {
222
 
                        get
223
 
                        {
224
 
                                if (IsDisposing && !HasBuffer)
225
 
                                        throw new InvalidOperationException ("Plugin is disposing already");
226
 
 
227
 
                                return note.Buffer; 
228
 
                        }
229
 
                }
230
 
 
231
 
                public bool HasWindow
232
 
                {
233
 
                        get { return note.HasWindow; }
234
 
                }
235
 
 
236
 
                public NoteWindow Window
237
 
                {
238
 
                        get
239
 
                        {
240
 
                                if (IsDisposing && !HasWindow)
241
 
                                        throw new InvalidOperationException ("Plugin is disposing already");
242
 
 
243
 
                                return note.Window; 
244
 
                        }
245
 
                }
246
 
 
247
 
                public NoteManager Manager
248
 
                {
249
 
                        get { return note.Manager; }
250
 
                }
251
 
 
252
 
                void OnNoteOpenedEvent (object sender, EventArgs args)
253
 
                {
254
 
                        OnNoteOpened ();
255
 
 
256
 
                        if (plugin_menu_items != null) {
257
 
                                foreach (Gtk.Widget item in plugin_menu_items) {
258
 
                                        if (item.Parent == null || 
259
 
                                            item.Parent != Window.PluginMenu)
260
 
                                                Window.PluginMenu.Add (item);
261
 
                                }
262
 
                        }
263
 
 
264
 
                        if (text_menu_items != null) {
265
 
                                foreach (Gtk.Widget item in text_menu_items) {
266
 
                                        if (item.Parent == null || 
267
 
                                            item.Parent != Window.TextMenu) {
268
 
                                                Window.TextMenu.Add (item);
269
 
                                                Window.TextMenu.ReorderChild (item, 7);
270
 
                                        }
271
 
                                }
272
 
                        }
273
 
                }
274
 
 
275
 
                public void AddPluginMenuItem (Gtk.MenuItem item)
276
 
                {
277
 
                        if (IsDisposing)
278
 
                                throw new InvalidOperationException ("Plugin is disposing already");
279
 
 
280
 
                        if (plugin_menu_items == null)
281
 
                                plugin_menu_items = new List<Gtk.MenuItem> ();
282
 
 
283
 
                        plugin_menu_items.Add (item);
284
 
 
285
 
                        if (note.IsOpened)
286
 
                                Window.PluginMenu.Add (item);
287
 
                }
288
 
 
289
 
                public void AddTextMenuItem (Gtk.MenuItem item)
290
 
                {
291
 
                        if (IsDisposing)
292
 
                                throw new InvalidOperationException ("Plugin is disposing already");
293
 
 
294
 
                        if (text_menu_items == null)
295
 
                                text_menu_items = new List<Gtk.MenuItem> ();
296
 
 
297
 
                        text_menu_items.Add (item);
298
 
 
299
 
                        if (note.IsOpened) {
300
 
                                Window.TextMenu.Add (item);
301
 
                                Window.TextMenu.ReorderChild (item, 7);
302
 
                        }
303
 
                }
304
 
        }
305
 
 
306
 
        public class PluginManager
307
 
        {
308
 
                readonly string plugins_dir;
309
 
 
310
 
                readonly IList<Type> plugin_types;
311
 
                readonly IDictionary<Note, PluginTable> attached_plugins;
312
 
 
313
 
                readonly FileSystemWatcher dir_watcher;
314
 
                readonly FileSystemWatcher sys_dir_watcher;
315
 
 
316
 
                static bool check_plugin_unloading;
317
 
 
318
 
                // Plugins in the tomboy.exe assembly, always loaded.
319
 
                static Type[] stock_plugins = {
320
 
                        typeof (NoteRenameWatcher),
321
 
#if FIXED_GTKSPELL
322
 
                        typeof (NoteSpellChecker),
323
 
#endif
324
 
                        typeof (NoteUrlWatcher),
325
 
                        typeof (NoteLinkWatcher),
326
 
                        typeof (NoteWikiWatcher),
327
 
                        typeof (MouseHandWatcher),
328
 
                        typeof (NoteTagsWatcher),
329
 
                        
330
 
                        // Not ready yet:
331
 
                        // typeof (NoteRelatedToWatcher),
332
 
                        // typeof (IndentWatcher),
333
 
                };
334
 
 
335
 
                public PluginManager (string plugins_dir)
336
 
                {
337
 
                        this.plugins_dir = plugins_dir;
338
 
 
339
 
                        try {
340
 
                                dir_watcher = new FileSystemWatcher (plugins_dir, "*.dll");
341
 
                                dir_watcher.Created += OnPluginCreated;
342
 
                                dir_watcher.Deleted += OnPluginDeleted;
343
 
                                dir_watcher.EnableRaisingEvents = true;
344
 
                        } catch (ArgumentException e) { 
345
 
                                Logger.Log ("Error creating a FileSystemWatcher on \"{0}\": {1}",
346
 
                                            plugins_dir, e.Message);
347
 
                                dir_watcher = null;
348
 
                        }
349
 
                        
350
 
                        try {
351
 
                                sys_dir_watcher = 
352
 
                                        new FileSystemWatcher (Defines.SYS_PLUGINS_DIR, "*.dll");
353
 
                                sys_dir_watcher.Created += OnPluginCreated;
354
 
                                sys_dir_watcher.Deleted += OnPluginDeleted;
355
 
                                sys_dir_watcher.EnableRaisingEvents = true;
356
 
                        } catch (ArgumentException e) {
357
 
                                Logger.Log ("Error creating a FileSystemWatcher on \"{0}\": {1}", 
358
 
                                            Defines.SYS_PLUGINS_DIR, e.Message);
359
 
                                sys_dir_watcher = null;
360
 
                        }
361
 
 
362
 
                        plugin_types = FindPluginTypes ();
363
 
                        attached_plugins = new Dictionary<Note, PluginTable> ();
364
 
 
365
 
                }
366
 
 
367
 
                public static bool CheckPluginUnloading
368
 
                {
369
 
                        get { return check_plugin_unloading; }
370
 
                        set { check_plugin_unloading = value; }
371
 
                }
372
 
 
373
 
                // Run file manager for ~/.tomboy/Plugins
374
 
                public void ShowPluginsDirectory ()
375
 
                {
376
 
                        string command, args;
377
 
 
378
 
                        // FIXME: There has to be a better way to check this...
379
 
                        if (Environment.GetEnvironmentVariable ("GNOME_DESKTOP_SESSION_ID") == null &&
380
 
                            (Environment.GetEnvironmentVariable ("KDE_FULL_SESSION") != null ||
381
 
                             Environment.GetEnvironmentVariable ("KDEHOME") != null ||
382
 
                             Environment.GetEnvironmentVariable ("KDEDIR") != null)) {
383
 
                                Logger.Log ("Starting Konqueror...");
384
 
 
385
 
                                command = "konqueror";
386
 
                                args = plugins_dir;
387
 
                        } else {
388
 
                                Logger.Log ("Starting Nautilus...");
389
 
 
390
 
                                command = "nautilus";
391
 
                                args = string.Format ("--no-desktop --no-default-window {0}",
392
 
                                                      plugins_dir);
393
 
                        }
394
 
 
395
 
                        try {
396
 
                                Process.Start (command, args);
397
 
                        } catch (Exception e) {
398
 
                                Logger.Log ("Error opening file browser \"{0}\" to \"{1}\": {2}",
399
 
                                            command,
400
 
                                            plugins_dir,
401
 
                                            e.Message);
402
 
                        }
403
 
                }
404
 
 
405
 
                public void LoadPluginsForNote (Note note)
406
 
                {
407
 
                        foreach (Type type in plugin_types as 
408
 
                                        System.Collections.Generic.IEnumerable<Type>) {
409
 
                                if (IsPluginEnabled (type))
410
 
                                        AttachPlugin (type, note);
411
 
                        }
412
 
 
413
 
                        // Make sure we remove plugins when a note is deleted
414
 
                        note.Manager.NoteDeleted += OnNoteDeleted;
415
 
                }
416
 
 
417
 
                static void CleanupOldPlugins (string plugins_dir)
418
 
                {
419
 
                        // NOTE: These might be symlinks to the system-installed
420
 
                        // versions, so use unlink() just in case.
421
 
 
422
 
                        // Remove old version 0.3.[12] "Uninstalled Plugins" symlink
423
 
                        string uninstalled_dir = Path.Combine (plugins_dir, "Uninstalled Plugins");
424
 
                        if (Directory.Exists (uninstalled_dir)) {
425
 
                                Logger.Log ("Removing old \"Uninstalled Plugins\" " +
426
 
                                                   "directory...");
427
 
                                try {
428
 
                                        Syscall.unlink (uninstalled_dir);
429
 
                                } catch (Exception e) {
430
 
                                        Logger.Log ("Error removing: {0}", e);
431
 
                                }
432
 
                        }
433
 
 
434
 
                        // Remove old version 0.3.[12] "ExportToHTML.dll" file
435
 
                        string export_to_html_dll = Path.Combine (plugins_dir, "ExportToHTML.dll");
436
 
                        if (File.Exists (export_to_html_dll)) {
437
 
                                Logger.Log ("Removing old \"ExportToHTML.dll\" plugin...");
438
 
                                try {
439
 
                                        Syscall.unlink (export_to_html_dll);
440
 
                                } catch (Exception e) {
441
 
                                        Logger.Log ("Error removing: {0}", e);
442
 
                                }
443
 
                        }
444
 
 
445
 
                        // Remove old version 0.3.[12] "PrintNotes.dll" file
446
 
                        string print_notes_dll = Path.Combine (plugins_dir, "PrintNotes.dll");
447
 
                        if (File.Exists (print_notes_dll)) {
448
 
                                Logger.Log ("Removing old \"PrintNotes.dll\" plugin...");
449
 
                                try {
450
 
                                        Syscall.unlink (print_notes_dll);
451
 
                                } catch (Exception e) {
452
 
                                        Logger.Log ("Error removing: {0}", e);
453
 
                                }
454
 
                        }
455
 
                }
456
 
 
457
 
                public static void CreatePluginsDir (string plugins_dir)
458
 
                {
459
 
                        // Create Plugins dir
460
 
                        if (!Directory.Exists (plugins_dir))
461
 
                                Directory.CreateDirectory (plugins_dir);
462
 
 
463
 
                        // Clean up old plugin remnants
464
 
                        CleanupOldPlugins (plugins_dir);
465
 
                }
466
 
 
467
 
                void OnNoteDeleted (object sender, Note deleted)
468
 
                {
469
 
                        // Clean out the plugins for this deleted note.
470
 
                        PluginTable note_plugins = null;
471
 
                        
472
 
                        if (attached_plugins.TryGetValue (deleted, out note_plugins)) {
473
 
                                foreach (NotePlugin plugin in note_plugins.Values as
474
 
                                                System.Collections.Generic.IEnumerable<NotePlugin>) {
475
 
                                        try {
476
 
                                                plugin.Dispose ();
477
 
                                        } catch (Exception e) {
478
 
                                                Logger.Fatal (
479
 
                                                        "Cannot dispose {0}: {1}", 
480
 
                                                        plugin.GetType(), e);
481
 
                                        }
482
 
                                }
483
 
 
484
 
                                note_plugins.Clear ();
485
 
                                attached_plugins.Remove (deleted);
486
 
                        }
487
 
                }
488
 
 
489
 
                void AttachPlugin (Type type, Note note)
490
 
                {
491
 
                        PluginTable note_plugins;
492
 
 
493
 
                        if (!attached_plugins.TryGetValue (note, out note_plugins)) {
494
 
                                note_plugins = new Dictionary<Type, NotePlugin> ();
495
 
                                attached_plugins [note] = note_plugins;
496
 
                        }
497
 
 
498
 
                        if (typeof (NotePlugin).IsAssignableFrom (type)) {
499
 
                                NotePlugin plugin;
500
 
 
501
 
                                try {
502
 
                                        plugin = (NotePlugin)Activator.CreateInstance (type);
503
 
                                        plugin.Initialize (note);
504
 
                                } catch (Exception e) {
505
 
                                        Logger.Fatal (
506
 
                                                        "Cannot initialize {0} for note '{1}': {2}", 
507
 
                                                        type, note.Title, e);
508
 
                                        plugin = null;
509
 
                                }
510
 
 
511
 
                                if (null != plugin)
512
 
                                        note_plugins[type] = plugin;
513
 
                        }
514
 
                }
515
 
 
516
 
                void AttachPlugin (Type type)
517
 
                {
518
 
                        if (typeof (NotePlugin).IsAssignableFrom (type)) {
519
 
                                // A plugin may add or remove notes when being
520
 
                                // created. Therefore, it is best to iterate
521
 
                                // through a copy of the notes list to avoid
522
 
                                // "System.InvalidOperationException: out of sync"
523
 
                                List<Note> notes_copy =
524
 
                                        new List<Note> (attached_plugins.Keys);                         
525
 
                                foreach (Note note in notes_copy)
526
 
                                        AttachPlugin (type, note);
527
 
                        }
528
 
                }
529
 
 
530
 
                void DetachPlugin (Type type)
531
 
                {
532
 
                        List<PluginReference> references;
533
 
                        
534
 
                        if (CheckPluginUnloading) {
535
 
                                references = new List<PluginReference> ();
536
 
                        } else {
537
 
                                references = null;
538
 
                        }
539
 
 
540
 
                        DetachPlugin (type, references);
541
 
 
542
 
                        Logger.Debug ("Starting garbage collection...");
543
 
 
544
 
                        GC.Collect();
545
 
                        GC.WaitForPendingFinalizers ();
546
 
                        
547
 
                        Logger.Debug ("Garbage collection complete.");
548
 
 
549
 
                        if (!CheckPluginUnloading) 
550
 
                                return;
551
 
 
552
 
                        Logger.Debug ("Checking plugin references...");
553
 
 
554
 
                        int finalized = 0, leaking = 0;
555
 
 
556
 
                        foreach (PluginReference r in references) {
557
 
                                object plugin = r.Target;
558
 
 
559
 
                                if (null != plugin) {
560
 
                                        Logger.Fatal (
561
 
                                                "Leaking reference on {0}: '{1}'",
562
 
                                                plugin, r.Description);
563
 
 
564
 
                                        ++leaking;
565
 
                                } else {
566
 
                                        ++finalized;
567
 
                                }
568
 
                        }
569
 
 
570
 
                        if (leaking > 0) {
571
 
                                string heapshot =
572
 
                                        "http://svn.myrealbox.com/source/trunk/heap-shot";
573
 
                                string title = String.Format (
574
 
                                        Catalog.GetString ("Cannot fully disable {0}."),
575
 
                                        type);
576
 
                                string message = String.Format (Catalog.GetString (
577
 
                                        "Cannot fully disable {0} as there still are " +
578
 
                                        "at least {1} reference to this plugin. This " +
579
 
                                        "indicates a programming error. Contact the " +
580
 
                                        "plugin's author and report this problem.\n\n" +
581
 
                                        "<b>Developer Information:</b> This problem " +
582
 
                                        "usually occurs when the plugin's Dispose " +
583
 
                                        "method fails to disconnect all event handlers. " +
584
 
                                        "The heap-shot profiler ({2}) can help to " +
585
 
                                        "identify leaking references."),
586
 
                                        type, leaking, heapshot);
587
 
 
588
 
                                HIGMessageDialog dialog = new HIGMessageDialog (
589
 
                                        null, 0, Gtk.MessageType.Error, Gtk.ButtonsType.Ok,
590
 
                                        title, message);
591
 
 
592
 
                                dialog.Run ();
593
 
                                dialog.Destroy ();
594
 
                        }
595
 
 
596
 
                        Logger.Debug ("finalized: {0}, leaking: {1}", finalized, leaking);
597
 
                }
598
 
 
599
 
                // Dispose loop has to happen on separate stack frame when debugging,
600
 
                // as otherwise the local variable "plugin" will cause at least one
601
 
                // plugin instance to not be garbage collected. Just assigning
602
 
                // null to the variable will not help, as the JIT optimizes this
603
 
                // assignment away.
604
 
                void DetachPlugin (Type type, List<PluginReference> references)
605
 
                {
606
 
                        if (typeof (NotePlugin).IsAssignableFrom (type)) {
607
 
                                foreach (KeyValuePair<Note, PluginTable> pair 
608
 
                                                in attached_plugins as
609
 
                                                        System.Collections.Generic.IEnumerable<
610
 
                                                                System.Collections.Generic.KeyValuePair<
611
 
                                                                        Note, PluginTable>>) {
612
 
                                        NotePlugin plugin = null;
613
 
 
614
 
                                        if (pair.Value.TryGetValue (type, out plugin)) {
615
 
                                                pair.Value[type] = null;
616
 
                                                pair.Value.Remove (type);
617
 
 
618
 
                                                try {
619
 
                                                        plugin.Dispose();
620
 
                                                } catch (Exception e) {
621
 
                                                        Logger.Fatal (
622
 
                                                                "Cannot dispose {0} for note '{1}': {2}", 
623
 
                                                                type, pair.Key.Title, e);
624
 
                                                }
625
 
 
626
 
                                                if (null != references)
627
 
                                                        references.Add (new PluginReference (
628
 
                                                                        plugin, pair.Key.Title));
629
 
                                        }
630
 
                                }
631
 
                        }
632
 
                }
633
 
 
634
 
                void OnPluginCreated (object sender, FileSystemEventArgs args)
635
 
                {
636
 
                        Logger.Log ("Plugin '{0}' Created", 
637
 
                                           Path.GetFileName (args.FullPath));
638
 
 
639
 
                        IList<Type> asm_plugins = FindPluginTypesInFile (args.FullPath);
640
 
 
641
 
                        // Add the plugin to the list
642
 
                        // and load the added plugin for all existing plugged in notes
643
 
                        foreach (Type type in asm_plugins as
644
 
                                        System.Collections.Generic.IEnumerable<Type>) {
645
 
                                if (IsPluginEnabled (type)) {
646
 
                                        plugin_types.Add (type);
647
 
                                        AttachPlugin (type);
648
 
                                }
649
 
                        }
650
 
 
651
 
                        asm_plugins.Clear ();
652
 
                }
653
 
 
654
 
                void OnPluginDeleted (object sender, FileSystemEventArgs args)
655
 
                {
656
 
                        Logger.Log ("Plugin '{0}' Deleted", 
657
 
                                           Path.GetFileName (args.FullPath));
658
 
 
659
 
                        List<Type> kill_list = new List<Type> ();
660
 
 
661
 
                        // Find the plugins in the deleted assembly
662
 
                        foreach (Type type in plugin_types as
663
 
                                        System.Collections.Generic.IEnumerable<Type>) {
664
 
                                if (type.Assembly.Location == args.FullPath)
665
 
                                        kill_list.Add (type);
666
 
                        }
667
 
 
668
 
                        foreach (Type type in kill_list) {
669
 
                                plugin_types.Remove (type);
670
 
                                DetachPlugin (type);
671
 
                        }
672
 
 
673
 
                        kill_list.Clear ();
674
 
                }
675
 
 
676
 
                public Type[] Plugins
677
 
                {
678
 
                        get
679
 
                        {
680
 
                                List<Type> plugins = new List<Type> (plugin_types.Count);
681
 
 
682
 
                                foreach (Type type in plugin_types as
683
 
                                                System.Collections.Generic.IEnumerable<Type>) {
684
 
                                        if (!IsBuiltin (type))
685
 
                                                plugins.Add (type);
686
 
                                }
687
 
 
688
 
                                return plugins.ToArray ();
689
 
                        }
690
 
                }
691
 
 
692
 
                static string[] GetActivePlugins ()
693
 
                {
694
 
                        return (string[]) Preferences.Get (Preferences.ENABLED_PLUGINS);
695
 
                }
696
 
 
697
 
                public static bool IsBuiltin (Type plugin)
698
 
                {
699
 
                        return plugin.Assembly == typeof (PluginManager).Assembly;
700
 
                }
701
 
 
702
 
                public bool IsPluginEnabled (Type plugin)
703
 
                {
704
 
                        return IsBuiltin (plugin) || 
705
 
                                        Array.IndexOf (GetActivePlugins (), plugin.Name) >= 0;
706
 
                }
707
 
 
708
 
                public void SetPluginEnabled (Type plugin, bool enabled)
709
 
                {
710
 
                        List<string> active_plugins = new List<string> (GetActivePlugins ());
711
 
                        int index = active_plugins.IndexOf (plugin.Name);
712
 
 
713
 
                        if (enabled != (index >= 0)) {
714
 
                                if (enabled) {
715
 
                                        active_plugins.Add (plugin.Name);
716
 
                                        AttachPlugin (plugin);
717
 
                                } else {
718
 
                                        active_plugins.RemoveAt (index);
719
 
                                        DetachPlugin (plugin);
720
 
                                }
721
 
 
722
 
                                Preferences.Set (Preferences.ENABLED_PLUGINS,
723
 
                                                active_plugins.ToArray ());
724
 
                        }
725
 
                }
726
 
 
727
 
                public static PluginInfoAttribute GetPluginInfo(Type plugin)
728
 
                {
729
 
                        return Attribute.GetCustomAttribute (plugin, 
730
 
                                        typeof (PluginInfoAttribute)) as PluginInfoAttribute;
731
 
                }
732
 
 
733
 
                public static string GetPluginName (Type type, PluginInfoAttribute info)
734
 
                {
735
 
                        string name = null;
736
 
                        
737
 
                        if (null != info)
738
 
                                name = info.Name;
739
 
                        if (null == name)
740
 
                                name = type.Name;
741
 
 
742
 
                        return name;
743
 
                }
744
 
 
745
 
                public static string GetPluginVersion (Type type, PluginInfoAttribute info)
746
 
                {
747
 
                        string version = null;
748
 
                        
749
 
                        if (null != info)
750
 
                                version = info.Version;
751
 
 
752
 
                        if (null == version) {
753
 
                                foreach (Attribute attribute
754
 
                                                in type.Assembly.GetCustomAttributes (true)) {
755
 
                                        AssemblyInformationalVersionAttribute productVersion =
756
 
                                                attribute as AssemblyInformationalVersionAttribute;
757
 
 
758
 
                                        if (null != productVersion) {
759
 
                                                version = productVersion.InformationalVersion;
760
 
                                                break;
761
 
                                        }
762
 
 
763
 
                                        AssemblyFileVersionAttribute fileVersion = 
764
 
                                                attribute as AssemblyFileVersionAttribute;
765
 
 
766
 
                                        if (null != fileVersion) {
767
 
                                                version = fileVersion.Version;
768
 
                                                break;
769
 
                                        }
770
 
                                }
771
 
                        }
772
 
 
773
 
                        if (null == version)
774
 
                                version = type.Assembly.GetName().Version.ToString();
775
 
 
776
 
                        return version;
777
 
                }
778
 
 
779
 
                public static string GetPluginName (Type type)
780
 
                {
781
 
                        return GetPluginName (type, GetPluginInfo (type));
782
 
                }
783
 
 
784
 
                public static Gtk.Widget CreatePreferencesWidget (PluginInfoAttribute info)
785
 
                {
786
 
                        if (null != info && null != info.PreferencesWidget)
787
 
                                return (Gtk.Widget)Activator.CreateInstance (info.PreferencesWidget);
788
 
 
789
 
                        return null;
790
 
                }
791
 
 
792
 
                IList<Type> FindPluginTypes ()
793
 
                {
794
 
                        List<Type> all_plugin_types = new List<Type> ();
795
 
 
796
 
                        all_plugin_types.AddRange (stock_plugins);
797
 
                        all_plugin_types.AddRange (
798
 
                                        FindPluginTypesInDirectory (Defines.SYS_PLUGINS_DIR));
799
 
                        all_plugin_types.AddRange (FindPluginTypesInDirectory (plugins_dir));
800
 
 
801
 
                        return all_plugin_types;
802
 
                }
803
 
 
804
 
                static IList<Type> FindPluginTypesInDirectory (string dirpath)
805
 
                {
806
 
                        IList<Type> dir_plugin_types = new List<Type> ();
807
 
                        string [] files;
808
 
                        
809
 
                        try {
810
 
                                files = Directory.GetFiles (dirpath, "*.dll");
811
 
                        } catch (Exception e) {
812
 
                                Logger.Log ("Error getting plugin types from {0}: {1}", 
813
 
                                            dirpath, 
814
 
                                            e.Message);
815
 
                                return dir_plugin_types;
816
 
                        }
817
 
 
818
 
                        foreach (string file in files) {
819
 
                                Console.Write ("Trying Plugin: {0} ... ", Path.GetFileName (file));
820
 
                                
821
 
                                try {
822
 
                                        IList<Type> asm_plugins = FindPluginTypesInFile (file);
823
 
                                        foreach (Type type in asm_plugins as
824
 
                                                        System.Collections.Generic.IEnumerable<Type>) {
825
 
                                                dir_plugin_types.Add (type);
826
 
                                        }
827
 
                                } catch (Exception e) {
828
 
                                        Logger.Log ("Failed.\n{0}", e);
829
 
                                }
830
 
                        }
831
 
 
832
 
                        return dir_plugin_types;
833
 
                }
834
 
 
835
 
                static IList<Type> FindPluginTypesInFile (string filepath)
836
 
                {
837
 
                        Assembly asm = Assembly.LoadFrom (filepath);
838
 
                        return FindPluginTypesInAssembly (asm);
839
 
                }
840
 
 
841
 
                static IList<Type> FindPluginTypesInAssembly (Assembly asm)
842
 
                {
843
 
                        IList<Type> asm_plugins = new List<Type> ();
844
 
                        Type [] types = asm.GetTypes ();
845
 
                        bool found_one = false;
846
 
 
847
 
                        foreach (Type type in types) {
848
 
                                if (typeof (IPlugin).IsAssignableFrom (type)) {
849
 
                                        Console.Write ("{0}. ", type.FullName);
850
 
                                        asm_plugins.Add (type);
851
 
                                        found_one = true;
852
 
                                }
853
 
                        }
854
 
 
855
 
                        Logger.Log ("{0}", found_one ? "Done." : "Skipping.");
856
 
 
857
 
                        return asm_plugins;
858
 
                }
859
 
        }
860
 
}