PageRenderTime 63ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/xiphos-3.1.5/src/main/display.cc

#
C++ | 2241 lines | 1900 code | 213 blank | 128 comment | 234 complexity | a0db172148799857a2d6270012151b7d MD5 | raw file
Possible License(s): GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. * Xiphos Bible Study Tool
  3. * display.cc -
  4. *
  5. * Copyright (C) 2000-2011 Xiphos Developer Team
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU Library General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
  20. */
  21. #ifdef HAVE_CONFIG_H
  22. #include <config.h>
  23. #endif
  24. #include <glib.h>
  25. #include <osishtmlhref.h>
  26. #include <thmlhtmlhref.h>
  27. #include <gbfhtmlhref.h>
  28. #include <teihtmlhref.h>
  29. #ifndef NO_SWORD_SET_RENDER_NOTE_NUMBERS
  30. /* these files are erroneously uninstalled as of previous release */
  31. #include <osisxhtml.h>
  32. #include <thmlxhtml.h>
  33. #include <gbfxhtml.h>
  34. #endif
  35. #include <osisvariants.h>
  36. #include <thmlvariants.h>
  37. #include <swmgr.h>
  38. #include <swmodule.h>
  39. #include <versekey.h>
  40. #include <regex.h>
  41. #include <string.h>
  42. #include <ctype.h>
  43. #include <assert.h>
  44. #ifdef USE_GTKMOZEMBED
  45. #ifndef USE_XIPHOS_HTML
  46. # define USE_XIPHOS_HTML
  47. #endif
  48. # ifdef PACKAGE_BUGREPORT
  49. # undef PACKAGE_BUGREPORT
  50. # endif
  51. # ifdef PACKAGE_NAME
  52. # undef PACKAGE_NAME
  53. # endif
  54. # ifdef PACKAGE_STRING
  55. # undef PACKAGE_STRING
  56. # endif
  57. # ifdef PACKAGE_TARNAME
  58. # undef PACKAGE_TARNAME
  59. # endif
  60. # ifdef PACKAGE_VERSION
  61. # undef PACKAGE_VERSION
  62. # endif
  63. #endif /* USE_GTKMOZEMBED */
  64. #ifdef USE_WEBKIT
  65. #ifndef USE_XIPHOS_HTML
  66. # define USE_XIPHOS_HTML
  67. #endif
  68. #endif
  69. #include "main/display.hh"
  70. #include "main/settings.h"
  71. #include "main/global_ops.hh"
  72. #include "main/sword.h"
  73. #include "main/modulecache.hh"
  74. #include "main/xml.h"
  75. #include "gui/utilities.h"
  76. #include "gui/widgets.h"
  77. #include "gui/dialog.h"
  78. #include "backend/sword_main.hh"
  79. #include "gui/debug_glib_null.h"
  80. // for one-time content rendering.
  81. extern ModuleCache::CacheMap ModuleMap;
  82. // for tracking personal annotations.
  83. typedef struct {
  84. gchar *module;
  85. gchar *book;
  86. int chapter;
  87. int verse;
  88. GString *annotation;
  89. } marked_element;
  90. typedef std::map <int, marked_element *> MC;
  91. MC marked_cache;
  92. gchar *marked_cache_modname = NULL, *marked_cache_book = NULL;
  93. int marked_cache_chapter = -1;
  94. int footnote, xref;
  95. #define HTML_START "<html><head><meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"><style type=\"text/css\"><!-- A { text-decoration:none } *[dir=rtl] { text-align: right; } %s --></style></head>"
  96. // CSS style blocks to control blocked strongs+morph output
  97. // BOTH is when the user wants to see both types of markup.
  98. // ONE is when he wants one or the other, so we can use a single
  99. // specification which overlays both on top of one another.
  100. #define CSS_BLOCK_BOTH \
  101. " * { line-height: 3.8em; }" \
  102. " .word { position: relative; top: 0.0em; left: 0; }" \
  103. " .strongs { position: absolute; top: 0.3em; left: 0; white-space: nowrap; z-index: 2 }" \
  104. " .morph { position: absolute; top: 1.2em; left: 0; white-space: nowrap; z-index: 1 }"
  105. #define CSS_BLOCK_ONE \
  106. " * { line-height: 2.7em; }" \
  107. " .word { position: relative; top: 0.0em; left: 0; }" \
  108. " .strongs { position: absolute; top: 0.8em; left: 0; white-space: nowrap; }" \
  109. " .morph { position: absolute; top: 0.8em; left: 0; white-space: nowrap; }"
  110. #define DOUBLE_SPACE " * { line-height: 2em ! important; }"
  111. using namespace sword;
  112. using namespace std;
  113. //
  114. // user annotation cache filling.
  115. //
  116. #define NUM_REPLACE 4
  117. struct replace {
  118. gchar c;
  119. gchar *s;
  120. } replacement[NUM_REPLACE] = {
  121. // < and > must be first.
  122. { '<', (gchar *)"&lt;" },
  123. { '>', (gchar *)"&gt;" },
  124. { '\n', (gchar *)"<br />" },
  125. #ifdef USE_XIPHOS_HTML
  126. { '"', (gchar *)"&quot;" },
  127. #else
  128. { '"', (gchar *)"'" },
  129. #endif /* !USE_XIPHOS_HTML */
  130. };
  131. void
  132. marked_cache_fill(gchar *modname, gchar *key)
  133. {
  134. gchar *s, *t, *err, *mhold;
  135. char *key_book = g_strdup(main_get_osisref_from_key((const char *)modname,
  136. (const char *)key));
  137. int key_chapter; //, key_verse;
  138. // free the old cache. first free contents, then the map itself.
  139. MC::iterator it;
  140. for (it = marked_cache.begin();
  141. it != marked_cache.end();
  142. ++it) {
  143. marked_element *e = (*it).second;
  144. g_free(e->module);
  145. g_string_free(e->annotation, TRUE);
  146. delete e;
  147. }
  148. marked_cache.clear();
  149. // tear apart the key (e.g. "Gen.1.1").
  150. *(s = strrchr(key_book, '.')) = '\0';
  151. *(t = strrchr(key_book, '.')) = '\0';
  152. key_chapter = atoi(t+1);
  153. // key_verse = atoi(s+1);
  154. // remember exactly what chapter this cache is for
  155. g_free(marked_cache_modname);
  156. g_free(marked_cache_book);
  157. marked_cache_modname = g_strdup(modname);
  158. marked_cache_book = g_strdup(key_book);
  159. marked_cache_chapter = key_chapter;
  160. // load up the annotation content
  161. marked_element *e = new marked_element;
  162. if (xml_set_section_ptr("osisrefmarkedverses") && xml_get_label()) {
  163. do {
  164. e->module = xml_get_label();
  165. s = xml_get_list();
  166. e->annotation = g_string_new(s);
  167. g_free(s);
  168. gchar *m = e->module;
  169. mhold = g_strdup(m);
  170. // tear apart "NASB Gen.1.1"
  171. if ((s = strrchr(m, '.')) == NULL) // rightmost dot
  172. goto fail;
  173. *s = '\0';
  174. if ((t = strrchr(m, '.')) == NULL) // chapter:verse
  175. goto fail;
  176. *t = '\0';
  177. e->chapter = atoi(t+1);
  178. e->verse = atoi(s+1);
  179. if ((s = strchr(m, ' ')) == NULL) // leftmost space
  180. goto fail;
  181. *(s++) = '\0';
  182. e->book = s;
  183. // now properly delimited: module & book, plus numeric c:v.
  184. g_free(mhold);
  185. // for fast reference: is this annotation relevant?
  186. if ((key_chapter != e->chapter) ||
  187. (*m && (strcasecmp(m, modname) != 0)) ||
  188. (strcasecmp(e->book, key_book) != 0)) {
  189. // junk: re-use same element in next loop.
  190. g_free(e->module);
  191. g_string_free(e->annotation, TRUE);
  192. } else {
  193. // replace embedded badness characters.
  194. for (int i = 0; i < NUM_REPLACE; ++i) {
  195. for (s = strchr(e->annotation->str, replacement[i].c);
  196. s;
  197. s = strchr(e->annotation->str, replacement[i].c)) {
  198. (void) g_string_erase(e->annotation,
  199. s - (e->annotation->str), 1);
  200. (void) g_string_insert(e->annotation,
  201. s - (e->annotation->str),
  202. replacement[i].s);
  203. }
  204. }
  205. // valid: insert + get fresh one to work with.
  206. marked_cache[e->verse] = e;
  207. e = new marked_element;
  208. }
  209. } while (xml_next_item() && xml_get_label());
  210. // remove extra element that we necessarily have at loop's end.
  211. delete e;
  212. }
  213. g_free(key_book);
  214. return;
  215. fail:
  216. err = g_strdup_printf(_("Improperly encoded personal annotation label:\n'%s'"),
  217. mhold);
  218. gui_generic_warning(err);
  219. g_free(err);
  220. g_free(key_book);
  221. return;
  222. }
  223. //
  224. // user annotation cache checking: is "this verse" annotated?
  225. //
  226. marked_element *
  227. marked_cache_check(int thisVerse)
  228. {
  229. MC::iterator it = marked_cache.find(thisVerse);
  230. if (it != marked_cache.end())
  231. return (*it).second;
  232. return NULL;
  233. }
  234. //
  235. // utility function to blank `<img src="foo.jpg" />' content from text.
  236. //
  237. void
  238. ClearImages(gchar *text)
  239. {
  240. gchar *s, *t;
  241. for (s = strstr(text, "<img "); s; s = strstr(s, "<img ")) {
  242. if ((t = strchr(s+4, '>'))) {
  243. while (s <= t)
  244. *(s++) = ' ';
  245. } else {
  246. GS_message(("ClearImages: no img end: %s\n", s));
  247. return;
  248. }
  249. }
  250. }
  251. //
  252. // utility function to blank `<font face="..." />' content from text.
  253. //
  254. void
  255. ClearFontFaces(gchar *text)
  256. {
  257. gchar *s, *t;
  258. // huge assumption: no nested <font> specs: <font face="">
  259. // is never followed by another <font anything> before </font>.
  260. for (s = strstr(text, "<font face=\"Galax"); s; s = strstr(s, "<font face=\"Galax")) {
  261. if ((t = strchr(s+15, '>'))) {
  262. while (s <= t)
  263. *(s++) = ' ';
  264. s = strstr(s, "</font>");
  265. t = s + 6;
  266. while (s <= t)
  267. *(s++) = ' ';
  268. } else {
  269. GS_message(("ClearFontFaces: no font end: %s\n", s));
  270. return;
  271. }
  272. }
  273. }
  274. //
  275. // utility function for block_render() below.
  276. // having a word + annotation in hand, stuff them into the buffer.
  277. // span class names are from CSS_BLOCK macros.
  278. // we garbage-collect here so block_render doesn't have to.
  279. //
  280. #ifdef USE_XIPHOS_HTML
  281. #define ALIGN_WORD "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
  282. #else
  283. #define ALIGN_WORD " " // gtkhtml3 has no alignment need.
  284. #endif /* USE_XIPHOS_HTML */
  285. void
  286. block_dump(SWBuf& rendered,
  287. const char **word,
  288. const char **strongs,
  289. const char **morph)
  290. {
  291. #ifdef USE_XIPHOS_HTML
  292. int wlen, min_length, slen, mlen;
  293. #endif
  294. char *s, *s0, *t;
  295. // unannotated words need no help.
  296. if (*word && (*strongs == NULL) && (*morph == NULL)) {
  297. rendered += "<span class=\"word\">";
  298. rendered += *word;
  299. rendered += "</span>";
  300. g_free((char *)*word);
  301. *word = NULL;
  302. rendered += " ";
  303. return;
  304. }
  305. if (*word == NULL) {
  306. // we need to cobble up something to take the place of
  307. // a word, in order that the strongs/morph not overlay.
  308. *word = g_strdup(ALIGN_WORD);
  309. }
  310. rendered += "<span class=\"word\">";
  311. if (*strongs) {
  312. s = g_strrstr(*strongs, "</a>");
  313. *s = '\0';
  314. t = (char*)strrchr(*strongs, '>') + 1;
  315. // correct weird NASB lexicon references.
  316. if ((s0 = (char*)strchr(*strongs, '!'))) {
  317. do {
  318. *s0 = *(s0+1);
  319. ++s0;
  320. } while (*s0 != '"');
  321. *s0 = ' ';
  322. }
  323. *s = '<';
  324. #ifdef USE_XIPHOS_HTML
  325. slen = s - t;
  326. s = (char*)strstr(*strongs, "&lt;");
  327. *s = *(s+1) = *(s+2) = *(s+3) = ' ';
  328. s = strstr(s, "&gt;");
  329. *s = *(s+1) = *(s+2) = *(s+3) = ' ';
  330. #endif /* USE_XIPHOS_HTML */
  331. // gross hack needed to handle new class="..." in sword -r2512.
  332. if ((s = (char*)strstr(*strongs, " class=\"strongs\">"))) {
  333. memcpy(s, "> ", 17);
  334. if ((s = (char*)strstr(s, " class=\"strongs\">")))
  335. memcpy(s, "> ", 17);
  336. }
  337. }
  338. #ifdef USE_XIPHOS_HTML
  339. else
  340. slen = 0;
  341. #endif /* USE_XIPHOS_HTML */
  342. if (*morph) {
  343. s = s0 = (char*)g_strrstr(*morph, "\">") + 2;
  344. t = strchr(s, '<');
  345. for (/* */; s < t; ++s)
  346. if (isupper(*s))
  347. *s = tolower(*s);
  348. for (s = strchr(s0, ' '); s && (s < t); s = strchr(s, ' '))
  349. *s = '-';
  350. s = g_strrstr(*morph, "</a>");
  351. *s = '\0';
  352. t = (char*)strrchr(*morph, '>') + 1;
  353. *s = '<';
  354. #ifdef USE_XIPHOS_HTML
  355. mlen = s - t;
  356. s = (char*)strchr(*morph, '(');
  357. *s = ' ';
  358. s = strrchr(s, ')');
  359. *s = ' ';
  360. #endif /* USE_XIPHOS_HTML */
  361. // gross hack needed to handle new class="..." in sword -r2512.
  362. if ((s = (char*)strstr(*morph, " class=\"morph\">"))) {
  363. memcpy(s, "> ", 15);
  364. if ((s = (char*)strstr(s, " class=\"morph\">")))
  365. memcpy(s, "> ", 15);
  366. }
  367. }
  368. #ifdef USE_XIPHOS_HTML
  369. else
  370. mlen = 0;
  371. min_length = 2 + max(slen, mlen);
  372. #endif /* USE_XIPHOS_HTML */
  373. rendered += *word;
  374. #ifdef USE_XIPHOS_HTML
  375. for (wlen = strlen(*word); wlen <= min_length; ++wlen)
  376. rendered += "&nbsp;";
  377. #endif /* USE_XIPHOS_HTML */
  378. g_free((char *)*word);
  379. *word = NULL;
  380. rendered += "<span class=\"strongs\">";
  381. rendered += (*strongs ? *strongs : "&nbsp;");
  382. rendered += "</span>";
  383. if (*strongs) g_free((char *)*strongs);
  384. *strongs = NULL;
  385. rendered += "<span class=\"morph\">";
  386. rendered += (*morph ? *morph : "&nbsp;");
  387. rendered += "</span>";
  388. if (*morph) g_free((char *)*morph);
  389. *morph = NULL;
  390. rendered += "</span> <span class=\"word\">&nbsp;</span>";
  391. }
  392. //
  393. // re-process a block of text so as to envelope its strong's and morph
  394. // references in <span> blocks which will be interpreted by the CSS
  395. // directives to put each ref immediately below the word it annotates.
  396. // this keeps the text linearly readable while providing annotation.
  397. //
  398. // secondary interface.
  399. // text destination is provided, ready to go.
  400. // this means we are able to recurse when needed.
  401. #ifdef USE_XIPHOS_HTML
  402. #define EMPTY_WORD ""
  403. #else
  404. #define EMPTY_WORD "&nbsp;"
  405. #endif /* USE_XIPHOS_HTML */
  406. void
  407. block_render_secondary(const char *text,
  408. SWBuf& rendered)
  409. {
  410. const char *word = NULL,
  411. *strongs = NULL,
  412. *morph = NULL;
  413. int bracket;
  414. const char *s;
  415. char *t, *u;
  416. for (s = text; *s; ++s) {
  417. switch (*s) {
  418. case ' ':
  419. case '\t':
  420. break;
  421. case '<':
  422. // <token> causes a lot of grief, because we must
  423. // keep it bound with its </token> terminator,
  424. // esp. because anchors contain <small></small>. *sigh*
  425. // i believe we see only anchors + font switches here.
  426. if ((((*(s+1) == 'a') || (*(s+1) == 'A')) && (*(s+2) == ' ')) ||
  427. (((*(s+1) == 'b') || (*(s+1) == 'B') ||
  428. (*(s+1) == 'i') || (*(s+1) == 'I') ||
  429. (*(s+1) == 'u') || (*(s+1) == 'U')) && (*(s+2) == '>'))) {
  430. if (word)
  431. block_dump(rendered, &word, &strongs, &morph);
  432. static char end[5] = "</X>";
  433. end[2] = *(s+1);
  434. again:
  435. if ((t = strstr((char *)s, end)) == NULL) {
  436. GS_warning(("No %s in %s\n", end, s));
  437. break;
  438. }
  439. // yet another nightmare:
  440. // if the markup results in e.g. doubled <i>
  441. // (bogus "<hi><hi>word</hi></hi>"),
  442. // then we will mis-assess termination.
  443. // so we search for the same markup embedded within.
  444. // if we find an internal set, we just wipe it out.
  445. static char embedded[4] = "<X>";
  446. embedded[1] = *(s+1);
  447. if ((u = g_strstr_len(s+3, t-(s+3), embedded))) {
  448. *u = *(u+1) = *(u+2) =
  449. *t = *(t+1) = *(t+2) = *(t+3) = ' ';
  450. goto again; // yuck, yes, i know...
  451. }
  452. // nasb eph 5:31: whole verse is an italicized
  453. // quotation of gen 2:24...containing strongs.
  454. // in event of font switch, output bare switch
  455. // controls but recurse on content within.
  456. if ((*(s+1) == 'a') || (*(s+1) == 'A')) {
  457. // <a href> anchor -- uninteresting.
  458. t += 4;
  459. word = g_strndup(s, t-s);
  460. s = t-1;
  461. } else {
  462. // font switch nightmare.
  463. word = g_strndup(s, 3);
  464. rendered += word;
  465. g_free((char *)word);
  466. word = g_strndup(s+3, t-(s+3));
  467. block_render_secondary(word, rendered);
  468. g_free((char *)word);
  469. word = NULL;
  470. rendered += end;
  471. s = t+3;
  472. }
  473. break;
  474. } else if (!strncmp(s+1, "small>", 6)) {
  475. // strongs and morph are bounded by "<small>".
  476. if ((t = strstr((char *)s, "</small>")) == NULL) {
  477. GS_warning(("No </small> in %s\n", s));
  478. break;
  479. }
  480. t += 8;
  481. // this is very dicey -- phenomenological/
  482. // observable about Sword filters' provision.
  483. // strongs: "<em>&lt;...&gt;</em>"
  484. // morph: "<em>(...)</em>"
  485. // if Sword ever changes this, we're dead.
  486. const char *u = s+11;
  487. while ((*u != '(') && (*u != '&'))
  488. ++u; // it has to be one or the other.
  489. if (*u == '(') {
  490. if (morph) {
  491. block_dump(rendered, &word, &strongs, &morph);
  492. word = g_strdup(EMPTY_WORD);
  493. }
  494. morph = g_strndup(s, t-s);
  495. } else {
  496. if (strongs) {
  497. block_dump(rendered, &word, &strongs, &morph);
  498. word = g_strdup(EMPTY_WORD);
  499. }
  500. strongs = g_strndup(s, t-s);
  501. }
  502. s = t-1;
  503. break;
  504. }
  505. // ...fall through to ordinary text...
  506. // (includes other "<>"-bounded markup.)
  507. default:
  508. if (word)
  509. block_dump(rendered, &word, &strongs, &morph);
  510. // here's an unfortunate problem. consider:
  511. // L<font size="-1">ORD</font> followed by strongs.
  512. // the SPC breaks it into 2 "words", very badly.
  513. // we need to track <> use to get it as *1* word,
  514. // before we capture the strongs, or just the latter
  515. // half of it ("size=...") goes inside the <span>.
  516. bracket = 0;
  517. word = s;
  518. do {
  519. while ((*s == ' ') || (*s == '\t'))
  520. s++;
  521. for ( /* */ ;
  522. *s && (*s != ' ') && (*s != '\t');
  523. ++s) {
  524. if (*s == '<') {
  525. if (!strncmp(s+1, "small>", 6) ||
  526. ((*(s+1) == 'a') &&
  527. (*(s+2) == ' '))) {
  528. // "break 2;"
  529. goto outword;
  530. }
  531. bracket++;
  532. }
  533. else if (*s == '>')
  534. bracket--;
  535. assert(bracket >= 0);
  536. }
  537. } while (bracket != 0);
  538. outword:
  539. word = g_strndup(word, s-word);
  540. s--;
  541. }
  542. }
  543. if (word || strongs || morph)
  544. block_dump(rendered, &word, &strongs, &morph);
  545. }
  546. // entry interface.
  547. // initializes for secondary interface.
  548. const char *
  549. block_render(const char *text)
  550. {
  551. static SWBuf rendered;
  552. rendered = "";
  553. block_render_secondary(text, rendered);
  554. return rendered.c_str();
  555. }
  556. //
  557. // in-place removal of inconvenient-to-the-user content, and note/xref marking.
  558. //
  559. GString *
  560. CleanupContent(GString *text,
  561. GLOBAL_OPS *ops,
  562. char *name,
  563. bool reset = true)
  564. {
  565. if (ops->image_content == 0)
  566. ClearImages((gchar *)text->str);
  567. else if ((ops->image_content == -1) && // "unknown"
  568. (strcasestr(text->str, "<img ") != NULL)) {
  569. ops->image_content = 1; // now known.
  570. main_save_module_options(name, "Image Content", 1);
  571. }
  572. if (ops->respect_font_faces == 0)
  573. ClearFontFaces((gchar *)text->str);
  574. else if ((ops->respect_font_faces == -1) && // "unknown"
  575. (strcasestr(text->str, "<font face=\"Galax") != NULL)) {
  576. ops->respect_font_faces = 1; // now known.
  577. main_save_module_options(name, "Respect Font Faces", 1);
  578. }
  579. gint pos;
  580. gchar value[50], *reported, *s = text->str;
  581. // test for any 'n="X"' content. if so, use it directly.
  582. if ((reported = backend->get_entry_attribute("Footnote", "1", "n", false))) {
  583. g_free(reported); // dispose of test junk.
  584. #ifdef NO_SWORD_SET_RENDER_NOTE_NUMBERS
  585. //
  586. // with recent engine change to auto-render
  587. // note/xref markers, this is unneeded.
  588. //
  589. gchar *notetype;
  590. // operate on notes+xrefs together: both are "Footnote".
  591. while ((s = strchr(s, '*'))) {
  592. if ((*(s+1) != 'n') && (*(s+1) != 'x')) {
  593. s++; // that's not our marker.
  594. continue;
  595. }
  596. again:
  597. g_snprintf(value, 5, "%d", ++footnote);
  598. if ((reported = backend->get_entry_attribute("Footnote", value, "n", false))) {
  599. notetype = backend->get_entry_attribute("Footnote", value, "type", false);
  600. if (notetype &&
  601. (((*(s+1) == 'n') && !strcmp(notetype, "crossReference")) ||
  602. ((*(s+1) == 'x') && strcmp(notetype, "crossReference")))) {
  603. // only 1 of notes & xrefs is active.
  604. g_free(notetype);
  605. g_free(reported);
  606. goto again;
  607. }
  608. g_free(notetype);
  609. pos = s-(text->str)+2;
  610. text = g_string_insert(text, pos, reported);
  611. g_free(reported);
  612. s = text->str + pos + 1;
  613. }
  614. }
  615. // na??vet?Š: if any verse uses 'n=', all do: reset for next verse.
  616. if (reset)
  617. footnote = 0;
  618. #endif /* NO_SWORD_SET_RENDER_NOTE_NUMBERS */
  619. }
  620. // otherwise we simply count notes & xrefs through the verse.
  621. else if (ops->xrefnotenumbers) {
  622. while ((s = strstr(s, "*n"))) {
  623. g_snprintf(value, 5, "%d", ++footnote);
  624. pos = s-(text->str)+2;
  625. text = g_string_insert(text, pos, value);
  626. s = text->str + pos + 1;
  627. }
  628. s = text->str;
  629. while ((s = strstr(s, "*x"))) {
  630. g_snprintf(value, 5, "%d", ++xref);
  631. pos = s-(text->str)+2;
  632. text = g_string_insert(text, pos, value);
  633. s = text->str + pos + 1;
  634. }
  635. }
  636. return text;
  637. }
  638. //
  639. // utility function to fill headers from verses.
  640. //
  641. void
  642. CacheHeader(ModuleCache::CacheVerse& cVerse,
  643. SWModule& mod,
  644. GLOBAL_OPS *ops, BackEnd *be)
  645. {
  646. int x = 0;
  647. gchar heading[8];
  648. const gchar *preverse, *preverse2;
  649. GString *text = g_string_new("");
  650. cVerse.SetHeader("");
  651. sprintf(heading, "%d", x);
  652. while ((preverse = be->get_entry_attribute("Heading", "Preverse",
  653. heading)) != NULL) {
  654. preverse2 = mod.RenderText(preverse);
  655. g_string_printf(text,
  656. "<br/><b>%s</b><br/><br/>",
  657. (((ops->strongs || ops->lemmas) ||
  658. ops->morphs)
  659. ? block_render(preverse2)
  660. : preverse2));
  661. text = CleanupContent(text, ops, mod.Name(), false);
  662. cVerse.AppendHeader(text->str);
  663. // g_free((gchar *)preverse2);
  664. g_free((gchar *)preverse);
  665. ++x;
  666. sprintf(heading, "%d", x);
  667. }
  668. g_string_free(text, TRUE);
  669. }
  670. void
  671. set_morph_order(SWModule& imodule)
  672. {
  673. for (FilterList::const_iterator it =
  674. imodule.getRenderFilters().begin();
  675. it != imodule.getRenderFilters().end();
  676. it++) {
  677. OSISHTMLHREF *f = dynamic_cast<OSISHTMLHREF *>(*it);
  678. if (f)
  679. f->setMorphFirst();
  680. }
  681. }
  682. #ifdef NO_SWORD_SET_RENDER_NOTE_NUMBERS
  683. // placeholder for older Sword.
  684. #define set_render_numbers(x,y) /* nothing */
  685. #else
  686. void
  687. set_render_numbers(SWModule& imodule, GLOBAL_OPS *ops)
  688. {
  689. for (FilterList::const_iterator it =
  690. imodule.getRenderFilters().begin();
  691. it != imodule.getRenderFilters().end();
  692. it++) {
  693. OSISHTMLHREF *f1 = dynamic_cast<OSISHTMLHREF *>(*it);
  694. if (f1)
  695. f1->setRenderNoteNumbers((ops->xrefnotenumbers != 0));
  696. ThMLHTMLHREF *f2 = dynamic_cast<ThMLHTMLHREF *>(*it);
  697. if (f2)
  698. f2->setRenderNoteNumbers((ops->xrefnotenumbers != 0));
  699. GBFHTMLHREF *f3 = dynamic_cast<GBFHTMLHREF *>(*it);
  700. if (f3)
  701. f3->setRenderNoteNumbers((ops->xrefnotenumbers != 0));
  702. TEIHTMLHREF *f4 = dynamic_cast<TEIHTMLHREF *>(*it);
  703. if (f4)
  704. f4->setRenderNoteNumbers((ops->xrefnotenumbers != 0));
  705. OSISXHTML *f5 = dynamic_cast<OSISXHTML *>(*it);
  706. if (f5)
  707. f5->setRenderNoteNumbers((ops->xrefnotenumbers != 0));
  708. ThMLXHTML *f6 = dynamic_cast<ThMLXHTML *>(*it);
  709. if (f6)
  710. f6->setRenderNoteNumbers((ops->xrefnotenumbers != 0));
  711. GBFXHTML *f7 = dynamic_cast<GBFXHTML *>(*it);
  712. if (f7)
  713. f7->setRenderNoteNumbers((ops->xrefnotenumbers != 0));
  714. }
  715. }
  716. #endif /* !NO_SWORD_SET_RENDER_NOTE_NUMBERS */
  717. //
  718. // display of commentary by chapter.
  719. //
  720. char
  721. GTKEntryDisp::DisplayByChapter(SWModule &imodule)
  722. {
  723. imodule.setSkipConsecutiveLinks(true);
  724. VerseKey *key = (VerseKey *)(SWKey *)imodule;
  725. bool before_curVerse(true);
  726. int curVerse = key->Verse();
  727. int curChapter = key->Chapter();
  728. int curBook = key->Book();
  729. gchar *buf, *vbuf, *num;
  730. char *ModuleName = imodule.Name();
  731. GString *rework; // for image size analysis rework.
  732. footnote = xref = 0;
  733. // we come into this routine with swbuf init'd with
  734. // boilerplate html startup, plus ops and mf ready.
  735. cache_flags = ConstructFlags(ops);
  736. is_rtol = main_is_mod_rtol(ModuleName);
  737. strongs_and_morph = ((ops->strongs || ops->lemmas) &&
  738. ops->morphs);
  739. strongs_or_morph = ((ops->strongs || ops->lemmas) ||
  740. ops->morphs);
  741. if (strongs_and_morph)
  742. set_morph_order(imodule);
  743. set_render_numbers(imodule, ops);
  744. // open the table.
  745. if (settings.showversenum) {
  746. buf = g_strdup_printf("<font face=\"%s\"><table border=\"0\""
  747. " cellpadding=\"5\" cellspacing=\"0\">",
  748. ((mf->old_font) ? mf->old_font : ""));
  749. swbuf.append(buf);
  750. g_free(buf);
  751. }
  752. for (key->Verse(1);
  753. (key->Book() == curBook) &&
  754. (key->Chapter() == curChapter) &&
  755. !imodule.Error();
  756. imodule++) {
  757. ModuleCache::CacheVerse& cVerse = ModuleMap
  758. [ModuleName]
  759. [key->Testament()]
  760. [key->Book()]
  761. [key->Chapter()]
  762. [key->Verse()];
  763. // use the module cache rather than re-accessing Sword.
  764. // but editable personal commentaries don't use the cache.
  765. if (!cVerse.CacheIsValid(cache_flags) &&
  766. (backend->module_type(imodule.Name()) != PERCOM_TYPE)) {
  767. rework = g_string_new(strongs_or_morph
  768. ? block_render(imodule.RenderText())
  769. : imodule.RenderText());
  770. rework = CleanupContent(rework, ops, imodule.Name());
  771. cVerse.SetText(rework->str, cache_flags);
  772. } else {
  773. if (backend->module_type(imodule.Name()) == PERCOM_TYPE)
  774. rework = g_string_new(strongs_or_morph
  775. ? block_render(imodule.getRawEntry())
  776. : imodule.getRawEntry());
  777. else
  778. rework = g_string_new(cVerse.GetText());
  779. }
  780. // add an anchor for where in the chapter we are.
  781. // (commentaries can have big sections on 1 verse [<hr>],
  782. // and many missing verses [<a name>].)
  783. if ((curVerse != 1) && // not at top of pane
  784. ((curVerse == key->Verse()) ||
  785. (before_curVerse && (key->Verse() > curVerse)))) {
  786. buf = NULL;
  787. vbuf = g_strdup_printf("<tr><td>%s<hr/></td><td><hr/></td></tr>",
  788. // repeated conditional check here
  789. ((before_curVerse &&
  790. (key->Verse() > curVerse))
  791. ? (buf = g_strdup_printf(
  792. "<a name=\"%d\"> </a>", curVerse))
  793. : ""));
  794. g_free(buf);
  795. swbuf.append(vbuf);
  796. g_free(vbuf);
  797. }
  798. swbuf.append("<tr>");
  799. // insert verse numbers
  800. num = main_format_number(key->Verse());
  801. vbuf = g_strdup_printf((settings.showversenum
  802. ? "<td valign=\"top\" align=\"right\">"
  803. "<a name=\"%d\" href=\"sword:///%s\">"
  804. "<font size=\"%+d\" color=\"%s\">%s%s%s%s%s%s%s</font></a></td>"
  805. : "<p/><a name=\"%d\"> </a>"),
  806. key->Verse(),
  807. (char*)key->getText(),
  808. settings.verse_num_font_size + settings.base_font_size,
  809. settings.bible_verse_num_color,
  810. (settings.verse_num_superscript ? superscript_start : ""),
  811. (settings.verse_num_bracket ? "[" : ""),
  812. (settings.verse_num_bold ? bold_start : ""),
  813. num,
  814. (settings.verse_num_bold ? bold_end : ""),
  815. (settings.verse_num_bracket ? "]" : ""),
  816. (settings.verse_num_superscript ? superscript_end : ""));
  817. g_free(num);
  818. swbuf.append(vbuf);
  819. if (settings.showversenum) {
  820. buf = g_strdup_printf("<td><font size=\"%+d\">",
  821. mf->old_font_size_value);
  822. swbuf.append(buf);
  823. g_free(buf);
  824. }
  825. swbuf.append(settings.imageresize
  826. ? AnalyzeForImageSize(rework->str,
  827. GDK_WINDOW(gtk_widget_get_window(gtkText)))
  828. : rework->str /* left as-is */);
  829. if (settings.showversenum)
  830. swbuf.append("</font></td>");
  831. g_free(vbuf);
  832. swbuf.append("</tr>");
  833. before_curVerse = (key->Verse() < curVerse);
  834. }
  835. // if we haven't gotten around to placing the anchor, do so now.
  836. if (before_curVerse) {
  837. buf = g_strdup_printf("<tr><td><a name=\"%d\"> </a><hr/></td><td><hr/></td></tr>",
  838. curVerse);
  839. swbuf.append(buf);
  840. g_free(buf);
  841. }
  842. // close the table.
  843. if (settings.showversenum)
  844. swbuf.append("</table></font>");
  845. swbuf.append("</div></font></body></html>");
  846. buf = g_strdup_printf("%d", curVerse);
  847. HtmlOutput((char *)swbuf.c_str(), gtkText, mf, buf);
  848. g_free(buf);
  849. free_font(mf);
  850. mf = NULL;
  851. g_free(ops);
  852. ops = NULL;
  853. return 0;
  854. }
  855. //
  856. // general display of entries: commentary, genbook, lexdict
  857. //
  858. char
  859. GTKEntryDisp::Display(SWModule &imodule)
  860. {
  861. #ifdef USE_XIPHOS_HTML
  862. if (!gtk_widget_get_realized(GTK_WIDGET(gtkText)))
  863. gtk_widget_realize(gtkText);
  864. #endif
  865. gchar *buf;
  866. mf = get_font(imodule.Name());
  867. swbuf = "";
  868. footnote = xref = 0;
  869. ops = main_new_globals(imodule.Name());
  870. GString *rework; // for image size analysis rework.
  871. (const char *)imodule; // snap to entry
  872. main_set_global_options(ops);
  873. strongs_and_morph = ((ops->strongs || ops->lemmas) &&
  874. ops->morphs);
  875. strongs_or_morph = ((ops->strongs || ops->lemmas) ||
  876. ops->morphs);
  877. if (strongs_and_morph)
  878. set_morph_order(imodule);
  879. set_render_numbers(imodule, ops);
  880. buf = g_strdup_printf(HTML_START
  881. "<body bgcolor=\"%s\" text=\"%s\" link=\"%s\">"
  882. "<font face=\"%s\" size=\"%+d\">"
  883. "[<a href=\"passagestudy.jsp?action=showModInfo&value=%s&module=%s\">"
  884. "<font color=\"%s\">*%s*</font></a>]<br/>",
  885. (strongs_and_morph // both
  886. ? CSS_BLOCK_BOTH
  887. : (strongs_or_morph // either
  888. ? CSS_BLOCK_ONE
  889. : (ops->doublespace // neither
  890. ? DOUBLE_SPACE
  891. : ""))),
  892. settings.bible_bg_color,
  893. settings.bible_text_color,
  894. settings.link_color,
  895. ((mf->old_font) ? mf->old_font : ""),
  896. mf->old_font_size_value,
  897. imodule.Description(),
  898. imodule.Name(),
  899. settings.bible_verse_num_color,
  900. imodule.Name());
  901. swbuf.append(buf);
  902. g_free(buf);
  903. swbuf.appendFormatted("<div dir=%s>",
  904. ((is_rtol && !ops->transliteration)
  905. ? "rtl"
  906. : "ltr"));
  907. //
  908. // the rest of this routine is irrelevant if we are
  909. // instead heading off to show a whole chapter
  910. // (this option can be enabled only in commentaries.)
  911. //
  912. if (ops->commentary_by_chapter)
  913. return DisplayByChapter(imodule);
  914. // we will use the module cache for regular commentaries,
  915. // which navigate/change a lot, whereas pers.comms, lexdicts,
  916. // and genbooks still do fresh access every time -- the nature
  917. // of those modules' use won't buy much with a module cache.
  918. // there is some unfortunate but unavoidable code duplication
  919. // for handling potential clearing of images, due to the
  920. // difference in how modules are being accessed.
  921. if (backend->module_type(imodule.Name()) == COMMENTARY_TYPE) {
  922. VerseKey *key = (VerseKey *)(SWKey *)imodule;
  923. cache_flags = ConstructFlags(ops);
  924. const char *ModuleName = imodule.Name();
  925. ModuleCache::CacheVerse& cVerse = ModuleMap
  926. [ModuleName]
  927. [key->Testament()]
  928. [key->Book()]
  929. [key->Chapter()]
  930. [key->Verse()];
  931. // use the module cache rather than re-accessing Sword.
  932. if (!cVerse.CacheIsValid(cache_flags)) {
  933. rework = g_string_new(strongs_or_morph
  934. ? block_render(imodule.RenderText())
  935. : imodule.RenderText());
  936. rework = CleanupContent(rework, ops, imodule.Name());
  937. cVerse.SetText(rework->str, cache_flags);
  938. } else
  939. rework = g_string_new(cVerse.GetText());
  940. } else {
  941. if ((backend->module_type(imodule.Name()) == PERCOM_TYPE) ||
  942. (backend->module_type(imodule.Name()) == PRAYERLIST_TYPE))
  943. rework = g_string_new(strongs_or_morph
  944. ? block_render(imodule.getRawEntry())
  945. : imodule.getRawEntry());
  946. else
  947. rework = g_string_new(strongs_or_morph
  948. ? block_render(imodule.RenderText())
  949. : imodule.RenderText());
  950. rework = CleanupContent(rework, ops, imodule.Name());
  951. }
  952. swbuf.append(settings.imageresize
  953. ? AnalyzeForImageSize(rework->str,
  954. GDK_WINDOW(gtk_widget_get_window(gtkText)))
  955. : rework->str /* left as-is */);
  956. swbuf.append("</div></font></body></html>");
  957. HtmlOutput((char *)swbuf.c_str(), gtkText, mf, NULL);
  958. free_font(mf);
  959. mf = NULL;
  960. g_free(ops);
  961. ops = NULL;
  962. return 0;
  963. }
  964. void
  965. GTKChapDisp::getVerseBefore(SWModule &imodule)
  966. {
  967. gchar *buf;
  968. char *num;
  969. sword::VerseKey *key = (VerseKey *)(SWKey *)imodule;
  970. int curVerse = key->Verse();
  971. int chapter = key->Chapter();
  972. int curBook = key->Book();
  973. int curTest = key->Testament();
  974. #if 0
  975. const char *ModuleName = imodule.Name();
  976. #endif
  977. key->Verse(1);
  978. imodule--;
  979. if (imodule.Error()) {
  980. num = main_format_number(chapter);
  981. buf = g_strdup_printf("<a name=\"TOP\"></a><div style=\"text-align: center\">"
  982. "<p><b><font size=\"%+d\">%s</font></b></p>"
  983. "<b>%s %s</b></div>",
  984. 1 + mf->old_font_size_value,
  985. imodule.Description(),
  986. _("Chapter"),
  987. num);
  988. g_free(num);
  989. swbuf.append(buf);
  990. g_free(buf);
  991. } else {
  992. if (strongs_and_morph)
  993. set_morph_order(imodule);
  994. set_render_numbers(imodule, ops);
  995. #if 0 // with footnote counting, we no longer cache before/after verses.
  996. ModuleCache::CacheVerse& cVerse = ModuleMap
  997. [ModuleName]
  998. [key->Testament()]
  999. [key->Book()]
  1000. [key->Chapter()]
  1001. [key->Verse()];
  1002. if (!cVerse.CacheIsValid(cache_flags))
  1003. cVerse.SetText((strongs_or_morph
  1004. ? block_render((const char *)imodule)
  1005. : (const char *)imodule),
  1006. cache_flags);
  1007. #else
  1008. const gchar *text = (strongs_or_morph
  1009. ? block_render((const char *)imodule)
  1010. : (const char *)imodule);
  1011. #endif /* !0 */
  1012. swbuf.appendFormatted("<div dir=%s>",
  1013. ((is_rtol && !ops->transliteration)
  1014. ? "rtl"
  1015. : "ltr"));
  1016. num = main_format_number(key->Verse());
  1017. buf = g_strdup_printf(settings.showversenum
  1018. ? "&nbsp; <a name=\"%d\" href=\"sword:///%s\">"
  1019. "<font size=\"%+d\" color=\"%s\">%s%s%s%s%s%s%s</font></a>&nbsp;"
  1020. : "&nbsp; <a name=\"%d\"> </a>",
  1021. 0,
  1022. (char*)key->getText(),
  1023. ((settings.versestyle)
  1024. ? settings.verse_num_font_size + settings.base_font_size
  1025. : settings.base_font_size - 2),
  1026. settings.bible_verse_num_color,
  1027. (settings.verse_num_superscript ? superscript_start : ""),
  1028. (settings.verse_num_bracket ? "[" : ""),
  1029. (settings.verse_num_bold ? bold_start : ""),
  1030. num,
  1031. (settings.verse_num_bold ? bold_end : ""),
  1032. (settings.verse_num_bracket ? "]" : ""),
  1033. (settings.verse_num_superscript ? superscript_end : ""));
  1034. g_free(num);
  1035. swbuf.append(buf);
  1036. g_free(buf);
  1037. num = main_format_number(chapter);
  1038. buf = g_strdup_printf(
  1039. "%s%s<br/><a name=\"TOP\"></a><hr/><div style=\"text-align: center\"><b>%s %s</b></div>",
  1040. #if 0
  1041. cVerse.GetText(),
  1042. #else
  1043. text,
  1044. #endif
  1045. // extra break when excess strongs/morph space.
  1046. (strongs_or_morph ? "<br/>" : ""),
  1047. _("Chapter"), num);
  1048. g_free(num);
  1049. swbuf.append(buf);
  1050. g_free(buf);
  1051. swbuf.append("</div>");
  1052. }
  1053. imodule++;
  1054. if (!ops->headings)
  1055. return;
  1056. //
  1057. // Display content at 0:0 and n:0.
  1058. //
  1059. char oldAutoNorm = key->AutoNormalize();
  1060. key->AutoNormalize(0);
  1061. for (int i = 0; i < 2; ++i) {
  1062. // Get chapter 0 iff we're in chapter 1.
  1063. if ((i == 0) && (chapter != 1))
  1064. continue;
  1065. key->Chapter(i*chapter);
  1066. key->Verse(0);
  1067. #if 0 // with footnote counting, we no longer cache before/after verses.
  1068. ModuleCache::CacheVerse& cVerse = ModuleMap
  1069. [ModuleName]
  1070. [key->Testament()]
  1071. [key->Book()]
  1072. [key->Chapter()]
  1073. [key->Verse()];
  1074. if (!cVerse.CacheIsValid(cache_flags))
  1075. cVerse.SetText((strongs_or_morph
  1076. ? block_render((const char *)imodule)
  1077. : (const char *)imodule),
  1078. cache_flags);
  1079. buf = g_strdup_printf("%s<br />", cVerse.GetText());
  1080. #else
  1081. buf = g_strdup_printf("%s<br />", (strongs_or_morph
  1082. ? block_render((const char *)imodule)
  1083. : (const char *)imodule));
  1084. #endif /* !0 */
  1085. swbuf.append(buf);
  1086. g_free(buf);
  1087. }
  1088. key->AutoNormalize(oldAutoNorm);
  1089. key->Book(curBook);
  1090. key->Chapter(chapter);
  1091. key->Verse(curVerse);
  1092. key->Testament(curTest);
  1093. }
  1094. void
  1095. GTKChapDisp::getVerseAfter(SWModule &imodule)
  1096. {
  1097. gchar *buf;
  1098. #if 0
  1099. const char *ModuleName = imodule.Name();
  1100. #endif
  1101. sword::VerseKey *key = (VerseKey *)(SWKey *)imodule;
  1102. imodule++;
  1103. if (imodule.Error()) {
  1104. buf = g_strdup_printf(
  1105. "%s<hr/><div style=\"text-align: center\"><p><b>%s</b></p></div>",
  1106. // extra break when excess strongs/morph space.
  1107. (strongs_or_morph ? "<br/><br/>" : ""),
  1108. imodule.Description());
  1109. swbuf.append(buf);
  1110. g_free(buf);
  1111. } else {
  1112. imodule--;
  1113. char *num = main_format_number(key->Chapter());
  1114. buf = g_strdup_printf(
  1115. "%s<hr/><div style=\"text-align: center\"><b>%s %s</b></div>",
  1116. (strongs_or_morph ? "<br/><br/>" : ""),
  1117. _("Chapter"), num);
  1118. swbuf.append(buf);
  1119. g_free(buf);
  1120. g_free(num);
  1121. swbuf.appendFormatted("<div dir=%s>",
  1122. ((is_rtol && !ops->transliteration)
  1123. ? "rtl"
  1124. : "ltr"));
  1125. num = main_format_number(key->Verse());
  1126. buf = g_strdup_printf(settings.showversenum
  1127. ? "&nbsp; <a name=\"%d\" href=\"sword:///%s\">"
  1128. "<font size=\"%+d\" color=\"%s\">%s%s%s%s%s%s%s</font></a>&nbsp;"
  1129. : "&nbsp; <a name=\"%d\"> </a>",
  1130. 0,
  1131. (char*)key->getText(),
  1132. ((settings.versestyle)
  1133. ? settings.verse_num_font_size + settings.base_font_size
  1134. : settings.base_font_size - 2),
  1135. settings.bible_verse_num_color,
  1136. (settings.verse_num_superscript ? superscript_start : ""),
  1137. (settings.verse_num_bracket ? "[" : ""),
  1138. (settings.verse_num_bold ? bold_start : ""),
  1139. num,
  1140. (settings.verse_num_bold ? bold_end : ""),
  1141. (settings.verse_num_bracket ? "]" : ""),
  1142. (settings.verse_num_superscript ? superscript_end : ""));
  1143. g_free(num);
  1144. swbuf.append(buf);
  1145. g_free(buf);
  1146. if (strongs_and_morph)
  1147. set_morph_order(imodule);
  1148. set_render_numbers(imodule, ops);
  1149. #if 0 // with footnote counting, we no longer cache before/after verses.
  1150. ModuleCache::CacheVerse& cVerse = ModuleMap
  1151. [ModuleName]
  1152. [key->Testament()]
  1153. [key->Book()]
  1154. [key->Chapter()]
  1155. [key->Verse()];
  1156. if (!cVerse.CacheIsValid(cache_flags))
  1157. cVerse.SetText((strongs_or_morph
  1158. ? block_render((const char *)imodule)
  1159. : (const char *)imodule),
  1160. cache_flags);
  1161. swbuf.append(cVerse.GetText());
  1162. #else
  1163. swbuf.append((strongs_or_morph
  1164. ? block_render((const char *)imodule)
  1165. : (const char *)imodule));
  1166. #endif /* !0 */
  1167. swbuf.append("</div>");
  1168. }
  1169. }
  1170. char
  1171. GTKChapDisp::Display(SWModule &imodule)
  1172. {
  1173. // following line ensures linked verses work correctly
  1174. // it does not solve the problem of marking groups of verses (1-4), etc
  1175. imodule.setSkipConsecutiveLinks(true);
  1176. VerseKey *key = (VerseKey *)(SWKey *)imodule;
  1177. int curVerse = key->Verse();
  1178. int curChapter = key->Chapter();
  1179. int curBook = key->Book();
  1180. gchar *buf;
  1181. char *num;
  1182. const gchar *paragraphMark = NULL;
  1183. gboolean newparagraph = FALSE;
  1184. GString *rework; // for image size analysis rework.
  1185. char *ModuleName = imodule.Name();
  1186. ops = main_new_globals(ModuleName);
  1187. cache_flags = ConstructFlags(ops);
  1188. marked_element *e = NULL;
  1189. is_rtol = main_is_mod_rtol(ModuleName);
  1190. mf = get_font(ModuleName);
  1191. if (!gtk_widget_get_realized (GTK_WIDGET(gtkText))) return 0;
  1192. strongs_and_morph = ((ops->strongs || ops->lemmas) &&
  1193. ops->morphs);
  1194. strongs_or_morph = ((ops->strongs || ops->lemmas) ||
  1195. ops->morphs);
  1196. if (strongs_and_morph)
  1197. set_morph_order(imodule);
  1198. set_render_numbers(imodule, ops);
  1199. // when strongs/morph are on, the anchor boundary must be smaller.
  1200. // or if main window is too small to keep curverse in-pane,
  1201. // of it the user wants really big fonts.
  1202. gint display_boundary = (((settings.gs_height < 500) ||
  1203. (mf->old_font_size_value > 2))
  1204. ? 0
  1205. : (strongs_or_morph ? 1 : 2));
  1206. // if we are no longer where annotations were current, re-load.
  1207. if (strcasecmp(ModuleName,
  1208. (marked_cache_modname ? marked_cache_modname : "")) ||
  1209. strcasecmp(key->getBookAbbrev(), marked_cache_book) ||
  1210. (curChapter != marked_cache_chapter))
  1211. marked_cache_fill(ModuleName, settings.currentverse);
  1212. if (!strcmp(ModuleName, "KJV"))
  1213. paragraphMark = "&para;&nbsp;";
  1214. else
  1215. paragraphMark = "";
  1216. swbuf = "";
  1217. footnote = xref = 0;
  1218. buf = g_strdup_printf(HTML_START
  1219. "<body bgcolor=\"%s\" text=\"%s\" link=\"%s\">"
  1220. "<font face=\"%s\" size=\"%+d\">",
  1221. // strongs & morph specs win over dblspc.
  1222. (strongs_and_morph // both
  1223. ? CSS_BLOCK_BOTH
  1224. : (strongs_or_morph // either
  1225. ? CSS_BLOCK_ONE
  1226. : (ops->doublespace // neither
  1227. ? DOUBLE_SPACE
  1228. : ""))),
  1229. settings.bible_bg_color,
  1230. settings.bible_text_color,
  1231. settings.link_color,
  1232. ((mf->old_font) ? mf->old_font : ""),
  1233. mf->old_font_size_value);
  1234. swbuf.append(buf);
  1235. g_free(buf);
  1236. swbuf.appendFormatted("<div dir=%s>",
  1237. ((is_rtol && !ops->transliteration)
  1238. ? "rtl"
  1239. : "ltr"));
  1240. main_set_global_options(ops);
  1241. getVerseBefore(imodule);
  1242. for (key->Verse(1);
  1243. (key->Book() == curBook) &&
  1244. (key->Chapter() == curChapter) &&
  1245. !imodule.Error();
  1246. imodule++) {
  1247. ModuleCache::CacheVerse& cVerse = ModuleMap
  1248. [ModuleName]
  1249. [key->Testament()]
  1250. [key->Book()]
  1251. [key->Chapter()]
  1252. [key->Verse()];
  1253. // use the module cache rather than re-accessing Sword.
  1254. if (!cVerse.HeaderIsValid())
  1255. CacheHeader(cVerse, imodule, ops, be);
  1256. if (cache_flags & ModuleCache::Headings) {
  1257. swbuf.append(settings.imageresize
  1258. ? AnalyzeForImageSize(cVerse.GetHeader(),
  1259. GDK_WINDOW(gtk_widget_get_window(gtkText)))
  1260. : cVerse.GetHeader() /* left as-is */);
  1261. } else
  1262. cVerse.InvalidateHeader();
  1263. if (!cVerse.CacheIsValid(cache_flags)) {
  1264. rework = g_string_new(strongs_or_morph
  1265. ? block_render(imodule.RenderText())
  1266. : imodule.RenderText());
  1267. rework = CleanupContent(rework, ops, imodule.Name());
  1268. cVerse.SetText(rework->str, cache_flags);
  1269. } else
  1270. rework = g_string_new(cVerse.GetText());
  1271. // special contrasty highlighting
  1272. if (((e = marked_cache_check(key->Verse())) &&
  1273. settings.annotate_highlight) ||
  1274. ((key->Verse() == curVerse) &&
  1275. settings.versehighlight)) {
  1276. buf = g_strdup_printf(
  1277. #ifdef USE_XIPHOS_HTML
  1278. "<span style=\"background-color: %s\">"
  1279. #else
  1280. "<table bgcolor=\"%s\"><tr><td>"
  1281. #endif
  1282. "<font face=\"%s\" size=\"%+d\">",
  1283. ((settings.annotate_highlight && e)
  1284. ? settings.highlight_fg
  1285. : settings.highlight_bg),
  1286. ((mf->old_font) ? mf->old_font : ""),
  1287. mf->old_font_size_value);
  1288. swbuf.append(buf);
  1289. g_free(buf);
  1290. }
  1291. num = main_format_number(key->Verse());
  1292. buf = g_strdup_printf(settings.showversenum
  1293. ? "&nbsp; <span class=\"word\"><a name=\"%d\" href=\"sword:///%s\">"
  1294. "<font size=\"%+d\" color=\"%s\">%s%s%s%s%s%s%s</font></a></span>&nbsp;"
  1295. : "&nbsp; <a name=\"%d\"> </a>",
  1296. key->Verse(),
  1297. (char*)key->getText(),
  1298. settings.verse_num_font_size + settings.base_font_size,
  1299. (((settings.versehighlight && (key->Verse() == curVerse)) ||
  1300. (e && settings.annotate_highlight))
  1301. ? ((e && settings.annotate_highlight)
  1302. ? settings.highlight_bg
  1303. : settings.highlight_fg)
  1304. : settings.bible_verse_num_color),
  1305. (settings.verse_num_superscript ? superscript_start : ""),
  1306. (settings.verse_num_bracket ? "[" : ""),
  1307. (settings.verse_num_bold ? bold_start : ""),
  1308. num,
  1309. (settings.verse_num_bold ? bold_end : ""),
  1310. (settings.verse_num_bracket ? "]" : ""),
  1311. (settings.verse_num_superscript ? superscript_end : ""));
  1312. g_free(num);
  1313. swbuf.append(buf);
  1314. g_free(buf);
  1315. // insert the userfootnote reference
  1316. if (e) {
  1317. buf = g_strdup_printf("<span class=\"word\">"
  1318. "<a href=\"passagestudy.jsp?action=showUserNote&"
  1319. "module=%s&passage=%s&value=%s\"><small>"
  1320. "<sup>*u</sup></small></a></span>&nbsp;",
  1321. settings.MainWindowModule,
  1322. (char*)key->getShortText(),
  1323. e->annotation->str);
  1324. swbuf.append(buf);
  1325. g_free(buf);
  1326. }
  1327. if ((key->Verse() == curVerse) || (e && settings.annotate_highlight)) {
  1328. buf = g_strdup_printf("<font color=\"%s\">",
  1329. ((settings.versehighlight ||
  1330. (e && settings.annotate_highlight))
  1331. ? ((e && settings.annotate_highlight)
  1332. ? settings.highlight_bg
  1333. : settings.highlight_fg)
  1334. : settings.currentverse_color));
  1335. swbuf.append(buf);
  1336. g_free(buf);
  1337. }
  1338. if (newparagraph && settings.versestyle) {
  1339. newparagraph = FALSE;
  1340. swbuf.append(paragraphMark);;
  1341. }
  1342. #ifndef USE_XIPHOS_HTML
  1343. // correct a highlight glitch: in poetry verses which end in
  1344. // a forced line break, we must remove the break to prevent
  1345. // the enclosing <table> from producing a double break.
  1346. if ((settings.versehighlight ||
  1347. (e && settings.annotate_highlight)) && // doing <table> h/l.
  1348. !settings.versestyle && // paragraph format.
  1349. (key->Verse() == curVerse)) {
  1350. GString *text = g_string_new(NULL);
  1351. g_string_printf(text, "%s", rework->str);
  1352. if (!strcmp(text->str + text->len - 6, "<br />")) {
  1353. text->len -= 6;
  1354. *(text->str + text->len) = '\0';
  1355. }
  1356. else if (!strcmp(text->str + text->len - 4, "<br/>")) {
  1357. text->len -= 4;
  1358. *(text->str + text->len) = '\0';
  1359. }
  1360. swbuf.append(settings.imageresize
  1361. ? AnalyzeForImageSize(text->str,
  1362. GDK_WINDOW(gtk_widget_get_window(gtkText)))
  1363. : text->str /* left as-is */);
  1364. g_string_free(text, TRUE);
  1365. } else
  1366. #endif /* USE_XIPHOS_HTML */
  1367. swbuf.append(settings.imageresize
  1368. ? AnalyzeForImageSize(rework->str,
  1369. GDK_WINDOW(gtk_widget_get_window(gtkText)))
  1370. : rework->str /* left as-is */);
  1371. if (key->Verse() == curVerse) {
  1372. swbuf.append("</font>");
  1373. ReadAloud(curVerse, rework->str);
  1374. }
  1375. if (settings.versestyle) {
  1376. if (strstr(rework->str, "<!p>")) {
  1377. newparagraph = TRUE;
  1378. } else {
  1379. newparagraph = FALSE;
  1380. }
  1381. if ((key->Verse() != curVerse) ||
  1382. (!settings.versehighlight &&
  1383. (!e || !settings.annotate_highlight)))
  1384. swbuf.append("<br/>");
  1385. #ifdef USE_XIPHOS_HTML
  1386. else if (key->Verse() == curVerse)
  1387. swbuf.append("<br/>");
  1388. #endif
  1389. }
  1390. // special contrasty highlighting
  1391. if (((key->Verse() == curVerse) && settings.versehighlight) ||
  1392. (e && settings.annotate_highlight))
  1393. #ifdef USE_XIPHOS_HTML
  1394. swbuf.append("</font></span>");
  1395. #else
  1396. swbuf.append("</font></td></tr></table>");
  1397. #endif
  1398. }
  1399. getVerseAfter(imodule);
  1400. // Reset the Bible location before GTK gets access:
  1401. // Mouse activity destroys this key, so we must be finished with it.
  1402. key->Book(curBook);
  1403. key->Chapter(curChapter);
  1404. key->Verse(curVerse);
  1405. swbuf.append("</div></font></body></html>");
  1406. #ifdef USE_XIPHOS_HTML
  1407. if (strongs_and_morph && (curVerse != 1))
  1408. buf = g_strdup_printf("%d", curVerse);
  1409. else /* this is not dangling: connects to following "if" */
  1410. #endif /* USE_XIPHOS_HTML */
  1411. if ((curVerse == 1) || (display_boundary >= curVerse))
  1412. buf = g_strdup("TOP");
  1413. else if (curVerse > display_boundary)
  1414. buf = g_strdup_printf("%d", curVerse - display_boundary);
  1415. else
  1416. buf = NULL;
  1417. HtmlOutput((char *)swbuf.c_str(), gtkText, mf, buf);
  1418. if (buf)
  1419. g_free(buf);
  1420. free_font(mf);
  1421. mf = NULL;
  1422. g_free(ops);
  1423. ops = NULL;
  1424. return 0;
  1425. }
  1426. //
  1427. // display of commentary by chapter.
  1428. //
  1429. char
  1430. DialogEntryDisp::DisplayByChapter(SWModule &imodule)
  1431. {
  1432. imodule.setSkipConsecutiveLinks(true);
  1433. VerseKey *key = (VerseKey *)(SWKey *)imodule;
  1434. int curVerse = key->Verse();
  1435. int curChapter = key->Chapter();
  1436. int curBook = key->Book();
  1437. gchar *buf;
  1438. char *ModuleName = imodule.Name();
  1439. GString *rework; // for image size analysis rework.
  1440. footnote = xref = 0;
  1441. // we come into this routine with swbuf init'd with
  1442. // boilerplate html startup, plus ops and mf ready.
  1443. cache_flags = ConstructFlags(ops);
  1444. is_rtol = main_is_mod_rtol(ModuleName);
  1445. strongs_and_morph = ((ops->strongs || ops->lemmas) &&
  1446. ops->morphs);
  1447. strongs_or_morph = ((ops->strongs || ops->lemmas) ||
  1448. ops->morphs);
  1449. if (strongs_and_morph)
  1450. set_morph_order(imodule);
  1451. set_render_numbers(imodule, ops);
  1452. swbuf.appendFormatted("<div dir=%s>",
  1453. ((is_rtol && !ops->transliteration)
  1454. ? "rtl"
  1455. : "ltr"));
  1456. for (key->Verse(1);
  1457. (key->Book() == curBook) &&
  1458. (key->Chapter() == curChapter) &&
  1459. !imodule.Error();
  1460. imodule++) {
  1461. ModuleCache::CacheVerse& cVerse = ModuleMap
  1462. [ModuleName]
  1463. [key->Testament()]
  1464. [key->Book()]
  1465. [key->Chapter()]
  1466. [key->Verse()];
  1467. // use the module cache rather than re-accessing Sword.
  1468. if (!cVerse.CacheIsValid(cache_flags)) {
  1469. rework = g_string_new(strongs_or_morph
  1470. ? block_render(imodule.RenderText())
  1471. : imodule.RenderText());
  1472. rework = CleanupContent(rework, ops, imodule.Name());
  1473. cVerse.SetText(rework->str, cache_flags);
  1474. } else
  1475. rework = g_string_new(cVerse.GetText());
  1476. buf = g_strdup_printf("<p /><a name=\"%d\"> </a>",
  1477. key->Verse());
  1478. swbuf.append(buf);
  1479. g_free(buf);
  1480. swbuf.append(settings.imageresize
  1481. ? AnalyzeForImageSize(rework->str,
  1482. GDK_WINDOW(gtk_widget_get_window(gtkText)))
  1483. : rework->str /* left as-is */);
  1484. }
  1485. swbuf.append("</div></font></body></html>");
  1486. buf = g_strdup_printf("%d", curVerse);
  1487. HtmlOutput((char *)swbuf.c_str(), gtkText, mf, buf);
  1488. g_free(buf);
  1489. free_font(mf);
  1490. mf = NULL;
  1491. g_free(ops);
  1492. ops = NULL;
  1493. return 0;
  1494. }
  1495. char
  1496. DialogEntryDisp::Display(SWModule &imodule)
  1497. {
  1498. swbuf = "";
  1499. char *buf;
  1500. mf = get_font(imodule.Name());
  1501. ops = main_new_globals(imodule.Name());
  1502. main_set_global_options(ops);
  1503. GString *rework; // for image size analysis rework.
  1504. footnote = xref = 0;
  1505. (const char *)imodule; // snap to entry
  1506. buf = g_strdup_printf(HTML_START
  1507. "<body bgcolor=\"%s\" text=\"%s\" link=\"%s\">"
  1508. "<font face=\"%s\" size=\"%+d\">"
  1509. "<font color=\"%s\">"
  1510. "<a href=\"passagestudy.jsp?action=showModInfo&value=%s&module=%s\">"
  1511. "[*%s*]</a></font><br/>",
  1512. (ops->doublespace ? DOUBLE_SPACE : ""),
  1513. settings.bible_bg_color,
  1514. settings.bible_text_color,
  1515. settings.link_color,
  1516. ((mf->old_font) ? mf->old_font : ""),
  1517. mf->old_font_size_value,
  1518. settings.bible_verse_num_color,
  1519. imodule.Description(),
  1520. imodule.Name(),
  1521. imodule.Name());
  1522. swbuf.append(buf);
  1523. g_free(buf);
  1524. //
  1525. // the rest of this routine is irrelevant if we are
  1526. // instead heading off to show a whole chapter
  1527. //
  1528. if (ops->commentary_by_chapter)
  1529. return DisplayByChapter(imodule);
  1530. if (be->module_type(imodule.Name()) == COMMENTARY_TYPE) {
  1531. VerseKey *key = (VerseKey *)(SWKey *)imodule;
  1532. cache_flags = ConstructFlags(ops);
  1533. const char *ModuleName = imodule.Name();
  1534. ModuleCache::CacheVerse& cVerse = ModuleMap
  1535. [ModuleName]
  1536. [key->Testament()]
  1537. [key->Book()]
  1538. [key->Chapter()]
  1539. [key->Verse()];
  1540. // use the module cache rather than re-accessing Sword.
  1541. if (!cVerse.CacheIsValid(cache_flags)) {
  1542. rework = g_string_new((const char *)imodule);
  1543. rework = CleanupContent(rework, ops, imodule.Name());
  1544. cVerse.SetText(rework->str, cache_flags);
  1545. } else
  1546. rework = g_string_new(cVerse.GetText());
  1547. } else {
  1548. if ((be->module_type(imodule.Name()) == PERCOM_TYPE) ||
  1549. (be->module_type(imodule.Name()) == PRAYERLIST_TYPE))
  1550. rework = g_string_new(imodule.getRawEntry());
  1551. else
  1552. rework = g_string_new((const char *)imodule);
  1553. rework = CleanupContent(rework, ops, imodule.Name());
  1554. }
  1555. swbuf.append(settings.imageresize
  1556. ? AnalyzeForImageSize(rework->str,
  1557. GDK_WINDOW(gtk_widget_get_window(gtkText)))
  1558. : rework->str /* left as-is */);
  1559. swbuf.append("</font></body></html>");
  1560. HtmlOutput((char *)swbuf.c_str(), gtkText, mf, NULL);
  1561. free_font(mf);
  1562. mf = NULL;
  1563. g_free(ops);
  1564. ops = NULL;
  1565. return 0;
  1566. }
  1567. char
  1568. DialogChapDisp::Display(SWModule &imodule)
  1569. {
  1570. imodule.setSkipConsecutiveLinks(true);
  1571. VerseKey *key = (VerseKey *)(SWKey *)imodule;
  1572. int curVerse = key->Verse();
  1573. int curChapter = key->Chapter();
  1574. int curBook = key->Book();
  1575. gchar *buf;
  1576. char *num;
  1577. const gchar *paragraphMark

Large files files are truncated, but you can click here to view the full file