43
45
Buffer.MarkSet += OnMarkSet;
44
46
Buffer.InsertText += OnInsertText;
45
47
Buffer.DeleteRange += OnDeleteRange;
47
49
Window.Editor.FocusOutEvent += OnEditorFocusOut;
49
51
// FIXME: Needed because we hide on delete event, and
56
58
Buffer.RemoveAllTags (TitleStart, TitleEnd);
57
59
Buffer.ApplyTag (title_tag, TitleStart, TitleEnd);
60
62
void OnEditorFocusOut (object sender, Gtk.FocusOutEventArgs args)
62
64
// TODO: Duplicated from Update(); refactor instead
179
181
Buffer.MoveMark (Buffer.SelectionBound, TitleStart);
180
182
Buffer.MoveMark (Buffer.InsertMark, TitleEnd);
183
String.Format (Catalog.GetString ("A note with the title " +
184
"<b>{0}</b> already exists. " +
185
"Please choose another name " +
186
"for this note before " +
185
String.Format (Catalog.GetString ("A note with the title " +
186
"<b>{0}</b> already exists. " +
187
"Please choose another name " +
188
"for this note before " +
190
192
/// Only pop open a warning dialog when one isn't already present
191
193
/// Had to add this check because this method is being called twice.
192
194
if (title_taken_dialog == null) {
193
195
title_taken_dialog =
194
new HIGMessageDialog (Window,
195
Gtk.DialogFlags.DestroyWithParent,
196
Gtk.MessageType.Warning,
198
Catalog.GetString ("Note title taken"),
196
new HIGMessageDialog (Window,
197
Gtk.DialogFlags.DestroyWithParent,
198
Gtk.MessageType.Warning,
200
Catalog.GetString ("Note title taken"),
200
202
title_taken_dialog.Modal = true;
201
203
title_taken_dialog.Response +=
202
delegate (object sender, Gtk.ResponseArgs args) {
203
title_taken_dialog.Destroy ();
204
title_taken_dialog = null;
204
delegate (object sender, Gtk.ResponseArgs args) {
205
title_taken_dialog.Destroy ();
206
title_taken_dialog = null;
208
210
title_taken_dialog.Present ();
213
215
public class NoteSpellChecker : NoteAddin
215
217
IntPtr obj_ptr = IntPtr.Zero;
218
220
static bool gtkspell_available_result;
220
222
[DllImport ("libgtkspell")]
221
static extern IntPtr gtkspell_new_attach (IntPtr text_view,
223
static extern IntPtr gtkspell_new_attach (IntPtr text_view,
225
227
[DllImport ("libgtkspell")]
226
228
static extern void gtkspell_detach (IntPtr obj);
231
233
Gtk.TextView test_view = new Gtk.TextView ();
232
IntPtr test_ptr = gtkspell_new_attach (test_view.Handle,
234
IntPtr test_ptr = gtkspell_new_attach (test_view.Handle,
235
237
if (test_ptr != IntPtr.Zero)
236
238
gtkspell_detach (test_ptr);
243
public static bool GtkSpellAvailable
246
if (!gtkspell_available_tested) {
245
public static bool GtkSpellAvailable
248
if (!gtkspell_available_tested) {
247
249
gtkspell_available_result = DetectGtkSpellAvailable ();
248
250
gtkspell_available_tested = true;
338
Buffer.RemoveTag ("gtkspell-misspelled",
340
Buffer.RemoveTag ("gtkspell-misspelled",
346
348
public class NoteUrlWatcher : NoteAddin
349
351
Gtk.TextMark click_mark;
351
const string URL_REGEX =
352
@"((\b((news|http|https|ftp|file|irc)://|mailto:|(www|ftp)\.|\S*@\S*\.)|(^|\s)/\S+/|(^|\s)~/\S+)\S*\b/?)";
353
const string URL_REGEX =
354
@"((\b((news|http|https|ftp|file|irc)://|mailto:|(www|ftp)\.|\S*@\S*\.)|(^|\s)/\S+/|(^|\s)~/\S+)\S*\b/?)";
354
356
static Regex regex;
355
357
static bool text_event_connected;
357
359
static NoteUrlWatcher ()
359
regex = new Regex (URL_REGEX,
360
RegexOptions.IgnoreCase | RegexOptions.Compiled);
361
regex = new Regex (URL_REGEX,
362
RegexOptions.IgnoreCase | RegexOptions.Compiled);
361
363
text_event_connected = false;
385
387
url_tag.Activated += OnUrlTagActivated;
386
388
text_event_connected = true;
389
391
url_tag.Activated += OnUrlTagActivated;
392
394
click_mark = Buffer.CreateMark (null, Buffer.StartIter, true);
412
414
// to /home/alex/foo.
413
415
if (url.StartsWith ("www."))
414
416
url = "http://" + url;
415
else if (url.StartsWith ("/") &&
416
url.LastIndexOf ("/") > 1)
417
else if (url.StartsWith ("/") &&
418
url.LastIndexOf ("/") > 1)
417
419
url = "file://" + url;
418
420
else if (url.StartsWith ("~/"))
420
Path.Combine (Environment.GetEnvironmentVariable ("HOME"),
422
Path.Combine (Environment.GetEnvironmentVariable ("HOME"),
422
424
else if (url.IndexOf ("@") > 1 &&
423
url.IndexOf (".") > 3 &&
424
!url.StartsWith ("mailto:"))
425
url.IndexOf (".") > 3 &&
426
!url.StartsWith ("mailto:"))
425
427
url = "mailto:" + url;
430
void OpenUrl (string url)
432
void OpenUrl (string url)
432
434
if (url != string.Empty) {
433
435
Logger.Log ("Opening url '{0}'...", url);
440
442
string message = String.Format ("{0}: {1}", url, error);
442
HIGMessageDialog dialog =
443
new HIGMessageDialog (Window,
444
Gtk.DialogFlags.DestroyWithParent,
445
Gtk.MessageType.Info,
447
Catalog.GetString ("Cannot open location"),
444
HIGMessageDialog dialog =
445
new HIGMessageDialog (Window,
446
Gtk.DialogFlags.DestroyWithParent,
447
Gtk.MessageType.Info,
449
Catalog.GetString ("Cannot open location"),
450
452
dialog.Destroy ();
453
455
bool OnUrlTagActivated (NoteTag sender,
458
460
string url = GetUrl (start, end);
469
471
void ApplyUrlToBlock (Gtk.TextIter start, Gtk.TextIter end)
471
NoteBuffer.GetBlockExtents (ref start,
473
256 /* max url length */,
473
NoteBuffer.GetBlockExtents (ref start,
475
256 /* max url length */,
476
478
Buffer.RemoveTag (url_tag, start, end);
478
for (Match match = regex.Match (start.GetSlice (end));
480
match = match.NextMatch ()) {
480
for (Match match = regex.Match (start.GetSlice (end));
482
match = match.NextMatch ()) {
481
483
System.Text.RegularExpressions.Group group = match.Groups [1];
484
486
Logger.Log ("Highlighting url: '{0}' at offset {1}",
489
491
Gtk.TextIter start_cpy = start;
592
594
static bool text_event_connected;
594
public override void Initialize ()
596
public override void Initialize ()
596
598
Manager.NoteDeleted += OnNoteDeleted;
597
599
Manager.NoteAdded += OnNoteAdded;
612
614
public override void OnNoteOpened ()
615
617
// NOTE: This avoid multiple link opens for cases where
616
618
// the GtkSpell version is fixed to allow TagTable
617
619
// sharing. This is because if the TagTable is shared,
623
625
broken_link_tag.Activated += OnLinkTagActivated;
624
626
text_event_connected = true;
627
629
link_tag.Activated += OnLinkTagActivated;
628
630
broken_link_tag.Activated += OnLinkTagActivated;
631
633
Buffer.InsertText += OnInsertText;
632
634
Buffer.DeleteRange += OnDeleteRange;
693
695
if (range.Text.ToLower () != old_title_lower)
696
Logger.Log ("Replacing '{0}' with '{1}'",
698
Logger.Log ("Replacing '{0}' with '{1}'",
700
702
Gtk.TextIter start_iter = range.Start;
701
703
Gtk.TextIter end_iter = range.End;
718
720
title_end.ForwardChars (hit.End);
720
722
// Only link against whole words/phrases
721
if ((!title_start.StartsWord () && !title_start.StartsSentence ()) ||
722
(!title_end.EndsWord() && !title_end.EndsSentence()))
723
if ((!title_start.StartsWord () && !title_start.StartsSentence ()) ||
724
(!title_end.EndsWord() && !title_end.EndsSentence()))
725
727
// Don't create links inside URLs
726
728
if (title_start.HasTag (url_tag))
729
Logger.Log ("Matching Note title '{0}' at {1}-{2}...",
731
Logger.Log ("Matching Note title '{0}' at {1}-{2}...",
734
736
Buffer.RemoveTag (broken_link_tag, title_start, title_end);
735
737
Buffer.ApplyTag (link_tag, title_start, title_end);
738
void HighlightNoteInBlock (Note find_note, Gtk.TextIter start, Gtk.TextIter end)
740
void HighlightNoteInBlock (Note find_note, Gtk.TextIter start, Gtk.TextIter end)
740
742
string buffer_text = start.GetText (end).ToLower();
741
743
string find_title_lower = find_note.Title.ToLower ();
749
TrieHit hit = new TrieHit (idx,
750
idx + find_title_lower.Length,
751
TrieHit hit = new TrieHit (idx,
752
idx + find_title_lower.Length,
753
755
DoHighlight (hit, start, end);
755
757
idx += find_title_lower.Length;
759
void HighlightInBlock (Gtk.TextIter start, Gtk.TextIter end)
761
void HighlightInBlock (Gtk.TextIter start, Gtk.TextIter end)
761
763
ArrayList hits = Manager.TitleTrie.FindMatches (start.GetSlice (end));
762
764
foreach (TrieHit hit in hits) {
767
void UnhighlightInBlock (Gtk.TextIter start, Gtk.TextIter end)
769
void UnhighlightInBlock (Gtk.TextIter start, Gtk.TextIter end)
769
771
Buffer.RemoveTag (link_tag, start, end);
774
776
Gtk.TextIter start = args.Start;
775
777
Gtk.TextIter end = args.End;
777
NoteBuffer.GetBlockExtents (ref start,
779
Manager.TitleTrie.MaxLength,
779
NoteBuffer.GetBlockExtents (ref start,
781
Manager.TitleTrie.MaxLength,
782
784
UnhighlightInBlock (start, end);
783
785
HighlightInBlock (start, end);
791
793
Gtk.TextIter end = args.Pos;
793
NoteBuffer.GetBlockExtents (ref start,
795
Manager.TitleTrie.MaxLength,
795
NoteBuffer.GetBlockExtents (ref start,
797
Manager.TitleTrie.MaxLength,
798
800
UnhighlightInBlock (start, end);
799
801
HighlightInBlock (start, end);
807
809
if (link == null) {
808
810
Logger.Log ("Creating note '{0}'...", link_name);
810
link = Manager.Create (link_name);
812
link = Manager.Create (link_name);
816
// FIXME: We used to also check here for (link != this.Note), but
817
// somehow this was causing problems receiving clicks for the
818
// wrong instance of a note (see bug #413234). Since a
819
// link:internal tag is never applied around text that's the same
820
// as the current note's title, it's safe to omit this check and
821
// also works around the bug.
818
// FIXME: We used to also check here for (link != this.Note), but
819
// somehow this was causing problems receiving clicks for the
820
// wrong instance of a note (see bug #413234). Since a
821
// link:internal tag is never applied around text that's the same
822
// as the current note's title, it's safe to omit this check and
823
// also works around the bug.
823
825
Logger.Log ("Opening note '{0}' on click...", link_name);
824
826
link.Window.Present ();
887
static string [] PatronymicPrefixes =
888
new string [] { "Mc", "Mac", "Le", "La", "De", "Van" };
889
static string [] PatronymicPrefixes =
890
new string [] { "Mc", "Mac", "Le", "La", "De", "Van" };
890
892
bool IsPatronymicName (string word)
892
894
foreach (string prefix in PatronymicPrefixes) {
893
895
if (word.StartsWith (prefix) &&
894
char.IsUpper (word [prefix.Length]))
896
char.IsUpper (word [prefix.Length]))
901
903
void ApplyWikiwordToBlock (Gtk.TextIter start, Gtk.TextIter end)
903
NoteBuffer.GetBlockExtents (ref start,
905
80 /* max wiki name */,
905
NoteBuffer.GetBlockExtents (ref start,
907
80 /* max wiki name */,
908
910
Buffer.RemoveTag (broken_link_tag, start, end);
910
for (Match match = regex.Match (start.GetText (end));
912
match = match.NextMatch ()) {
912
for (Match match = regex.Match (start.GetText (end));
914
match = match.NextMatch ()) {
913
915
System.Text.RegularExpressions.Group group = match.Groups [1];
915
917
if (IsPatronymicName (group.ToString ()))
918
920
Logger.Log ("Highlighting wikiword: '{0}' at offset {1}",
922
924
Gtk.TextIter start_cpy = start;
923
925
start_cpy.ForwardChars (group.Index);
1034
1036
int pointer_x, pointer_y;
1035
1037
Gdk.ModifierType pointer_mask;
1037
Window.Editor.GdkWindow.GetPointer (out pointer_x,
1039
Window.Editor.GdkWindow.GetPointer (out pointer_x,
1041
1043
bool hovering = false;
1043
1045
// Figure out if we're on a link by getting the text
1044
1046
// iter at the mouse point, and checking for tags that
1045
1047
// start with "link:"...
1047
1049
int buffer_x, buffer_y;
1048
1050
Window.Editor.WindowToBufferCoords (Gtk.TextWindowType.Widget,
1054
1056
Gtk.TextIter iter = Window.Editor.GetIterAtLocation (buffer_x, buffer_y);
1056
1058
foreach (Gtk.TextTag tag in iter.Tags) {
1063
// Don't show hand if Shift or Control is pressed
1065
// Don't show hand if Shift or Control is pressed
1064
1066
bool avoid_hand = (pointer_mask & (Gdk.ModifierType.ShiftMask |
1065
Gdk.ModifierType.ControlMask)) != 0;
1067
Gdk.ModifierType.ControlMask)) != 0;
1067
1069
if (hovering != hovering_on_link) {
1068
1070
hovering_on_link = hovering;
1070
1072
Gdk.Window win = Window.Editor.GetWindow (Gtk.TextWindowType.Text);
1071
1073
if (hovering && !avoid_hand)
1072
1074
win.Cursor = hand_cursor;
1074
1076
win.Cursor = normal_cursor;
1079
1081
public class NoteTagsWatcher : NoteAddin
1081
1083
static NoteTagsWatcher ()
1104
1106
Logger.Debug ("\t{0}", tag.Name);
1108
1110
void OnTagAdded (Note note, Tag tag)
1110
1112
Logger.Debug ("Tag added to {0}: {1}", note.Title, tag.Name);
1113
1115
void OnTagRemoving (Note note, Tag tag)
1115
1117
Logger.Debug ("Removing tag from {0}: {1}", note.Title, tag.Name);
1119
1121
// Keep the TagManager clean by removing tags that are no longer
1120
1122
// tagging any other notes.
1122
1124
void OnTagRemoved (Note note, string tag_name)
1124
1126
Tag tag = TagManager.GetTag (tag_name);
1125
Logger.Debug ("Watchers.OnTagRemoved popularity count: {0}", tag.Popularity);
1127
Logger.Debug ("Watchers.OnTagRemoved popularity count: {0}", tag.Popularity);
1126
1128
if (tag.Popularity == 0)
1127
1129
TagManager.RemoveTag (tag);