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 @@
+
+
+
+
+
+
+