1 /*
2 * Xiphos Bible Study Tool
3 * previewer.cc -
4 *
5 * Copyright (C) 2000-2020 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
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <gtk/gtk.h>
27 #include <swmgr.h>
28 #include <swmodule.h>
29 #include <stringmgr.h>
30 #include <localemgr.h>
31
32 extern "C" {
33 #include "gui/bibletext.h"
34 }
35
36 #include <ctype.h>
37 #include <time.h>
38 #include "gui/widgets.h"
39 #include "gui/sidebar.h"
40 #include "gui/utilities.h"
41 #include "main/previewer.h"
42 #include "main/settings.h"
43 #include "main/sword.h"
44 #include "main/xml.h"
45 #include "backend/sword_main.hh"
46
47 #include "xiphos_html/xiphos_html.h"
48
49 #include "gui/debug_glib_null.h"
50
51 #define HTML_START "<html><head><meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\
52 <style type=\"text/css\"><!-- \
53 A { text-decoration:none } \
54 *[dir=rtl] { text-align: right;} \
55 body {background-color:%s;color:%s;} \
56 a:link{color:%s} -->\
57 </style></head><body>"
58
59 using namespace std;
60
61 static GtkWidget *previewer_html_widget;
62
main_set_previewer_widget(int in_sidebar)63 void main_set_previewer_widget(int in_sidebar)
64 {
65 if (in_sidebar)
66 previewer_html_widget = sidebar.html_viewer_widget;
67 else
68 previewer_html_widget = widgets.html_previewer_text;
69 }
70
71 /******************************************************************************
72 * Name
73 * main_init_previewer
74 *
75 * Synopsis
76 * #include "main/previewer.h"
77 *
78 * void main_init_previewer(void)
79 *
80 * Description
81 * clear the information viewer
82 *
83 * Return value
84 * void
85 */
86
main_init_previewer(void)87 void main_init_previewer(void)
88 {
89 GString *str = g_string_new(NULL);
90 gchar *buf = _("Previewer");
91 MOD_FONT *mf = get_font(settings.MainWindowModule);
92
93 g_string_printf(str,
94 HTML_START
95 "<font color=\"grey\" face=\"%s\" size=\"%+d\"><b>%s</b></font><hr/></body></html>",
96 settings.bible_bg_color, settings.bible_text_color,
97 settings.link_color,
98 ((mf->old_font) ? mf->old_font : ""),
99 mf->old_font_size_value - 1,
100 buf);
101 free_font(mf);
102 HtmlOutput(str->str, previewer_html_widget, NULL, NULL);
103 g_string_free(str, TRUE);
104 }
105
106 /******************************************************************************
107 * Name
108 * main_information_viewer
109 *
110 * Synopsis
111 * #include "main/previewer.h"
112 *
113 * void main_information_viewer(GtkWidget * html_widget, gchar * mod_name,
114 * gchar * text, gchar *key, gchar * type)
115 *
116 * Description
117 * display information in the information previewer
118 *
119 * Return value
120 * void
121 */
122
main_information_viewer(const gchar * mod_name,const gchar * text,const gchar * key,const gchar * action,const gchar * type,const gchar * morph_text,const gchar * morph)123 void main_information_viewer(const gchar *mod_name,
124 const gchar *text,
125 const gchar *key,
126 const gchar *action,
127 const gchar *type,
128 const gchar *morph_text,
129 const gchar *morph)
130 {
131 GString *tmp_str = g_string_new(NULL);
132 GString *str;
133 MOD_FONT *mf = get_font((gchar *)mod_name);
134
135 g_string_printf(tmp_str,
136 HTML_START
137 "<font face=\"%s\" size=\"%+d\">",
138 settings.bible_bg_color, settings.bible_text_color,
139 settings.link_color,
140 (mf->old_font ? mf->old_font : "none"),
141 mf->old_font_size_value - 1);
142
143 str = g_string_new(tmp_str->str);
144
145 if (type) {
146 if (*type == 'n') {
147 g_string_printf(tmp_str,
148 "<font color=\"grey\">%s</font><hr/>",
149 _("Footnote"));
150 str = g_string_append(str, tmp_str->str);
151 } else if (*type == 'u') {
152 g_string_printf(tmp_str,
153 "<font color=\"grey\">%s:<br/>%s</font><hr/>",
154 _("User Annotation"), key);
155 str = g_string_append(str, tmp_str->str);
156 } else if (*type == 'x') {
157 g_string_printf(tmp_str,
158 "<font color=\"grey\">%s</font><hr/>",
159 _("Cross Reference"));
160 str = g_string_append(str, tmp_str->str);
161 } else if (!strcmp(action, "showStrongs")) {
162 g_string_printf(tmp_str,
163 "<font color=\"grey\">%s: %s</font><hr/>",
164 _("Strongs"), key);
165 str = g_string_append(str, tmp_str->str);
166 } else if (!strcmp(action, "showMorph")) {
167 g_string_printf(tmp_str,
168 "<font color=\"grey\">%s: %s</font><hr/>",
169 _("Morphology"), key);
170 str = g_string_append(str, tmp_str->str);
171 }
172 } else {
173 const char *abbreviation = main_get_abbreviation(mod_name);
174 g_string_printf(tmp_str,
175 "<font color=\"grey\">%s: %s</font><hr/>",
176 (abbreviation ? abbreviation : mod_name),
177 key);
178 str = g_string_append(str, tmp_str->str);
179 }
180
181 if (action && (!strcmp(action, "showStrongsMorph"))) {
182 g_string_printf(tmp_str,
183 "<font color=\"grey\">%s: %s</font><hr/>",
184 _("Strongs"), key);
185 str = g_string_append(str, tmp_str->str);
186 str = g_string_append(str, text);
187
188 g_string_printf(tmp_str,
189 "<font color=\"grey\"><br/><br/>%s: %s</font><hr/>",
190 _("Morphology"), morph);
191 str = g_string_append(str, tmp_str->str);
192 str = g_string_append(str, morph_text);
193 } else {
194 str = g_string_append(str, text);
195 }
196
197 str = g_string_append(str, "</font></body></html>");
198
199 HtmlOutput((char *)AnalyzeForImageSize(str->str,
200 GDK_WINDOW(gtk_widget_get_window(previewer_html_widget))),
201 previewer_html_widget, mf, NULL);
202 free_font(mf);
203 g_string_free(str, TRUE);
204 g_string_free(tmp_str, TRUE);
205 }
206
207 /******************************************************************************
208 * Name
209 * mark_search_words
210 *
211 * Synopsis
212 * #include "main/previewer.h"
213 *
214 * void mark_search_words(GString *str)
215 *
216 * Description
217 * highlights ("purplifies," formerly) search terms in results.
218 *
219 * Return value
220 * void
221 */
222
mark_search_words(GString * str)223 void mark_search_words(GString *str)
224 {
225 gchar *tmpbuf, *buf, *searchbuf;
226 gint len_overall, len_word, len_tail, len_prefix;
227 gchar closestr[40], openstr[80];
228
229 /* regular expression search results **fixme** */
230 if ((settings.searchType == 0) ||
231 (settings.searchText[0] == '\0')) {
232 return;
233 }
234 XI_message(("%s", settings.searchText));
235
236 /* open and close tags */
237 sprintf(openstr,
238 "<span style=\"background-color: %s; color: %s\">",
239 settings.highlight_fg, settings.highlight_bg);
240 sprintf(closestr, "</span>");
241
242 searchbuf = g_utf8_casefold(g_strdup(settings.searchText), -1);
243 if (g_str_has_prefix(searchbuf, "\"")) {
244 searchbuf = g_strdelimit(searchbuf, "\"", ' ');
245 g_strstrip(searchbuf);
246 }
247
248 buf = g_utf8_casefold(str->str, -1);
249
250 /* if we have a muti word search go here */
251 if (settings.searchType == -2 || settings.searchType == -4) {
252 char *token;
253 GList *list, *listbase;
254 gint count = 0, i = 0;
255
256 list = NULL;
257 /* separate the search words and add them to a glist */
258 if ((token = strtok(searchbuf, " ")) != NULL) {
259 if (!isalnum(*token) && isalnum(*(token + 1)))
260 token++; // skip leading punctuation.
261 if (!strncmp(token, "lemma:", 6))
262 token += 7; // skip the H/G qualifier.
263 list = g_list_append(list, token);
264 ++count;
265 while ((token = strtok(NULL, " ")) != NULL) {
266 if (!isalnum(*token) && isalnum(*(token + 1)))
267 token++;
268 if (!strncmp(token, "lemma:", 6))
269 token += 7; // skip the H/G qualifier.
270 list = g_list_append(list, token);
271 ++count;
272 }
273 /* if we have only one word */
274 } else {
275 list = g_list_append(list, searchbuf);
276 count = 1;
277 }
278
279 listbase = list = g_list_first(list);
280
281 for (i = 0; i < count; i++) {
282 len_overall = strlen(buf);
283 if (settings.searchType == -4) {
284 // remove metachars and anything following ("WORD*")
285 for (tmpbuf = (gchar *)list->data;
286 *tmpbuf && isalnum(*tmpbuf);
287 ++tmpbuf)
288 ; // nothing, just skipping to end or non-alnum
289 if ((tmpbuf != list->data) && *tmpbuf)
290 *tmpbuf = '\0';
291 else if (!sword::stricmp((gchar *)list->data, "and") ||
292 !sword::stricmp((gchar *)list->data, "or") ||
293 !sword::stricmp((gchar *)list->data, "not")) {
294 // don't color boolean ops.
295 goto next_word;
296 }
297 }
298 len_word = strlen((gchar *)list->data);
299
300 /* find search word in verse */
301 while ((tmpbuf = g_strrstr(buf, (gchar *)list->data)) != NULL) {
302 char *angle_open, *angle_close;
303
304 len_tail = strlen(tmpbuf);
305 len_prefix = len_overall - len_tail;
306
307 // find html <> delimiters preceding this word.
308 *tmpbuf = '\0';
309 angle_open = strrchr(buf, '<');
310 angle_close = strrchr(buf, '>');
311
312 // contortions about that preceding markup.
313 // note placement of <> in good/bad examples.
314 // good: anything WORD anything [none]
315 // good: <token> anything WORD anything [outside]
316 // bad: token> anything <token WORD anything [inside]
317 if (!angle_open || // no open (safe), or
318 (angle_close && // there exists close and
319 (angle_open < angle_close))) // open is before close
320 {
321 // add end tag first
322 str = g_string_insert(str,
323 (len_prefix + len_word),
324 closestr);
325 // then add start tag
326 str = g_string_insert(str,
327 len_prefix,
328 openstr);
329 }
330
331 len_overall = len_prefix;
332 }
333 next_word:
334 g_free(buf);
335 if ((list = g_list_next(list)))
336 buf = g_utf8_casefold(str->str, -1);
337 }
338 g_list_free(listbase);
339
340 /* else we have a phrase and only need to mark it */
341 } else {
342 len_overall = strlen(buf);
343 len_word = strlen(searchbuf);
344 tmpbuf = strstr(buf, searchbuf);
345 if (tmpbuf) {
346 len_tail = strlen(tmpbuf);
347 len_prefix = len_overall - len_tail;
348 /* place end tag first */
349 str = g_string_insert(str, (len_prefix + len_word),
350 closestr);
351 /* then place start tag */
352 /* don't re-assign str here, to keep cppcheck happy */
353 (void) g_string_insert(str, len_prefix, openstr);
354 }
355 }
356 g_free(searchbuf);
357 }
358
359 /******************************************************************************
360 * Name
361 * main_entry_display
362 *
363 * Synopsis
364 * #include ".h"
365 *
366 * void main_entry_display(gpointer data, gchar * mod_name,
367 * gchar * text, gchar * key, gboolean show_key)
368 *
369 * Description
370 * display Sword modules one verse (entry) at a time
371 *
372 * Return value
373 * void
374 */
375
main_entry_display(gpointer data,gchar * mod_name,gchar * text,gchar * key,gboolean show_key)376 void main_entry_display(gpointer data, gchar *mod_name,
377 gchar *text, gchar *key, gboolean show_key)
378 {
379 GtkWidget *html_widget = (GtkWidget *)data;
380 GString *tmp_str = g_string_new(NULL);
381 GString *str;
382 MOD_FONT *mf = get_font(mod_name);
383
384 g_string_printf(tmp_str,
385 HTML_START,
386 settings.bible_bg_color, settings.bible_text_color,
387 settings.link_color);
388
389 str = g_string_new(tmp_str->str);
390
391 g_string_printf(tmp_str,
392 "<font face=\"%s\" size=\"%+d\">",
393 (mf->old_font ? mf->old_font : "none"),
394 mf->old_font_size_value - 1);
395 str = g_string_append(str, tmp_str->str);
396
397 /* show key in html widget */
398 if (show_key) {
399 const char *abbreviation = main_get_abbreviation(mod_name);
400 if ((settings.displaySearchResults)) {
401 g_string_printf(tmp_str,
402 "<a href=\"sword://%s/%s\">"
403 "<font color=\"%s\">[%s] %s </font></a><br />",
404 mod_name,
405 key,
406 settings.link_color,
407 (abbreviation ? abbreviation : mod_name),
408 key);
409 } else {
410 g_string_printf(tmp_str,
411 "<a href=\"passagestudy.jsp?action=showModInfo&value=%s&module=%s\">"
412 "<font color=\"%s\">[%s]</a></font><br>[%s]<br />",
413 backend->module_description(mod_name),
414 mod_name,
415 settings.link_color,
416 (abbreviation ? abbreviation : mod_name),
417 key);
418 }
419 str = g_string_append(str, tmp_str->str);
420 }
421
422 if (settings.displaySearchResults) {
423 GString *search_str = g_string_new(text);
424 mark_search_words(search_str);
425 str = g_string_append(str, search_str->str);
426 g_string_free(search_str, TRUE);
427 } else {
428 str = g_string_append(str, text);
429 }
430
431 g_string_printf(tmp_str, " %s", "</font></body></html>");
432 str = g_string_append(str, tmp_str->str);
433
434 HtmlOutput((char *)AnalyzeForImageSize(str->str,
435 GDK_WINDOW(gtk_widget_get_window(html_widget))),
436 html_widget, mf, NULL);
437 free_font(mf);
438 g_string_free(str, TRUE);
439 g_string_free(tmp_str, TRUE);
440 }
441