From 2eabf48a3eaf5b1e639c2a145feb1374bdea5e6e Mon Sep 17 00:00:00 2001 From: lafricain79 Date: Fri, 20 Mar 2026 11:22:51 +0100 Subject: [PATCH 1/2] feat: convert bookmarks to tags with color highlighting - Add COL_COLOR column to bookmark tree store (bookmarks_treeview.h/c) - Add color attribute to XML folder nodes (xml.c/h) - New folder dialog with color picker (ui/folder.gtkbuilder, ui/folder.glade) - Tag color saved and restored from bookmarks.xml - Verse highlighting in Bible text using tag color (display.cc) - Text color auto-adjusted for dark backgrounds (luminance) - Multi-reference support: semicolons, commas, verse ranges - Popup menu for multi-reference bookmarks navigation - Drag and drop reordering enabled by default - Cairo color dot in treeview for tagged folders - Current verse green highlight disabled when tag color present - Fix bookmark folder labels localization: gettext now initialized before default bookmarks are created, so folder names (Personal, What must I do to be saved?, etc.) are properly translated on first run Closes #968 Closes #685 --- po/POTFILES.in | 1 + po/fr.po | 318 +++++++++++++++------------ po/xiphos.pot | 298 +++++++++++++------------ src/gtk/bookmark_dialog.c | 239 ++++++++++++-------- src/gtk/bookmarks_menu.c | 413 ++++++++++++++++++++++++----------- src/gtk/bookmarks_treeview.c | 366 ++++++++++++++++++++++++++++--- src/gtk/utilities.c | 23 +- src/gui/bookmarks_menu.h | 4 + src/gui/bookmarks_treeview.h | 6 + src/main/display.cc | 174 ++++++++++++++- src/main/main.c | 22 ++ src/main/settings.c | 3 +- src/main/xml.c | 57 +++++ src/main/xml.h | 4 + ui/CMakeLists.txt | 2 + ui/folder.glade | 120 ++++++++++ ui/folder.gtkbuilder | 152 +++++++++++++ 17 files changed, 1654 insertions(+), 548 deletions(-) create mode 100644 ui/folder.glade create mode 100644 ui/folder.gtkbuilder diff --git a/po/POTFILES.in b/po/POTFILES.in index c7940a3ed..978073f61 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -93,6 +93,7 @@ src/xiphos_html/xiphos_html.c ## Glade files ui/bookmarks.glade [type: gettext/glade]ui/bookmarks.gtkbuilder +[type: gettext/glade]ui/folder.gtkbuilder [type: gettext/glade]ui/editor_link_dialog.gtkbuilder ui/editor_note.xml ui/editor_studypad.xml diff --git a/po/fr.po b/po/fr.po index 785e69e77..47a6f7c4f 100644 --- a/po/fr.po +++ b/po/fr.po @@ -26,8 +26,8 @@ msgid "" msgstr "" "Project-Id-Version: Xiphos 4.0.7\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-03-21 22:16+0100\n" -"PO-Revision-Date: 2026-03-21 22:20+0100\n" +"POT-Creation-Date: 2026-04-05 16:37+0200\n" +"PO-Revision-Date: 2026-04-05 16:43+0200\n" "Last-Translator: Cyrille \n" "Language-Team: none\n" "Language: fr\n" @@ -252,13 +252,13 @@ msgid "Daily Devotion" msgstr "Lectures du jour" #: ../src/gtk/about_modules.c:458 ../src/gtk/mod_mgr.c:1616 -#: ../src/gtk/utilities.c:609 ../src/main/sidebar.cc:1085 +#: ../src/gtk/utilities.c:612 ../src/main/sidebar.cc:1085 #: ../src/main/sidebar.cc:1087 msgid "Maps" msgstr "Cartes" #: ../src/gtk/about_modules.c:460 ../src/gtk/mod_mgr.c:1622 -#: ../src/gtk/utilities.c:615 ../src/main/sidebar.cc:1096 +#: ../src/gtk/utilities.c:618 ../src/main/sidebar.cc:1096 #: ../src/main/sidebar.cc:1098 msgid "Images" msgstr "Images" @@ -510,80 +510,86 @@ msgstr "" "Tous nos remerciements vont à Troy Griffitts et toutes\n" "les autres personnes qui font vivre le Projet SWORD." -#. info->stock_icon = GTK_STOCK_OPEN; -#: ../src/gtk/bookmark_dialog.c:163 ../src/gtk/bookmarks_menu.c:361 -#: ../src/gtk/bookmarks_menu.c:653 ../src/gtk/bookmarks_menu.c:753 -#: ../src/gtk/bookmarks_treeview.c:138 ../ui/editor_studypad.xml.h:15 -#: ../ui/xi-menus.glade.h:60 ../ui/xi-menus-popup.gtkbuilder.h:37 -msgid "Bookmark" -msgstr "Signet" +#: ../src/gtk/bookmark_dialog.c:186 +msgid "New Tag" +msgstr "Nouvelle étiquette :" -#: ../src/gtk/bookmark_dialog.c:165 ../src/gtk/bookmarks_menu.c:755 -#: ../src/gtk/bookmarks_treeview.c:139 -msgid "Enter Folder Name" -msgstr "Saisir le nom du dossier" - -#: ../src/gtk/bookmark_dialog.c:167 ../src/gtk/bookmarks_menu.c:757 -msgid "Folder Name" -msgstr "Nom du dossier" - -#: ../src/gtk/bookmark_dialog.c:168 ../src/gtk/bookmarks_menu.c:758 -#: ../src/gtk/bookmarks_treeview.c:141 -msgid "Folder: " -msgstr "Dossier : " - -#: ../src/gtk/bookmarks_menu.c:200 +#: ../src/gtk/bookmarks_menu.c:208 msgid "Specify bookmarks file" msgstr "Spécifie le fichier des signets" -#: ../src/gtk/bookmarks_menu.c:350 ../ui/xi-menus.glade.h:65 +#: ../src/gtk/bookmarks_menu.c:389 +msgid "Edit Tag" +msgstr "Éditer l’étiquette" + +#: ../src/gtk/bookmarks_menu.c:449 ../ui/xi-menus.glade.h:65 #: ../ui/xi-menus-popup.gtkbuilder.h:29 msgid "Edit" msgstr "Édition" -#: ../src/gtk/bookmarks_menu.c:364 -msgid "Folder name: " -msgstr "Nom du dossier : " +#: ../src/gtk/bookmarks_menu.c:450 ../src/gtk/bookmarks_menu.c:717 +#: ../src/gtk/bookmarks_treeview.c:139 ../ui/editor_studypad.xml.h:15 +#: ../ui/xi-menus.glade.h:60 ../ui/xi-menus-popup.gtkbuilder.h:37 +msgid "Bookmark" +msgstr "Signet" -#: ../src/gtk/bookmarks_menu.c:367 +#: ../src/gtk/bookmarks_menu.c:452 msgid "Bookmark name: " msgstr "Nom du signet : " -#: ../src/gtk/bookmarks_menu.c:370 ../src/gtk/bookmarks_menu.c:661 +#: ../src/gtk/bookmarks_menu.c:456 ../src/gtk/bookmarks_menu.c:725 msgid "Verse: " msgstr "Verset : " -#: ../src/gtk/bookmarks_menu.c:371 ../src/gtk/bookmarks_menu.c:662 +#: ../src/gtk/bookmarks_menu.c:457 ../src/gtk/bookmarks_menu.c:726 msgid "Module: " msgstr "Module : " -#: ../src/gtk/bookmarks_menu.c:493 +#: ../src/gtk/bookmarks_menu.c:557 msgid "Remove the selected folder" msgstr "Supprimer le dossier sélectionné" -#: ../src/gtk/bookmarks_menu.c:494 +#: ../src/gtk/bookmarks_menu.c:558 msgid "(and all its contents)?" msgstr "(et tout son contenu) ?" -#: ../src/gtk/bookmarks_menu.c:498 +#: ../src/gtk/bookmarks_menu.c:562 msgid "Remove the selected bookmark" msgstr "Supprimer le signet sélectionné" -#: ../src/gtk/bookmarks_menu.c:654 +#: ../src/gtk/bookmarks_menu.c:718 msgid "Add" msgstr "Ajouter" -#: ../src/gtk/bookmarks_menu.c:660 +#: ../src/gtk/bookmarks_menu.c:724 msgid "Label: " msgstr "Étiquette : " -#: ../src/gtk/bookmarks_treeview.c:102 ../src/gtk/export_bookmarks.c:172 +#: ../src/gtk/bookmarks_menu.c:841 ../ui/bookmarks.glade.h:4 +#: ../ui/bookmarks.gtkbuilder.h:4 ../ui/xi-menus.glade.h:122 +#: ../ui/xi-menus-popup.gtkbuilder.h:5 +msgid "New Folder" +msgstr "Nouveau dossier" + +#: ../src/gtk/bookmarks_menu.c:966 +msgid "Choose folder color" +msgstr "Choisir une couleur de dossier" + +#: ../src/gtk/bookmarks_treeview.c:103 ../src/gtk/export_bookmarks.c:172 #: ../src/gtk/export_bookmarks.c:229 #, c-format msgid "Search result: %s" msgstr "Résultat de la requête : %s" -#: ../src/gtk/bookmarks_treeview.c:747 +#: ../src/gtk/bookmarks_treeview.c:140 +msgid "Enter Folder Name" +msgstr "Saisir le nom du dossier" + +#: ../src/gtk/bookmarks_treeview.c:142 +msgid "Folder: " +msgstr "Dossier : " + +#: ../src/gtk/bookmarks_treeview.c:876 msgid "" "Opening a multi-reference bookmark in\n" "separate tabs is not supported." @@ -591,8 +597,8 @@ msgstr "" "L’ouverture d’un signet avec de multiples références dans\n" "un onglet séparé n’est pas supporté." -#: ../src/gtk/bookmarks_treeview.c:850 ../src/gtk/sidebar.c:1561 -#: ../src/gtk/sidebar.c:1613 +#: ../src/gtk/bookmarks_treeview.c:1157 ../src/gtk/sidebar.c:1540 +#: ../src/gtk/sidebar.c:1592 msgid "Bookmarks" msgstr "Signets" @@ -651,7 +657,7 @@ msgid "Click to choose a date" msgstr "Cliquer pour choisir une date" #: ../src/gtk/export_bookmarks.c:175 ../src/gtk/export_bookmarks.c:232 -#: ../src/gtk/sidebar.c:1583 ../src/gtk/sidebar.c:1631 +#: ../src/gtk/sidebar.c:1562 ../src/gtk/sidebar.c:1610 #, c-format msgid "Verse List" msgstr "_Versets" @@ -790,8 +796,8 @@ msgstr "Xiphos - Logiciel d’étude biblique" msgid "Open a new tab" msgstr "Ouvre un nouvel onglet" -#: ../src/gtk/main_window.c:1164 ../src/gtk/sidebar.c:581 -#: ../src/gtk/sidebar.c:617 ../src/main/sidebar.cc:655 +#: ../src/gtk/main_window.c:1164 ../src/gtk/sidebar.c:579 +#: ../src/gtk/sidebar.c:596 ../src/main/sidebar.cc:655 #: ../src/main/sidebar.cc:1020 ../src/main/sidebar.cc:1021 #: ../src/main/sidebar.cc:1022 msgid "Standard View" @@ -1174,9 +1180,9 @@ msgstr "Terminé" #: ../src/gtk/mod_mgr.c:1190 ../src/gtk/mod_mgr.c:1192 #: ../src/gtk/mod_mgr.c:1302 ../src/gtk/mod_mgr.c:1335 -#: ../src/gtk/mod_mgr.c:1337 ../src/gtk/utilities.c:429 -#: ../src/gtk/utilities.c:492 ../src/gtk/utilities.c:494 -#: ../src/gtk/utilities.c:985 ../src/main/sidebar.cc:807 +#: ../src/gtk/mod_mgr.c:1337 ../src/gtk/utilities.c:432 +#: ../src/gtk/utilities.c:495 ../src/gtk/utilities.c:497 +#: ../src/gtk/utilities.c:988 ../src/main/sidebar.cc:807 #: ../src/main/sidebar.cc:878 ../src/main/sidebar.cc:880 msgid "Unknown" msgstr "Inconnu" @@ -1198,39 +1204,39 @@ msgstr "" "Modules classés\n" "par type" -#: ../src/gtk/mod_mgr.c:1580 ../src/gtk/utilities.c:571 +#: ../src/gtk/mod_mgr.c:1580 ../src/gtk/utilities.c:574 #: ../src/main/sidebar.cc:1004 ../src/main/sidebar.cc:1006 msgid "Biblical Texts" msgstr "Textes bibliques" -#: ../src/gtk/mod_mgr.c:1586 ../src/gtk/utilities.c:577 +#: ../src/gtk/mod_mgr.c:1586 ../src/gtk/utilities.c:580 #: ../src/main/sidebar.cc:666 ../src/main/sidebar.cc:1030 #: ../src/main/sidebar.cc:1032 msgid "Commentaries" msgstr "Commentaires" -#: ../src/gtk/mod_mgr.c:1592 ../src/gtk/utilities.c:584 +#: ../src/gtk/mod_mgr.c:1592 ../src/gtk/utilities.c:587 #: ../src/main/sidebar.cc:1041 ../src/main/sidebar.cc:1043 msgid "Dictionaries" msgstr "Dictionnaires" -#: ../src/gtk/mod_mgr.c:1598 ../src/gtk/utilities.c:590 +#: ../src/gtk/mod_mgr.c:1598 ../src/gtk/utilities.c:593 #: ../src/main/sidebar.cc:1052 ../src/main/sidebar.cc:1054 msgid "Glossaries" msgstr "Glossaires" -#: ../src/gtk/mod_mgr.c:1604 ../src/gtk/utilities.c:597 +#: ../src/gtk/mod_mgr.c:1604 ../src/gtk/utilities.c:600 #: ../src/main/sidebar.cc:1063 ../src/main/sidebar.cc:1065 msgid "Daily Devotionals" msgstr "Calendriers bibliques" -#: ../src/gtk/mod_mgr.c:1610 ../src/gtk/utilities.c:603 +#: ../src/gtk/mod_mgr.c:1610 ../src/gtk/utilities.c:606 #: ../src/main/sidebar.cc:674 ../src/main/sidebar.cc:1074 #: ../src/main/sidebar.cc:1076 msgid "General Books" msgstr "Livres généraux" -#: ../src/gtk/mod_mgr.c:1628 ../src/gtk/utilities.c:621 +#: ../src/gtk/mod_mgr.c:1628 ../src/gtk/utilities.c:624 #: ../src/main/sidebar.cc:1107 ../src/main/sidebar.cc:1109 msgid "Cult/Unorthodox" msgstr "Hétérodoxie" @@ -1251,8 +1257,8 @@ msgstr "Mise à jour disponible" msgid "Uninstalled" msgstr "Non installé" -#: ../src/gtk/mod_mgr.c:1653 ../src/gtk/sidebar.c:628 -#: ../src/gtk/utilities.c:629 ../src/main/prayerlists.cc:233 +#: ../src/gtk/mod_mgr.c:1653 ../src/gtk/sidebar.c:607 +#: ../src/gtk/utilities.c:632 ../src/main/prayerlists.cc:233 #: ../src/main/sidebar.cc:1118 ../src/main/sidebar.cc:1120 msgid "Prayer List/Journal" msgstr "Liste de prières / Journal" @@ -1315,7 +1321,7 @@ msgid "Choose" msgstr "Sélection" #: ../src/gtk/mod_mgr.c:2025 ../src/gtk/preferences_dialog.c:2254 -#: ../src/gtk/sidebar.c:1593 ../src/gtk/sidebar.c:1639 +#: ../src/gtk/sidebar.c:1572 ../src/gtk/sidebar.c:1618 msgid "Modules" msgstr "Modules" @@ -1458,11 +1464,10 @@ msgstr "Détacher/Attacher" msgid "Module Options" msgstr "Options du module" -#: ../src/gtk/parallel_view.c:216 ../src/gtk/sidebar.c:581 -#: ../src/gtk/sidebar.c:586 ../src/gtk/sidebar.c:617 -#: ../src/gtk/tabbed_browser.c:240 ../src/main/sidebar.cc:638 -#: ../src/main/sidebar.cc:1012 ../src/main/sidebar.cc:1013 -#: ../src/main/sidebar.cc:1014 +#: ../src/gtk/parallel_view.c:216 ../src/gtk/sidebar.c:579 +#: ../src/gtk/sidebar.c:596 ../src/gtk/tabbed_browser.c:239 +#: ../src/main/sidebar.cc:638 ../src/main/sidebar.cc:1012 +#: ../src/main/sidebar.cc:1013 ../src/main/sidebar.cc:1014 msgid "Parallel View" msgstr "Affichage parallèle" @@ -1496,7 +1501,7 @@ msgstr "BibleSync : %s (mot de passe \"%s\")." msgid "Disabled" msgstr "Désactivé" -#: ../src/gtk/preferences_dialog.c:1005 ../src/main/xml.c:96 +#: ../src/gtk/preferences_dialog.c:1005 ../src/main/xml.c:97 msgid "Personal" msgstr "Notes personnelles" @@ -1736,7 +1741,7 @@ msgstr "" msgid "Module is neither Bible nor commentary" msgstr "Le module n’est ni une Bible ni un commentaire" -#: ../src/gtk/search_dialog.c:1212 ../src/gtk/sidebar.c:980 +#: ../src/gtk/search_dialog.c:1212 ../src/gtk/sidebar.c:959 msgid "BibleSync is not active for transmit." msgstr "BibleSync n’est pas actif pour émission." @@ -1816,31 +1821,23 @@ msgstr "Basse" msgid "Upper" msgstr "Haute" -#: ../src/gtk/sidebar.c:588 -msgid "Open in Detached Window" -msgstr "Ouvrir dans une fenêtre séparée" - -#: ../src/gtk/sidebar.c:589 -msgid "Open in New Tab" -msgstr "Ouvrir dans un nouvel onglet" - -#: ../src/gtk/sidebar.c:840 +#: ../src/gtk/sidebar.c:819 msgid "Open module in editor?" msgstr "Ouvrir le module dans l’éditeur ?" -#: ../src/gtk/sidebar.c:841 +#: ../src/gtk/sidebar.c:820 msgid "If no, it will open for display only." msgstr "Si la réponse est négative, il sera ouvert en lecture seulement." -#: ../src/gtk/sidebar.c:941 +#: ../src/gtk/sidebar.c:920 msgid "Paste verse references" msgstr "Coller les références de verset" -#: ../src/gtk/sidebar.c:943 +#: ../src/gtk/sidebar.c:922 msgid "List:" msgstr "Liste :" -#: ../src/gtk/sidebar.c:1572 ../src/gtk/sidebar.c:1622 +#: ../src/gtk/sidebar.c:1551 ../src/gtk/sidebar.c:1601 msgid "Search" msgstr "Rechercher" @@ -1852,7 +1849,7 @@ msgstr "Barre latérale" msgid "Powered by the SWORD Project" msgstr "Xiphos utilise les logiciels du Projet SWORD" -#: ../src/gtk/tabbed_browser.c:347 +#: ../src/gtk/tabbed_browser.c:346 msgid "Can't create tabs dir." msgstr "Impossible de créer le répertoire tabs." @@ -1870,55 +1867,55 @@ msgstr "Nouveau nom" msgid "Remove the selected item" msgstr "Supprimer l’élément sélectionné" -#: ../src/gtk/utilities.c:80 +#: ../src/gtk/utilities.c:81 msgid "January" msgstr "Janvier" -#: ../src/gtk/utilities.c:81 +#: ../src/gtk/utilities.c:82 msgid "February" msgstr "Février" -#: ../src/gtk/utilities.c:82 +#: ../src/gtk/utilities.c:83 msgid "March" msgstr "Mars" -#: ../src/gtk/utilities.c:83 +#: ../src/gtk/utilities.c:84 msgid "April" msgstr "Avril" -#: ../src/gtk/utilities.c:84 +#: ../src/gtk/utilities.c:85 msgid "May" msgstr "Mai" -#: ../src/gtk/utilities.c:85 +#: ../src/gtk/utilities.c:86 msgid "June" msgstr "Juin" -#: ../src/gtk/utilities.c:86 +#: ../src/gtk/utilities.c:87 msgid "July" msgstr "Juillet" -#: ../src/gtk/utilities.c:87 +#: ../src/gtk/utilities.c:88 msgid "August" msgstr "Août" -#: ../src/gtk/utilities.c:88 +#: ../src/gtk/utilities.c:89 msgid "September" msgstr "Septembre" -#: ../src/gtk/utilities.c:89 +#: ../src/gtk/utilities.c:90 msgid "October" msgstr "Octobre" -#: ../src/gtk/utilities.c:90 +#: ../src/gtk/utilities.c:91 msgid "November" msgstr "Novembre" -#: ../src/gtk/utilities.c:91 +#: ../src/gtk/utilities.c:92 msgid "December" msgstr "Décembre" -#: ../src/gtk/utilities.c:2021 +#: ../src/gtk/utilities.c:2024 msgid "" "An image file's size could not be determined.\n" "Xiphos cannot resize images to fit window." @@ -2046,7 +2043,7 @@ msgstr "" "

