diff --git a/src/gtk/bookmarks_menu.c b/src/gtk/bookmarks_menu.c index 677f402c0..a75fb4175 100644 --- a/src/gtk/bookmarks_menu.c +++ b/src/gtk/bookmarks_menu.c @@ -284,6 +284,31 @@ G_MODULE_EXPORT void on_allow_reordering_activate(GtkMenuItem *menuitem, gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))); } +/****************************************************************************** + * Name + * on_crossref_popup_activate + * + * Synopsis + * #include "gui/bookmarks_menu.h" + * + * void on_crossref_popup_activate(GtkMenuItem *menuitem, + * gpointer user_data) + * + * Description + * toggle popup menu for cross-references + * + * Return value + * void + */ +G_MODULE_EXPORT void on_crossref_popup_activate(GtkMenuItem *menuitem, + gpointer user_data) +{ + settings.crossref_popup = + gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem)); + xml_set_value("Xiphos", "misc", "crossref_popup", + settings.crossref_popup ? "1" : "0"); +} + /****************************************************************************** * Name * on_dialog_activate @@ -1042,6 +1067,10 @@ void gui_create_bookmark_menu(void) menu.bibletime = UI_GET_ITEM(gxml, "import_bibletime_bookmarks1"); menu.remove = UI_GET_ITEM(gxml, "remove_folder"); menu.set_color = UI_GET_ITEM(gxml, "set_tag_color"); + menu.crossref_popup = UI_GET_ITEM(gxml, "crossref_popup"); + gtk_check_menu_item_set_active( + GTK_CHECK_MENU_ITEM(menu.crossref_popup), + settings.crossref_popup); gtk_widget_set_sensitive(menu.in_tab, FALSE); gtk_widget_set_sensitive(menu.in_dialog, FALSE); diff --git a/src/gtk/bookmarks_treeview.c b/src/gtk/bookmarks_treeview.c index e47eeda4d..ac5e4329a 100644 --- a/src/gtk/bookmarks_treeview.c +++ b/src/gtk/bookmarks_treeview.c @@ -800,6 +800,13 @@ static GtkTreeModel *create_model(void) * void */ +static void lambda_open_url(GtkMenuItem *item, gpointer data) +{ + const gchar *url = (const gchar *)g_object_get_data(G_OBJECT(item), "url"); + if (url) + main_url_handler(url, TRUE); +} + static gboolean button_release_event(GtkWidget *widget, GdkEventButton *event, gpointer data) { @@ -902,34 +909,146 @@ static gboolean button_release_event(GtkWidget *widget, } if (is_selected) { if (!gtk_tree_model_iter_has_child(GTK_TREE_MODEL(model), &selected) && key != NULL) { - // might have an abbrev. get the real. const gchar *real_mod = main_abbrev_to_name(module); - gchar *url = NULL; - - if (!strcmp(module, "studypad")) - url = - g_strdup_printf("passagestudy.jsp?action=showStudypad&" - "type=9&value=%s&module=%s", - main_url_encode(key), - main_url_encode((real_mod - ? real_mod - : module))); - - else if (button_one) - url = - g_strdup_printf("passagestudy.jsp?action=showBookmark&" - "type=%s&value=%s&module=%s", - "currentTab", - main_url_encode(key), - main_url_encode((real_mod - ? real_mod - : module))); - if (url) { + /* multi if contains ";", "-" (range) or comma between verse numbers */ + gboolean multi = (strchr(key, ';') != NULL); + gboolean is_range = FALSE; + if (!multi && strchr(key, '-')) { + /* verse range: navigate to first verse directly */ + const gchar *colon = strchr(key, ':'); + const gchar *dash = strchr(key, '-'); + if (colon && dash && dash > colon) { + is_range = TRUE; + range_start = g_strndup(key, dash - key); + } + } + if (!multi && strchr(key, ',')) { + /* check if comma separates verses: "Eph 2:8,9" */ + const gchar *colon = strchr(key, ':'); + const gchar *comma = strchr(key, ','); + if (colon && comma && comma > colon) + multi = TRUE; + } + + if (is_range && button_one && range_start) { + /* navigate directly to first verse of range */ + gchar *url = g_strdup_printf( + "passagestudy.jsp?action=showBookmark&" + "type=%s&value=%s&module=%s", + "currentTab", + main_url_encode(range_start), + main_url_encode((real_mod ? real_mod : module))); main_url_handler(url, TRUE); g_free(url); + g_free(range_start); + range_start = NULL; + } else if (multi && button_one && settings.crossref_popup) { + /* split on ";" first, then expand "book ch:v1,v2" */ + GList *refs = NULL; + gchar **semis = g_strsplit(key, ";", -1); + gchar *last_book = NULL; + for (gint si = 0; semis[si]; si++) { + gchar *part = g_strstrip(semis[si]); + if (!*part) continue; + gchar *full_part; + /* if no space in part but has colon, prepend last book */ + if (last_book && strchr(part, ':') && !strchr(part, ' ')) + full_part = g_strdup_printf("%s %s", last_book, part); + else { + full_part = g_strdup(part); + /* extract book name: before last space before colon */ + const gchar *col = strchr(full_part, ':'); + if (col) { + const gchar *sp = col; + while (sp > full_part && *sp != ' ') sp--; + if (sp > full_part) { + g_free(last_book); + last_book = g_strndup(full_part, sp - full_part); + } + } + } + /* handle comma-separated verses */ + const gchar *colon = strchr(full_part, ':'); + const gchar *comma = strchr(full_part, ','); + if (colon && comma && comma > colon) { + gchar *prefix = g_strndup(full_part, colon - full_part + 1); + gchar **vnums = g_strsplit(colon + 1, ",", -1); + for (gint vi = 0; vnums[vi]; vi++) { + gchar *vn = g_strstrip(vnums[vi]); + if (*vn) + refs = g_list_append(refs, + g_strdup_printf("%s%s", prefix, vn)); + } + g_strfreev(vnums); + g_free(prefix); + } else { + refs = g_list_append(refs, g_strdup(full_part)); + } + g_free(full_part); + } + g_free(last_book); + g_strfreev(semis); + GtkWidget *popup = gtk_menu_new(); + for (GList *l = refs; l; l = l->next) { + gchar *ref = (gchar *)l->data; + GtkWidget *item = gtk_menu_item_new_with_label(ref); + const gchar *dash = strchr(ref, '-'); + const gchar *colon = strchr(ref, ':'); + gchar *nav_ref = (dash && colon && dash > colon) + ? g_strndup(ref, dash - ref) + : g_strdup(ref); + gchar *url = g_strdup_printf( + "passagestudy.jsp?action=showBookmark&" + "type=%s&value=%s&module=%s", + "currentTab", + main_url_encode(nav_ref), + main_url_encode((real_mod ? real_mod : module))); + g_free(nav_ref); + g_object_set_data_full(G_OBJECT(item), "url", url, g_free); + g_signal_connect(item, "activate", + G_CALLBACK(lambda_open_url), NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(popup), item); + } + g_list_free_full(refs, g_free); + gtk_widget_show_all(popup); +#if GTK_CHECK_VERSION(3, 22, 0) + gtk_menu_popup_at_pointer(GTK_MENU(popup), NULL); +#else + gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL, 1, + gtk_get_current_event_time()); +#endif + } else if (multi && button_one && !settings.crossref_popup) { + gchar *url = g_strdup_printf( + "passagestudy.jsp?action=showBookmark&" + "type=%s&value=%s&module=%s", + "currentTab", + main_url_encode(key), + main_url_encode((real_mod ? real_mod : module))); + main_url_handler(url, TRUE); + g_free(url); + } else { + gchar *url = NULL; + if (!strcmp(module, "studypad")) + url = g_strdup_printf( + "passagestudy.jsp?action=showStudypad&" + "type=9&value=%s&module=%s", + main_url_encode(key), + main_url_encode((real_mod ? real_mod : module))); + else if (button_one) + url = g_strdup_printf( + "passagestudy.jsp?action=showBookmark&" + "type=%s&value=%s&module=%s", + "currentTab", + main_url_encode(key), + main_url_encode((real_mod ? real_mod : module))); + if (url) { + main_url_handler(url, TRUE); + g_free(url); + } } } g_free(caption); + g_free(range_start); g_free(key); g_free(module); } diff --git a/src/gui/bookmarks_menu.h b/src/gui/bookmarks_menu.h index 41d1109ce..ddb4f67a3 100644 --- a/src/gui/bookmarks_menu.h +++ b/src/gui/bookmarks_menu.h @@ -42,6 +42,7 @@ struct _bookmark_menu GtkWidget *rr_submenu; GtkWidget *remove; GtkWidget *set_color; + GtkWidget *crossref_popup; }; typedef struct _bookmark_menu BOOKMARK_MENU; extern BOOKMARK_MENU menu; @@ -58,6 +59,10 @@ void bibletime_bookmarks_activate(GtkMenuItem *menuitem, gpointer user_data); void on_allow_reordering_activate(GtkMenuItem *menuitem, gpointer user_data); +void on_allow_reordering_activate(GtkMenuItem *menuitem, + gpointer user_data); +void on_crossref_popup_activate(GtkMenuItem *menuitem, + gpointer user_data); void on_dialog_activate(GtkMenuItem *menuitem, gpointer user_data); void on_edit_item_activate(GtkMenuItem *menuitem, diff --git a/src/main/settings.c b/src/main/settings.c index 90e1d4a02..eb96f777e 100644 --- a/src/main/settings.c +++ b/src/main/settings.c @@ -1059,6 +1059,12 @@ if (!settings.morph_heb_lex || strlen(settings.morph_heb_lex) == 0) { xml_add_new_item_to_section("misc", "annotatehighlight", "1"); settings.annotate_highlight = 1; } + if ((buf = xml_get_value("misc", "crossref_popup"))) + settings.crossref_popup = atoi(buf); + else { + xml_add_new_item_to_section("misc", "crossref_popup", "0"); + settings.crossref_popup = 0; + } if ((buf = xml_get_value("misc", "xrefsinverselist"))) settings.xrefs_in_verse_list = atoi(buf); diff --git a/src/main/settings.h b/src/main/settings.h index 19283f31c..1388a4848 100644 --- a/src/main/settings.h +++ b/src/main/settings.h @@ -103,6 +103,7 @@ struct _settings readaloud, /* pass text through "festival -server" */ versehighlight, /* do special fg/bg for current verse */ annotate_highlight, /* do special bg/fg for user annotations */ + crossref_popup, /* open cross-references in a popup menu */ /* saved startup displays */ display_parallel, /* detached parallel */ display_modmgr, /* mod.mgr */ diff --git a/ui/xi-menus-popup.gtkbuilder b/ui/xi-menus-popup.gtkbuilder index ffe9822f3..e5128f09c 100644 --- a/ui/xi-menus-popup.gtkbuilder +++ b/ui/xi-menus-popup.gtkbuilder @@ -123,6 +123,16 @@ + + + True + False + Open cross-references in a popup menu + Popup menu for cross-references + True + + + True diff --git a/ui/xi-menus.glade b/ui/xi-menus.glade index c023a63bf..dc161a762 100644 --- a/ui/xi-menus.glade +++ b/ui/xi-menus.glade @@ -1774,6 +1774,17 @@ + + + True + Open cross-references in a popup menu + Open cross-references in a popup menu + True + False + + + + True