[gnote] Use separate builtin app plugin for link management on create/delete/rename



commit 5a0bf562d09f80564a69a13bacca535aa5d394bc
Author: Aurimas Černius <aurisc4 gmail com>
Date:   Mon Jun 8 22:16:50 2020 +0300

    Use separate builtin app plugin for link management on create/delete/rename

 src/addinmanager.cpp |  29 +++++-
 src/watchers.cpp     | 283 +++++++++++++++++++++++++++++----------------------
 src/watchers.hpp     |  42 +++++---
 3 files changed, 219 insertions(+), 135 deletions(-)
---
diff --git a/src/addinmanager.cpp b/src/addinmanager.cpp
index 722aba72..537dc2e5 100644
--- a/src/addinmanager.cpp
+++ b/src/addinmanager.cpp
@@ -1,7 +1,7 @@
 /*
  * gnote
  *
- * Copyright (C) 2010-2017,2019 Aurimas Cernius
+ * Copyright (C) 2010-2017,2019-2020 Aurimas Cernius
  * Copyright (C) 2009, 2010 Debarshi Ray
  * Copyright (C) 2009 Hubert Figuiere
  *
@@ -72,6 +72,31 @@ namespace gnote {
     } \
   } while(0)
 
+#define SETUP_APP_ADDIN(key, KEY, klass) \
+  do { \
+    if(key == KEY) { \
+      Glib::RefPtr<Gio::Settings> settings = m_preferences \
+        .get_schema_settings(Preferences::SCHEMA_GNOTE); \
+      if(settings->get_boolean(key)) { \
+        auto iter = m_app_addins.find(typeid(klass).name()); \
+        if(iter != m_app_addins.end()) { \
+          iter->second->initialize(); \
+        } \
+        else { \
+          auto addin = klass::create(); \
+          m_app_addins.insert(std::make_pair(typeid(klass).name(), addin)); \
+          addin->initialize(m_gnote, m_note_manager); \
+        } \
+      } \
+      else { \
+        auto addin = m_app_addins.find(typeid(klass).name()); \
+        if(addin != m_app_addins.end()) { \
+          addin->second->shutdown(); \
+        } \
+      } \
+    } \
+  } while(0)
+
 namespace {
   template <typename AddinType>
   Glib::ustring get_id_for_addin(const AbstractAddin & addin, const std::map<Glib::ustring, AddinType*> & 
addins)
@@ -270,6 +295,7 @@ namespace {
       REGISTER_BUILTIN_NOTE_ADDIN(NoteUrlWatcher);
     }
     if(settings->get_boolean(Preferences::ENABLE_AUTO_LINKS)) {
+      REGISTER_APP_ADDIN(AppLinkWatcher);
       REGISTER_BUILTIN_NOTE_ADDIN(NoteLinkWatcher);
     }
     if(settings->get_boolean(Preferences::ENABLE_WIKIWORDS)) {
@@ -573,6 +599,7 @@ namespace {
   {
     SETUP_NOTE_ADDIN(key, Preferences::ENABLE_URL_LINKS, NoteUrlWatcher);
     SETUP_NOTE_ADDIN(key, Preferences::ENABLE_AUTO_LINKS, NoteLinkWatcher);
+    SETUP_APP_ADDIN(key, Preferences::ENABLE_AUTO_LINKS, AppLinkWatcher);
     SETUP_NOTE_ADDIN(key, Preferences::ENABLE_WIKIWORDS, NoteWikiWatcher);
   }
 
diff --git a/src/watchers.cpp b/src/watchers.cpp
index af201337..ecede1bc 100644
--- a/src/watchers.cpp
+++ b/src/watchers.cpp
@@ -1,7 +1,7 @@
 /*
  * gnote
  *
- * Copyright (C) 2010-2015,2017,2019 Aurimas Cernius
+ * Copyright (C) 2010-2015,2017,2019-2020 Aurimas Cernius
  * Copyright (C) 2010 Debarshi Ray
  * Copyright (C) 2009 Hubert Figuiere
  *
@@ -756,217 +756,256 @@ namespace gnote {
 
   ////////////////////////////////////////////////////////////////////////
 
-  bool NoteLinkWatcher::s_text_event_connected = false;
-
-  NoteAddin * NoteLinkWatcher::create()
+  ApplicationAddin *AppLinkWatcher::create()
   {
-    return new NoteLinkWatcher;
+    return new AppLinkWatcher;
   }
 
-
-  void NoteLinkWatcher::initialize ()
+  AppLinkWatcher::AppLinkWatcher()
+    : m_initialized(false)
   {
-    m_on_note_deleted_cid = manager().signal_note_deleted.connect(
-      sigc::mem_fun(*this, &NoteLinkWatcher::on_note_deleted));
-    m_on_note_added_cid = manager().signal_note_added.connect(
-      sigc::mem_fun(*this, &NoteLinkWatcher::on_note_added));
-    m_on_note_renamed_cid = manager().signal_note_renamed.connect(
-      sigc::mem_fun(*this, &NoteLinkWatcher::on_note_renamed));
-
-    m_link_tag = get_note()->get_tag_table()->get_link_tag();
-    m_broken_link_tag = get_note()->get_tag_table()->get_broken_link_tag();
   }
 
+  void AppLinkWatcher::initialize()
+  {
+    if(m_initialized) {
+      return;
+    }
+    m_initialized = true;
+    m_on_note_deleted_cid = note_manager().signal_note_deleted.connect(
+      sigc::mem_fun(*this, &AppLinkWatcher::on_note_deleted));
+    m_on_note_added_cid = note_manager().signal_note_added.connect(
+      sigc::mem_fun(*this, &AppLinkWatcher::on_note_added));
+    m_on_note_renamed_cid = note_manager().signal_note_renamed.connect(
+      sigc::mem_fun(*this, &AppLinkWatcher::on_note_renamed));
+  }
 
-  void NoteLinkWatcher::shutdown ()
+  void AppLinkWatcher::shutdown()
   {
+    m_initialized = false;
     m_on_note_deleted_cid.disconnect();
     m_on_note_added_cid.disconnect();
     m_on_note_renamed_cid.disconnect();
   }
 
+  bool AppLinkWatcher::initialized()
+  {
+    return m_initialized;
+  }
 
-  void NoteLinkWatcher::on_note_opened ()
+  void AppLinkWatcher::on_note_added(const NoteBase::Ptr & added)
   {
-    // NOTE: This avoid multiple link opens
-    // now that notes always perform TagTable
-    // sharing.  This is because if the TagTable is shared,
-    // we will connect to the same Tag's event source each
-    // time a note is opened, and get called multiple times
-    // for each button press.  Fixes bug #305813.
-    if (!s_text_event_connected) {
-      m_link_tag->signal_activate().connect(
-        sigc::mem_fun(*this, &NoteLinkWatcher::on_link_tag_activated));
-      m_broken_link_tag->signal_activate().connect(
-        sigc::mem_fun(*this, &NoteLinkWatcher::on_link_tag_activated));
-      s_text_event_connected = true;
+    for(auto & note : note_manager().get_notes()) {
+      if(added == note) {
+        continue;
+      }
+
+      if(!contains_text(note, added->get_title())) {
+        continue;
+      }
+
+      // Highlight previously unlinked text
+      auto n = std::static_pointer_cast<Note>(note);
+      auto buffer = n->get_buffer();
+      highlight_in_block(note_manager(), n, buffer->begin(), buffer->end());
     }
-    get_buffer()->signal_insert().connect(
-      sigc::mem_fun(*this, &NoteLinkWatcher::on_insert_text));
-    get_buffer()->signal_apply_tag().connect(
-      sigc::mem_fun(*this, &NoteLinkWatcher::on_apply_tag));
-    get_buffer()->signal_erase().connect(
-      sigc::mem_fun(*this, &NoteLinkWatcher::on_delete_range));
   }
 
-  
-  bool NoteLinkWatcher::contains_text(const Glib::ustring & text)
+  void AppLinkWatcher::on_note_deleted(const NoteBase::Ptr & deleted)
   {
-    Glib::ustring body = get_note()->text_content().lowercase();
-    Glib::ustring match = text.lowercase();
+    auto link_tag = std::static_pointer_cast<Note>(deleted)->get_tag_table()->get_link_tag();
+    auto broken_link_tag = std::static_pointer_cast<Note>(deleted)->get_tag_table()->get_broken_link_tag();
 
-    return body.find(match) != Glib::ustring::npos;
-  }
+    for(auto & note : note_manager().get_notes()) {
+      if(deleted == note) {
+        continue;
+      }
 
+      if(!contains_text(note, deleted->get_title())) {
+        continue;
+      }
 
-  void NoteLinkWatcher::on_note_added(const NoteBase::Ptr & added)
-  {
-    if (added == get_note()) {
-      return;
-    }
+      Glib::ustring old_title_lower = deleted->get_title().lowercase();
+      auto buffer = std::static_pointer_cast<Note>(note)->get_buffer();
 
-    if (!contains_text (added->get_title())) {
-      return;
-    }
+      // Turn all link:internal to link:broken for the deleted note.
+      utils::TextTagEnumerator enumerator(buffer, link_tag);
+      while(enumerator.move_next()) {
+        const utils::TextRange & range(enumerator.current());
+        if(enumerator.current().text().lowercase() != old_title_lower)
+          continue;
 
-    // Highlight previously unlinked text
-    highlight_in_block (get_buffer()->begin(), get_buffer()->end());
+        buffer->remove_tag(link_tag, range.start(), range.end());
+        buffer->apply_tag(broken_link_tag, range.start(), range.end());
+      }
+    }
   }
 
-  void NoteLinkWatcher::on_note_deleted(const NoteBase::Ptr & deleted)
+  void AppLinkWatcher::on_note_renamed(const NoteBase::Ptr & renamed, const Glib::ustring & /*old_title*/)
   {
-    if (deleted == get_note()) {
-      return;
-    }
+    for(auto & note : note_manager().get_notes()) {
+      if(renamed == note) {
+        continue;
+      }
 
-    if (!contains_text (deleted->get_title())) {
-      return;
+      // Highlight previously unlinked text
+      if(contains_text(note, renamed->get_title())) {
+        auto n = std::static_pointer_cast<Note>(note);
+        auto buffer = n->get_buffer();
+        highlight_note_in_block(note_manager(), n, std::static_pointer_cast<Note>(renamed), buffer->begin(), 
buffer->end());
+      }
     }
+  }
 
-    Glib::ustring old_title_lower = deleted->get_title().lowercase();
+  bool AppLinkWatcher::contains_text(const NoteBase::Ptr & note, const Glib::ustring & text)
+  {
+    Glib::ustring body = std::static_pointer_cast<Note>(note)->text_content().lowercase();
+    Glib::ustring match = text.lowercase();
 
-    // Turn all link:internal to link:broken for the deleted note.
-    utils::TextTagEnumerator enumerator(get_buffer(), m_link_tag);
-    while (enumerator.move_next()) {
-      const utils::TextRange & range(enumerator.current());
-      if (enumerator.current().text().lowercase() != old_title_lower)
-        continue;
+    return body.find(match) != Glib::ustring::npos;
+  }
 
-      get_buffer()->remove_tag (m_link_tag, range.start(), range.end());
-      get_buffer()->apply_tag (m_broken_link_tag, range.start(), range.end());
+  void AppLinkWatcher::highlight_in_block(NoteManagerBase & note_manager, const Note::Ptr & note, const 
Gtk::TextIter & start, const Gtk::TextIter & end)
+  {
+    TrieHit<NoteBase::WeakPtr>::ListPtr hits = note_manager.find_trie_matches(start.get_slice(end));
+    for(TrieHit<NoteBase::WeakPtr>::List::const_iterator iter = hits->begin(); iter != hits->end(); ++iter) {
+      do_highlight(note_manager, note, **iter, start, end);
     }
   }
 
-
-  void NoteLinkWatcher::on_note_renamed(const NoteBase::Ptr& renamed, const Glib::ustring& /*old_title*/)
+  void AppLinkWatcher::highlight_note_in_block(NoteManagerBase & note_manager, const Note::Ptr & note, const 
NoteBase::Ptr & find_note, const Gtk::TextIter & start, const Gtk::TextIter & end)
   {
-    if (renamed == get_note()) {
-      return;
-    }
+    Glib::ustring buffer_text = start.get_text(end).lowercase();
+    Glib::ustring find_title_lower = find_note->get_title().lowercase();
+    int idx = 0;
 
-    // Highlight previously unlinked text
-    if (contains_text (renamed->get_title())) {
-      highlight_note_in_block(std::static_pointer_cast<Note>(renamed), get_buffer()->begin(), 
get_buffer()->end());
+    while (true) {
+      idx = buffer_text.find(find_title_lower, idx);
+      if (idx < 0)
+        break;
+
+      TrieHit<NoteBase::WeakPtr> hit(idx, idx + find_title_lower.length(), find_title_lower, find_note);
+      do_highlight(note_manager, note, hit, start, end);
+
+      idx += find_title_lower.length();
     }
   }
 
-  
-  void NoteLinkWatcher::do_highlight(const TrieHit<NoteBase::WeakPtr> & hit,
-                                     const Gtk::TextIter & start,
-                                     const Gtk::TextIter &)
+  void AppLinkWatcher::do_highlight(NoteManagerBase & note_manager, const Note::Ptr & note, const 
TrieHit<NoteBase::WeakPtr> & hit, const Gtk::TextIter & start, const Gtk::TextIter &)
   {
     // Some of these checks should be replaced with fixes to
     // TitleTrie.FindMatches, probably.
-    if (hit.value().expired()) {
+    if(hit.value().expired()) {
       DBG_OUT("DoHighlight: null pointer error for '%s'." , hit.key().c_str());
       return;
     }
       
-    if (!manager().find(hit.key())) {
-      DBG_OUT ("DoHighlight: '%s' links to non-existing note." ,
-               hit.key().c_str());
+    if(!note_manager.find(hit.key())) {
+      DBG_OUT("DoHighlight: '%s' links to non-existing note." , hit.key().c_str());
       return;
     }
       
     NoteBase::Ptr hit_note(hit.value());
 
-    if (hit.key().lowercase() != hit_note->get_title().lowercase()) { // == 0 if same string
-      DBG_OUT ("DoHighlight: '%s' links wrongly to note '%s'." ,
-               hit.key().c_str(),
-               hit_note->get_title().c_str());
+    if(hit.key().lowercase() != hit_note->get_title().lowercase()) { // == 0 if same string
+      DBG_OUT("DoHighlight: '%s' links wrongly to note '%s'." , hit.key().c_str(), 
hit_note->get_title().c_str());
       return;
     }
       
-    if (hit_note == get_note())
+    if(hit_note == note)
       return;
 
     Gtk::TextIter title_start = start;
-    title_start.forward_chars (hit.start());
+    title_start.forward_chars(hit.start());
 
     Gtk::TextIter title_end = start;
-    title_end.forward_chars (hit.end());
+    title_end.forward_chars(hit.end());
 
     // Only link against whole words/phrases
-    if ((!title_start.starts_word () && !title_start.starts_sentence ()) ||
+    if((!title_start.starts_word() && !title_start.starts_sentence()) ||
         (!title_end.ends_word() && !title_end.ends_sentence())) {
       return;
     }
 
     // Don't create links inside URLs
-    if(get_note()->get_tag_table()->has_link_tag(title_start)) {
+    if(note->get_tag_table()->has_link_tag(title_start)) {
       return;
     }
 
-    DBG_OUT ("Matching Note title '%s' at %d-%d...",
-             hit.key().c_str(), hit.start(), hit.end());
+    DBG_OUT("Matching Note title '%s' at %d-%d...", hit.key().c_str(), hit.start(), hit.end());
 
-    get_note()->get_tag_table()->foreach(
-      [this, title_start, title_end](const Glib::RefPtr<Gtk::TextTag> & tag) {
-        remove_link_tag(tag, title_start, title_end);
+    auto link_tag = note->get_tag_table()->get_link_tag();
+    note->get_tag_table()->foreach(
+      [note, title_start, title_end](const Glib::RefPtr<Gtk::TextTag> & tag) {
+        remove_link_tag(note, tag, title_start, title_end);
     });
-    get_buffer()->apply_tag (m_link_tag, title_start, title_end);
+    note->get_buffer()->apply_tag(link_tag, title_start, title_end);
   }
 
-  void NoteLinkWatcher::remove_link_tag(const Glib::RefPtr<Gtk::TextTag> & tag,
-                                        const Gtk::TextIter & start, const Gtk::TextIter & end)
+  void AppLinkWatcher::remove_link_tag(const Note::Ptr & note, const Glib::RefPtr<Gtk::TextTag> & tag, const 
Gtk::TextIter & start, const Gtk::TextIter & end)
   {
     NoteTag::Ptr note_tag = NoteTag::Ptr::cast_dynamic(tag);
-    if (note_tag && note_tag->can_activate()) {
-      get_buffer()->remove_tag(note_tag, start, end);
+    if(note_tag && note_tag->can_activate()) {
+      note->get_buffer()->remove_tag(note_tag, start, end);
     }
   }
 
-  void NoteLinkWatcher::highlight_note_in_block (const NoteBase::Ptr & find_note,
-                                                 const Gtk::TextIter & start,
-                                                 const Gtk::TextIter & end)
+
+  ////////////////////////////////////////////////////////////////////////
+
+  bool NoteLinkWatcher::s_text_event_connected = false;
+
+  NoteAddin * NoteLinkWatcher::create()
   {
-    Glib::ustring buffer_text = start.get_text(end).lowercase();
-    Glib::ustring find_title_lower = find_note->get_title().lowercase();
-    int idx = 0;
+    return new NoteLinkWatcher;
+  }
 
-    while (true) {
-      idx = buffer_text.find(find_title_lower, idx);
-      if (idx < 0)
-        break;
 
-      TrieHit<NoteBase::WeakPtr> hit(idx, idx + find_title_lower.length(),
-                             find_title_lower, find_note);
-      do_highlight (hit, start, end);
+  void NoteLinkWatcher::initialize ()
+  {
+    m_link_tag = get_note()->get_tag_table()->get_link_tag();
+    m_broken_link_tag = get_note()->get_tag_table()->get_broken_link_tag();
+  }
 
-      idx += find_title_lower.length();
-    }
 
+  void NoteLinkWatcher::shutdown ()
+  {
   }
 
 
+  void NoteLinkWatcher::on_note_opened ()
+  {
+    // NOTE: This avoid multiple link opens
+    // now that notes always perform TagTable
+    // sharing.  This is because if the TagTable is shared,
+    // we will connect to the same Tag's event source each
+    // time a note is opened, and get called multiple times
+    // for each button press.  Fixes bug #305813.
+    if (!s_text_event_connected) {
+      m_link_tag->signal_activate().connect(
+        sigc::mem_fun(*this, &NoteLinkWatcher::on_link_tag_activated));
+      m_broken_link_tag->signal_activate().connect(
+        sigc::mem_fun(*this, &NoteLinkWatcher::on_link_tag_activated));
+      s_text_event_connected = true;
+    }
+    get_buffer()->signal_insert().connect(
+      sigc::mem_fun(*this, &NoteLinkWatcher::on_insert_text));
+    get_buffer()->signal_apply_tag().connect(
+      sigc::mem_fun(*this, &NoteLinkWatcher::on_apply_tag));
+    get_buffer()->signal_erase().connect(
+      sigc::mem_fun(*this, &NoteLinkWatcher::on_delete_range));
+  }
+
+
+  void NoteLinkWatcher::do_highlight(const TrieHit<NoteBase::WeakPtr> & hit, const Gtk::TextIter & start, 
const Gtk::TextIter & end)
+  {
+    AppLinkWatcher::do_highlight(manager(), get_note(), hit, start, end);
+  }
+
   void NoteLinkWatcher::highlight_in_block(const Gtk::TextIter & start,
                                            const Gtk::TextIter & end)
   {
-    TrieHit<NoteBase::WeakPtr>::ListPtr hits = manager().find_trie_matches (start.get_slice (end));
-    for(TrieHit<NoteBase::WeakPtr>::List::const_iterator iter = hits->begin();
-        iter != hits->end(); ++iter) {
-      do_highlight (**iter, start, end);
-    }
+    AppLinkWatcher::highlight_in_block(manager(), get_note(), start, end);
   }
 
   void NoteLinkWatcher::unhighlight_in_block(const Gtk::TextIter & start,
diff --git a/src/watchers.hpp b/src/watchers.hpp
index cce0a5df..b74fc3f7 100644
--- a/src/watchers.hpp
+++ b/src/watchers.hpp
@@ -1,7 +1,7 @@
 /*
  * gnote
  *
- * Copyright (C) 2010-2015,2017,2019 Aurimas Cernius
+ * Copyright (C) 2010-2015,2017,2019-2020 Aurimas Cernius
  * Copyright (C) 2009 Hubert Figuiere
  *
  * This program is free software: you can redistribute it and/or modify
@@ -39,6 +39,7 @@ extern "C" {
 #include <gtkmm/textiter.h>
 #include <gtkmm/texttag.h>
 
+#include "applicationaddin.hpp"
 #include "noteaddin.hpp"
 #include "triehit.hpp"
 #include "utils.hpp"
@@ -47,6 +48,7 @@ namespace gnote {
 
   class Preferences;
   class NoteEditor;
+  class NoteManagerBase;
   class NoteTag;
 
   class NoteRenameWatcher
@@ -177,6 +179,33 @@ namespace gnote {
   };
 
 
+  class AppLinkWatcher
+    : public ApplicationAddin
+  {
+  public:
+    static ApplicationAddin *create();
+    static void highlight_in_block(NoteManagerBase &, const Note::Ptr &, const Gtk::TextIter &, const 
Gtk::TextIter &);
+    static void do_highlight(NoteManagerBase &, const Note::Ptr &, const TrieHit<NoteBase::WeakPtr> &, const 
Gtk::TextIter & ,const Gtk::TextIter &);
+    static void remove_link_tag(const Note::Ptr & note, const Glib::RefPtr<Gtk::TextTag> & tag, const 
Gtk::TextIter & start, const Gtk::TextIter & end);
+
+    AppLinkWatcher();
+    virtual void initialize() override;
+    virtual void shutdown() override;
+    virtual bool initialized() override;
+  private:
+    static bool contains_text(const NoteBase::Ptr & note, const Glib::ustring & text);
+    static void highlight_note_in_block(NoteManagerBase &, const Note::Ptr &, const NoteBase::Ptr &, const 
Gtk::TextIter &, const Gtk::TextIter &);
+    void on_note_added(const NoteBase::Ptr &);
+    void on_note_deleted(const NoteBase::Ptr &);
+    void on_note_renamed(const NoteBase::Ptr&, const Glib::ustring&);
+
+    bool m_initialized;
+    sigc::connection m_on_note_deleted_cid;
+    sigc::connection m_on_note_added_cid;
+    sigc::connection m_on_note_renamed_cid;
+  };
+
+
   class NoteLinkWatcher
     : public NoteAddin
   {
@@ -187,21 +216,13 @@ namespace gnote {
     virtual void on_note_opened() override;
 
   private:
-    bool contains_text(const Glib::ustring & text);
-    void on_note_added(const NoteBase::Ptr &);
-    void on_note_deleted(const NoteBase::Ptr &);
-    void on_note_renamed(const NoteBase::Ptr&, const Glib::ustring&);
     void do_highlight(const TrieHit<NoteBase::WeakPtr> & , const Gtk::TextIter &,const Gtk::TextIter &);
-    void highlight_note_in_block (const NoteBase::Ptr &, const Gtk::TextIter &,
-                                  const Gtk::TextIter &);
     void highlight_in_block(const Gtk::TextIter &,const Gtk::TextIter &);
     void unhighlight_in_block(const Gtk::TextIter &,const Gtk::TextIter &);
     void on_delete_range(const Gtk::TextIter &,const Gtk::TextIter &);
     void on_insert_text(const Gtk::TextIter &, const Glib::ustring &, int);
     void on_apply_tag(const Glib::RefPtr<Gtk::TextBuffer::Tag> & tag,
                       const Gtk::TextIter & start, const Gtk::TextIter &end);
-    void remove_link_tag(const Glib::RefPtr<Gtk::TextTag> & tag,
-                         const Gtk::TextIter & start, const Gtk::TextIter & end);
 
     bool open_or_create_link(const NoteEditor &, const Gtk::TextIter &,const Gtk::TextIter &);
     bool on_link_tag_activated(const NoteEditor &,
@@ -210,9 +231,6 @@ namespace gnote {
     NoteTag::Ptr m_link_tag;
     NoteTag::Ptr m_broken_link_tag;
 
-    sigc::connection m_on_note_deleted_cid;
-    sigc::connection m_on_note_added_cid;
-    sigc::connection m_on_note_renamed_cid;
     static bool s_text_event_connected;
   };
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]