Ce module n’a aucun contenu à cette référence." -#: ../src/main/display.cc:266 +#: ../src/main/display.cc:260 #, c-format msgid "" "Improperly encoded personal annotation label:\n" @@ -2055,8 +2052,8 @@ msgstr "" "Ce nom d’annotation personnelle est improprement formé :\n" "’%s’" -#: ../src/main/display.cc:1286 ../src/main/display.cc:1343 -#: ../src/main/display.cc:1384 ../ui/export-dialog.glade.h:14 +#: ../src/main/display.cc:1215 ../src/main/display.cc:1272 +#: ../src/main/display.cc:1313 ../ui/export-dialog.glade.h:14 #: ../ui/export-dialog.gtkbuilder.h:14 msgid "Chapter" msgstr "Chapitre" @@ -2065,23 +2062,23 @@ msgstr "Chapitre" msgid "Xiphos does not understand more than one argument." msgstr "Xiphos n’accepte pas plus d’un argument." -#: ../src/main/main.c:258 +#: ../src/main/main.c:280 msgid "Initiating HTML" msgstr "Initialisation HTML" -#: ../src/main/main.c:263 +#: ../src/main/main.c:285 msgid "Building Interface" msgstr "Construction de l’interface" -#: ../src/main/main.c:266 +#: ../src/main/main.c:288 msgid "Starting Sword" msgstr "Démarrage de Sword" -#: ../src/main/main.c:269 +#: ../src/main/main.c:291 msgid "Loading Settings" msgstr "Chargement des préférences" -#: ../src/main/main.c:272 +#: ../src/main/main.c:294 msgid "Displaying Xiphos" msgstr "Affichage de Xiphos" @@ -2534,7 +2531,7 @@ msgstr "Impossible de créer le dossier " msgid "can not create " msgstr "impossible de créer " -#: ../src/main/settings.c:211 +#: ../src/main/settings.c:212 msgid "" "Empty settings file -- backup recovery attempted.\n" "Some information may have been lost." @@ -2542,12 +2539,12 @@ msgstr "" "Fichier de configuration vide -- Essai de restauration.\n" "Des informations ont pu être perdues." -#: ../src/main/settings.c:214 +#: ../src/main/settings.c:215 msgid "Empty settings file -- no backup?!? Information lost!" msgstr "" "Fichier de configuration vide -- pas de sauvegarde ?!? Informations perdues !" -#: ../src/main/settings.c:241 +#: ../src/main/settings.c:242 msgid "" "There are no Bibles installed.\n" "Evidently, you declined to install any.\n" @@ -2561,7 +2558,7 @@ msgstr "" "Xiphos ne peut fonctionner sans une Bible à présenter,\n" "et va maintenant se terminer." -#: ../src/main/settings.c:247 +#: ../src/main/settings.c:248 msgid "Bible module installation complete." msgstr "L’installation des modules est terminée." @@ -2706,67 +2703,67 @@ msgstr "Erreur d’aperçu :\n" msgid "Show %s in main window" msgstr "Affiche %s dans la fenêtre principale" -#: ../src/main/xml.c:87 ../src/main/xml.c:443 ../src/main/xml.c:574 +#: ../src/main/xml.c:88 ../src/main/xml.c:500 ../src/main/xml.c:631 #, c-format msgid "Document not created successfully. \n" msgstr "Erreur de création du document. \n" -#: ../src/main/xml.c:99 +#: ../src/main/xml.c:100 msgid "What must I do to be saved?" msgstr "Comment être sauvé ?" -#: ../src/main/xml.c:100 ../src/main/xml.c:101 +#: ../src/main/xml.c:101 ../src/main/xml.c:102 msgid "Acts 16:31" msgstr "Act 16:31" -#: ../src/main/xml.c:102 ../src/main/xml.c:103 +#: ../src/main/xml.c:103 ../src/main/xml.c:104 msgid "Eph 2:8,9" msgstr "Éph 2:8,9" -#: ../src/main/xml.c:104 ../src/main/xml.c:105 +#: ../src/main/xml.c:105 ../src/main/xml.c:106 msgid "Romans 1:16" msgstr "Romains 1:16" -#: ../src/main/xml.c:107 +#: ../src/main/xml.c:108 msgid "What is the Gospel?" msgstr "Qu’est-ce que l’Évangile ?" -#: ../src/main/xml.c:108 ../src/main/xml.c:109 +#: ../src/main/xml.c:109 ../src/main/xml.c:110 msgid "1 Cor 15:1-4" msgstr "1 Cor 15:1-4" -#: ../src/main/xml.c:238 ../src/main/xml.c:384 ../src/main/xml.c:1504 +#: ../src/main/xml.c:295 ../src/main/xml.c:441 ../src/main/xml.c:1561 #, c-format msgid "Document not parsed successfully. \n" msgstr "Erreur de lecture du document. \n" -#: ../src/main/xml.c:244 ../src/main/xml.c:390 ../src/main/xml.c:410 -#: ../src/main/xml.c:1061 ../src/main/xml.c:1391 ../src/main/xml.c:1587 +#: ../src/main/xml.c:301 ../src/main/xml.c:447 ../src/main/xml.c:467 +#: ../src/main/xml.c:1118 ../src/main/xml.c:1448 ../src/main/xml.c:1644 #, c-format msgid "empty document \n" msgstr "document vide \n" -#: ../src/main/xml.c:250 +#: ../src/main/xml.c:307 #, c-format msgid "wrong type, root node != SwordBookmarks\n" msgstr "erreur de type, root node != SwordBookmarks\n" -#: ../src/main/xml.c:396 +#: ../src/main/xml.c:453 #, c-format msgid "wrong type, root node != Copy_Export\n" msgstr "erreur de type, root node !=Copy_Export\n" -#: ../src/main/xml.c:473 +#: ../src/main/xml.c:530 #, c-format msgid "%s
%s
Chapter %d

