[gnote] Implement template bar



commit ca0333f3af313f3ac961871c5b0c9a11b934a428
Author: Aurimas Äernius <aurisc4 gmail com>
Date:   Sun Oct 16 23:06:16 2011 +0300

    Implement template bar
    
    Ported Tomboy feature.
    This adds to template note a template bar with the following:
    - Button to convert to regular note
    - Checkbox to save size
    - Checkbox to save selection
    - Checkbox to save title

 src/note.cpp                        |  162 ++++++-----------------------------
 src/note.hpp                        |  145 ++++++++++++++++++++++++++++++-
 src/notebooks/notebook.cpp          |   10 +-
 src/notebooks/notebooknoteaddin.cpp |   48 +++++++++-
 src/notebooks/notebooknoteaddin.hpp |    8 ++-
 src/notemanager.cpp                 |  130 +++++++++++++++++++++-------
 src/notemanager.hpp                 |    6 +-
 src/notewindow.cpp                  |  104 ++++++++++++++++++++++
 src/notewindow.hpp                  |   18 ++++
 src/tagmanager.cpp                  |    3 +
 src/tagmanager.hpp                  |    3 +
 src/watchers.cpp                    |    4 +-
 12 files changed, 458 insertions(+), 183 deletions(-)
---
diff --git a/src/note.cpp b/src/note.cpp
index 7590230..527b0b4 100644
--- a/src/note.cpp
+++ b/src/note.cpp
@@ -124,141 +124,13 @@ namespace gnote {
 
   }
 
-  class NoteData
-  {
-  public:
-    typedef std::map<std::string, Tag::Ptr> TagMap;
-    NoteData(const std::string & _uri);
-
-    const std::string & uri() const
-      {
-        return m_uri;
-      }
-    const std::string & title() const
-      {
-        return m_title;
-      }
-    std::string & title()
-      {
-        return m_title;
-      }
-    const std::string & text() const
-      { 
-        return m_text;
-      }
-    std::string & text()
-      { 
-        return m_text;
-      }
-    const sharp::DateTime & create_date() const
-      {
-        return m_create_date;
-      }
-    sharp::DateTime & create_date()
-      {
-        return m_create_date;
-      }
-    const sharp::DateTime & change_date() const
-      {
-        return m_change_date;
-      }
-    void set_change_date(const sharp::DateTime & date)
-      {
-        m_change_date = date;
-        m_metadata_change_date = date;
-      }
-    const sharp::DateTime & metadata_change_date() const
-      {
-        return m_metadata_change_date;
-      }
-    sharp::DateTime & metadata_change_date()
-      {
-        return m_metadata_change_date;
-      }
-    int cursor_position() const
-      {
-        return m_cursor_pos;
-      }
-    void set_cursor_position(int new_pos)
-      {
-        m_cursor_pos = new_pos;
-      }
-    int width() const
-      {
-        return m_width;
-      }
-    int & width()
-      {
-        return m_width;
-      }
-    int height() const
-      {
-        return m_height;
-      }
-    int & height()
-      {
-        return m_height;
-      }
-    int x() const
-      {
-        return m_x;
-      }
-    int & x()
-      {
-        return m_x;
-      }
-    int y() const
-      {
-        return m_y;
-      }
-    int & y()
-      {
-        return m_y;
-      }
-    const TagMap & tags() const
-      {
-        return m_tags;
-      }
-    TagMap & tags()
-      {
-        return m_tags;
-      }
-    
-    bool is_open_on_startup() const
-      {
-        return m_open_on_startup;
-      }
-    void set_is_open_on_startup(bool v)
-      {
-        m_open_on_startup = v;
-      }
-    void set_position_extent(int x, int y, int width, int height);
-    bool has_position();
-    bool has_extent();
-
-  private:
-    const std::string m_uri;
-    std::string       m_title;
-    std::string       m_text;
-    sharp::DateTime             m_create_date;
-    sharp::DateTime             m_change_date;
-    sharp::DateTime             m_metadata_change_date;
-    int               m_cursor_pos;
-    int               m_width, m_height;
-    int               m_x, m_y;
-    bool              m_open_on_startup;
-
-    TagMap m_tags;
-  
-    static const int  s_noPosition;
-  };
-
 
   const int  NoteData::s_noPosition = -1;
 
   NoteData::NoteData(const std::string & _uri)
     : m_uri(_uri)
     , m_cursor_pos(0)
+    , m_selection_bound_pos(s_noPosition)
     , m_width(0)
     , m_height(0)
     , m_x(s_noPosition)
