[gnote] Add Table Of Content add-in



commit b0178747f9a1926cb23f25099c03da107c1565d5
Author: Aurimas Černius <aurisc4 gmail com>
Date:   Sat Apr 27 19:06:17 2013 +0300

    Add Table Of Content add-in
    
    Bug 698059.
    Thanks Luc Pionchon.

 configure.ac                                       |    1 +
 help/C/gnote-addin-tableofcontent.page             |  187 ++++++++++
 help/Makefile.am                                   |    1 +
 po/POTFILES.in                                     |    2 +
 src/addins/Makefile.am                             |    1 +
 src/addins/tableofcontent/Makefile.am              |   23 ++
 src/addins/tableofcontent/NEWS                     |   60 ++++
 src/addins/tableofcontent/TODO                     |  108 ++++++
 .../tableofcontent/tableofcontent.desktop.in       |   10 +
 src/addins/tableofcontent/tableofcontent.hpp       |   47 +++
 .../tableofcontent/tableofcontentmenuitem.cpp      |   88 +++++
 .../tableofcontent/tableofcontentmenuitem.hpp      |   56 +++
 .../tableofcontent/tableofcontentnoteaddin.cpp     |  368 ++++++++++++++++++++
 .../tableofcontent/tableofcontentnoteaddin.hpp     |   95 +++++
 14 files changed, 1047 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 6c47960..72ca3eb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -169,6 +169,7 @@ src/addins/printnotes/Makefile
 src/addins/replacetitle/Makefile
 src/addins/statistics/Makefile
 src/addins/stickynoteimport/Makefile
+src/addins/tableofcontent/Makefile
 src/addins/tomboyimport/Makefile
 src/addins/underline/Makefile
 src/addins/webdavsyncservice/Makefile
