Logo Search packages:      
Sourcecode: bibledit version File versions  Download package

editor.cpp

/*
** Copyright (C) 2003-2006 Teus Benschop.
**  
** 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 2 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, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**  
*/


#include "utilities.h"
#include <libgen.h>
#include <glib.h>
#include <config.h>
#include "editor.h"
#include "highlight.h"
#include "chapter2.h"
#include "usfm.h"
#include "editornote.h"
#include <gdk/gdkkeysyms.h>
#include "projectutils.h"
#include "generalconfig.h"
#include "projectconfig.h"


Editor::Editor (int dummy)
{
}


Editor::~Editor ()
{
}



void Editor::textview_text_set (GtkWidget * textview)
{
  textview_text = textview;
  textbuffer_text = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview));
  // Tag for highlighting a line.
  // The colour names can be found in rgb.txt of the X system.
  // On Fedore Core 3 this is in /usr/X11R6/lib/X11/rgb.txt
  text_line_tag = gtk_text_tag_table_lookup (gtk_text_buffer_get_tag_table (textbuffer_text), "linetag");
  if (text_line_tag == NULL)
    text_line_tag = gtk_text_buffer_create_tag (textbuffer_text, "linetag", "background", "light yellow", NULL);
}


void Editor::textview_notes_set (GtkWidget * textview)
{
  textview_notes = textview;
  textbuffer_notes = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview));
  // Tag for highlighting.
  notes_line_tag = gtk_text_tag_table_lookup (gtk_text_buffer_get_tag_table (textbuffer_notes), "linetag");
  if (notes_line_tag == NULL)
    notes_line_tag = gtk_text_buffer_create_tag (textbuffer_notes, "linetag", "background", "light yellow", NULL);
}


void Editor::chapter_load (unsigned int number, vector<ustring> * lines)
// Loads chapter number in the editor if lines is NULL, else loads from lines.
{
  // Reset id related variable. A text tag in the editor gets an id number
  // larger than 0, and that indicates it has an associated data container.
  // The id number we take for that is the current size of the editor objects.
  // As 0 is not acceptable, fill the container with a dummy object to fill 
  // place 0.
  editorobjects.clear();
  EditorObject zero_object (eotNone);
  editorobjects.push_back (zero_object);

  // Configuration
  GeneralConfiguration genconfig (0);
  
  // Load text.
  if (lines) {
    load_text (* lines);
  } else {
    myproject = genconfig.project ();
    mybook = genconfig.book ();
    vector <ustring> mylines;
    mylines = project_retrieve_chapter (myproject, mybook, number);
    load_text (mylines);
  }
 
  // Format screen.
  gtk_text_buffer_set_text (textbuffer_notes, "", -1);
  format ();
  
  // Set the editors ummodified.
  gtk_text_buffer_set_modified (textbuffer_text, false);
  gtk_text_buffer_set_modified (textbuffer_notes, false);
}


void Editor::load_text (vector<ustring> & lines)
{
  // Clear buffer.
  gtk_text_buffer_set_text (textbuffer_text, "", 0);
  
  // Remove all tags from the buffer.
  GtkTextIter startiter;
  GtkTextIter enditer;
  gtk_text_buffer_get_start_iter (textbuffer_text, &startiter);
  gtk_text_buffer_get_end_iter (textbuffer_text, &enditer);
  gtk_text_buffer_remove_all_tags (textbuffer_text, &startiter, &enditer);
  
  // Prepare for tagging the versenumbers.
  Highlight highlight (textbuffer_text, textview_text);

  // Set the new text.
  Chapter2 chapter2 (lines);
  for (unsigned int i = 0; i < chapter2.versenumbers.size (); i++) {
    // Insert the tag with a name of the current verse number.
    // E.g. for verse 1 the name is "1", and for verse 1-3a it is "1-3a".
    GtkTextTag * tag = highlight.get_tag (chapter2.versenumbers[i]);
    // Insert the text tagged with this tag, at the end of the buffer.
    gtk_text_buffer_get_end_iter (textbuffer_text, &enditer);
    gtk_text_buffer_insert_with_tags (textbuffer_text, &enditer, chapter2.versetext[i].c_str(), -1, tag, NULL);
    gtk_text_buffer_get_end_iter (textbuffer_text, &enditer);
    gtk_text_buffer_insert_with_tags (textbuffer_text, &enditer, "\n", 1, tag, NULL);
  }
}