@@ -364,6 +236,13 @@ namespace gnote {
       }
       m_buffer->place_cursor(cursor);
 
+      if(m_data->selection_bound_position() >= 0) {
+        // Move selection bound to last-saved position
+        Gtk::TextIter selection_bound;
+        selection_bound = m_buffer->get_iter_at_offset(m_data->selection_bound_position());
+        m_buffer->move_mark(m_buffer->get_selection_bound(), selection_bound);
+      }
+
       // New events should create Undo actions
       m_buffer->undoer().thaw_undo ();
     }
@@ -565,14 +444,20 @@ namespace gnote {
     }
   }
 
-  void Note::on_buffer_insert_mark_set(const Gtk::TextBuffer::iterator & iter,
-                                       const Glib::RefPtr<Gtk::TextBuffer::Mark> & insert)
+  void Note::on_buffer_mark_set(const Gtk::TextBuffer::iterator & iter,
+                                const Glib::RefPtr<Gtk::TextBuffer::Mark> & insert)
   {
-    if(insert != m_buffer->get_insert()) {
+    if(insert == m_buffer->get_insert()) {
+      m_data.data().set_cursor_position(iter.get_offset());
+    }
+    else if(insert == m_buffer->get_selection_bound()) {
+      m_data.data().set_selection_bound_position(iter.get_offset());
+    }
+    else {
       return;
     }
-    m_data.data().set_cursor_position(iter.get_offset());
-    DBG_OUT("BufferInsertSetMark queueing save");
+
+    DBG_OUT("OnBufferSetMark queueing save");
     queue_save(NO_CHANGE);
   }
 
@@ -1126,7 +1011,7 @@ namespace gnote {
       m_buffer->signal_remove_tag().connect(
         sigc::mem_fun(*this, &Note::on_buffer_tag_removed));
       m_buffer->signal_mark_set().connect(
-        sigc::mem_fun(*this, &Note::on_buffer_insert_mark_set));
+        sigc::mem_fun(*this, &Note::on_buffer_mark_set));
     }
     return m_buffer;
   }
