diff --git a/src/gtk/bookmark_dialog.c b/src/gtk/bookmark_dialog.c
index 824c1eeb2..6262f974a 100644
--- a/src/gtk/bookmark_dialog.c
+++ b/src/gtk/bookmark_dialog.c
@@ -33,6 +33,7 @@
#include "gui/bookmarks_treeview.h"
#include "gui/dialog.h"
#include "gui/utilities.h"
+#include "gui/widgets.h"
#include "main/display.hh"
#include "main/sword.h"
@@ -54,7 +55,7 @@ static GtkWidget *entry_module;
static GtkWidget *textview;
static GtkTextBuffer *textbuffer;
static gchar *note;
-
+static gchar *global_module_name = NULL;
GtkWidget *bookmark_dialog;
GtkWidget *mark_verse_dialog;
@@ -71,6 +72,13 @@ void on_buffer_changed(GtkTextBuffer *textbuffer, gpointer user_data)
XI_message(("note: %s", note));
}
+static void toggle_color_clicked(GtkButton *btn, GtkWidget *colorbtn)
+{
+ gboolean active = gtk_widget_is_sensitive(colorbtn);
+ gtk_widget_set_sensitive(colorbtn, !active);
+ gtk_button_set_label(btn, active ? _("Add color") : _("No color"));
+}
+
/******************************************************************************
* Name
* add_bookmark_button
@@ -93,36 +101,48 @@ static void add_bookmark_button(void)
GtkTreeIter iter;
BOOKMARK_DATA *data;
GtkTreeSelection *selection;
+ const gchar *module_from_entry;
+ const gchar *module_to_use;
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
if (!gtk_tree_selection_get_selected(selection, NULL, &selected))
return;
- data = g_new(BOOKMARK_DATA, 1);
+ data = g_new0(BOOKMARK_DATA, 1);
data->caption = g_strdup((gchar *)gtk_entry_get_text(GTK_ENTRY(entry_label)));
data->key = g_strdup((gchar *)gtk_entry_get_text(GTK_ENTRY(entry_key)));
- data->module = g_strdup((gchar *)gtk_entry_get_text(GTK_ENTRY(entry_module)));
- if (!strcmp(data->module, "studypad"))
- data->module_desc = "studypad";
- else
- data->module_desc =
- g_strdup(main_get_module_description(data->module));
- data->description = g_strdup((gchar *)gtk_entry_get_text(GTK_ENTRY(entry_label)));
+ module_from_entry = gtk_entry_get_text(GTK_ENTRY(entry_module));
+ if (module_from_entry && strlen(module_from_entry) > 0) {
+ module_to_use = module_from_entry;
+ } else if (global_module_name && strlen(global_module_name) > 0) {
+ module_to_use = global_module_name;
+ } else {
+ module_to_use = "";
+ }
+ data->module = g_strdup(module_to_use);
+
+ if (data->module && strlen(data->module) > 0) {
+ if (!strcmp(data->module, "studypad"))
+ data->module_desc = g_strdup("studypad");
+ else
+ data->module_desc = g_strdup(main_get_module_description(data->module));
+ } else {
+ data->module_desc = g_strdup("");
+ }
+
+ data->description = g_strdup((gchar *)gtk_entry_get_text(GTK_ENTRY(entry_label)));
data->is_leaf = TRUE;
+ data->color = NULL;
data->opened = bm_pixbufs->pixbuf_helpdoc;
data->closed = NULL;
+
gui_add_item_to_tree(&iter, &selected, data);
bookmarks_changed = TRUE;
gui_save_bookmarks(NULL, NULL);
- g_free(data->caption);
- g_free(data->key);
- g_free(data->module);
- g_free(data->description);
- g_free(data);
}
/******************************************************************************
@@ -147,56 +167,100 @@ static void add_folder_button(void)
GtkTreeIter iter;
BOOKMARK_DATA *data;
GtkTreeSelection *selection;
- char *t;
- gint test;
- GS_DIALOG *info;
- GString *str;
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
if (!gtk_tree_selection_get_selected(selection, NULL, &selected))
return;
- t = "|";
- str = g_string_new("");
- info = gui_new_dialog();
- //info->stock_icon = GTK_STOCK_OPEN;
- info->title = _("Bookmark");
- g_string_printf(str, "%s",
- _("Enter Folder Name"));
- info->label_top = str->str;
- info->text1 = g_strdup(_("Folder Name"));
- info->label1 = _("Folder: ");
- info->ok = TRUE;
- info->cancel = TRUE;
-
- data = g_new(BOOKMARK_DATA, 1);
- /*** open dialog to get name for new folder ***/
- test = gui_gs_dialog(info);
- if (test == GS_OK) {
- char *buf = g_strdelimit(info->text1, t, ' ');
- data->caption = g_strdup(buf);
- data->key = NULL;
- data->module = NULL;
- data->module_desc = NULL;
- data->description = NULL;
+ gchar *glade_file = gui_general_user_file("folder" UI_SUFFIX, TRUE);
+ if (!glade_file) return;
+
+#ifdef USE_GTKBUILDER
+ GtkBuilder *gxml = gtk_builder_new();
+ gtk_builder_add_from_file(gxml, glade_file, NULL);
+#else
+ GladeXML *gxml = glade_xml_new(glade_file, NULL, NULL);
+#endif
+ g_free(glade_file);
+
+ GtkWidget *entry = GTK_WIDGET(UI_GET_ITEM(gxml, "folder_entry_name"));
+ GtkWidget *dialog = GTK_WIDGET(UI_GET_ITEM(gxml, "dialog_folder"));
+ GtkWidget *colorbtn = GTK_WIDGET(UI_GET_ITEM(gxml, "folder_color_button"));
+ GtkWidget *clearbtn = GTK_WIDGET(UI_GET_ITEM(gxml, "folder_clear_color"));
+
+#ifdef USE_GTKBUILDER
+ if (!dialog || !entry || !colorbtn || !clearbtn) {
+ g_object_unref(gxml);
+ return;
+ }
+#else
+ if (!dialog || !entry || !colorbtn || !clearbtn) return;
+
+ /* Détruire les boutons inactifs de Glade et ajouter les vrais boutons GTK2 */
+ GtkWidget *btn_ok = GTK_WIDGET(UI_GET_ITEM(gxml, "folder_ok"));
+ GtkWidget *btn_cancel = GTK_WIDGET(UI_GET_ITEM(gxml, "folder_cancel"));
+ if (btn_ok) gtk_widget_destroy(btn_ok);
+ if (btn_cancel) gtk_widget_destroy(btn_cancel);
+
+ gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+ gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_OK, GTK_RESPONSE_OK);
+ gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
+#endif
+
+ gtk_window_set_title(GTK_WINDOW(dialog), _("New Tag"));
+ gtk_entry_set_text(GTK_ENTRY(entry), "");
+
+ /* Cette ligne est commune, on la sort du #ifdef */
+ gtk_button_set_label(GTK_BUTTON(clearbtn), _("Add color"));
+ g_signal_connect(clearbtn, "clicked",
+ G_CALLBACK(toggle_color_clicked), colorbtn);
+ g_signal_connect(colorbtn, "color-set",
+ G_CALLBACK(toggle_color_clicked), clearbtn);
+
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) {
+ const gchar *name = gtk_entry_get_text(GTK_ENTRY(entry));
+ gchar *color = NULL;
+
+ if (g_strcmp0(gtk_button_get_label(GTK_BUTTON(clearbtn)), _("No color")) == 0) {
+#if defined(USE_GTKBUILDER) && GTK_CHECK_VERSION(3, 4, 0)
+ GdkRGBA rgba;
+ gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(colorbtn), &rgba);
+ if (rgba.red < 0.99 || rgba.green < 0.99 || rgba.blue < 0.99)
+ color = g_strdup_printf("#%02X%02X%02X",
+ (guint)(rgba.red * 255),
+ (guint)(rgba.green * 255),
+ (guint)(rgba.blue * 255));
+#else
+ /* API de récupération de couleur pour GTK2 */
+ GdkColor gdk_color;
+ gtk_color_button_get_color(GTK_COLOR_BUTTON(colorbtn), &gdk_color);
+ if (gdk_color.red < 65000 || gdk_color.green < 65000 || gdk_color.blue < 65000)
+ color = g_strdup_printf("#%02X%02X%02X",
+ gdk_color.red >> 8,
+ gdk_color.green >> 8,
+ gdk_color.blue >> 8);
+#endif
+ }
+
+ data = g_new0(BOOKMARK_DATA, 1);
+ data->caption = g_strdelimit(g_strdup(name), "/|><.'`\"", ' ');
+ data->color = color;
data->is_leaf = FALSE;
data->opened = bm_pixbufs->pixbuf_opened;
data->closed = bm_pixbufs->pixbuf_closed;
gui_add_item_to_tree(&iter, &selected, data);
bookmarks_changed = TRUE;
gui_save_bookmarks(NULL, NULL);
- g_free(data->caption);
- GtkTreePath *path =
- gtk_tree_model_get_path(GTK_TREE_MODEL(model), &iter);
- gtk_tree_view_expand_to_path(GTK_TREE_VIEW(treeview),
- path);
+
+ GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(model), &iter);
+ gtk_tree_view_expand_to_path(GTK_TREE_VIEW(treeview), path);
gtk_tree_selection_select_path(selection, path);
gtk_tree_path_free(path);
}
- g_free(data);
- g_free(info->text1);
- g_free(info);
- g_string_free(str, TRUE);
+ gtk_widget_destroy(dialog);
+#ifdef USE_GTKBUILDER
+ g_object_unref(gxml);
+#endif
}
/******************************************************************************
@@ -232,7 +296,6 @@ void on_dialog_response(GtkDialog *dialog,
break;
}
}
-
/******************************************************************************
* Name
* on_dialog_enter
@@ -249,8 +312,8 @@ void on_dialog_response(GtkDialog *dialog,
void on_dialog_enter(void)
{
- on_dialog_response(GTK_DIALOG(bookmark_dialog),
- GTK_RESPONSE_OK, NULL);
+ if (bookmark_dialog)
+ gtk_dialog_response(GTK_DIALOG(bookmark_dialog), GTK_RESPONSE_OK);
}
/******************************************************************************
@@ -441,8 +504,6 @@ static GtkWidget *_create_bookmark_dialog(gchar *label,
/* lookup the root widget */
bookmark_dialog = UI_GET_ITEM(gxml, "dialog");
- g_signal_connect(bookmark_dialog, "response",
- G_CALLBACK(on_dialog_response), NULL);
/* treeview */
treeview = UI_GET_ITEM(gxml, "treeview");
@@ -577,11 +638,37 @@ static GtkWidget *_create_mark_verse_dialog(gchar *module, gchar *key)
void gui_bookmark_dialog(gchar *label, gchar *module_name, gchar *key)
{
- GtkWidget *dialog =
- _create_bookmark_dialog(label, module_name, key);
+ GtkWidget *dialog;
+ gint response;
+
+ if (global_module_name != NULL) {
+ g_free(global_module_name);
+ global_module_name = NULL;
+ }
+
+ if (module_name != NULL) {
+ global_module_name = g_strdup(module_name);
+ }
+
+ dialog = _create_bookmark_dialog(label, module_name, key);
if (!dialog)
return;
- gtk_dialog_run(GTK_DIALOG(dialog));
+
+ while (TRUE) {
+ response = gtk_dialog_run(GTK_DIALOG(dialog));
+ if (response == GTK_RESPONSE_ACCEPT) {
+ /* New folder — keep dialog open */
+ add_folder_button();
+ } else if (response == GTK_RESPONSE_OK) {
+ /* Add bookmark — close dialog */
+ add_bookmark_button();
+ break;
+ } else {
+ /* Cancel or destroy */
+ break;
+ }
+ }
+ gtk_widget_destroy(dialog);
}
/******************************************************************************
diff --git a/src/gtk/bookmarks_menu.c b/src/gtk/bookmarks_menu.c
index 66fd28e24..677f402c0 100644
--- a/src/gtk/bookmarks_menu.c
+++ b/src/gtk/bookmarks_menu.c
@@ -50,8 +50,8 @@
#include "gui/widgets.h"
#include "main/settings.h"
-#include "main/sidebar.h"
#include "main/sword.h"
+#include "main/sidebar.h"
#include "main/xml.h"
#include "main/module_dialogs.h"
#include "main/url.hh"
@@ -68,6 +68,21 @@ BOOKMARK_MENU menu;
gboolean bookmarks_changed;
+static void toggle_color_clicked(GtkButton *btn, GtkWidget *other)
+{
+ if (GTK_IS_COLOR_BUTTON(btn)) {
+ /* colorbtn clicked — enable "No color" */
+ gtk_button_set_label(GTK_BUTTON(other), _("No color"));
+ } else {
+ /* clearbtn clicked — toggle */
+ gboolean has_color =
+ g_strcmp0(gtk_button_get_label(GTK_BUTTON(btn)),
+ _("No color")) == 0;
+ gtk_button_set_label(GTK_BUTTON(btn),
+ has_color ? _("Add color") : _("No color"));
+ }
+}
+
/******************************************************************************
* Name
* save_treeview_to_xml_bookmarks
@@ -90,12 +105,12 @@ static void save_treeview_to_xml_bookmarks(GtkTreeIter *iter,
xmlNodePtr root_node = NULL;
xmlNodePtr cur_node = NULL;
xmlDocPtr root_doc;
- // xmlAttrPtr root_attr;
gchar *caption = NULL;
gchar *key = NULL;
gchar *module = NULL;
gchar *mod_desc = NULL;
gchar *description = NULL;
+ gchar *color = NULL;
if (!bookmarks_changed)
return;
@@ -104,7 +119,6 @@ static void save_treeview_to_xml_bookmarks(GtkTreeIter *iter,
if (root_doc != NULL) {
root_node = xmlNewNode(NULL, (const xmlChar *)"SwordBookmarks");
- //root_attr =
xmlNewProp(root_node, (const xmlChar *)"syntaxVersion",
(const xmlChar *)"1");
xmlDocSetRootElement(root_doc, root_node);
@@ -112,24 +126,31 @@ static void save_treeview_to_xml_bookmarks(GtkTreeIter *iter,
do {
gtk_tree_model_get(GTK_TREE_MODEL(model), iter,
- 2, &caption,
- 3, &key,
- 4, &module,
- 5, &mod_desc, 6, &description, -1);
+ COL_CAPTION, &caption,
+ COL_KEY, &key,
+ COL_MODULE, &module,
+ COL_MODULE_DESC, &mod_desc,
+ COL_DESCRIPTION, &description,
+ COL_COLOR, &color,
+ -1);
if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(model), iter)) {
- cur_node =
- xml_add_folder_to_parent(root_node, caption);
+ /* folder node — write color attribute when present */
+ cur_node = xml_add_folder_to_parent_colored(root_node,
+ caption,
+ color);
utilities_parse_treeview(cur_node, iter,
GTK_TREE_MODEL(model));
- } else
+ } else {
xml_add_bookmark_to_parent(root_node,
description,
key, module, mod_desc);
- g_free(caption);
- g_free(key);
- g_free(module);
- g_free(mod_desc);
- g_free(description);
+ }
+// g_free(caption);
+// g_free(key);
+// g_free(module);
+// g_free(mod_desc);
+// g_free(description);
+// g_free(color);
} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), iter));
xmlSaveFormatFile(filename, root_doc, 1);
@@ -167,7 +188,9 @@ static void add_item_to_tree(GtkTreeIter *iter, GtkTreeIter *parent,
COL_KEY, data->key,
COL_MODULE, data->module,
COL_MODULE_DESC, data->module_desc,
- COL_DESCRIPTION, data->description, -1);
+ COL_DESCRIPTION, data->description,
+ COL_COLOR, data->color,
+ -1);
}
/******************************************************************************
@@ -296,8 +319,8 @@ G_MODULE_EXPORT void on_dialog_activate(GtkMenuItem *menuitem,
if (module && (main_get_mod_type(module) == PERCOM_TYPE)) {
editor_create_new(module, key, TRUE);
use_dialog = FALSE;
- g_free(module);
- g_free(key);
+// g_free(module);
+// g_free(key);
return;
}
@@ -310,8 +333,8 @@ G_MODULE_EXPORT void on_dialog_activate(GtkMenuItem *menuitem,
g_free(url);
}
use_dialog = FALSE;
- g_free(module);
- g_free(key);
+// g_free(module);
+// g_free(key);
}
/******************************************************************************
@@ -333,76 +356,139 @@ G_MODULE_EXPORT void on_dialog_activate(GtkMenuItem *menuitem,
G_MODULE_EXPORT void on_edit_item_activate(GtkMenuItem *menuitem,
gpointer user_data)
{
- GS_DIALOG *info;
- gint test;
GtkTreeSelection *selection;
GtkTreeIter selected;
- // GtkTreeIter iter;
- gchar *caption = NULL;
- gchar *key = NULL;
- gchar *module = NULL;
- gchar *mod_desc = NULL;
- gchar *description = NULL;
- gboolean is_leaf;
- GString *str;
-
- str = g_string_new(NULL);
- g_string_printf(str, "%s", _("Edit"));
+ gchar *caption = NULL, *key = NULL, *module = NULL;
+ gchar *mod_desc = NULL, *description = NULL, *current_color = NULL;
selection = gtk_tree_view_get_selection(bookmark_tree);
if (!gtk_tree_selection_get_selected(selection, NULL, &selected))
return;
gtk_tree_model_get(GTK_TREE_MODEL(model), &selected,
- 2, &caption,
- 3, &key,
- 4, &module, 5, &mod_desc, 6, &description, -1);
+ COL_CAPTION, &caption, COL_KEY, &key,
+ COL_MODULE, &module, COL_MODULE_DESC, &mod_desc,
+ COL_DESCRIPTION, &description,
+ COL_COLOR, ¤t_color, -1);
- info = gui_new_dialog();
- info->title = _("Bookmark");
- info->label_top = str->str;
- if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(model), &selected)) {
- info->label1 = _("Folder name: ");
- is_leaf = FALSE;
- } else {
- info->label1 = _("Bookmark name: ");
- info->text2 = g_strdup(key);
- info->text3 = g_strdup(module);
- info->label2 = _("Verse: ");
- info->label3 = _("Module: ");
- is_leaf = TRUE;
- }
+ if (!key || !*key) {
- info->text1 = g_strdup(caption);
- info->ok = TRUE;
- info->cancel = TRUE;
+ /* --- Folder: use the dedicated folder dialog --- */
+ gchar *glade_file = gui_general_user_file("folder" UI_SUFFIX, TRUE);
+ if (!glade_file) goto cleanup;
+#ifdef USE_GTKBUILDER
+ GtkBuilder *gxml = gtk_builder_new();
+ gtk_builder_add_from_file(gxml, glade_file, NULL);
+#else
+ GladeXML *gxml = glade_xml_new(glade_file, NULL, NULL);
+#endif
+ GtkWidget *dialog = GTK_WIDGET(UI_GET_ITEM(gxml, "dialog_folder"));
+ GtkWidget *entry = GTK_WIDGET(UI_GET_ITEM(gxml, "folder_entry_name"));
+ GtkWidget *colorbtn = GTK_WIDGET(UI_GET_ITEM(gxml, "folder_color_button"));
+ GtkWidget *clearbtn = GTK_WIDGET(UI_GET_ITEM(gxml, "folder_clear_color"));
- test = gui_gs_dialog(info);
- if (test == GS_OK) {
- BOOKMARK_DATA *data = g_new(BOOKMARK_DATA, 1);
- data->caption = info->text1;
- data->key = NULL;
- data->module = NULL;
- data->module_desc = NULL;
- data->description = NULL;
- if (is_leaf) {
- data->opened = bm_pixbufs->pixbuf_helpdoc;
- data->closed = NULL;
- data->key = info->text2;
- data->module = info->text3;
- data->module_desc =
- g_strdup(main_get_module_description(info->text3));
- if ((strlen(description) > 1) || (strcmp(caption, info->text1))) {
- data->description = info->text1;
- } else
- data->description = NULL;
- data->is_leaf = TRUE;
+#ifdef USE_GTKBUILDER
+ if (!dialog || !entry || !colorbtn || !clearbtn) {
+ g_object_unref(gxml); goto cleanup;
+ }
+#else
+ if (!dialog || !entry || !colorbtn || !clearbtn) goto cleanup;
+
+ GtkWidget *btn_ok = GTK_WIDGET(UI_GET_ITEM(gxml, "folder_ok"));
+ GtkWidget *btn_cancel = GTK_WIDGET(UI_GET_ITEM(gxml, "folder_cancel"));
+ if (btn_ok) gtk_widget_destroy(btn_ok);
+ if (btn_cancel) gtk_widget_destroy(btn_cancel);
+
+ gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+ gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_OK, GTK_RESPONSE_OK);
+ gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
+#endif
+ gtk_window_set_title(GTK_WINDOW(dialog), _("Edit Tag"));
+ gtk_entry_set_text(GTK_ENTRY(entry), caption ? caption : "");
+
+ if (current_color && *current_color) {
+#if defined(USE_GTKBUILDER) && GTK_CHECK_VERSION(3, 4, 0)
+ GdkRGBA rgba;
+ if (gdk_rgba_parse(&rgba, current_color))
+ gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(colorbtn), &rgba);
+#else
+ GdkColor gdk_color;
+ if (gdk_color_parse(current_color, &gdk_color))
+ gtk_color_button_set_color(GTK_COLOR_BUTTON(colorbtn), &gdk_color);
+#endif
} else {
- data->opened = bm_pixbufs->pixbuf_opened;
- data->closed = bm_pixbufs->pixbuf_closed;
- data->is_leaf = FALSE;
+ gtk_button_set_label(GTK_BUTTON(clearbtn), _("Add color"));
}
- gtk_tree_store_set(GTK_TREE_STORE(model), &selected,
+ g_signal_connect(clearbtn, "clicked",
+ G_CALLBACK(toggle_color_clicked), colorbtn);
+ g_signal_connect(colorbtn, "color-set",
+ G_CALLBACK(toggle_color_clicked), clearbtn);
+
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) {
+ const gchar *name = gtk_entry_get_text(GTK_ENTRY(entry));
+ gchar *new_color = NULL;
+ gchar *new_caption;
+ if (g_strcmp0(gtk_button_get_label(GTK_BUTTON(clearbtn)), _("No color")) == 0) {
+#if defined(USE_GTKBUILDER) && GTK_CHECK_VERSION(3, 4, 0)
+ GdkRGBA rgba;
+ gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(colorbtn), &rgba);
+ if (rgba.red < 0.99 || rgba.green < 0.99 || rgba.blue < 0.99)
+ new_color = g_strdup_printf("#%02X%02X%02X",
+ (guint)(rgba.red * 255),
+ (guint)(rgba.green * 255),
+ (guint)(rgba.blue * 255));
+#else
+ GdkColor gdk_color;
+ gtk_color_button_get_color(GTK_COLOR_BUTTON(colorbtn), &gdk_color);
+ if (gdk_color.red < 65000 || gdk_color.green < 65000 || gdk_color.blue < 65000)
+ new_color = g_strdup_printf("#%02X%02X%02X",
+ gdk_color.red >> 8,
+ gdk_color.green >> 8,
+ gdk_color.blue >> 8);
+#endif
+ }
+ new_caption = g_strdelimit(g_strdup(name), "/|><.'`\"", ' ');
+
+ bookmarks_changed = TRUE;
+ gtk_tree_store_set(GTK_TREE_STORE(model), &selected,
+ COL_CAPTION, new_caption,
+ COL_COLOR, new_color, -1);
+ gui_save_bookmarks(NULL, NULL);
+ main_display_bible(NULL, settings.currentverse);
+// g_free(new_caption);
+// g_free(new_color);
+ }
+ gtk_widget_destroy(dialog);
+#ifdef USE_GTKBUILDER
+ g_object_unref(gxml);
+#endif
+ } else {
+ /* --- Leaf bookmark: generic dialog --- */
+ GS_DIALOG *info = gui_new_dialog();
+ GString *str = g_string_new(NULL);
+ g_string_printf(str, "%s", _("Edit"));
+ info->title = _("Bookmark");
+ info->label_top = str->str;
+ info->label1 = _("Bookmark name: ");
+ info->text1 = g_strdup(caption);
+ info->text2 = g_strdup(key);
+ info->text3 = g_strdup(module);
+ info->label2 = _("Verse: ");
+ info->label3 = _("Module: ");
+ info->ok = TRUE;
+ info->cancel = TRUE;
+ if (gui_gs_dialog(info) == GS_OK) {
+ BOOKMARK_DATA *data = g_new0(BOOKMARK_DATA, 1);
+ data->caption = g_strdup(info->text1);
+ data->key = g_strdup(info->text2);
+ data->module = g_strdup(info->text3);
+ data->module_desc = g_strdup(main_get_module_description(info->text3));
+ data->description = ((description && strlen(description) > 1) ||
+ (caption && strcmp(caption, info->text1)))
+ ? g_strdup(info->text1) : NULL;
+ data->is_leaf = TRUE;
+ data->opened = bm_pixbufs->pixbuf_helpdoc;
+ gtk_tree_store_set(GTK_TREE_STORE(model), &selected,
COL_OPEN_PIXBUF, data->opened,
COL_CLOSED_PIXBUF, data->closed,
COL_CAPTION, data->caption,
@@ -410,26 +496,22 @@ G_MODULE_EXPORT void on_edit_item_activate(GtkMenuItem *menuitem,
COL_MODULE, data->module,
COL_MODULE_DESC, data->module_desc,
COL_DESCRIPTION, data->description, -1);
- bookmarks_changed = TRUE;
- gui_save_bookmarks(NULL, NULL);
- g_free(data);
+ bookmarks_changed = TRUE;
+ gui_save_bookmarks(NULL, NULL);
+ }
+// g_free(info->text1);
+// if (info->text2) g_free(info->text2);
+// if (info->text3) g_free(info->text3);
+// g_free(info);
+// g_string_free(str, TRUE);
}
- g_free(info->text1); // we used g_strdup()
- if (info->text2)
- g_free(info->text2);
- if (info->text3)
- g_free(info->text3);
- g_free(info);
- g_free(caption);
- g_free(key);
- g_free(module);
- g_free(mod_desc);
- g_free(description);
- g_string_free(str, TRUE);
+cleanup:
+// g_free(caption); g_free(key); g_free(module);
+// g_free(mod_desc); g_free(description); g_free(current_color);
+(void)0;
}
-//dialog_export_bookmarks_response_cb
-//dialog_export_bookmarks_close_cb
+
/******************************************************************************
* Name
* on_remove_folder_activate
@@ -508,10 +590,10 @@ G_MODULE_EXPORT void on_delete_item_activate(GtkMenuItem *menuitem,
bookmarks_changed = TRUE;
gui_save_bookmarks(NULL, NULL);
}
- g_free(caption);
- g_free(key);
- g_free(module);
- g_free(str);
+// g_free(caption);
+// g_free(key);
+// g_free(module);
+// g_free(str);
}
/******************************************************************************
@@ -665,15 +747,16 @@ void on_add_bookmark_activate(GtkMenuItem *menuitem, gpointer user_data)
test = gui_gs_dialog(info);
if (test == GS_OK) {
- data->caption = info->text1;
- data->key = info->text2;
- data->module = info->text3;
+ data->caption = g_strdup(info->text1);
+ data->key = g_strdup(info->text2);
+ data->module = g_strdup(info->text3);
data->module_desc =
g_strdup(main_get_module_description(info->text3));
if (!strcmp(data->caption, buf))
data->description = NULL;
else
- data->description = info->text1;
+ data->description = g_strdup(info->text1);
+ data->color = NULL; /* bookmark leaves never have a color */
data->is_leaf = TRUE;
data->opened = bm_pixbufs->pixbuf_helpdoc;
data->closed = NULL;
@@ -681,12 +764,11 @@ void on_add_bookmark_activate(GtkMenuItem *menuitem, gpointer user_data)
bookmarks_changed = TRUE;
gui_save_bookmarks(NULL, NULL);
}
- g_free(info->text1); /* we used g_strdup() */
- g_free(info->text2);
- g_free(info->text3);
- g_free(info);
- g_free(data);
- g_string_free(str, TRUE);
+// g_free(info->text1); /* we used g_strdup() */
+// g_free(info->text2);
+// g_free(info->text3);
+// g_free(info);
+// g_string_free(str, TRUE);
}
/******************************************************************************
@@ -734,53 +816,92 @@ G_MODULE_EXPORT void on_new_folder_activate(GtkMenuItem *menuitem,
{
GtkTreeIter selected;
GtkTreeIter iter;
- // gchar *caption = NULL;
- // gchar *key = NULL;
- // gchar *module = NULL;
- char *t;
- gint test;
- GS_DIALOG *info;
BOOKMARK_DATA *data;
- GString *str;
if (!gtk_tree_selection_get_selected(current_selection, NULL, &selected))
return;
- t = "/|><.'`\"";
- str = g_string_new("");
- info = gui_new_dialog();
- //info->stock_icon = GTK_STOCK_OPEN;
- info->title = _("Bookmark");
- g_string_printf(str, "%s",
- _("Enter Folder Name"));
- info->label_top = str->str;
- info->text1 = g_strdup(_("Folder Name"));
- info->label1 = _("Folder: ");
- info->ok = TRUE;
- info->cancel = TRUE;
+ gchar *glade_file = gui_general_user_file("folder" UI_SUFFIX, TRUE);
+ g_return_if_fail(glade_file != NULL);
+#ifdef USE_GTKBUILDER
+ GtkBuilder *gxml = gtk_builder_new();
+ gtk_builder_add_from_file(gxml, glade_file, NULL);
+#else
+ GladeXML *gxml = glade_xml_new(glade_file, NULL, NULL);
+#endif
+ g_free(glade_file);
- data = g_new(BOOKMARK_DATA, 1);
- /*** open dialog to get name for new folder ***/
- test = gui_gs_dialog(info);
- if (test == GS_OK) {
- char *buf = g_strdelimit(info->text1, t, ' ');
- data->caption = g_strdup(buf);
- data->key = NULL;
- data->module = NULL;
- data->module_desc = NULL;
- data->description = NULL;
+ GtkWidget *dialog = GTK_WIDGET(UI_GET_ITEM(gxml, "dialog_folder"));
+ GtkWidget *entry = GTK_WIDGET(UI_GET_ITEM(gxml, "folder_entry_name"));
+ GtkWidget *colorbtn = GTK_WIDGET(UI_GET_ITEM(gxml, "folder_color_button"));
+ GtkWidget *clearbtn = GTK_WIDGET(UI_GET_ITEM(gxml, "folder_clear_color"));
+
+#ifdef USE_GTKBUILDER
+ if (!dialog || !entry || !colorbtn || !clearbtn) {
+ g_printerr("ERROR: dialog_folder widgets not found\n");
+ g_object_unref(gxml);
+ return;
+ }
+#else
+ if (!dialog || !entry || !colorbtn || !clearbtn) return;
+
+ GtkWidget *btn_ok = GTK_WIDGET(UI_GET_ITEM(gxml, "folder_ok"));
+ GtkWidget *btn_cancel = GTK_WIDGET(UI_GET_ITEM(gxml, "folder_cancel"));
+ if (btn_ok) gtk_widget_destroy(btn_ok);
+ if (btn_cancel) gtk_widget_destroy(btn_cancel);
+
+ gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+ gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_OK, GTK_RESPONSE_OK);
+ gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
+#endif
+
+ gtk_window_set_title(GTK_WINDOW(dialog), _("New Folder"));
+ gtk_entry_set_text(GTK_ENTRY(entry), "");
+ gtk_button_set_label(GTK_BUTTON(clearbtn), _("Add color"));
+ g_signal_connect(clearbtn, "clicked",
+ G_CALLBACK(toggle_color_clicked), colorbtn);
+ g_signal_connect(colorbtn, "color-set",
+ G_CALLBACK(toggle_color_clicked), clearbtn);
+
+ gint response = gtk_dialog_run(GTK_DIALOG(dialog));
+ if (response == GTK_RESPONSE_OK) {
+ const gchar *name = gtk_entry_get_text(GTK_ENTRY(entry));
+ gchar *color = NULL;
+
+ if (g_strcmp0(gtk_button_get_label(GTK_BUTTON(clearbtn)), _("No color")) == 0) {
+#if defined(USE_GTKBUILDER) && GTK_CHECK_VERSION(3, 4, 0)
+ GdkRGBA rgba;
+ gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(colorbtn), &rgba);
+ if (rgba.red < 0.99 || rgba.green < 0.99 || rgba.blue < 0.99)
+ color = g_strdup_printf("#%02X%02X%02X",
+ (guint)(rgba.red * 255),
+ (guint)(rgba.green * 255),
+ (guint)(rgba.blue * 255));
+#else
+ GdkColor gdk_color;
+ gtk_color_button_get_color(GTK_COLOR_BUTTON(colorbtn), &gdk_color);
+ if (gdk_color.red < 65000 || gdk_color.green < 65000 || gdk_color.blue < 65000)
+ color = g_strdup_printf("#%02X%02X%02X",
+ gdk_color.red >> 8,
+ gdk_color.green >> 8,
+ gdk_color.blue >> 8);
+#endif
+ }
+
+ data = g_new0(BOOKMARK_DATA, 1);
+ data->caption = g_strdelimit(g_strdup(name), "/|><.'`\"", ' ');
+ data->color = color;
data->is_leaf = FALSE;
data->opened = bm_pixbufs->pixbuf_opened;
data->closed = bm_pixbufs->pixbuf_closed;
- add_item_to_tree(&iter, &selected, data);
bookmarks_changed = TRUE;
+ add_item_to_tree(&iter, &selected, data);
gui_save_bookmarks(NULL, NULL);
- g_free(data->caption);
}
- g_free(data);
- g_free(info->text1);
- g_free(info);
- g_string_free(str, TRUE);
+ gtk_widget_destroy(dialog);
+#ifdef USE_GTKBUILDER
+ g_object_unref(gxml);
+#endif
}
/******************************************************************************
@@ -820,9 +941,9 @@ G_MODULE_EXPORT void on_open_in_tab_activate(GtkMenuItem *menuitem,
main_url_encode(key),
main_url_encode(module));
main_url_handler(url, TRUE);
- g_free(key);
- g_free(module);
- g_free(url);
+// g_free(key);
+// g_free(module);
+// g_free(url);
}
/******************************************************************************
@@ -842,6 +963,53 @@ G_MODULE_EXPORT void on_open_in_tab_activate(GtkMenuItem *menuitem,
* void
*/
+#if GTK_CHECK_VERSION(3, 4, 0)
+G_MODULE_EXPORT void on_set_tag_color_activate(GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+ GtkTreeIter selected;
+ gchar *color = NULL;
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(bookmark_tree);
+
+ if (!gtk_tree_selection_get_selected(selection, NULL, &selected))
+ return;
+
+ /* Only folders get a color */
+ if (!gtk_tree_model_iter_has_child(GTK_TREE_MODEL(model), &selected))
+ return;
+
+ GtkWidget *dialog = gtk_color_chooser_dialog_new(
+ _("Choose folder color"), GTK_WINDOW(widgets.app));
+
+ /* Pre-load existing color if any */
+ gtk_tree_model_get(GTK_TREE_MODEL(model), &selected,
+ COL_COLOR, &color, -1);
+ if (color && *color) {
+ GdkRGBA rgba;
+ if (gdk_rgba_parse(&rgba, color))
+ gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &rgba);
+ }
+// g_free(color);
+
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) {
+ GdkRGBA rgba;
+ gchar *hex;
+ gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &rgba);
+ hex = g_strdup_printf("#%02X%02X%02X",
+ (guint)(rgba.red * 255),
+ (guint)(rgba.green * 255),
+ (guint)(rgba.blue * 255));
+ bookmarks_changed = TRUE;
+ gtk_tree_store_set(GTK_TREE_STORE(model), &selected,
+ COL_COLOR, hex, -1);
+// g_free(hex);
+ gui_save_bookmarks(NULL, NULL);
+ main_display_bible(NULL, settings.currentverse);
+ }
+ gtk_widget_destroy(dialog);
+}
+#endif
+
void gui_create_bookmark_menu(void)
{
gchar *glade_file;
@@ -873,6 +1041,7 @@ void gui_create_bookmark_menu(void)
menu.reorder = UI_GET_ITEM(gxml, "allow_reordering");
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");
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 91b70dff7..e47eeda4d 100644
--- a/src/gtk/bookmarks_treeview.c
+++ b/src/gtk/bookmarks_treeview.c
@@ -28,6 +28,7 @@
#include
#include
+#include
#include
#include
#include
@@ -153,26 +154,47 @@ void gui_verselist_to_bookmarks(GList *verses, gint save_as_single)
bm_pixbufs->pixbuf_opened,
COL_CLOSED_PIXBUF,
bm_pixbufs->pixbuf_closed,
- COL_CAPTION, info->text1,
+ COL_CAPTION, g_strdup(info->text1),
COL_KEY, NULL, COL_MODULE, NULL, -1);
// set_results_position((char) 1); // TOP
GString *str = g_string_new(" ");
while (verses) {
+ BOOKMARK_DATA *data;
+
list_item = (RESULTS *)verses->data;
module_name = list_item->module;
gchar *tmpbuf = list_item->key;
g_string_printf(str, "%s, %s", tmpbuf,
module_name);
XI_message(("bookmark: %s", str->str));
+
+ data = g_new(BOOKMARK_DATA, 1);
+ data->caption = g_strdup(str->str);
+ data->key = g_strdup(tmpbuf);
+ data->module = g_strdup(module_name);
+ if (!strcmp(data->module, "studypad"))
+ data->module_desc = g_strdup("studypad");
+ else
+ data->module_desc = g_strdup(main_get_module_description(data->module));
+ data->description = g_strdup("");
+ data->is_leaf = TRUE;
+ data->opened = bm_pixbufs->pixbuf_helpdoc;
+ data->closed = NULL;
+ data->color = NULL;
+
gtk_tree_store_append(GTK_TREE_STORE(model),
&iter, &parent);
gtk_tree_store_set(GTK_TREE_STORE(model), &iter,
- COL_OPEN_PIXBUF,
- bm_pixbufs->pixbuf_helpdoc,
- COL_CLOSED_PIXBUF, NULL,
- COL_CAPTION, str->str,
- COL_KEY, tmpbuf,
- COL_MODULE, module_name, -1);
+ COL_OPEN_PIXBUF, data->opened,
+ COL_CLOSED_PIXBUF, data->closed,
+ COL_CAPTION, data->caption,
+ COL_KEY, data->key,
+ COL_MODULE, data->module,
+ COL_MODULE_DESC, data->module_desc,
+ COL_DESCRIPTION, data->description,
+ COL_COLOR, data->color,
+ -1);
+
verses = g_list_next(verses);
}
g_string_free(str, TRUE);
@@ -203,6 +225,7 @@ void gui_verselist_to_bookmarks(GList *verses, gint save_as_single)
static void get_xml_folder_data(xmlNodePtr cur, BOOKMARK_DATA *data)
{
xmlChar *folder;
+ gchar *color;
folder = xmlGetProp(cur, (const xmlChar *)"caption");
data->caption = g_strdup((char *)folder);
@@ -213,6 +236,12 @@ static void get_xml_folder_data(xmlNodePtr cur, BOOKMARK_DATA *data)
data->is_leaf = FALSE;
data->opened = bm_pixbufs->pixbuf_opened;
data->closed = bm_pixbufs->pixbuf_closed;
+
+ /* color is optional — NULL when the folder has no tag color assigned */
+ color = xml_get_folder_color(cur);
+ data->color = color ? g_strdup(color) : NULL;
+ if (color)
+ xmlFree((xmlChar *)color);
}
/******************************************************************************
@@ -261,6 +290,7 @@ static void get_xml_bookmark_data(xmlNodePtr cur, BOOKMARK_DATA *data)
data->description = g_strdup((char *)description);
data->module_desc = g_strdup((char *)mod_desc);
data->is_leaf = TRUE;
+ data->color = NULL; /* leaves (bookmarks) never carry a color */
}
/******************************************************************************
@@ -281,16 +311,6 @@ static void get_xml_bookmark_data(xmlNodePtr cur, BOOKMARK_DATA *data)
static void free_bookmark_data(BOOKMARK_DATA *data)
{
- if (data->caption)
- g_free(data->caption);
- if (data->key)
- g_free(data->key);
- if (data->module)
- g_free(data->module);
- if (data->module_desc)
- g_free(data->module_desc);
- if (data->description)
- g_free(data->description);
}
/******************************************************************************
@@ -322,7 +342,9 @@ void gui_add_item_to_tree(GtkTreeIter *iter, GtkTreeIter *parent,
COL_KEY, data->key,
COL_MODULE, data->module,
COL_MODULE_DESC, data->module_desc,
- COL_DESCRIPTION, data->description, -1);
+ COL_DESCRIPTION, data->description,
+ COL_COLOR, data->color,
+ -1);
}
/******************************************************************************
@@ -345,22 +367,18 @@ void gui_add_item_to_tree(GtkTreeIter *iter, GtkTreeIter *parent,
static void add_node(xmlNodePtr cur, GtkTreeIter *parent)
{
GtkTreeIter iter;
- BOOKMARK_DATA data, *p = NULL;
+ BOOKMARK_DATA *p = NULL;
xmlNodePtr work = NULL;
- p = &data;
- if (cur == NULL)
- return;
-
for (work = cur->xmlChildrenNode; work; work = work->next) {
if (!xmlStrcmp(work->name, (const xmlChar *)"Bookmark")) {
+ p = g_new(BOOKMARK_DATA, 1);
get_xml_bookmark_data(work, p);
gui_add_item_to_tree(&iter, parent, p);
- free_bookmark_data(p);
} else if (!xmlStrcmp(work->name, (const xmlChar *)"Folder")) {
+ p = g_new(BOOKMARK_DATA, 1);
get_xml_folder_data(work, p);
gui_add_item_to_tree(&iter, parent, p);
- free_bookmark_data(p);
add_node(work, &iter);
}
}
@@ -386,32 +404,28 @@ void gui_parse_bookmarks(GtkTreeView *tree, const xmlChar *file,
GtkTreeIter *parent)
{
xmlNodePtr cur = NULL;
- BOOKMARK_DATA data, *p = NULL;
+ BOOKMARK_DATA *p = NULL;
GtkTreeIter iter;
GtkTreePath *path;
- p = &data;
cur = xml_load_bookmark_file(file);
- //cur = cur->xmlChildrenNode;
+
while (cur != NULL) {
if (!xmlStrcmp(cur->name, (const xmlChar *)"Bookmark")) {
+ p = g_new(BOOKMARK_DATA, 1);
get_xml_bookmark_data(cur, p);
gui_add_item_to_tree(&iter, parent, p);
- free_bookmark_data(p);
} else {
+ p = g_new(BOOKMARK_DATA, 1);
get_xml_folder_data(cur, p);
if (p->caption) {
gui_add_item_to_tree(&iter, parent, p);
}
- free_bookmark_data(p);
add_node(cur, &iter);
}
-
- if (cur->next)
- cur = cur->next;
- else
- break;
+ cur = cur->next;
}
+
xml_free_bookmark_doc();
path = gtk_tree_model_get_path(GTK_TREE_MODEL(model), parent);
gtk_tree_view_expand_to_path(tree, path);
@@ -512,8 +526,7 @@ static void row_deleted(GtkTreeModel *treemodel, GtkTreePath *arg1,
static void create_pixbufs(void)
{
- GtkTextDirection dir =
- gtk_widget_get_direction(GTK_WIDGET(widgets.app));
+ GtkTextDirection dir = gtk_widget_get_direction(GTK_WIDGET(widgets.app));
bm_pixbufs = g_new0(BookMarksPixbufs, 1);
@@ -578,6 +591,94 @@ static void create_pixbufs(void)
* void
*/
+/* Creates a round color swatch pixbuf using Cairo (14x14 px) */
+#ifdef USE_GTK_3
+static GdkPixbuf *make_color_dot(const gchar *hex_color)
+{
+ const gint SIZE = 14;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ GdkPixbuf *pixbuf;
+ GdkRGBA rgba;
+
+ if (!hex_color || !*hex_color)
+ return NULL;
+ if (!gdk_rgba_parse(&rgba, hex_color))
+ return NULL;
+
+ surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, SIZE, SIZE);
+ cr = cairo_create(surface);
+
+ /* transparent background */
+ cairo_set_source_rgba(cr, 0, 0, 0, 0);
+ cairo_paint(cr);
+
+ /* filled circle */
+ cairo_set_source_rgba(cr, rgba.red, rgba.green, rgba.blue, 1.0);
+ cairo_arc(cr, SIZE/2.0, SIZE/2.0, SIZE/2.0 - 1, 0, 2 * G_PI);
+ cairo_fill_preserve(cr);
+
+ /* thin dark border */
+ cairo_set_source_rgba(cr, 0, 0, 0, 0.35);
+ cairo_set_line_width(cr, 1.0);
+ cairo_stroke(cr);
+
+ cairo_destroy(cr);
+ pixbuf = gdk_pixbuf_get_from_surface(surface, 0, 0, SIZE, SIZE);
+ cairo_surface_destroy(surface);
+ return pixbuf;
+}
+
+/* Cell data function: generates the color dot pixbuf on the fly */
+static void color_dot_cell_func(GtkTreeViewColumn *col,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ gchar *color = NULL;
+ GdkPixbuf *dot = NULL;
+ gtk_tree_model_get(tree_model, iter, COL_COLOR, &color, -1);
+ dot = make_color_dot(color);
+ g_object_set(renderer, "pixbuf", dot, NULL);
+ if (dot) g_object_unref(dot);
+ g_free(color);
+}
+#endif
+
+#ifndef USE_GTK_3
+static void color_dot_cell_func_gtk2(GtkTreeViewColumn *tree_column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ gchar *color_str = NULL;
+ gtk_tree_model_get(tree_model, iter, COL_COLOR, &color_str, -1);
+ if (color_str && *color_str) {
+ GdkColor color;
+ if (gdk_color_parse(color_str, &color)) {
+ /* Crée un petit carré de couleur de 12x12 pixels */
+ GdkPixbuf *pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, 12, 12);
+ if (pixbuf) {
+ guint32 r = color.red >> 8;
+ guint32 g = color.green >> 8;
+ guint32 b = color.blue >> 8;
+ guint32 pixel = (r << 24) | (g << 16) | (b << 8) | 0xFF;
+ gdk_pixbuf_fill(pixbuf, pixel);
+ g_object_set(cell, "pixbuf", pixbuf, NULL);
+ g_object_unref(pixbuf);
+ }
+ } else {
+ g_object_set(cell, "pixbuf", NULL, NULL);
+ }
+ } else {
+ g_object_set(cell, "pixbuf", NULL, NULL);
+ }
+ g_free(color_str);
+}
+#endif
+
void gui_add_columns(GtkTreeView *tree)
{
GtkTreeViewColumn *column;
@@ -588,10 +689,27 @@ void gui_add_columns(GtkTreeView *tree)
renderer = GTK_CELL_RENDERER(gtk_cell_renderer_pixbuf_new());
gtk_tree_view_column_pack_start(column, renderer, FALSE);
gtk_tree_view_column_set_attributes(column, renderer,
- "pixbuf", COL_OPEN_PIXBUF,
- "pixbuf-expander-open", COL_OPEN_PIXBUF,
- "pixbuf-expander-closed", COL_CLOSED_PIXBUF, NULL);
+ "pixbuf", COL_OPEN_PIXBUF,
+ "pixbuf-expander-open", COL_OPEN_PIXBUF,
+ "pixbuf-expander-closed", COL_CLOSED_PIXBUF, NULL);
+
+#ifdef USE_GTK_3
+ /* Color dot renderer — round swatch drawn with Cairo */
+ renderer = GTK_CELL_RENDERER(gtk_cell_renderer_pixbuf_new());
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ gtk_tree_view_column_set_cell_data_func(column, renderer,
+ color_dot_cell_func,
+ NULL, NULL);
+#else
+ /* GTK2 Windows — colored square drawn via pixbuf */
+ renderer = GTK_CELL_RENDERER(gtk_cell_renderer_pixbuf_new());
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ gtk_tree_view_column_set_cell_data_func(column, renderer,
+ color_dot_cell_func_gtk2,
+ NULL, NULL);
+#endif
+ /* Caption renderer */
renderer = GTK_CELL_RENDERER(gtk_cell_renderer_text_new());
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_attributes(column, renderer,
@@ -653,12 +771,14 @@ static GtkTreeModel *create_model(void)
{
/* create tree store */
model = gtk_tree_store_new(N_COLUMNS,
- GDK_TYPE_PIXBUF,
- GDK_TYPE_PIXBUF,
- G_TYPE_STRING,
- G_TYPE_STRING,
- G_TYPE_STRING,
- G_TYPE_STRING, G_TYPE_STRING);
+ GDK_TYPE_PIXBUF, /* COL_OPEN_PIXBUF */
+ GDK_TYPE_PIXBUF, /* COL_CLOSED_PIXBUF */
+ G_TYPE_STRING, /* COL_CAPTION */
+ G_TYPE_STRING, /* COL_KEY */
+ G_TYPE_STRING, /* COL_MODULE */
+ G_TYPE_STRING, /* COL_MODULE_DESC */
+ G_TYPE_STRING, /* COL_DESCRIPTION */
+ G_TYPE_STRING); /* COL_COLOR */
return GTK_TREE_MODEL(model);
}
@@ -690,6 +810,7 @@ static gboolean button_release_event(GtkWidget *widget,
gchar *key = NULL;
gchar *module = NULL;
gchar *mod_desc = NULL;
+ gchar *range_start = NULL;
gchar *description = NULL;
button_one = FALSE;
@@ -831,6 +952,87 @@ static gboolean button_release_event(GtkWidget *widget,
* GtkWidget*
*/
+/**
+ * bookmark_get_tag_color_for_key:
+ * @osiskey: a verse key string, e.g. "Gen 1:1"
+ *
+ * Walks the bookmark GtkTreeStore and returns the color of the
+ * first tag-group folder that contains a bookmark matching @osiskey.
+ * Returns NULL if none found. Caller must g_free() the result.
+ */
+static void debug_dump_recursive(GtkTreeIter *parent, int depth)
+{
+ GtkTreeIter child;
+ if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(model), &child, parent))
+ return;
+ do {
+ gchar *caption = NULL, *color = NULL;
+ gtk_tree_model_get(GTK_TREE_MODEL(model), &child,
+ COL_CAPTION, &caption, COL_COLOR, &color, -1);
+ debug_dump_recursive(&child, depth + 1);
+ g_free(caption); g_free(color);
+ } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &child));
+}
+
+void bookmark_debug_dump_colors(void)
+{
+ GtkTreeIter root;
+ if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &root)) {
+ }
+ debug_dump_recursive(&root, 0);
+}
+
+
+gchar *bookmark_get_tag_color_for_key(const gchar *osiskey)
+{
+ GtkTreeIter folder, child;
+ gchar *color = NULL;
+
+ if (!osiskey || !model)
+ return NULL;
+
+ /* iterate top-level folders */
+ if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &folder))
+ return NULL;
+
+ do {
+ gchar *folder_color = NULL;
+ gtk_tree_model_get(GTK_TREE_MODEL(model), &folder,
+ COL_COLOR, &folder_color, -1);
+ if (!folder_color || !*folder_color) {
+ g_free(folder_color);
+ continue;
+ }
+ /* scan children of this colored folder */
+ if (gtk_tree_model_iter_children(GTK_TREE_MODEL(model),
+ &child, &folder)) {
+ do {
+ gchar *key = NULL;
+ gtk_tree_model_get(GTK_TREE_MODEL(model),
+ &child,
+ COL_KEY, &key, -1);
+ /* compare case-insensitively;
+ * also try stripping trailing spaces */
+ gchar *k = key ? g_strstrip(g_strdup(key)) : NULL;
+ gchar *q = osiskey ? g_strstrip(g_strdup(osiskey)) : NULL;
+ gboolean match = (k && q && !g_ascii_strcasecmp(k, q));
+ g_free(k); g_free(q);
+ if (match) {
+ color = g_strdup(folder_color);
+ g_free(key);
+ g_free(folder_color);
+ return color;
+ }
+ g_free(key);
+ } while (gtk_tree_model_iter_next(
+ GTK_TREE_MODEL(model), &child));
+ }
+ g_free(folder_color);
+ } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &folder));
+
+ return NULL;
+}
+
GtkWidget *gui_create_bookmark_tree(void)
{
GtkWidget *tree;
@@ -858,9 +1060,10 @@ GtkWidget *gui_create_bookmark_tree(void)
GINT_TO_POINTER(0));
use_dialog = FALSE;
bookmark_tree = GTK_TREE_VIEW(tree);
+ gtk_tree_view_set_reorderable(bookmark_tree, TRUE);
g_signal_connect(model, "row-changed",
- G_CALLBACK(row_changed), NULL);
+ G_CALLBACK(row_changed), NULL);
g_signal_connect(model, "row-deleted",
G_CALLBACK(row_deleted), NULL);
diff --git a/src/gtk/utilities.c b/src/gtk/utilities.c
index e72c9f64c..d26ac3aa9 100644
--- a/src/gtk/utilities.c
+++ b/src/gtk/utilities.c
@@ -55,6 +55,7 @@
#include "main/sword.h"
#include "main/url.hh"
#include "main/xml.h"
+#include "gui/bookmarks_treeview.h"
#ifdef WIN32
#undef DATADIR
@@ -153,32 +154,34 @@ void utilities_parse_treeview(xmlNodePtr parent, GtkTreeIter *tree_parent,
gchar *module = NULL;
gchar *mod_desc = NULL;
gchar *description = NULL;
-
+ gchar *color = NULL;
gtk_tree_model_iter_children(GTK_TREE_MODEL(model), &child,
tree_parent);
-
do {
gtk_tree_model_get(GTK_TREE_MODEL(model), &child,
- 2, &caption,
- 3, &key,
- 4, &module,
- 5, &mod_desc, 6, &description, -1);
+ COL_CAPTION, &caption,
+ COL_KEY, &key,
+ COL_MODULE, &module,
+ COL_MODULE_DESC, &mod_desc,
+ COL_DESCRIPTION, &description,
+ COL_COLOR, &color,
+ -1);
if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(model),
&child)) {
- cur_node = xml_add_folder_to_parent(parent,
- caption);
+ cur_node = xml_add_folder_to_parent_colored(parent,
+ caption,
+ color);
utilities_parse_treeview(cur_node, &child, model);
-
} else
xml_add_bookmark_to_parent(parent,
description,
key, module, mod_desc);
-
g_free(caption);
g_free(key);
g_free(module);
g_free(mod_desc);
g_free(description);
+ g_free(color);
} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &child));
}
diff --git a/src/gui/bookmarks_menu.h b/src/gui/bookmarks_menu.h
index 0a62b7969..41d1109ce 100644
--- a/src/gui/bookmarks_menu.h
+++ b/src/gui/bookmarks_menu.h
@@ -41,6 +41,7 @@ struct _bookmark_menu
GtkWidget *bibletime;
GtkWidget *rr_submenu;
GtkWidget *remove;
+ GtkWidget *set_color;
};
typedef struct _bookmark_menu BOOKMARK_MENU;
extern BOOKMARK_MENU menu;
@@ -75,6 +76,9 @@ void on_insert_bookmark_activate(GtkMenuItem *menuitem,
gpointer user_data);
void on_new_folder_activate(GtkMenuItem *menuitem,
gpointer user_data);
+#if GTK_CHECK_VERSION(3, 4, 0)
+void on_set_tag_color_activate(GtkMenuItem *menuitem, gpointer user_data);
+#endif
void on_open_in_tab_activate(GtkMenuItem *menuitem,
gpointer user_data);
diff --git a/src/gui/bookmarks_treeview.h b/src/gui/bookmarks_treeview.h
index f7be52e60..7464737b7 100644
--- a/src/gui/bookmarks_treeview.h
+++ b/src/gui/bookmarks_treeview.h
@@ -27,6 +27,7 @@ extern "C" {
#endif
#include
#include
+
enum {
COL_OPEN_PIXBUF,
COL_CLOSED_PIXBUF,
@@ -35,6 +36,8 @@ enum {
COL_MODULE,
COL_MODULE_DESC,
COL_DESCRIPTION,
+ COL_COLOR, /* hex color string for tag groups, e.g. "#378ADD"
+ * NULL or "" means no color assigned (plain bookmark folder) */
N_COLUMNS
};
@@ -53,6 +56,7 @@ struct _bookmark_data
gchar *module;
gchar *module_desc;
gchar *description;
+ gchar *color; /* hex color string for folders/tag groups, NULL for leaves */
gboolean is_leaf;
GdkPixbuf *opened;
GdkPixbuf *closed;
@@ -69,6 +73,8 @@ void gui_add_item_to_tree(GtkTreeIter *iter, GtkTreeIter *parent,
void gui_verselist_to_bookmarks(GList *verses,
gint save_as_single);
GtkWidget *gui_create_bookmark_tree(void);
+void bookmark_debug_dump_colors(void);
+gchar *bookmark_get_tag_color_for_key(const gchar *osiskey);
void gui_parse_bookmarks(GtkTreeView *tree, const xmlChar *file,
GtkTreeIter *parent);
GtkWidget *gui_create_dialog_add_bookmark(gchar *label,
diff --git a/src/main/main.c b/src/main/main.c
index ef18f02b1..155ac597c 100644
--- a/src/main/main.c
+++ b/src/main/main.c
@@ -251,6 +251,28 @@ int main(int argc, char *argv[])
*/
settings_init(argc, argv, newconfigs, newbookmarks);
+ /* Phase 2: backup bookmarks.xml once on first run after upgrade.
+ * The backup is skipped if it already exists. */
+ {
+ gchar *bm = g_strdup_printf("%s/bookmarks/bookmarks.xml",
+ settings.gSwordDir);
+ gchar *bmbak = g_strdup_printf("%s/bookmarks/bookmarks.xml.bak",
+ settings.gSwordDir);
+ if (g_file_test(bm, G_FILE_TEST_EXISTS) &&
+ !g_file_test(bmbak, G_FILE_TEST_EXISTS)) {
+ GError *err = NULL;
+ GFile *src_f = g_file_new_for_path(bm);
+ GFile *dst_f = g_file_new_for_path(bmbak);
+ g_file_copy(src_f, dst_f,
+ G_FILE_COPY_NONE, NULL, NULL, NULL, &err);
+ if (err) g_error_free(err);
+ g_object_unref(src_f);
+ g_object_unref(dst_f);
+ }
+ g_free(bm);
+ g_free(bmbak);
+ }
+
gui_init(argc, argv);
gui_splash_init();
diff --git a/src/main/settings.c b/src/main/settings.c
index 1c9b9c3ca..e5f099b6a 100644
--- a/src/main/settings.c
+++ b/src/main/settings.c
@@ -182,6 +182,7 @@ int settings_init(int argc, char **argv, int new_configs,
g_free(sword_dir);
/* moved here from crud locations in backend. */
+
main_init_language_map();
language_init();
diff --git a/src/main/xml.c b/src/main/xml.c
index 4bd694779..1587d0d1b 100644
--- a/src/main/xml.c
+++ b/src/main/xml.c
@@ -24,6 +24,7 @@
#endif
#include
+#include
#include
#include
#include
@@ -141,6 +142,62 @@ xmlNodePtr xml_add_folder_to_parent(xmlNodePtr parent, gchar *caption)
return cur_node;
}
+/******************************************************************************
+ * Name
+ * xml_add_folder_to_parent_colored
+ *
+ * Synopsis
+ * #include "main/xml.h"
+ *
+ * xmlNodePtr xml_add_folder_to_parent_colored(xmlNodePtr parent,
+ * gchar *caption,
+ * const gchar *color)
+ *
+ * Description
+ * Like xml_add_folder_to_parent() but also writes the optional "color"
+ * attribute (e.g. "#378ADD") used by tag groups. Pass NULL or "" to
+ * omit the attribute (behaves identically to the plain variant).
+ *
+ * Return value
+ * xmlNodePtr - the new Folder node
+ */
+
+xmlNodePtr xml_add_folder_to_parent_colored(xmlNodePtr parent,
+ gchar *caption,
+ const gchar *color)
+{
+ xmlNodePtr cur_node = xml_add_folder_to_parent(parent, caption);
+
+ if (color && *color)
+ (void)xmlNewProp(cur_node,
+ (const xmlChar *)"color",
+ (const xmlChar *)color);
+ return cur_node;
+}
+
+/******************************************************************************
+ * Name
+ * xml_get_folder_color
+ *
+ * Synopsis
+ * #include "main/xml.h"
+ *
+ * gchar *xml_get_folder_color(xmlNodePtr node)
+ *
+ * Description
+ * Returns the "color" attribute of a Folder node, or NULL when absent.
+ * The caller must free the returned string with xmlFree() (it is an
+ * xmlChar * cast to gchar *).
+ *
+ * Return value
+ * gchar * (xmlChar * underneath) — caller must xmlFree(), or NULL
+ */
+
+gchar *xml_get_folder_color(xmlNodePtr node)
+{
+ return (gchar *)xmlGetProp(node, (const xmlChar *)"color");
+}
+
/******************************************************************************
* Name
* xml_add_bookmark_to_parent
diff --git a/src/main/xml.h b/src/main/xml.h
index eb3d84ff4..143cb6778 100644
--- a/src/main/xml.h
+++ b/src/main/xml.h
@@ -32,6 +32,10 @@ void xml_add_new_section_to_settings_doc(char *section);
void xml_new_bookmark_file(void);
xmlNodePtr xml_add_folder_to_parent(xmlNodePtr parent,
gchar *caption);
+xmlNodePtr xml_add_folder_to_parent_colored(xmlNodePtr parent,
+ gchar *caption,
+ const gchar *color);
+gchar *xml_get_folder_color(xmlNodePtr node);
void xml_add_bookmark_to_parent(xmlNodePtr parent, gchar *caption,
gchar *key, gchar *module,
const gchar *mod_desc);
diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt
index 0079ef804..045a106a4 100644
--- a/ui/CMakeLists.txt
+++ b/ui/CMakeLists.txt
@@ -21,6 +21,7 @@ if (NOT GTK2)
# Gtk3 only stuff
install (FILES
bookmarks.gtkbuilder
+ folder.gtkbuilder
editor_link_dialog.gtkbuilder
export-dialog.gtkbuilder
markverse.gtkbuilder
@@ -40,6 +41,7 @@ else (NOT GTK2)
# Gtk2 only stuff
install (FILES
bookmarks.glade
+ folder.glade
editor_link_dialog.glade
export-dialog.glade
markverse.glade
diff --git a/ui/folder.glade b/ui/folder.glade
new file mode 100644
index 000000000..ae93b6b81
--- /dev/null
+++ b/ui/folder.glade
@@ -0,0 +1,120 @@
+
+
+
+ False
+ Folder
+ 320
+ 150
+ GDK_WINDOW_TYPE_HINT_DIALOG
+
+
+ True
+ 8
+ 10
+
+
+
+
+ True
+ GTK_BUTTONBOX_END
+
+
+ True
+ True
+ gtk-cancel
+ True
+
+
+
+
+ True
+ True
+ gtk-ok
+ True
+
+
+
+
+ False
+ GTK_PACK_END
+
+
+
+
+
+
+ True
+ 8
+
+
+ True
+ Name:
+ 0
+
+
+ False
+ False
+
+
+
+
+ True
+ True
+
+
+ True
+ True
+
+
+
+
+
+
+
+
+ True
+ 8
+
+
+ True
+ Tag color:
+ 0
+
+
+ False
+ False
+
+
+
+
+ True
+ True
+ False
+
+
+ False
+ False
+
+
+
+
+ True
+ True
+ No color
+
+
+ False
+ False
+
+
+
+
+ False
+ True
+
+
+
+
+
+
+
diff --git a/ui/folder.gtkbuilder b/ui/folder.gtkbuilder
new file mode 100644
index 000000000..15ab50b7e
--- /dev/null
+++ b/ui/folder.gtkbuilder
@@ -0,0 +1,152 @@
+
+
+
+
+