void Editor::format_notes ()
/*
Formats the bible notes: footnotes, endnotes, crossreferences.
This function may be called with an empty notes buffer, but also with a full one.
Therefore we need to set up a strategy for how to deal with this issue.
- From the notebuffer, we extract all relevant data, and temporally store it 
  with their id.
- We clear the note buffer.
- We go through the text to look for any raw usfm bible notes code, or any
  formatted, and write the appropriate text in the notes buffer, and format 
  any raw code.
*/
{
  // Configuration data.
  GeneralConfiguration genconfig (0);
  
  {
    // Store current note text.
    map<int, ustring> current_notes = text_note_get_all ();
    gtk_text_buffer_set_text (textbuffer_notes, "", 0);
    // Iterate through the whole buffer, 
    // reinsert the original usfm code for any note caller,
    // remove that note caller,
    // and clear the editor object after that.
    GtkTextIter iter1, iter2;
    gtk_text_buffer_get_start_iter (textbuffer_text, &iter2);
    iter1 = iter2;
    int id_last_deleted = -1;
    while (gtk_text_iter_forward_char (&iter2)) {
      // Go through the tags that apply to this iterator.
      GSList *tags = NULL, *tagp = NULL;
      tags = gtk_text_iter_get_tags (&iter1);
      for (tagp = tags;  tagp != NULL;  tagp = tagp->next) {
        GtkTextTag *tag = (GtkTextTag *) tagp->data;
        gint id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tag), "id"));
        EditorObjectType objecttype = editorobjects[id].type;
        if ((objecttype == eotFootnote) || (objecttype == eotEndnote) || (objecttype == eotCrossreference) || (id == id_last_deleted)) {
          // Replace note caller with actual usfm code.
          gtk_text_buffer_delete (textbuffer_text, &iter1, &iter2);
          int offset = gtk_text_iter_get_offset (&iter2);
          gtk_text_buffer_insert (textbuffer_text, &iter1, current_notes[id].c_str(), -1);
          gtk_text_buffer_get_iter_at_offset (textbuffer_text, &iter2, offset);
          // Clear relevant objects.
          current_notes[id].clear();
          editorobjects[id].clear();
          // Store this id so that any next character with this id gets deleted too.
          // This is needed in case of note callers that are more than one character long.
          id_last_deleted = id;      
        }
      }
      // Free tags list if tags were found.
      if (tags) {
        g_slist_free (tags);
      } 
      // Next iteration.
      iter1 = iter2;
    }
  }

  // Usfm data.
  ProjectConfiguration projectconfig (genconfig.project ());
  Usfm usfm (projectconfig.stylesheet());

  // Prepare for the notes.
  EditorNote editornote (usfm);

  // Go through each line of the textbuffer.
  int number_of_lines = gtk_text_buffer_get_line_count (textbuffer_text);
  GtkTextIter iterator;
  GtkTextIter endofline;
  // Iterate through all lines.
  for (int i = 0; i < number_of_lines; i++) {
    gtk_text_buffer_get_iter_at_line (textbuffer_text, &iterator, i);
    // Ensure that also the last line, without a newline character, gets taken.
    if (i + 1 == number_of_lines) {
      gtk_text_buffer_get_end_iter (textbuffer_text, &endofline);
    } else {
      gtk_text_buffer_get_iter_at_line (textbuffer_text, &endofline, i + 1);
      gtk_text_iter_backward_char (&endofline);
    }
    // Get the line.
    ustring line = gtk_text_buffer_get_text (textbuffer_text, &iterator, &endofline, false);
    // Handle the notes: footnote, endnote, xref.
    editornote.extract (line);
    for (unsigned int i2 = 0; i2 < editornote.texts.size(); i2++) {
      // The caller, make it smaller and superscript.
      // Attach some data to the tag, to make it recognizable.
      // The rendered caller should not be editable, and this can be done using the 
      // "editable" property, but the problem is that when there are two or more
      // callers in sequence, the whole area becomes non-editable, so nothing
      // can be inserted in between. Also cutting the caller no longer works.
      // Handle this in the keyboard handlers.
      // In Paratext, when there is a footnote caller like "$$", one can insert 
      // a space in between, and after save, it only has the caller $ left in 
      // the text.
      int id = editorobjects.size();
      {
        GtkTextTag *tag;
        tag = gtk_text_buffer_create_tag (textbuffer_text, NULL,
              "editable", false, "rise", 5 * PANGO_SCALE, "scale", PANGO_SCALE_SMALL, NULL);
        g_object_set_data (G_OBJECT (tag), "id", GINT_TO_POINTER (id));
        GtkTextIter startiter, enditer;
        gtk_text_buffer_get_iter_at_line_offset (textbuffer_text, &startiter, i, editornote.positions[i2]);
        gtk_text_buffer_get_iter_at_line_offset (textbuffer_text, &enditer, i, editornote.positions[i2] + editornote.lengths[i2]);
        gtk_text_buffer_delete (textbuffer_text, &startiter, &enditer);
        gtk_text_buffer_insert_with_tags (textbuffer_text, &startiter, editornote.rendered_callers[i2].c_str(), editornote.rendered_callers[i2].length(), tag, NULL);
        EditorObject editorobject (editornote.types[i2]);
        editorobject.note_set (editornote.openers[i2], editornote.callers[i2], editornote.closers[i2]);
        editorobjects.push_back (editorobject);
      }
      // Set the note text in the textbuffer for display.
      {
        // Note caller.
        GtkTextTag *callertag;
        callertag = gtk_text_buffer_create_tag (textbuffer_notes, NULL, 
          "editable", false, "rise", 5 * PANGO_SCALE, "scale", PANGO_SCALE_SMALL, NULL);
        g_object_set_data (G_OBJECT (callertag), "id", GINT_TO_POINTER (id));
        GtkTextIter iter;
        gtk_text_buffer_get_end_iter (textbuffer_notes, &iter);
        ustring callertext = editornote.rendered_callers[i2] + " ";
        gtk_text_buffer_insert_with_tags (textbuffer_notes, &iter, callertext.c_str(), -1, callertag, NULL);
        // Note body.
        GtkTextTag *bodytag;
        bodytag = gtk_text_buffer_create_tag (textbuffer_notes, NULL, NULL);
        g_object_set_data (G_OBJECT (bodytag), "id", GINT_TO_POINTER (id));
        gtk_text_buffer_get_end_iter (textbuffer_notes, &iter);
        ustring bodytext = editornote.texts[i2];
        gtk_text_buffer_insert_with_tags (textbuffer_notes, &iter, bodytext.c_str(), -1, bodytag, NULL);
        // Extra info
        GtkTextTag *infotag;
        infotag = gtk_text_buffer_create_tag (textbuffer_notes, NULL, 
          "editable", false, "scale", PANGO_SCALE_SMALL, "foreground", "gray", NULL);
        gtk_text_buffer_get_end_iter (textbuffer_notes, &iter);
        ustring infotext = end_note_signature (editornote.types[i2]);
        infotext.append ("\n");
        gtk_text_buffer_insert_with_tags (textbuffer_notes, &iter, infotext.c_str(), -1, infotag, NULL);
      }
    }
  }  
  
  // If there are no footnotes, indicate that in grey colour.
  {
    GtkTextIter iter1, iter2;
    gtk_text_buffer_get_start_iter (textbuffer_notes, &iter1);
    gtk_text_buffer_get_end_iter (textbuffer_notes, &iter2);
    if (gtk_text_iter_equal (&iter1, &iter2)) {
      gtk_text_buffer_set_text (textbuffer_notes, "This chapter has no notes", -1);
    }
  }
  
  // Process pending events.
  while (gtk_events_pending()) gtk_main_iteration ();
}


