1 /*
2 * Xiphos Bible Study Tool
3 * main_window.c - main window gui
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
28 #include "xiphos_html/xiphos_html.h"
29 #include "main/sword.h"
30 #include "main/settings.h"
31 #include "main/xml.h"
32 #include "main/search_dialog.h"
33 #include "main/url.hh"
34 #include "main/biblesync_glue.h"
35
36 #include "biblesync/biblesync-version.hh"
37
38 #include "gui/xiphos.h"
39 #include "gui/main_window.h"
40 #include "gui/main_menu.h"
41 #include "gui/sidebar.h"
42 #include "gui/utilities.h"
43 #include "gui/bibletext.h"
44 #include "gui/parallel_view.h"
45 #include "gui/commentary.h"
46 #include "gui/gbs.h"
47 #include "gui/dialog.h"
48 #include "gui/find_dialog.h"
49 #include "gui/dictlex.h"
50 #include "gui/search_dialog.h"
51 #include "gui/commentary_dialog.h"
52 #include "gui/bibletext_dialog.h"
53 #include "gui/bookmark_dialog.h"
54 #include "gui/search_dialog.h"
55 #include "gui/navbar_versekey.h"
56 #include "gui/tabbed_browser.h"
57 #include "gui/widgets.h"
58 #include "gui/tabbed_browser.h"
59 #include "gui/menu_popup.h"
60 #include "gui/preferences_dialog.h"
61
62 #include "editor/slib-editor.h"
63
64 #include "gui/debug_glib_null.h"
65
66 /* X keyboard #definitions, to handle shortcuts */
67 /* we must define the categories of #definitions we need. */
68 #define XK_MISCELLANY
69 #define XK_LATIN1
70 #include <X11/keysymdef.h>
71
72 WIDGETS widgets;
73
74 extern gboolean shift_key_pressed;
75
76 static GtkWidget *nav_toolbar;
77 static int main_window_created = FALSE;
78
79 /******************************************************************************
80 * Name
81 * gui_show_hide_texts
82 *
83 * Synopsis
84 * #include "gui/main_window.h"
85 *
86 * void gui_show_hide_texts(gboolean choice)
87 *
88 * Description
89 * Show/hide bible texts
90 *
91 * Return value
92 * void
93 */
94
gui_show_hide_texts(gboolean choice)95 void gui_show_hide_texts(gboolean choice)
96 {
97 settings.showtexts = choice;
98 gui_tab_set_showtexts(choice);
99 gui_set_tab_label(settings.currentverse, TRUE);
100 if (choice == FALSE) {
101 if (main_window_created)
102 gtk_widget_hide(widgets.vpaned);
103 xml_set_value("Xiphos", "misc", "showtexts", "0");
104 } else {
105 if (main_window_created) {
106 gtk_widget_show(widgets.vpaned);
107 gtk_widget_show(nav_toolbar);
108 }
109 xml_set_value("Xiphos", "misc", "showtexts", "1");
110 }
111 /*if (main_window_created)
112 gui_set_bible_comm_layout(); */
113 }
114
115 /******************************************************************************
116 * Name
117 * gui_show_hide_preview
118 *
119 * Synopsis
120 * #include "gui/main_window.h"
121 *
122 * void gui_show_hide_preview(gboolean choice)
123 *
124 * Description
125 * Show/hide bible texts
126 *
127 * Return value
128 * void
129 */
130
gui_show_hide_preview(gboolean choice)131 void gui_show_hide_preview(gboolean choice)
132 {
133 settings.showpreview = choice;
134 gui_tab_set_showpreview(choice);
135 if (choice == FALSE) {
136 if (main_window_created)
137 gtk_widget_hide(widgets.box_side_preview);
138 gtk_widget_hide(widgets.vbox_previewer);
139 xml_set_value("Xiphos", "misc", "showpreview", "0");
140 } else {
141 if (main_window_created) {
142 if (settings.show_previewer_in_sidebar)
143 gtk_widget_show(widgets.box_side_preview);
144 else
145 gtk_widget_show(widgets.vbox_previewer);
146 }
147
148 xml_set_value("Xiphos", "misc", "showpreview", "1");
149 }
150 }
151
152 /******************************************************************************
153 * Name
154 * gui_show_hide_comms
155 *
156 * Synopsis
157 * #include "gui/main_window.h"
158 *
159 * void gui_show_hide_comms(gboolean choice)
160 *
161 * Description
162 * Show/hide Commentaries
163 *
164 * Return value
165 * void
166 */
167
gui_show_hide_comms(gboolean choice)168 void gui_show_hide_comms(gboolean choice)
169 {
170 settings.showcomms = choice;
171 gui_tab_set_showcomms(choice);
172 gui_set_tab_label(settings.currentverse, TRUE);
173 if (choice == FALSE) {
174 if (main_window_created)
175 gtk_widget_hide(widgets.notebook_comm_book);
176 xml_set_value("Xiphos", "misc", "showcomms", "0");
177 } else {
178 if (main_window_created)
179 gtk_widget_show(widgets.notebook_comm_book);
180 xml_set_value("Xiphos", "misc", "showcomms", "1");
181 }
182 if (main_window_created)
183 gui_set_bible_comm_layout();
184 }
185
186 /******************************************************************************
187 * Name
188 * gui_show_hide_dicts
189 *
190 * Synopsis
191 * #include "gui/main_window.h"
192 *
193 * void gui_show_hide_dicts(gboolean choice)
194 *
195 * Description
196 * Show/hide Dictionaries-Lexicons
197 *
198 * Return value
199 * void
200 */
201
gui_show_hide_dicts(gboolean choice)202 void gui_show_hide_dicts(gboolean choice)
203 {
204 settings.showdicts = choice;
205 gui_tab_set_showdicts(choice);
206 gui_set_tab_label(settings.currentverse, TRUE);
207 if (choice == FALSE) {
208 if (main_window_created)
209 gtk_widget_hide(widgets.box_dict);
210 xml_set_value("Xiphos", "misc", "showdicts", "0");
211 } else {
212 if (main_window_created)
213 gtk_widget_show(widgets.box_dict);
214 xml_set_value("Xiphos", "misc", "showdicts", "1");
215 }
216 if (main_window_created)
217 gui_set_bible_comm_layout();
218 }
219
220 /******************************************************************************
221 * Name
222 * gui_set_bible_comm_layout
223 *
224 * Synopsis
225 * #include "gui/main_window.h"
226 *
227 * void gui_set_bible_comm_layout(void)
228 *
229 * Description
230 *
231 *
232 * Return value
233 * void
234 */
235
gui_set_bible_comm_layout(void)236 void gui_set_bible_comm_layout(void)
237 {
238 gtk_paned_set_position(GTK_PANED(widgets.hpaned),
239 settings.biblepane_width);
240 gtk_paned_set_position(GTK_PANED(widgets.vpaned),
241 settings.biblepane_height);
242 gtk_paned_set_position(GTK_PANED(widgets.vpaned2),
243 settings.commpane_height);
244
245 if ((settings.showcomms == TRUE) || (settings.showdicts == TRUE)) {
246 gtk_widget_show(widgets.vpaned2);
247 }
248
249 gtk_paned_set_position(GTK_PANED(widgets.hpaned),
250 (settings.showtexts
251 ? settings.biblepane_width
252 : 0));
253
254 gtk_paned_set_position(GTK_PANED(widgets.vpaned2),
255 (settings.showcomms
256 ? settings.commpane_height
257 : 0));
258
259 gtk_paned_set_position(GTK_PANED(widgets.vpaned2),
260 (settings.showdicts
261 ? settings.commpane_height
262 : settings.gs_height));
263
264 if ((settings.showcomms == FALSE) && (settings.showdicts == FALSE)) {
265 gtk_widget_hide(widgets.vpaned2);
266 gtk_paned_set_position(GTK_PANED(widgets.hpaned),
267 settings.gs_width);
268 }
269
270 if ((settings.showcomms == TRUE) || (settings.showdicts == TRUE)) {
271 gtk_paned_set_position(GTK_PANED(widgets.hpaned),
272 settings.biblepane_width);
273 }
274 if (((settings.showcomms == FALSE) && (settings.showtexts == FALSE)) || ((settings.comm_showing == FALSE) && (settings.showtexts == FALSE)))
275 gtk_widget_hide(nav_toolbar);
276 else
277 gtk_widget_show(nav_toolbar);
278
279 gtk_notebook_set_current_page(GTK_NOTEBOOK(widgets.notebook_comm_book),
280 (settings.comm_showing ? 0 : 1));
281 }
282
283 /******************************************************************************
284 * Name
285 * gui_change_window_title
286 *
287 * Synopsis
288 * #include "gui/main_window.h"
289 *
290 * void gui_change_window_title(gchar * module_name)
291 *
292 * Description
293 *
294 *
295 * Return value
296 * void
297 */
298
gui_change_window_title(gchar * module_name)299 void gui_change_window_title(gchar *module_name)
300 {
301 gchar *title;
302
303 if (cur_passage_tab) {
304 /* borrowed from tabbed_browser.c:pick_tab_label() */
305 /* echo the current tab's module (full name) in title bar */
306 if (cur_passage_tab->showtexts || cur_passage_tab->comm_showing) {
307 title =
308 (cur_passage_tab->showtexts ? cur_passage_tab->text_mod : (cur_passage_tab->commentary_mod ? cur_passage_tab->commentary_mod : "[no commentary]"));
309 } else {
310 title = (cur_passage_tab->showcomms
311 ? (cur_passage_tab->book_mod
312 ? cur_passage_tab->book_mod
313 : "[no book]")
314 : (cur_passage_tab->dictlex_mod
315 ? cur_passage_tab->dictlex_mod
316 : "[no dict]"));
317 }
318 } else
319 title = module_name;
320
321 /*
322 * set program title to current module name
323 */
324 title = g_strdup(main_get_module_description(title));
325 if (!title)
326 title =
327 g_strdup(main_get_module_description(settings.MainWindowModule));
328 title =
329 g_strdup_printf("%s - %s", (title ? title : "[no title]"),
330 settings.program_title);
331 gtk_window_set_title(GTK_WINDOW(widgets.app), title);
332 g_free(title);
333 }
334
delete_event(GtkWidget * widget,GdkEvent * event,gpointer user_data)335 static gboolean delete_event(GtkWidget *widget,
336 GdkEvent *event, gpointer user_data)
337 {
338 on_quit_activate(NULL, NULL);
339 return TRUE;
340 }
341
342 /******************************************************************************
343 * Name
344 * on_epaned_button_release_event
345 *
346 * Synopsis
347 * #include "gui/main_window.h"
348 *
349 * gboolean on_epaned_button_release_event(GtkWidget * widget,
350 * GdkEventButton * event, gpointer user_data)
351 *
352 * Description
353 * get and store pane sizes
354 *
355 * Return value
356 * gboolean
357 */
358
epaned_button_release_event(GtkWidget * widget,GdkEventButton * event,gpointer user_data)359 static gboolean epaned_button_release_event(GtkWidget *widget,
360 GdkEventButton *event,
361 gpointer user_data)
362 {
363 gint panesize;
364 gchar layout[80];
365
366 panesize = gtk_paned_get_position(GTK_PANED(widget));
367
368 if (panesize > 15) {
369 if (!strcmp((gchar *)user_data, "epaned")) {
370 settings.sidebar_width = panesize;
371 sprintf(layout, "%d", settings.sidebar_width);
372 xml_set_value("Xiphos", "layout",
373 "shortcutbar", layout);
374 }
375 if (!strcmp((gchar *)user_data, "vpaned")) {
376 settings.biblepane_height = panesize;
377 sprintf(layout, "%d", settings.biblepane_height);
378 xml_set_value("Xiphos", "layout",
379 "bibleheight", layout);
380 }
381 if (!strcmp((gchar *)user_data, "vpaned2")) {
382 settings.commpane_height = panesize;
383 sprintf(layout, "%d", settings.commpane_height);
384 xml_set_value("Xiphos", "layout",
385 "commentaryheight", layout);
386 }
387 if (!strcmp((gchar *)user_data, "hpaned1")) {
388 settings.biblepane_width = panesize;
389 sprintf(layout, "%d", settings.biblepane_width);
390 xml_set_value("Xiphos", "layout",
391 "textpane", layout);
392 }
393 return FALSE;
394 }
395 return TRUE;
396 }
397
398 /******************************************************************************
399 * Name
400 * final_pane_sizes
401 *
402 * Synopsis
403 * #include "gui/main_window.h"
404 *
405 * void final_pane_sizes()
406 *
407 * Description
408 * on quit, make a last query for pane sizes.
409 * this is needed because e.g. "maximize window" in the title bar does
410 * not call on_epaned_button_release_event for each newly-modified pane.
411 *
412 * Return value
413 * void
414 */
415
final_pane_sizes()416 void final_pane_sizes()
417 {
418 epaned_button_release_event(GTK_WIDGET(widgets.epaned), NULL,
419 (gchar *)"epaned");
420 epaned_button_release_event(GTK_WIDGET(widgets.vpaned), NULL,
421 (gchar *)"vpaned");
422 epaned_button_release_event(GTK_WIDGET(widgets.vpaned2), NULL,
423 (gchar *)"vpaned2");
424 epaned_button_release_event(GTK_WIDGET(widgets.hpaned), NULL,
425 (gchar *)"hpaned1");
426 }
427
428 /******************************************************************************
429 * Name
430 * on_configure_event
431 *
432 * Synopsis
433 * #include "gui/main_window.h"
434 *
435 * gboolean on_configure_event(GtkWidget * widget,
436 * GdkEventConfigure * event,
437 * gpointer user_data)
438 *
439 * Description
440 * remember placement+size of main window.
441 *
442 * Return value
443 * gboolean
444 */
445
on_configure_event(GtkWidget * widget,GdkEventConfigure * event,gpointer user_data)446 static gboolean on_configure_event(GtkWidget *widget,
447 GdkEventConfigure *event,
448 gpointer user_data)
449 {
450 gchar layout[80];
451 gint x;
452 gint y;
453
454 gdk_window_get_root_origin(gtk_widget_get_window(widgets.app), &x, &y);
455
456 settings.gs_width = event->width;
457 settings.gs_height = event->height;
458 settings.app_x = x;
459 settings.app_y = y;
460
461 #if GTK_CHECK_VERSION(3, 12, 0)
462 sprintf(layout, "%d", gtk_window_is_maximized(GTK_WINDOW(widgets.app)));
463 xml_set_value("Xiphos", "layout", "maximized", layout);
464 #endif
465
466 sprintf(layout, "%d", settings.gs_width);
467 xml_set_value("Xiphos", "layout", "width", layout);
468
469 sprintf(layout, "%d", settings.gs_height);
470 xml_set_value("Xiphos", "layout", "height", layout);
471
472 sprintf(layout, "%d", settings.app_x);
473 xml_set_value("Xiphos", "layout", "app_x", layout);
474
475 sprintf(layout, "%d", settings.app_y);
476 xml_set_value("Xiphos", "layout", "app_y", layout);
477 xml_save_settings_doc(settings.fnconfigure);
478
479 return FALSE;
480 }
481
on_notebook_bible_parallel_switch_page(GtkNotebook * notebook,gint page_num,GList ** tl)482 static void on_notebook_bible_parallel_switch_page(GtkNotebook *notebook,
483 gint page_num,
484 GList **tl)
485 {
486 }
487
488 #ifdef USE_GTK_3
on_notebook_comm_book_switch_page(GtkNotebook * notebook,gpointer arg,gint page_num,GList ** tl)489 static void on_notebook_comm_book_switch_page(GtkNotebook *notebook,
490 gpointer arg,
491 gint page_num, GList **tl)
492 #else
493 static void on_notebook_comm_book_switch_page(GtkNotebook *notebook,
494 GtkNotebookPage *page,
495 gint page_num, GList **tl)
496 #endif
497 {
498 gchar *url = NULL;
499
500 if (page_num == 0) {
501 settings.comm_showing = TRUE;
502 gtk_widget_show(nav_toolbar);
503 } else {
504 settings.comm_showing = FALSE;
505 if (!settings.showtexts)
506 gtk_widget_hide(nav_toolbar);
507 }
508
509 gui_update_tab_struct(NULL,
510 settings.CommWindowModule,
511 NULL,
512 NULL,
513 NULL,
514 NULL,
515 settings.comm_showing,
516 settings.showtexts,
517 settings.showpreview,
518 settings.showcomms, settings.showdicts);
519 if (settings.comm_showing)
520 main_display_commentary(settings.CommWindowModule,
521 settings.currentverse);
522 else if (settings.book_mod && *settings.book_mod) {
523 url = g_strdup_printf("sword://%s/%ld", settings.book_mod,
524 settings.book_offset);
525 main_url_handler(url, TRUE);
526 g_free(url);
527 }
528 gui_set_tab_label(settings.currentverse, TRUE);
529 }
530
new_base_font_size(gboolean up)531 static void new_base_font_size(gboolean up)
532 {
533 if (up) {
534 settings.base_font_size++;
535 if (settings.base_font_size > 5)
536 settings.base_font_size = 5;
537 } else {
538 settings.base_font_size--;
539 if (settings.base_font_size < -2)
540 settings.base_font_size = -2;
541 }
542
543 if (settings.base_font_size_str)
544 g_free(settings.base_font_size_str);
545 settings.base_font_size_str =
546 g_strdup_printf("%+d", settings.base_font_size);
547
548 xml_set_value("Xiphos", "fontsize", "basefontsize",
549 settings.base_font_size_str);
550 redisplay_to_realign();
551 }
552
553 /* temporary shorthand for too-common use */
554 #define sM settings.MainWindowModule
555 #define sC settings.CommWindowModule
556 #define sD settings.DictWindowModule
557 #define sB settings.book_mod
558 #define sV settings.currentverse
559
kbd_toggle_option(gboolean cond,gchar * option)560 static void kbd_toggle_option(gboolean cond, gchar *option)
561 {
562 gchar *msg;
563
564 if (cond) {
565 int opt = !main_get_one_option(sM, option); // negate.
566 main_save_module_options(sM, option, opt);
567 gchar *url = g_strdup_printf("sword://%s/%s", sM, sV);
568 main_url_handler(url, TRUE);
569 g_free(url);
570 msg =
571 g_strdup_printf("%s %s", option, (opt ? "on" : "off"));
572 gui_set_statusbar(msg);
573 g_free(msg);
574 } else {
575 msg = g_strdup_printf(_("Module has no support for %s."),
576 option);
577 gui_generic_warning(msg);
578 g_free(msg);
579 }
580 }
581
on_vbox1_key_press_event(GtkWidget * widget,GdkEventKey * event,gpointer user_data)582 static gboolean on_vbox1_key_press_event(GtkWidget *widget, GdkEventKey *event,
583 gpointer user_data)
584 {
585 /* these are the mods we actually use for global keys, we always only check for these set */
586 guint state =
587 event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK |
588 GDK_MOD1_MASK | GDK_MOD4_MASK);
589
590 switch (event->keyval) {
591 case XK_Shift_L: /* shift keys - we need this for locking strongs (and */
592 case XK_Shift_R: /* other stuff) while moving mouse to previewer */
593 shift_key_pressed = TRUE;
594 /* no break? hm... */
595
596 case XK_a:
597 case XK_A:
598 if (state == GDK_MOD1_MASK) { // Alt-A annotation
599 gui_mark_verse_dialog(sM, sV);
600 } else if (state ==
601 (GDK_CONTROL_MASK | GDK_MOD1_MASK |
602 GDK_SHIFT_MASK))
603 on_biblesync_kbd(3); // BSP audience
604 break;
605
606 case XK_b:
607 case XK_B:
608 if (state == GDK_MOD1_MASK) { // Alt-B bookmark
609 gchar *label = g_strdup_printf("%s, %s", sV, sM);
610 gui_bookmark_dialog(label, sM, sV);
611 g_free(label);
612 }
613 break;
614
615 case XK_c:
616 case XK_C:
617 if (state == GDK_MOD1_MASK) { // Alt-C commentary pane
618 gtk_widget_grab_focus(navbar_versekey.lookup_entry);
619 gtk_notebook_set_current_page(GTK_NOTEBOOK(widgets.notebook_comm_book),
620 0);
621 }
622 #if BIBLESYNC_VERSION_NUM >= 2000000000
623 else if (state == (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_SHIFT_MASK)) {
624 // BSP chat
625 if (settings.bs_mode == 0)
626 gui_generic_warning(_("BibleSync is not active."));
627 else {
628 GS_DIALOG *info = gui_new_dialog();
629 #if GTK_CHECK_VERSION(3, 10, 0)
630 info->stock_icon = g_strdup("dialog-question");
631 #else
632 info->stock_icon = g_strdup(GTK_STOCK_DIALOG_QUESTION);
633 #endif
634 info->label_top = g_strdup(_("BibleSync Chat"));
635 info->text1 = g_strdup(_("[say this]"));
636 info->label1 = _("Comment:");
637 info->ok = TRUE;
638 info->cancel = TRUE;
639
640 gint test = gui_gs_dialog(info);
641 if (test == GS_OK)
642 biblesync_chat(info->text1);
643
644 g_free(info->label_top);
645 g_free(info->text1);
646 g_free(info);
647 }
648 }
649 #endif /* biblesync >= 2.0.0 */
650 break;
651
652 case XK_d:
653 case XK_D:
654 if (state == GDK_MOD1_MASK) // Alt-D dictionary entry
655 gtk_widget_grab_focus(widgets.entry_dict);
656 break;
657
658 case XK_f:
659 case XK_F:
660 if (state == GDK_CONTROL_MASK) { // Ctrl-F find text
661 if (settings.showtexts) {
662 gui_find_dlg(widgets.html_text,
663 sM, FALSE, NULL);
664 } else if (settings.showcomms) {
665 if (settings.comm_showing) {
666 gui_find_dlg(widgets.html_comm,
667 sC, FALSE, NULL);
668 } else {
669 gui_find_dlg(widgets.html_book,
670 sB, FALSE, NULL);
671 }
672 } else if (settings.showdicts) {
673 gui_find_dlg(widgets.html_dict,
674 sD, FALSE, NULL);
675 } else
676 gui_generic_warning(_("Xiphos: No windows."));
677 }
678 break;
679
680 case XK_g:
681 case XK_G:
682 if (state == GDK_MOD1_MASK) { // Alt-G genbook entry
683 gtk_notebook_set_current_page(GTK_NOTEBOOK(widgets.notebook_comm_book),
684 1);
685 gtk_widget_grab_focus(navbar_book.lookup_entry);
686 }
687 break;
688
689 case XK_j:
690 if (state == 0) // J "next verse"
691 access_on_down_eventbox_button_release_event(VERSE_BUTTON);
692 break;
693
694 case XK_k:
695 if (state == 0) // K "previous verse"
696 access_on_up_eventbox_button_release_event(VERSE_BUTTON);
697 break;
698
699 case XK_l:
700 case XK_L:
701 if (state == GDK_CONTROL_MASK) // Ctrl-L verse entry
702 gtk_widget_grab_focus(navbar_versekey.lookup_entry);
703 else if (state == GDK_MOD1_MASK) // Alt-L lemma
704 kbd_toggle_option((main_check_for_global_option(sM, "ThMLLemma") ||
705 main_check_for_global_option(sM, "OSISLemma")),
706 "Lemmas");
707 break;
708
709 case XK_m:
710 case XK_M:
711 if (state == GDK_MOD1_MASK) // Alt-M morph
712 {
713 kbd_toggle_option((main_check_for_global_option(sM, "GBFMorph") ||
714 main_check_for_global_option(sM, "ThMLMorph") ||
715 main_check_for_global_option(sM, "OSISMorph")),
716 "Morphological Tags");
717 }
718 break;
719
720 case XK_n:
721 case XK_N:
722 if (state == GDK_CONTROL_MASK) // Ctrl-N verse
723 access_on_down_eventbox_button_release_event(VERSE_BUTTON);
724 else if (state == 0) // n chapter
725 access_on_down_eventbox_button_release_event(CHAPTER_BUTTON);
726 else if (state == GDK_SHIFT_MASK) // N book
727 access_on_down_eventbox_button_release_event(BOOK_BUTTON);
728 else if (state == (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_SHIFT_MASK)) {
729 // BSP transient navigate
730 if (biblesync_active_xmit_allowed()) {
731 biblesync_prep_and_xmit(sM, sV);
732 gui_set_statusbar(_("BibleSync: Current navigation sent."));
733 } else {
734 gui_generic_warning(_("BibleSync: Not speaking."));
735 }
736 }
737 break;
738
739 case XK_o:
740 case XK_O:
741 if (state ==
742 (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_SHIFT_MASK))
743 on_biblesync_kbd(0); // BSP off
744 break;
745
746 case XK_p:
747 case XK_P:
748 if (state == GDK_CONTROL_MASK) // Ctrl-P verse
749 access_on_up_eventbox_button_release_event(VERSE_BUTTON);
750 else if (state == 0) // p chapter
751 access_on_up_eventbox_button_release_event(CHAPTER_BUTTON);
752 else if (state == GDK_SHIFT_MASK) // P book
753 access_on_up_eventbox_button_release_event(BOOK_BUTTON);
754 else if (state == GDK_MOD1_MASK) // Alt-P parallel detach
755 on_undockInt_activate(NULL);
756 else if (state ==
757 (GDK_CONTROL_MASK | GDK_MOD1_MASK |
758 GDK_SHIFT_MASK))
759 on_biblesync_kbd(1); // BSP personal
760 break;
761
762 case XK_q:
763 case XK_Q:
764 if (state == GDK_CONTROL_MASK) // Ctrl-Q quit
765 delete_event(NULL, NULL, NULL);
766 break;
767
768 case XK_r:
769 case XK_R:
770 if (state == GDK_MOD1_MASK) // Alt-R red words
771 {
772 kbd_toggle_option(((main_check_for_global_option(sM, "GBFRedLetterWords")) ||
773 (main_check_for_global_option(sM, "OSISRedLetterWords"))),
774 "Words of Christ in Red");
775 }
776 break;
777
778 case XK_s:
779 case XK_S:
780 if (state == GDK_MOD1_MASK) // Alt-S strong's
781 {
782 kbd_toggle_option(((main_check_for_global_option(sM, "GBFStrongs")) ||
783 (main_check_for_global_option(sM, "ThMLStrongs")) ||
784 (main_check_for_global_option(sM, "OSISStrongs"))),
785 "Strong's Numbers");
786 } else if (state ==
787 (GDK_CONTROL_MASK | GDK_MOD1_MASK |
788 GDK_SHIFT_MASK))
789 on_biblesync_kbd(2); // BSP speaker
790 break;
791
792 case XK_t:
793 case XK_T:
794 if (state == GDK_CONTROL_MASK) // Ctrl-T open a new tab
795 on_notebook_main_new_tab_clicked(NULL, NULL);
796 else if (state == GDK_MOD1_MASK) // Alt-T transliteration
797 kbd_toggle_option(true, "Transliteration");
798 break;
799
800 case XK_z:
801 case XK_Z:
802 if (state == GDK_MOD1_MASK) // Alt-Z open personal commentary
803 access_to_edit_percomm();
804 break;
805
806 case XK_plus: // Ctrl-Plus Increase base font size
807 if (state == (GDK_CONTROL_MASK | GDK_SHIFT_MASK))
808 new_base_font_size(TRUE);
809 break;
810
811 case XK_minus: // Ctrl-Minus Decrease base font size
812 if (state == GDK_CONTROL_MASK)
813 new_base_font_size(FALSE);
814 break;
815
816 case XK_0: // Ctrl-0 (zero) Neutralize base font size.
817 if (state == GDK_CONTROL_MASK) {
818 settings.base_font_size = 1;
819 new_base_font_size(FALSE);
820 }
821 break;
822
823 // ctrl-DIGIT [1-9] selects DIGIT-th tab.
824 case XK_1:
825 case XK_2:
826 case XK_3:
827 case XK_4:
828 case XK_5:
829 case XK_6:
830 case XK_7:
831 case XK_8:
832 case XK_9:
833 if (state == GDK_CONTROL_MASK)
834 gui_select_nth_tab((event->keyval - XK_0) - 1); /* 0-based list */
835
836 case XK_F1: // F1 help
837 if (state == 0)
838 on_help_contents_activate(NULL, NULL);
839 break;
840
841 case XK_F2: // F2 preferences
842 if (state == 0)
843 on_preferences_activate(NULL, NULL);
844 break;
845
846 case XK_F3: // F3 search
847 if (state == 0)
848 main_open_search_dialog();
849 else if (state == GDK_CONTROL_MASK)
850 gtk_notebook_set_current_page(GTK_NOTEBOOK(widgets.notebook_sidebar),
851 2);
852 break;
853
854 case XK_F4: // F4 module manager
855 if (state == 0)
856 on_module_manager_activate(NULL, NULL);
857 else if (state == GDK_CONTROL_MASK)
858 gui_close_passage_tab(gtk_notebook_page_num(GTK_NOTEBOOK(widgets.notebook_main),
859 ((PASSAGE_TAB_INFO *)
860 cur_passage_tab)->page_widget));
861 break;
862
863 case XK_F10: // Shift-F10 bible module right click
864 if (state == GDK_SHIFT_MASK)
865 gui_menu_popup(NULL, sM, NULL);
866 /* FIXME: needs the html widget as first pram */
867 break;
868
869 case XK_F11: // F11 open current bible in separate window, maximized.
870 if (state == 0)
871 main_dialogs_open(sM, NULL, TRUE);
872 break;
873 }
874 XI_message(("on_vbox1_key_press_event\nkeycode: %d, keysym: %0x, state: %d",
875 event->hardware_keycode, event->keyval, state));
876 return FALSE;
877 }
878
on_vbox1_key_release_event(GtkWidget * widget,GdkEventKey * event,gpointer user_data)879 static gboolean on_vbox1_key_release_event(GtkWidget *widget,
880 GdkEventKey *event,
881 gpointer user_data)
882 {
883 switch (event->keyval) {
884 case XK_Shift_L:
885 case XK_Shift_R:
886 shift_key_pressed = FALSE;
887 break;
888 }
889 return FALSE;
890 }
891
892 /******************************************************************************
893 * Name
894 * create_mainwindow
895 *
896 * Synopsis
897 * #include "gui/main_window.h"
898 *
899 * void create_mainwindow(void)
900 *
901 * Description
902 * create xiphos gui
903 *
904 * Return value
905 * void
906 */
907
create_mainwindow(void)908 void create_mainwindow(void)
909 {
910 char *imagename;
911 GtkWidget *vbox_gs;
912 GtkWidget *menu;
913 GtkWidget *hbox25;
914 GtkWidget *tab_button_icon;
915 GtkWidget *label;
916 #ifndef USE_WEBKIT2
917 GtkWidget *scrolledwindow;
918 #endif
919 GtkWidget *box_book;
920 GdkPixbuf *pixbuf;
921 /*
922 GTK_SHADOW_NONE
923 GTK_SHADOW_IN
924 GTK_SHADOW_OUT
925 GTK_SHADOW_ETCHED_IN
926 GTK_SHADOW_ETCHED_OUT
927 */
928 settings.shadow_type = GTK_SHADOW_IN;
929
930 XI_print(("%s xiphos-%s\n", "Starting", VERSION));
931 XI_print(("%s\n\n", "Building Xiphos interface"));
932
933 widgets.studypad_dialog = NULL;
934
935 /* A rough scektch of the main window (widgets.app) and it's children
936 * widgets.app
937 * |
938 * vbox_gs
939 * |
940 * +--|----+
941 * | |
942 * menu hbox25
943 * |
944 * widgets.epaned
945 * |
946 * widgets.vboxMain
947 * | |
948 * widgets.hboxtb widgets.page
949 * | |
950 * widgets.hpaned nav_toolbar
951 * | |
952 * widgets.vpaned---+ +----------------------------widgets.vpaned2---------------+
953 * | | | |
954 * widgets.vbox_previewer widgets.vbox_text widgets.notebook_comm_book widgets.box_dict
955 * | | |
956 * widgets.notebook_bible_parallel widgets.box_comm box_book
957 * |
958 * widgets.notebook_text
959 *
960 */
961
962 // The toplevel Xiphos window
963 widgets.app = gtk_window_new(GTK_WINDOW_TOPLEVEL);
964 gtk_window_set_title(GTK_WINDOW(widgets.app), _("Xiphos - Bible Study Software"));
965 g_object_set_data(G_OBJECT(widgets.app), "widgets.app", widgets.app);
966 gtk_window_set_default_size(GTK_WINDOW(widgets.app), 680, 425);
967 gtk_widget_set_can_focus(widgets.app, 1);
968 gtk_window_set_resizable(GTK_WINDOW(widgets.app), TRUE);
969
970 // The app icon.
971 // FIXME:: This should be a big copy of the logo because GTK does the scaling (GTK 3.16?)
972 imagename = image_locator("gs2-48x48.png");
973 pixbuf = gdk_pixbuf_new_from_file(imagename, NULL);
974 g_free(imagename);
975 gtk_window_set_icon(GTK_WINDOW(widgets.app), pixbuf);
976
977 // The main box for our toplevel window.
978 UI_VBOX(vbox_gs, FALSE, 0);
979 gtk_widget_show(vbox_gs);
980 gtk_container_add(GTK_CONTAINER(widgets.app), vbox_gs);
981
982 // Add the main menu.
983 menu = gui_create_main_menu();
984 gtk_box_pack_start(GTK_BOX(vbox_gs), menu, FALSE, TRUE, 0);
985
986 // Another box
987 UI_HBOX(hbox25, FALSE, 0);
988 gtk_widget_show(hbox25);
989 gtk_box_pack_start(GTK_BOX(vbox_gs), hbox25, TRUE, TRUE, 0);
990
991 // widgets.epaned
992 widgets.epaned = UI_HPANE();
993 gtk_widget_show(widgets.epaned);
994 #if !GTK_CHECK_VERSION(3, 14, 0)
995 gtk_container_set_border_width(GTK_CONTAINER(widgets.epaned), 4);
996 #endif
997 gtk_box_pack_start(GTK_BOX(hbox25), widgets.epaned, TRUE, TRUE, 0);
998 // Another box
999 UI_VBOX(widgets.vboxMain, FALSE, 0);
1000 gtk_widget_show(widgets.vboxMain);
1001 gtk_paned_pack2(GTK_PANED(widgets.epaned), widgets.vboxMain, TRUE, TRUE);
1002 #if !GTK_CHECK_VERSION(3, 14, 0)
1003 gtk_container_set_border_width(GTK_CONTAINER(widgets.vboxMain), 2);
1004 #endif
1005
1006 /*
1007 * Notebook to have separate passages opened at once the passages are not
1008 * actually open but are switched between similar to bookmarks
1009 */
1010 UI_HBOX(widgets.hboxtb, FALSE, 0);
1011 if (settings.browsing)
1012 gtk_widget_show(widgets.hboxtb);
1013 gtk_box_pack_start(GTK_BOX(widgets.vboxMain), widgets.hboxtb, FALSE, FALSE, 0);
1014
1015 widgets.button_new_tab = gtk_button_new();
1016 // Don't show button here in case !settings.browsing
1017
1018 #if GTK_CHECK_VERSION(3, 10, 0)
1019 tab_button_icon = gtk_image_new_from_icon_name("tab-new-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
1020 #else
1021 tab_button_icon = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_SMALL_TOOLBAR);
1022 #endif
1023
1024 gtk_widget_show(tab_button_icon);
1025 gtk_container_add(GTK_CONTAINER(widgets.button_new_tab), tab_button_icon);
1026 gtk_button_set_relief(GTK_BUTTON(widgets.button_new_tab), GTK_RELIEF_NONE);
1027 gtk_box_pack_start(GTK_BOX(widgets.hboxtb), widgets.button_new_tab, FALSE, FALSE, 0);
1028 gtk_widget_set_tooltip_text(widgets.button_new_tab, _("Open a new tab"));
1029
1030 widgets.notebook_main = gtk_notebook_new();
1031 gtk_widget_show(widgets.notebook_main);
1032 gtk_box_pack_start(GTK_BOX(widgets.hboxtb), widgets.notebook_main, TRUE, TRUE, 0);
1033 gtk_widget_set_size_request(widgets.notebook_main, -1, 25);
1034 gtk_notebook_set_scrollable(GTK_NOTEBOOK(widgets.notebook_main), TRUE);
1035 gtk_notebook_popup_enable(GTK_NOTEBOOK(widgets.notebook_main));
1036 gtk_notebook_set_show_border(GTK_NOTEBOOK(widgets.notebook_main), FALSE);
1037 // Main passage tabbed notebook end
1038
1039 // Another box
1040 UI_VBOX(widgets.page, FALSE, 0);
1041 gtk_widget_show(widgets.page);
1042 gtk_box_pack_start(GTK_BOX(widgets.vboxMain), widgets.page, TRUE, TRUE, 0);
1043
1044 //nav toolbar
1045 nav_toolbar = gui_navbar_versekey_new();
1046 gtk_box_pack_start(GTK_BOX(widgets.page), nav_toolbar, FALSE, FALSE, 0);
1047
1048 // widgets.hpaned
1049 widgets.hpaned = UI_HPANE();
1050 gtk_widget_show(widgets.hpaned);
1051 gtk_box_pack_start(GTK_BOX(widgets.page), widgets.hpaned, TRUE, TRUE, 0);
1052
1053 // widgets.vpaned
1054 widgets.vpaned = UI_VPANE();
1055 gtk_widget_show(widgets.vpaned);
1056 gtk_widget_set_size_request(widgets.vpaned, 50, -1);
1057 gtk_paned_pack1(GTK_PANED(widgets.hpaned), widgets.vpaned, TRUE, FALSE);
1058
1059 // widgets.vpaned2
1060 widgets.vpaned2 = UI_VPANE();
1061 gtk_widget_set_size_request(widgets.vpaned2, 50, -1);
1062
1063 // widgets.vbox_text
1064 UI_VBOX(widgets.vbox_text, FALSE, 0);
1065 gtk_widget_show(widgets.vbox_text);
1066 gtk_paned_pack1(GTK_PANED(widgets.vpaned), widgets.vbox_text, TRUE, TRUE);
1067
1068 // Bible/parallel notebook
1069 widgets.notebook_bible_parallel = gtk_notebook_new();
1070 gtk_widget_show(widgets.notebook_bible_parallel);
1071 gtk_box_pack_start(GTK_BOX(widgets.vbox_text), widgets.notebook_bible_parallel, TRUE, TRUE, 0);
1072 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(widgets.notebook_bible_parallel), GTK_POS_BOTTOM);
1073 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(widgets.notebook_bible_parallel), TRUE);
1074 gtk_notebook_set_show_border(GTK_NOTEBOOK(widgets.notebook_bible_parallel), FALSE);
1075 gtk_container_set_border_width(GTK_CONTAINER(widgets.notebook_bible_parallel), 1);
1076
1077 g_signal_connect(G_OBJECT(widgets.notebook_bible_parallel), "change-current-page", G_CALLBACK(on_notebook_bible_parallel_switch_page), NULL);
1078
1079 // Text notebook (The bible text show in the standard view)
1080 widgets.notebook_text = gui_create_bible_pane();
1081 gtk_container_add(GTK_CONTAINER(widgets.notebook_bible_parallel), widgets.notebook_text);
1082
1083 label = gtk_label_new(_("Standard View"));
1084 gtk_widget_show(label);
1085 gtk_notebook_set_tab_label(GTK_NOTEBOOK(widgets.notebook_bible_parallel), gtk_notebook_get_nth_page(GTK_NOTEBOOK(widgets.notebook_bible_parallel), 0), label);
1086
1087 // Another box (For the previewer?)
1088 UI_VBOX(widgets.vbox_previewer, FALSE, 0);
1089 gtk_widget_show(widgets.vbox_previewer);
1090 gtk_paned_pack2(GTK_PANED(widgets.vpaned), widgets.vbox_previewer, TRUE, TRUE);
1091 gtk_container_set_border_width(GTK_CONTAINER(widgets.vbox_previewer), 2);
1092
1093 #ifndef USE_WEBKIT2
1094 scrolledwindow = gtk_scrolled_window_new(NULL, NULL);
1095 gtk_widget_show(scrolledwindow);
1096 gtk_box_pack_start(GTK_BOX(widgets.vbox_previewer), scrolledwindow, TRUE, TRUE, 0);
1097 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1098 gtk_scrolled_window_set_shadow_type((GtkScrolledWindow *) scrolledwindow, settings.shadow_type);
1099 #endif
1100 widgets.html_previewer_text = GTK_WIDGET(XIPHOS_HTML_NEW(NULL, FALSE, VIEWER_TYPE));
1101 gtk_widget_show(widgets.html_previewer_text);
1102 #ifdef USE_WEBKIT2
1103 gtk_box_pack_start(GTK_BOX(widgets.vbox_previewer), widgets.html_previewer_text, TRUE, TRUE, 0);
1104 #else
1105 gtk_container_add(GTK_CONTAINER(scrolledwindow), widgets.html_previewer_text);
1106 #endif
1107
1108 // Commentary/book notebook
1109 widgets.notebook_comm_book = gtk_notebook_new();
1110 gtk_widget_show(widgets.notebook_comm_book);
1111
1112 gtk_paned_pack1(GTK_PANED(widgets.vpaned2), widgets.notebook_comm_book, TRUE, TRUE);
1113 gtk_container_set_border_width(GTK_CONTAINER(widgets.notebook_comm_book), 1);
1114
1115 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(widgets.notebook_comm_book), GTK_POS_BOTTOM);
1116 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(widgets.notebook_comm_book), TRUE);
1117 gtk_notebook_set_show_border(GTK_NOTEBOOK(widgets.notebook_comm_book), FALSE);
1118
1119 // Commentary pane
1120 widgets.box_comm = gui_create_commentary_pane();
1121 gtk_container_add(GTK_CONTAINER(widgets.notebook_comm_book), widgets.box_comm);
1122
1123 label = gtk_label_new(_("Commentary View"));
1124 gtk_widget_show(label);
1125 gtk_notebook_set_tab_label(GTK_NOTEBOOK(widgets.notebook_comm_book), gtk_notebook_get_nth_page(GTK_NOTEBOOK(widgets.notebook_comm_book), 0), label);
1126
1127 // Book pane
1128 box_book = gui_create_book_pane();
1129 gtk_container_add(GTK_CONTAINER(widgets.notebook_comm_book), box_book);
1130
1131 label = gtk_label_new(_("Book View"));
1132 gtk_widget_show(label);
1133 gtk_notebook_set_tab_label(GTK_NOTEBOOK(widgets.notebook_comm_book), gtk_notebook_get_nth_page(GTK_NOTEBOOK(widgets.notebook_comm_book), 1), label);
1134
1135 // Dict/lex
1136 widgets.box_dict = gui_create_dictionary_pane();
1137 gtk_paned_pack2(GTK_PANED(widgets.vpaned2), widgets.box_dict, TRUE, TRUE);
1138
1139 // Statusbar
1140 widgets.appbar = gtk_statusbar_new();
1141
1142 #ifndef USE_GTK_3
1143 gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(widgets.appbar), TRUE);
1144 #endif
1145 gtk_box_pack_start(GTK_BOX(vbox_gs), widgets.appbar, FALSE, TRUE, 0);
1146 gui_set_statusbar(_("Welcome to Xiphos"));
1147
1148 gtk_paned_pack2(GTK_PANED(widgets.hpaned), widgets.vpaned2, TRUE, FALSE);
1149 gtk_widget_grab_focus(navbar_versekey.lookup_entry);
1150
1151 gtk_window_set_default_size((GtkWindow *)widgets.app, settings.gs_width, settings.gs_height);
1152 gtk_widget_show_all(widgets.app);
1153
1154 if (settings.statusbar != 1)
1155 gtk_widget_hide(widgets.appbar);
1156
1157 /* must connect signals *after* instantiating window above, */
1158 /* immediately above, otherwise window creation induces */
1159 /* configure_event, wiping out user's saved geometry specs. */
1160 /* *important*: drain gtk event queue first (i.e. sync). */
1161
1162 sync_windows();
1163 g_signal_connect((gpointer)vbox_gs, "key_press_event", G_CALLBACK(on_vbox1_key_press_event), NULL);
1164 g_signal_connect((gpointer)vbox_gs, "key_release_event", G_CALLBACK(on_vbox1_key_release_event), NULL);
1165
1166 g_signal_connect(G_OBJECT(widgets.notebook_comm_book), "switch_page", G_CALLBACK(on_notebook_comm_book_switch_page), NULL);
1167
1168 g_signal_connect(G_OBJECT(widgets.app), "delete_event", G_CALLBACK(delete_event), NULL);
1169
1170 g_signal_connect((gpointer)widgets.app, "configure_event", G_CALLBACK(on_configure_event), NULL);
1171 g_signal_connect(G_OBJECT(widgets.epaned), "button_release_event", G_CALLBACK(epaned_button_release_event), (gchar *)"epaned");
1172 g_signal_connect(G_OBJECT(widgets.vpaned), "button_release_event", G_CALLBACK(epaned_button_release_event), (gchar *)"vpaned");
1173 g_signal_connect(G_OBJECT(widgets.vpaned2), "button_release_event", G_CALLBACK(epaned_button_release_event), (gchar *)"vpaned2");
1174 g_signal_connect(G_OBJECT(widgets.hpaned), "button_release_event", G_CALLBACK(epaned_button_release_event), (gchar *)"hpaned1");
1175
1176 main_window_created = TRUE;
1177 }
1178
1179 /******************************************************************************
1180 * Name
1181 * gui_show_main_window
1182 *
1183 * Synopsis
1184 * #include "gui/main_window.h"
1185 *
1186 * void gui_show_main_window(void)
1187 *
1188 * Description
1189 * display the app after all is created
1190 *
1191 * Return value
1192 * void
1193 */
1194
gui_show_main_window(void)1195 void gui_show_main_window(void)
1196 {
1197 gtk_widget_show(widgets.app);
1198 }
1199