@@ -1275,6 +1160,9 @@ namespace gnote {
         else if(name == "cursor-position") {
           note->set_cursor_position(boost::lexical_cast<int>(xml.read_string()));
         }
+        else if(name == "selection-bound-position") {
+          note->set_selection_bound_position(boost::lexical_cast<int>(xml.read_string()));
+        }
         else if(name == "width") {
           note->width() = boost::lexical_cast<int>(xml.read_string());
         }
@@ -1423,6 +1311,10 @@ namespace gnote {
     xml.write_string (boost::lexical_cast<std::string>(note.cursor_position()));
     xml.write_end_element ();
 
+    xml.write_start_element("", "selection-bound-position", "");
+    xml.write_string(boost::lexical_cast<std::string>(note.selection_bound_position()));
+    xml.write_end_element();
+
     xml.write_start_element ("", "width", "");
     xml.write_string (boost::lexical_cast<std::string>(note.width()));
     xml.write_end_element ();
diff --git a/src/note.hpp b/src/note.hpp
index 8f1659a..71ee475 100644
--- a/src/note.hpp
+++ b/src/note.hpp
@@ -48,10 +48,149 @@ namespace gnote {
 
 class NoteManager;
 
-class NoteData;
 class NoteWindow;
 class NoteTagTable;
 
+
+class NoteData
+{
+public:
+  typedef std::map<std::string, Tag::Ptr> TagMap;
+  NoteData(const std::string & _uri);
+
+  const std::string & uri() const
+    {
+      return m_uri;
+    }
+  const std::string & title() const
+    {
+      return m_title;
+    }
+  std::string & title()
+    {
+      return m_title;
+    }
+  const std::string & text() const
+    { 
+      return m_text;
+    }
+  std::string & text()
+    { 
+      return m_text;
+    }
+  const sharp::DateTime & create_date() const
+    {
+      return m_create_date;
+    }
+  sharp::DateTime & create_date()
+    {
+      return m_create_date;
+    }
+  const sharp::DateTime & change_date() const
+    {
+      return m_change_date;
+    }
+  void set_change_date(const sharp::DateTime & date)
+    {
+      m_change_date = date;
+      m_metadata_change_date = date;
+    }
+  const sharp::DateTime & metadata_change_date() const
+    {
+      return m_metadata_change_date;
+    }
+  sharp::DateTime & metadata_change_date()
+    {
+      return m_metadata_change_date;
+    }
+  int cursor_position() const
+    {
+      return m_cursor_pos;
+    }
+  void set_cursor_position(int new_pos)
+    {
+      m_cursor_pos = new_pos;
+    }
+  int selection_bound_position() const
+    {
+      return m_selection_bound_pos;
+    }
+  void set_selection_bound_position(int pos)
+    {
+      m_selection_bound_pos = pos;
+    }
+  int width() const
+    {
+      return m_width;
+    }
+  int & width()
+    {
+      return m_width;
+    }
+  int height() const
+    {
+      return m_height;
+    }
+  int & height()
+    {
+      return m_height;
+    }
+  int x() const
+    {
+      return m_x;
+    }
+  int & x()
+    {
+      return m_x;
+    }
+  int y() const
+    {
+      return m_y;
+    }
+  int & y()
+    {
+      return m_y;
+    }
+  const TagMap & tags() const
+    {
+      return m_tags;
+    }
+  TagMap & tags()
+    {
+      return m_tags;
+    }
+
+  bool is_open_on_startup() const
+    {
+      return m_open_on_startup;
+    }
+  void set_is_open_on_startup(bool v)
+    {
+      m_open_on_startup = v;
+    }
+  void set_position_extent(int x, int y, int width, int height);
+  bool has_position();
+  bool has_extent();
+
+private:
+  const std::string m_uri;
+  std::string       m_title;
+  std::string       m_text;
+  sharp::DateTime             m_create_date;
+  sharp::DateTime             m_change_date;
+  sharp::DateTime             m_metadata_change_date;
+  int               m_cursor_pos;
+  int               m_selection_bound_pos;
+  int               m_width, m_height;
+  int               m_x, m_y;
+  bool              m_open_on_startup;
+
+  TagMap m_tags;
+ 
+  static const int  s_noPosition;
+};
+
+
 class NoteDataBufferSynchronizer
 {
 public:
@@ -230,8 +369,8 @@ private:
   void on_buffer_tag_removed(const Glib::RefPtr<Gtk::TextTag> &tag,
                              const Gtk::TextBuffer::iterator &, 
                              const Gtk::TextBuffer::iterator &);
-  void on_buffer_insert_mark_set(const Gtk::TextBuffer::iterator & iter,
-                                 const Glib::RefPtr<Gtk::TextBuffer::Mark> & insert);
+  void on_buffer_mark_set(const Gtk::TextBuffer::iterator & iter,
+                          const Glib::RefPtr<Gtk::TextBuffer::Mark> & insert);
   bool on_window_configure(GdkEventConfigure *ev);
   bool on_window_destroyed(GdkEventAny *ev);
   void on_save_timeout();
diff --git a/src/notebooks/notebook.cpp b/src/notebooks/notebook.cpp
index 746215e..61f7d5a 100644
--- a/src/notebooks/notebook.cpp
+++ b/src/notebooks/notebook.cpp
@@ -160,15 +160,15 @@ namespace notebooks {
 
   Note::Ptr Notebook::create_notebook_note()
   {
-    Note::Ptr template_note = find_template_note();
+    std::string temp_title;
+    Note::Ptr note_template = get_template_note();
     NoteManager & note_manager = Gnote::obj().default_note_manager();
 
-    Note::Ptr note = note_manager.create();
-    if(template_note)
-      NoteManager::replace_body_if_differ(note, template_note);
+    temp_title = note_manager.get_unique_name(_("New Note"), note_manager.get_notes().size());
+    Note::Ptr note = note_manager.create_note_from_template(temp_title, note_template);
 
     // Add the notebook tag
-    note->add_tag (m_tag);
+    note->add_tag(m_tag);
 
     return note;
   }
diff --git a/src/notebooks/notebooknoteaddin.cpp b/src/notebooks/notebooknoteaddin.cpp
index a4701bc..250edbf 100644
--- a/src/notebooks/notebooknoteaddin.cpp
+++ b/src/notebooks/notebooknoteaddin.cpp
@@ -38,6 +38,7 @@ namespace notebooks {
   bool               NotebookNoteAddin::s_static_inited = false;
   Glib::RefPtr<Gdk::Pixbuf> NotebookNoteAddin::s_notebookIcon;
   Glib::RefPtr<Gdk::Pixbuf> NotebookNoteAddin::s_newNotebookIcon;
+  Tag::Ptr           NotebookNoteAddin::s_templateTag;
 
   void NotebookNoteAddin::_init_static()
   {
@@ -47,6 +48,15 @@ namespace notebooks {
       s_static_inited = true;
     }
   }
+
+
+  Tag::Ptr NotebookNoteAddin::get_template_tag()
+  {
+    if(!s_templateTag) {
+      s_templateTag = TagManager::obj().get_or_create_system_tag(TagManager::TEMPLATE_NOTE_SYSTEM_TAG);
+    }
+    return s_templateTag;
+  }
   
 
   NotebookNoteAddin::NotebookNoteAddin()
@@ -85,6 +95,30 @@ namespace notebooks {
       .connect(sigc::mem_fun(*this, &NotebookNoteAddin::on_note_added_to_notebook));
     m_note_removed_cid = NotebookManager::instance().signal_note_removed_from_notebook()
       .connect(sigc::mem_fun(*this, &NotebookNoteAddin::on_note_removed_from_notebook));
+
+    get_note()->signal_tag_added()
+      .connect(sigc::mem_fun(*this, &NotebookNoteAddin::on_note_tag_added));
+
+    // TODO: Make sure this is handled in NotebookNoteAddin, too
+    get_note()->signal_tag_removed()
+      .connect(sigc::mem_fun(*this, &NotebookNoteAddin::on_note_tag_removed));
+  }
+
+
+  void NotebookNoteAddin::on_note_tag_added(const Note & note, const Tag::Ptr & tag)
+  {
+    Note::Ptr taggedNote = const_cast<Note&>(note).shared_from_this();
+    if(taggedNote == get_note() && tag == get_template_tag()) {
+      update_button_sensitivity(true);
+    }
+  }
+
+
+  void NotebookNoteAddin::on_note_tag_removed(const Note::Ptr & taggedNote, const std::string & tag)
+  {
+    if(taggedNote == get_note() && tag == get_template_tag()->normalized_name()) {
+      update_button_sensitivity(false);
+    }
   }
 
 
@@ -105,15 +139,19 @@ namespace notebooks {
     }
     if(!m_toolButton) {
       initialize_tool_button();
+      update_button_sensitivity(get_note()->contains_tag(get_template_tag()));
+    }
+  }
 
-      // Disable the notebook button if this note is a template note
-      Tag::Ptr templateTag = TagManager::obj().get_or_create_system_tag (TagManager::TEMPLATE_NOTE_SYSTEM_TAG);
-      if (get_note()->contains_tag (templateTag)) {
-        m_toolButton->set_sensitive(false);
-      }
+
+  void NotebookNoteAddin::update_button_sensitivity(bool isTemplate)
+  {
+    if(m_toolButton) {
+      m_toolButton->set_sensitive(!isTemplate);
     }
   }
 
+
   void NotebookNoteAddin::on_menu_shown()
   {
     update_menu();
diff --git a/src/notebooks/notebooknoteaddin.hpp b/src/notebooks/notebooknoteaddin.hpp
index dbff554..a75b808 100644
--- a/src/notebooks/notebooknoteaddin.hpp
+++ b/src/notebooks/notebooknoteaddin.hpp
@@ -1,6 +1,7 @@
 /*
  * gnote
  *
+ * Copyright (C) 2011 Aurimas Cernius
  * Copyright (C) 2009 Hubert Figuiere
  *
  * This program is free software: you can redistribute it and/or modify
@@ -39,7 +40,8 @@ namespace notebooks {
     : public NoteAddin
   {
   public:
-    static NoteAddin * create();    
+    static NoteAddin * create();
+    static Tag::Ptr get_template_tag();
     virtual void initialize ();
     virtual void shutdown ();
     virtual void on_note_opened ();
@@ -49,6 +51,9 @@ namespace notebooks {
 
   private:
     void initialize_tool_button();
+    void on_note_tag_added(const Note &, const Tag::Ptr &);
+    void on_note_tag_removed(const Note::Ptr &, const std::string &);
+    void update_button_sensitivity(bool);
     void on_menu_shown();
     void on_note_added_to_notebook(const Note &, const Notebook::Ptr &);
     void on_note_removed_from_notebook(const Note &, const Notebook::Ptr &);
@@ -69,6 +74,7 @@ namespace notebooks {
     static bool               s_static_inited;
     static Glib::RefPtr<Gdk::Pixbuf> s_notebookIcon;
     static Glib::RefPtr<Gdk::Pixbuf> s_newNotebookIcon;
+    static Tag::Ptr           s_templateTag;
   };
 
 }
diff --git a/src/notemanager.cpp b/src/notemanager.cpp
index fbf9f58..061eb2c 100644
--- a/src/notemanager.cpp
+++ b/src/notemanager.cpp
@@ -548,44 +548,28 @@ namespace gnote {
   Note::Ptr NoteManager::create_new_note (std::string title, const std::string & guid)
   {
     std::string body;
+
     title = split_title_from_content (title, body);
       
     if (title.empty()) {
       title = get_unique_name(_("New Note"), m_notes.size());
     }
 
+    Note::Ptr template_note = get_or_create_template_note();
+
     if (body.empty()) {
-      std::string content = get_note_template_content(title);
-      Note::Ptr new_note = create_new_note (title, content, guid);
-      new_note->get_buffer()->select_note_body();
-      // Use the body from the template note
-      Note::Ptr template_note = find_template_note();
-      if(template_note)
-        replace_body_if_differ(new_note, template_note);
-      return new_note;
+      return create_note_from_template(title, template_note, guid);
     }
 
-    Glib::ustring header = title + "\n\n";
-    std::string content =
-      boost::str(boost::format("<note-content>%1%%2%</note-content>") %
-                 utils::XmlEncoder::encode (header) 
-                 % utils::XmlEncoder::encode (body));
-    return create_new_note (title, content, guid);
-  }
+    // Use a simple "Describe..." body and highlight
+    // it so it can be easily overwritten
+    std::string content = get_note_template_content(title);
+    Note::Ptr new_note = create_new_note (title, content, guid);
 
-  //replace dest body by src one, if the text is different
-  void NoteManager::replace_body_if_differ(Note::Ptr dest, const Note::Ptr src)
-  {
-    std::string dest_body = sharp::string_trim(sharp::string_replace_first(dest->text_content(),
-                                                                           dest->get_title(), ""));
-    std::string src_body = sharp::string_trim(sharp::string_replace_first(src->text_content(),
-                                                                          src->get_title(), ""));
-    if(dest_body != src_body) {
-      std::string xml_content = sharp::string_replace_first(src->xml_content(),
-        sharp::string_trim(utils::XmlEncoder::encode(src->get_title())),
-        sharp::string_trim(utils::XmlEncoder::encode(dest->get_title())));
-      dest->set_xml_content(xml_content);
-    }
+    // Select the inital text so typing will overwrite the body text
+    new_note->get_buffer()->select_note_body();
+
+    return new_note;
   }
 
   // Create a new note with the specified Xml content
@@ -716,9 +700,95 @@ namespace gnote {
     return Note::Ptr();
   }
 
+  std::string NoteManager::sanitize_xml_content(const std::string & xml_content)
+  {
+    std::string::size_type pos = xml_content.find('\n');
+    int i = (pos == std::string::npos) ? -1 : pos;
+    std::string result(xml_content);
+
+    while (--i >= 0) {
+      if(xml_content[i] == '\r') {
+        continue;
+      }
+
+      if(std::isspace(result[i])) {
+        result.erase(i, 1);
+      }
+      else {
+        break;
+      }
+    }
+
+    return result;
+  }
+
+  /// <summary>
+  /// Creates a new note with the given titel based on the template note.
+  /// </summary>
+  /// <param name="title">
+  /// A <see cref="System.String"/>
+  /// </param>
+  /// <param name="template_note">
+  /// A <see cref="Note"/>
+  /// </param>
+  /// <returns>
+  /// A <see cref="Note"/>
+  /// </returns>
+  Note::Ptr NoteManager::create_note_from_template(const std::string & title, const Note::Ptr & template_note)
+  {
+    return create_note_from_template(title, template_note, "");
+  }
+
+  // Creates a new note with the given title and guid with body based on
+  // the template note.
+  Note::Ptr NoteManager::create_note_from_template(const std::string & title, const Note::Ptr & template_note, const std::string & guid)
+  {
+    std::string new_title(title);
+    Tag::Ptr template_save_title = TagManager::obj().get_or_create_system_tag(TagManager::TEMPLATE_NOTE_SAVE_TITLE_SYSTEM_TAG);
+    if(template_note->contains_tag(template_save_title)) {
+      new_title = get_unique_name(template_note->get_title(), m_notes.size());
+    }
+
+    // Use the body from the template note
+    std::string xml_content = sharp::string_replace_first(template_note->xml_content(),
+                                                          utils::XmlEncoder::encode(template_note->get_title()),
+                                                          utils::XmlEncoder::encode(new_title));
+    xml_content = sanitize_xml_content(xml_content);
+
+    Note::Ptr new_note = create_new_note(new_title, xml_content, guid);
+
+    // Copy template note's properties
+    Tag::Ptr template_save_size = TagManager::obj().get_or_create_system_tag(TagManager::TEMPLATE_NOTE_SAVE_SIZE_SYSTEM_TAG);
+    if(template_note->data().has_extent() && template_note->contains_tag(template_save_size)) {
+      new_note->data().height() = template_note->data().height();
+      new_note->data().width() = template_note->data().width();
+    }
+
+    Tag::Ptr template_save_selection = TagManager::obj().get_or_create_system_tag(TagManager::TEMPLATE_NOTE_SAVE_SELECTION_SYSTEM_TAG);
+    if(template_note->data().cursor_position() > 0 && template_note->contains_tag(template_save_selection)) {
+      Glib::RefPtr<Gtk::TextBuffer> buffer = new_note->get_buffer();
+      Gtk::TextIter iter;
+
+      // Because the titles will be different between template and
+      // new note, we can't just drop the cursor at template's
+      // CursorPosition. Whitespace after the title makes this more
+      // complicated so let's just start counting from the line after the title.
+      int title_offset_difference = buffer->get_iter_at_line(1).get_offset()
+                                    - template_note->get_buffer()->get_iter_at_line(1).get_offset();
+
+      iter = buffer->get_iter_at_offset(template_note->data().cursor_position() + title_offset_difference);
+      buffer->place_cursor(iter);
+
+      iter = buffer->get_iter_at_offset(template_note->data().selection_bound_position() + title_offset_difference);
+      buffer->move_mark(buffer->get_selection_bound(), iter);
+    }
+
+    return new_note;
+  }
+
   // Find a title that does not exist using basename and id as
   // a starting point
-  std::string NoteManager::get_unique_name (std::string basename, int id) const
+  std::string NoteManager::get_unique_name(const std::string & basename, int id) const
   {
     std::string title;
     while (true) {
diff --git a/src/notemanager.hpp b/src/notemanager.hpp
index b1ea8d1..efe6e85 100644
--- a/src/notemanager.hpp
+++ b/src/notemanager.hpp
@@ -75,13 +75,14 @@ namespace gnote {
       }
     Note::Ptr find(const std::string &) const;
     Note::Ptr find_by_uri(const std::string &) const;
-    std::string get_unique_name (std::string basename, int id) const;
+    static std::string sanitize_xml_content(const std::string & xml_content);
+    Note::Ptr create_note_from_template(const std::string & title, const Note::Ptr & template_note);
+    std::string get_unique_name (const std::string & basename, int id) const;
     void delete_note(const Note::Ptr & note);
 
     Note::Ptr create();
     Note::Ptr create(const std::string & title);
     Note::Ptr create(const std::string & title, const std::string & xml_content);
-    static void replace_body_if_differ(Note::Ptr dest, const Note::Ptr src);
     // Import a note read from file_path
     // Will ensure the sanity including the unique title.
     Note::Ptr import_note(const std::string & file_path);
@@ -120,6 +121,7 @@ namespace gnote {
     Note::Ptr create_new_note (std::string title, const std::string & guid);
     Note::Ptr create_new_note (const std::string & title, const std::string & xml_content, 
                              const std::string & guid);
+    Note::Ptr create_note_from_template(const std::string & title, const Note::Ptr & template_note, const std::string & guid);
     /** add the note to the manager and setup signals */
     void add_note(const Note::Ptr &);
     void _common_init(const std::string & directory, const std::string & backup);
diff --git a/src/notewindow.cpp b/src/notewindow.cpp
index 39c9604..5e86000 100644
--- a/src/notewindow.cpp
+++ b/src/notewindow.cpp
@@ -44,6 +44,7 @@
 #include "recentchanges.hpp"
 #include "search.hpp"
 #include "actionmanager.hpp"
+#include "tagmanager.hpp"
 #include "sharp/exception.hpp"
 #include "sharp/string.hpp"
 
@@ -55,6 +56,11 @@ namespace gnote {
     , m_note(note)
     , m_global_keys(NULL)
   {
+    m_template_tag = TagManager::obj().get_or_create_system_tag(TagManager::TEMPLATE_NOTE_SYSTEM_TAG);
+    m_template_save_size_tag = TagManager::obj().get_or_create_system_tag(TagManager::TEMPLATE_NOTE_SAVE_SIZE_SYSTEM_TAG);
+    m_template_save_selection_tag = TagManager::obj().get_or_create_system_tag(TagManager::TEMPLATE_NOTE_SAVE_SELECTION_SYSTEM_TAG);
+    m_template_save_title_tag = TagManager::obj().get_or_create_system_tag(TagManager::TEMPLATE_NOTE_SAVE_TITLE_SYSTEM_TAG);
+
 //    get_window()->set_icon_name("gnote");
     set_default_size(450, 360);
     set_resizable(true);
@@ -91,6 +97,8 @@ namespace gnote {
     m_toolbar = manage(make_toolbar());
     m_toolbar->show();
 
+    m_template_widget = make_template_bar();
+
     // The main editor widget
     m_editor = manage(new NoteEditor(note.get_buffer()));
     m_editor->signal_populate_popup().connect(sigc::mem_fun(*this, &NoteWindow::on_populate_popup));
@@ -121,6 +129,7 @@ namespace gnote {
 
     Gtk::VBox *box = manage(new Gtk::VBox (false, 2));
     box->pack_start(*m_toolbar, false, false, 0);
+    box->pack_start(*m_template_widget, false, false, 0);
     box->pack_start(*m_editor_window, true, true, 0);
     box->pack_start(*m_find_bar, false, false, 0);
 
@@ -487,6 +496,101 @@ namespace gnote {
   }
 
 
+  Gtk::Box * NoteWindow::make_template_bar()
+  {
+    Gtk::VBox * bar = manage(new Gtk::VBox());
+
+    Gtk::Label * infoLabel = manage(new Gtk::Label(
+      _("This note is a template note. It determines the default content of regular notes, and will not show up in the note menu or search window.")));
+    infoLabel->set_line_wrap(true);
+
+    Gtk::Button * untemplateButton = manage(new Gtk::Button(_("Convert to regular note")));
+    untemplateButton->signal_clicked().connect(sigc::mem_fun(*this, &NoteWindow::on_untemplate_button_click));
+
+    m_save_size_check_button = manage(new Gtk::CheckButton(_("Save Si_ze"), true));
+    m_save_size_check_button->set_active(m_note.contains_tag(m_template_save_size_tag));
+    m_save_size_check_button->signal_toggled().connect(sigc::mem_fun(*this, &NoteWindow::on_save_size_check_button_toggled));
+
+    m_save_selection_check_button = manage(new Gtk::CheckButton(_("Save Se_lection"), true));
+    m_save_selection_check_button->set_active(m_note.contains_tag(m_template_save_selection_tag));
+    m_save_selection_check_button->signal_toggled().connect(sigc::mem_fun(*this, &NoteWindow::on_save_selection_check_button_toggled));
+
+    m_save_title_check_button = manage(new Gtk::CheckButton(_("Save _Title"), true));
+    m_save_title_check_button->set_active(m_note.contains_tag(m_template_save_title_tag));
+    m_save_title_check_button->signal_toggled().connect(sigc::mem_fun(*this, &NoteWindow::on_save_title_check_button_toggled));
+
+    bar->pack_start(*infoLabel);
+    bar->pack_start(*untemplateButton);
+    bar->pack_start(*m_save_size_check_button);
+    bar->pack_start(*m_save_selection_check_button);
+    bar->pack_start(*m_save_title_check_button);
+
+    if(m_note.contains_tag(m_template_tag)) {
+      bar->show_all();
+    }
+
+    m_note.signal_tag_added().connect(sigc::mem_fun(*this, &NoteWindow::on_note_tag_added));
+    m_note.signal_tag_removed().connect(sigc::mem_fun(*this, &NoteWindow::on_note_tag_removed));
+
+    return bar;
+  }
+
+
+  void NoteWindow::on_untemplate_button_click()
+  {
+    m_note.remove_tag(m_template_tag);
+  }
+
+
+  void NoteWindow::on_save_size_check_button_toggled()
+  {
+    if(m_save_size_check_button->get_active()) {
+      m_note.add_tag(m_template_save_size_tag);
+    }
+    else {
+      m_note.remove_tag(m_template_save_size_tag);
+    }
+  }
+
+
+  void NoteWindow::on_save_selection_check_button_toggled()
+  {
+    if(m_save_selection_check_button->get_active()) {
+      m_note.add_tag(m_template_save_selection_tag);
+    }
+    else {
+      m_note.remove_tag(m_template_save_selection_tag);
+    }
+  }
+
+
+  void NoteWindow::on_save_title_check_button_toggled()
+  {
+    if(m_save_title_check_button->get_active()) {
+      m_note.add_tag(m_template_save_title_tag);
+    }
+    else {
+      m_note.remove_tag(m_template_save_title_tag);
+    }
+  }
+
+
+  void NoteWindow::on_note_tag_added(const Note&, const Tag::Ptr & tag)
+  {
+    if(tag == m_template_tag) {
+      m_template_widget->show_all();
+    }
+  }
+
+
+  void NoteWindow::on_note_tag_removed(const Note::Ptr&, const std::string & tag)
+  {
+    if(tag == m_template_tag->normalized_name()) {
+      m_template_widget->hide();
+    }
+  }
+
+
   //
   // Find context menu
   //
diff --git a/src/notewindow.hpp b/src/notewindow.hpp
index 3016a55..294e1b0 100644
--- a/src/notewindow.hpp
+++ b/src/notewindow.hpp
@@ -35,10 +35,12 @@
 #include <gtkmm/textview.h>
 #include <gtkmm/scrolledwindow.h>
 
+#include "note.hpp"
 #include "undo.hpp"
 #include "utils.hpp"
 #include "notebuffer.hpp"
 #include "preferences.hpp"
+#include "tag.hpp"
 
 namespace gnote {
 
@@ -148,6 +150,13 @@ private:
   Gtk::Toolbar * make_toolbar();
   void sync_item_selected();
   Gtk::Menu * make_plugin_menu();
+  Gtk::Box * make_template_bar();
+  void on_untemplate_button_click();
+  void on_save_size_check_button_toggled();
+  void on_save_selection_check_button_toggled();
+  void on_save_title_check_button_toggled();
+  void on_note_tag_added(const Note&, const Tag::Ptr&);
+  void on_note_tag_removed(const Note::Ptr&, const std::string&);
   Gtk::Menu * make_find_menu();
   void find_button_clicked();
   void find_next_activate();
@@ -170,9 +179,18 @@ private:
   Gtk::ScrolledWindow          *m_editor_window;
   NoteFindBar                  *m_find_bar;
   Gtk::ToolButton              *m_delete_button;
+  Gtk::Box                     *m_template_widget;
+  Gtk::CheckButton             *m_save_size_check_button;
+  Gtk::CheckButton             *m_save_selection_check_button;
+  Gtk::CheckButton             *m_save_title_check_button;
 
   utils::GlobalKeybinder       *m_global_keys;
   utils::InterruptableTimeout  *m_mark_set_timeout;
+
+  Tag::Ptr m_template_tag;
+  Tag::Ptr m_template_save_size_tag;
+  Tag::Ptr m_template_save_selection_tag;
+  Tag::Ptr m_template_save_title_tag;
 };
 
 class NoteFindBar
diff --git a/src/tagmanager.cpp b/src/tagmanager.cpp
index bb97f6e..85b8f81 100644
--- a/src/tagmanager.cpp
+++ b/src/tagmanager.cpp
@@ -34,6 +34,9 @@
 namespace gnote {
 
   const char * TagManager::TEMPLATE_NOTE_SYSTEM_TAG = "template";
+  const char * TagManager::TEMPLATE_NOTE_SAVE_SIZE_SYSTEM_TAG = "template:save-size";
+  const char * TagManager::TEMPLATE_NOTE_SAVE_SELECTION_SYSTEM_TAG = "template:save-selection";
+  const char * TagManager::TEMPLATE_NOTE_SAVE_TITLE_SYSTEM_TAG = "template:save-title";
 
   namespace {
     int compare_tags_sort_func (const Gtk::TreeIter & a, 
diff --git a/src/tagmanager.hpp b/src/tagmanager.hpp
index be9e062..c71fcd3 100644
--- a/src/tagmanager.hpp
+++ b/src/tagmanager.hpp
@@ -43,6 +43,9 @@ public:
   TagManager();
 
   static const char * TEMPLATE_NOTE_SYSTEM_TAG;
+  static const char * TEMPLATE_NOTE_SAVE_SIZE_SYSTEM_TAG;
+  static const char * TEMPLATE_NOTE_SAVE_SELECTION_SYSTEM_TAG;
+  static const char * TEMPLATE_NOTE_SAVE_TITLE_SYSTEM_TAG;
   Tag::Ptr get_tag (const std::string & tag_name) const;
   Tag::Ptr get_or_create_tag(const std::string &);
   Tag::Ptr get_system_tag (const std::string & tag_name) const;
diff --git a/src/watchers.cpp b/src/watchers.cpp
index 5b677a8..210fe5f 100644
--- a/src/watchers.cpp
+++ b/src/watchers.cpp
@@ -1272,8 +1272,8 @@ namespace gnote {
   void NoteTagsWatcher::on_tag_removed(const Note::Ptr&, const std::string& tag_name)
   {
     Tag::Ptr tag = TagManager::obj().get_tag (tag_name);
-    DBG_OUT ("Watchers.OnTagRemoved popularity count: %d", tag->popularity());
-    if (tag->popularity() == 0) {
+    DBG_OUT ("Watchers.OnTagRemoved popularity count: %d", tag ? tag->popularity() : 0);
+    if (tag && tag->popularity() == 0) {
       TagManager::obj().remove_tag (tag);
     }
   }



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