void Editor::chapter_save ()
{
  // If text wasn't changed, bail out.
  bool buffer_modified = gtk_text_buffer_get_modified (textbuffer_text);
  if (gtk_text_buffer_get_modified (textbuffer_notes))
    buffer_modified = true;
  if (!buffer_modified) 
    return;
  // Get chapter text.   
  GtkTextIter startiter, enditer;
  gtk_text_buffer_get_start_iter (textbuffer_text, &startiter);
  gtk_text_buffer_get_end_iter (textbuffer_text, &enditer);
  ustring chaptertext = text_get (&startiter, &enditer);
  // Clean it up a bit and divide it into lines.
  ParseLine parseline (trim (chaptertext));
  // Add verse information.
  CategorizeChapterVerse ccv (parseline.lines);
  // Store chapter in database.
  project_store_chapter (myproject, mybook, ccv);
  // Set the buffer to unmodified.
  gtk_text_buffer_set_modified (textbuffer_text, false);
}


void Editor::footnote_body_scroll_to ()
// Place the cursor at the beginning of the nearest footnote, 
// and scroll the widget to make the note visible.
{
  // Go through the whole editor and look up the id of the nearest footnote.
  GtkTextIter iter;
  // Calculate cursor position offset.
  gtk_text_buffer_get_iter_at_mark (textbuffer_text, &iter, gtk_text_buffer_get_insert (textbuffer_text));
  int cursor_offset = gtk_text_iter_get_offset (&iter);
  // Go through the whole buffer and gather offsets of footnotes relative to cursor position.
  // Take smallest.
  int nearest_id = -1;
  int smallest_offset = 1000000;  
  gtk_text_buffer_get_start_iter (textbuffer_text, &iter);
  while (gtk_text_iter_forward_char (&iter)) {
    // Go through the tags that apply to this iterator.
    GSList *tags = NULL, *tagp = NULL;
    tags = gtk_text_iter_get_toggled_tags (&iter, true);
    for (tagp = tags;  tagp != NULL;  tagp = tagp->next) {
      GtkTextTag *tag = (GtkTextTag *) tagp->data;
      gint id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tag), "id"));
      EditorObjectType objecttype = editorobjects[id].type;
      if ((objecttype == eotFootnote) || (objecttype == eotEndnote) || (objecttype == eotCrossreference)) {
        int offset = abs (gtk_text_iter_get_offset (&iter) - cursor_offset);
        if (offset < smallest_offset) {
          nearest_id = id;
          smallest_offset = offset;
        }
      }
    }
    // Free tags list if tags were found.
    if (tags) {
      g_slist_free (tags);
    } 
  }
  
  // Remove previous highlight for this note, if any.
  GtkTextIter highlightstartiter;
  GtkTextIter highlightenditer;
  gtk_text_buffer_get_start_iter (textbuffer_notes, &highlightstartiter);
  gtk_text_buffer_get_end_iter (textbuffer_notes, &highlightenditer);
  gtk_text_buffer_remove_tag (textbuffer_notes, notes_line_tag, &highlightstartiter, &highlightenditer);

  // Prepare for highlighting the active note.
  gtk_text_buffer_get_start_iter (textbuffer_notes, &highlightenditer);
  bool highlight_start_iter_initialized = false;
  
  // If applicable, scroll to footnote with given id and place cursor there.
  if (nearest_id > 0) {
    GtkTextTag * tag_scrolled_to = NULL;
    GtkTextIter iter;
    gtk_text_buffer_get_start_iter (textbuffer_notes, &iter);
    while (gtk_text_iter_forward_char (&iter)) {
      // Go through the tags that apply to this iterator.
      GSList *tags = NULL, *tagp = NULL;
      tags = gtk_text_iter_get_tags (&iter);
      for (tagp = tags;  tagp != NULL;  tagp = tagp->next) {
        GtkTextTag *tag = (GtkTextTag *) tagp->data;
        gint id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tag), "id"));
        if (id == nearest_id) {
          if (tag != tag_scrolled_to) {
            gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (textview_notes), &iter, 0, true, 0.5, 0.5);
            gtk_text_buffer_place_cursor (textbuffer_notes, &iter);
            tag_scrolled_to = tag;
          }
          if (!highlight_start_iter_initialized) {
            highlightstartiter = iter;
            highlight_start_iter_initialized = true;
          }
          highlightenditer = iter;
        }
      }
      // Free tags list if tags were found.
      if (tags) {
        g_slist_free (tags);
      } 
    }
  }

  // Highlight the note.
  gtk_text_iter_forward_char (&highlightenditer);
  gtk_text_buffer_apply_tag (textbuffer_notes, notes_line_tag, &highlightstartiter, &highlightenditer);
}


