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

navigation.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 "libraries.h"
#include <libgen.h>
#include <glib.h>
#include "navigation.h"
#include "combobox.h"
#include "utilities.h"
#include "gwrappers.h"
#include "bible.h"
#include "projectutils.h"


Navigation::Navigation (GtkWidget * bookcombo, GtkWidget * chaptercombo, GtkWidget * versecombo, bool * settingcombos):
  genconfig (0)
{
  // Save/initialize variables.
  changed = false;
  book = combobox_get_active_string (bookcombo);
  chapter = combobox_get_active_string (chaptercombo);
  verse = combobox_get_active_string (versecombo);
  mybookcombo = bookcombo;
  mychaptercombo = chaptercombo;
  myversecombo = versecombo;
  mysettingcombos = settingcombos;
  anotherbook = false;
  anotherchapter = false;
}


Navigation::~Navigation ()
{
}


void Navigation::nextbook ()
{
  vector <ustring> strings = combobox_get_strings (mybookcombo);
  if (strings.size () == 0)
    return;
  unsigned int index = 0;
  for (unsigned int i = 0; i < strings.size (); i++) {
    if (book == strings[i])
      index = i;
  }
  if (index == (strings.size () - 1)) {
    return;
  }
  book = strings[++index];
  chapter = "1";
  // Find the first verse.
  vector<ustring> verses = project_get_verses (genconfig.project(), book, convert_to_int (chapter));
  if (verses.size () > 1)
    // Get the first verse of the chapter which is not "0".
    if (verses[0] == "0")
      verse = verses[1];
    else
      verse = verses[0];
  else
    verse = "0";
  changed = true;
  set_combos ();
  store_in_configuration ();
}


void Navigation::previousbook ()
{
  vector <ustring> strings = combobox_get_strings (mybookcombo);
  if (strings.size () == 0)
    return;
  unsigned int index = 0;
  for (unsigned int i = 0; i < strings.size (); i++) {
    if (book == strings[i])
      index = i;
  }
  if (index == 0) {
    return;
  }
  book = strings[--index];
  chapter = "1";
  // Find proper first verse.
  vector<ustring> verses = project_get_verses (genconfig.project(), book, convert_to_int (chapter));
  if (verses.size () > 1)
    // Get the first verse of the chapter which is not "0".
    if (verses[0] == "0")
      verse = verses[1];
    else
      verse = verses[0];
  else
    verse = "0";
  changed = true;
  set_combos ();
  store_in_configuration ();
}


void Navigation::nextchapter ()
{
  vector <ustring> strings = combobox_get_strings (mychaptercombo);
  if (strings.size () == 0)
    return;
  unsigned int index = 0;
  for (unsigned int i = 0; i < strings.size (); i++) {
    if (chapter == strings[i])
      index = i;
  }
  if (index == (strings.size () - 1)) {
    crossboundarieschapter (true);
    return;
  }
  chapter = strings[++index];
  // Find proper first verse.
  vector<ustring> verses = project_get_verses (genconfig.project(), book, convert_to_int (chapter));
  if (verses.size () > 1)
    verse = verses[1];
  else
    verse = "0";
  changed = true;
  set_combos ();
  store_in_configuration ();
}


void Navigation::previouschapter ()
{
  vector <ustring> strings = combobox_get_strings (mychaptercombo);
  if (strings.size () == 0)
    return;
  unsigned int index = 0;
  for (unsigned int i = 0; i < strings.size (); i++) {
    if (chapter == strings[i])
      index = i;
  }
  if (index == 0) {
    crossboundarieschapter (false);
    return;
  }
  chapter = strings[--index];
  // Find proper first verse.
  vector<ustring> verses = project_get_verses (genconfig.project(), book, convert_to_int (chapter));
  if (verses.size () > 1)
    verse = verses[1];
  else
    verse = "0";
  changed = true;
  set_combos ();
  store_in_configuration ();
}