" msgstr "%s
%s
Chapitre %d

" -#: ../src/main/xml.c:477 +#: ../src/main/xml.c:534 #, c-format msgid "
Chapter %d

" msgstr "
Chapitre %d

" -#: ../src/main/xml.c:480 +#: ../src/main/xml.c:537 #, c-format msgid "" "%s\n" @@ -2779,7 +2776,7 @@ msgstr "" "Chapitre %d\n" "\n" -#: ../src/main/xml.c:483 +#: ../src/main/xml.c:540 #, c-format msgid "" "\n" @@ -2792,12 +2789,12 @@ msgstr "" "Chapitre %d\n" "\n" -#: ../src/main/xml.c:492 +#: ../src/main/xml.c:549 #, c-format msgid "%s%s: %s Chapter %d

" msgstr "%s%s: %s Chapitre %d

" -#: ../src/main/xml.c:495 +#: ../src/main/xml.c:552 #, c-format msgid "" "%s: %s Chapter %d\n" @@ -2806,82 +2803,82 @@ msgstr "" "%s: %s Chapitre %d\n" "\n" -#: ../src/main/xml.c:503 +#: ../src/main/xml.c:560 #, c-format msgid "  [%d]" msgstr "  [%d]" -#: ../src/main/xml.c:506 +#: ../src/main/xml.c:563 #, c-format msgid " [%d]" msgstr " [%d]" -#: ../src/main/xml.c:515 +#: ../src/main/xml.c:572 #, c-format msgid "%s%s (%s %d:%d%s)" msgstr "%s%s (%s %d:%d%s)" -#: ../src/main/xml.c:518 +#: ../src/main/xml.c:575 #, c-format msgid "%s(%s %d:%d%s)%s " msgstr "%s(%s %d:%d%s)%s " -#: ../src/main/xml.c:520 +#: ../src/main/xml.c:577 #, c-format msgid "%s (%s %d:%d%s)" msgstr "%s (%s %d:%d%s)" -#: ../src/main/xml.c:523 +#: ../src/main/xml.c:580 #, c-format msgid "(%s %d:%d%s) %s" msgstr "(%s %d:%d%s) %s" -#: ../src/main/xml.c:531 +#: ../src/main/xml.c:588 #, c-format msgid " %s%s" msgstr " %s%s" -#: ../src/main/xml.c:533 ../src/main/xml.c:537 +#: ../src/main/xml.c:590 ../src/main/xml.c:594 #, c-format msgid "%s(%s %d:%d-%d%s)" msgstr "%s(%s %d:%d-%d%s)" -#: ../src/main/xml.c:535 +#: ../src/main/xml.c:592 #, c-format msgid "(%s %d:%d-%d%s)
" msgstr "(%s %d:%d-%d%s)
" -#: ../src/main/xml.c:540 +#: ../src/main/xml.c:597 #, c-format msgid "(%s %d:%d-%d%s)\n" msgstr "(%s %d:%d-%d%s)\n" -#: ../src/main/xml.c:589 +#: ../src/main/xml.c:646 msgid "Old Testament" msgstr "Ancien Testament" -#: ../src/main/xml.c:591 +#: ../src/main/xml.c:648 msgid "Gen - Mal" msgstr "Ge - Mal" -#: ../src/main/xml.c:597 +#: ../src/main/xml.c:654 msgid "New Testament" msgstr "Nouveau Testament" -#: ../src/main/xml.c:599 +#: ../src/main/xml.c:656 msgid "Mat - Rev" msgstr "Mt - Ap" -#: ../src/main/xml.c:608 +#: ../src/main/xml.c:665 msgid "Sample Module List" msgstr "Exemple de liste de modules" -#: ../src/main/xml.c:1067 ../src/main/xml.c:1397 +#: ../src/main/xml.c:1124 ../src/main/xml.c:1454 #, c-format msgid "wrong type, root node != %s\n" msgstr "erreur de type, root node != %s\n" -#: ../src/main/xml.c:1549 +#: ../src/main/xml.c:1606 #, c-format msgid "" "Save of settings failed! stat %d, size %d\n" @@ -2890,7 +2887,7 @@ msgstr "" "La sauvegarde de la configuration a échouée ! stat %d, taille %d\n" "%s" -#: ../src/main/xml.c:1551 +#: ../src/main/xml.c:1608 msgid "Attempting to revert to previous save." msgstr "Tentative de retour à la sauvegarde précédente." @@ -2906,11 +2903,6 @@ msgstr "Fenêtre de dialogue des signets" msgid "Create new folder" msgstr "Crée un nouveau dossier" -#: ../ui/bookmarks.glade.h:4 ../ui/bookmarks.gtkbuilder.h:4 -#: ../ui/xi-menus.glade.h:122 ../ui/xi-menus-popup.gtkbuilder.h:5 -msgid "New Folder" -msgstr "Nouveau dossier" - #: ../ui/bookmarks.glade.h:5 ../ui/bookmarks.gtkbuilder.h:3 msgid "New ..." msgstr "Nouveau..." @@ -2951,6 +2943,30 @@ msgstr "Entrée Module" msgid "Bookmark Folder Treeview" msgstr "Arborescence Dossier des signets" +#: ../ui/folder.gtkbuilder.h:1 +msgid "Folder" +msgstr "Dossier :" + +#: ../ui/folder.gtkbuilder.h:2 +msgid "Name:" +msgstr "Nom :" + +#: ../ui/folder.gtkbuilder.h:3 +msgid "Tag color:" +msgstr "Couleur d’étiquette :" + +#: ../ui/folder.gtkbuilder.h:4 +msgid "Choose a highlight color for this tag group" +msgstr "Choisir une couleur de surlignement pour ce groupe d’étiquette" + +#: ../ui/folder.gtkbuilder.h:5 +msgid "No color" +msgstr "Pas de couleur" + +#: ../ui/folder.gtkbuilder.h:6 +msgid "Remove tag color from this folder" +msgstr "Supprimer la couleur de l’étiquette de ce dossier" + # T'es sûr de la traduction? #: ../ui/editor_link_dialog.gtkbuilder.h:2 msgid "Test Link" @@ -5653,6 +5669,18 @@ msgstr "Supprimer cet élément :" msgid "Edit this item" msgstr "Éditer cet élément" +#~ msgid "Folder Name" +#~ msgstr "Nom du dossier" + +#~ msgid "Folder name: " +#~ msgstr "Nom du dossier : " + +#~ msgid "Open in Detached Window" +#~ msgstr "Ouvrir dans une fenêtre séparée" + +#~ msgid "Open in New Tab" +#~ msgstr "Ouvrir dans un nouvel onglet" + #, c-format #~ msgid "language: %s" #~ msgstr "la langue : %s" diff --git a/po/xiphos.pot b/po/xiphos.pot index db17ce954..3418d90d2 100644 --- a/po/xiphos.pot +++ b/po/xiphos.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-03-21 22:16+0100\n" +"POT-Creation-Date: 2026-04-05 16:37+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -225,13 +225,13 @@ msgid "Daily Devotion" msgstr "" #: ../src/gtk/about_modules.c:458 ../src/gtk/mod_mgr.c:1616 -#: ../src/gtk/utilities.c:609 ../src/main/sidebar.cc:1085 +#: ../src/gtk/utilities.c:612 ../src/main/sidebar.cc:1085 #: ../src/main/sidebar.cc:1087 msgid "Maps" msgstr "" #: ../src/gtk/about_modules.c:460 ../src/gtk/mod_mgr.c:1622 -#: ../src/gtk/utilities.c:615 ../src/main/sidebar.cc:1096 +#: ../src/gtk/utilities.c:618 ../src/main/sidebar.cc:1096 #: ../src/main/sidebar.cc:1098 msgid "Images" msgstr "" @@ -442,87 +442,93 @@ msgid "" "us The SWORD Project." msgstr "" -#. info->stock_icon = GTK_STOCK_OPEN; -#: ../src/gtk/bookmark_dialog.c:163 ../src/gtk/bookmarks_menu.c:361 -#: ../src/gtk/bookmarks_menu.c:653 ../src/gtk/bookmarks_menu.c:753 -#: ../src/gtk/bookmarks_treeview.c:138 ../ui/editor_studypad.xml.h:15 -#: ../ui/xi-menus.glade.h:60 ../ui/xi-menus-popup.gtkbuilder.h:37 -msgid "Bookmark" -msgstr "" - -#: ../src/gtk/bookmark_dialog.c:165 ../src/gtk/bookmarks_menu.c:755 -#: ../src/gtk/bookmarks_treeview.c:139 -msgid "Enter Folder Name" +#: ../src/gtk/bookmark_dialog.c:186 +msgid "New Tag" msgstr "" -#: ../src/gtk/bookmark_dialog.c:167 ../src/gtk/bookmarks_menu.c:757 -msgid "Folder Name" +#: ../src/gtk/bookmarks_menu.c:208 +msgid "Specify bookmarks file" msgstr "" -#: ../src/gtk/bookmark_dialog.c:168 ../src/gtk/bookmarks_menu.c:758 -#: ../src/gtk/bookmarks_treeview.c:141 -msgid "Folder: " +#: ../src/gtk/bookmarks_menu.c:389 +msgid "Edit Tag" msgstr "" -#: ../src/gtk/bookmarks_menu.c:200 -msgid "Specify bookmarks file" -msgstr "" - -#: ../src/gtk/bookmarks_menu.c:350 ../ui/xi-menus.glade.h:65 +#: ../src/gtk/bookmarks_menu.c:449 ../ui/xi-menus.glade.h:65 #: ../ui/xi-menus-popup.gtkbuilder.h:29 msgid "Edit" msgstr "" -#: ../src/gtk/bookmarks_menu.c:364 -msgid "Folder name: " +#: ../src/gtk/bookmarks_menu.c:450 ../src/gtk/bookmarks_menu.c:717 +#: ../src/gtk/bookmarks_treeview.c:139 ../ui/editor_studypad.xml.h:15 +#: ../ui/xi-menus.glade.h:60 ../ui/xi-menus-popup.gtkbuilder.h:37 +msgid "Bookmark" msgstr "" -#: ../src/gtk/bookmarks_menu.c:367 +#: ../src/gtk/bookmarks_menu.c:452 msgid "Bookmark name: " msgstr "" -#: ../src/gtk/bookmarks_menu.c:370 ../src/gtk/bookmarks_menu.c:661 +#: ../src/gtk/bookmarks_menu.c:456 ../src/gtk/bookmarks_menu.c:725 msgid "Verse: " msgstr "" -#: ../src/gtk/bookmarks_menu.c:371 ../src/gtk/bookmarks_menu.c:662 +#: ../src/gtk/bookmarks_menu.c:457 ../src/gtk/bookmarks_menu.c:726 msgid "Module: " msgstr "" -#: ../src/gtk/bookmarks_menu.c:493 +#: ../src/gtk/bookmarks_menu.c:557 msgid "Remove the selected folder" msgstr "" -#: ../src/gtk/bookmarks_menu.c:494 +#: ../src/gtk/bookmarks_menu.c:558 msgid "(and all its contents)?" msgstr "" -#: ../src/gtk/bookmarks_menu.c:498 +#: ../src/gtk/bookmarks_menu.c:562 msgid "Remove the selected bookmark" msgstr "" -#: ../src/gtk/bookmarks_menu.c:654 +#: ../src/gtk/bookmarks_menu.c:718 msgid "Add" msgstr "" -#: ../src/gtk/bookmarks_menu.c:660 +#: ../src/gtk/bookmarks_menu.c:724 msgid "Label: " msgstr "" -#: ../src/gtk/bookmarks_treeview.c:102 ../src/gtk/export_bookmarks.c:172 +#: ../src/gtk/bookmarks_menu.c:841 ../ui/bookmarks.glade.h:4 +#: ../ui/bookmarks.gtkbuilder.h:4 ../ui/xi-menus.glade.h:122 +#: ../ui/xi-menus-popup.gtkbuilder.h:5 +msgid "New Folder" +msgstr "" + +#: ../src/gtk/bookmarks_menu.c:966 +msgid "Choose folder color" +msgstr "" + +#: ../src/gtk/bookmarks_treeview.c:103 ../src/gtk/export_bookmarks.c:172 #: ../src/gtk/export_bookmarks.c:229 #, c-format msgid "Search result: %s" msgstr "" -#: ../src/gtk/bookmarks_treeview.c:747 +#: ../src/gtk/bookmarks_treeview.c:140 +msgid "Enter Folder Name" +msgstr "" + +#: ../src/gtk/bookmarks_treeview.c:142 +msgid "Folder: " +msgstr "" + +#: ../src/gtk/bookmarks_treeview.c:876 msgid "" "Opening a multi-reference bookmark in\n" "separate tabs is not supported." msgstr "" -#: ../src/gtk/bookmarks_treeview.c:850 ../src/gtk/sidebar.c:1561 -#: ../src/gtk/sidebar.c:1613 +#: ../src/gtk/bookmarks_treeview.c:1157 ../src/gtk/sidebar.c:1540 +#: ../src/gtk/sidebar.c:1592 msgid "Bookmarks" msgstr "" @@ -580,7 +586,7 @@ msgid "Click to choose a date" msgstr "" #: ../src/gtk/export_bookmarks.c:175 ../src/gtk/export_bookmarks.c:232 -#: ../src/gtk/sidebar.c:1583 ../src/gtk/sidebar.c:1631 +#: ../src/gtk/sidebar.c:1562 ../src/gtk/sidebar.c:1610 #, c-format msgid "Verse List" msgstr "" @@ -718,8 +724,8 @@ msgstr "" msgid "Open a new tab" msgstr "" -#: ../src/gtk/main_window.c:1164 ../src/gtk/sidebar.c:581 -#: ../src/gtk/sidebar.c:617 ../src/main/sidebar.cc:655 +#: ../src/gtk/main_window.c:1164 ../src/gtk/sidebar.c:579 +#: ../src/gtk/sidebar.c:596 ../src/main/sidebar.cc:655 #: ../src/main/sidebar.cc:1020 ../src/main/sidebar.cc:1021 #: ../src/main/sidebar.cc:1022 msgid "Standard View" @@ -1024,9 +1030,9 @@ msgstr "" #: ../src/gtk/mod_mgr.c:1190 ../src/gtk/mod_mgr.c:1192 #: ../src/gtk/mod_mgr.c:1302 ../src/gtk/mod_mgr.c:1335 -#: ../src/gtk/mod_mgr.c:1337 ../src/gtk/utilities.c:429 -#: ../src/gtk/utilities.c:492 ../src/gtk/utilities.c:494 -#: ../src/gtk/utilities.c:985 ../src/main/sidebar.cc:807 +#: ../src/gtk/mod_mgr.c:1337 ../src/gtk/utilities.c:432 +#: ../src/gtk/utilities.c:495 ../src/gtk/utilities.c:497 +#: ../src/gtk/utilities.c:988 ../src/main/sidebar.cc:807 #: ../src/main/sidebar.cc:878 ../src/main/sidebar.cc:880 msgid "Unknown" msgstr "" @@ -1044,39 +1050,39 @@ msgid "" "Module Type" msgstr "" -#: ../src/gtk/mod_mgr.c:1580 ../src/gtk/utilities.c:571 +#: ../src/gtk/mod_mgr.c:1580 ../src/gtk/utilities.c:574 #: ../src/main/sidebar.cc:1004 ../src/main/sidebar.cc:1006 msgid "Biblical Texts" msgstr "" -#: ../src/gtk/mod_mgr.c:1586 ../src/gtk/utilities.c:577 +#: ../src/gtk/mod_mgr.c:1586 ../src/gtk/utilities.c:580 #: ../src/main/sidebar.cc:666 ../src/main/sidebar.cc:1030 #: ../src/main/sidebar.cc:1032 msgid "Commentaries" msgstr "" -#: ../src/gtk/mod_mgr.c:1592 ../src/gtk/utilities.c:584 +#: ../src/gtk/mod_mgr.c:1592 ../src/gtk/utilities.c:587 #: ../src/main/sidebar.cc:1041 ../src/main/sidebar.cc:1043 msgid "Dictionaries" msgstr "" -#: ../src/gtk/mod_mgr.c:1598 ../src/gtk/utilities.c:590 +#: ../src/gtk/mod_mgr.c:1598 ../src/gtk/utilities.c:593 #: ../src/main/sidebar.cc:1052 ../src/main/sidebar.cc:1054 msgid "Glossaries" msgstr "" -#: ../src/gtk/mod_mgr.c:1604 ../src/gtk/utilities.c:597 +#: ../src/gtk/mod_mgr.c:1604 ../src/gtk/utilities.c:600 #: ../src/main/sidebar.cc:1063 ../src/main/sidebar.cc:1065 msgid "Daily Devotionals" msgstr "" -#: ../src/gtk/mod_mgr.c:1610 ../src/gtk/utilities.c:603 +#: ../src/gtk/mod_mgr.c:1610 ../src/gtk/utilities.c:606 #: ../src/main/sidebar.cc:674 ../src/main/sidebar.cc:1074 #: ../src/main/sidebar.cc:1076 msgid "General Books" msgstr "" -#: ../src/gtk/mod_mgr.c:1628 ../src/gtk/utilities.c:621 +#: ../src/gtk/mod_mgr.c:1628 ../src/gtk/utilities.c:624 #: ../src/main/sidebar.cc:1107 ../src/main/sidebar.cc:1109 msgid "Cult/Unorthodox" msgstr "" @@ -1095,8 +1101,8 @@ msgstr "" msgid "Uninstalled" msgstr "" -#: ../src/gtk/mod_mgr.c:1653 ../src/gtk/sidebar.c:628 -#: ../src/gtk/utilities.c:629 ../src/main/prayerlists.cc:233 +#: ../src/gtk/mod_mgr.c:1653 ../src/gtk/sidebar.c:607 +#: ../src/gtk/utilities.c:632 ../src/main/prayerlists.cc:233 #: ../src/main/sidebar.cc:1118 ../src/main/sidebar.cc:1120 msgid "Prayer List/Journal" msgstr "" @@ -1158,7 +1164,7 @@ msgid "Choose" msgstr "" #: ../src/gtk/mod_mgr.c:2025 ../src/gtk/preferences_dialog.c:2254 -#: ../src/gtk/sidebar.c:1593 ../src/gtk/sidebar.c:1639 +#: ../src/gtk/sidebar.c:1572 ../src/gtk/sidebar.c:1618 msgid "Modules" msgstr "" @@ -1291,11 +1297,10 @@ msgstr "" msgid "Module Options" msgstr "" -#: ../src/gtk/parallel_view.c:216 ../src/gtk/sidebar.c:581 -#: ../src/gtk/sidebar.c:586 ../src/gtk/sidebar.c:617 -#: ../src/gtk/tabbed_browser.c:240 ../src/main/sidebar.cc:638 -#: ../src/main/sidebar.cc:1012 ../src/main/sidebar.cc:1013 -#: ../src/main/sidebar.cc:1014 +#: ../src/gtk/parallel_view.c:216 ../src/gtk/sidebar.c:579 +#: ../src/gtk/sidebar.c:596 ../src/gtk/tabbed_browser.c:239 +#: ../src/main/sidebar.cc:638 ../src/main/sidebar.cc:1012 +#: ../src/main/sidebar.cc:1013 ../src/main/sidebar.cc:1014 msgid "Parallel View" msgstr "" @@ -1327,7 +1332,7 @@ msgstr "" msgid "Disabled" msgstr "" -#: ../src/gtk/preferences_dialog.c:1005 ../src/main/xml.c:96 +#: ../src/gtk/preferences_dialog.c:1005 ../src/main/xml.c:97 msgid "Personal" msgstr "" @@ -1518,7 +1523,7 @@ msgstr "" msgid "Module is neither Bible nor commentary" msgstr "" -#: ../src/gtk/search_dialog.c:1212 ../src/gtk/sidebar.c:980 +#: ../src/gtk/search_dialog.c:1212 ../src/gtk/sidebar.c:959 msgid "BibleSync is not active for transmit." msgstr "" @@ -1596,31 +1601,23 @@ msgstr "" msgid "Upper" msgstr "" -#: ../src/gtk/sidebar.c:588 -msgid "Open in Detached Window" -msgstr "" - -#: ../src/gtk/sidebar.c:589 -msgid "Open in New Tab" -msgstr "" - -#: ../src/gtk/sidebar.c:840 +#: ../src/gtk/sidebar.c:819 msgid "Open module in editor?" msgstr "" -#: ../src/gtk/sidebar.c:841 +#: ../src/gtk/sidebar.c:820 msgid "If no, it will open for display only." msgstr "" -#: ../src/gtk/sidebar.c:941 +#: ../src/gtk/sidebar.c:920 msgid "Paste verse references" msgstr "" -#: ../src/gtk/sidebar.c:943 +#: ../src/gtk/sidebar.c:922 msgid "List:" msgstr "" -#: ../src/gtk/sidebar.c:1572 ../src/gtk/sidebar.c:1622 +#: ../src/gtk/sidebar.c:1551 ../src/gtk/sidebar.c:1601 msgid "Search" msgstr "" @@ -1632,7 +1629,7 @@ msgstr "" msgid "Powered by the SWORD Project" msgstr "" -#: ../src/gtk/tabbed_browser.c:347 +#: ../src/gtk/tabbed_browser.c:346 msgid "Can't create tabs dir." msgstr "" @@ -1650,55 +1647,55 @@ msgstr "" msgid "Remove the selected item" msgstr "" -#: ../src/gtk/utilities.c:80 +#: ../src/gtk/utilities.c:81 msgid "January" msgstr "" -#: ../src/gtk/utilities.c:81 +#: ../src/gtk/utilities.c:82 msgid "February" msgstr "" -#: ../src/gtk/utilities.c:82 +#: ../src/gtk/utilities.c:83 msgid "March" msgstr "" -#: ../src/gtk/utilities.c:83 +#: ../src/gtk/utilities.c:84 msgid "April" msgstr "" -#: ../src/gtk/utilities.c:84 +#: ../src/gtk/utilities.c:85 msgid "May" msgstr "" -#: ../src/gtk/utilities.c:85 +#: ../src/gtk/utilities.c:86 msgid "June" msgstr "" -#: ../src/gtk/utilities.c:86 +#: ../src/gtk/utilities.c:87 msgid "July" msgstr "" -#: ../src/gtk/utilities.c:87 +#: ../src/gtk/utilities.c:88 msgid "August" msgstr "" -#: ../src/gtk/utilities.c:88 +#: ../src/gtk/utilities.c:89 msgid "September" msgstr "" -#: ../src/gtk/utilities.c:89 +#: ../src/gtk/utilities.c:90 msgid "October" msgstr "" -#: ../src/gtk/utilities.c:90 +#: ../src/gtk/utilities.c:91 msgid "November" msgstr "" -#: ../src/gtk/utilities.c:91 +#: ../src/gtk/utilities.c:92 msgid "December" msgstr "" -#: ../src/gtk/utilities.c:2021 +#: ../src/gtk/utilities.c:2024 msgid "" "An image file's size could not be determined.\n" "Xiphos cannot resize images to fit window." @@ -1803,15 +1800,15 @@ msgid "" "