bool Editor::footnote_caller_remove (bool backspace)
// Remove possible footnote caller we are at, and the associated footnote body.
// If BackSpace was pressed, remove a caller before the insertion point.
// This function is called by the keyboard handler, and returns true if a footnote
// has been deleted, so that the keyboard handler knows whether to process
// keyboard events further.
{
  // Return value.
  bool footnote_deleted = false;
  // Variables pointing to data to be deleted.
  GtkTextTag * tag_to_be_deleted = NULL;
  // Get iterator at the insertion point. If backspace go one back.
  GtkTextIter iter;
  gtk_text_buffer_get_iter_at_mark (textbuffer_text, &iter, gtk_text_buffer_get_insert (textbuffer_text));
  if (backspace)
    gtk_text_iter_backward_char (&iter);
  // Get tags at that iterator and see whether there are any bible notes.
  GSList *tags = NULL, *tagp = NULL;
  tags = gtk_text_iter_get_tags (&iter);
  for (tagp = tags;  tagp != NULL;  tagp = tagp->next) {
    GtkTextTag *tag = (GtkTextTag *) tagp->data;
    gint id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tag), "id"));
    EditorObjectType objecttype = editorobjects[id].type;
    if ((objecttype == eotFootnote) || (objecttype == eotEndnote) || (objecttype == eotCrossreference)) {
      tag_to_be_deleted = tag;
    }
  }
  // Free tags list if tags were found.
  if (tags) {
    g_slist_free (tags);
  } 
  // If we've got a GtkTextTag to be deleted, delete the note caller.
  // As the footnote caller can be either one character, in most cases, but also
  // two like when we've numbers like 10, 11, etc., or even three in extreme
  // cases, do it so: Iterate 3 characters back, and from there look 6 ahead
  // and delete anything in between which has this tag.
  if (tag_to_be_deleted) {
    GtkTextIter startiter = iter;
    GtkTextIter enditer = iter;
    gtk_text_iter_backward_chars (&iter, 3);
    for (unsigned int i = 0; i < 6; i++) {
      if (gtk_text_iter_begins_tag (&iter, tag_to_be_deleted)) {
        startiter = iter;
      }
      if (gtk_text_iter_ends_tag (&iter, tag_to_be_deleted)) {
        enditer = iter;
      }
      gtk_text_iter_forward_char (&iter);
    }
    gtk_text_buffer_delete (textbuffer_text, &startiter, &enditer);    
    footnote_deleted = true;
  }
  // If something was deleted, reformat the screen.
  if (footnote_deleted)
    format ();
  // Returnvalue shows whether we deleted a note.
  return footnote_deleted;
}