void Navigation::nextverse ()
{
  vector <ustring> strings = combobox_get_strings (myversecombo);
  if (strings.size () == 0)
    return;
  unsigned int index = 0;
  for (unsigned int i = 0; i < strings.size (); i++) {
    if (verse == strings[i])
      index = i;
  }
  if (index == (strings.size () - 1)) {
    crossboundariesverse (true);
    return;
  }
  verse = strings[++index];
  changed = true;
  set_combos ();
  store_in_configuration ();
}


void Navigation::previousverse ()
{
  vector <ustring> strings = combobox_get_strings (myversecombo);
  if (strings.size () == 0)
    return;
  unsigned int index = 0;
  for (unsigned int i = 0; i < strings.size (); i++) {
    if (verse == strings[i])
      index = i;
  }
  if (index == 0) {
    crossboundariesverse (false);
    return;
  }
  verse = strings[--index];
  changed = true;
  set_combos ();
  store_in_configuration ();
}


void Navigation::crossboundariesverse (bool forward)
/*
Handles the situation where a requested change in a verse needs to cross
the boundaries of a book or a chapter.
*/
{
  // Index of the currently opened book in the current project.
  int bookindex = -1;
  vector<ustring> allbooks = project_get_books (genconfig.project());
  for (unsigned int i = 0; i < allbooks.size (); i++) {
    if (book == allbooks[i])
      bookindex = i;
  }
  // If the requested book does not exist, bail out.
  if (bookindex < 0) {
    return;
  }
  // Get the previous book, and the next book.
  int previousbookindex = bookindex - 1;
  previousbookindex = CLAMP (previousbookindex, 0, bookindex);
  unsigned int nextbookindex = bookindex + 1;
  nextbookindex = CLAMP (nextbookindex, 0, allbooks.size() - 1);
  // Get a list of all references in these on the most three books.
  vector<ustring> books;
  vector<unsigned int> chapters;
  vector<ustring> verses;
  for (unsigned int i = previousbookindex; i <= nextbookindex; i++) {
    // Get the book metrics.
    vector<unsigned int> bookchapters = project_get_chapters (genconfig.project(), allbooks[i]);
    for (unsigned int i2 = 0; i2 < bookchapters.size(); i2++) {
      vector<ustring> chapterverses = project_get_verses (genconfig.project(), allbooks[i], bookchapters[i2]);
      for (unsigned int i3 = 0; i3 < chapterverses.size(); i3++) {
        books.push_back (allbooks[i]);
        chapters.push_back (bookchapters[i2]);
        verses.push_back (chapterverses[i3]);
      }
    }
  }
  // Find the current reference in the list.
  unsigned int referenceindex = 0;
  unsigned int localchapter = convert_to_int (chapter);
  for (unsigned int i = 0; i < books.size(); i++) {
    if (verse == verses[i]) {
      if (localchapter == chapters[i]) {
        if (book == books[i]) {
          referenceindex = i;
          break;
        }
      }
    }
  }
  // Get the next or previous value.
  if (forward) {
    if (referenceindex == (chapters.size() - 1))
      return;
    referenceindex++;
  } else {
    if (referenceindex == 0)
      return;
    referenceindex--;
  }
  // Ok, give us the new values.
  book = books[referenceindex];
  chapter = convert_to_string (chapters[referenceindex]);
  verse = verses[referenceindex];
  changed = true;
  set_combos ();
  store_in_configuration ();
}


