Basic openoffice.org control, and listening for new presentation documents, still...
[kworship.git] / kworship / bible / KwBiblePassage.cpp
blob468e95353c0c0e49c06bbeb87779cee4c65f75a0
1 /***************************************************************************
2 * This file is part of KWorship. *
3 * Copyright 2008 James Hogan <james@albanarts.com> *
4 * *
5 * KWorship is free software: you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation, either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * KWorship is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with KWorship. If not, write to the Free Software Foundation, *
17 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
18 ***************************************************************************/
20 /**
21 * @file KwBiblePassage.cpp
22 * @brief A passage of rendered bible text.
23 * @author James Hogan <james@albanarts.com>
26 #include "KwBiblePassage.h"
28 #include <KLocale>
30 #include <QDomDocument>
31 #include <QDomElement>
34 * Constructors + destructor
37 /// Default constructor.
38 KwBiblePassage::KwBiblePassage()
39 : m_managerId()
40 , m_moduleId()
41 , m_rightToLeft(false)
42 , m_firstBook(0)
43 , m_numBooks(0)
44 , m_books(0)
48 /// Destructor.
49 KwBiblePassage::~KwBiblePassage()
54 * DOM filters
57 /// Import the passage in a DOM.
58 void KwBiblePassage::importFromDom(const QDomElement& element)
60 // Manager id element
61 QDomElement managerElem = element.firstChildElement("manager");
62 if (!managerElem.isNull())
64 m_managerId = managerElem.text();
66 else
68 m_managerId = QString();
71 // Module id element
72 QDomElement moduleElem = element.firstChildElement("module");
73 if (!moduleElem.isNull())
75 m_moduleId = moduleElem.text();
77 else
79 m_moduleId = QString();
82 // Text direction element
83 QDomElement directionElem = element.firstChildElement("direction");
84 if (!directionElem.isNull())
86 m_rightToLeft = (directionElem.text().toLower() == "rtl");
88 else
90 m_rightToLeft = false;
93 bool success;
95 // Books element
96 QDomElement booksElem = element.firstChildElement("books");
97 if (!booksElem.isNull())
99 int firstBook = booksElem.attribute("first").toInt(&success);
100 if (!success)
102 firstBook = 0;
104 int numBooks = booksElem.attribute("count").toInt(&success);
105 if (!success)
107 numBooks = 0;
109 initBooks(firstBook, numBooks);
111 // Book elements
112 QDomNodeList books = booksElem.elementsByTagName("book");
113 for (int book = 0; book < books.count(); ++book)
115 QDomElement bookElem = books.at(book).toElement();
117 int bookId = bookElem.attribute("num").toInt(&success);
118 if (success && bookId >= m_firstBook && bookId < m_firstBook+m_numBooks)
120 // Chapters element
121 QDomElement chaptersElem = bookElem.firstChildElement("chapters");
122 if (!chaptersElem.isNull())
124 int firstChapter = chaptersElem.attribute("first").toInt(&success);
125 if (!success)
127 firstChapter = 0;
129 int numChapters = chaptersElem.attribute("count").toInt(&success);
130 if (!success)
132 numChapters = 0;
134 initBook(bookId, bookElem.attribute("name", "unknown"), firstChapter, numChapters);
136 // Chapter elements
137 QDomNodeList chapters = chaptersElem.elementsByTagName("chapter");
138 for (int chapter = 0; chapter < chapters.count(); ++chapter)
140 QDomElement chapterElem = chapters.at(chapter).toElement();
142 int chapterId = chapterElem.attribute("num").toInt(&success);
143 if (success && chapterId >= firstChapter && chapterId < firstChapter+numChapters)
145 // Verses element
146 QDomElement versesElem = chapterElem.firstChildElement("verses");
147 if (!versesElem.isNull())
149 int firstVerse = versesElem.attribute("first").toInt(&success);
150 if (!success)
152 firstVerse = 0;
154 int numVerses = versesElem.attribute("count").toInt(&success);
155 if (!success)
157 numVerses = 0;
159 initChapter(bookId, chapterId, firstVerse, numVerses);
161 // Verse elements
162 QDomNodeList verses = versesElem.elementsByTagName("verse");
163 for (int verse = 0; verse < verses.count(); ++verse)
165 QDomElement verseElem = verses.at(verse).toElement();
167 int verseId = verseElem.attribute("num").toInt(&success);
168 if (success && verseId >= firstVerse && verseId < firstVerse+numVerses)
170 QString headings, content;
172 // Headings element
173 QDomElement headingsElem = verseElem.firstChildElement("headings");
174 if (!headingsElem.isNull())
176 headings = headingsElem.text();
179 // Content element
180 QDomElement contentElem = verseElem.firstChildElement("content");
181 if (!contentElem.isNull())
183 content = contentElem.text();
186 initVerse(bookId, chapterId, verseId, headings, content);
198 /// Export the passage using DOM.
199 void KwBiblePassage::exportToDom(QDomDocument& document, QDomElement& element) const
201 // Manager id element
202 QDomElement managerElem = document.createElement("manager");
203 element.appendChild(managerElem);
204 managerElem.appendChild(document.createTextNode(m_managerId));
206 // Module id element
207 QDomElement moduleElem = document.createElement("module");
208 element.appendChild(moduleElem);
209 moduleElem.appendChild(document.createTextNode(m_moduleId));
211 // Text direction element
212 QDomElement directionElem = document.createElement("direction");
213 element.appendChild(directionElem);
214 directionElem.appendChild(document.createTextNode(m_rightToLeft ? "rtl" : "ltr"));
216 // Books element
217 QDomElement booksElem = document.createElement("books");
218 element.appendChild(booksElem);
219 booksElem.setAttribute("first", m_firstBook);
220 booksElem.setAttribute("count", m_numBooks);
222 Book* book = m_books;
223 for (int bookInd = 0; bookInd < m_numBooks; ++bookInd)
225 // Book element
226 QDomElement bookElem = document.createElement("book");
227 booksElem.appendChild(bookElem);
228 bookElem.setAttribute("num", m_firstBook + bookInd);
229 bookElem.setAttribute("name", book->name);
231 // Chapters element
232 QDomElement chaptersElem = document.createElement("chapters");
233 bookElem.appendChild(chaptersElem);
234 chaptersElem.setAttribute("first", book->firstChapter);
235 chaptersElem.setAttribute("count", book->numChapters);
237 Chapter* chapter = book->chapters;
238 for (int chapterInd = 0; chapterInd < book->numChapters; ++chapterInd)
240 // Chapter element
241 QDomElement chapterElem = document.createElement("chapter");
242 chaptersElem.appendChild(chapterElem);
243 chapterElem.setAttribute("num", book->firstChapter + chapterInd);
245 // Verses element
246 QDomElement versesElem = document.createElement("verses");
247 chapterElem.appendChild(versesElem);
248 versesElem.setAttribute("first", chapter->firstVerse);
249 versesElem.setAttribute("count", chapter->numVerses);
251 Verse* verse = chapter->verses;
252 for (int verseInd = 0; verseInd < chapter->numVerses; ++verseInd)
254 // Verse element
255 QDomElement verseElem = document.createElement("verse");
256 versesElem.appendChild(verseElem);
257 verseElem.setAttribute("num", chapter->firstVerse + verseInd);
259 // Headings
260 if (!verse->headings.isEmpty())
262 QDomElement headingsElem = document.createElement("headings");
263 verseElem.appendChild(headingsElem);
264 headingsElem.appendChild(document.createTextNode(verse->headings));
267 // Content
268 if (!verse->content.isEmpty())
270 QDomElement contentElem = document.createElement("content");
271 verseElem.appendChild(contentElem);
272 contentElem.appendChild(document.createTextNode(verse->content));
275 ++verse;
278 ++chapter;
281 ++book;
286 * Main interface
289 /// Clear the passage.
290 void KwBiblePassage::clearBooks()
292 // Deep cleanup operation
293 if (0 != m_books)
295 int lastBook = m_firstBook + m_numBooks;
296 for (int bookNumber = m_firstBook; bookNumber < lastBook; ++bookNumber)
298 clearChapters(bookNumber);
300 delete [] m_books;
301 m_books = 0;
305 /// Clear the chapters in a book.
306 void KwBiblePassage::clearChapters(int bookNumber)
308 Q_ASSERT(0 != m_books && bookNumber >= m_firstBook && bookNumber < m_firstBook+m_numBooks);
309 Book* book = &m_books[bookNumber-m_firstBook];
310 if (0 != book->chapters)
312 int maxChapter = book->firstChapter + book->numChapters;
313 for (int chapterNumber = book->firstChapter; chapterNumber < maxChapter; ++chapterNumber)
315 clearVerses(bookNumber, chapterNumber);
317 delete [] book->chapters;
318 book->chapters = 0;
322 /// Clear the verses in a chapter.
323 void KwBiblePassage::clearVerses(int bookNumber, int chapterNumber)
325 bookNumber -= m_firstBook;
326 Q_ASSERT(0 != m_books && bookNumber >= 0 && bookNumber < m_numBooks);
327 chapterNumber -= m_books[bookNumber].firstChapter;
328 Q_ASSERT(0 != m_books[bookNumber].chapters && chapterNumber >= 0 && chapterNumber < m_books[bookNumber].numChapters);
329 Chapter* chapter = &m_books[bookNumber].chapters[chapterNumber];
330 if (0 != chapter->verses)
332 delete [] chapter->verses;
333 chapter->verses = 0;
337 /// Set the source module.
338 void KwBiblePassage::setSource(const QString& managerId, const QString& moduleId, bool rightToLeft)
340 m_managerId = managerId;
341 m_moduleId = moduleId;
342 m_rightToLeft = rightToLeft;
345 /// Initialise the books in the passage.
346 void KwBiblePassage::initBooks(int firstBook, int numBooks)
348 clearBooks();
349 m_firstBook = firstBook;
350 m_numBooks = numBooks;
351 if (m_numBooks > 0)
353 m_books = new Book[numBooks];
354 for (int i = 0; i < numBooks; ++i)
356 m_books[i].firstChapter = -1;
357 m_books[i].numChapters = 0;
358 m_books[i].chapters = 0;
363 /// Initialise a book in the passage.
364 void KwBiblePassage::initBook(int bookNumber,
365 const QString& name, int firstChapter, int numChapters)
367 clearChapters(bookNumber);
368 bookNumber -= m_firstBook;
369 Book* book = &m_books[bookNumber];
370 book->name = name;
371 book->firstChapter = firstChapter;
372 book->numChapters = numChapters;
373 if (numChapters > 0)
375 book->chapters = new Chapter[numChapters];
376 for (int i = 0; i < numChapters; ++i)
378 book->chapters[i].firstVerse = -1;
379 book->chapters[i].numVerses = 0;
380 book->chapters[i].verses = 0;
385 /// Initialise a chapter in the passage.
386 void KwBiblePassage::initChapter(int bookNumber, int chapterNumber,
387 int firstVerse, int numVerses)
389 clearVerses(bookNumber, chapterNumber);
390 bookNumber -= m_firstBook;
391 chapterNumber -= m_books[bookNumber].firstChapter;
392 Chapter* chapter = &m_books[bookNumber].chapters[chapterNumber];
393 chapter->firstVerse = firstVerse;
394 chapter->numVerses = numVerses;
395 if (numVerses > 0)
397 chapter->verses = new Verse[numVerses];
401 /// Initialise a verse in the passage.
402 void KwBiblePassage::initVerse(int bookNumber, int chapterNumber, int verseNumber,
403 const QString& headings, const QString& content)
405 bookNumber -= m_firstBook;
406 Q_ASSERT(0 != m_books && bookNumber >= 0 && bookNumber < m_numBooks);
407 chapterNumber -= m_books[bookNumber].firstChapter;
408 Q_ASSERT(0 != m_books[bookNumber].chapters && chapterNumber >= 0 && chapterNumber < m_books[bookNumber].numChapters);
409 verseNumber -= m_books[bookNumber].chapters[chapterNumber].firstVerse;
410 Q_ASSERT(0 != m_books[bookNumber].chapters[chapterNumber].verses && verseNumber >= 0 && verseNumber < m_books[bookNumber].chapters[chapterNumber].numVerses);
412 Verse* verse = &m_books[bookNumber].chapters[chapterNumber].verses[verseNumber];
413 verse->headings = headings;
414 verse->content = content;
418 * Accessors
421 /// Find whether the passage is empty.
422 bool KwBiblePassage::isEmpty() const
424 return (m_books == 0);
427 /// Get textual key of this passage.
428 QString KwBiblePassage::textualKey() const
430 // Empty range
431 if (0 == m_numBooks)
433 return i18nc("empty bible passage", "empty");
435 Q_ASSERT(m_books[0].numChapters > 0);
436 Q_ASSERT(m_books[0].chapters[0].numVerses > 0);
439 * @bug 2008-dec-22: There is a bug in Qt's handling of right-to-left scripts somewhere
440 * in QString::arg(QString) - in this case from m_books[i].name.
441 * It results in the substitutions taking place in the wrong order.
442 * There also appear to be similar problems rendering even if the
443 * substitutions appear to take place correctly.
444 * I haven't managed to find a satisfactory work-around for this problem
445 * so I'm ignoring it for the time being.
448 QString result = i18nc("bible index (book, chapter:verse)", "%1 %2",
449 m_books[0].name,
450 i18nc("bible index (chapter:verse)", "%1:%2",
451 m_books[0].firstChapter,
452 m_books[0].chapters[0].firstVerse));
453 Book* lastBook = &m_books[m_numBooks - 1];
454 Chapter* lastChapter = &lastBook->chapters[lastBook->numChapters - 1];
455 // Overlapping multiple books
456 if (m_numBooks > 1)
458 result = i18nc("bible passage range (book chapter:verse, book chapter:verse)", "%1 - %2", result,
459 i18nc("bible index (book, chapter:verse)", "%1 %2",
460 lastBook->name,
461 i18nc("bible index (chapter:verse)", "%1:%2",
462 lastBook->firstChapter + lastBook->numChapters - 1,
463 lastChapter->firstVerse + lastChapter->numVerses - 1)));
465 // Overlapping multiple chapters
466 else if (lastBook->numChapters > 1)
468 result = i18nc("bible passage range (book chapter:verse, chapter:verse)", "%1 - %2", result,
469 i18nc("bible index (chapter:verse)", "%1:%2",
470 lastBook->firstChapter + lastBook->numChapters - 1,
471 lastChapter->firstVerse + lastChapter->numVerses - 1));
473 // Overlapping multiple verses
474 else if (lastChapter->numVerses > 1)
476 result = i18nc("bible passage range (book chapter:verse, verse)", "%1-%2", result,
477 lastChapter->firstVerse + lastChapter->numVerses - 1);
480 // Module name is quite important too
481 return i18nc("bible index (range, version)", "%1 (%2)", result, m_moduleId);
484 /// Find whether the text is right to left.
485 bool KwBiblePassage::isRightToLeft() const
487 return m_rightToLeft;
490 /// Get the first book number in the passage.
491 int KwBiblePassage::firstBookNumber() const
493 return m_firstBook;
496 /// Get the last book number in the passage.
497 int KwBiblePassage::lastBookNumber() const
499 return m_firstBook + m_numBooks - 1;
502 /// Get the first chapter number in a book of the passage.
503 int KwBiblePassage::firstChapterNumber(int bookNumber) const
505 bookNumber -= m_firstBook;
506 Q_ASSERT(0 != m_books && bookNumber >= 0 && bookNumber < m_numBooks);
508 Book* book = &m_books[bookNumber];
509 return book->firstChapter;
512 /// Get the last chapter number in a book of the passage.
513 int KwBiblePassage::lastChapterNumber(int bookNumber) const
515 bookNumber -= m_firstBook;
516 Q_ASSERT(0 != m_books && bookNumber >= 0 && bookNumber < m_numBooks);
518 Book* book = &m_books[bookNumber];
519 return book->firstChapter + book->numChapters - 1;
522 /// Get the first verse number in a chapter of the passage.
523 int KwBiblePassage::firstVerseNumber(int bookNumber, int chapterNumber) const
525 bookNumber -= m_firstBook;
526 Q_ASSERT(0 != m_books && bookNumber >= 0 && bookNumber < m_numBooks);
527 chapterNumber -= m_books[bookNumber].firstChapter;
528 Q_ASSERT(0 != m_books[bookNumber].chapters && chapterNumber >= 0 && chapterNumber < m_books[bookNumber].numChapters);
530 Chapter* chapter = &m_books[bookNumber].chapters[chapterNumber];
531 return chapter->firstVerse;
534 /// Get the last verse number in a chapter of the passage.
535 int KwBiblePassage::lastVerseNumber(int bookNumber, int chapterNumber) const
537 bookNumber -= m_firstBook;
538 Q_ASSERT(0 != m_books && bookNumber >= 0 && bookNumber < m_numBooks);
539 chapterNumber -= m_books[bookNumber].firstChapter;
540 Q_ASSERT(0 != m_books[bookNumber].chapters && chapterNumber >= 0 && chapterNumber < m_books[bookNumber].numChapters);
542 Chapter* chapter = &m_books[bookNumber].chapters[chapterNumber];
543 return chapter->firstVerse + chapter->numVerses - 1;
546 /// Get the headings before a verse.
547 QString KwBiblePassage::verseHeadings(int bookNumber, int chapterNumber, int verseNumber, bool plain) const
549 bookNumber -= m_firstBook;
550 Q_ASSERT(0 != m_books && bookNumber >= 0 && bookNumber < m_numBooks);
551 chapterNumber -= m_books[bookNumber].firstChapter;
552 Q_ASSERT(0 != m_books[bookNumber].chapters && chapterNumber >= 0 && chapterNumber < m_books[bookNumber].numChapters);
553 verseNumber -= m_books[bookNumber].chapters[chapterNumber].firstVerse;
554 Q_ASSERT(0 != m_books[bookNumber].chapters[chapterNumber].verses && verseNumber >= 0 && verseNumber < m_books[bookNumber].chapters[chapterNumber].numVerses);
556 Verse* verse = &m_books[bookNumber].chapters[chapterNumber].verses[verseNumber];
557 QString result = verse->headings;
558 if (plain)
560 result.replace( QRegExp("<[^>]*>"), "" );
562 return result;
565 /// Get the content of a verse.
566 QString KwBiblePassage::verseContent(int bookNumber, int chapterNumber, int verseNumber, bool plain) const
568 bookNumber -= m_firstBook;
569 Q_ASSERT(0 != m_books && bookNumber >= 0 && bookNumber < m_numBooks);
570 chapterNumber -= m_books[bookNumber].firstChapter;
571 Q_ASSERT(0 != m_books[bookNumber].chapters && chapterNumber >= 0 && chapterNumber < m_books[bookNumber].numChapters);
572 verseNumber -= m_books[bookNumber].chapters[chapterNumber].firstVerse;
573 Q_ASSERT(0 != m_books[bookNumber].chapters[chapterNumber].verses && verseNumber >= 0 && verseNumber < m_books[bookNumber].chapters[chapterNumber].numVerses);
575 Verse* verse = &m_books[bookNumber].chapters[chapterNumber].verses[verseNumber];
576 QString result = verse->content;
577 if (plain)
579 result.replace( QRegExp("<[^>]*>"), "" );
581 return result;
585 * Text extraction
588 /// Get the entire passaage rendered as HTML.
589 QString KwBiblePassage::renderedText() const
591 QString result;
592 result += QString("<div dir=\"%1\">").arg(m_rightToLeft ? "rtl" : "ltr");
594 Book* book = m_books;
595 for (int bookIndex = 0; bookIndex < m_numBooks; ++bookIndex)
597 Chapter* chapter = book->chapters;
598 for (int chapterIndex = 0; chapterIndex < book->numChapters; ++chapterIndex)
600 Verse* verse = chapter->verses;
601 for (int verseIndex = 0; verseIndex < chapter->numVerses; ++verseIndex)
603 if (!verse->headings.isEmpty())
605 result += verse->headings;
607 if (!verse->content.isEmpty())
609 result += QString("<sup>%1</sup>").arg(chapter->firstVerse + verseIndex);
610 result += verse->content;
612 ++verse;
614 ++chapter;
616 ++book;
619 result += "</div>";
620 return result;