ustring Editor::text_get (GtkTextIter * startiter, GtkTextIter * enditer)
// Iterate through the whole buffer and collect and assemble usfm text.
{
  // Some variables needed.    
  ustring text;
  GtkTextIter iter = * startiter;
  // Go through this part of the buffer.  
  while (gtk_text_iter_in_range (&iter, startiter, enditer)) {
    // Go through the tags that apply to this iterator.
    GSList *tags = NULL, *tagp = NULL;
    tags = gtk_text_iter_get_tags (&iter);
    bool tagsfound = false;
    for (tagp = tags;  tagp != NULL;  tagp = tagp->next) {
      GtkTextTag *tag = (GtkTextTag *) tagp->data;
      gint id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tag), "id"));
      if (id != 0) {
        tagsfound = true;
        EditorObjectType objecttype = editorobjects[id].type;
        switch (objecttype) {
          case eotNone: 
            break;
          case eotFootnote:
          case eotEndnote:
          case eotCrossreference:
          {
            // In case of a tag for a note, do not output the normal text, but
            // at the start of the tag, output the stored data for this note.
            if (gtk_text_iter_begins_tag (&iter, tag)) {
              text.append (text_note_get (id));
            }
            break;
          }
          default :
          {
          }
        }
      }
    }
    // Free tags list if tags were found.
    if (tags) {
      g_slist_free (tags);
    } 
    if (!tagsfound) {
      // If no tags found, handle normal text.
      GtkTextIter iter2 = iter;
      gtk_text_iter_forward_char (&iter2);
      text.append (gtk_text_iter_get_text (&iter, &iter2));
    }
    // Next iteration.
    gtk_text_iter_forward_char (&iter);
  }
   
  // Give result.
  return text;
}


