~ubuntu-branches/ubuntu/wily/tomboy/wily-proposed

« back to all changes in this revision

Viewing changes to .pc/05_add_start_u1_note.patch/Tomboy/NoteManager.cs

  • Committer: Package Import Robot
  • Author(s): Rodney Dawes
  • Date: 2013-02-04 13:11:52 UTC
  • mfrom: (4.4.14 sid)
  • Revision ID: package-import@ubuntu.com-20130204131152-fv902dic25x5v3rb
Tags: 1.12.0-1ubuntu2
* debian/patches/02_sync_save_button_sensitive.patch:
* debian/patches/03_u1_as_default_sync.patch:
* debian/patches/05_add_start_u1_note.patch:
* debian/patches/06_use_ubuntu_sso.patch:
  - Remove patches to default to Ubuntu One for notes sync. (LP: #1115460)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
 
2
 
using System;
3
 
using System.IO;
4
 
using System.Collections.Generic;
5
 
using System.Threading;
6
 
 
7
 
using Mono.Unix;
8
 
 
9
 
namespace Tomboy
10
 
{
11
 
        public delegate void NotesChangedHandler (object sender, Note changed);
12
 
 
13
 
        public class NoteManager
14
 
        {
15
 
                string notes_dir;
16
 
                string backup_dir;
17
 
                List<Note> notes;
18
 
                AddinManager addin_mgr;
19
 
                TrieController trie_controller;
20
 
 
21
 
                public static string NoteTemplateTitle = Catalog.GetString ("New Note Template");
22
 
 
23
 
                static string start_note_uri = String.Empty;
24
 
 
25
 
                static void OnSettingChanged (object sender, NotifyEventArgs args)
26
 
                {
27
 
                        switch (args.Key) {
28
 
                        case Preferences.START_NOTE_URI:
29
 
                                start_note_uri = args.Value as string;
30
 
                                break;
31
 
                        }
32
 
                }
33
 
 
34
 
                public NoteManager (string directory) :
35
 
                        this (directory, Path.Combine (directory, "Backup"))
36
 
                {
37
 
                }
38
 
 
39
 
                public NoteManager (string directory, string backup_directory)
40
 
                {
41
 
                        Logger.Debug ("NoteManager created with note path \"{0}\".", directory);
42
 
 
43
 
                        notes_dir = directory;
44
 
                        backup_dir = backup_directory;
45
 
                }
46
 
 
47
 
                public bool Initialized {
48
 
                        get; private set;
49
 
                }
50
 
 
51
 
                public TomboyCommandLine CommandLine {
52
 
                        get; set;
53
 
                }
54
 
 
55
 
                // TODO: Decide if/how to enforce
56
 
                public bool ReadOnly {
57
 
                        get; set;
58
 
                }
59
 
 
60
 
                /// <summary>
61
 
                /// Use Gtk.Application.Invoke to invoke the Action delegate
62
 
                /// once this NoteManager is initialized. If this NoteManager
63
 
                /// is already initialized, Gtk.Application.Invoke is *not*
64
 
                /// used (for performance reasons).  In other words, this method
65
 
                /// should only be called from the GTK+ main thread.
66
 
                /// </summary>
67
 
                public void GtkInvoke (Action a)
68
 
                {
69
 
                        if (!Initialized)
70
 
                                new Thread (() => {
71
 
                                        while (!Initialized)
72
 
                                                ;
73
 
                                        Gtk.Application.Invoke ((o, e) => a ());
74
 
                                }).Start ();
75
 
                        else
76
 
                                a ();
77
 
                }
78
 
 
79
 
                public void Invoke (Action a)
80
 
                {
81
 
                        if (!Initialized)
82
 
                                new Thread (() => {
83
 
                                        while (!Initialized)
84
 
                                                ;
85
 
                                        a ();
86
 
                                }).Start ();
87
 
                        else
88
 
                                a ();
89
 
                }
90
 
 
91
 
                public void Initialize ()
92
 
                {
93
 
                        notes = new List<Note> ();
94
 
 
95
 
                        string conf_dir = Services.NativeApplication.ConfigurationDirectory;
96
 
 
97
 
                        string old_notes_dir = null;
98
 
                        bool migration_needed = false;
99
 
 
100
 
                        bool first_run = FirstRun ();
101
 
                        if (first_run) {
102
 
                                old_notes_dir = Services.NativeApplication.PreOneDotZeroNoteDirectory;
103
 
                                migration_needed = DirectoryExists (old_notes_dir);
104
 
                        }
105
 
                        CreateNotesDir ();
106
 
                        if (!Directory.Exists (conf_dir))
107
 
                                Directory.CreateDirectory (conf_dir);
108
 
 
109
 
                        if (migration_needed) {
110
 
                                // Copy notes
111
 
                                foreach (string noteFile in Directory.GetFiles (old_notes_dir, "*.note"))
112
 
                                        File.Copy (noteFile, Path.Combine (notes_dir, Path.GetFileName (noteFile)));
113
 
 
114
 
                                // Copy deleted notes
115
 
                                string old_backup = Path.Combine (old_notes_dir, "Backup");
116
 
                                if (DirectoryExists (old_backup)) {
117
 
                                        Directory.CreateDirectory (backup_dir);
118
 
                                        foreach (string noteFile in Directory.GetFiles (old_backup, "*.note"))
119
 
                                                File.Copy (noteFile, Path.Combine (backup_dir, Path.GetFileName (noteFile)));
120
 
                                }
121
 
 
122
 
                                // Copy configuration data
123
 
                                // NOTE: Add-in configuration data copied by AddinManager
124
 
                                string sync_manifest_name = "manifest.xml";
125
 
                                string old_sync_manifest_path =
126
 
                                        Path.Combine (old_notes_dir, sync_manifest_name);
127
 
                                if (File.Exists (old_sync_manifest_path))
128
 
                                        File.Copy (old_sync_manifest_path,
129
 
                                                   Path.Combine (conf_dir, sync_manifest_name));
130
 
 
131
 
 
132
 
                                // NOTE: Not copying cached data
133
 
 
134
 
                                first_run = false;
135
 
                        }
136
 
 
137
 
                        trie_controller = CreateTrieController ();
138
 
                        addin_mgr = new AddinManager (conf_dir,
139
 
                                                      migration_needed ? old_notes_dir : null);
140
 
 
141
 
                        if (first_run) {
142
 
                                // First run. Create "Start Here" notes.
143
 
                                CreateStartNotes ();
144
 
                        } else {
145
 
                                LoadNotes ();
146
 
                        }
147
 
 
148
 
                        if (migration_needed) {
149
 
                                // Create migration notification note
150
 
                                // Translators: The title of the data migration note
151
 
                                string base_migration_note_title = Catalog.GetString ("Your Notes Have Moved!");
152
 
                                string migration_note_title = base_migration_note_title;
153
 
 
154
 
                                // NOTE: Uncomment to generate a title suitable for
155
 
                                //       putting in the note collection
156
 
//                              int count = 1;
157
 
//                              while (Find (migration_note_title) != null) {
158
 
//                                      migration_note_title = base_migration_note_title +
159
 
//                                              string.Format (" ({0})", ++count);
160
 
//                              }
161
 
 
162
 
                                string migration_note_content_template = Catalog.GetString (
163
 
// Translators: The contents (not including the title) of the data migration note. {0}, {1}, {2}, {3}, and {4} are replaced by directory paths and should not be changed
164
 
@"In the latest version of Tomboy, your note files have moved.  You have probably never cared where your notes are stored, and if you still don't care, please go ahead and <bold>delete this note</bold>.  :-)
165
 
 
166
 
Your old note directory is still safe and sound at <link:url>{0}</link:url> . If you go back to an older version of Tomboy, it will look for notes there.
167
 
 
168
 
But we've copied your notes and configuration info into new directories, which will be used from now on:
169
 
 
170
 
<list><list-item dir=""ltr"">Notes can now be found at <link:url>{1}</link:url>
171
 
</list-item><list-item dir=""ltr"">Configuration is at <link:url>{2}</link:url>
172
 
</list-item><list-item dir=""ltr"">You can install add-ins at <link:url>{3}</link:url>
173
 
</list-item><list-item dir=""ltr"">Log files can be found at <link:url>{4}</link:url></list-item></list>
174
 
 
175
 
Ciao!");
176
 
                                string migration_note_content =
177
 
                                        "<note-content version=\"1.0\">" +
178
 
                                        migration_note_title + "\n\n" +
179
 
                                        string.Format (migration_note_content_template,
180
 
                                                       old_notes_dir + Path.DirectorySeparatorChar,
181
 
                                                       notes_dir + Path.DirectorySeparatorChar,
182
 
                                                       conf_dir + Path.DirectorySeparatorChar,
183
 
                                                       Path.Combine (conf_dir, "addins") + Path.DirectorySeparatorChar,
184
 
                                                       Services.NativeApplication.LogDirectory + Path.DirectorySeparatorChar) +
185
 
                                        "</note-content>";
186
 
 
187
 
                                // NOTE: Uncomment to create a Tomboy note and
188
 
                                //       show it to the user
189
 
//                              Note migration_note = Create (migration_note_title,
190
 
//                                                            migration_note_content);
191
 
//                              migration_note.QueueSave (ChangeType.ContentChanged);
192
 
//                              migration_note.Window.Show ();
193
 
 
194
 
                                // Place file announcing migration in old note directory
195
 
                                try {
196
 
                                        using (var writer = File.CreateText (Path.Combine (old_notes_dir,
197
 
                                                                                           migration_note_title.ToUpper ().Replace (" ", "_"))))
198
 
                                                writer.Write (migration_note_content);
199
 
                                } catch {}
200
 
                        }
201
 
 
202
 
                        Tomboy.ExitingEvent += OnExitingEvent;
203
 
                        Initialized = true;
204
 
                }
205
 
                
206
 
                
207
 
                // Create the TrieController. For overriding in test methods.
208
 
                protected virtual TrieController CreateTrieController ()
209
 
                {
210
 
                        return new TrieController (this);
211
 
                }
212
 
 
213
 
                // For overriding in test methods.
214
 
                protected virtual bool DirectoryExists (string directory)
215
 
                {
216
 
                        return Directory.Exists (directory);
217
 
                }
218
 
 
219
 
                // For overriding in test methods.
220
 
                protected virtual DirectoryInfo CreateDirectory (string directory)
221
 
                {
222
 
                        return Directory.CreateDirectory (directory);
223
 
                }
224
 
 
225
 
                protected virtual bool FirstRun ()
226
 
                {
227
 
                        return !DirectoryExists (notes_dir);
228
 
                }
229
 
 
230
 
                // Create the notes directory if it doesn't exist yet.
231
 
                void CreateNotesDir ()
232
 
                {
233
 
                        if (!DirectoryExists (notes_dir)) {
234
 
                                // First run. Create storage directory.
235
 
                                CreateDirectory (notes_dir);
236
 
                        }
237
 
                }
238
 
 
239
 
                void OnNoteRename (Note note, string old_title)
240
 
                {
241
 
                        if (NoteRenamed != null)
242
 
                                NoteRenamed (note, old_title);
243
 
                        this.notes.Sort (new CompareDates ());
244
 
                }
245
 
 
246
 
                void OnNoteSave (Note note)
247
 
                {
248
 
                        if (NoteSaved != null)
249
 
                                NoteSaved (note);
250
 
                        this.notes.Sort (new CompareDates ());
251
 
                }
252
 
 
253
 
                void OnBufferChanged (Note note)
254
 
                {
255
 
                        if (NoteBufferChanged != null)
256
 
                                NoteBufferChanged (note);
257
 
                }
258
 
 
259
 
                protected virtual void CreateStartNotes ()
260
 
                {
261
 
                        // FIXME: Delay the creation of the start notes so the panel/tray
262
 
                        // icon has enough time to appear so that Tomboy.TrayIconShowing
263
 
                        // is valid.  Then, we'll be able to instruct the user where to
264
 
                        // find the Tomboy icon.
265
 
                        //string icon_str = Tomboy.TrayIconShowing ?
266
 
                        //     Catalog.GetString ("System Tray Icon area") :
267
 
                        //     Catalog.GetString ("GNOME Panel");
268
 
                        string start_note_content =
269
 
                                Catalog.GetString ("<note-content>" +
270
 
                                                   "Start Here\n\n" +
271
 
                                                   "<bold>Welcome to Tomboy!</bold>\n\n" +
272
 
                                                   "Use this \"Start Here\" note to begin organizing " +
273
 
                                                   "your ideas and thoughts.\n\n" +
274
 
                                                   "You can create new notes to hold your ideas by " +
275
 
                                                   "selecting the \"Create New Note\" item from the " +
276
 
                                                   "Tomboy Notes menu in your GNOME Panel. " +
277
 
                                                   "Your note will be saved automatically.\n\n" +
278
 
                                                   "Then organize the notes you create by linking " +
279
 
                                                   "related notes and ideas together!\n\n" +
280
 
                                                   "We've created a note called " +
281
 
                                                   "<link:internal>Using Links in Tomboy</link:internal>.  " +
282
 
                                                   "Notice how each time we type <link:internal>Using " +
283
 
                                                   "Links in Tomboy</link:internal> it automatically " +
284
 
                                                   "gets underlined?  Click on the link to open the note." +
285
 
                                                   "</note-content>");
286
 
 
287
 
                        string links_note_content =
288
 
                                Catalog.GetString ("<note-content>" +
289
 
                                                   "Using Links in Tomboy\n\n" +
290
 
                                                   "Notes in Tomboy can be linked together by " +
291
 
                                                   "highlighting text in the current note and clicking" +
292
 
                                                   " the <bold>Link</bold> button above in the toolbar.  " +
293
 
                                                   "Doing so will create a new note and also underline " +
294
 
                                                   "the note's title in the current note.\n\n" +
295
 
                                                   "Changing the title of a note will update links " +
296
 
                                                   "present in other notes.  This prevents broken links " +
297
 
                                                   "from occurring when a note is renamed.\n\n" +
298
 
                                                   "Also, if you type the name of another note in your " +
299
 
                                                   "current note, it will automatically be linked for you." +
300
 
                                                   "</note-content>");
301
 
 
302
 
                        try {
303
 
                                Note start_note = Create (Catalog.GetString ("Start Here"),
304
 
                                                          start_note_content);
305
 
                                start_note.QueueSave (ChangeType.ContentChanged);
306
 
                                Preferences.Set (Preferences.START_NOTE_URI, start_note.Uri);
307
 
 
308
 
                                Note links_note = Create (Catalog.GetString ("Using Links in Tomboy"),
309
 
                                                          links_note_content);
310
 
                                links_note.QueueSave (ChangeType.ContentChanged);
311
 
 
312
 
                                if (!Tomboy.IsPanelApplet)
313
 
                                        start_note.Window.Show ();
314
 
                        } catch (Exception e) {
315
 
                                Logger.Warn ("Error creating start notes: {0}\n{1}",
316
 
                                             e.Message, e.StackTrace);
317
 
                        }
318
 
                }
319
 
 
320
 
                protected virtual void LoadNotes ()
321
 
                {
322
 
                        Logger.Debug ("Loading notes");
323
 
                        string [] files = Directory.GetFiles (notes_dir, "*.note");
324
 
 
325
 
                        foreach (string file_path in files) {
326
 
                                try {
327
 
                                        Note note = Note.Load (file_path, this);
328
 
                                        if (note != null) {
329
 
                                                note.Renamed += OnNoteRename;
330
 
                                                note.Saved += OnNoteSave;
331
 
                                                note.BufferChanged += OnBufferChanged;
332
 
                                                notes.Add (note);
333
 
                                        }
334
 
                                } catch (System.Xml.XmlException e) {
335
 
                                        Logger.Error ("Error parsing note XML, skipping \"{0}\": {1}",
336
 
                                                    file_path,
337
 
                                                    e.Message);
338
 
                                } catch (System.IO.IOException e) {
339
 
                                        Logger.Error ("Note {0} can not be loaded - file corrupted?: {1}",
340
 
                                                    file_path,
341
 
                                                    e.Message);
342
 
                                        Gtk.MessageDialog md =
343
 
                                          new Gtk.MessageDialog(null,Gtk.DialogFlags.DestroyWithParent,
344
 
                                                              Gtk.MessageType.Error,
345
 
                                                              Gtk.ButtonsType.Close,
346
 
                                                              "Skipping a note.\n {0} can not be loaded - Error loading file!",
347
 
                                                              file_path
348
 
                                                             );
349
 
                                        md.Run();
350
 
                                        md.Destroy();
351
 
                                } catch (System.UnauthorizedAccessException e) {
352
 
                                        Logger.Error ("Note {0} can not be loaded - access denied: {1}",
353
 
                                                    file_path,
354
 
                                                    e.Message);
355
 
                                        Gtk.MessageDialog md =
356
 
                                          new Gtk.MessageDialog(null,Gtk.DialogFlags.DestroyWithParent,
357
 
                                                              Gtk.MessageType.Error,
358
 
                                                              Gtk.ButtonsType.Close,
359
 
                                                              "Skipping a note.\n {0} can not be loaded - Access denied!",
360
 
                                                              file_path
361
 
                                                             );
362
 
                                        md.Run();
363
 
                                        md.Destroy();
364
 
                                }       
365
 
                        }
366
 
                        
367
 
                        notes.Sort (new CompareDates ());
368
 
 
369
 
                        // Update the trie so addins can access it, if they want.
370
 
                        trie_controller.Update ();
371
 
 
372
 
                        if (NotesLoaded != null)
373
 
                                NotesLoaded (this, EventArgs.Empty);
374
 
 
375
 
                        bool startup_notes_enabled = (bool)
376
 
                                                     Preferences.Get (Preferences.ENABLE_STARTUP_NOTES);
377
 
 
378
 
                        // Load all the addins for our notes.
379
 
                        // Iterating through copy of notes list, because list may be
380
 
                        // changed when loading addins.
381
 
                        List<Note> notesCopy = new List<Note> (notes);
382
 
                        foreach (Note note in notesCopy) {
383
 
                                addin_mgr.LoadAddinsForNote (note);
384
 
 
385
 
                                // Show all notes that were visible when tomboy was shut down
386
 
                                if (note.IsOpenOnStartup) {
387
 
                                        if (startup_notes_enabled)
388
 
                                                note.Window.Show ();
389
 
 
390
 
                                        note.QueueSave (ChangeType.NoChange);
391
 
                                }
392
 
                        }
393
 
 
394
 
                        // Make sure that a Start Note Uri is set in the preferences, and
395
 
                        // make sure that the Uri is valid to prevent bug #508982. This
396
 
                        // has to be done here for long-time Tomboy users who won't go
397
 
                        // through the CreateStartNotes () process.
398
 
                        if (StartNoteUri == String.Empty ||
399
 
                            FindByUri(StartNoteUri) == null) {
400
 
                                // Attempt to find an existing Start Here note
401
 
                                Note start_note = Find (Catalog.GetString ("Start Here"));
402
 
                                if (start_note != null)
403
 
                                        Preferences.Set (Preferences.START_NOTE_URI, start_note.Uri);
404
 
                        }
405
 
 
406
 
                        if (NotesLoaded != null)
407
 
                                NotesLoaded (this, EventArgs.Empty);
408
 
                }
409
 
 
410
 
                void OnExitingEvent (object sender, EventArgs args)
411
 
                {
412
 
                        // Call ApplicationAddin.Shutdown () on all the known ApplicationAddins
413
 
                        foreach (ApplicationAddin addin in addin_mgr.GetApplicationAddins ()) {
414
 
                                try {
415
 
                                        addin.Shutdown ();
416
 
                                } catch (Exception e) {
417
 
                                        Logger.Warn ("Error calling {0}.Shutdown (): {1}",
418
 
                                                     addin.GetType ().ToString (), e.Message);
419
 
                                }
420
 
                        }
421
 
 
422
 
                        Logger.Debug ("Saving unsaved notes...");
423
 
                        
424
 
                        // Use a copy of the notes to prevent bug #510442 (crash on exit
425
 
                        // when iterating the notes to save them.
426
 
                        List<Note> notesCopy = new List<Note> (notes);
427
 
                        foreach (Note note in notesCopy) {
428
 
                                // If the note is visible, it will be shown automatically on
429
 
                                // next startup
430
 
                                if (note.HasWindow && note.Window.Visible)
431
 
                                        note.IsOpenOnStartup = true;
432
 
                                else
433
 
                                        note.IsOpenOnStartup = false; /* bgo #672482 */
434
 
 
435
 
                                note.Save ();
436
 
                        }
437
 
                }
438
 
 
439
 
                public void Delete (Note note)
440
 
                {
441
 
                        if (File.Exists (note.FilePath)) {
442
 
                                if (backup_dir != null) {
443
 
                                        if (!Directory.Exists (backup_dir))
444
 
                                                Directory.CreateDirectory (backup_dir);
445
 
 
446
 
                                        string backup_path =
447
 
                                                Path.Combine (backup_dir,
448
 
                                                              Path.GetFileName (note.FilePath));
449
 
                                        if (File.Exists (backup_path))
450
 
                                                File.Delete (backup_path);
451
 
 
452
 
                                        File.Move (note.FilePath, backup_path);
453
 
                                } else
454
 
                                        File.Delete (note.FilePath);
455
 
                        }
456
 
 
457
 
                        notes.Remove (note);
458
 
                        note.Delete ();
459
 
 
460
 
                        Logger.Debug ("Deleting note '{0}'.", note.Title);
461
 
 
462
 
                        if (NoteDeleted != null)
463
 
                                NoteDeleted (this, note);
464
 
                }
465
 
 
466
 
                string MakeNewFileName ()
467
 
                {
468
 
                        return MakeNewFileName (Guid.NewGuid ().ToString ());
469
 
                }
470
 
 
471
 
                string MakeNewFileName (string guid)
472
 
                {
473
 
                        return Path.Combine (notes_dir, guid + ".note");
474
 
                }
475
 
 
476
 
                // Create a new note with a generated title
477
 
                public Note Create ()
478
 
                {
479
 
                        int new_num = notes.Count;
480
 
                        string temp_title;
481
 
 
482
 
                        while (true) {
483
 
                                temp_title = String.Format (Catalog.GetString ("New Note {0}"),
484
 
                                                            ++new_num);
485
 
                                if (Find (temp_title) == null)
486
 
                                        break;
487
 
                        }
488
 
 
489
 
                        return Create (temp_title);
490
 
                }
491
 
 
492
 
                public static string SplitTitleFromContent (string title, out string body)
493
 
                {
494
 
                        body = null;
495
 
 
496
 
                        if (title == null)
497
 
                                return null;
498
 
 
499
 
                        title = title.Trim();
500
 
                        if (title == string.Empty)
501
 
                                return null;
502
 
 
503
 
                        string[] lines = title.Split (new char[] { '\n', '\r' }, 2);
504
 
                        if (lines.Length > 0) {
505
 
                                title = lines [0];
506
 
                                title = title.Trim ();
507
 
                                title = title.TrimEnd ('.', ',', ';');
508
 
                                if (title == string.Empty)
509
 
                                        return null;
510
 
                        }
511
 
 
512
 
                        if (lines.Length > 1)
513
 
                                body = lines [1];
514
 
 
515
 
                        return title;
516
 
                }
517
 
 
518
 
                public Note Create (string title)
519
 
                {
520
 
                        return CreateNewNote (title, null);
521
 
                }
522
 
 
523
 
                public Note Create (string title, string xml_content)
524
 
                {
525
 
                        return CreateNewNote (title, xml_content, null);
526
 
                }
527
 
                
528
 
                /// <summary>
529
 
                /// Creates a new note with GUID.
530
 
                /// </summary>
531
 
                /// <returns>
532
 
                /// Empty note with specified title and GUID.
533
 
                /// </returns>
534
 
                /// <param name='title'>
535
 
                /// Title.
536
 
                /// </param>
537
 
                /// <param name='guid'>
538
 
                /// GUID.
539
 
                /// </param>
540
 
                public Note CreateWithGuid (string title, string guid)
541
 
                {
542
 
                        return CreateNewNote (title, "", guid);
543
 
                }
544
 
 
545
 
                // Create a new note with the specified title from the default
546
 
                // template note. Optionally the body can be overridden by appending
547
 
                // it to title.
548
 
                private Note CreateNewNote (string title, string guid)
549
 
                {
550
 
                        string body = null;
551
 
 
552
 
                        title = SplitTitleFromContent (title, out body);
553
 
                        if (title == null)
554
 
                                return null;
555
 
                        
556
 
                        Note template_note = GetOrCreateTemplateNote ();
557
 
 
558
 
                        if (String.IsNullOrEmpty (body))
559
 
                                return CreateNoteFromTemplate (title, template_note, guid);
560
 
                        
561
 
                        // Use a simple "Describe..." body and highlight
562
 
                        // it so it can be easily overwritten
563
 
                        body = Catalog.GetString ("Describe your new note here.");
564
 
 
565
 
                        string header = title + "\n\n";
566
 
                        string content =
567
 
                                String.Format ("<note-content>{0}{1}</note-content>",
568
 
                                               XmlEncoder.Encode (header),
569
 
                                               XmlEncoder.Encode (body));
570
 
 
571
 
                        Note new_note = CreateNewNote (title, content, guid);
572
 
 
573
 
                        // Select the inital text so typing will overwrite the body text
574
 
                        NoteBuffer buffer = new_note.Buffer;
575
 
                        Gtk.TextIter iter = buffer.GetIterAtOffset (header.Length);
576
 
                        buffer.MoveMark (buffer.SelectionBound, iter);
577
 
                        buffer.MoveMark (buffer.InsertMark, buffer.EndIter);
578
 
 
579
 
                        return new_note;
580
 
                }
581
 
 
582
 
                // Create a new note with the specified Xml content
583
 
                private Note CreateNewNote (string title, string xml_content, string guid)
584
 
                {
585
 
                        if (title == null || title == string.Empty)
586
 
                                throw new Exception ("Invalid title");
587
 
 
588
 
                        if (Find (title) != null)
589
 
                                throw new Exception ("A note with this title already exists: " + title);
590
 
 
591
 
                        string filename;
592
 
                        if (guid != null)
593
 
                                filename = MakeNewFileName (guid);
594
 
                        else
595
 
                                filename = MakeNewFileName ();
596
 
 
597
 
                        Note new_note = Note.CreateNewNote (title, filename, this);
598
 
                        new_note.XmlContent = xml_content;
599
 
                        new_note.Renamed += OnNoteRename;
600
 
                        new_note.Saved += OnNoteSave;
601
 
                        new_note.BufferChanged += OnBufferChanged;
602
 
 
603
 
                        notes.Add (new_note);
604
 
 
605
 
                        // Load all the addins for the new note
606
 
                        addin_mgr.LoadAddinsForNote (new_note);
607
 
 
608
 
                        if (NoteAdded != null)
609
 
                                NoteAdded (this, new_note);
610
 
 
611
 
                        return new_note;
612
 
                }
613
 
                
614
 
                /// <summary>
615
 
                /// Get the existing template note or create a new one
616
 
                /// if it doesn't already exist.
617
 
                /// </summary>
618
 
                /// <returns>
619
 
                /// A <see cref="Note"/>
620
 
                /// </returns>
621
 
                public Note GetOrCreateTemplateNote ()
622
 
                {
623
 
                        // The default template note will have the system template tag and
624
 
                        // will belong to zero notebooks. We find this by searching all
625
 
                        // notes with the TemplateNoteSystemTag and check that it's
626
 
                        // notebook == null
627
 
                        Note template_note = null;
628
 
                        Tag template_tag = TagManager.GetOrCreateSystemTag (TagManager.TemplateNoteSystemTag);
629
 
                        foreach (Note note in template_tag.Notes) {
630
 
                                if (Notebooks.NotebookManager.GetNotebookFromNote (note) == null) {
631
 
                                        template_note = note;
632
 
                                        break;
633
 
                                }
634
 
                        }
635
 
                        
636
 
                        if (template_note == null) {
637
 
                                template_note =
638
 
                                        Create (NoteTemplateTitle,
639
 
                                                        GetNoteTemplateContent (NoteTemplateTitle));
640
 
                                        
641
 
                                // Select the initial text
642
 
                                NoteBuffer buffer = template_note.Buffer;
643
 
                                Gtk.TextIter iter = buffer.GetIterAtLineOffset (2, 0);
644
 
                                buffer.MoveMark (buffer.SelectionBound, iter);
645
 
                                buffer.MoveMark (buffer.InsertMark, buffer.EndIter);
646
 
 
647
 
                                // Flag this as a template note
648
 
                                template_note.AddTag (template_tag);
649
 
 
650
 
                                template_note.QueueSave (ChangeType.ContentChanged);
651
 
                        }
652
 
                        
653
 
                        return template_note;
654
 
                }
655
 
                
656
 
                public static string GetNoteTemplateContent (string title)
657
 
                {
658
 
                        const string base_xml =
659
 
                                "<note-content>" +
660
 
                                "<note-title>{0}</note-title>\n\n" +
661
 
                                "{1}" +
662
 
                                "</note-content>";
663
 
 
664
 
                        return string.Format (base_xml,
665
 
                                              XmlEncoder.Encode (title),
666
 
                                              Catalog.GetString ("Describe your new note here."));
667
 
                }
668
 
 
669
 
                public Note Find (string linked_title)
670
 
                {
671
 
                        foreach (Note note in notes) {
672
 
                                if (note.Title.ToLower () == linked_title.ToLower ())
673
 
                                        return note;
674
 
                        }
675
 
                        return null;
676
 
                }
677
 
 
678
 
                public Note FindByUri (string uri)
679
 
                {
680
 
                        foreach (Note note in notes) {
681
 
                                if (note.Uri == uri)
682
 
                                        return note;
683
 
                        }
684
 
                        return null;
685
 
                }
686
 
                
687
 
                // Removes any trailing whitespace on the title line
688
 
                public static string SanitizeXmlContent (string xml_content)
689
 
                {
690
 
                        int i = String.IsNullOrEmpty (xml_content) ? -1 : xml_content.IndexOf ('\n');
691
 
                        while (--i >= 0) {
692
 
                                if (xml_content [i].Equals ('\r'))
693
 
                                        continue;
694
 
                        
695
 
                                if (Char.IsWhiteSpace (xml_content [i]))
696
 
                                        xml_content = xml_content.Remove (i,1);
697
 
                                else
698
 
                                        break;
699
 
                        }
700
 
                        
701
 
                        return xml_content;
702
 
                }
703
 
                
704
 
                /// <summary>
705
 
                /// Creates a new note with the given titel based on the template note.
706
 
                /// </summary>
707
 
                /// <param name="title">
708
 
                /// A <see cref="System.String"/>
709
 
                /// </param>
710
 
                /// <param name="template_note">
711
 
                /// A <see cref="Note"/>
712
 
                /// </param>
713
 
                /// <returns>
714
 
                /// A <see cref="Note"/>
715
 
                /// </returns>
716
 
                public Note CreateNoteFromTemplate (string title, Note template_note)
717
 
                {
718
 
                        return CreateNoteFromTemplate (title, template_note, null);
719
 
                }
720
 
                
721
 
                // Creates a new note with the given title and guid with body based on
722
 
                // the template note.
723
 
                private Note CreateNoteFromTemplate (string title, Note template_note, string guid)
724
 
                {
725
 
                        Tag template_save_title = TagManager.GetOrCreateSystemTag (TagManager.TemplateNoteSaveTitleSystemTag);
726
 
                        if (template_note.ContainsTag (template_save_title))
727
 
                                title = GetUniqueName (template_note.Title, notes.Count);
728
 
                        
729
 
                        // Use the body from the template note
730
 
                        string xml_content =
731
 
                                template_note.XmlContent.Replace (XmlEncoder.Encode (template_note.Title),
732
 
                                                                  XmlEncoder.Encode (title));
733
 
                        xml_content = SanitizeXmlContent (xml_content);
734
 
 
735
 
                        Note new_note = CreateNewNote (title, xml_content, guid);
736
 
                        
737
 
                        // Copy template note's properties
738
 
                        Tag template_save_size = TagManager.GetOrCreateSystemTag (TagManager.TemplateNoteSaveSizeSystemTag);
739
 
                        if (template_note.Data.HasExtent () && template_note.ContainsTag (template_save_size)) {
740
 
                                new_note.Data.Height = template_note.Data.Height;
741
 
                                new_note.Data.Width = template_note.Data.Width;
742
 
                        }
743
 
                        
744
 
                        Tag template_save_selection = TagManager.GetOrCreateSystemTag (TagManager.TemplateNoteSaveSelectionSystemTag);
745
 
                        if (template_note.Data.CursorPosition > 0 && template_note.ContainsTag (template_save_selection)) {
746
 
                                Gtk.TextBuffer buffer = new_note.Buffer;
747
 
                                Gtk.TextIter iter;
748
 
                                
749
 
                                // Because the titles will be different between template and
750
 
                                // new note, we can't just drop the cursor at template's
751
 
                                // CursorPosition. Whitespace after the title makes this more
752
 
                                // complicated so let's just start counting from the line after the title.
753
 
                                int title_offset_difference = buffer.GetIterAtLine (1).Offset - template_note.Buffer.GetIterAtLine (1).Offset;
754
 
                                
755
 
                                iter = buffer.GetIterAtOffset (template_note.Data.CursorPosition + title_offset_difference);
756
 
                                buffer.PlaceCursor(iter);
757
 
                                
758
 
                                iter = buffer.GetIterAtOffset (template_note.Data.SelectionBoundPosition + title_offset_difference);
759
 
                                buffer.MoveMark (buffer.SelectionBound.Name, iter);
760
 
                        }
761
 
                        
762
 
                        return new_note;
763
 
                }
764
 
                
765
 
                // Find a title that does not exist using basename and id as
766
 
                // a starting point
767
 
                public string GetUniqueName (string basename, int id)
768
 
                {
769
 
                        string title;
770
 
                        while (true) {
771
 
                                title = String.Concat (basename, " ", id++);
772
 
                                if (Find (title) == null)
773
 
                                        break;
774
 
                        }
775
 
                        
776
 
                        return title;
777
 
                }
778
 
 
779
 
                class CompareDates : IComparer<Note>
780
 
                {
781
 
                        public int Compare (Note a, Note b)
782
 
                        {
783
 
                                // Sort in reverse chrono order...
784
 
                                if (a == null || b == null)
785
 
                                        return -1;
786
 
                                else
787
 
                                        return DateTime.Compare (b.ChangeDate,
788
 
                                                                 a.ChangeDate);
789
 
                        }
790
 
                }
791
 
 
792
 
                public static string StartNoteUri
793
 
                {
794
 
                        get {
795
 
                                if (String.IsNullOrEmpty (start_note_uri)) {
796
 
                                        // Watch the START_NOTE_URI setting and update it so that the
797
 
                                        // StartNoteUri property doesn't generate a call to
798
 
                                        // Preferences.Get () each time it's accessed.
799
 
                                        start_note_uri =
800
 
                                                Preferences.Get (Preferences.START_NOTE_URI) as string;
801
 
                                        Preferences.SettingChanged -= OnSettingChanged;
802
 
                                        Preferences.SettingChanged += OnSettingChanged;
803
 
                                }
804
 
                                return start_note_uri;
805
 
                        }
806
 
                }
807
 
 
808
 
                public List<Note> Notes
809
 
                {
810
 
                        get {
811
 
                                // FIXME: Only sort on change by listening to
812
 
                                //        Note.Saved or Note.Buffer.Changed
813
 
                                //notes.Sort (new CompareDates ());
814
 
                                return notes;
815
 
                        }
816
 
                }
817
 
 
818
 
                public TrieTree TitleTrie
819
 
                {
820
 
                        get {
821
 
                                return trie_controller.TitleTrie;
822
 
                        }
823
 
                }
824
 
 
825
 
                public AddinManager AddinManager
826
 
                {
827
 
                        get {
828
 
                                return addin_mgr;
829
 
                        }
830
 
                }
831
 
 
832
 
                public string NoteDirectoryPath
833
 
                {
834
 
                        get {
835
 
                                return notes_dir;
836
 
                        }
837
 
                }
838
 
 
839
 
                public event NotesChangedHandler NoteDeleted;
840
 
                public event NotesChangedHandler NoteAdded;
841
 
                public event NoteRenameHandler NoteRenamed;
842
 
                public event NoteSavedHandler NoteSaved;
843
 
                public event Action<Note> NoteBufferChanged;
844
 
                public event EventHandler NotesLoaded;
845
 
        }
846
 
 
847
 
        public class TrieController
848
 
        {
849
 
                TrieTree title_trie;
850
 
                NoteManager manager;
851
 
 
852
 
                public TrieController (NoteManager manager)
853
 
                {
854
 
                        this.manager = manager;
855
 
                        manager.NoteDeleted += OnNoteDeleted;
856
 
                        manager.NoteAdded += OnNoteAdded;
857
 
                        manager.NoteRenamed += OnNoteRenamed;
858
 
 
859
 
                        Update ();
860
 
                }
861
 
 
862
 
                void OnNoteAdded (object sender, Note added)
863
 
                {
864
 
                        Update ();
865
 
                }
866
 
 
867
 
                void OnNoteDeleted (object sender, Note deleted)
868
 
                {
869
 
                        Update ();
870
 
                }
871
 
 
872
 
                void OnNoteRenamed (Note renamed, string old_title)
873
 
                {
874
 
                        Update ();
875
 
                }
876
 
 
877
 
                public void Update ()
878
 
                {
879
 
                        title_trie = new TrieTree (false /* !case_sensitive */);
880
 
 
881
 
                        foreach (Note note in manager.Notes) {
882
 
                                title_trie.AddKeyword (note.Title, note);
883
 
                        }
884
 
 
885
 
                        title_trie.ComputeFailureGraph ();
886
 
                }
887
 
 
888
 
                public TrieTree TitleTrie
889
 
                {
890
 
                        get {
891
 
                                return title_trie;
892
 
                        }
893
 
                }
894
 
        }
895
 
}