This module has no content at this point.
" msgstr "" -#: ../src/main/display.cc:266 +#: ../src/main/display.cc:260 #, c-format msgid "" "Improperly encoded personal annotation label:\n" "'%s'" msgstr "" -#: ../src/main/display.cc:1286 ../src/main/display.cc:1343 -#: ../src/main/display.cc:1384 ../ui/export-dialog.glade.h:14 +#: ../src/main/display.cc:1215 ../src/main/display.cc:1272 +#: ../src/main/display.cc:1313 ../ui/export-dialog.glade.h:14 #: ../ui/export-dialog.gtkbuilder.h:14 msgid "Chapter" msgstr "" @@ -1820,23 +1817,23 @@ msgstr "" msgid "Xiphos does not understand more than one argument." msgstr "" -#: ../src/main/main.c:258 +#: ../src/main/main.c:280 msgid "Initiating HTML" msgstr "" -#: ../src/main/main.c:263 +#: ../src/main/main.c:285 msgid "Building Interface" msgstr "" -#: ../src/main/main.c:266 +#: ../src/main/main.c:288 msgid "Starting Sword" msgstr "" -#: ../src/main/main.c:269 +#: ../src/main/main.c:291 msgid "Loading Settings" msgstr "" -#: ../src/main/main.c:272 +#: ../src/main/main.c:294 msgid "Displaying Xiphos" msgstr "" @@ -2274,17 +2271,17 @@ msgstr "" msgid "can not create " msgstr "" -#: ../src/main/settings.c:211 +#: ../src/main/settings.c:212 msgid "" "Empty settings file -- backup recovery attempted.\n" "Some information may have been lost." msgstr "" -#: ../src/main/settings.c:214 +#: ../src/main/settings.c:215 msgid "Empty settings file -- no backup?!? Information lost!" msgstr "" -#: ../src/main/settings.c:241 +#: ../src/main/settings.c:242 msgid "" "There are no Bibles installed.\n" "Evidently, you declined to install any.\n" @@ -2294,7 +2291,7 @@ msgid "" "and will now exit." msgstr "" -#: ../src/main/settings.c:247 +#: ../src/main/settings.c:248 msgid "Bible module installation complete." msgstr "" @@ -2408,67 +2405,67 @@ msgstr "" msgid "Show %s in main window" msgstr "" -#: ../src/main/xml.c:87 ../src/main/xml.c:443 ../src/main/xml.c:574 +#: ../src/main/xml.c:88 ../src/main/xml.c:500 ../src/main/xml.c:631 #, c-format msgid "Document not created successfully. \n" msgstr "" -#: ../src/main/xml.c:99 +#: ../src/main/xml.c:100 msgid "What must I do to be saved?" msgstr "" -#: ../src/main/xml.c:100 ../src/main/xml.c:101 +#: ../src/main/xml.c:101 ../src/main/xml.c:102 msgid "Acts 16:31" msgstr "" -#: ../src/main/xml.c:102 ../src/main/xml.c:103 +#: ../src/main/xml.c:103 ../src/main/xml.c:104 msgid "Eph 2:8,9" msgstr "" -#: ../src/main/xml.c:104 ../src/main/xml.c:105 +#: ../src/main/xml.c:105 ../src/main/xml.c:106 msgid "Romans 1:16" msgstr "" -#: ../src/main/xml.c:107 +#: ../src/main/xml.c:108 msgid "What is the Gospel?" msgstr "" -#: ../src/main/xml.c:108 ../src/main/xml.c:109 +#: ../src/main/xml.c:109 ../src/main/xml.c:110 msgid "1 Cor 15:1-4" msgstr "" -#: ../src/main/xml.c:238 ../src/main/xml.c:384 ../src/main/xml.c:1504 +#: ../src/main/xml.c:295 ../src/main/xml.c:441 ../src/main/xml.c:1561 #, c-format msgid "Document not parsed successfully. \n" msgstr "" -#: ../src/main/xml.c:244 ../src/main/xml.c:390 ../src/main/xml.c:410 -#: ../src/main/xml.c:1061 ../src/main/xml.c:1391 ../src/main/xml.c:1587 +#: ../src/main/xml.c:301 ../src/main/xml.c:447 ../src/main/xml.c:467 +#: ../src/main/xml.c:1118 ../src/main/xml.c:1448 ../src/main/xml.c:1644 #, c-format msgid "empty document \n" msgstr "" -#: ../src/main/xml.c:250 +#: ../src/main/xml.c:307 #, c-format msgid "wrong type, root node != SwordBookmarks\n" msgstr "" -#: ../src/main/xml.c:396 +#: ../src/main/xml.c:453 #, c-format msgid "wrong type, root node != Copy_Export\n" msgstr "" -#: ../src/main/xml.c:473 +#: ../src/main/xml.c:530 #, c-format msgid "%s
%s
Chapter %d