ustring Editor::text_get_selection ()
// Convenience function. Returns selected usfm code.
{
  GtkTextIter startiter, enditer;
  gtk_text_buffer_get_iter_at_mark (textbuffer_text, &startiter, gtk_text_buffer_get_insert (textbuffer_text));
  gtk_text_buffer_get_iter_at_mark (textbuffer_text, &enditer, gtk_text_buffer_get_selection_bound (textbuffer_text));
  gtk_text_iter_order (&startiter, &enditer);
  return text_get (&startiter, &enditer);
}


ustring Editor::text_get_all ()
// Convenience function. Returns all usfm code.
{
  GtkTextIter startiter, enditer;
  gtk_text_buffer_get_start_iter (textbuffer_text, &startiter);
  gtk_text_buffer_get_end_iter (textbuffer_text, &enditer);
  gtk_text_iter_order (&startiter, &enditer);
  return text_get (&startiter, &enditer);
}


ustring Editor::text_note_get (gint id)
// Gets the usfm text for a note with id = n.
// It gets the data from the objects stored in memory and from the note textview.
{
  // Variables.
  ustring text, opener, caller, closer;
  // The stored object.
  EditorObject editorobject (editorobjects[id]);
  editorobject.note_get (opener, caller, closer);
  // Start assembling usfm text.
  text.append (opener);
  text.append (caller);
  text.append (" ");
  // Extract the text from the notes textview.
  // There is a problem that text, added at the very end of a note, does not
  // have the same tag applied as the existing note text. Solve that by 
  // iterating beyond the note, till a certain signature is encountered.
  ustring notetext;
  bool was_right_id = false;
  GtkTextIter iter1, iter2;
  gtk_text_buffer_get_start_iter (textbuffer_notes, &iter2);
  iter1 = iter2;
  while (gtk_text_iter_forward_char (&iter2)) {
    // Go through the tags that apply to this iterator.
    GSList *tags = NULL, *tagp = NULL;
    tags = gtk_text_iter_get_tags (&iter1);
    for (tagp = tags;  tagp != NULL;  tagp = tagp->next) {
      GtkTextTag *tag = (GtkTextTag *) tagp->data;
      gint id2 = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tag), "id"));
      if (id2 == id) {
        was_right_id = true;
      } 
    }
    if (was_right_id) {
      notetext.append (gtk_text_iter_get_text (&iter1, &iter2));
    }
    // Free tags list if tags were found.
    if (tags) {
      g_slist_free (tags);
    } 
    // Next iteration.
    iter1 = iter2;
  }
  // Clear out first bit: caller and space.
  notetext.erase (0, notetext.find (" "));
  notetext = trim (notetext);
  // Clear out anything after signature.
  {
    ustring signature = end_note_signature (editorobject.type);
    size_t position = notetext.find (signature);
    notetext.erase (position, 1000000);
    notetext = trim (notetext);
  }
  text.append (notetext);
  text.append (closer);
  // Return result.
  return text;
}