void Navigation::crossboundarieschapter (bool forward)
/*
This handles the situation where a request for a change of chapter needs
to cross the boundary of a book.
*/
{
  // Index of the currently opened book in the currently opened scripture.
  int bookindex = -1;
  vector<ustring> allbooks = project_get_books (genconfig.project());
  for (unsigned int i = 0; i < allbooks.size (); i++) {
    if (book == allbooks[i])
      bookindex = i;
  }
  // If the requested book does not exist, bail out.
  if (bookindex < 0) {
    return;
  }
  // Get the previous book, and the next book.
  int previousbookindex = bookindex - 1;
  previousbookindex = CLAMP (previousbookindex, 0, bookindex);
  unsigned int nextbookindex = bookindex + 1;
  nextbookindex = CLAMP (nextbookindex, 0, allbooks.size() - 1);
  // Get a list of all references in these on the most three books.
  vector<ustring> books;
  vector<unsigned int> chapters;
  vector<ustring> first_verses;
  for (unsigned int i = previousbookindex; i <= nextbookindex; i++) {
    // Get the book metrics.
    vector<unsigned int> bookchapters = project_get_chapters (genconfig.project(), allbooks[i]);
    for (unsigned int i2 = 0; i2 < bookchapters.size(); i2++) {
      books.push_back (allbooks[i]);
      chapters.push_back (bookchapters[i2]);
      vector<ustring> chapterverses = project_get_verses (genconfig.project(), allbooks[i], bookchapters[i2]);
      // We take the first verse of each chapter, if available, else we take v 0.
      if (chapterverses.size() > 1)
        first_verses.push_back (chapterverses[1]);
      else 
        first_verses.push_back (chapterverses[0]);
    }
  }
  // Find the current reference in the list.
  unsigned int referenceindex = 0;
  unsigned int localchapter = convert_to_int (chapter);
  for (unsigned int i = 0; i < books.size(); i++) {
    if (localchapter == chapters[i]) {
      if (book == books[i]) {
        referenceindex = i;
        break;
      }
    }
  }
  // Get the next or previous value.
  if (forward) {
    if (referenceindex == (chapters.size() - 1))
      return;
    referenceindex++;
  } else {
    if (referenceindex == 0)
      return;
    referenceindex--;
  }
  // Ok, give us the new values.
  book = books[referenceindex];
  chapter = convert_to_string (chapters[referenceindex]);
  if (forward) {
    verse = first_verses[referenceindex];
  } else {
    verse = first_verses[referenceindex];
  }
  changed = true;
  set_combos ();
  store_in_configuration ();
}


void Navigation::gotobook ()
// Goes to the book indicated in the combobox.
{
  changed = true;
  anotherbook = true;
  chapter = "1";
  verse = "1";
  set_combos ();
  store_in_configuration ();
}


void Navigation::gotochapter ()
// Goes to the chapter indicated in the combobox.
{
  changed = true;
  anotherchapter = true;
  verse = "1";
  set_combos ();
  store_in_configuration ();
}


void Navigation::gotoverse ()
// Goes to the verse indicated in the combobox.
{
  changed = true;
  set_combos ();
  store_in_configuration ();
}


void Navigation::gotoversenumber (const ustring& versenumber)
// Goes to "versenumber".
{
  if (versenumber != verse) {
    verse = versenumber;
    changed = true;
    set_combos ();
    store_in_configuration ();
  }
}


void Navigation::gotoreference (const ustring& bookname, const ustring& chapternumber, const ustring& versenumber)
// Goes to an arbitrary reference.
{
  // If no scripture is open, do nothing.
  if (genconfig.project().empty())
    return;
  // If the requested book does not exist, do nothing.
  if (!project_book_exists (genconfig.project(), bookname))
    return;
  // Save book/chapter/verse information.
  book = bookname;
  chapter = chapternumber;
  verse = versenumber;
  // Carry it out.
  changed = true;
  set_combos ();
  // Store config.
  store_in_configuration ();
}