diff --git a/help/C/gnote-addin-tableofcontent.page b/help/C/gnote-addin-tableofcontent.page
new file mode 100644
index 0000000..81fa12d
--- /dev/null
+++ b/help/C/gnote-addin-tableofcontent.page
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+      "Table of Content" is a Note add-in for Gnote.
+      It lists Note's table of content in a menu.
+
+      Copyright (c)  2013  Luc Pionchon <pionchon luc gmail com>
+
+      Permission is granted to copy, distribute and/or modify this document
+      under the terms of the GNU Free Documentation License, Version 1.1
+      or any later version published by the Free Software Foundation;
+      with no Invariant Sections, with no Front-Cover Texts, and with no
+      Back-Cover Texts.
+      FIXME: is this true? (no invariant, no front, no back?)
+      FIXME: does the statement have to show in the rendered document?
+
+      A copy of the license is included in the section entitled "GNU
+      Free Documentation License".
+      FIXME: is this true? Is there a (GNOME? Yelp?) common GFDL topic?
+-->
+<page xmlns="http://projectmallard.org/1.0/";
+      type="topic"
+      id="addin-tableofcontent">
+
+  <info>
+    <revision
+      status="candidate"
+      pkgversion="gnote-addin-tableofcontent-version-0.9"
+    />
+    <!-- Status:
+      stub        contains little to no real content
+      incomplete  outline of all information, but lacking content
+      draft       all content available, but unpolished
+      review      ready to be reviewed by editors
+      candidate   reviewed and awaiting a final approval
+      final       approved and ready for publication or distribution
+      outdated    was once complete or nearly complete, but needs to be revised to reflect changes
+
+      Other possible attributes:
+      version=""        revision version
+      docversion=""     enclosing document
+      date="2011-03-08" revision date
+    -->
+    <license href="http://www.gnu.org/licenses/fdl-1.1.html";><p>
+      This <em>help topic</em> is licensied under the
+      <link  href="http://www.gnu.org/licenses/fdl-1.1.html";>
+        GNU Free Documentation License, Version 1.1</link> (GFDL).
+      It means that you are free to copy, distribute and/or modify this document, under some conditions.
+    </p>
+    </license>
+
+    <credit type="author copyright">
+      <name>Luc Pionchon</name><email>pionchon luc gmail com</email><years>2013</years></credit>
+    <!-- Credit type: (can have several types)
+      author        somebody responsible for writing content
+      editor        somebody who has provided reviews or editorial corrections
+      copyright     a person or entity which holds copyright on the work
+      maintainer    the person or entity currently responsible for the work
+      collaborator  somebody who has provided extensive rough information
+      translator    somebody who has translated the work into another language
+      publisher     a person or entity who distributes formatted copies
+
+      <credit> can have a URL, for example for a publisher, using FOAF namespace:
+        <page xmlns="http://xmlns.com/foaf/0.1/";>http://flossmanuals.net/</page>
+    -->
+
+    <link type="guide" xref="index#advanced"/>
+    <link type="topic" xref="addins-preferences"/>
+    <link type="topic" xref="editing-notes"/>
+    <link type="topic" xref="searching-notes"/>
+    <desc>Navigate long structured notes</desc>
+  </info>
+
+
+<title>Note's Table of Content</title>
+
+<desc><em>Navigate long structured notes</em></desc>
+
+<note><p>
+  This is an add-in to <app>Gnotes</app>.
+  When this add-in is installed,
+  there is a <gui>Table of Content</gui> entry in the <gui>Tools</gui> menu.
+  See  <link xref="addins-preferences"/> to know how to activate an add-in.
+</p></note>
+
+
+<section id="nuttshell"><title>In a nutshell</title>
+
+<list type="numbered">
+  <item>
+    <p>a header is a full line of text, formatted with <gui>Bold+Huge</gui> or <gui>Bold+Large</gui></p>
+  </item>
+  <item>
+    <p>select a full line and set it has a header with
+       <keyseq><key>Ctrl</key><key>1</key></keyseq> or <keyseq><key>Ctrl</key><key>2</key></keyseq>
+    </p>
+  </item>
+  <item>
+    <p>open the table of content with <keyseq><key>Ctrl</key><key>Alt</key><key>1</key></keyseq></p>
+  </item>
+</list>
+
+</section>
+
+
+<section id="navigating"><title>Navigating through long notes</title>
+
+<p>
+When writting a long note, it may become difficult to navigate through.
+It is then natural to structure the note by marking sections and subsections with headers.
+</p>
+
+<p>
+This add-in makes it easy to navigate through long structured notes.
+Once you have set headers in your note, the table of content of the note will show in a menu.
+Then you can jump directly from the menu to the selected header.
+</p>
+
+</section>
+
+
+<section id="setheaders"><title>Structuring your notes with headers</title>
+
+<p>
+A header is simply a complete line with a specific text formatting:
+</p>
+<list>
+  <item><p>headers of level 1, for sections, are lines formatted with a <gui>Bold+Huge</gui> font</p></item>
+  <item><p>headers of level 2, for subsections, are lines formatted with a <gui>Bold+Large</gui> 
font</p></item>
+</list>
+
+
+<p>To format a header line, first select a full line of text, then either:</p>
+
+<list>
+  <item>
+    <p>use the commands <gui>Level 1 Header</gui> and <gui>Level 2 Header</gui>,
+       in the <guiseq><gui>Tools</gui><gui>Table of Content</gui></guiseq> menu</p>
+  </item>
+
+  <item>
+    <p>use the keyboard shortcuts <keyseq><key>Ctrl</key><key>1</key></keyseq> for headers of level 1,
+    and <keyseq><key>Ctrl</key><key>2</key></keyseq> for headers of level 2</p>
+ </item>
+</list>
+
+<p>Alternatively, on a new line, activate the command and then enter the header title.</p>
+
+<p>You can actually even use the normal formatting commands from the <gui>Text</gui> menu:
+    <gui>Bold</gui>, <gui>Large</gui> and <gui>Huge</gui>, and their respective keyboard shortcuts.
+    If, for example, your header line is already formatted as <gui>Huge</gui>, then you just need to make it 
also <gui>Bold</gui> to turn it into a header of level 1.
+    See <link xref="editing-notes"/> to know how to set text style.
+</p>
+
+</section>
+
+
+<section id="showtable"><title>Showing the table of content</title>
+
+<p>You can access the table of content, either:</p>
+<list>
+<item><p>in the <guiseq><gui>toolbar</gui><gui>Tools</gui><gui>Table of Content</gui></guiseq> 
menu</p></item>
+<item><p>in the <guiseq><gui>contextual menu</gui><gui>Table of Content</gui></guiseq> 
(<keyseq>right-click</keyseq> on the note window)</p></item>
+<item><p>in a <gui>popup menu</gui>, with the keyboard shortcut 
<keyseq><key>Ctrl</key><key>Alt</key><key>1</key></keyseq></p></item>
+<p>Then you can navigate up and down with the <gui>arrow keys</gui> and jump to the selected header with 
<key>enter</key>. Or just <keyseq>click</keyseq> with your mouse on the header to jump to.</p>
+</list>
+
+</section>
+
+
+<section id="troubleshooting"><title>Troubleshooting</title>
+<terms>
+  <item><title><em style="strong">One header does not show in the table of content</em></title><p>
+    there might be some characters in the header line, like whitespaces, which are not formatted as a 
header. Make sure that <em>the whole line</em> is formatted as a header. See <link xref="#setheaders"/>.</p>
+  </item>
+  <item><title><em style="strong">There is an empty line in the table of content</em></title><p>
+    an empty line, possibly with whitespaces, is formatted as a header. Jump to this line, and delete the 
area.</p>
+  </item>
+
+  <item><title></title><p>
+  </p>
+  </item>
+
+</terms>
+
+</section>
+
+</page>
\ No newline at end of file
diff --git a/help/Makefile.am b/help/Makefile.am
index 7c36510..4aa87bb 100644
--- a/help/Makefile.am
+++ b/help/Makefile.am
@@ -32,6 +32,7 @@ HELP_FILES = \
        gnote-addin-replacetitle.page \
        gnote-addin-sync-local.page \
        gnote-addin-sync-webdav.page \
+       gnote-addin-tableofcontent.page \
        gnote-addin-timestamp.page \
        gnote-addin-underline.page \
        gnote-bulleted-lists.page \
diff --git a/po/POTFILES.in b/po/POTFILES.in
index f5f2352..ce34cd5 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -31,6 +31,8 @@ src/addins/statistics/statisticsapplicationaddin.cpp
 src/addins/statistics/statisticswidget.cpp
 src/addins/stickynoteimport/stickynoteimport.desktop.in
 src/addins/stickynoteimport/stickynoteimportnoteaddin.cpp
+src/addins/tableofcontent/tableofcontent.desktop.in
+src/addins/tableofcontent/tableofcontentnoteaddin.cpp
 src/addins/tomboyimport/tomboyimport.desktop.in
 src/addins/underline/underline.desktop.in
 src/addins/underline/underlinemenuitem.cpp