map <int, ustring> Editor::text_note_get_all ()
// Gets a map with all note text in it.
{
  // Result variable.
  map <int, ustring> result;
  
  // Get all available ids in the notes buffer.
  vector<int> ids;
  {
    set<int> map_ids;
    GtkTextIter iter1, iter2;
    gtk_text_buffer_get_start_iter (textbuffer_notes, &iter2);
    iter1 = iter2;
    while (gtk_text_iter_forward_char (&iter2)) {
      // Go through the tags that apply to this iterator.
      GSList *tags = NULL, *tagp = NULL;
      tags = gtk_text_iter_get_tags (&iter1);
      for (tagp = tags;  tagp != NULL;  tagp = tagp->next) {
        GtkTextTag *tag = (GtkTextTag *) tagp->data;
        gint id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tag), "id"));
        if (id > 0) {
          EditorObjectType objecttype = editorobjects[id].type;
          if ((objecttype == eotFootnote) || (objecttype == eotEndnote) || (objecttype == eotCrossreference)) {        
            map_ids.insert (id);
          }
        }
      }
      // Free tags list if tags were found.
      if (tags) {
        g_slist_free (tags);
      } 
      // Next iteration.
      iter1 = iter2;
    }
    ids.assign (map_ids.begin(), map_ids.end());
  }

  // Get text for all ids.
  for (unsigned int i = 0; i < ids.size(); i++) {
    result[ids[i]] = text_note_get (ids[i]);
  }
  
  // Result.
  return result;
}


void Editor::format ()
{
  format_notes ();
  footnote_body_scroll_to ();
}


void Editor::footnote_caller_scroll_to ()
/*
This function is called whenever there is cursor movement in the footnote editor.
It scrolls the text editor to the appropriate footnote caller.
*/
{
  // Get the id of the footnote that has the cursor.
  int footnote_id = 0;
  GtkTextIter iter;
  gtk_text_buffer_get_iter_at_mark (textbuffer_notes, &iter, gtk_text_buffer_get_insert (textbuffer_notes));
  GSList *tags = NULL, *tagp = NULL;
  tags = gtk_text_iter_get_tags (&iter);
  for (tagp = tags;  tagp != NULL;  tagp = tagp->next) {
    GtkTextTag *tag = (GtkTextTag *) tagp->data;
    gint id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tag), "id"));
    if (id > 0)
      footnote_id = id;
  }
  if (tags) {
    g_slist_free (tags);
  } 
  // If we did not find any relevant id, bail out.
  if (footnote_id == 0)
    return;
  // Go through the whole editor and look for this id. Position cursor there.  
  gtk_text_buffer_get_start_iter (textbuffer_text, &iter);
  while (gtk_text_iter_forward_char (&iter)) {
    // Go through the tags that start at this iterator.
    GSList *tags = NULL, *tagp = NULL;
    tags = gtk_text_iter_get_toggled_tags (&iter, true);
    for (tagp = tags;  tagp != NULL;  tagp = tagp->next) {
      GtkTextTag *tag = (GtkTextTag *) tagp->data;
      gint id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tag), "id"));
      if (id == footnote_id) {
        // Place cursor and scroll.
        gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (textview_text), &iter, 0, true, 0.5, 0.5);
        gtk_text_buffer_place_cursor (textbuffer_text, &iter);
        // Bail out, but first free memory.
        if (tags)
          g_slist_free (tags);
        return;
      }
    }
    // Free tags list if tags were found.
    if (tags) {
      g_slist_free (tags);
    } 
  }
}


ustring Editor::end_note_signature (EditorObjectType objecttype)
{
  ustring signature = " (";
  if (objecttype == eotFootnote) signature.append ("footnote");
  if (objecttype == eotEndnote) signature.append ("endnote");
  if (objecttype == eotCrossreference) signature.append ("crossreference");
  signature.append (")");
  return signature;  
}

Generated by  Doxygen 1.6.0   Back to index