" msgstr "" -#: ../src/main/xml.c:477 +#: ../src/main/xml.c:534 #, c-format msgid "
Chapter %d

" msgstr "" -#: ../src/main/xml.c:480 +#: ../src/main/xml.c:537 #, c-format msgid "" "%s\n" @@ -2477,7 +2474,7 @@ msgid "" "\n" msgstr "" -#: ../src/main/xml.c:483 +#: ../src/main/xml.c:540 #, c-format msgid "" "\n" @@ -2486,101 +2483,101 @@ msgid "" "\n" msgstr "" -#: ../src/main/xml.c:492 +#: ../src/main/xml.c:549 #, c-format msgid "%s%s: %s Chapter %d

" msgstr "" -#: ../src/main/xml.c:495 +#: ../src/main/xml.c:552 #, c-format msgid "" "%s: %s Chapter %d\n" "\n" msgstr "" -#: ../src/main/xml.c:503 +#: ../src/main/xml.c:560 #, c-format msgid "  [%d]" msgstr "" -#: ../src/main/xml.c:506 +#: ../src/main/xml.c:563 #, c-format msgid " [%d]" msgstr "" -#: ../src/main/xml.c:515 +#: ../src/main/xml.c:572 #, c-format msgid "%s%s (%s %d:%d%s)" msgstr "" -#: ../src/main/xml.c:518 +#: ../src/main/xml.c:575 #, c-format msgid "%s(%s %d:%d%s)%s " msgstr "" -#: ../src/main/xml.c:520 +#: ../src/main/xml.c:577 #, c-format msgid "%s (%s %d:%d%s)" msgstr "" -#: ../src/main/xml.c:523 +#: ../src/main/xml.c:580 #, c-format msgid "(%s %d:%d%s) %s" msgstr "" -#: ../src/main/xml.c:531 +#: ../src/main/xml.c:588 #, c-format msgid " %s%s" msgstr "" -#: ../src/main/xml.c:533 ../src/main/xml.c:537 +#: ../src/main/xml.c:590 ../src/main/xml.c:594 #, c-format msgid "%s(%s %d:%d-%d%s)" msgstr "" -#: ../src/main/xml.c:535 +#: ../src/main/xml.c:592 #, c-format msgid "(%s %d:%d-%d%s)
" msgstr "" -#: ../src/main/xml.c:540 +#: ../src/main/xml.c:597 #, c-format msgid "(%s %d:%d-%d%s)\n" msgstr "" -#: ../src/main/xml.c:589 +#: ../src/main/xml.c:646 msgid "Old Testament" msgstr "" -#: ../src/main/xml.c:591 +#: ../src/main/xml.c:648 msgid "Gen - Mal" msgstr "" -#: ../src/main/xml.c:597 +#: ../src/main/xml.c:654 msgid "New Testament" msgstr "" -#: ../src/main/xml.c:599 +#: ../src/main/xml.c:656 msgid "Mat - Rev" msgstr "" -#: ../src/main/xml.c:608 +#: ../src/main/xml.c:665 msgid "Sample Module List" msgstr "" -#: ../src/main/xml.c:1067 ../src/main/xml.c:1397 +#: ../src/main/xml.c:1124 ../src/main/xml.c:1454 #, c-format msgid "wrong type, root node != %s\n" msgstr "" -#: ../src/main/xml.c:1549 +#: ../src/main/xml.c:1606 #, c-format msgid "" "Save of settings failed! stat %d, size %d\n" "%s" msgstr "" -#: ../src/main/xml.c:1551 +#: ../src/main/xml.c:1608 msgid "Attempting to revert to previous save." msgstr "" @@ -2596,11 +2593,6 @@ msgstr "" msgid "Create new folder" msgstr "" -#: ../ui/bookmarks.glade.h:4 ../ui/bookmarks.gtkbuilder.h:4 -#: ../ui/xi-menus.glade.h:122 ../ui/xi-menus-popup.gtkbuilder.h:5 -msgid "New Folder" -msgstr "" - #: ../ui/bookmarks.glade.h:5 ../ui/bookmarks.gtkbuilder.h:3 msgid "New ..." msgstr "" @@ -2641,6 +2633,30 @@ msgstr "" msgid "Bookmark Folder Treeview" msgstr "" +#: ../ui/folder.gtkbuilder.h:1 +msgid "Folder" +msgstr "" + +#: ../ui/folder.gtkbuilder.h:2 +msgid "Name:" +msgstr "" + +#: ../ui/folder.gtkbuilder.h:3 +msgid "Tag color:" +msgstr "" + +#: ../ui/folder.gtkbuilder.h:4 +msgid "Choose a highlight color for this tag group" +msgstr "" + +#: ../ui/folder.gtkbuilder.h:5 +msgid "No color" +msgstr "" + +#: ../ui/folder.gtkbuilder.h:6 +msgid "Remove tag color from this folder" +msgstr "" + #: ../ui/editor_link_dialog.gtkbuilder.h:2 msgid "Test Link" msgstr "" diff --git a/src/gtk/bookmark_dialog.c b/src/gtk/bookmark_dialog.c index 824c1eeb2..a34c57148 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; @@ -89,40 +90,51 @@ void on_buffer_changed(GtkTextBuffer *textbuffer, gpointer user_data) static void add_bookmark_button(void) { - GtkTreeIter selected; - GtkTreeIter iter; - BOOKMARK_DATA *data; - GtkTreeSelection *selection; - - 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->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))); - - data->is_leaf = TRUE; - 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); + GtkTreeIter selected; + 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->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))); + + + 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->opened = bm_pixbufs->pixbuf_helpdoc; + data->closed = NULL; + + gui_add_item_to_tree(&iter, &selected, data); + bookmarks_changed = TRUE; + gui_save_bookmarks(NULL, NULL); + } /****************************************************************************** @@ -147,56 +159,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 */ + g_signal_connect_swapped(clearbtn, "clicked", + G_CALLBACK(gtk_widget_set_sensitive), colorbtn); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) { + const gchar *name = gtk_entry_get_text(GTK_ENTRY(entry)); + gchar *color = NULL; + + if (gtk_widget_is_sensitive(colorbtn)) { +#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; + 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->caption); + g_free(data->color); + g_free(data); } - 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 } /****************************************************************************** @@ -218,19 +274,6 @@ static void add_folder_button(void) void on_dialog_response(GtkDialog *dialog, gint response_id, gpointer user_data) { - switch (response_id) { - case GTK_RESPONSE_CANCEL: /* cancel button pressed */ - case GTK_RESPONSE_NONE: /* dialog destroyed */ - gtk_widget_destroy(GTK_WIDGET(dialog)); - break; - case GTK_RESPONSE_OK: /* add button pressed */ - add_bookmark_button(); - gtk_widget_destroy(GTK_WIDGET(dialog)); - break; - case GTK_RESPONSE_ACCEPT: /* add folder pressed */ - add_folder_button(); - break; - } } /****************************************************************************** @@ -249,8 +292,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 +484,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 +618,27 @@ static GtkWidget *_create_mark_verse_dialog(gchar *module, gchar *key) void gui_bookmark_dialog(gchar *label, gchar *module_name, gchar *key) { + global_module_name = module_name; GtkWidget *dialog = _create_bookmark_dialog(label, module_name, key); if (!dialog) return; - gtk_dialog_run(GTK_DIALOG(dialog)); + gint response; + 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..41afd3d0a 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" @@ -90,12 +90,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 +104,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 +111,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(color); } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), iter)); xmlSaveFormatFile(filename, root_doc, 1); @@ -167,7 +173,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); } /****************************************************************************** @@ -333,99 +341,155 @@ 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); - - 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; - } - - info->text1 = g_strdup(caption); - info->ok = TRUE; - info->cancel = TRUE; + COL_CAPTION, &caption, COL_KEY, &key, + COL_MODULE, &module, COL_MODULE_DESC, &mod_desc, + COL_DESCRIPTION, &description, + COL_COLOR, ¤t_color, -1); + +if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(model), &selected)) { + /* --- 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; - } else { - data->opened = bm_pixbufs->pixbuf_opened; - data->closed = bm_pixbufs->pixbuf_closed; - data->is_leaf = FALSE; +#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 } - gtk_tree_store_set(GTK_TREE_STORE(model), &selected, - 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, -1); - bookmarks_changed = TRUE; - gui_save_bookmarks(NULL, NULL); - g_free(data); + g_signal_connect_swapped(clearbtn, "clicked", + G_CALLBACK(gtk_widget_set_sensitive), colorbtn); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) { + const gchar *name = gtk_entry_get_text(GTK_ENTRY(entry)); + gchar *new_color = NULL; + + if (gtk_widget_is_sensitive(colorbtn)) { +#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 + } + + gchar *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 = info->text1; + data->key = info->text2; + data->module = info->text3; + data->module_desc = g_strdup(main_get_module_description(info->text3)); + data->description = ((description && strlen(description) > 1) || + (caption && strcmp(caption, info->text1))) + ? 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, + COL_KEY, data->key, + 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->module_desc); + g_free(data); + } + 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); } //dialog_export_bookmarks_response_cb @@ -674,6 +738,7 @@ void on_add_bookmark_activate(GtkMenuItem *menuitem, gpointer user_data) data->description = NULL; else data->description = info->text1; + data->color = NULL; /* bookmark leaves never have a color */ data->is_leaf = TRUE; data->opened = bm_pixbufs->pixbuf_helpdoc; data->closed = NULL; @@ -734,53 +799,93 @@ 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), ""); + + g_signal_connect_swapped(clearbtn, "clicked", + G_CALLBACK(gtk_widget_set_sensitive), colorbtn); + + 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 (gtk_widget_is_sensitive(colorbtn)) { +#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); + data->opened = bm_pixbufs->pixbuf_opened; + data->closed = bm_pixbufs->pixbuf_closed; bookmarks_changed = TRUE; + add_item_to_tree(&iter, &selected, data); gui_save_bookmarks(NULL, NULL); g_free(data->caption); + g_free(data->color); + g_free(data); } - 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 } /****************************************************************************** @@ -842,6 +947,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; @@ -872,7 +1024,8 @@ void gui_create_bookmark_menu(void) menu.delete = UI_GET_ITEM(gxml, "delete_item"); 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.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..9c913deec 100644 --- a/src/gtk/bookmarks_treeview.c +++ b/src/gtk/bookmarks_treeview.c @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -203,6 +204,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 +215,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 +269,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 */ } /****************************************************************************** @@ -291,6 +300,8 @@ static void free_bookmark_data(BOOKMARK_DATA *data) g_free(data->module_desc); if (data->description) g_free(data->description); + if (data->color) + g_free(data->color); } /****************************************************************************** @@ -322,7 +333,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); } /****************************************************************************** @@ -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; @@ -592,6 +693,24 @@ void gui_add_columns(GtkTreeView *tree) "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 +772,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); } @@ -680,6 +801,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) { @@ -690,6 +818,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; @@ -781,34 +910,131 @@ 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 (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) { + /* 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); + gchar *url = g_strdup_printf( + "passagestudy.jsp?action=showBookmark&" + "type=%s&value=%s&module=%s", + "currentTab", + main_url_encode(ref), + main_url_encode((real_mod ? real_mod : module))); + 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 { + 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); } @@ -831,6 +1057,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,6 +1165,7 @@ 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); diff --git a/src/gtk/utilities.c b/src/gtk/utilities.c index bfb646cb2..38dc19afe 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 @@ -151,32 +152,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/display.cc b/src/main/display.cc index f5cd59ffd..edbb58f8c 100644 --- a/src/main/display.cc +++ b/src/main/display.cc @@ -52,6 +52,7 @@ #include "main/xml.h" #include "gui/utilities.h" +#include "gui/bookmarks_treeview.h" #include "gui/widgets.h" #include "gui/dialog.h" @@ -1422,6 +1423,157 @@ GTKChapDisp::getVerseAfter(SWModule &imodule) } } +/* Returns white or black depending on background luminance */ +static const char *text_color_for_bg(const gchar *hex_color) +{ + if (!hex_color || hex_color[0] != '#' || strlen(hex_color) < 7) + return "#000000"; + int r = 0, g = 0, b = 0; + sscanf(hex_color + 1, "%02x%02x%02x", &r, &g, &b); + /* relative luminance (ITU-R BT.709) */ + double lum = 0.2126 * r + 0.7152 * g + 0.0722 * b; + return (lum < 128.0) ? "#FFFFFF" : "#000000"; +} + +/* Returns the tag color for a given VerseKey by comparing OSIS refs. + * Walks the GtkTreeStore directly from C++. */ +static gchar *get_tag_color_for_versekey(VerseKey *vk) +{ + extern GtkTreeStore *model; + if (!model || !vk) return NULL; + + gchar *osisref = g_strdup_printf("%s.%d.%d", + vk->getOSISBookName(), + vk->getChapter(), + vk->getVerse()); + + GtkTreeIter root; + if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &root)) { + g_free(osisref); return NULL; + } + + GQueue *stack = g_queue_new(); + GQueue *colors = g_queue_new(); + GtkTreeIter child; + if (gtk_tree_model_iter_children(GTK_TREE_MODEL(model), &child, &root)) { + do { + GtkTreeIter *copy = g_new(GtkTreeIter, 1); + *copy = child; + g_queue_push_tail(stack, copy); + g_queue_push_tail(colors, NULL); + } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &child)); + } + + gchar *result = NULL; + while (!g_queue_is_empty(stack) && !result) { + GtkTreeIter *iter = (GtkTreeIter *)g_queue_pop_head(stack); + gchar *inherited = (gchar *)g_queue_pop_head(colors); + gchar *node_color = NULL, *node_key = NULL; + gtk_tree_model_get(GTK_TREE_MODEL(model), iter, + COL_COLOR, &node_color, + COL_KEY, &node_key, -1); + const gchar *effective = (node_color && *node_color) + ? node_color : inherited; + if (node_key) { + /* handle multi-reference keys with ranges, semicolons, commas */ + gchar **parts = g_strsplit(node_key, ";", -1); + gchar *last_book_chap = NULL; + for (gint pi = 0; parts[pi] && !result; pi++) { + gchar *part = g_strstrip(parts[pi]); + if (!*part) continue; + /* resolve partial ref: "16:36" -> "Acts 16:36" */ + gchar *full_part; + if (last_book_chap && !strchr(part, ' ') && strchr(part, ':')) + full_part = g_strdup_printf("%s %s", last_book_chap, part); + else + full_part = g_strdup(part); + /* check for verse range: "1 Cor 15:1-4" */ + const gchar *col = strchr(full_part, ':'); + const gchar *dash = col ? strchr(col, '-') : NULL; + if (col && dash) { + /* range: check if curVerse is between start and end */ + gchar *range_ref = g_strndup(full_part, dash - full_part); + VerseKey vk_start; + vk_start.setLocale(vk->getLocale()); + vk_start.setText(range_ref); + g_free(range_ref); + int verse_end = atoi(dash + 1); + /* compare numerically */ + gboolean same_book = !strcmp(vk->getOSISBookName(), + vk_start.getOSISBookName()); + if (same_book && + vk->getChapter() == vk_start.getChapter() && + vk->getVerse() >= vk_start.getVerse() && + vk->getVerse() <= verse_end && + effective) + result = g_strdup(effective); + } else { + /* handle comma-separated verses */ + gchar **verses = g_strsplit(full_part, ",", -1); + gchar *book_chapter = NULL; + for (gint vi = 0; verses[vi] && !result; vi++) { + gchar *v = g_strstrip(verses[vi]); + gchar *full_ref; + if (strchr(v, ' ') || strchr(v, '.') || !book_chapter) + full_ref = g_strdup(v); + else + full_ref = g_strdup_printf("%s:%s", book_chapter, v); + VerseKey vk2; + vk2.setLocale(vk->getLocale()); + vk2.setText(full_ref); + gchar *ref2 = g_strdup_printf("%s.%d.%d", + vk2.getOSISBookName(), + vk2.getChapter(), + vk2.getVerse()); + if (!g_ascii_strcasecmp(osisref, ref2) && effective) + result = g_strdup(effective); + if (!book_chapter && strchr(full_ref, ':')) { + gchar *colon = strrchr(full_ref, ':'); + book_chapter = g_strndup(full_ref, colon - full_ref); + } + g_free(ref2); + g_free(full_ref); + } + g_strfreev(verses); + g_free(book_chapter); + } + /* update last_book_chap for partial ref resolution */ + if (col) { + const gchar *sp = col; + while (sp > full_part && *sp != ' ') sp--; + if (sp > full_part) { + g_free(last_book_chap); + last_book_chap = g_strndup(full_part, sp - full_part); + } + } + g_free(full_part); + } + g_free(last_book_chap); + g_strfreev(parts); + g_free(node_key); + } else { + if (gtk_tree_model_iter_children(GTK_TREE_MODEL(model), + &child, iter)) { + do { + GtkTreeIter *copy = g_new(GtkTreeIter, 1); + *copy = child; + g_queue_push_tail(stack, copy); + g_queue_push_tail(colors, + effective ? g_strdup(effective) : NULL); + } while (gtk_tree_model_iter_next( + GTK_TREE_MODEL(model), &child)); + } + } + g_free(node_color); + g_free(inherited); + g_free(iter); + } + g_queue_free_full(stack, g_free); + g_queue_free_full(colors, g_free); + g_free(osisref); + return result; +} + char GTKChapDisp::display(SWModule &imodule) { @@ -1558,10 +1710,23 @@ GTKChapDisp::display(SWModule &imodule) // special contrasty highlighting marked_element *e; + gchar *tag_color = get_tag_color_for_versekey(key); + /* tag-group color highlight */ + if (tag_color) { + const char *fg = text_color_for_bg(tag_color); + buf = g_strdup_printf( + "", + tag_color, fg, tag_color); + swbuf.append(buf); + g_free(buf); + } + if (((e = marked_cache_check(key->getVerse())) && settings.annotate_highlight) || ((key->getVerse() == curVerse) && - settings.versehighlight)) { + settings.versehighlight && !tag_color)) { buf = g_strdup_printf( "" "", @@ -1644,6 +1809,13 @@ GTKChapDisp::display(SWModule &imodule) ReadAloud(curVerse, rework->str); } + /* close tag-group color span */ + if (tag_color) { + swbuf.append(""); + g_free(tag_color); + tag_color = NULL; + } + if (settings.versestyle) { if ((key->getVerse() != curVerse) || (!settings.versehighlight && 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..83e17329e 100644 --- a/src/main/settings.c +++ b/src/main/settings.c @@ -182,9 +182,10 @@ 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(); + main_init_language_map(); + gui_init(argc, argv); init_bookmarks(new_bookmarks); 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 @@ + + + + + False + False + Folder + 320 + 150 + dialog + + + True + False + vertical + 8 + 10 + + + True + False + end + + + gtk-cancel + True + True + True + + + False + False + 0 + + + + + gtk-ok + True + True + True + + + False + False + 1 + + + + + False + True + end + 0 + + + + + True + False + 8 + + + True + False + Name: + 0 + + + False + False + 0 + + + + + True + True + True + + + True + True + 1 + + + + + False + True + 1 + + + + + True + False + 8 + + + True + False + Tag color: + 0 + + + False + False + 0 + + + + + True + True + Choose a highlight color for this tag group + False + + + False + False + 1 + + + + + No color + True + True + Remove tag color from this folder + + + False + False + 2 + + + + + False + True + 2 + + + + + + folder_cancel + folder_ok + + + From 7be125d07d9dee72e46fff4eb605b5eac6f47d3d Mon Sep 17 00:00:00 2001 From: lafricain79 Date: Sun, 5 Apr 2026 23:14:27 +0200 Subject: [PATCH 2/2] Translation for new tag entries --- src/gtk/bookmark_dialog.c | 58 ++++++++------ src/gtk/bookmarks_menu.c | 92 +++++++++++----------- src/gtk/bookmarks_treeview.c | 144 +++++++++++++++++------------------ 3 files changed, 148 insertions(+), 146 deletions(-) diff --git a/src/gtk/bookmark_dialog.c b/src/gtk/bookmark_dialog.c index a34c57148..cb0dedc5d 100644 --- a/src/gtk/bookmark_dialog.c +++ b/src/gtk/bookmark_dialog.c @@ -101,7 +101,7 @@ static void add_bookmark_button(void) 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))); @@ -128,6 +128,7 @@ static void add_bookmark_button(void) 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; @@ -245,9 +246,6 @@ static void add_folder_button(void) 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->caption); - g_free(data->color); - g_free(data); } gtk_widget_destroy(dialog); #ifdef USE_GTKBUILDER @@ -618,27 +616,37 @@ static GtkWidget *_create_mark_verse_dialog(gchar *module, gchar *key) void gui_bookmark_dialog(gchar *label, gchar *module_name, gchar *key) { - global_module_name = module_name; - GtkWidget *dialog = - _create_bookmark_dialog(label, module_name, key); - if (!dialog) - return; - gint response; - 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); + 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; + + 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 41afd3d0a..69a6fce0f 100644 --- a/src/gtk/bookmarks_menu.c +++ b/src/gtk/bookmarks_menu.c @@ -130,12 +130,12 @@ static void save_treeview_to_xml_bookmarks(GtkTreeIter *iter, description, key, module, mod_desc); } - g_free(caption); - g_free(key); - g_free(module); - g_free(mod_desc); - g_free(description); - g_free(color); +// 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); @@ -304,8 +304,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; } @@ -318,8 +318,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); } /****************************************************************************** @@ -435,8 +435,8 @@ if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(model), &selected)) { COL_COLOR, new_color, -1); gui_save_bookmarks(NULL, NULL); main_display_bible(NULL, settings.currentverse); - g_free(new_caption); - g_free(new_color); +// g_free(new_caption); +// g_free(new_color); } gtk_widget_destroy(dialog); #ifdef USE_GTKBUILDER @@ -459,13 +459,13 @@ if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(model), &selected)) { info->cancel = TRUE; if (gui_gs_dialog(info) == GS_OK) { BOOKMARK_DATA *data = g_new0(BOOKMARK_DATA, 1); - 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)); data->description = ((description && strlen(description) > 1) || (caption && strcmp(caption, info->text1))) - ? info->text1 : NULL; + ? g_strdup(info->text1) : NULL; data->is_leaf = TRUE; data->opened = bm_pixbufs->pixbuf_helpdoc; gtk_tree_store_set(GTK_TREE_STORE(model), &selected, @@ -478,22 +478,20 @@ if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(model), &selected)) { COL_DESCRIPTION, data->description, -1); bookmarks_changed = TRUE; gui_save_bookmarks(NULL, NULL); - g_free(data->module_desc); - g_free(data); } - 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); +// if (info->text2) g_free(info->text2); +// if (info->text3) g_free(info->text3); +// g_free(info); +// 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); +// 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 @@ -572,10 +570,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); } /****************************************************************************** @@ -729,15 +727,15 @@ 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; @@ -746,12 +744,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); } /****************************************************************************** @@ -878,9 +875,6 @@ G_MODULE_EXPORT void on_new_folder_activate(GtkMenuItem *menuitem, bookmarks_changed = TRUE; add_item_to_tree(&iter, &selected, data); gui_save_bookmarks(NULL, NULL); - g_free(data->caption); - g_free(data->color); - g_free(data); } gtk_widget_destroy(dialog); #ifdef USE_GTKBUILDER @@ -925,9 +919,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); } /****************************************************************************** @@ -973,7 +967,7 @@ G_MODULE_EXPORT void on_set_tag_color_activate(GtkMenuItem *menuitem, if (gdk_rgba_parse(&rgba, color)) gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &rgba); } - g_free(color); +// g_free(color); if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) { GdkRGBA rgba; @@ -986,7 +980,7 @@ G_MODULE_EXPORT void on_set_tag_color_activate(GtkMenuItem *menuitem, bookmarks_changed = TRUE; gtk_tree_store_set(GTK_TREE_STORE(model), &selected, COL_COLOR, hex, -1); - g_free(hex); +// g_free(hex); gui_save_bookmarks(NULL, NULL); main_display_bible(NULL, settings.currentverse); } diff --git a/src/gtk/bookmarks_treeview.c b/src/gtk/bookmarks_treeview.c index 9c913deec..84539ab5e 100644 --- a/src/gtk/bookmarks_treeview.c +++ b/src/gtk/bookmarks_treeview.c @@ -154,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); @@ -290,18 +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); - if (data->color) - g_free(data->color); } /****************************************************************************** @@ -357,26 +366,22 @@ 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; - xmlNodePtr work = NULL; - - p = &data; - if (cur == NULL) - return; - - for (work = cur->xmlChildrenNode; work; work = work->next) { - if (!xmlStrcmp(work->name, (const xmlChar *)"Bookmark")) { - 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")) { - get_xml_folder_data(work, p); - gui_add_item_to_tree(&iter, parent, p); - free_bookmark_data(p); - add_node(work, &iter); - } - } + GtkTreeIter iter; + BOOKMARK_DATA *p = NULL; + xmlNodePtr work = NULL; + + 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); + } 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); + add_node(work, &iter); + } + } } /****************************************************************************** @@ -395,40 +400,35 @@ static void add_node(xmlNodePtr cur, GtkTreeIter *parent) * void */ -void gui_parse_bookmarks(GtkTreeView *tree, const xmlChar *file, - GtkTreeIter *parent) +void gui_parse_bookmarks(GtkTreeView *tree, const xmlChar *file, GtkTreeIter *parent) { - xmlNodePtr cur = NULL; - BOOKMARK_DATA 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")) { - get_xml_bookmark_data(cur, p); - gui_add_item_to_tree(&iter, parent, p); - free_bookmark_data(p); - } else { - 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; - } - xml_free_bookmark_doc(); - path = gtk_tree_model_get_path(GTK_TREE_MODEL(model), parent); - gtk_tree_view_expand_to_path(tree, path); - gtk_tree_path_free(path); + xmlNodePtr cur = NULL; + BOOKMARK_DATA *p = NULL; + GtkTreeIter iter; + GtkTreePath *path; + + cur = xml_load_bookmark_file(file); + + 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); + } else { + p = g_new(BOOKMARK_DATA, 1); + get_xml_folder_data(cur, p); + if (p->caption) { + gui_add_item_to_tree(&iter, parent, p); + } + add_node(cur, &iter); + } + 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); + gtk_tree_path_free(path); } /******************************************************************************