diff --git a/src/addins/Makefile.am b/src/addins/Makefile.am
index 6d211d3..3df3e82 100644
--- a/src/addins/Makefile.am
+++ b/src/addins/Makefile.am
@@ -13,6 +13,7 @@ SUBDIRS = backlinks \
        replacetitle \
        statistics \
        stickynoteimport \
+       tableofcontent \
        tomboyimport \
        underline \
        webdavsyncservice \
diff --git a/src/addins/tableofcontent/Makefile.am b/src/addins/tableofcontent/Makefile.am
new file mode 100644
index 0000000..2c87d23
--- /dev/null
+++ b/src/addins/tableofcontent/Makefile.am
@@ -0,0 +1,23 @@
+
+include $(builddir)/../addins.mk
+
+ INTLTOOL_DESKTOP_RULE@
+
+desktop_in_files = tableofcontent.desktop.in
+desktop_files    = $(desktop_in_files:.desktop.in=.desktop)
+
+addinsdir = $(ADDINSDIR)
+addins_LTLIBRARIES = tableofcontent.la
+addins_DATA = $(desktop_files)
+
+
+tableofcontent_la_SOURCES = \
+       tableofcontent.hpp \
+       tableofcontentnoteaddin.hpp \
+       tableofcontentnoteaddin.cpp \
+       tableofcontentmenuitem.hpp  \
+       tableofcontentmenuitem.cpp  \
+       $(NULL)
+
+EXTRA_DIST = $(desktop_in_files)
+DISTCLEANFILES = $(desktop_files)
diff --git a/src/addins/tableofcontent/NEWS b/src/addins/tableofcontent/NEWS
new file mode 100644
index 0000000..cf71afe
--- /dev/null
+++ b/src/addins/tableofcontent/NEWS
@@ -0,0 +1,60 @@
+NEWS for Add-in: Table of Content
+=================================
+
+(NOTE: little release manual:
+ update NEWS + .desktop version/enabled=false + help topic status/pkgversion ==> commit -m "version x.y")
+
+
+== 0.9
+- renamed labels "Level 1 Header" to "Header Level 1" (resp. 2)
+- set help topic status to "candidate"
+- typo in .desktop description
+- code: general code review, various minor updates in code, including:
+  - add-in description in copyright headers
+  - various code comments
+  - renamed header level enum: s/Level/Header/ and s/H1,H2,H3/Title,Level_1,Level_2/
+    (so forget the "HTML alike", now Ctrl-1/2 matches Level_1/2 (instead of H2/H3!))
+  - renamed get_range_level() into get_header_level_for_range()
+
+== 0.8
+- Added Help entry in toc menu
+- Set Ctrl-Alt-1 accelerator on toc menu item
+- Removed "Show in a Popup Menu" entry in toc menu
+- Don't show action entries in detatched popup.
+- Don't show (empty toc) label on menus with action entries.
+- Removed italic on action menu entries
+
+== 0.7
+- ported to master/3.9.0 by Aurimas
+- .desktop.in, use unicode ©, set Authors and Copyright not translatable
+- added NEWS file
+- when toc is empty, use just a dummy unsensitive item rather than an explanatory text.
+  explanations will be in the manual/help.
+- added menu entries to set headers level 1 and 2, with Ctrl-1/2 accelerators
+- added menu entry to show the accelerator to open the toc in a popup menu
+- toc action entries set in italic to differentiate from the toc entries
+- "Show in a popup menu" action set unsensitive (it does not make sense to trigger it from a menu)
+- added help page
+
+== 0.6
+- code cleanup from Aurimas review
+
+== 0.5
+- now compiles with gnote-3.6.0
+
+== 0.4
+- fixed segfault and double menu bug (I think so)
+- code cleanup
+
+== 0.3
+- added TableofcontentModule::copyright()
+- used a namespaced-enum for header Levels. New file tableofcontent.hpp
+- code review changes (https://bugzilla.gnome.org/show_bug.cgi?id=698059)
+
+== 0.2
+- add toc submenu in contextual menu
+- Ctrl-Alt-1 opens toc in a popup menu
+
+== 0.1
+- port from Tomboy version, for gnote-0.7.6
+  https://github.com/oluc/tomboy/tree/TableOfContentAddin/Tomboy/Addins/TableOfContent
diff --git a/src/addins/tableofcontent/TODO b/src/addins/tableofcontent/TODO
new file mode 100644
index 0000000..dfd45b6
--- /dev/null
+++ b/src/addins/tableofcontent/TODO
@@ -0,0 +1,108 @@
+TODO for Add-in: Table of Content
+=================================
+
+
+- On Ctrl-1/2 toggle, toggle only B/L/H tags, not all tags
+  (this is a bug!)
+  ex: within all fixed width text, setting a header, the header must remain also fixed width
+
+- Huge is not huge enough
+    There is little visual difference between large and huge
+    if large uses uppercase and huge lowercase it makes the difference thiner
+    it also depends on the letters, as some takes visually more space
+    when they are close to each other it may help to distinguish
+    when there is only one header in the window, it's hard to say if it's of level 1 or 2
+
+    See https://bugzilla.gnome.org/show_bug.cgi?id=657601
+    Could also be: general config options for font sizes/factor (for S/N/L/H)
+    Could also be: allow any sizes <size:n>, with n = factor (not only S/N/L/H)
+
+- Highlight the header when jumping to it
+    ==> More visual, especially when the header does not scroll up to the head of the window
+    remove highlight as soon as cursor moved
+    (like search results, for example)
+
+- When no headers, still show the note title in toc
+  this is usefull to jump to the top of a long (unstructured) note
+
+- Check all FIXMEs, and more code cleanup
+
+
+== Maybe
+
+- Undo as a single user action, when setting headers
+    Note: tomboy/gnote handles poorly undo/redo,
+    this item may need to upgrade the undo system
+
+- When entering header text, stop formatting on 'enter'
+  UC: 1. cursor at beginning of a line
+      2. Ctrl-1/2
+      3. enter header title
+      4. press 'enter'
+      --> currently: if you keep typing text, the new line keep being bold+huge/large
+      --> wanted: stop formatting as a header.
+      /!\ this conflicts with a user who actually *wants* to type bold+huge/large *text* with new lines
+      ==> when entering a header, to move back to text formatting: arrow down, or Ctrl-1/2
+          TO CHECK: does the later actually works?
+
+- Set where the toc menu item appears in the Tools menu
+    --> next to "What Links Here?"
+    (I think there is a way to say where)
+
+- Keyboard shortcuts for jumping between sessions
+  Jump to next section
+  Jump to previous section
+  Q: What shortcuts? the ones with up/down arrow ar all(?) taken?
+  Alt-right and Alt-left, usually used for prev/next, are used for bullet list indentation
+       (can this change?)
+
+- Customizable header tags
+  « a header level x is a full line with tags Tx »
+
+  Each Header has:
+  - a list of tags       ex: "bold + size:huge"
+  - a level              gint
+  - a keyboard shortcut  by default Ctrl+<header level>
+  - an icon              (or not)
+  - a Unicode string     (or not) to tabulate the header in the toc
+  Ex:
+      L   key      tag list              icon                 tab string
+     --------------------------------------------------------------------
+      1   Ctrl+1   "bold + size:huge"    "stock:GO_FORWARD"   ""
+      2   Ctrl+2   "bold + size:large"   ""                   "└→"
+      3   Ctrl+3   "underline"           ""                   "  •"
+
+  Todo: make code generic + (g)settings + preference UI + help topic
+
+
+== Maybe not
+
+- Headers could (also?) have a syntax (vs text formatting)
+  For example a prefix, or prefix + suffix
+  like text lightweight markup languages (asciidoc/markdown/etc.)
+  ex: == Chapter
+  ex: === Section
+  ex: ==== Subsection
+
+- Section cross references
+  if header "FooBar" exists, #FooBar is shown as a link
+  when activating #FooBar, it jumps to the section
+  Maybe also: AnotherNote#Foo open AnotherNote and jumps to Foo section
+  Then it would allow URI like note://note-reference#here
+  Note: need better syntax than just '#'
+  Note: maybe strip whitespaces before comparing? (?)
+  ex: "See also #Resources sction below" --> Resources
+  /!\ need to cross update, if a section name is changed/removed
+      --> need to listen to all changes, maybe to heavy?
+      --> or would need to set specific tags for sections and xrefs(?)
+
+- More shortcuts for formatting? + configuration panel?
+       Ctrl-shift-B
+       Ctrl-shift-H
+       Ctrl-shift-L
+       Ctrl-shift-B-L
+       Ctrl-shift-B-H
+       (update: I don't remember what it's meant to do! Need to check Tomboy mailinglist)
+    (update: I think it's from the time where Ctrl-1 would set the whole line(s) where the cursor or 
selection is. So these shortcuts would set the whole line(s) with corresponding formatting tags)
+    Then we could have Ctrl-Shift-1 and Ctrl-Shift-2
+    Though I disabled this because of some misfunction(?) (or a just bug?)(?)
diff --git a/src/addins/tableofcontent/tableofcontent.desktop.in 
b/src/addins/tableofcontent/tableofcontent.desktop.in
new file mode 100644
index 0000000..daac4f3
--- /dev/null
+++ b/src/addins/tableofcontent/tableofcontent.desktop.in
@@ -0,0 +1,10 @@
+[AddinInfo]
+Id=TableofcontentAddin
+_Name=Table of Content
+_Description=Navigate long structured notes. Set section and subsection headers in your note, and the Table 
of Content will show in a menu.
+Authors=Luc Pionchon
+Category=Tools
+Version=0.9
+DefaultEnabled=false
+Module=tableofcontent
+Copyright=© 2013 Luc Pionchon
diff --git a/src/addins/tableofcontent/tableofcontent.hpp b/src/addins/tableofcontent/tableofcontent.hpp
new file mode 100644
index 0000000..7257858
--- /dev/null
+++ b/src/addins/tableofcontent/tableofcontent.hpp
@@ -0,0 +1,47 @@
+/*
+ * "Table of Content" is a Note add-in for Gnote.
+ *  It lists Note's table of content in a menu.
+ *
+ * Copyright (C) 2013 Luc Pionchon <pionchon luc gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* An enum for different header levels in the toc */
+
+
+/* Note: the enum is in this file because
+         when it was in tableofcontentnoteaddin.hpp,
+         I could not use it also in tableofcontentmenuitem.cpp,
+         there was a scope error, which I could not solve.
+ */
+
+#ifndef __TABLEOFCONTENT_HPP_
+#define __TABLEOFCONTENT_HPP_
+
+namespace tableofcontent {
+
+namespace Header { // Header level,
+  enum Type {      //  Header::Type     (can be used as a type)
+    Title,         //  Header::Title    == Note title
+    Level_1,       //  Header::Level_1  == 1st level header == Ctrl-1
+    Level_2,       //  Header::Level_2  == 2nd level header == Ctrl-2
+    None           //  Header::None
+  };
+}
+
+
+}
+
+#endif
\ No newline at end of file
diff --git a/src/addins/tableofcontent/tableofcontentmenuitem.cpp 
b/src/addins/tableofcontent/tableofcontentmenuitem.cpp
new file mode 100644
index 0000000..c63c1db
--- /dev/null
+++ b/src/addins/tableofcontent/tableofcontentmenuitem.cpp
@@ -0,0 +1,88 @@
+/*
+ * "Table of Content" is a Note add-in for Gnote.
+ *  It lists Note's table of content in a menu.
+ *
+ * Copyright (C) 2013 Luc Pionchon <pionchon luc gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* A subclass of ImageMenuItem to show a toc menu item */
+
+#include <gtkmm/stock.h>
+
+#include "iconmanager.hpp"
+#include "notewindow.hpp"
+
+#include "tableofcontentmenuitem.hpp"
+#include "tableofcontent.hpp"
+
+namespace tableofcontent {
+
+
+TableofcontentMenuItem::TableofcontentMenuItem (
+                            const gnote::Note::Ptr & note,
+                            const std::string      & header,
+                            Header::Type             header_level,
+                            int                      header_position)
+  : m_note            (note)
+  , m_header_position (header_position)
+{
+  //Create a new menu item, with style depending on the header level:
+  /* +-----------------+
+     |[] NOTE TITLE    | <---- Title     == note icon  + bold note title
+     | > Header 1      | <---- Level_1   == arrow icon + header title
+     | > Header 1      |
+     |   └→ header 2   | <---- Level_2   == (no icon)  + indent string + header title
+     |   └→ header 2   |
+     |   └→ header 2   |
+     | > Header 1      |
+     +-----------------+
+   */
+
+  set_use_underline (false); //we don't want potential '_' in the header to be used as mnemonic
+
+  if (header_level == Header::Title) {
+    set_image(*manage(new Gtk::Image(gnote::IconManager::obj().get_icon(gnote::IconManager::NOTE, 16))));
+    Gtk::Label *label = (Gtk::Label*)get_child();
+    label->set_markup("<b>" + header + "</b>");
+  }
+  else if (header_level == Header::Level_1) {
+    set_image(*manage(new Gtk::Image(Gtk::Stock::GO_FORWARD, Gtk::ICON_SIZE_MENU)));
+    set_label(header);
+  }
+  else if (header_level == Header::Level_2) {
+    set_label("└→  " + header);
+  }
+}
+
+
+void TableofcontentMenuItem::on_activate ()
+{
+  if (!m_note) {
+    return;
+  }
+
+  // Scroll the TextView and place the cursor on the header
+  Gtk::TextIter header_iter;
+  header_iter = m_note->get_buffer()->get_iter_at_offset (m_header_position);
+  m_note->get_window()->editor()->scroll_to (header_iter,
+                                              0.0, //[0.0,0.5] within_margin, margin as a fraction of screen 
size.
+                                              0.0, //[0.0,1.0] horizontal alignment of mark within visible 
area.
+                                              0.0);//[0.0,1.0] vertical alignment of mark within visible 
area.
+  m_note->get_buffer()->place_cursor (header_iter);
+}
+
+
+} //namespace
\ No newline at end of file
diff --git a/src/addins/tableofcontent/tableofcontentmenuitem.hpp 
b/src/addins/tableofcontent/tableofcontentmenuitem.hpp
new file mode 100644
index 0000000..e1d92f8
--- /dev/null
+++ b/src/addins/tableofcontent/tableofcontentmenuitem.hpp
@@ -0,0 +1,56 @@
+/*
+ * "Table of Content" is a Note add-in for Gnote.
+ *  It lists Note's table of content in a menu.
+ *
+ * Copyright (C) 2013 Luc Pionchon <pionchon luc gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* A subclass of ImageMenuItem to show a toc menu item */
+
+#ifndef __TABLEOFCONTENT_MENU_ITEM_HPP_
+#define __TABLEOFCONTENT_MENU_ITEM_HPP_
+
+#include <string>
+#include <gtkmm/imagemenuitem.h>
+
+#include "note.hpp"
+#include "tableofcontent.hpp"
+
+
+namespace tableofcontent {
+
+class TableofcontentMenuItem : public Gtk::ImageMenuItem
+{
+public:
+  TableofcontentMenuItem ( const gnote::Note::Ptr & note,
+                           const std::string      & header,
+                           Header::Type             header_level,
+                           int                      header_position
+                         );
+
+protected:
+  virtual void on_activate ();
+
+private:
+  gnote::Note::Ptr m_note;            //the Note referenced by the menu item
+  int              m_header_position; //the position of the header in the Note
+                                        // == offset in the GtkTextBuffer
+};
+
+
+}
+
+#endif
\ No newline at end of file
diff --git a/src/addins/tableofcontent/tableofcontentnoteaddin.cpp 
b/src/addins/tableofcontent/tableofcontentnoteaddin.cpp
new file mode 100644
index 0000000..3360836
--- /dev/null
+++ b/src/addins/tableofcontent/tableofcontentnoteaddin.cpp
@@ -0,0 +1,368 @@
+/*
+ * "Table of Content" is a Note add-in for Gnote.
+ *  It lists Note's table of content in a menu.
+ *
+ * Copyright (C) 2013 Luc Pionchon <pionchon luc gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* A subclass of NoteAddin, implementing the Table of Content add-in */
+
+#include <glibmm/i18n.h>
+
+#include <gtkmm/stock.h>
+#include <gtkmm/separatormenuitem.h>
+
+#include "sharp/string.hpp"
+
+#include "notemanager.hpp"
+#include "notewindow.hpp"
+#include "notebuffer.hpp"
+#include "utils.hpp"
+
+#include "tableofcontent.hpp"
+#include "tableofcontentnoteaddin.hpp"
+#include "tableofcontentmenuitem.hpp"
+
+namespace tableofcontent {
+
+TableofcontentModule::TableofcontentModule()
+{
+  ADD_INTERFACE_IMPL(TableofcontentNoteAddin);
+}
+
+
+TableofcontentNoteAddin::TableofcontentNoteAddin()
+  : m_menu_item      (NULL)
+  , m_toc_menu       (NULL)
+  , m_toc_menu_built (false)
+{
+}
+
+void TableofcontentNoteAddin::initialize () {}
+void TableofcontentNoteAddin::shutdown   () {}
+
+
+Gtk::ImageMenuItem * new_toc_menu_item ()
+//create a menu item like: "[]_Table_of_Content______Ctrl-Alt-1__>"
+{
+  Gtk::ImageMenuItem * menu_item = manage(new Gtk::ImageMenuItem ());
+  menu_item->set_image(*manage(new Gtk::Image(Gtk::Stock::JUMP_TO, Gtk::ICON_SIZE_MENU)));
+
+  Gtk::AccelLabel *acclabel = manage(new Gtk::AccelLabel(_("Table of Content")));
+  acclabel->set_alignment (Gtk::ALIGN_START);
+  /* I don't have gtkmm-3.6, but I have gtk-3.6 */
+  /* TO UNCOMMENT *///acclabel->set_accel (GDK_KEY_1, Gdk::CONTROL_MASK | Gdk::MOD1_MASK);
+  /* TO DELETE    */gtk_accel_label_set_accel (acclabel->gobj (),GDK_KEY_1, GdkModifierType 
(GDK_CONTROL_MASK | GDK_MOD1_MASK));
+  acclabel->show ();
+
+  menu_item->add (*acclabel);
+
+  return menu_item;
+}
+
+
+void TableofcontentNoteAddin::on_note_opened ()
+{
+  // TOC menu
+  m_toc_menu = manage(new Gtk::Menu());
+  m_toc_menu->signal_hide().connect(
+                sigc::mem_fun(*this, &TableofcontentNoteAddin::on_menu_hidden));
+  m_toc_menu->show_all ();
+
+  m_menu_item = new_toc_menu_item ();
+  m_menu_item->set_submenu(*m_toc_menu);
+  m_menu_item->signal_activate().connect(
+                 sigc::mem_fun(*this, &TableofcontentNoteAddin::on_menu_item_activated));
+  m_menu_item->show ();
+
+  add_plugin_menu_item (m_menu_item);
+
+  // Reacts to key press events
+  get_note()->get_window()->signal_key_press_event().connect(
+    sigc::mem_fun(*this, &TableofcontentNoteAddin::on_key_pressed));
+
+  // TOC can show up also in the contextual menu
+  get_note()->get_window()->editor()->signal_populate_popup().connect(
+    sigc::mem_fun(*this, &TableofcontentNoteAddin::on_populate_popup));
+
+  // Header tags
+  m_tag_bold  = get_note()->get_tag_table()->lookup ("bold");
+  m_tag_large = get_note()->get_tag_table()->lookup ("size:large");
+  m_tag_huge  = get_note()->get_tag_table()->lookup ("size:huge");
+}
+
+
+void TableofcontentNoteAddin::on_menu_item_activated ()
+{
+  if(m_toc_menu_built) {
+    return;
+  }
+  populate_toc_menu (m_toc_menu);
+  m_toc_menu_built = true;
+}
+
+
+void TableofcontentNoteAddin::on_menu_hidden ()
+{
+  m_toc_menu_built = false; //force the submenu to rebuild next time it's supposed to show
+}
+
+
+void TableofcontentNoteAddin::populate_toc_menu (Gtk::Menu *toc_menu, bool has_action_entries)
+//populate a menu with Note's table of content
+{
+  // Clear out the old list
+  std::vector<Gtk::Widget*> menu_items = toc_menu->get_children();
+  for(std::vector<Gtk::Widget*>::reverse_iterator iter = menu_items.rbegin();
+      iter != menu_items.rend(); ++iter) {
+    toc_menu->remove(**iter);
+  }
+
+  // Build a new list
+  std::list<TableofcontentMenuItem*> items;
+  get_tableofcontent_menu_items(items);
+
+  for(std::list<TableofcontentMenuItem*>::iterator iter = items.begin();
+      iter != items.end(); ++iter) {
+    TableofcontentMenuItem *item(*iter);
+    item->show_all();
+    toc_menu->append(*item);
+  }
+
+  // Action menu items, or nothing
+  if (has_action_entries == false) {
+    if (toc_menu->get_children().size() == 0) { // no toc items, and no action entries = empty menu
+      Gtk::MenuItem *item = manage(new Gtk::MenuItem(_("(empty table of content)")));
+      item->set_sensitive(false);
+      item->show();
+      toc_menu->append(*item);
+    }
+  }
+  else {
+    Gtk::MenuItem *item;
+
+    if (toc_menu->get_children().size() != 0) { //there are toc items, we add a separator
+      item = manage(new Gtk::SeparatorMenuItem ());
+      item->show ();
+      toc_menu->append(*item);
+    }
+
+    item = manage(new Gtk::MenuItem (_("Header Level 1")));
+    item->add_accelerator("activate", get_note()->get_window()->get_accel_group(), GDK_KEY_1, 
Gdk::CONTROL_MASK, Gtk::ACCEL_VISIBLE);
+    item->signal_activate().connect(sigc::mem_fun(*this, &TableofcontentNoteAddin::on_level_1_activated));
+    item->show ();
+    toc_menu->append(*item);
+
+    item = manage(new Gtk::MenuItem (_("Header Level 2")));
+    item->add_accelerator("activate", get_note()->get_window()->get_accel_group(), GDK_KEY_2, 
Gdk::CONTROL_MASK, Gtk::ACCEL_VISIBLE);
+    item->signal_activate().connect(sigc::mem_fun(*this, &TableofcontentNoteAddin::on_level_2_activated));
+    item->show ();
+    toc_menu->append(*item);
+
+    item = manage(new Gtk::MenuItem (_("Table of Content Help")));
+    item->signal_activate().connect(sigc::mem_fun(*this, &TableofcontentNoteAddin::on_toc_help_activated));
+    item->show ();
+    toc_menu->append(*item);
+  }
+
+}
+
+
+void TableofcontentNoteAddin::on_populate_popup (Gtk::Menu* popup_menu)
+//prepened a toc submenu in the contextual menu
+{
+  Gtk::Menu *toc_menu = manage(new Gtk::Menu());
+  populate_toc_menu (toc_menu);
+
+  Gtk::SeparatorMenuItem *separator = manage(new Gtk::SeparatorMenuItem ());
+  separator->show ();
+  popup_menu->prepend (*separator);
+
+  Gtk::ImageMenuItem *menu_item = new_toc_menu_item ();
+  menu_item->set_submenu (*toc_menu);
+  menu_item->show ();
+
+  popup_menu->prepend (*menu_item);
+}
+
+
+bool TableofcontentNoteAddin::has_tag_over_range (Glib::RefPtr<Gtk::TextTag> tag, Gtk::TextIter start, 
Gtk::TextIter end)
+//return true if tag is set from start to end
+{
+  bool has = false;
+  Gtk::TextIter iter = start;
+  while (iter.compare(end) != 0 && (has = iter.has_tag(tag))){
+    iter.forward_char();
+  }
+  return has;
+}
+
+
+Header::Type TableofcontentNoteAddin::get_header_level_for_range (Gtk::TextIter start, Gtk::TextIter end)
+//return the header level from start to end
+{
+  if (has_tag_over_range (m_tag_bold, start, end)) {
+
+    if (has_tag_over_range (m_tag_huge , start, end)) {
+        return Header::Level_1;
+    }
+    else if (has_tag_over_range (m_tag_large, start, end)) {
+        return Header::Level_2;
+    }
+    else {
+        return Header::None;
+    }
+  }
+  else {
+      return Header::None;
+  }
+}
+
+
+void TableofcontentNoteAddin::get_tableofcontent_menu_items(std::list<TableofcontentMenuItem*> & items)
+//go through the note text, and list all lines tagged as header,
+//and for each header, create a new TableofcontentMenuItem.
+{
+  TableofcontentMenuItem *item = NULL;
+
+  std::string header;
+  Header::Type header_level;
+  int         header_position;
+
+  Gtk::TextIter iter, iter_end, eol;
+
+  //for each line of the buffer,
+  //check if the full line has bold and (large or huge) tags
+  iter     = get_note()->get_buffer()->begin();
+  iter_end = get_note()->get_buffer()->end();
+
+  while (iter != iter_end) {
+    eol = iter;
+    eol.forward_to_line_end();
+
+    header_level = get_header_level_for_range (iter, eol);
+
+    if (header_level == Header::Level_1 || header_level == Header::Level_2) {
+      header_position = iter.get_offset();
+      header = iter.get_text(eol);
+
+      if (items.size() == 0) {
+        //It's the first header found,
+        //we also insert an entry linked to the Note's title:
+        item = manage(new TableofcontentMenuItem (get_note(), get_note()->get_title(), Header::Title, 0));
+        items.push_back(item);
+      }
+      item = manage(new TableofcontentMenuItem (get_note(), header, header_level, header_position));
+      items.push_back(item);
+    }
+    iter.forward_visible_line(); //next line
+  }
+}
+
+void TableofcontentNoteAddin::on_level_1_activated()
+{
+  headification_switch (Header::Level_1);
+}
+void TableofcontentNoteAddin::on_level_2_activated()
+{
+  headification_switch (Header::Level_2);
+}
+void TableofcontentNoteAddin::on_toc_popup_activated()
+{
+  if(m_toc_menu_built == false) {
+    populate_toc_menu(m_toc_menu, false);
+    m_toc_menu_built = true;
+  }
+  m_toc_menu->popup(0, 0);
+}
+void TableofcontentNoteAddin::on_toc_help_activated()
+{
+  gnote::NoteWindow* window = get_note()->get_window();
+  gnote::utils::show_help("gnote", "addin-tableofcontent",
+    window->get_screen()->gobj(), dynamic_cast<Gtk::Window*>(window->host()));
+}
+
+
+bool TableofcontentNoteAddin::on_key_pressed(GdkEventKey *ev)
+//return true if signal handled, false otherwise
+//NOTE: if a menu item has an accelerator,
+//      its entry is needed until the toc menu is built a first time,
+//      then the menu item accelerator takes the signals.
+{
+  switch(ev->keyval) {
+
+  case GDK_KEY_1:
+      if (ev->state == (GDK_CONTROL_MASK | GDK_MOD1_MASK)) {// Ctrl-Alt-1
+        on_toc_popup_activated();
+        return true;
+      }
+      else if (ev->state == GDK_CONTROL_MASK) { // Ctrl-1
+        on_level_1_activated ();
+        return true;
+      }
+      else {
+        return false;
+      }
+  break;
+
+  case GDK_KEY_2:
+      if (ev->state == GDK_CONTROL_MASK) { // Ctrl-2
+        on_level_2_activated ();
+        return true;
+      }
+      else {
+        return false;
+      }
+  break;
+
+  default:
+    return false;
+  }
+
+  return false;
+}
+
+
+void TableofcontentNoteAddin::headification_switch (Header::Type header_request)
+//apply the correct header style to the current selection
+//switch:  Level_1 <--> Level_2 <--> text
+{
+  Glib::RefPtr<gnote::NoteBuffer> buffer = get_note()->get_buffer();
+  Gtk::TextIter start, end;
+
+  buffer->get_selection_bounds (start, end);
+
+  Header::Type current_header = get_header_level_for_range (start, end);
+
+  buffer->remove_all_tags (start, end);//reset all tags
+
+  if( current_header == Header::Level_1 && header_request == Header::Level_2) { //existing vs requested
+    buffer->set_active_tag ("bold");
+    buffer->set_active_tag ("size:large");
+  }
+  else if( current_header == Header::Level_2 && header_request == Header::Level_1) {
+    buffer->set_active_tag ("bold");
+    buffer->set_active_tag ("size:huge");
+  }
+  else if( current_header == Header::None) {
+    buffer->set_active_tag ("bold");
+    buffer->set_active_tag ( (header_request == Header::Level_1)?"size:huge":"size:large");
+  }
+
+}
+
+
+} //namespace
\ No newline at end of file
diff --git a/src/addins/tableofcontent/tableofcontentnoteaddin.hpp 
b/src/addins/tableofcontent/tableofcontentnoteaddin.hpp
new file mode 100644
index 0000000..7a799a8
--- /dev/null
+++ b/src/addins/tableofcontent/tableofcontentnoteaddin.hpp
@@ -0,0 +1,95 @@
+/*
+ * "Table of Content" is a Note add-in for Gnote.
+ *  It lists Note's table of content in a menu.
+ *
+ * Copyright (C) 2013 Luc Pionchon <pionchon luc gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* A subclass of NoteAddin, implementing the Table of Content add-in */
+
+#ifndef __TABLEOFCONTENT_NOTEADDIN_HPP_
+#define __TABLEOFCONTENT_NOTEADDIN_HPP_
+
+#include <list>
+
+#include <gtkmm/imagemenuitem.h>
+#include <gtkmm/menu.h>
+
+#include "sharp/dynamicmodule.hpp"
+#include "note.hpp"
+#include "noteaddin.hpp"
+
+#include "tableofcontent.hpp"
+
+
+namespace tableofcontent {
+
+class TableofcontentModule : public sharp::DynamicModule
+{
+public:
+  TableofcontentModule();
+};
+DECLARE_MODULE(TableofcontentModule);
+
+class TableofcontentMenuItem;
+
+
+class TableofcontentNoteAddin : public gnote::NoteAddin
+{
+public:
+  static TableofcontentNoteAddin *create()
+    {
+      return new TableofcontentNoteAddin;
+    }
+  TableofcontentNoteAddin();
+
+  virtual void initialize ();
+  virtual void shutdown ();
+  virtual void on_note_opened ();
+
+private:
+  void on_menu_item_activated ();
+  void on_menu_hidden ();
+  bool on_key_pressed (GdkEventKey *ev);
+  void on_populate_popup (Gtk::Menu* popup_menu);
+  void on_level_1_activated ();
+  void on_level_2_activated ();
+  void on_toc_popup_activated ();
+  void on_toc_help_activated ();
+
+
+  void populate_toc_menu (Gtk::Menu *toc_menu, bool has_action_entries = true);
+
+  bool has_tag_over_range (Glib::RefPtr<Gtk::TextTag> tag, Gtk::TextIter start, Gtk::TextIter end);
+  Header::Type get_header_level_for_range (Gtk::TextIter start, Gtk::TextIter end);
+
+  void get_tableofcontent_menu_items (std::list<TableofcontentMenuItem*> & items);
+
+  void headification_switch (Header::Type header_request);
+
+  Gtk::ImageMenuItem *m_menu_item;       // the TOC menu item, in the Tools menu
+  Gtk::Menu          *m_toc_menu;        // the TOC submenu, containing the TOC
+  bool                m_toc_menu_built;  // whereas toc_menu is already built
+
+  Glib::RefPtr<Gtk::TextTag> m_tag_bold; // the tags used to mark headers
+  Glib::RefPtr<Gtk::TextTag> m_tag_large;
+  Glib::RefPtr<Gtk::TextTag> m_tag_huge;
+};
+
+
+}
+
+#endif
\ No newline at end of file



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