void Navigation::set_combos ()
// Sets the new values in the combo boxes for book, chapter and verse.
{
  // If there was no change, do nothing.
  if (!changed) 
    return;
  // If there is no scripture opened, do nothing.
  if (genconfig.project().empty())
    return;
  // Indicate we're going to change comboboxes.
  * mysettingcombos = true;
  try {
    // Find out what needs to be changed: book, chapter, verse.
    bool new_book = (book != combobox_get_active_string (mybookcombo));
    if (anotherbook)
      new_book = true;
    bool new_chapter = (chapter != combobox_get_active_string (mychaptercombo));
    if (anotherchapter)
      new_chapter = true;
    bool new_verse = (verse != combobox_get_active_string (myversecombo));
    // Index of the book in the currently opened scripture.
    if (!project_book_exists (genconfig.project(), book)) {
      return;      
    }
    // Deal with a new book.
    if (new_book) {
      // Set the text in the combo box.
      combobox_set_string (mybookcombo, book);
      // Put the chapters in the chapter combo box.
      combobox_clear_strings (mychaptercombo);
      vector<unsigned int> chapters = project_get_chapters (genconfig.project(), book);
      for (unsigned int i = 0; i < chapters.size(); i++) {
        gtk_combo_box_append_text (GTK_COMBO_BOX (mychaptercombo), convert_to_string (chapters[i]).c_str ());
      }
      new_chapter = true;
    }
    // Bring the new chapter within the limits
    if (new_chapter) {
      unsigned int int_goto_chapter = convert_to_int (chapter);
      if (int_goto_chapter < 0)
        chapter = "0";
      vector<unsigned int> chapters = project_get_chapters (genconfig.project(), book);
      unsigned int maxchapter = chapters[chapters.size() - 1];
      if (int_goto_chapter > maxchapter)
        chapter = convert_to_string (maxchapter);
    }
    // Deal with a new chapter
    if (new_chapter) {
      // Set the text in the combo box.
      combobox_set_string (mychaptercombo, chapter);
      // Put the number of verses in combo_verse
      combobox_clear_strings (myversecombo);
      combobox_set_strings (myversecombo, project_get_verses (genconfig.project(), book, convert_to_int (chapter)));
      new_verse = true;
    }
    // Deal with a new verse.
    if (new_verse) {
      // Bring the new verse within its limits.
      polish_up_verse ();
      // Set the text in the combo box.
      combobox_set_string (myversecombo, verse);
    }
  }
  catch (exception & ex) {
    gw_critical (ex.what());
  }
  // Indicate we're ready changing comboboxes.
  * mysettingcombos = false;
}


void Navigation::store_in_configuration ()
// Store values in configuration.
{
  if (changed) {
    genconfig.book_set (book);
    genconfig.chapter_set (chapter);
    genconfig.verse_set (verse);
  }
}


void Navigation::polish_up_verse ()
// This function ensures that the verse we try to go to is within the range
// of currently being displayed verses.
{
  // See whether the verse is among the ones displayed.
  vector<ustring> verses = project_get_verses (genconfig.project(), book, convert_to_int (chapter));
  set<ustring> verses_set (verses.begin(), verses.end());
  if (verses_set.find (verse) != verses_set.end())
    return;
  // The verse was not found. See whether it can be found within the current verses.
  // E.g. is a combined verse (10-12) is displayed, and we go to verse 11, then
  // this 11 is within the range 10-12, hence we should go to 12-12.
  // It supports sequences in the form of v 1,2,3.
  // It supports ranges in the form of v 1b-3 and v 2-4a and v 2b-5a.
  int int_verse = convert_to_int (number_in_string (verse));
  int_verse = int_verse * 2;
  if (verse.find ("B") != string::npos)
    int_verse++;
  for (unsigned int i = 0; i < verses.size(); i++) {
    // Get a container full of encoded verses.
    vector<int> encoded_verses = verses_encode (verses[i]);
    set<int> expanded_verses (encoded_verses.begin(), encoded_verses.end());
    // See whether our verse is in it.
    if (expanded_verses.find (int_verse) != expanded_verses.end()) {
      verse = verses[i];
      return;
    }
  }
  // Last attempt: take first verse in chapter.
  verse = verses[0];  
}

Generated by  Doxygen 1.6.0   Back to index