commit eb20d9677148594c6e6f94525a15a455bc15482d
parent b02e4d69ddb4aa63c4c971e08caf4ee8e95ae3c7
Author: Christian Grothoff <christian@grothoff.org>
Date: Thu, 30 Jul 2026 08:55:11 +0200
get rid of deprecated Gtk3 artifacts
Diffstat:
39 files changed, 2827 insertions(+), 2663 deletions(-)
diff --git a/contrib/anastasis-gtk.css b/contrib/anastasis-gtk.css
@@ -23,6 +23,15 @@ frame.flat {
border-style: none;
}
+/* The theme gives the cells of a GtkColumnView far more vertical padding than
+ a GtkCellRenderer had, which would make every one of our lists a third
+ taller than it used to be. The progress lists ask for their padding through
+ the margins of their labels instead. */
+columnview > listview > row > cell {
+ padding-top: 2px;
+ padding-bottom: 2px;
+}
+
/* AG_mark_invalid_input() adds this class to the identity attribute widget
whose contents the reducer rejected. */
.anastasis-invalid,
diff --git a/contrib/anastasis_gtk_deny_singlefactor.ui b/contrib/anastasis_gtk_deny_singlefactor.ui
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<interface>
- <requires lib="gtk" version="4.0"/>
- <object class="GtkMessageDialog" id="anastasis_gtk_deny_singlefactor_dialog">
- <property name="title" translatable="1">Single-factor authentication is not allowed!</property>
- <property name="modal">1</property>
- <property name="message-type">warning</property>
- <property name="text" translatable="1">To keep your secret secure, we require at least two authentication options, and recommend you specify at least three authentication options before proceeding.</property>
- <property name="secondary-text" translatable="1">Select "back" to add additional authentication options.</property>
- <signal name="response" handler="anastasis_gtk_deny_singlefactor_dialog_response_cb" swapped="no"/>
- <child internal-child="content_area">
- <object class="GtkBox">
- <property name="orientation">vertical</property>
- <property name="spacing">2</property>
- <child internal-child="action_area">
- <object class="GtkBox">
- <property name="valign">center</property>
- <property name="can-focus">0</property>
- <property name="homogeneous">1</property>
- <property name="spacing">6</property>
- <property name="halign">end</property>
- <child>
- <object class="GtkButton" id="back_button">
- <property name="label" translatable="1">_Back</property>
- <property name="focusable">1</property>
- <property name="receives-default">1</property>
- <property name="tooltip-text" translatable="1">Go back to the previous dialog to add more authentication options.</property>
- <property name="margin-start">5</property>
- <property name="margin-end">5</property>
- <property name="use-underline">1</property>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <action-widgets>
- <action-widget response="-6">back_button</action-widget>
- </action-widgets>
- </object>
-</interface>
-
diff --git a/contrib/anastasis_gtk_edit_providers.ui b/contrib/anastasis_gtk_edit_providers.ui
@@ -20,17 +20,16 @@ along with Anastasis-gtk. If not, see <http://www.gnu.org/licenses/>.
Author: Christian Grothoff-->
<interface>
<requires lib="gtk" version="4.0"/>
- <object class="GtkListStore" id="provider_liststore">
- <columns>
- <column type="gchararray"/>
- <column type="gchararray"/>
- <column type="gchararray"/>
- <column type="gchararray"/>
- <column type="gboolean"/>
- <column type="gboolean"/>
- <column type="gboolean"/>
- <column type="gchararray"/>
- </columns>
+ <object class="GListStore" id="provider_liststore">
+ <property name="item-type">AGRow</property>
+ </object>
+ <object class="GtkSortListModel" id="provider_sorted">
+ <property name="model">provider_liststore</property>
+ </object>
+ <object class="GtkSingleSelection" id="provider_selection">
+ <property name="model">provider_sorted</property>
+ <property name="autoselect">0</property>
+ <property name="can-unselect">1</property>
</object>
<object class="GtkDialog" id="edit_provider_dialog">
<property name="modal">1</property>
@@ -96,77 +95,58 @@ Author: Christian Grothoff-->
<property name="hexpand">1</property>
<property name="margin-top">10</property>
<property name="child">
- <object class="GtkViewport">
+ <object class="GtkScrolledWindow">
+ <property name="height-request">300</property>
<property name="child">
- <object class="GtkTreeView" id="provider_tree_view">
- <property name="height-request">300</property>
- <property name="model">provider_liststore</property>
- <child internal-child="selection">
- <object class="GtkTreeSelection">
- <property name="mode">browse</property>
- </object>
- </child>
+ <object class="GtkColumnView" id="provider_tree_view">
+ <property name="model">provider_selection</property>
<child>
- <object class="GtkTreeViewColumn" id="enabled_column">
- <property name="spacing">5</property>
+ <object class="GtkColumnViewColumn" id="enabled_column">
<property name="title" translatable="1">Use</property>
- <property name="reorderable">1</property>
- <property name="sort-indicator">1</property>
- <property name="sort-column-id">6</property>
- <child>
- <object class="GtkCellRendererToggle" id="use_toggle">
- <signal name="toggled" handler="provider_toggle_callback" swapped="no"/>
+ <property name="factory">
+ <object class="GtkSignalListItemFactory">
+ <signal name="setup" handler="AG_cell_setup_provider_use"/>
+ <signal name="bind" handler="AG_cell_bind_provider_use"/>
+ <signal name="unbind" handler="AG_cell_unbind"/>
</object>
- <attributes>
- <attribute name="sensitive">5</attribute>
- <attribute name="activatable">5</attribute>
- <attribute name="active">4</attribute>
- <attribute name="inconsistent">6</attribute>
- </attributes>
- </child>
+ </property>
</object>
</child>
<child>
- <object class="GtkTreeViewColumn" id="url_column">
- <property name="spacing">5</property>
- <property name="sizing">autosize</property>
+ <object class="GtkColumnViewColumn" id="url_column">
<property name="title" translatable="1">Provider</property>
- <child>
- <object class="GtkCellRendererText" id="url_renderer"/>
- <attributes>
- <attribute name="text">7</attribute>
- </attributes>
- </child>
+ <property name="factory">
+ <object class="GtkSignalListItemFactory">
+ <signal name="setup" handler="AG_cell_setup_text"/>
+ <signal name="bind" handler="AG_cell_bind_provider_name"/>
+ <signal name="unbind" handler="AG_cell_unbind"/>
+ </object>
+ </property>
</object>
</child>
<child>
- <object class="GtkTreeViewColumn" id="liability_column">
- <property name="spacing">5</property>
- <property name="sizing">autosize</property>
+ <object class="GtkColumnViewColumn" id="liability_column">
<property name="title" translatable="1">Liability limit</property>
- <child>
- <object class="GtkCellRendererText" id="liability_limit_text"/>
- <attributes>
- <attribute name="visible">5</attribute>
- <attribute name="text">3</attribute>
- </attributes>
- </child>
+ <property name="factory">
+ <object class="GtkSignalListItemFactory">
+ <signal name="setup" handler="AG_cell_setup_text"/>
+ <signal name="bind" handler="AG_cell_bind_provider_liability"/>
+ <signal name="unbind" handler="AG_cell_unbind"/>
+ </object>
+ </property>
</object>
</child>
<child>
- <object class="GtkTreeViewColumn">
- <property name="spacing">5</property>
+ <object class="GtkColumnViewColumn" id="status_column">
+ <property name="expand">1</property>
<property name="title" translatable="1">Status</property>
- <property name="reorderable">1</property>
- <property name="sort-indicator">1</property>
- <property name="sort-column-id">6</property>
- <child>
- <object class="GtkCellRendererText" id="status"/>
- <attributes>
- <attribute name="foreground">2</attribute>
- <attribute name="text">1</attribute>
- </attributes>
- </child>
+ <property name="factory">
+ <object class="GtkSignalListItemFactory">
+ <signal name="setup" handler="AG_cell_setup_text"/>
+ <signal name="bind" handler="AG_cell_bind_provider_status"/>
+ <signal name="unbind" handler="AG_cell_unbind"/>
+ </object>
+ </property>
</object>
</child>
<child>
@@ -268,4 +248,3 @@ Author: Christian Grothoff-->
</child>
</object>
</interface>
-
diff --git a/contrib/anastasis_gtk_main_window.ui b/contrib/anastasis_gtk_main_window.ui
@@ -20,49 +20,8 @@ along with anastasis-gtk. If not, see <http://www.gnu.org/licenses/>.
Author: Christian Grothoff, Dennis Neufeld-->
<interface>
<requires lib="gtk" version="4.0"/>
- <object class="GtkListStore" id="anastasis_gtk_backup_progress_liststore">
- <columns>
- <column type="gchararray"/>
- <column type="gchararray"/>
- <column type="gchararray"/>
- </columns>
- <data>
- <row>
- <col id="0" translatable="yes">1. Location & currency</col>
- <col id="1">^(CONTINENT|COUNTRY)_SELECTING$</col>
- <col id="2" translatable="yes">Where you live will determine which personal information can be used to identify your backup.</col>
- </row>
- <row>
- <col id="0" translatable="yes">2. Personal information</col>
- <col id="1">^USER_ATTRIBUTES_COLLECTING$</col>
- <col id="2" translatable="yes">Your personal information will not be shared with anyone. But you must provide the same information during recovery.\</col>
- </row>
- <row>
- <col id="0" translatable="yes">3. Authorization methods</col>
- <col id="1">^AUTHENTICATIONS_EDITING$</col>
- <col id="2" translatable="yes">Specify which methods could be used to authenticate you during recovery.</col>
- </row>
- <row>
- <col id="0" translatable="yes">4. Recovery policies</col>
- <col id="1">^POLICIES_REVIEWING$</col>
- <col id="2" translatable="yes">These are possible ways how the secret could be recovered. Please review and possibly edit the policies.</col>
- </row>
- <row>
- <col id="0" translatable="yes">5. Enter secret</col>
- <col id="1">^SECRET_EDITING$</col>
- <col id="2" translatable="yes">Please specify the secret to be backed up.</col>
- </row>
- <row>
- <col id="0" translatable="yes">6. Payment (optional)</col>
- <col id="1">^.*_PAYING$</col>
- <col id="2" translatable="yes">Some Anastasis providers require payment for their service. Please pay.</col>
- </row>
- <row>
- <col id="0" translatable="yes">7. Backup completed</col>
- <col id="1">BACKUP_FINISHED</col>
- <col id="2" translatable="yes">Your backup is complete. Please do test the recovery on occasion.</col>
- </row>
- </data>
+ <object class="GListStore" id="anastasis_gtk_backup_progress_liststore">
+ <property name="item-type">AGRow</property>
</object>
<object class="GtkAdjustment" id="anastasis_gtk_enter_secret_hadjustment">
<property name="upper">100</property>
@@ -87,46 +46,11 @@ Author: Christian Grothoff, Dennis Neufeld-->
<property name="step-increment">1</property>
<property name="page-increment">10</property>
</object>
- <object class="GtkListStore" id="anastasis_gtk_recovery_progress_liststore">
- <columns>
- <column type="gchararray"/>
- <column type="gchararray"/>
- <column type="gchararray"/>
- </columns>
- <data>
- <row>
- <col id="0" translatable="yes">1. Location & currency</col>
- <col id="1">^(CONTINENT|COUNTRY)_SELECTING$</col>
- <col id="2" translatable="yes">Where you live will determine which personal information can be used to identify your backup.</col>
- </row>
- <row>
- <col id="0" translatable="yes">2. Personal information</col>
- <col id="1">^USER_ATTRIBUTES_COLLECTING$</col>
- <col id="2" translatable="yes">Your personal information will not be shared with anyone. But you must provide the same information during recovery.</col>
- </row>
- <row>
- <col id="0" translatable="yes">3. Select secret</col>
- <col id="1">^SECRET_SELECTING$</col>
- <col id="2" translatable="yes">Please select which secret to recover. You may switch to a different version or provider.</col>
- </row>
- <row>
- <col id="0" translatable="yes">4. Solve challenges</col>
- <col id="1">^CHALLENGE_SELECTING$</col>
- <col id="2" translatable="yes">Please select an authentication challenge to pass to recover the secret.</col>
- </row>
- <row>
- <col id="0" translatable="yes">5. Secret recovered</col>
- <col id="1">^RECOVERY_FINISHED$</col>
- <col id="2" translatable="yes">Your secret was successfully recovered.</col>
- </row>
- </data>
+ <object class="GListStore" id="anastasis_gtk_recovery_progress_liststore">
+ <property name="item-type">AGRow</property>
</object>
- <object class="GtkListStore" id="authentication_methods_liststore">
- <columns>
- <column type="gchararray"/>
- <column type="gchararray"/>
- <column type="guint"/>
- </columns>
+ <object class="GListStore" id="authentication_methods_liststore">
+ <property name="item-type">AGRow</property>
</object>
<object class="GtkAdjustment" id="backup_adjustmenth">
<property name="upper">100</property>
@@ -138,46 +62,14 @@ Author: Christian Grothoff, Dennis Neufeld-->
<property name="step-increment">1</property>
<property name="page-increment">10</property>
</object>
- <object class="GtkListStore" id="backup_provider_liststore">
- <columns>
- <column type="gchararray"/>
- <column type="guint64"/>
- <column type="gchararray"/>
- <column type="gboolean"/>
- <column type="gchararray"/>
- </columns>
+ <object class="GListStore" id="backup_provider_liststore">
+ <property name="item-type">AGRow</property>
</object>
- <object class="GtkListStore" id="challenge_status_liststore">
- <columns>
- <column type="guint"/>
- <column type="gchararray"/>
- <column type="gboolean"/>
- <column type="gchararray"/>
- <column type="GdkPixbuf"/>
- <column type="gchararray"/>
- <column type="gchararray"/>
- <column type="gboolean"/>
- <column type="gboolean"/>
- <column type="gchararray"/>
- <column type="gchararray"/>
- <column type="gboolean"/>
- <column type="gboolean"/>
- <column type="gchararray"/>
- <column type="gchararray"/>
- <column type="gchararray"/>
- </columns>
+ <object class="GListStore" id="continent_liststore">
+ <property name="item-type">AGRow</property>
</object>
- <object class="GtkListStore" id="continent_liststore">
- <columns>
- <column type="gchararray"/>
- <column type="gchararray"/>
- </columns>
- </object>
- <object class="GtkListStore" id="country_liststore">
- <columns>
- <column type="gchararray"/>
- <column type="gchararray"/>
- </columns>
+ <object class="GListStore" id="country_liststore">
+ <property name="item-type">AGRow</property>
</object>
<object class="GtkAdjustment" id="expiration_year_adjustment">
<property name="upper">9999</property>
@@ -190,19 +82,8 @@ Author: Christian Grothoff, Dennis Neufeld-->
<property name="can-shrink">0</property>
<property name="content-fit">scale-down</property>
</object>
- <object class="GtkTreeStore" id="policy_review_treestore">
- <columns>
- <column type="gchararray"/>
- <column type="gchararray"/>
- <column type="gchararray"/>
- <column type="gchararray"/>
- <column type="gchararray"/>
- <column type="guint"/>
- <column type="gboolean"/>
- <column type="guint"/>
- <column type="gboolean"/>
- <column type="gchararray"/>
- </columns>
+ <object class="GListStore" id="policy_review_treestore">
+ <property name="item-type">AGRow</property>
</object>
<object class="GtkAdjustment" id="policy_version_adjustment">
<property name="upper">4294967295</property>
@@ -237,28 +118,71 @@ Author: Christian Grothoff, Dennis Neufeld-->
<property name="step-increment">1</property>
<property name="page-increment">10</property>
</object>
- <object class="GtkListStore" id="secret_selection_liststore">
- <columns>
- <column type="gchararray"/>
- <column type="gint"/>
- <column type="gint"/>
- <column type="gchararray"/>
- <column type="gchararray"/>
- <column type="guint64"/>
- <column type="gpointer"/>
- </columns>
+ <object class="GListStore" id="secret_selection_liststore">
+ <property name="item-type">AGRow</property>
</object>
<object class="GtkAdjustment" id="secret_selection_v_adjustment">
<property name="upper">100</property>
<property name="step-increment">1</property>
<property name="page-increment">10</property>
</object>
- <object class="GtkListStore" id="unpaid_qrcodes_liststore">
- <columns>
- <column type="GdkPixbuf"/>
- <column type="gchararray"/>
- <column type="gchararray"/>
- </columns>
+ <object class="GListStore" id="unpaid_qrcodes_liststore">
+ <property name="item-type">AGRow</property>
+ </object>
+ <object class="GtkSingleSelection" id="anastasis_gtk_backup_progress_tree_selection">
+ <property name="model">anastasis_gtk_backup_progress_liststore</property>
+ <property name="autoselect">0</property>
+ <property name="can-unselect">1</property>
+ </object>
+ <object class="GtkSingleSelection" id="anastasis_gtk_recovery_progress_tree_selection">
+ <property name="model">anastasis_gtk_recovery_progress_liststore</property>
+ <property name="autoselect">0</property>
+ <property name="can-unselect">1</property>
+ </object>
+ <object class="GtkSingleSelection" id="anastasis_gtk_continent_selection">
+ <property name="model">continent_liststore</property>
+ <property name="autoselect">0</property>
+ <property name="can-unselect">1</property>
+ <signal name="notify::selected" handler="anastasis_gtk_continent_selection_changed_cb"/>
+ </object>
+ <object class="GtkSingleSelection" id="anastasis_gtk_country_selection">
+ <property name="model">country_liststore</property>
+ <property name="autoselect">0</property>
+ <property name="can-unselect">1</property>
+ <signal name="notify::selected" handler="anastasis_gtk_country_selection_changed_cb"/>
+ </object>
+ <object class="GtkSortListModel" id="authentication_methods_sorted">
+ <property name="model">authentication_methods_liststore</property>
+ </object>
+ <object class="GtkSingleSelection" id="anastasis_gtk_authentication_methods_selection">
+ <property name="model">authentication_methods_sorted</property>
+ <property name="autoselect">0</property>
+ <property name="can-unselect">1</property>
+ <signal name="notify::selected" handler="auth_method_selection_changed_cb"/>
+ </object>
+ <object class="GtkSingleSelection" id="anastasis_gtk_review_policy_selection">
+ <property name="autoselect">0</property>
+ <property name="can-unselect">1</property>
+ </object>
+ <object class="GtkSingleSelection" id="unpaid_qr_tree_selection">
+ <property name="model">unpaid_qrcodes_liststore</property>
+ <property name="autoselect">0</property>
+ <property name="can-unselect">1</property>
+ <signal name="notify::selected" handler="unpaid_qr_tree_selection_changed_cb"/>
+ </object>
+ <object class="GtkSingleSelection" id="anastasis_gtk_backup_complete_selection">
+ <property name="model">backup_provider_liststore</property>
+ <property name="autoselect">0</property>
+ <property name="can-unselect">1</property>
+ </object>
+ <object class="GtkSortListModel" id="secret_selection_sorted">
+ <property name="model">secret_selection_liststore</property>
+ </object>
+ <object class="GtkSingleSelection" id="anastasis_gtk_secret_selection_treeselection">
+ <property name="model">secret_selection_sorted</property>
+ <property name="autoselect">0</property>
+ <property name="can-unselect">1</property>
+ <signal name="notify::selected" handler="anastasis_gtk_secret_selection_treeselection_changed_cb"/>
</object>
<object class="GtkWindow" id="anastasis_gtk_main_window">
<property name="default-width">700</property>
@@ -289,31 +213,19 @@ Author: Christian Grothoff, Dennis Neufeld-->
<property name="vadjustment">backup_adjustmentv</property>
<property name="propagate-natural-width">1</property>
<property name="child">
- <object class="GtkTreeView" id="anastasis_gtk_backup_progress_treeview">
- <property name="hadjustment">backup_adjustmenth</property>
- <property name="vadjustment">backup_adjustmentv</property>
- <property name="model">anastasis_gtk_backup_progress_liststore</property>
- <property name="headers-clickable">0</property>
- <property name="enable-search">0</property>
- <property name="search-column">0</property>
- <property name="show-expanders">0</property>
- <property name="tooltip-column">2</property>
- <child internal-child="selection">
- <object class="GtkTreeSelection" id="anastasis_gtk_backup_progress_tree_selection"/>
- </child>
+ <object class="GtkColumnView" id="anastasis_gtk_backup_progress_treeview">
+ <property name="model">anastasis_gtk_backup_progress_tree_selection</property>
<child>
- <object class="GtkTreeViewColumn" id="anastasis_gtk_backup_progress_description_column">
- <property name="spacing">5</property>
- <property name="sizing">autosize</property>
+ <object class="GtkColumnViewColumn" id="anastasis_gtk_backup_progress_description_column">
+ <property name="expand">1</property>
<property name="title" translatable="1">Back up a secret</property>
- <child>
- <object class="GtkCellRendererText" id="anastasis_gtk_backup_progress_description">
- <property name="ypad">15</property>
+ <property name="factory">
+ <object class="GtkSignalListItemFactory">
+ <signal name="setup" handler="AG_cell_setup_progress"/>
+ <signal name="bind" handler="AG_cell_bind_progress"/>
+ <signal name="unbind" handler="AG_cell_unbind"/>
</object>
- <attributes>
- <attribute name="text">0</attribute>
- </attributes>
- </child>
+ </property>
</object>
</child>
<child>
@@ -337,31 +249,19 @@ Author: Christian Grothoff, Dennis Neufeld-->
<property name="vadjustment">recovery_adjustmentv</property>
<property name="propagate-natural-width">1</property>
<property name="child">
- <object class="GtkTreeView" id="anastasis_gtk_recovery_progress_treeview">
- <property name="hadjustment">recovery_adjustmenth</property>
- <property name="vadjustment">recovery_adjustmentv</property>
- <property name="model">anastasis_gtk_recovery_progress_liststore</property>
- <property name="headers-clickable">0</property>
- <property name="enable-search">0</property>
- <property name="search-column">0</property>
- <property name="show-expanders">0</property>
- <property name="tooltip-column">2</property>
- <child internal-child="selection">
- <object class="GtkTreeSelection" id="anastasis_gtk_recovery_progress_tree_selection"/>
- </child>
+ <object class="GtkColumnView" id="anastasis_gtk_recovery_progress_treeview">
+ <property name="model">anastasis_gtk_recovery_progress_tree_selection</property>
<child>
- <object class="GtkTreeViewColumn" id="anastasis_gtk_recovery_progress_description_colum">
- <property name="spacing">5</property>
- <property name="sizing">autosize</property>
+ <object class="GtkColumnViewColumn" id="anastasis_gtk_recovery_progress_description_colum">
+ <property name="expand">1</property>
<property name="title" translatable="1">Recover a secret</property>
- <child>
- <object class="GtkCellRendererText" id="anastasis_gtk_recovery_progress_description">
- <property name="ypad">15</property>
+ <property name="factory">
+ <object class="GtkSignalListItemFactory">
+ <signal name="setup" handler="AG_cell_setup_progress"/>
+ <signal name="bind" handler="AG_cell_bind_progress"/>
+ <signal name="unbind" handler="AG_cell_unbind"/>
</object>
- <attributes>
- <attribute name="text">0</attribute>
- </attributes>
- </child>
+ </property>
</object>
</child>
<child>
@@ -576,26 +476,20 @@ Author: Christian Grothoff, Dennis Neufeld-->
<property name="hexpand">1</property>
<property name="focusable">1</property>
<property name="child">
- <object class="GtkTreeView" id="anastasis_gtk_continent_treeview">
+ <object class="GtkColumnView" id="anastasis_gtk_continent_treeview">
<property name="focusable">1</property>
- <property name="model">continent_liststore</property>
- <property name="enable-search">0</property>
- <property name="search-column">0</property>
- <property name="activate-on-single-click">1</property>
- <child internal-child="selection">
- <object class="GtkTreeSelection" id="anastasis_gtk_continent_selection">
- <signal name="changed" handler="anastasis_gtk_continent_selection_changed_cb" swapped="no"/>
- </object>
- </child>
+ <property name="model">anastasis_gtk_continent_selection</property>
<child>
- <object class="GtkTreeViewColumn" id="continent_column">
+ <object class="GtkColumnViewColumn" id="continent_column">
+ <property name="expand">1</property>
<property name="title" translatable="1">Continent</property>
- <child>
- <object class="GtkCellRendererText" id="continent_name_cell_renderer"/>
- <attributes>
- <attribute name="text">1</attribute>
- </attributes>
- </child>
+ <property name="factory">
+ <object class="GtkSignalListItemFactory">
+ <signal name="setup" handler="AG_cell_setup_text"/>
+ <signal name="bind" handler="AG_cell_bind_continent"/>
+ <signal name="unbind" handler="AG_cell_unbind"/>
+ </object>
+ </property>
</object>
</child>
</object>
@@ -610,26 +504,20 @@ Author: Christian Grothoff, Dennis Neufeld-->
<property name="hexpand">1</property>
<property name="focusable">1</property>
<property name="child">
- <object class="GtkTreeView" id="anastasis_gtk_country_treeview">
+ <object class="GtkColumnView" id="anastasis_gtk_country_treeview">
<property name="focusable">1</property>
- <property name="model">country_liststore</property>
- <property name="enable-search">0</property>
- <property name="search-column">0</property>
- <property name="activate-on-single-click">1</property>
- <child internal-child="selection">
- <object class="GtkTreeSelection" id="anastasis_gtk_country_selection">
- <signal name="changed" handler="anastasis_gtk_country_selection_changed_cb" swapped="no"/>
- </object>
- </child>
+ <property name="model">anastasis_gtk_country_selection</property>
<child>
- <object class="GtkTreeViewColumn" id="country_column">
+ <object class="GtkColumnViewColumn" id="country_column">
+ <property name="expand">1</property>
<property name="title" translatable="1">Country</property>
- <child>
- <object class="GtkCellRendererText" id="country_name_cell_renderer"/>
- <attributes>
- <attribute name="text">0</attribute>
- </attributes>
- </child>
+ <property name="factory">
+ <object class="GtkSignalListItemFactory">
+ <signal name="setup" handler="AG_cell_setup_text"/>
+ <signal name="bind" handler="AG_cell_bind_country"/>
+ <signal name="unbind" handler="AG_cell_unbind"/>
+ </object>
+ </property>
</object>
</child>
</object>
@@ -1439,43 +1327,32 @@ with <a href="https://www.ngi.eu/ngi-projects/ledger/">funding&l
<object class="GtkScrolledWindow">
<property name="focusable">1</property>
<property name="child">
- <object class="GtkTreeView" id="anastasis_gtk_authentication_methods_list">
+ <object class="GtkColumnView" id="anastasis_gtk_authentication_methods_list">
<property name="focusable">1</property>
- <property name="model">authentication_methods_liststore</property>
- <property name="enable-search">0</property>
- <property name="search-column">0</property>
- <child internal-child="selection">
- <object class="GtkTreeSelection" id="anastasis_gtk_authentication_methods_selection">
- <signal name="changed" handler="auth_method_selection_changed_cb" swapped="no"/>
- </object>
- </child>
+ <property name="model">anastasis_gtk_authentication_methods_selection</property>
<child>
- <object class="GtkTreeViewColumn" id="auth_method_type">
- <property name="sizing">autosize</property>
+ <object class="GtkColumnViewColumn" id="auth_method_type">
<property name="title" translatable="1">Type</property>
- <property name="clickable">1</property>
- <property name="reorderable">1</property>
- <property name="sort-indicator">1</property>
- <property name="sort-column-id">0</property>
- <child>
- <object class="GtkCellRendererText" id="type"/>
- <attributes>
- <attribute name="text">0</attribute>
- </attributes>
- </child>
+ <property name="factory">
+ <object class="GtkSignalListItemFactory">
+ <signal name="setup" handler="AG_cell_setup_text"/>
+ <signal name="bind" handler="AG_cell_bind_auth_type"/>
+ <signal name="unbind" handler="AG_cell_unbind"/>
+ </object>
+ </property>
</object>
</child>
<child>
- <object class="GtkTreeViewColumn" id="auth_method_visual">
- <property name="sizing">autosize</property>
+ <object class="GtkColumnViewColumn" id="auth_method_visual">
<property name="title" translatable="1">Details</property>
<property name="expand">1</property>
- <child>
- <object class="GtkCellRendererText" id="visualization"/>
- <attributes>
- <attribute name="text">1</attribute>
- </attributes>
- </child>
+ <property name="factory">
+ <object class="GtkSignalListItemFactory">
+ <signal name="setup" handler="AG_cell_setup_text"/>
+ <signal name="bind" handler="AG_cell_bind_auth_details"/>
+ <signal name="unbind" handler="AG_cell_unbind"/>
+ </object>
+ </property>
</object>
</child>
</object>
@@ -1619,78 +1496,56 @@ You can also use "back" to add additional authentication methods.</pro
<property name="vexpand">1</property>
<property name="focusable">1</property>
<property name="child">
- <object class="GtkTreeView" id="anastasis_gtk_review_policy_treeview">
+ <object class="GtkColumnView" id="anastasis_gtk_review_policy_treeview">
<property name="focusable">1</property>
- <property name="model">policy_review_treestore</property>
- <property name="search-column">0</property>
- <signal name="row-activated" handler="anastasis_gtk_review_policy_treeview_row_activated_cb" swapped="no"/>
- <child internal-child="selection">
- <object class="GtkTreeSelection"/>
- </child>
+ <property name="model">anastasis_gtk_review_policy_selection</property>
<child>
- <object class="GtkTreeViewColumn" id="policy_name_column0">
- <property name="spacing">5</property>
- <property name="sizing">autosize</property>
+ <object class="GtkColumnViewColumn" id="policy_name_column0">
<property name="title" translatable="1">Policy</property>
- <child>
- <object class="GtkCellRendererText" id="policy_name_column"/>
- <attributes>
- <attribute name="text">0</attribute>
- </attributes>
- </child>
+ <property name="factory">
+ <object class="GtkSignalListItemFactory">
+ <signal name="setup" handler="AG_cell_setup_policy_name"/>
+ <signal name="bind" handler="AG_cell_bind_policy_name"/>
+ <signal name="unbind" handler="AG_cell_unbind"/>
+ </object>
+ </property>
</object>
</child>
<child>
- <object class="GtkTreeViewColumn" id="challenge_type_column">
- <property name="spacing">5</property>
- <property name="sizing">autosize</property>
+ <object class="GtkColumnViewColumn" id="challenge_type_column">
<property name="title" translatable="1">Type</property>
- <child>
- <object class="GtkCellRendererText" id="challenge_type_column1"/>
- <attributes>
- <attribute name="text">1</attribute>
- </attributes>
- </child>
+ <property name="factory">
+ <object class="GtkSignalListItemFactory">
+ <signal name="setup" handler="AG_cell_setup_text"/>
+ <signal name="bind" handler="AG_cell_bind_policy_type"/>
+ <signal name="unbind" handler="AG_cell_unbind"/>
+ </object>
+ </property>
</object>
</child>
<child>
- <object class="GtkTreeViewColumn" id="cost_column">
- <property name="spacing">5</property>
- <property name="sizing">autosize</property>
+ <object class="GtkColumnViewColumn" id="cost_column">
<property name="title" translatable="1">Cost</property>
- <child>
- <object class="GtkCellRendererText" id="cost_text_column"/>
- <attributes>
- <attribute name="text">2</attribute>
- </attributes>
- </child>
+ <property name="factory">
+ <object class="GtkSignalListItemFactory">
+ <signal name="setup" handler="AG_cell_setup_text"/>
+ <signal name="bind" handler="AG_cell_bind_policy_cost"/>
+ <signal name="unbind" handler="AG_cell_unbind"/>
+ </object>
+ </property>
</object>
</child>
<child>
- <object class="GtkTreeViewColumn" id="provider_column">
- <property name="spacing">5</property>
- <property name="sizing">autosize</property>
+ <object class="GtkColumnViewColumn" id="provider_column">
+ <property name="expand">1</property>
<property name="title" translatable="1">Provider</property>
- <child>
- <object class="GtkCellRendererText" id="provider_url_column"/>
- <attributes>
- <attribute name="text">9</attribute>
- </attributes>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkTreeViewColumn" id="expiration_column">
- <property name="visible">0</property>
- <property name="spacing">5</property>
- <property name="sizing">autosize</property>
- <property name="title" translatable="1">Expiration</property>
- <child>
- <object class="GtkCellRendererText" id="expiration_time_str"/>
- <attributes>
- <attribute name="text">4</attribute>
- </attributes>
- </child>
+ <property name="factory">
+ <object class="GtkSignalListItemFactory">
+ <signal name="setup" handler="AG_cell_setup_text"/>
+ <signal name="bind" handler="AG_cell_bind_policy_provider"/>
+ <signal name="unbind" handler="AG_cell_unbind"/>
+ </object>
+ </property>
</object>
</child>
<child>
@@ -1704,6 +1559,7 @@ You can also use "back" to add additional authentication methods.</pro
<signal name="key-pressed" handler="anastasis_gtk_review_policy_treeview_key_pressed_cb"/>
</object>
</child>
+ <signal name="activate" handler="anastasis_gtk_review_policy_treeview_row_activated_cb"/>
</object>
</property>
<property name="has-frame">1</property>
@@ -2177,45 +2033,37 @@ date of your secret's recovery data.</property>
<property name="orientation">vertical</property>
<property name="spacing">5</property>
<child>
- <object class="GtkTreeView" id="unpaid_qr_treeview">
+ <object class="GtkColumnView" id="unpaid_qr_treeview">
<property name="vexpand">1</property>
<property name="focusable">1</property>
- <property name="model">unpaid_qrcodes_liststore</property>
- <property name="enable-search">0</property>
- <property name="search-column">1</property>
- <child internal-child="selection">
- <object class="GtkTreeSelection" id="unpaid_qr_tree_selection">
- <signal name="changed" handler="unpaid_qr_tree_selection_changed_cb" swapped="no"/>
- </object>
- </child>
+ <property name="margin-top">5</property>
+ <property name="margin-bottom">5</property>
+ <property name="model">unpaid_qr_tree_selection</property>
<child>
- <object class="GtkTreeViewColumn">
- <property name="spacing">10</property>
- <property name="sizing">autosize</property>
+ <object class="GtkColumnViewColumn" id="unpaid_qr_image_column">
<property name="title" translatable="1">Payment QR Code</property>
<property name="expand">1</property>
- <child>
- <object class="GtkCellRendererPixbuf" id="unpaid_pixbuf"/>
- <attributes>
- <attribute name="pixbuf">0</attribute>
- </attributes>
- </child>
+ <property name="factory">
+ <object class="GtkSignalListItemFactory">
+ <signal name="setup" handler="AG_cell_setup_qr_image"/>
+ <signal name="bind" handler="AG_cell_bind_qr_image"/>
+ <signal name="unbind" handler="AG_cell_unbind"/>
+ </object>
+ </property>
</object>
</child>
<child>
- <object class="GtkTreeViewColumn" id="Pay URI">
- <property name="sizing">autosize</property>
+ <object class="GtkColumnViewColumn" id="pay_uri_column">
<property name="title" translatable="1">Taler Pay URI</property>
- <child>
- <object class="GtkCellRendererText" id="pay_uri"/>
- <attributes>
- <attribute name="text">1</attribute>
- </attributes>
- </child>
+ <property name="factory">
+ <object class="GtkSignalListItemFactory">
+ <signal name="setup" handler="AG_cell_setup_text"/>
+ <signal name="bind" handler="AG_cell_bind_qr_uri"/>
+ <signal name="unbind" handler="AG_cell_unbind"/>
+ </object>
+ </property>
</object>
</child>
- <property name="margin-top">5</property>
- <property name="margin-bottom">5</property>
</object>
</child>
</object>
@@ -2317,50 +2165,32 @@ date of your secret's recovery data.</property>
<property name="vexpand">1</property>
<property name="focusable">1</property>
<property name="child">
- <object class="GtkTreeView" id="anastasis_gtk_backup_complete_treeview">
+ <object class="GtkColumnView" id="anastasis_gtk_backup_complete_treeview">
<property name="focusable">1</property>
- <property name="model">backup_provider_liststore</property>
- <property name="search-column">0</property>
- <child internal-child="selection">
- <object class="GtkTreeSelection"/>
- </child>
+ <property name="model">anastasis_gtk_backup_complete_selection</property>
<child>
- <object class="GtkTreeViewColumn">
- <property name="sizing">autosize</property>
+ <object class="GtkColumnViewColumn" id="backup_provider_column">
<property name="title" translatable="1">Provider</property>
- <child>
- <object class="GtkCellRendererText" id="provider_url"/>
- <attributes>
- <attribute name="text">4</attribute>
- </attributes>
- </child>
+ <property name="factory">
+ <object class="GtkSignalListItemFactory">
+ <signal name="setup" handler="AG_cell_setup_text"/>
+ <signal name="bind" handler="AG_cell_bind_backup_provider"/>
+ <signal name="unbind" handler="AG_cell_unbind"/>
+ </object>
+ </property>
</object>
</child>
<child>
- <object class="GtkTreeViewColumn">
- <property name="spacing">5</property>
- <property name="sizing">autosize</property>
+ <object class="GtkColumnViewColumn" id="backup_version_column">
+ <property name="expand">1</property>
<property name="title" translatable="1">Version</property>
- <child>
- <object class="GtkCellRendererText" id="policy_version"/>
- <attributes>
- <attribute name="visible">3</attribute>
- <attribute name="text">1</attribute>
- </attributes>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkTreeViewColumn">
- <property name="visible">0</property>
- <property name="title" translatable="1">Expiration time</property>
- <child>
- <object class="GtkCellRendererText" id="expiration"/>
- <attributes>
- <attribute name="visible">3</attribute>
- <attribute name="text">2</attribute>
- </attributes>
- </child>
+ <property name="factory">
+ <object class="GtkSignalListItemFactory">
+ <signal name="setup" handler="AG_cell_setup_text"/>
+ <signal name="bind" handler="AG_cell_bind_backup_version"/>
+ <signal name="unbind" handler="AG_cell_unbind"/>
+ </object>
+ </property>
</object>
</child>
</object>
@@ -2582,91 +2412,39 @@ explicitly add the provider which you used before for the backup.</property>
<property name="focusable">1</property>
<property name="hadjustment">secret_selection_h_adjustment</property>
<property name="vadjustment">secret_selection_v_adjustment</property>
+ <property name="has-frame">1</property>
<property name="child">
- <object class="GtkViewport" id="anastasis_secret_selection_viewport">
- <property name="hadjustment">secret_selection_h_adjustment</property>
- <property name="vadjustment">secret_selection_v_adjustment</property>
- <property name="child">
- <object class="GtkTreeView" id="anastasis_secret_selection_treeview">
- <property name="focusable">1</property>
- <property name="hadjustment">secret_selection_h_adjustment</property>
- <property name="vadjustment">secret_selection_v_adjustment</property>
- <property name="model">secret_selection_liststore</property>
- <child internal-child="selection">
- <object class="GtkTreeSelection" id="anastasis_gtk_secret_selection_treeselection">
- <signal name="changed" handler="anastasis_gtk_secret_selection_treeselection_changed_cb" swapped="no"/>
- </object>
- </child>
- <child>
- <object class="GtkTreeViewColumn" id="secret_name_column">
- <property name="resizable">1</property>
- <property name="sizing">autosize</property>
- <property name="title" translatable="1">Secret name</property>
- <property name="clickable">1</property>
- <property name="sort-indicator">1</property>
- <property name="sort-column-id">3</property>
- <child>
- <object class="GtkCellRendererText" id="backup_name_renderer"/>
- <attributes>
- <attribute name="text">3</attribute>
- </attributes>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkTreeViewColumn" id="backup_time_column">
- <property name="sizing">fixed</property>
- <property name="title" translatable="1">Backup time</property>
- <property name="clickable">1</property>
- <property name="sort-indicator">1</property>
- <property name="sort-column-id">5</property>
- <child>
- <object class="GtkCellRendererText" id="backup_time_renderer"/>
- <attributes>
- <attribute name="text">4</attribute>
- </attributes>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkTreeViewColumn" id="backup_version_column">
- <property name="visible">0</property>
- <property name="title" translatable="1">Version</property>
- <property name="clickable">1</property>
- <property name="sort-indicator">1</property>
- <property name="sort-order">descending</property>
- <property name="sort-column-id">1</property>
- <child>
- <object class="GtkCellRendererText" id="backup_version_renderer">
- <property name="visible">0</property>
- </object>
- <attributes>
- <attribute name="text">1</attribute>
- </attributes>
- </child>
+ <object class="GtkColumnView" id="anastasis_secret_selection_treeview">
+ <property name="focusable">1</property>
+ <property name="model">anastasis_gtk_secret_selection_treeselection</property>
+ <child>
+ <object class="GtkColumnViewColumn" id="secret_name_column">
+ <property name="title" translatable="1">Secret name</property>
+ <property name="resizable">1</property>
+ <property name="factory">
+ <object class="GtkSignalListItemFactory">
+ <signal name="setup" handler="AG_cell_setup_text"/>
+ <signal name="bind" handler="AG_cell_bind_secret_name"/>
+ <signal name="unbind" handler="AG_cell_unbind"/>
</object>
- </child>
- <child>
- <object class="GtkTreeViewColumn" id="provider_url_column1">
- <property name="visible">0</property>
- <property name="resizable">1</property>
- <property name="title" translatable="1">Provider</property>
- <property name="clickable">1</property>
- <child>
- <object class="GtkCellRendererText" id="provider_url_renderer">
- <property name="visible">0</property>
- </object>
- <attributes>
- <attribute name="text">0</attribute>
- </attributes>
- </child>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkColumnViewColumn" id="backup_time_column">
+ <property name="expand">1</property>
+ <property name="title" translatable="1">Backup time</property>
+ <property name="factory">
+ <object class="GtkSignalListItemFactory">
+ <signal name="setup" handler="AG_cell_setup_text"/>
+ <signal name="bind" handler="AG_cell_bind_secret_time"/>
+ <signal name="unbind" handler="AG_cell_unbind"/>
</object>
- </child>
+ </property>
</object>
- </property>
+ </child>
</object>
</property>
- <property name="has-frame">1</property>
</object>
</child>
</object>
@@ -2837,4 +2615,3 @@ explicitly add the provider which you used before for the backup.</property>
<property name="default-widget">anastasis_gtk_main_window_forward_button</property>
</object>
</interface>
-
diff --git a/contrib/anastasis_gtk_open_file_dialog.ui b/contrib/anastasis_gtk_open_file_dialog.ui
@@ -1,106 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-Copyright (C) 2019-2021 Anastasis SARL
-
-This file is part of anastasis-gtk.
-
-anastasis-gtk is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-anastasis-gtk is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with anastasis-gtk. If not, see <http://www.gnu.org/licenses/>.
-
-Author: Christian Grothoff-->
-<interface>
- <requires lib="gtk" version="4.0"/>
- <object class="GtkFileFilter" id="anastasis_file_filter">
- <patterns>
- <pattern>*.ana</pattern>
- </patterns>
- </object>
- <object class="GtkFileChooserDialog" id="open_file_dialog">
- <property name="modal">1</property>
- <property name="destroy-with-parent">1</property>
- <property name="filter">anastasis_file_filter</property>
- <property name="default-widget">button1</property>
- <signal name="response" handler="open_directory_dialog_response_cb" swapped="no"/>
- <child internal-child="content_area">
- <object class="GtkBox">
- <property name="orientation">vertical</property>
- <property name="spacing">2</property>
- <child internal-child="action_area">
- <object class="GtkBox">
- <property name="valign">center</property>
- <property name="can-focus">0</property>
- <property name="homogeneous">1</property>
- <property name="spacing">6</property>
- <property name="halign">end</property>
- <child>
- <object class="GtkButton" id="button2">
- <property name="focusable">1</property>
- <property name="receives-default">1</property>
- <property name="child">
- <object class="GtkBox">
- <property name="spacing">2</property>
- <property name="halign">center</property>
- <child>
- <object class="GtkImage">
- <property name="icon-name">gtk-cancel</property>
- </object>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="label" translatable="1">_Cancel</property>
- <property name="use-underline">1</property>
- <property name="mnemonic-widget">button2</property>
- </object>
- </child>
- </object>
- </property>
- </object>
- </child>
- <child>
- <object class="GtkButton" id="button1">
- <property name="focusable">1</property>
- <property name="receives-default">1</property>
- <property name="child">
- <object class="GtkBox">
- <property name="spacing">2</property>
- <property name="halign">center</property>
- <child>
- <object class="GtkImage">
- <property name="icon-name">document-open</property>
- </object>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="label" translatable="1">_Open</property>
- <property name="use-underline">1</property>
- <property name="mnemonic-widget">button1</property>
- </object>
- </child>
- </object>
- </property>
- </object>
- </child>
- </object>
- </child>
- <child>
- <placeholder/>
- </child>
- </object>
- </child>
- <action-widgets>
- <action-widget response="-6">button2</action-widget>
- <action-widget response="-5">button1</action-widget>
- </action-widgets>
- </object>
-</interface>
-
diff --git a/contrib/anastasis_gtk_open_secret_dialog.ui b/contrib/anastasis_gtk_open_secret_dialog.ui
@@ -1,100 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-Copyright (C) 2019-2021 Anastasis SARL
-
-This file is part of anastasis-gtk.
-
-anastasis-gtk is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-anastasis-gtk is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with anastasis-gtk. If not, see <http://www.gnu.org/licenses/>.
-
-Author: Christian Grothoff-->
-<interface>
- <requires lib="gtk" version="4.0"/>
- <object class="GtkFileChooserDialog" id="open_file_dialog">
- <property name="modal">1</property>
- <property name="destroy-with-parent">1</property>
- <property name="default-widget">button1</property>
- <signal name="response" handler="open_secret_dialog_response_cb" swapped="no"/>
- <child internal-child="content_area">
- <object class="GtkBox">
- <property name="orientation">vertical</property>
- <property name="spacing">2</property>
- <child internal-child="action_area">
- <object class="GtkBox">
- <property name="valign">center</property>
- <property name="can-focus">0</property>
- <property name="homogeneous">1</property>
- <property name="spacing">6</property>
- <property name="halign">end</property>
- <child>
- <object class="GtkButton" id="button2">
- <property name="focusable">1</property>
- <property name="receives-default">1</property>
- <property name="child">
- <object class="GtkBox">
- <property name="spacing">2</property>
- <property name="halign">center</property>
- <child>
- <object class="GtkImage">
- <property name="icon-name">gtk-cancel</property>
- </object>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="label" translatable="1">_Cancel</property>
- <property name="use-underline">1</property>
- <property name="mnemonic-widget">button2</property>
- </object>
- </child>
- </object>
- </property>
- </object>
- </child>
- <child>
- <object class="GtkButton" id="button1">
- <property name="focusable">1</property>
- <property name="receives-default">1</property>
- <property name="child">
- <object class="GtkBox">
- <property name="spacing">2</property>
- <property name="halign">center</property>
- <child>
- <object class="GtkImage">
- <property name="icon-name">document-open</property>
- </object>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="label" translatable="1">_Open</property>
- <property name="use-underline">1</property>
- <property name="mnemonic-widget">button1</property>
- </object>
- </child>
- </object>
- </property>
- </object>
- </child>
- </object>
- </child>
- <child>
- <placeholder/>
- </child>
- </object>
- </child>
- <action-widgets>
- <action-widget response="-6">button2</action-widget>
- <action-widget response="-5">button1</action-widget>
- </action-widgets>
- </object>
-</interface>
-
diff --git a/contrib/anastasis_gtk_save_file_dialog.ui b/contrib/anastasis_gtk_save_file_dialog.ui
@@ -1,105 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-Copyright (C) 2019-2021 Anastasis SARL
-
-This file is part of anastasis-gtk.
-
-anastasis-gtk is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-anastasis-gtk is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with anastasis-gtk. If not, see <http://www.gnu.org/licenses/>.
-
-Author: Christian Grothoff-->
-<interface>
- <requires lib="gtk" version="4.0"/>
- <object class="GtkFileFilter" id="anastasis_file_filter">
- <patterns>
- <pattern>*.ana</pattern>
- </patterns>
- </object>
- <object class="GtkFileChooserDialog" id="save_file_dialog">
- <property name="action">save</property>
- <property name="filter">anastasis_file_filter</property>
- <property name="default-widget">button2</property>
- <signal name="response" handler="save_directory_dialog_response_cb" swapped="no"/>
- <child internal-child="content_area">
- <object class="GtkBox">
- <property name="orientation">vertical</property>
- <property name="spacing">2</property>
- <child internal-child="action_area">
- <object class="GtkBox">
- <property name="valign">center</property>
- <property name="can-focus">0</property>
- <property name="homogeneous">1</property>
- <property name="spacing">6</property>
- <property name="halign">end</property>
- <child>
- <object class="GtkButton" id="button1">
- <property name="focusable">1</property>
- <property name="receives-default">1</property>
- <property name="child">
- <object class="GtkBox">
- <property name="spacing">2</property>
- <property name="halign">center</property>
- <child>
- <object class="GtkImage">
- <property name="icon-name">gtk-cancel</property>
- </object>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="label" translatable="1">_Cancel</property>
- <property name="use-underline">1</property>
- <property name="mnemonic-widget">button1</property>
- </object>
- </child>
- </object>
- </property>
- </object>
- </child>
- <child>
- <object class="GtkButton" id="button2">
- <property name="focusable">1</property>
- <property name="receives-default">1</property>
- <property name="child">
- <object class="GtkBox">
- <property name="spacing">2</property>
- <property name="halign">center</property>
- <child>
- <object class="GtkImage">
- <property name="icon-name">document-save</property>
- </object>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="label" translatable="1">_Save</property>
- <property name="use-underline">1</property>
- <property name="mnemonic-widget">button2</property>
- </object>
- </child>
- </object>
- </property>
- </object>
- </child>
- </object>
- </child>
- <child>
- <placeholder/>
- </child>
- </object>
- </child>
- <action-widgets>
- <action-widget response="-6">button1</action-widget>
- <action-widget response="-3">button2</action-widget>
- </action-widgets>
- </object>
-</interface>
-
diff --git a/contrib/anastasis_gtk_save_secret_dialog.ui b/contrib/anastasis_gtk_save_secret_dialog.ui
@@ -1,105 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-Copyright (C) 2019-2021 Anastasis SARL
-
-This file is part of anastasis-gtk.
-
-anastasis-gtk is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-anastasis-gtk is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with anastasis-gtk. If not, see <http://www.gnu.org/licenses/>.
-
-Author: Christian Grothoff-->
-<interface>
- <requires lib="gtk" version="4.0"/>
- <object class="GtkFileFilter" id="anastasis_file_filter">
- <patterns>
- <pattern>*.secret</pattern>
- </patterns>
- </object>
- <object class="GtkFileChooserDialog" id="save_file_dialog">
- <property name="action">save</property>
- <property name="filter">anastasis_file_filter</property>
- <property name="default-widget">button2</property>
- <signal name="response" handler="save_secret_dialog_response_cb" swapped="no"/>
- <child internal-child="content_area">
- <object class="GtkBox">
- <property name="orientation">vertical</property>
- <property name="spacing">2</property>
- <child internal-child="action_area">
- <object class="GtkBox">
- <property name="valign">center</property>
- <property name="can-focus">0</property>
- <property name="homogeneous">1</property>
- <property name="spacing">6</property>
- <property name="halign">end</property>
- <child>
- <object class="GtkButton" id="button1">
- <property name="focusable">1</property>
- <property name="receives-default">1</property>
- <property name="child">
- <object class="GtkBox">
- <property name="spacing">2</property>
- <property name="halign">center</property>
- <child>
- <object class="GtkImage">
- <property name="icon-name">gtk-cancel</property>
- </object>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="label" translatable="1">_Cancel</property>
- <property name="use-underline">1</property>
- <property name="mnemonic-widget">button1</property>
- </object>
- </child>
- </object>
- </property>
- </object>
- </child>
- <child>
- <object class="GtkButton" id="button2">
- <property name="focusable">1</property>
- <property name="receives-default">1</property>
- <property name="child">
- <object class="GtkBox">
- <property name="spacing">2</property>
- <property name="halign">center</property>
- <child>
- <object class="GtkImage">
- <property name="icon-name">document-save</property>
- </object>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="label" translatable="1">_Save</property>
- <property name="use-underline">1</property>
- <property name="mnemonic-widget">button2</property>
- </object>
- </child>
- </object>
- </property>
- </object>
- </child>
- </object>
- </child>
- <child>
- <placeholder/>
- </child>
- </object>
- </child>
- <action-widgets>
- <action-widget response="-6">button1</action-widget>
- <action-widget response="-3">button2</action-widget>
- </action-widgets>
- </object>
-</interface>
-
diff --git a/contrib/anastasis_gtk_warn_multifactor.ui b/contrib/anastasis_gtk_warn_multifactor.ui
@@ -1,70 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<interface>
- <requires lib="gtk" version="4.0"/>
- <object class="GtkMessageDialog" id="anastasis_gtk_confirm_multifactor_dialog">
- <property name="title" translatable="1">Confirm use of few authentication methods.</property>
- <property name="modal">1</property>
- <property name="message-type">warning</property>
- <property name="text" translatable="1">To keep your secret secure, we recommend you specify at least three authentication options before proceeding.</property>
- <property name="secondary-text" translatable="1">Select "back" to add additional authentication options.</property>
- <property name="focus-widget">next_button</property>
- <signal name="response" handler="anastasis_gtk_confirm_multifactor_dialog_response_cb" swapped="no"/>
- <child internal-child="content_area">
- <object class="GtkBox">
- <property name="orientation">vertical</property>
- <property name="spacing">2</property>
- <child internal-child="action_area">
- <object class="GtkBox">
- <property name="valign">center</property>
- <property name="can-focus">0</property>
- <property name="homogeneous">1</property>
- <property name="spacing">6</property>
- <property name="halign">end</property>
- <child>
- <object class="GtkButton" id="back_button">
- <property name="label" translatable="1">_Back</property>
- <property name="focusable">1</property>
- <property name="receives-default">1</property>
- <property name="tooltip-text" translatable="1">Go back to the previous dialog to add more authentication options.</property>
- <property name="margin-start">5</property>
- <property name="margin-end">5</property>
- <property name="use-underline">1</property>
- </object>
- </child>
- <child>
- <object class="GtkButton" id="next_button">
- <property name="focusable">1</property>
- <property name="tooltip-text" translatable="1">Proceed without adding additional authentication options.</property>
- <property name="margin-start">5</property>
- <property name="margin-end">5</property>
- <property name="child">
- <object class="GtkBox">
- <property name="spacing">2</property>
- <property name="halign">center</property>
- <child>
- <object class="GtkImage">
- <property name="icon-name">media-skip-forward</property>
- </object>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="label" translatable="1">_Next</property>
- <property name="use-underline">1</property>
- <property name="mnemonic-widget">next_button</property>
- </object>
- </child>
- </object>
- </property>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <action-widgets>
- <action-widget response="-6">back_button</action-widget>
- <action-widget response="-5">next_button</action-widget>
- </action-widgets>
- </object>
-</interface>
-
diff --git a/contrib/meson.build b/contrib/meson.build
@@ -16,17 +16,11 @@ install_data(
'anastasis_gtk_challenge_question.ui',
'anastasis_gtk_challenge_template.ui',
'anastasis_gtk_challenge_totp.ui',
- 'anastasis_gtk_deny_singlefactor.ui',
'anastasis_gtk_edit_providers.ui',
'anastasis_gtk_edit_policy.ui',
'anastasis_gtk_main_window.ui',
- 'anastasis_gtk_open_file_dialog.ui',
- 'anastasis_gtk_open_secret_dialog.ui',
'anastasis_gtk_policy_template.ui',
- 'anastasis_gtk_save_file_dialog.ui',
- 'anastasis_gtk_save_secret_dialog.ui',
'anastasis_gtk_user_sure.ui',
- 'anastasis_gtk_warn_multifactor.ui',
'bandiera_stelle.png',
'checkmark.svgz',
'freeotp.png',
diff --git a/po/POTFILES.in b/po/POTFILES.in
@@ -2,6 +2,7 @@ src/anastasis/anastasis-gtk_action.c
src/anastasis/anastasis-gtk_attributes.c
src/anastasis/anastasis-gtk_autocomplete.c
src/anastasis/anastasis-gtk.c
+src/anastasis/anastasis-gtk_cells.c
src/anastasis/anastasis-gtk_dispatch.c
src/anastasis/anastasis-gtk_handle-add-provider.c
src/anastasis/anastasis-gtk_handle-auth-delete-button-clicked.c
@@ -16,7 +17,6 @@ src/anastasis/anastasis-gtk_handle-continent-selected.c
src/anastasis/anastasis-gtk_handle-core-secret-changed.c
src/anastasis/anastasis-gtk_handle-core-secret-name-changed.c
src/anastasis/anastasis-gtk_handle-country-activated.c
-src/anastasis/anastasis-gtk_handle-country-unselected.c
src/anastasis/anastasis-gtk_handle-expiration-change.c
src/anastasis/anastasis-gtk_handle-identity-changed.c
src/anastasis/anastasis-gtk_handle-main-window-back-clicked.c
@@ -44,6 +44,7 @@ src/anastasis/anastasis-gtk_pe-delete-challenge.c
src/anastasis/anastasis-gtk_pe-delete-policy.c
src/anastasis/anastasis-gtk_pe-edit-policy.c
src/anastasis/anastasis-gtk_progress.c
+src/anastasis/anastasis-gtk_rows.c
src/anastasis/print.c
src/anastasis/test-print.c
src/util/builder.c
@@ -63,15 +64,9 @@ contrib/anastasis_gtk_challenge_iban.ui
contrib/anastasis_gtk_challenge_question.ui
contrib/anastasis_gtk_challenge_template.ui
contrib/anastasis_gtk_challenge_totp.ui
-contrib/anastasis_gtk_deny_singlefactor.ui
contrib/anastasis_gtk_edit_policy.ui
contrib/anastasis_gtk_edit_providers.ui
contrib/anastasis_gtk_main_window.ui
-contrib/anastasis_gtk_open_file_dialog.ui
-contrib/anastasis_gtk_open_secret_dialog.ui
contrib/anastasis_gtk_policy_template.ui
-contrib/anastasis_gtk_save_file_dialog.ui
-contrib/anastasis_gtk_save_secret_dialog.ui
contrib/anastasis_gtk_user_sure.ui
-contrib/anastasis_gtk_warn_multifactor.ui
contrib/this_stays_private.ui
diff --git a/src/anastasis/anastasis-gtk.c b/src/anastasis/anastasis-gtk.c
@@ -28,6 +28,8 @@
#include "anastasis_gtk_util.h"
#include "anastasis-gtk_action.h"
#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_progress.h"
+#include "anastasis-gtk_rows.h"
#include <jansson.h>
/**
@@ -223,11 +225,16 @@ run (void *cls)
AG_entry_attributes = GNUNET_CONTAINER_multihashmap_create (16,
GNUNET_NO);
ANASTASIS_GTK_setup_nls ();
+ /* the UI files name AGRow as the item type of their models, so the type
+ has to be registered before GtkBuilder parses them */
+ g_type_ensure (AG_TYPE_ROW);
if (GNUNET_OK !=
ANASTASIS_GTK_main_loop_build_window (ANASTASIS_GTK_project_data (),
AG_ml,
NULL))
return;
+ AG_lists_setup ();
+ AG_progress_setup ();
AG_cfg = ANASTASIS_GTK_main_loop_get_gtk_configuration (AG_ml);
ANASTASIS_GTK_main_loop_get_args (AG_ml,
&argc,
diff --git a/src/anastasis/anastasis-gtk_action.c b/src/anastasis/anastasis-gtk_action.c
@@ -32,6 +32,7 @@
#include "anastasis-gtk_helper.h"
#include "anastasis-gtk_handle-identity-changed.h"
#include "anastasis-gtk_progress.h"
+#include "anastasis-gtk_rows.h"
#include <jansson.h>
#include <qrencode.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
@@ -140,17 +141,7 @@ lookup_provider_name (const char *provider_url)
static void
set_country_selection_next (void)
{
- GtkTreeView *tv;
- GtkTreeSelection *sel;
- GtkTreeModel *model;
- GtkTreeIter iter;
-
- tv = GTK_TREE_VIEW (GCG_get_main_window_object (
- "anastasis_gtk_country_treeview"));
- sel = gtk_tree_view_get_selection (tv);
- if (gtk_tree_selection_get_selected (sel,
- &model,
- &iter))
+ if (NULL != AG_selected_row ("anastasis_gtk_country_selection"))
AG_enable_next ();
else
AG_insensitive ("anastasis_gtk_main_window_forward_button");
@@ -163,19 +154,12 @@ set_country_selection_next (void)
static void
action_continent_selecting (void)
{
- GtkListStore *country_liststore = GTK_LIST_STORE (
- GCG_get_main_window_object ("country_liststore"));
-
AG_hide_all_frames ();
- gtk_list_store_clear (country_liststore);
+ AG_list_clear ("country_liststore");
{
- GtkListStore *continent_liststore;
json_t *continents;
- continent_liststore
- = GTK_LIST_STORE (
- GCG_get_main_window_object ("continent_liststore"));
- gtk_list_store_clear (continent_liststore);
+ AG_list_clear ("continent_liststore");
continents = json_object_get (AG_redux_state,
"continents");
if (NULL != continents)
@@ -204,15 +188,13 @@ action_continent_selecting (void)
continue;
}
- gtk_list_store_insert_with_values (continent_liststore,
- NULL,
- -1,
- AG_CMC_CONTINENT_NAME,
- name,
- AG_CMC_CONTINENT_NAME_I18N,
- dgettext ("anastasis",
- name),
- -1);
+ AG_list_add ("continent_liststore",
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("name",
+ name),
+ GNUNET_JSON_pack_string ("name_i18n",
+ dgettext ("anastasis",
+ name))));
GNUNET_JSON_parse_free (spec);
}
}
@@ -246,7 +228,6 @@ action_continent_selecting (void)
static void
action_country_selecting (void)
{
- GtkListStore *country_liststore;
json_t *countries;
const char *selected_country;
@@ -256,16 +237,14 @@ action_country_selecting (void)
selected_country
= json_string_value (json_object_get (AG_redux_state,
"selected_country"));
- country_liststore = GTK_LIST_STORE (
- GCG_get_main_window_object ("country_liststore"));
- gtk_list_store_clear (country_liststore);
+ AG_list_clear ("country_liststore");
{
json_t *country;
size_t index;
+ guint position = 0;
json_array_foreach (countries, index, country)
{
- GtkTreeIter iter;
const char *code;
const char *name;
struct GNUNET_JSON_Specification spec[] = {
@@ -286,30 +265,20 @@ action_country_selecting (void)
continue;
}
- gtk_list_store_insert_with_values (
- country_liststore,
- &iter,
- -1,
- AG_CCMC_COUNTRY_NAME,
- dgettext ("anastasis",
- name),
- AG_CCMC_COUNTRY_CODE,
- code,
- -1);
+ AG_list_add ("country_liststore",
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("name",
+ dgettext ("anastasis",
+ name)),
+ GNUNET_JSON_pack_string ("code",
+ code)));
if ( (NULL != selected_country) &&
(NULL != code) &&
(0 == strcmp (code,
selected_country)) )
- {
- GtkTreeView *tv;
- GtkTreeSelection *sel;
-
- tv = GTK_TREE_VIEW (GCG_get_main_window_object (
- "anastasis_gtk_country_treeview"));
- sel = gtk_tree_view_get_selection (tv);
- gtk_tree_selection_select_iter (sel,
- &iter);
- }
+ AG_select_row ("anastasis_gtk_country_selection",
+ position);
+ position++;
GNUNET_JSON_parse_free (spec);
}
}
@@ -937,14 +906,11 @@ action_authentications_editing (void)
have_auth = false;
{
- GtkListStore *ls;
json_t *ams;
size_t index;
json_t *am;
- ls = GTK_LIST_STORE (GCG_get_main_window_object (
- "authentication_methods_liststore"));
- gtk_list_store_clear (ls);
+ AG_list_clear ("authentication_methods_liststore");
ams = json_object_get (AG_redux_state,
"authentication_methods");
json_array_foreach (ams,
@@ -965,14 +931,14 @@ action_authentications_editing (void)
GNUNET_JSON_parse (am,
spec,
NULL, NULL));
- gtk_list_store_insert_with_values (
- ls,
- NULL,
- -1,
- AG_AMMC_TYPE, type,
- AG_AMMC_VISUALIZATION, instructions,
- AG_AMMC_INDEX, (guint) index,
- -1);
+ AG_list_add ("authentication_methods_liststore",
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("type",
+ type),
+ GNUNET_JSON_pack_string ("visualization",
+ instructions),
+ GNUNET_JSON_pack_uint64 ("index",
+ index)));
have_auth = true;
}
}
@@ -1078,17 +1044,19 @@ action_policies_reviewing (void)
json_t *policies;
size_t pindex;
json_t *policy;
- GtkTreeStore *ts;
+ GListStore *ts;
AG_hide_all_frames ();
- ts = GTK_TREE_STORE (GCG_get_main_window_object ("policy_review_treestore"));
- gtk_tree_store_clear (ts);
+ ts = AG_list ("policy_review_treestore");
+ if (NULL == ts)
+ return;
+ g_list_store_remove_all (ts);
policies = json_object_get (AG_redux_state,
"policies");
GNUNET_assert (NULL != policies);
json_array_foreach (policies, pindex, policy)
{
- GtkTreeIter piter;
+ AGRow *prow;
json_t *methods;
struct GNUNET_JSON_Specification pspec[] = {
GNUNET_JSON_spec_json ("methods",
@@ -1107,12 +1075,15 @@ action_policies_reviewing (void)
GNUNET_break (0);
continue;
}
- gtk_tree_store_insert_with_values (ts,
- &piter,
- NULL, /* no parent */
- -1, /* append */
- -1);
-
+ /* the row is only added to the list once its challenges are known,
+ as that is when the list learns that it can be expanded */
+ prow = AG_row_new (GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_uint64 ("policy_index",
+ pindex),
+ GNUNET_JSON_pack_bool ("is_challenge",
+ false),
+ GNUNET_JSON_pack_string ("expiration",
+ "N/A")));
summary = NULL;
json_array_foreach (methods, mindex, method)
{
@@ -1144,7 +1115,6 @@ action_policies_reviewing (void)
jmethod = json_array_get (jmethods,
imethod);
{
- GtkTreeIter miter;
const char *instructions;
const char *type;
struct GNUNET_JSON_Specification tspec[] = {
@@ -1174,30 +1144,27 @@ action_policies_reviewing (void)
continue;
}
provider_name = lookup_provider_name (provider_url);
- gtk_tree_store_insert_with_values (
- ts,
- &miter,
- &piter, /* parent */
- -1, /* append */
- AG_PRMC_POLICY_NAME,
- instructions,
- AG_PRMC_METHOD_TYPE,
- type,
- AG_PRMC_COST,
- TALER_amount2s (&method_cost),
- AG_PRMC_PROVIDER_URL,
- provider_url,
- AG_PRMC_EXPIRATION_TIME_STR,
- "N/A",
- AG_PRMC_POLICY_INDEX,
- (guint) pindex,
- AG_PRMC_IS_CHALLENGE,
- TRUE,
- AG_PRMC_METHOD_INDEX,
- (guint) mindex,
- AG_PRMC_PROVIDER_NAME,
- provider_name,
- -1);
+ AG_list_append (AG_row_children (prow),
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("policy_name",
+ instructions),
+ GNUNET_JSON_pack_string ("method_type",
+ type),
+ GNUNET_JSON_pack_string ("cost",
+ TALER_amount2s (
+ &method_cost)),
+ GNUNET_JSON_pack_string ("provider_url",
+ provider_url),
+ GNUNET_JSON_pack_string ("expiration",
+ "N/A"),
+ GNUNET_JSON_pack_uint64 ("policy_index",
+ pindex),
+ GNUNET_JSON_pack_bool ("is_challenge",
+ true),
+ GNUNET_JSON_pack_uint64 ("method_index",
+ mindex),
+ GNUNET_JSON_pack_string ("provider_name",
+ provider_name)));
if (NULL == summary)
{
summary = GNUNET_strdup (type);
@@ -1218,27 +1185,15 @@ action_policies_reviewing (void)
}
if (NULL != summary)
{
- gtk_tree_store_set (ts,
- &piter,
- AG_PRMC_POLICY_NAME, summary,
- AG_PRMC_EXPIRATION_TIME_STR,
- "N/A",
- AG_PRMC_POLICY_INDEX,
- (guint) pindex,
- AG_PRMC_IS_CHALLENGE,
- FALSE,
- -1);
+ AG_row_set (prow,
+ "policy_name",
+ json_string (summary));
GNUNET_free (summary);
}
+ AG_list_add_row (ts,
+ prow);
GNUNET_JSON_parse_free (pspec);
}
- {
- GtkTreeView *tv;
-
- tv = GTK_TREE_VIEW (GCG_get_main_window_object (
- "anastasis_gtk_review_policy_treeview"));
- gtk_tree_view_expand_all (tv);
- }
AG_sensitive ("anastasis_gtk_main_window_prev_button");
AG_show ("anastasis_gtk_progress_vbox");
AG_progress_update ();
@@ -1563,56 +1518,81 @@ action_secret_editing (void)
}
+/**
+ * Add a row with the QR code of the payment request @a payto to the list of
+ * unpaid payment requests.
+ *
+ * @param payto the "taler://pay" URI to encode
+ * @param provider the provider that wants to be paid
+ */
+static void
+add_qrcode (const char *payto,
+ const char *provider)
+{
+ GtkWidget *w;
+ GdkPixbuf *pb;
+ GListStore *list;
+ AGRow *row;
+
+ w = GTK_WIDGET (GCG_get_main_window_object ("unpaid_qr_treeview"));
+ if (NULL == w)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ pb = AG_setup_qrcode (w,
+ payto,
+ strlen (payto));
+ if (NULL == pb)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _ ("Failed to initialize QR-code pixbuf for `%s'\n"),
+ payto);
+ return;
+ }
+ list = AG_list ("unpaid_qrcodes_liststore");
+ if (NULL == list)
+ {
+ g_object_unref (pb);
+ return;
+ }
+ row = AG_row_new (GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("url",
+ payto),
+ GNUNET_JSON_pack_string ("provider",
+ provider)));
+ /* the row must know its image before it is added to the list, as the
+ list renders it right away */
+ AG_row_set_image (row,
+ GDK_PAINTABLE (gdk_texture_new_for_pixbuf (pb)));
+ g_object_unref (pb);
+ AG_list_add_row (list,
+ row);
+}
+
+
static void
action_truths_paying (void)
{
json_t *pprs;
size_t index;
json_t *pt;
- GtkListStore *ls;
AG_hide_all_frames ();
- ls = GTK_LIST_STORE (GCG_get_main_window_object (
- "unpaid_qrcodes_liststore"));
- gtk_list_store_clear (ls);
+ AG_list_clear ("unpaid_qrcodes_liststore");
pprs = json_object_get (AG_redux_state,
"payments");
json_array_foreach (pprs, index, pt)
{
const char *payto = json_string_value (pt);
- GdkPixbuf *pb;
- GtkWidget *w;
- w = GTK_WIDGET (GCG_get_main_window_object ("unpaid_qr_treeview"));
- if (NULL == w)
- {
- GNUNET_break (0);
- continue;
- }
if (NULL == payto)
{
GNUNET_break (0);
continue;
}
- pb = AG_setup_qrcode (w,
- payto,
- strlen (payto));
- if (NULL == pb)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- _ ("Failed to initialize QR-code pixbuf for `%s'\n"),
- payto);
- continue;
- }
-
- gtk_list_store_insert_with_values (ls,
- NULL,
- -1, /* append */
- AG_UQRMC_QR_IMAGE, pb,
- AG_UQRMC_URL, payto,
- AG_UQRMC_PROVIDER, "",
- -1);
- g_object_unref (pb);
+ add_qrcode (payto,
+ "");
}
{
@@ -1648,12 +1628,9 @@ action_policies_paying (void)
json_t *pprs;
size_t index;
json_t *ppr;
- GtkListStore *ls;
AG_hide_all_frames ();
- ls = GTK_LIST_STORE (GCG_get_main_window_object (
- "unpaid_qrcodes_liststore"));
- gtk_list_store_clear (ls);
+ AG_list_clear ("unpaid_qrcodes_liststore");
pprs = json_object_get (AG_redux_state,
"policy_payment_requests");
json_array_foreach (pprs, index, ppr)
@@ -1667,15 +1644,7 @@ action_policies_paying (void)
&payto),
GNUNET_JSON_spec_end ()
};
- GdkPixbuf *pb;
- GtkWidget *w;
- w = GTK_WIDGET (GCG_get_main_window_object ("unpaid_qr_treeview"));
- if (NULL == w)
- {
- GNUNET_break (0);
- continue;
- }
if (GNUNET_OK !=
GNUNET_JSON_parse (ppr,
spec,
@@ -1684,25 +1653,8 @@ action_policies_paying (void)
GNUNET_break (0);
continue;
}
- pb = AG_setup_qrcode (w,
- payto,
- strlen (payto));
- if (NULL == pb)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- _ ("Failed to initialize QR-code pixbuf for `%s'\n"),
- payto);
- continue;
- }
-
- gtk_list_store_insert_with_values (ls,
- NULL,
- -1, /* append */
- AG_UQRMC_QR_IMAGE, pb,
- AG_UQRMC_URL, payto,
- AG_UQRMC_PROVIDER, provider,
- -1);
- g_object_unref (pb);
+ add_qrcode (payto,
+ provider);
}
{
json_t *args;
@@ -1741,15 +1693,12 @@ action_backup_finished (void)
json_t *det;
json_t *se;
const char *url;
- GtkListStore *ls;
struct GNUNET_TIME_Timestamp mexp;
AG_hide_all_frames ();
det = json_object_get (AG_redux_state,
"success_details");
- ls = GTK_LIST_STORE (GCG_get_main_window_object (
- "backup_provider_liststore"));
- gtk_list_store_clear (ls);
+ AG_list_clear ("backup_provider_liststore");
mexp = GNUNET_TIME_UNIT_FOREVER_TS;
json_object_foreach (det, url, se)
{
@@ -1774,21 +1723,18 @@ action_backup_finished (void)
}
mexp = GNUNET_TIME_timestamp_min (mexp,
pexp);
- gtk_list_store_insert_with_values (
- ls,
- NULL,
- -1, /* append */
- AG_BPC_PROVIDER_URL,
- url,
- AG_BPC_BACKUP_VERSION,
- (guint64) version,
- AG_BPC_EXPIRATION_TIME_STR,
- GNUNET_TIME_timestamp2s (pexp),
- AG_BPC_SUCCESS_FLAG,
- true,
- AG_BPC_PROVIDER_NAME,
- lookup_provider_name (url),
- -1);
+ AG_list_add ("backup_provider_liststore",
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("provider_url",
+ url),
+ GNUNET_JSON_pack_uint64 ("version",
+ version),
+ GNUNET_JSON_pack_string ("expiration",
+ GNUNET_TIME_timestamp2s (pexp)),
+ GNUNET_JSON_pack_bool ("success",
+ true),
+ GNUNET_JSON_pack_string ("provider_name",
+ lookup_provider_name (url))));
}
{
struct tm tv;
@@ -1968,7 +1914,7 @@ show_challenge_feedback (const char *uuid,
* policies again if they are at different providers under versions not
* queried up to the cursor.
*
- * @param cls closure with the `GtkListStore` to update.
+ * @param cls closure with the list of secrets to update.
* @param hcpd hash of the compressed policy document (unique per policy)
* @param provider_url which provider claims to have this policy
* @param version version of the policy at this provider
@@ -1988,34 +1934,30 @@ expand_policy_list (void *cls,
const char *secret_name,
const json_t *providers)
{
- GtkListStore *ls = cls;
- GtkTreeIter iter;
+ GListStore *ls = cls;
bool have_first;
- have_first = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls),
- &iter);
- gtk_list_store_insert_with_values (
+ have_first = (0 != g_list_model_get_n_items (G_LIST_MODEL (ls)));
+ AG_list_append (
ls,
- &iter,
- -1, /* append */
- AG_SSMC_PROVIDER_URL, provider_url,
- AG_SSMC_POLICY_VERSION, (gint) version,
- AG_SSMC_ATTRIBUTE_MASK, (gint) attribute_mask,
- AG_SSMC_SECRET_NAME, secret_name,
- AG_SSMC_POLICY_DATE_STRING, GNUNET_TIME_timestamp2s (server_time),
- AG_SSMC_POLICY_DATE_NUMERIC, (guint64) server_time.abs_time.abs_value_us,
- AG_SSMC_POLICY_PROVIDER_JSON, (gpointer) providers,
- -1);
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("provider_url",
+ provider_url),
+ GNUNET_JSON_pack_uint64 ("version",
+ version),
+ GNUNET_JSON_pack_uint64 ("attribute_mask",
+ attribute_mask),
+ GNUNET_JSON_pack_string ("secret_name",
+ secret_name),
+ GNUNET_JSON_pack_string ("date_string",
+ GNUNET_TIME_timestamp2s (server_time)),
+ GNUNET_JSON_pack_uint64 ("date",
+ server_time.abs_time.abs_value_us),
+ GNUNET_JSON_pack_array_incref ("providers",
+ (json_t *) providers)));
if (! have_first)
- {
- GtkTreeSelection *selection;
-
- selection = GTK_TREE_SELECTION (
- GCG_get_main_window_object (
- "anastasis_gtk_secret_selection_treeselection"));
- gtk_tree_selection_select_iter (selection,
- &iter);
- }
+ AG_select_row ("anastasis_gtk_secret_selection_treeselection",
+ 0);
}
@@ -2255,14 +2197,13 @@ long_poll_providers_action_cb (void *cls,
static void
begin_discovery (void)
{
- GtkListStore *ls;
+ GListStore *ls;
bool have_providers;
- ls = GTK_LIST_STORE (GCG_get_main_window_object (
- "secret_selection_liststore"));
+ ls = AG_list ("secret_selection_liststore");
GNUNET_assert (NULL != ls);
if (NULL == AG_pd)
- gtk_list_store_clear (ls);
+ g_list_store_remove_all (ls);
have_providers = sync_providers ();
if (NULL == AG_pd)
AG_pd = ANASTASIS_policy_discovery_start (AG_redux_state,
@@ -2744,15 +2685,12 @@ action_challenge_paying (void)
{
json_t *pprs;
json_t *ppr;
- GtkListStore *ls;
const char *uuid;
bool found = false;
const char *ps;
AG_hide_all_frames ();
- ls = GTK_LIST_STORE (GCG_get_main_window_object (
- "unpaid_qrcodes_liststore"));
- gtk_list_store_clear (ls);
+ AG_list_clear ("unpaid_qrcodes_liststore");
pprs = json_object_get (AG_redux_state,
"challenge_feedback");
json_object_foreach (pprs, uuid, ppr)
@@ -2775,8 +2713,6 @@ action_challenge_paying (void)
&ps),
GNUNET_JSON_spec_end ()
};
- GdkPixbuf *pb;
- GtkWidget *w;
if (GNUNET_OK !=
GNUNET_JSON_parse (ppr,
@@ -2795,22 +2731,8 @@ action_challenge_paying (void)
"payment"))
continue;
found = true;
- w = GTK_WIDGET (GCG_get_main_window_object ("unpaid_qr_treeview"));
- pb = AG_setup_qrcode (w,
- payto,
- strlen (payto));
- if (NULL == pb)
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- _ ("Failed to initialize QR-code pixbuf for `%s'\n"),
- payto);
- gtk_list_store_insert_with_values (ls,
- NULL,
- -1, /* append */
- AG_UQRMC_QR_IMAGE, pb,
- AG_UQRMC_URL, payto,
- AG_UQRMC_PROVIDER, provider,
- -1);
- g_object_unref (pb);
+ add_qrcode (payto,
+ (NULL != provider) ? provider : "");
break;
}
diff --git a/src/anastasis/anastasis-gtk_cells.c b/src/anastasis/anastasis-gtk_cells.c
@@ -0,0 +1,569 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2025 Anastasis SARL
+
+ Anastasis is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ Anastasis is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Anastasis; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/**
+ * @file src/anastasis/anastasis-gtk_cells.c
+ * @brief Widgets rendering the cells of our GtkColumnViews
+ * @author Christian Grothoff
+ */
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis_gtk_util.h"
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_rows.h"
+
+
+/**
+ * Padding above and below the rows of the two progress lists.
+ */
+#define PROGRESS_PADDING 15
+
+
+/**
+ * Return the row the cell of @a item shows and remember it on the
+ * widget of that cell, so that a right-click can find it again.
+ *
+ * @param item the cell being bound
+ * @return NULL if @a item shows no row
+ */
+static AGRow *
+bind_row (GtkListItem *item)
+{
+ AGRow *row = AG_row_of (gtk_list_item_get_item (item));
+ GtkWidget *child = gtk_list_item_get_child (item);
+
+ if (NULL != child)
+ AG_cell_set_row (child,
+ row);
+ return row;
+}
+
+
+/**
+ * Set the label of the cell of @a item to the @a field of its row.
+ *
+ * @param item the cell being bound
+ * @param field field of the row to show
+ */
+static void
+bind_text (GtkListItem *item,
+ const char *field)
+{
+ AGRow *row = bind_row (item);
+ const char *val = NULL;
+
+ if (NULL != row)
+ val = AG_row_string (row,
+ field);
+ gtk_label_set_text (GTK_LABEL (gtk_list_item_get_child (item)),
+ (NULL != val) ? val : "");
+}
+
+
+/**
+ * Define the "bind" handler @a name of a column showing the string
+ * @a field of the rows. The columns of our lists differ only in the
+ * field they show, but the UI files must name a distinct handler for
+ * each of them.
+ *
+ * @param name name of the handler to define
+ * @param field field of the row the column shows
+ */
+#define TEXT_CELL(name, field) \
+ void \
+ name (GtkSignalListItemFactory *f, \
+ GtkListItem *item, \
+ gpointer user_data) \
+ { \
+ (void) f; \
+ (void) user_data; \
+ bind_text (item, \
+ field); \
+ }
+
+
+/**
+ * Create the label rendering a cell of a text column.
+ *
+ * @param f the factory of the column
+ * @param item the cell to fill
+ * @param user_data the builder
+ */
+void
+AG_cell_setup_text (GtkSignalListItemFactory *f,
+ GtkListItem *item,
+ gpointer user_data)
+{
+ GtkWidget *label;
+
+ (void) f;
+ (void) user_data;
+ label = gtk_label_new (NULL);
+ gtk_label_set_xalign (GTK_LABEL (label),
+ 0.0);
+ gtk_widget_set_halign (label,
+ GTK_ALIGN_START);
+ gtk_list_item_set_child (item,
+ label);
+}
+
+
+/**
+ * Forget the row a cell showed once it is no longer shown.
+ *
+ * @param f the factory of the column
+ * @param item the cell that was unbound
+ * @param user_data the builder
+ */
+void
+AG_cell_unbind (GtkSignalListItemFactory *f,
+ GtkListItem *item,
+ gpointer user_data)
+{
+ GtkWidget *child = gtk_list_item_get_child (item);
+
+ (void) f;
+ (void) user_data;
+ if (NULL != child)
+ AG_cell_set_row (child,
+ NULL);
+}
+
+
+/* ****************** progress lists ******************* */
+
+
+/**
+ * Create the label rendering a step of one of the progress lists.
+ *
+ * @param f the factory of the column
+ * @param item the cell to fill
+ * @param user_data the builder
+ */
+void
+AG_cell_setup_progress (GtkSignalListItemFactory *f,
+ GtkListItem *item,
+ gpointer user_data)
+{
+ GtkWidget *label;
+
+ AG_cell_setup_text (f,
+ item,
+ user_data);
+ label = gtk_list_item_get_child (item);
+ gtk_widget_set_margin_top (label,
+ PROGRESS_PADDING);
+ gtk_widget_set_margin_bottom (label,
+ PROGRESS_PADDING);
+}
+
+
+/**
+ * Show the description of a step of one of the progress lists, using the
+ * long explanation as the tooltip.
+ *
+ * @param f the factory of the column
+ * @param item the cell to fill
+ * @param user_data the builder
+ */
+void
+AG_cell_bind_progress (GtkSignalListItemFactory *f,
+ GtkListItem *item,
+ gpointer user_data)
+{
+ AGRow *row;
+
+ (void) f;
+ (void) user_data;
+ bind_text (item,
+ "description");
+ row = AG_row_of (gtk_list_item_get_item (item));
+ if (NULL == row)
+ return;
+ gtk_widget_set_tooltip_text (gtk_list_item_get_child (item),
+ AG_row_string (row,
+ "tooltip"));
+}
+
+
+/* ************ continents, countries, methods ************* */
+
+
+TEXT_CELL (AG_cell_bind_continent, "name_i18n")
+TEXT_CELL (AG_cell_bind_country, "name")
+TEXT_CELL (AG_cell_bind_auth_type, "type")
+TEXT_CELL (AG_cell_bind_auth_details, "visualization")
+
+
+/* ******************* policy review ******************** */
+
+
+/**
+ * Create the expander and label rendering the first column of the policy
+ * review, where the challenges are nested under their policy.
+ *
+ * @param f the factory of the column
+ * @param item the cell to fill
+ * @param user_data the builder
+ */
+void
+AG_cell_setup_policy_name (GtkSignalListItemFactory *f,
+ GtkListItem *item,
+ gpointer user_data)
+{
+ GtkWidget *expander;
+ GtkWidget *label;
+
+ (void) f;
+ (void) user_data;
+ label = gtk_label_new (NULL);
+ gtk_label_set_xalign (GTK_LABEL (label),
+ 0.0);
+ gtk_widget_set_halign (label,
+ GTK_ALIGN_START);
+ expander = gtk_tree_expander_new ();
+ gtk_tree_expander_set_child (GTK_TREE_EXPANDER (expander),
+ label);
+ gtk_list_item_set_child (item,
+ expander);
+}
+
+
+/**
+ * Show the name of a policy, or the instructions of a challenge.
+ *
+ * @param f the factory of the column
+ * @param item the cell to fill
+ * @param user_data the builder
+ */
+void
+AG_cell_bind_policy_name (GtkSignalListItemFactory *f,
+ GtkListItem *item,
+ gpointer user_data)
+{
+ GtkTreeExpander *expander;
+ GtkWidget *label;
+ AGRow *row;
+ const char *name = NULL;
+
+ (void) f;
+ (void) user_data;
+ expander = GTK_TREE_EXPANDER (gtk_list_item_get_child (item));
+ gtk_tree_expander_set_list_row (expander,
+ GTK_TREE_LIST_ROW (
+ gtk_list_item_get_item (item)));
+ row = bind_row (item);
+ if (NULL != row)
+ name = AG_row_string (row,
+ "policy_name");
+ label = gtk_tree_expander_get_child (expander);
+ gtk_label_set_text (GTK_LABEL (label),
+ (NULL != name) ? name : "");
+}
+
+
+TEXT_CELL (AG_cell_bind_policy_type, "method_type")
+TEXT_CELL (AG_cell_bind_policy_cost, "cost")
+TEXT_CELL (AG_cell_bind_policy_provider, "provider_name")
+
+
+/* ****************** payment QR codes ******************* */
+
+
+/**
+ * Create the picture rendering a QR code.
+ *
+ * @param f the factory of the column
+ * @param item the cell to fill
+ * @param user_data the builder
+ */
+void
+AG_cell_setup_qr_image (GtkSignalListItemFactory *f,
+ GtkListItem *item,
+ gpointer user_data)
+{
+ GtkWidget *picture;
+
+ (void) f;
+ (void) user_data;
+ picture = gtk_picture_new ();
+ /* render the QR code at its natural size */
+ gtk_picture_set_can_shrink (GTK_PICTURE (picture),
+ FALSE);
+ gtk_picture_set_content_fit (GTK_PICTURE (picture),
+ GTK_CONTENT_FIT_SCALE_DOWN);
+ /* a GtkCellRendererPixbuf centred its image in the cell */
+ gtk_widget_set_halign (picture,
+ GTK_ALIGN_CENTER);
+ gtk_list_item_set_child (item,
+ picture);
+}
+
+
+/**
+ * Show the QR code of a payment request.
+ *
+ * @param f the factory of the column
+ * @param item the cell to fill
+ * @param user_data the builder
+ */
+void
+AG_cell_bind_qr_image (GtkSignalListItemFactory *f,
+ GtkListItem *item,
+ gpointer user_data)
+{
+ AGRow *row = bind_row (item);
+
+ (void) f;
+ (void) user_data;
+ gtk_picture_set_paintable (GTK_PICTURE (gtk_list_item_get_child (item)),
+ (NULL != row)
+ ? AG_row_image (row)
+ : NULL);
+}
+
+
+TEXT_CELL (AG_cell_bind_qr_uri, "url")
+
+
+/* ***************** completed backup ******************* */
+
+
+TEXT_CELL (AG_cell_bind_backup_provider, "provider_name")
+
+
+/**
+ * Show the version of the policy stored at a provider, but only for the
+ * providers the backup actually reached.
+ *
+ * @param f the factory of the column
+ * @param item the cell to fill
+ * @param user_data the builder
+ */
+void
+AG_cell_bind_backup_version (GtkSignalListItemFactory *f,
+ GtkListItem *item,
+ gpointer user_data)
+{
+ AGRow *row = bind_row (item);
+ GtkWidget *label = gtk_list_item_get_child (item);
+ char *version;
+
+ (void) f;
+ (void) user_data;
+ if (NULL == row)
+ {
+ gtk_widget_set_visible (label,
+ FALSE);
+ return;
+ }
+ GNUNET_asprintf (&version,
+ "%llu",
+ (unsigned long long) AG_row_uint (row,
+ "version"));
+ gtk_label_set_text (GTK_LABEL (label),
+ version);
+ GNUNET_free (version);
+ gtk_widget_set_visible (label,
+ AG_row_bool (row,
+ "success"));
+}
+
+
+/* ***************** secret selection ******************* */
+
+
+TEXT_CELL (AG_cell_bind_secret_name, "secret_name")
+TEXT_CELL (AG_cell_bind_secret_time, "date_string")
+
+
+/* ****************** provider list ******************** */
+
+
+/**
+ * The user toggled whether a provider is to be used.
+ *
+ * @param button the check button of the row
+ * @param user_data NULL
+ */
+static void
+provider_use_toggled_cb (GtkCheckButton *button,
+ gpointer user_data)
+{
+ AGRow *row;
+ bool active;
+
+ (void) user_data;
+ row = AG_cell_get_row (GTK_WIDGET (button));
+ if (NULL == row)
+ return; /* not bound to a row (yet) */
+ active = gtk_check_button_get_active (button);
+ if (active == AG_row_bool (row,
+ "enabled"))
+ return; /* we set the state ourselves while binding */
+ AG_row_set (row,
+ "enabled",
+ json_boolean (active));
+}
+
+
+/**
+ * Create the check button with which a provider is enabled or disabled.
+ *
+ * @param f the factory of the column
+ * @param item the cell to fill
+ * @param user_data the builder
+ */
+void
+AG_cell_setup_provider_use (GtkSignalListItemFactory *f,
+ GtkListItem *item,
+ gpointer user_data)
+{
+ GtkWidget *check;
+
+ (void) f;
+ (void) user_data;
+ check = gtk_check_button_new ();
+ gtk_widget_set_halign (check,
+ GTK_ALIGN_CENTER);
+ g_signal_connect (check,
+ "toggled",
+ G_CALLBACK (&provider_use_toggled_cb),
+ NULL);
+ gtk_list_item_set_child (item,
+ check);
+}
+
+
+/**
+ * Show whether a provider is to be used. Providers we could not talk to
+ * cannot be enabled, which is shown as an indeterminate state.
+ *
+ * @param f the factory of the column
+ * @param item the cell to fill
+ * @param user_data the builder
+ */
+void
+AG_cell_bind_provider_use (GtkSignalListItemFactory *f,
+ GtkListItem *item,
+ gpointer user_data)
+{
+ GtkCheckButton *check;
+ AGRow *row;
+
+ (void) f;
+ (void) user_data;
+ /* the row must be known before we change the state, or the "toggled"
+ handler cannot tell our own change from the user's */
+ row = bind_row (item);
+ check = GTK_CHECK_BUTTON (gtk_list_item_get_child (item));
+ if (NULL == row)
+ return;
+ gtk_check_button_set_active (check,
+ AG_row_bool (row,
+ "enabled"));
+ gtk_check_button_set_inconsistent (check,
+ ! AG_row_bool (row,
+ "sensitive"));
+ gtk_widget_set_sensitive (GTK_WIDGET (check),
+ AG_row_bool (row,
+ "sensitive"));
+}
+
+
+TEXT_CELL (AG_cell_bind_provider_name, "name")
+
+
+/**
+ * Show the liability limit of a provider, which we only know for the
+ * providers we could talk to.
+ *
+ * @param f the factory of the column
+ * @param item the cell to fill
+ * @param user_data the builder
+ */
+void
+AG_cell_bind_provider_liability (GtkSignalListItemFactory *f,
+ GtkListItem *item,
+ gpointer user_data)
+{
+ AGRow *row;
+
+ (void) f;
+ (void) user_data;
+ bind_text (item,
+ "liability");
+ row = AG_row_of (gtk_list_item_get_item (item));
+ gtk_widget_set_visible (gtk_list_item_get_child (item),
+ (NULL != row) &&
+ AG_row_bool (row,
+ "sensitive"));
+}
+
+
+/**
+ * Show the status of a provider in the colour that goes with it.
+ *
+ * @param f the factory of the column
+ * @param item the cell to fill
+ * @param user_data the builder
+ */
+void
+AG_cell_bind_provider_status (GtkSignalListItemFactory *f,
+ GtkListItem *item,
+ gpointer user_data)
+{
+ AGRow *row = bind_row (item);
+ GtkLabel *label = GTK_LABEL (gtk_list_item_get_child (item));
+ const char *status;
+ const char *color;
+
+ (void) f;
+ (void) user_data;
+ status = (NULL != row)
+ ? AG_row_string (row,
+ "status")
+ : NULL;
+ if (NULL == status)
+ {
+ gtk_label_set_text (label,
+ "");
+ return;
+ }
+ color = AG_row_string (row,
+ "color");
+ if (NULL == color)
+ {
+ gtk_label_set_text (label,
+ status);
+ return;
+ }
+ {
+ char *markup;
+
+ markup = g_markup_printf_escaped ("<span foreground=\"%s\">%s</span>",
+ color,
+ status);
+ gtk_label_set_markup (label,
+ markup);
+ g_free (markup);
+ }
+}
+
+
+/* end of anastasis-gtk_cells.c */
diff --git a/src/anastasis/anastasis-gtk_handle-auth-delete-button-clicked.c b/src/anastasis/anastasis-gtk_handle-auth-delete-button-clicked.c
@@ -26,6 +26,7 @@
#include <gnunet/gnunet_util_lib.h>
#include "anastasis-gtk_action.h"
#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_rows.h"
#include <jansson.h>
@@ -40,28 +41,19 @@ anastasis_gtk_authentication_method_delete_button_clicked_cb (GObject *object,
gpointer user_data)
{
json_t *args;
- guint index;
- GtkTreeSelection *ts;
- GtkTreeModel *model;
- GtkTreeIter iter;
+ AGRow *row;
- ts = GTK_TREE_SELECTION (GCG_get_main_window_object (
- "anastasis_gtk_authentication_methods_selection"));
- if (! gtk_tree_selection_get_selected (ts,
- &model,
- &iter))
+ row = AG_selected_row ("anastasis_gtk_authentication_methods_selection");
+ if (NULL == row)
{
GNUNET_break (0);
return;
}
- gtk_tree_model_get (model,
- &iter,
- AG_AMMC_INDEX, &index,
- -1);
AG_freeze ();
args = GNUNET_JSON_PACK (
GNUNET_JSON_pack_uint64 ("authentication_method",
- index));
+ AG_row_uint (row,
+ "index")));
AG_ra = ANASTASIS_redux_action (AG_redux_state,
"delete_authentication",
args,
@@ -71,17 +63,22 @@ anastasis_gtk_authentication_method_delete_button_clicked_cb (GObject *object,
}
+/**
+ * Callback invoked if the selected authentication method changed.
+ *
+ * @param selection the selection of the authentication method list
+ * @param pspec unused
+ * @param user_data unused
+ */
void
auth_method_selection_changed_cb (
- GtkTreeSelection *treeselection,
+ GtkSingleSelection *selection,
+ GParamSpec *pspec,
gpointer user_data)
{
- GtkTreeModel *model;
- GtkTreeIter iter;
-
- if (gtk_tree_selection_get_selected (treeselection,
- &model,
- &iter))
+ (void) pspec;
+ (void) user_data;
+ if (NULL != AG_selected (selection))
AG_sensitive ("anastasis_gtk_authentication_method_delete_button");
else
AG_insensitive ("anastasis_gtk_authentication_method_delete_button");
diff --git a/src/anastasis/anastasis-gtk_handle-auth-edit-provider-clicked.c b/src/anastasis/anastasis-gtk_handle-auth-edit-provider-clicked.c
@@ -29,6 +29,7 @@
#include "anastasis-gtk_action.h"
#include "anastasis-gtk_helper.h"
#include "anastasis-gtk_handle-main-window-forward-clicked.h"
+#include "anastasis-gtk_rows.h"
#include <sys/types.h>
#include <sys/wait.h>
#include <jansson.h>
@@ -111,13 +112,13 @@ xdg_open (const char *url)
/**
* Return the provider the user right-clicked on.
*
- * @param tree_view the tree view the context menu belongs to
+ * @param view the list the context menu belongs to
* @return the context, NULL if there is none
*/
static struct MenuContext *
-current_context (gpointer tree_view)
+current_context (gpointer view)
{
- return g_object_get_data (G_OBJECT (tree_view),
+ return g_object_get_data (G_OBJECT (view),
"anastasis-menu-context");
}
@@ -182,50 +183,11 @@ view_privacy_policy (GSimpleAction *action,
}
-void
-provider_toggle_callback (GtkCellRendererToggle *cell,
- gchar *path_str,
- gpointer user_data)
-{
- GtkBuilder *builder = GTK_BUILDER (user_data);
- GtkTreeIter iter;
- GtkTreePath *path;
- gboolean enabled;
- GtkTreeModel *tm;
-
- tm = GTK_TREE_MODEL (gtk_builder_get_object (builder,
- "provider_liststore"));
- if (NULL == tm)
- {
- GNUNET_break (0);
- return;
- }
- path = gtk_tree_path_new_from_string (path_str);
- if (! gtk_tree_model_get_iter (tm,
- &iter,
- path))
- {
- GNUNET_break (0);
- return;
- }
- gtk_tree_model_get (tm,
- &iter,
- AG_PMC_PROVIDER_ENABLED, &enabled,
- -1);
- enabled = ! enabled;
- gtk_list_store_set (GTK_LIST_STORE (tm),
- &iter,
- AG_PMC_PROVIDER_ENABLED, enabled,
- -1);
- gtk_tree_path_free (path);
-}
-
-
/**
* Return the context menu of @a tv, creating it on first use. A single
- * popover is reused for the lifetime of the tree view.
+ * popover is reused for the lifetime of the list.
*
- * @param tv the tree view
+ * @param tv the list
* @return the popover to show
*/
static GtkPopover *
@@ -283,7 +245,7 @@ get_popover (GtkWidget *tv)
/**
- * User right-clicked on the tree view: show the context menu that lets them
+ * User right-clicked on the list: show the context menu that lets them
* view the provider's privacy policy or terms of service.
*
* @param gesture the click gesture, restricted to button 3 by the UI file
@@ -299,57 +261,21 @@ provider_tree_view_pressed_cb (GtkGestureClick *gesture,
gdouble y,
gpointer user_data)
{
- GtkBuilder *builder = GTK_BUILDER (user_data);
GtkWidget *widget
= gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
- GtkTreeView *tv = GTK_TREE_VIEW (widget);
- GtkTreeModel *tm;
- GtkTreePath *path;
- GtkTreeIter iter;
struct MenuContext *ctx;
- int bx;
- int by;
+ AGRow *row;
(void) n_press;
- tm = GTK_TREE_MODEL (gtk_builder_get_object (builder,
- "provider_liststore"));
- if (NULL == tm)
- {
- GNUNET_break (0);
- return;
- }
- gtk_tree_view_convert_widget_to_bin_window_coords (tv,
- (int) x,
- (int) y,
- &bx,
- &by);
- if (! gtk_tree_view_get_path_at_pos (tv,
- bx,
- by,
- &path,
- NULL,
- NULL,
- NULL))
- {
- /* nothing selected */
- return;
- }
- if (! gtk_tree_model_get_iter (tm,
- &iter,
- path))
- {
- /* not sure how we got a path but no iter... */
- GNUNET_break (0);
- gtk_tree_path_free (path);
- return;
- }
- gtk_tree_path_free (path);
+ (void) user_data;
+ row = AG_row_at (widget,
+ y);
+ if (NULL == row)
+ return; /* the user clicked past the last provider */
ctx = GNUNET_new (struct MenuContext);
- gtk_tree_model_get (tm,
- &iter,
- AG_PMC_PROVIDER_URL, &ctx->url,
- -1);
- /* the tree view owns the context of the row currently pointed at; the
+ ctx->url = GNUNET_strdup (AG_row_string (row,
+ "url"));
+ /* the list owns the context of the row currently pointed at; the
previous one is released here */
g_object_set_data_full (G_OBJECT (widget),
"anastasis-menu-context",
@@ -382,27 +308,32 @@ url_add_button_clicked_cb (GtkButton *button,
gpointer user_data)
{
GtkBuilder *builder = GTK_BUILDER (user_data);
- GtkListStore *ls;
+ GListStore *ls;
GtkEntry *entry;
const char *url;
- ls = GTK_LIST_STORE (gtk_builder_get_object (builder,
- "provider_liststore"));
+ ls = AG_list_of (builder,
+ "provider_liststore");
GNUNET_assert (NULL != ls);
entry = GTK_ENTRY (gtk_builder_get_object (builder,
"url_entry"));
url = gtk_editable_get_text (GTK_EDITABLE (entry));
- gtk_list_store_insert_with_values (ls,
- NULL,
- -1,
- AG_PMC_PROVIDER_URL, url,
- AG_PMC_PROVIDER_STATUS, _ ("new"),
- AG_PMC_PROVIDER_STATUS_COLOR, "blue",
- AG_PMC_PROVIDER_ENABLED, true,
- AG_PMC_PROVIDER_SENSITIVE, false,
- AG_PMC_PROVIDER_NOT_SENSITIVE, true,
- AG_PMC_PROVIDER_NAME, url,
- -1);
+ AG_list_append (ls,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("url",
+ url),
+ GNUNET_JSON_pack_string ("status",
+ _ ("new")),
+ GNUNET_JSON_pack_string ("color",
+ "blue"),
+ GNUNET_JSON_pack_bool ("enabled",
+ true),
+ GNUNET_JSON_pack_bool ("sensitive",
+ false),
+ GNUNET_JSON_pack_bool ("not_sensitive",
+ true),
+ GNUNET_JSON_pack_string ("name",
+ url)));
gtk_editable_set_text (GTK_EDITABLE (entry),
"");
}
@@ -449,9 +380,9 @@ edit_provider_dialog_response_cb (GtkDialog *dialog,
gpointer user_data)
{
GtkBuilder *builder = GTK_BUILDER (user_data);
- GtkTreeModel *tm;
- GtkTreeIter iter;
+ GListModel *lm;
json_t *args;
+ guint n;
if (GTK_RESPONSE_APPLY != response_id)
{
@@ -459,38 +390,33 @@ edit_provider_dialog_response_cb (GtkDialog *dialog,
g_object_unref (G_OBJECT (builder));
return;
}
- tm = GTK_TREE_MODEL (gtk_builder_get_object (builder,
- "provider_liststore"));
- if (NULL == tm)
+ lm = G_LIST_MODEL (AG_list_of (builder,
+ "provider_liststore"));
+ if (NULL == lm)
{
GNUNET_break (0);
return;
}
args = json_object ();
- if (gtk_tree_model_get_iter_first (tm,
- &iter))
- do {
- gchar *url;
- gboolean enabled;
-
- gtk_tree_model_get (tm,
- &iter,
- AG_PMC_PROVIDER_URL, &url,
- AG_PMC_PROVIDER_ENABLED, &enabled,
- -1);
- GNUNET_assert (0 ==
- json_object_set_new (
- args,
- url,
- GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("status",
- enabled
- ? "not-contacted"
- : "disabled"))));
- g_free (url);
- }
- while (gtk_tree_model_iter_next (tm,
- &iter));
+ n = g_list_model_get_n_items (lm);
+ for (guint i = 0; i < n; i++)
+ {
+ AGRow *row = g_list_model_get_item (lm,
+ i);
+
+ GNUNET_assert (0 ==
+ json_object_set_new (
+ args,
+ AG_row_string (row,
+ "url"),
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("status",
+ AG_row_bool (row,
+ "enabled")
+ ? "not-contacted"
+ : "disabled"))));
+ g_object_unref (row);
+ }
gtk_window_destroy (GTK_WINDOW (dialog));
g_object_unref (G_OBJECT (builder));
AG_freeze ();
@@ -513,9 +439,18 @@ void
anastasis_gtk_edit_provider_list_clicked_cb (GtkButton *object,
gpointer user_data)
{
+ static const struct AG_SortSpec sorts[] = {
+ { .column = "enabled_column",
+ .field = "not_sensitive",
+ .numeric = true },
+ { .column = "status_column",
+ .field = "not_sensitive",
+ .numeric = true },
+ { .column = NULL }
+ };
GtkWidget *ad;
GtkBuilder *builder;
- GtkListStore *ls;
+ GListStore *ls;
json_t *providers;
builder = ANASTASIS_GTK_get_new_builder (
@@ -527,8 +462,12 @@ anastasis_gtk_edit_provider_list_clicked_cb (GtkButton *object,
GNUNET_break (0);
return;
}
- ls = GTK_LIST_STORE (gtk_builder_get_object (builder,
- "provider_liststore"));
+ AG_list_make_sortable (builder,
+ "provider_tree_view",
+ "provider_sorted",
+ sorts);
+ ls = AG_list_of (builder,
+ "provider_liststore");
providers = json_object_get (AG_redux_state,
"authentication_providers");
{
@@ -624,20 +563,27 @@ anastasis_gtk_edit_provider_list_clicked_cb (GtkButton *object,
}
if (NULL == name)
name = url;
- gtk_list_store_insert_with_values (
+ AG_list_append (
ls,
- NULL,
- -1,
- AG_PMC_PROVIDER_URL, url,
- AG_PMC_PROVIDER_STATUS, status_str,
- AG_PMC_PROVIDER_STATUS_COLOR, color,
- AG_PMC_PROVIDER_LIABILITY_LIMIT, ll_s,
- AG_PMC_PROVIDER_ENABLED, (0 != strcmp (status,
- "disabled")),
- AG_PMC_PROVIDER_SENSITIVE, sensitive,
- AG_PMC_PROVIDER_NOT_SENSITIVE, ! sensitive,
- AG_PMC_PROVIDER_NAME, name,
- -1);
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("url",
+ url),
+ GNUNET_JSON_pack_string ("status",
+ status_str),
+ GNUNET_JSON_pack_string ("color",
+ color),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("liability",
+ ll_s)),
+ GNUNET_JSON_pack_bool ("enabled",
+ 0 != strcmp (status,
+ "disabled")),
+ GNUNET_JSON_pack_bool ("sensitive",
+ sensitive),
+ GNUNET_JSON_pack_bool ("not_sensitive",
+ ! sensitive),
+ GNUNET_JSON_pack_string ("name",
+ name)));
GNUNET_free (status_str);
}
}
diff --git a/src/anastasis/anastasis-gtk_handle-continent-selected.c b/src/anastasis/anastasis-gtk_handle-continent-selected.c
@@ -26,29 +26,31 @@
#include <gnunet/gnunet_util_lib.h>
#include "anastasis-gtk_helper.h"
#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_rows.h"
#include <jansson.h>
/**
* Callback invoked if a continent is selected.
*
- * @param selection A GtkTreeSelection.
+ * @param selection the selection of the continent list
+ * @param pspec unused
* @param user_data user data set when the signal handler was connected.
*/
void
-anastasis_gtk_continent_selection_changed_cb (GtkTreeSelection *treeselection,
+anastasis_gtk_continent_selection_changed_cb (GtkSingleSelection *selection,
+ GParamSpec *pspec,
gpointer user_data)
{
- GtkTreeModel *model;
- GtkTreeIter iter;
- gchar *continent_name;
+ AGRow *row;
json_t *arguments;
+ (void) pspec;
+ (void) user_data;
if (AG_in_action)
return;
- if (! gtk_tree_selection_get_selected (treeselection,
- &model,
- &iter))
+ row = AG_selected (selection);
+ if (NULL == row)
{
AG_freeze ();
AG_ra = ANASTASIS_redux_action (AG_redux_state,
@@ -58,14 +60,10 @@ anastasis_gtk_continent_selection_changed_cb (GtkTreeSelection *treeselection,
NULL);
return;
}
- gtk_tree_model_get (model,
- &iter,
- AG_CMC_CONTINENT_NAME, &continent_name,
- -1);
arguments = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("continent",
- continent_name));
- g_free (continent_name);
+ AG_row_string (row,
+ "name")));
AG_freeze ();
AG_ra = ANASTASIS_redux_action (AG_redux_state,
"select_continent",
diff --git a/src/anastasis/anastasis-gtk_handle-country-activated.c b/src/anastasis/anastasis-gtk_handle-country-activated.c
@@ -27,26 +27,25 @@
#include "anastasis-gtk_helper.h"
#include "anastasis-gtk_action.h"
#include "anastasis-gtk_handle-main-window-forward-clicked.h"
+#include "anastasis-gtk_rows.h"
#include <jansson.h>
/**
* Callback invoked if a country is selected.
*
- * @param treeselection selection object
+ * @param selection the selection of the country list
+ * @param pspec unused
* @param user_data NULL
*/
void
-anastasis_gtk_country_selection_changed_cb (GtkTreeSelection *treeselection,
+anastasis_gtk_country_selection_changed_cb (GtkSingleSelection *selection,
+ GParamSpec *pspec,
gpointer user_data)
{
- GtkTreeModel *model;
- GtkTreeIter iter;
-
+ (void) pspec;
(void) user_data;
- if (! gtk_tree_selection_get_selected (treeselection,
- &model,
- &iter))
+ if (NULL == AG_selected (selection))
{
AG_insensitive ("anastasis_gtk_main_window_forward_button");
return;
diff --git a/src/anastasis/anastasis-gtk_handle-country-unselected.c b/src/anastasis/anastasis-gtk_handle-country-unselected.c
@@ -1,50 +0,0 @@
-/*
- This file is part of anastasis-gtk.
- Copyright (C) 2020 Anastasis SARL
-
- Anastasis is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published
- by the Free Software Foundation; either version 3, or (at your
- option) any later version.
-
- Anastasis is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Anastasis; see the file COPYING. If not, write to the
- Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA.
-*/
-/**
- * @file src/anastasis/anastasis-gtk_handle-country-unselected.c
- * @brief
- * @author Christian Grothoff
- * @author Dennis Neufeld
- */
-#include <gnunet/gnunet_util_lib.h>
-#include "anastasis-gtk_action.h"
-#include "anastasis-gtk_helper.h"
-#include <jansson.h>
-
-
-/**
- * Callback invoked if a country is unselected (unselected signal).
- *
- * @param selection A GtkTreeSelection.
- * @param user_data user data set when the signal handler was connected.
- */
-void
-anastasis_gtk_country_unselected (GtkTreeSelection *selection,
- gpointer user_data)
-{
- GtkTreeModel *model;
-
- if (gtk_tree_selection_get_selected (selection,
- &model,
- NULL))
- AG_enable_next ();
- else
- AG_insensitive ("anastasis_gtk_main_window_forward_button");
-}
diff --git a/src/anastasis/anastasis-gtk_handle-main-window-forward-clicked.c b/src/anastasis/anastasis-gtk_handle-main-window-forward-clicked.c
@@ -32,6 +32,7 @@
#include "anastasis-gtk_attributes.h"
#include "anastasis-gtk_dispatch.h"
#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_rows.h"
#include <jansson.h>
@@ -42,36 +43,22 @@
static void
forward_country_selecting (void)
{
- GtkTreeIter iter;
- GtkTreeView *tv;
- GtkTreeModel *model;
- GtkTreeSelection *sel;
- gchar *country_name;
- gchar *country_code;
+ AGRow *row;
json_t *arguments;
- tv = GTK_TREE_VIEW (GCG_get_main_window_object (
- "anastasis_gtk_country_treeview"));
- sel = gtk_tree_view_get_selection (tv);
- if (! gtk_tree_selection_get_selected (sel,
- &model,
- &iter))
+ row = AG_selected_row ("anastasis_gtk_country_selection");
+ if (NULL == row)
{
GNUNET_break (0);
return;
}
- gtk_tree_model_get (model,
- &iter,
- AG_CCMC_COUNTRY_NAME, &country_name,
- AG_CCMC_COUNTRY_CODE, &country_code,
- -1);
arguments = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("country",
- country_name),
+ AG_row_string (row,
+ "name")),
GNUNET_JSON_pack_string ("country_code",
- country_code));
- g_free (country_name);
- g_free (country_code);
+ AG_row_string (row,
+ "code")));
AG_freeze ();
AG_ra = ANASTASIS_redux_action (AG_redux_state,
"select_country",
@@ -206,21 +193,20 @@ AG_forward_user_attributes_collecting (void)
* if they really want to proceed with less than three
* authentication factors.
*
- * @param the dialog
- * @param response_id must be GTK_RESPONSE_OK to proceed
- * @param user_data the builder
+ * @param source the dialog
+ * @param res the result of the user's choice, button 1 to proceed
+ * @param user_data unused
*/
-void
-anastasis_gtk_confirm_multifactor_dialog_response_cb (
- GtkDialog *dialog,
- gint response_id,
- gpointer user_data)
+static void
+confirm_multifactor_response_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
{
- GtkBuilder *builder = user_data;
-
- gtk_window_destroy (GTK_WINDOW (dialog));
- g_object_unref (G_OBJECT (builder));
- if (GTK_RESPONSE_OK != response_id)
+ (void) user_data;
+ if (1 !=
+ gtk_alert_dialog_choose_finish (GTK_ALERT_DIALOG (source),
+ res,
+ NULL))
{
AG_thaw ();
return;
@@ -237,63 +223,72 @@ anastasis_gtk_confirm_multifactor_dialog_response_cb (
* Function called with the result of telling the
* user that they cannot use 1-FA.
*
- * @param the dialog
- * @param response_id must be GTK_RESPONSE_OK to proceed
- * @param user_data the builder
+ * @param source the dialog
+ * @param res the result of the user's choice, unused
+ * @param user_data unused
*/
-void
-anastasis_gtk_deny_singlefactor_dialog_response_cb (
- GtkDialog *dialog,
- gint response_id,
- gpointer user_data)
+static void
+deny_singlefactor_response_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
{
- GtkBuilder *builder = user_data;
-
- gtk_window_destroy (GTK_WINDOW (dialog));
- g_object_unref (G_OBJECT (builder));
+ (void) user_data;
+ (void) gtk_alert_dialog_choose_finish (GTK_ALERT_DIALOG (source),
+ res,
+ NULL);
AG_thaw ();
}
/**
+ * Return the main window, to be used as the parent of our dialogs.
+ *
+ * @return the main window
+ */
+static GtkWindow *
+main_window (void)
+{
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (GCG_get_main_window_object (
+ "anastasis_gtk_auth_button_grid"));
+ return GTK_WINDOW (gtk_widget_get_root (widget));
+}
+
+
+/**
* Launch dialog to question sanity of user providing
* too few authentication methods.
*/
static void
question_sanity (void)
{
- GtkBuilder *builder;
- GtkDialog *ad;
-
- builder = ANASTASIS_GTK_get_new_builder (
- ANASTASIS_GTK_project_data (),
- "anastasis_gtk_warn_multifactor.ui",
- NULL);
- if (NULL == builder)
- {
- GNUNET_break (0);
- return;
- }
- ad = GTK_DIALOG (gtk_builder_get_object (builder,
- "anastasis_gtk_confirm_multifactor_dialog"));
- if (NULL == ad)
- {
- GNUNET_break (0);
- g_object_unref (G_OBJECT (builder));
- return;
- }
- /* show dialog */
- {
- GtkRoot *toplevel;
- GtkWidget *widget;
+ GtkAlertDialog *ad;
+ const char *buttons[] = {
+ _ ("_Back"),
+ _ ("_Next"),
+ NULL
+ };
- widget = GTK_WIDGET (GCG_get_main_window_object (
- "anastasis_gtk_auth_button_grid"));
- toplevel = gtk_widget_get_root (widget);
- gtk_window_set_transient_for (GTK_WINDOW (ad),
- GTK_WINDOW (toplevel));
- gtk_window_present (GTK_WINDOW (ad));
- }
+ ad = gtk_alert_dialog_new (
+ "%s",
+ _ (
+ "To keep your secret secure, we recommend you specify at least three authentication options before proceeding."));
+ gtk_alert_dialog_set_detail (
+ ad,
+ _ ("Select \"back\" to add additional authentication options."));
+ gtk_alert_dialog_set_buttons (ad,
+ buttons);
+ gtk_alert_dialog_set_cancel_button (ad,
+ 0);
+ gtk_alert_dialog_set_default_button (ad,
+ 1);
+ gtk_alert_dialog_choose (ad,
+ main_window (),
+ NULL,
+ &confirm_multifactor_response_cb,
+ NULL);
+ g_object_unref (ad);
}
@@ -303,38 +298,31 @@ question_sanity (void)
static void
refuse_insanity (void)
{
- GtkBuilder *builder;
- GtkDialog *ad;
-
- builder = ANASTASIS_GTK_get_new_builder (
- ANASTASIS_GTK_project_data (),
- "anastasis_gtk_deny_singlefactor.ui",
- NULL);
- if (NULL == builder)
- {
- GNUNET_break (0);
- return;
- }
- ad = GTK_DIALOG (gtk_builder_get_object (builder,
- "anastasis_gtk_deny_singlefactor_dialog"));
- if (NULL == ad)
- {
- GNUNET_break (0);
- g_object_unref (G_OBJECT (builder));
- return;
- }
- /* show dialog */
- {
- GtkRoot *toplevel;
- GtkWidget *widget;
+ GtkAlertDialog *ad;
+ const char *buttons[] = {
+ _ ("_Back"),
+ NULL
+ };
- widget = GTK_WIDGET (GCG_get_main_window_object (
- "anastasis_gtk_auth_button_grid"));
- toplevel = gtk_widget_get_root (widget);
- gtk_window_set_transient_for (GTK_WINDOW (ad),
- GTK_WINDOW (toplevel));
- gtk_window_present (GTK_WINDOW (ad));
- }
+ ad = gtk_alert_dialog_new (
+ "%s",
+ _ (
+ "To keep your secret secure, we require at least two authentication options, and recommend you specify at least three authentication options before proceeding."));
+ gtk_alert_dialog_set_detail (
+ ad,
+ _ ("Select \"back\" to add additional authentication options."));
+ gtk_alert_dialog_set_buttons (ad,
+ buttons);
+ gtk_alert_dialog_set_cancel_button (ad,
+ 0);
+ gtk_alert_dialog_set_default_button (ad,
+ 0);
+ gtk_alert_dialog_choose (ad,
+ main_window (),
+ NULL,
+ &deny_singlefactor_response_cb,
+ NULL);
+ g_object_unref (ad);
}
@@ -403,51 +391,34 @@ forward_secret_editing (void)
static void
forward_secret_selecting (void)
{
- GtkTreeSelection *selection;
- GtkTreeModel *model;
- GtkTreeIter iter;
+ AGRow *row;
json_t *args;
- gchar *provider_url;
- gchar *secret_name;
- gint version;
- gint mask;
- const json_t *providers;
AG_freeze ();
AG_stop_long_action ();
- selection = GTK_TREE_SELECTION (
- GCG_get_main_window_object (
- "anastasis_gtk_secret_selection_treeselection"));
-
- if (! gtk_tree_selection_get_selected (selection,
- &model,
- &iter))
+ row = AG_selected_row ("anastasis_gtk_secret_selection_treeselection");
+ if (NULL == row)
{
GNUNET_break (0);
AG_insensitive ("anastasis_gtk_main_window_forward_button");
return;
}
- gtk_tree_model_get (model,
- &iter,
- AG_SSMC_PROVIDER_URL, &provider_url,
- AG_SSMC_POLICY_VERSION, &version,
- AG_SSMC_ATTRIBUTE_MASK, &mask,
- AG_SSMC_SECRET_NAME, &secret_name,
- AG_SSMC_POLICY_PROVIDER_JSON, &providers,
- -1);
args = GNUNET_JSON_PACK (
GNUNET_JSON_pack_array_incref ("providers",
- (json_t *) providers),
+ AG_row_json (row,
+ "providers")),
GNUNET_JSON_pack_uint64 ("attribute_mask",
- mask),
+ AG_row_uint (row,
+ "attribute_mask")),
GNUNET_JSON_pack_string ("secret_name",
- secret_name));
+ AG_row_string (row,
+ "secret_name")));
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Proceeding with policy version %u at provider %s\n",
- (unsigned int) version,
- provider_url);
- g_free (provider_url);
- g_free (secret_name);
+ (unsigned int) AG_row_uint (row,
+ "version"),
+ AG_row_string (row,
+ "provider_url"));
if (NULL != AG_pd)
{
ANASTASIS_policy_discovery_stop (AG_pd);
diff --git a/src/anastasis/anastasis-gtk_handle-payqr-selection-changed.c b/src/anastasis/anastasis-gtk_handle-payqr-selection-changed.c
@@ -25,24 +25,29 @@
#include <gnunet/gnunet_util_lib.h>
#include "anastasis-gtk_action.h"
#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_rows.h"
#include <jansson.h>
/**
* Callback invoked if the QR code selection changed.
*
- * @param selection A GtkTreeSelection.
+ * @param selection the selection of the list of payment requests
+ * @param pspec unused
* @param user_data user data set when the signal handler was connected.
*/
void
-unpaid_qr_tree_selection_changed_cb (GtkTreeSelection *selection,
+unpaid_qr_tree_selection_changed_cb (GtkSingleSelection *selection,
+ GParamSpec *pspec,
gpointer user_data)
{
- GtkTreeModel *model;
- GtkTreeIter iter;
+ AGRow *row;
GdkClipboard *cb;
GtkWidget *tv;
+ const char *uri = "";
+ (void) pspec;
+ (void) user_data;
tv = GTK_WIDGET (GCG_get_main_window_object ("unpaid_qr_treeview"));
if (NULL == tv)
{
@@ -51,23 +56,15 @@ unpaid_qr_tree_selection_changed_cb (GtkTreeSelection *selection,
}
cb = gtk_widget_get_primary_clipboard (tv);
GNUNET_assert (NULL != cb);
- if (gtk_tree_selection_get_selected (selection,
- &model,
- &iter))
+ row = AG_selected (selection);
+ if (NULL != row)
{
- char *uri;
+ const char *url = AG_row_string (row,
+ "url");
- gtk_tree_model_get (model,
- &iter,
- AG_UQRMC_URL, &uri,
- -1);
- gdk_clipboard_set_text (cb,
- uri);
- g_free (uri);
- }
- else
- {
- gdk_clipboard_set_text (cb,
- "");
+ if (NULL != url)
+ uri = url;
}
+ gdk_clipboard_set_text (cb,
+ uri);
}
diff --git a/src/anastasis/anastasis-gtk_handle-policy-activate.c b/src/anastasis/anastasis-gtk_handle-policy-activate.c
@@ -27,38 +27,39 @@
#include "anastasis-gtk_action.h"
#include "anastasis-gtk_helper.h"
#include "anastasis-gtk_pe.h"
+#include "anastasis-gtk_rows.h"
#include <jansson.h>
+/**
+ * The user double-clicked a row of the policy review.
+ *
+ * @param view the column view showing the policies
+ * @param position index of the row that was activated
+ * @param user_data NULL
+ */
void
anastasis_gtk_review_policy_treeview_row_activated_cb (
- GtkTreeView *tree_view,
- GtkTreePath *path,
- GtkTreeViewColumn *column,
+ GtkColumnView *view,
+ guint position,
gpointer user_data)
{
- GtkTreeModel *tm = gtk_tree_view_get_model (tree_view);
- GtkTreeIter iter;
- guint pindex;
- gboolean is_challenge;
+ GListModel *model = G_LIST_MODEL (gtk_column_view_get_model (view));
+ AGRow *row;
+ gpointer item;
- if (NULL == path)
- return;
- if (! gtk_tree_model_get_iter (tm,
- &iter,
- path))
+ (void) user_data;
+ item = g_list_model_get_item (model,
+ position);
+ if (NULL == item)
{
GNUNET_break (0);
return;
}
- gtk_tree_model_get (tm,
- &iter,
- AG_PRMC_POLICY_INDEX,
- &pindex,
- AG_PRMC_IS_CHALLENGE,
- &is_challenge,
- -1);
- if (! is_challenge)
- return;
- AG_edit_policy (pindex);
+ row = AG_row_of (item);
+ if (AG_row_bool (row,
+ "is_challenge"))
+ AG_edit_policy (AG_row_uint (row,
+ "policy_index"));
+ g_object_unref (item);
}
diff --git a/src/anastasis/anastasis-gtk_handle-policy-button.c b/src/anastasis/anastasis-gtk_handle-policy-button.c
@@ -27,11 +27,12 @@
#include "anastasis-gtk_action.h"
#include "anastasis-gtk_helper.h"
#include "anastasis-gtk_pe.h"
+#include "anastasis-gtk_rows.h"
#include <jansson.h>
/**
- * The user pressed a key while the policy treeview had the focus. 'Delete'
+ * The user pressed a key while the policy list had the focus. 'Delete'
* removes the selected policy or challenge.
*
* @param controller the key controller of the tree view
@@ -49,13 +50,8 @@ anastasis_gtk_review_policy_treeview_key_pressed_cb (
GdkModifierType state,
gpointer user_data)
{
- GtkTreeView *tv;
- GtkTreeSelection *ts;
- GtkTreeModel *tm;
- GtkTreeIter iter;
+ AGRow *row;
guint pindex;
- gboolean is_challenge;
- guint mindex;
(void) controller;
(void) keycode;
@@ -63,29 +59,20 @@ anastasis_gtk_review_policy_treeview_key_pressed_cb (
(void) user_data;
if (GDK_KEY_Delete != keyval)
return FALSE;
- tv = GTK_TREE_VIEW (GCG_get_main_window_object (
- "anastasis_gtk_review_policy_treeview"));
- ts = gtk_tree_view_get_selection (tv);
- if (! gtk_tree_selection_get_selected (ts,
- &tm,
- &iter))
+ row = AG_selected_row ("anastasis_gtk_review_policy_selection");
+ if (NULL == row)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Nothing selected, cannot delete\n");
return FALSE;
}
- gtk_tree_model_get (tm,
- &iter,
- AG_PRMC_POLICY_INDEX,
- &pindex,
- AG_PRMC_IS_CHALLENGE,
- &is_challenge,
- AG_PRMC_METHOD_INDEX,
- &mindex,
- -1);
- if (is_challenge)
+ pindex = AG_row_uint (row,
+ "policy_index");
+ if (AG_row_bool (row,
+ "is_challenge"))
AG_delete_challenge (pindex,
- mindex);
+ AG_row_uint (row,
+ "method_index"));
else
AG_delete_policy (pindex);
return TRUE;
diff --git a/src/anastasis/anastasis-gtk_handle-policy-meta.c b/src/anastasis/anastasis-gtk_handle-policy-meta.c
@@ -28,6 +28,7 @@
#include "anastasis-gtk_action.h"
#include "anastasis-gtk_helper.h"
#include "anastasis-gtk_pe.h"
+#include "anastasis-gtk_rows.h"
#include <jansson.h>
@@ -37,9 +38,10 @@
struct MenuContext
{
/**
- * Reference to the row that the user right-clicked.
+ * The row that the user right-clicked, NULL if they clicked past the
+ * last one.
*/
- GtkTreeRowReference *rr;
+ AGRow *row;
};
@@ -55,60 +57,12 @@ free_menu_context (gpointer cls)
{
struct MenuContext *ctx = cls;
- if (NULL != ctx->rr)
- gtk_tree_row_reference_free (ctx->rr);
+ g_clear_object (&ctx->row);
GNUNET_free (ctx);
}
/**
- * Resolve the row the user right-clicked into its policy coordinates.
- *
- * @param ctx context with the row reference
- * @param[out] pindex set to the index of the policy
- * @param[out] is_challenge set to true if the row is a challenge
- * @param[out] mindex set to the index of the challenge
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-lookup_row (struct MenuContext *ctx,
- guint *pindex,
- gboolean *is_challenge,
- guint *mindex)
-{
- GtkTreePath *path;
- GtkTreeModel *tm;
- GtkTreeIter iter;
-
- if (NULL == ctx->rr)
- return GNUNET_SYSERR;
- path = gtk_tree_row_reference_get_path (ctx->rr);
- tm = gtk_tree_row_reference_get_model (ctx->rr);
- if (NULL == path)
- return GNUNET_SYSERR;
- if (! gtk_tree_model_get_iter (tm,
- &iter,
- path))
- {
- gtk_tree_path_free (path);
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- gtk_tree_path_free (path);
- gtk_tree_model_get (tm,
- &iter,
- AG_PRMC_POLICY_INDEX,
- pindex,
- AG_PRMC_IS_CHALLENGE,
- is_challenge,
- AG_PRMC_METHOD_INDEX,
- mindex,
- -1);
- return GNUNET_OK;
-}
-
-
-/**
* The user selected the 'add policy' menu item.
*
* @param action the activated action
@@ -141,20 +95,18 @@ delete_from_ctx_menu (GSimpleAction *action,
{
struct MenuContext *ctx = user_data;
guint pindex;
- gboolean is_challenge;
- guint mindex;
(void) action;
(void) parameter;
- if (GNUNET_OK !=
- lookup_row (ctx,
- &pindex,
- &is_challenge,
- &mindex))
+ if (NULL == ctx->row)
return;
- if (is_challenge)
+ pindex = AG_row_uint (ctx->row,
+ "policy_index");
+ if (AG_row_bool (ctx->row,
+ "is_challenge"))
AG_delete_challenge (pindex,
- mindex);
+ AG_row_uint (ctx->row,
+ "method_index"));
else
AG_delete_policy (pindex);
}
@@ -173,19 +125,13 @@ edit_from_ctx_menu (GSimpleAction *action,
gpointer user_data)
{
struct MenuContext *ctx = user_data;
- guint pindex;
- gboolean is_challenge;
- guint mindex;
(void) action;
(void) parameter;
- if (GNUNET_OK !=
- lookup_row (ctx,
- &pindex,
- &is_challenge,
- &mindex))
+ if (NULL == ctx->row)
return;
- AG_edit_policy (pindex);
+ AG_edit_policy (AG_row_uint (ctx->row,
+ "policy_index"));
}
@@ -199,7 +145,7 @@ edit_from_ctx_menu (GSimpleAction *action,
*/
static GMenuModel *
build_menu (bool have_row,
- gboolean is_challenge)
+ bool is_challenge)
{
GMenu *menu = g_menu_new ();
@@ -241,9 +187,9 @@ build_menu (bool have_row,
/**
* Install the actions the context menu of @a tv activates; they operate on the
- * row referenced by @a ctx.
+ * row given by @a ctx.
*
- * @param tv the tree view the menu belongs to
+ * @param tv the list the menu belongs to
* @param ctx context to hand to the actions, ownership is taken
*/
static void
@@ -280,10 +226,10 @@ install_actions (GtkWidget *tv,
/**
* Return the context menu of @a tv, creating it on first use. A single
- * popover is reused for the lifetime of the tree view; only its model and
+ * popover is reused for the lifetime of the list; only its model and
* position change.
*
- * @param tv the tree view
+ * @param tv the list
* @return the popover to show
*/
static GtkPopover *
@@ -312,7 +258,7 @@ get_popover (GtkWidget *tv)
/**
- * We got a right-click on the policy treeview: compute which menu items are
+ * We got a right-click on the policy list: compute which menu items are
* applicable and display an appropriate menu.
*
* @param gesture the click gesture, restricted to button 3 by the UI file
@@ -330,58 +276,26 @@ anastasis_gtk_review_policy_treeview_pressed_cb (GtkGestureClick *gesture,
{
GtkWidget *widget
= gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
- GtkTreeView *tv = GTK_TREE_VIEW (widget);
struct MenuContext *ctx;
- GtkTreePath *path;
- gboolean is_challenge = FALSE;
- bool have_row = false;
- int bx;
- int by;
+ AGRow *row;
+ bool is_challenge = false;
(void) n_press;
(void) user_data;
ctx = GNUNET_new (struct MenuContext);
- gtk_tree_view_convert_widget_to_bin_window_coords (tv,
- (int) x,
- (int) y,
- &bx,
- &by);
- if (gtk_tree_view_get_path_at_pos (tv,
- bx,
- by,
- &path,
- NULL,
- NULL,
- NULL))
+ row = AG_row_at (widget,
+ y);
+ if (NULL != row)
{
- GtkTreeModel *tm = gtk_tree_view_get_model (tv);
- GtkTreeIter iter;
-
- if (gtk_tree_model_get_iter (tm,
- &iter,
- path))
- {
- have_row = true;
- ctx->rr = gtk_tree_row_reference_new (tm,
- path);
- gtk_tree_model_get (tm,
- &iter,
- AG_PRMC_IS_CHALLENGE,
- &is_challenge,
- -1);
- }
- else
- {
- /* not sure how we got a path but no iter... */
- GNUNET_break (0);
- }
- gtk_tree_path_free (path);
+ ctx->row = g_object_ref (row);
+ is_challenge = AG_row_bool (row,
+ "is_challenge");
}
install_actions (widget,
ctx);
{
GtkPopover *popover = get_popover (widget);
- GMenuModel *model = build_menu (have_row,
+ GMenuModel *model = build_menu (NULL != row,
is_challenge);
const GdkRectangle at = {
.x = (int) x,
diff --git a/src/anastasis/anastasis-gtk_handle-policy-selection-changed.c b/src/anastasis/anastasis-gtk_handle-policy-selection-changed.c
@@ -27,25 +27,25 @@
#include "anastasis-gtk_action.h"
#include "anastasis-gtk_attributes.h"
#include "anastasis-gtk_handle-identity-changed.h"
+#include "anastasis-gtk_rows.h"
#include <jansson.h>
/**
* Callback invoked if the policy selection changed.
*
- * @param selection A GtkTreeSelection.
+ * @param selection the selection of the secret list
+ * @param pspec unused
* @param user_data user data set when the signal handler was connected.
*/
void
anastasis_gtk_secret_selection_treeselection_changed_cb (
- GtkTreeSelection *selection,
+ GtkSingleSelection *selection,
+ GParamSpec *pspec,
gpointer user_data)
{
- GtkTreeModel *model;
- GtkTreeIter iter;
-
- if (gtk_tree_selection_get_selected (selection,
- &model,
- &iter))
+ (void) pspec;
+ (void) user_data;
+ if (NULL != AG_selected (selection))
{
AG_enable_next ();
}
diff --git a/src/anastasis/anastasis-gtk_handle-policy-version-changed.c b/src/anastasis/anastasis-gtk_handle-policy-version-changed.c
@@ -23,6 +23,7 @@
* @author Christian Grothoff
*/
#include <gnunet/gnunet_util_lib.h>
+#include "anastasis_gtk_util.h"
#include "anastasis-gtk_helper.h"
#include "anastasis-gtk_action.h"
#include "anastasis-gtk_attributes.h"
@@ -118,18 +119,20 @@ change_version (void)
* The user answered the question whether they accept losing their challenge
* solving progress.
*
- * @param dialog the warning dialog
- * @param response_id #GTK_RESPONSE_OK if the user confirmed
+ * @param source the warning dialog
+ * @param res the result of the user's choice, button 0 to proceed
* @param user_data unused
*/
static void
-reset_warning_response_cb (GtkDialog *dialog,
- gint response_id,
+reset_warning_response_cb (GObject *source,
+ GAsyncResult *res,
gpointer user_data)
{
(void) user_data;
- gtk_window_destroy (GTK_WINDOW (dialog));
- if (GTK_RESPONSE_OK == response_id)
+ if (0 ==
+ gtk_alert_dialog_choose_finish (GTK_ALERT_DIALOG (source),
+ res,
+ NULL))
{
change_version ();
return;
@@ -165,22 +168,31 @@ update_policy (void)
"challenge_feedback"))
{
GtkRoot *toplevel;
- GtkWidget *diag;
+ GtkAlertDialog *diag;
+ const char *buttons[] = {
+ _ ("_OK"),
+ _ ("_Cancel"),
+ NULL
+ };
toplevel = gtk_widget_get_root (
GTK_WIDGET (GCG_get_main_window_object (
"anastasis_gtk_main_window")));
- diag = gtk_message_dialog_new (
- GTK_WINDOW (toplevel),
- GTK_DIALOG_MODAL,
- GTK_MESSAGE_QUESTION,
- GTK_BUTTONS_OK_CANCEL,
+ diag = gtk_alert_dialog_new (
+ "%s",
_ ("This action will reset all of your challenge solving progress!"));
- g_signal_connect (diag,
- "response",
- G_CALLBACK (&reset_warning_response_cb),
- NULL);
- gtk_window_present (GTK_WINDOW (diag));
+ gtk_alert_dialog_set_buttons (diag,
+ buttons);
+ gtk_alert_dialog_set_cancel_button (diag,
+ 1);
+ gtk_alert_dialog_set_default_button (diag,
+ 0);
+ gtk_alert_dialog_choose (diag,
+ GTK_WINDOW (toplevel),
+ NULL,
+ &reset_warning_response_cb,
+ NULL);
+ g_object_unref (diag);
return;
}
change_version ();
diff --git a/src/anastasis/anastasis-gtk_handle-print.c b/src/anastasis/anastasis-gtk_handle-print.c
@@ -127,19 +127,13 @@ print_to_file (GtkWindow *parent_window,
{
/* report something went wrong; alas, we don't have a good
error message here (yet)...*/
- GtkDialogFlags flags = GTK_DIALOG_DESTROY_WITH_PARENT;
- GtkWidget *dialog;
-
- dialog = gtk_message_dialog_new (parent_window,
- flags,
- GTK_MESSAGE_ERROR,
- GTK_BUTTONS_CLOSE,
- _ ("Failed to generate PDF file."));
- g_signal_connect (dialog,
- "response",
- G_CALLBACK (>k_window_destroy),
- NULL);
- gtk_window_present (GTK_WINDOW (dialog));
+ GtkAlertDialog *dialog;
+
+ dialog = gtk_alert_dialog_new ("%s",
+ _ ("Failed to generate PDF file."));
+ gtk_alert_dialog_show (dialog,
+ parent_window);
+ g_object_unref (dialog);
}
}
@@ -147,24 +141,26 @@ print_to_file (GtkWindow *parent_window,
/**
* Function called once the user picked the file to print to.
*
- * @param dialog the file selection dialog
- * @param response_id #GTK_RESPONSE_ACCEPT if the user confirmed
- * @param user_data unused
+ * @param source the file dialog
+ * @param res the result of the user's choice
+ * @param user_data the window the dialog belongs to
*/
static void
-print_dialog_response_cb (GtkDialog *dialog,
- gint response_id,
+print_dialog_response_cb (GObject *source,
+ GAsyncResult *res,
gpointer user_data)
{
- GtkWindow *parent_window;
- char *filename = NULL;
-
- (void) user_data;
- parent_window = gtk_window_get_transient_for (GTK_WINDOW (dialog));
- if (GTK_RESPONSE_ACCEPT == response_id)
- filename = ANASTASIS_GTK_filechooser_get_filename_utf8 (
- GTK_FILE_CHOOSER (dialog));
- gtk_window_destroy (GTK_WINDOW (dialog));
+ GtkWindow *parent_window = user_data;
+ GFile *file;
+ char *filename;
+
+ file = gtk_file_dialog_save_finish (GTK_FILE_DIALOG (source),
+ res,
+ NULL);
+ if (NULL == file)
+ return; /* user aborted */
+ filename = ANASTASIS_GTK_file_get_path_utf8 (file);
+ g_object_unref (file);
if (NULL == filename)
return;
print_to_file (parent_window,
@@ -184,9 +180,7 @@ void
anastasis_gtk_print_details_button_clicked_cb (GtkWidget *button,
gpointer user_data)
{
- GtkWidget *dialog;
- GtkFileChooser *chooser;
- GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SAVE;
+ GtkFileDialog *dialog;
GtkWindow *parent_window;
GtkRoot *top;
@@ -201,21 +195,15 @@ anastasis_gtk_print_details_button_clicked_cb (GtkWidget *button,
GNUNET_break (0);
parent_window = NULL;
}
- dialog = gtk_file_chooser_dialog_new ("Save Personal Details to PDF file",
- parent_window,
- action,
- _ ("_Cancel"),
- GTK_RESPONSE_CANCEL,
- _ ("_Save"),
- GTK_RESPONSE_ACCEPT,
- NULL);
- chooser = GTK_FILE_CHOOSER (dialog);
- /* GTK4 always asks before overwriting, no need to request it */
- gtk_file_chooser_set_current_name (chooser,
- _ ("anastasis-personal-details.pdf"));
- g_signal_connect (dialog,
- "response",
- G_CALLBACK (&print_dialog_response_cb),
- NULL);
- gtk_window_present (GTK_WINDOW (dialog));
+ dialog = gtk_file_dialog_new ();
+ gtk_file_dialog_set_title (dialog,
+ _ ("Save Personal Details to PDF file"));
+ gtk_file_dialog_set_initial_name (dialog,
+ _ ("anastasis-personal-details.pdf"));
+ gtk_file_dialog_save (dialog,
+ parent_window,
+ NULL,
+ &print_dialog_response_cb,
+ parent_window);
+ g_object_unref (dialog);
}
diff --git a/src/anastasis/anastasis-gtk_handle-secret-buttons.c b/src/anastasis/anastasis-gtk_handle-secret-buttons.c
@@ -39,18 +39,18 @@ static magic_t magic;
/**
- * Function called from the open-file dialog upon completion.
+ * Function called once the user picked the file holding the secret.
*
- * @param dialog the secret selection dialog
- * @param response_id response code from the dialog
- * @param user_data the builder of the dialog
+ * @param source the file dialog
+ * @param res the result of the user's choice
+ * @param user_data unused
*/
-void
-open_secret_dialog_response_cb (GtkDialog *dialog,
- gint response_id,
- gpointer user_data)
+static void
+open_secret_ready_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
{
- GtkBuilder *builder = GTK_BUILDER (user_data);
+ GFile *file;
char *filename;
const char *fn;
size_t data_size;
@@ -59,16 +59,16 @@ open_secret_dialog_response_cb (GtkDialog *dialog,
GtkEntry *entry;
const char *name;
- if (GTK_RESPONSE_OK != response_id)
- {
- gtk_window_destroy (GTK_WINDOW (dialog));
- g_object_unref (G_OBJECT (builder));
+ (void) user_data;
+ file = gtk_file_dialog_open_finish (GTK_FILE_DIALOG (source),
+ res,
+ NULL);
+ if (NULL == file)
+ return; /* user aborted */
+ filename = ANASTASIS_GTK_file_get_path_utf8 (file);
+ g_object_unref (file);
+ if (NULL == filename)
return;
- }
- filename =
- ANASTASIS_GTK_filechooser_get_filename_utf8 (GTK_FILE_CHOOSER (dialog));
- gtk_window_destroy (GTK_WINDOW (dialog));
- g_object_unref (G_OBJECT (builder));
fn = strrchr (filename,
'/');
if (NULL == fn)
@@ -182,46 +182,34 @@ void
anastasis_gtk_enter_secret_open_button_clicked_cb (GtkButton *button,
gpointer user_data)
{
- GtkWidget *ad;
- GtkBuilder *builder;
+ GtkFileDialog *dialog;
- (void) button;
(void) user_data;
- builder = ANASTASIS_GTK_get_new_builder (
- ANASTASIS_GTK_project_data (),
- "anastasis_gtk_open_secret_dialog.ui",
- NULL);
- if (NULL == builder)
- {
- GNUNET_break (0);
- return;
- }
- ad = GTK_WIDGET (gtk_builder_get_object (builder,
- "open_file_dialog"));
- {
- GtkRoot *toplevel;
-
- toplevel = gtk_widget_get_root (GTK_WIDGET (button));
- gtk_window_set_transient_for (GTK_WINDOW (ad),
- GTK_WINDOW (toplevel));
- gtk_window_present (GTK_WINDOW (ad));
- }
+ dialog = gtk_file_dialog_new ();
+ gtk_file_dialog_set_title (dialog,
+ _ ("Open file with secret to back up"));
+ gtk_file_dialog_open (dialog,
+ GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (button))),
+ NULL,
+ &open_secret_ready_cb,
+ NULL);
+ g_object_unref (dialog);
}
/**
- * Function called from the open-directory dialog upon completion.
+ * Function called once the user picked the file to save the secret in.
*
- * @param dialog the pseudonym selection dialog
- * @param response_id response code from the dialog
- * @param user_data the builder of the dialog
+ * @param source the file dialog
+ * @param res the result of the user's choice
+ * @param user_data unused
*/
-void
-save_secret_dialog_response_cb (GtkDialog *dialog,
- gint response_id,
- gpointer user_data)
+static void
+save_secret_ready_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
{
- GtkBuilder *builder = GTK_BUILDER (user_data);
+ GFile *file;
char *filename;
size_t data_len = 0;
const char *text = NULL;
@@ -240,16 +228,16 @@ save_secret_dialog_response_cb (GtkDialog *dialog,
GNUNET_JSON_spec_end ()
};
- if (GTK_RESPONSE_ACCEPT != response_id)
- {
- gtk_window_destroy (GTK_WINDOW (dialog));
- g_object_unref (G_OBJECT (builder));
+ (void) user_data;
+ file = gtk_file_dialog_save_finish (GTK_FILE_DIALOG (source),
+ res,
+ NULL);
+ if (NULL == file)
+ return; /* user aborted */
+ filename = ANASTASIS_GTK_file_get_path_utf8 (file);
+ g_object_unref (file);
+ if (NULL == filename)
return;
- }
- filename =
- ANASTASIS_GTK_filechooser_get_filename_utf8 (GTK_FILE_CHOOSER (dialog));
- gtk_window_destroy (GTK_WINDOW (dialog));
- g_object_unref (G_OBJECT (builder));
cs = json_object_get (AG_redux_state,
"core_secret");
GNUNET_assert (NULL != cs);
@@ -331,8 +319,7 @@ anastasis_gtk_secret_save_as_button_clicked_cb (GtkButton *button,
.fn = NULL }
};
- GtkWidget *ad;
- GtkBuilder *builder;
+ GtkFileDialog *dialog;
const char *mime = NULL;
const char *fn = NULL;
json_t *cs;
@@ -348,7 +335,6 @@ anastasis_gtk_secret_save_as_button_clicked_cb (GtkButton *button,
GNUNET_JSON_spec_end ()
};
- (void) button;
(void) user_data;
cs = json_object_get (AG_redux_state,
"core_secret");
@@ -357,17 +343,6 @@ anastasis_gtk_secret_save_as_button_clicked_cb (GtkButton *button,
GNUNET_JSON_parse (cs,
spec,
NULL, NULL));
- builder = ANASTASIS_GTK_get_new_builder (
- ANASTASIS_GTK_project_data (),
- "anastasis_gtk_save_secret_dialog.ui",
- NULL);
- if (NULL == builder)
- {
- GNUNET_break (0);
- return;
- }
- ad = GTK_WIDGET (gtk_builder_get_object (builder,
- "save_file_dialog"));
if ( (NULL == fn) &&
(NULL != mime) )
{
@@ -381,16 +356,35 @@ anastasis_gtk_secret_save_as_button_clicked_cb (GtkButton *button,
break;
}
}
- gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (ad),
- fn);
+ dialog = gtk_file_dialog_new ();
+ gtk_file_dialog_set_title (dialog,
+ _ ("Save recovered secret"));
{
- GtkRoot *toplevel;
-
- toplevel = gtk_widget_get_root (GTK_WIDGET (button));
- gtk_window_set_transient_for (GTK_WINDOW (ad),
- GTK_WINDOW (toplevel));
- gtk_window_present (GTK_WINDOW (ad));
+ GtkFileFilter *filter;
+ GListStore *filters;
+
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (filter,
+ _ ("Secrets"));
+ gtk_file_filter_add_pattern (filter,
+ "*.secret");
+ filters = g_list_store_new (GTK_TYPE_FILE_FILTER);
+ g_list_store_append (filters,
+ filter);
+ g_object_unref (filter);
+ gtk_file_dialog_set_filters (dialog,
+ G_LIST_MODEL (filters));
+ g_object_unref (filters);
}
+ if (NULL != fn)
+ gtk_file_dialog_set_initial_name (dialog,
+ fn);
+ gtk_file_dialog_save (dialog,
+ GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (button))),
+ NULL,
+ &save_secret_ready_cb,
+ NULL);
+ g_object_unref (dialog);
}
diff --git a/src/anastasis/anastasis-gtk_helper.h b/src/anastasis/anastasis-gtk_helper.h
@@ -40,387 +40,6 @@ extern bool AG_have_error;
/**
- * Columns of the continent_liststore.
- */
-enum AG_ContinentsModelColumns
-{
- /**
- * A gchararray.
- */
- AG_CMC_CONTINENT_NAME = 0,
-
- /**
- * A gchararray.
- */
- AG_CMC_CONTINENT_NAME_I18N = 1
-};
-
-
-/**
- * Columns of the secret_selection_liststore.
- */
-enum AG_SecretSelectionModelColumns
-{
- /**
- * A gchararray.
- */
- AG_SSMC_PROVIDER_URL = 0,
-
- /**
- * A gint.
- */
- AG_SSMC_POLICY_VERSION = 1,
-
- /**
- * A gint.
- */
- AG_SSMC_ATTRIBUTE_MASK = 2,
-
- /**
- * A gchararray.
- */
- AG_SSMC_SECRET_NAME = 3,
-
- /**
- * A gchararray.
- */
- AG_SSMC_POLICY_DATE_STRING = 4,
-
- /**
- * A guint64.
- */
- AG_SSMC_POLICY_DATE_NUMERIC = 5,
-
- /**
- * A gpointer.
- */
- AG_SSMC_POLICY_PROVIDER_JSON = 6
-};
-
-
-/**
- * Columns of the currency_liststore.
- */
-enum AG_CurrencyModelColumns
-{
- /**
- * A gchararray.
- */
- AG_CMC_CURRENCY_NAME = 0
-};
-
-
-/**
- * Columns of the challenge_status_liststore.
- */
-enum AG_ChallengeStatusModelColumns
-{
- /**
- * A guint.
- */
- AG_CSM_CHALLENGE_OFFSET = 0,
-
- /**
- * A gchararray.
- */
- AG_CSM_CHALLENGE_UUID = 1,
-
- /**
- * A gboolean
- */
- AG_CSM_SOLVED = 2,
-
- /**
- * A gchararray.
- */
- AG_CSM_STATUS = 3,
-
- /**
- * A GdkPixBuf.
- */
- AG_CSM_PAYMENT_QR_CODE = 4,
-
- /**
- * A gchararray.
- */
- AG_CSM_ERROR_MESSAGE = 5,
-
- /**
- * A gchararray.
- */
- AG_CSM_PAYTO_URI = 6,
-
- /**
- * A gboolean.
- */
- AG_CSM_PAYING = 7,
-
- /**
- * A gboolean.
- */
- AG_CSM_HAS_ERROR = 8,
-
- /**
- * A gchararray.
- */
- AG_CSM_COST = 9,
-
- /**
- * A gchararray.
- */
- AG_CSM_REDIRECT_URL = 10,
-
- /**
- * A gboolean.
- */
- AG_CSM_HAVE_REDIRECT = 11,
-
- /**
- * A gboolean
- */
- AG_CSM_NOT_SOLVED = 12,
-
- /**
- * A gchararray
- */
- AG_CSM_TYPE = 13,
-
- /**
- * A gchararray
- */
- AG_CSM_INSTRUCTIONS = 14,
-
- /**
- * A gchararray
- */
- AG_CSM_PROVIDER_URL = 15
-
-};
-
-
-/**
- * Columns of the provider_liststore.
- */
-enum AG_ProviderModelColumns
-{
- /**
- * A gchararray. Contains the provider's base URL.
- */
- AG_PMC_PROVIDER_URL = 0,
-
- /**
- * A gchararray. Status of the provider in text.
- */
- AG_PMC_PROVIDER_STATUS = 1,
-
- /**
- * A gchararray. Color to use when rendering the status.
- */
- AG_PMC_PROVIDER_STATUS_COLOR = 2,
-
- /**
- * A gchararray. Amount as human-readable string with the provider's liability limit,
- */
- AG_PMC_PROVIDER_LIABILITY_LIMIT = 3,
-
- /**
- * A gboolean. true if this provider is enabled.
- */
- AG_PMC_PROVIDER_ENABLED = 4,
-
- /**
- * A gboolean. true if this provider can be enabled.
- */
- AG_PMC_PROVIDER_SENSITIVE = 5,
-
- /**
- * A gboolean. false if this provider can be enabled.
- */
- AG_PMC_PROVIDER_NOT_SENSITIVE = 6,
-
- /**
- * A gchararray. Contains the provider's business name, if known,
- * otherwise the provider's base URL.
- */
- AG_PMC_PROVIDER_NAME = 7,
-
-};
-
-
-/**
- * Columns of the backup_provider_liststore.
- */
-enum AG_BackupProviderColumns
-{
- /**
- * A gchararray.
- */
- AG_BPC_PROVIDER_URL = 0,
-
- /**
- * A guint64
- */
- AG_BPC_BACKUP_VERSION = 1,
-
- /**
- * A gchararray. // FIXME: #6823
- */
- AG_BPC_EXPIRATION_TIME_STR = 2,
-
- /**
- * A gboolean.
- */
- AG_BPC_SUCCESS_FLAG = 3,
-
- /**
- * A gchararray.
- */
- AG_BPC_PROVIDER_NAME = 4
-
-};
-
-/**
- * Columns of the country_liststore.
- */
-enum AG_CountryCodeModelColumns
-{
- /**
- * A gchararray.
- */
- AG_CCMC_COUNTRY_NAME = 0,
-
- /**
- * A gchararray.
- */
- AG_CCMC_COUNTRY_CODE = 1
-
-};
-
-/**
- * Columns of the authentication_methods_liststore.
- */
-enum AG_AuthenticationMethodsModelColumns
-{
- /**
- * A gchararray.
- */
- AG_AMMC_TYPE = 0,
-
- /**
- * A gchararray.
- */
- AG_AMMC_VISUALIZATION = 1,
-
- /**
- * A guint.
- */
- AG_AMMC_INDEX = 2
-};
-
-
-/**
- * Columns of the unpaid_qrcodes_liststore.
- */
-enum AG_UnpaidQrcodesModelColumns
-{
- /**
- * A GdkPixbuf.
- */
- AG_UQRMC_QR_IMAGE = 0,
-
- /**
- * A gchararray.
- */
- AG_UQRMC_URL = 1,
-
- /**
- * A gchararray.
- */
- AG_UQRMC_PROVIDER = 2
-};
-
-
-/**
- * Columns of the policy_review_treestore.
- */
-enum AG_PolicyReviewModelColumns
-{
- /**
- * A gchararray.
- */
- AG_PRMC_POLICY_NAME = 0,
-
- /**
- * A gchararray.
- */
- AG_PRMC_METHOD_TYPE = 1,
-
- /**
- * A gchararray.
- */
- AG_PRMC_COST = 2,
-
- /**
- * A gchararray.
- */
- AG_PRMC_PROVIDER_URL = 3,
-
- /**
- * A gchararray.
- */
- AG_PRMC_EXPIRATION_TIME_STR = 4,
-
- /**
- * A guint.
- */
- AG_PRMC_POLICY_INDEX = 5,
-
- /**
- * A gboolean. True on lines representing challenges.
- */
- AG_PRMC_IS_CHALLENGE = 6,
-
- /**
- * A guint.
- */
- AG_PRMC_METHOD_INDEX = 7,
-
- /**
- * A gboolean. True on lines representing solved challenges.
- */
- AG_PRMC_WAS_SOLVED = 8,
-
- /**
- * Name of the provider (if known, otherwise the provider's URL).
- */
- AG_PRMC_PROVIDER_NAME = 9
-
-};
-
-
-/**
- * Columns in the progress model liststores.
- */
-enum AG_ProgressModelColumns
-{
- /**
- * A gchararray.
- */
- AG_PRGMC_DESCRIPTION = 0,
-
- /**
- * A gchararray.
- */
- AG_PRGMC_REGEX = 1,
-
- /**
- * A gchararray.
- */
- AG_PRGMC_TOOLTIP = 2
-};
-
-
-/**
* Hide widget of the given @a name of the main window
*
* @param name widget to hide
diff --git a/src/anastasis/anastasis-gtk_io.c b/src/anastasis/anastasis-gtk_io.c
@@ -31,30 +31,54 @@
/**
- * Function called from the open-directory dialog upon completion.
+ * Return a file filter matching the files we store states in.
*
- * @param dialog the pseudonym selection dialog
- * @param response_id response code from the dialog
- * @param user_data the builder of the dialog
+ * @return the filter, ownership is passed to the caller
*/
-void
-open_directory_dialog_response_cb (GtkDialog *dialog,
- gint response_id,
- gpointer user_data)
+static GListModel *
+state_filters (void)
+{
+ GtkFileFilter *filter;
+ GListStore *filters;
+
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (filter,
+ _ ("Anastasis state files"));
+ gtk_file_filter_add_pattern (filter,
+ "*.ana");
+ filters = g_list_store_new (GTK_TYPE_FILE_FILTER);
+ g_list_store_append (filters,
+ filter);
+ g_object_unref (filter);
+ return G_LIST_MODEL (filters);
+}
+
+
+/**
+ * Function called once the user picked the state file to open.
+ *
+ * @param source the file dialog
+ * @param res the result of the user's choice
+ * @param user_data unused
+ */
+static void
+open_state_ready_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
{
- GtkBuilder *builder = GTK_BUILDER (user_data);
+ GFile *file;
char *filename;
- if (GTK_RESPONSE_OK != response_id)
- {
- gtk_window_destroy (GTK_WINDOW (dialog));
- g_object_unref (G_OBJECT (builder));
+ (void) user_data;
+ file = gtk_file_dialog_open_finish (GTK_FILE_DIALOG (source),
+ res,
+ NULL);
+ if (NULL == file)
+ return; /* user aborted */
+ filename = ANASTASIS_GTK_file_get_path_utf8 (file);
+ g_object_unref (file);
+ if (NULL == filename)
return;
- }
- filename =
- ANASTASIS_GTK_filechooser_get_filename_utf8 (GTK_FILE_CHOOSER (dialog));
- gtk_window_destroy (GTK_WINDOW (dialog));
- g_object_unref (G_OBJECT (builder));
AG_load (filename);
GNUNET_free (filename);
}
@@ -70,28 +94,23 @@ void
anastasis_gtk_open_state_clicked_cb (GtkButton *button,
gpointer user_data)
{
- GtkWidget *ad;
- GtkBuilder *builder;
-
- builder = ANASTASIS_GTK_get_new_builder (
- ANASTASIS_GTK_project_data (),
- "anastasis_gtk_open_file_dialog.ui",
- NULL);
- if (NULL == builder)
- {
- GNUNET_break (0);
- return;
- }
- ad = GTK_WIDGET (gtk_builder_get_object (builder,
- "open_file_dialog"));
- {
- GtkRoot *toplevel;
+ GtkFileDialog *dialog;
+ GListModel *filters;
- toplevel = gtk_widget_get_root (GTK_WIDGET (button));
- gtk_window_set_transient_for (GTK_WINDOW (ad),
- GTK_WINDOW (toplevel));
- gtk_window_present (GTK_WINDOW (ad));
- }
+ (void) user_data;
+ dialog = gtk_file_dialog_new ();
+ gtk_file_dialog_set_title (dialog,
+ _ ("Open saved Anastasis state"));
+ filters = state_filters ();
+ gtk_file_dialog_set_filters (dialog,
+ filters);
+ g_object_unref (filters);
+ gtk_file_dialog_open (dialog,
+ GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (button))),
+ NULL,
+ &open_state_ready_cb,
+ NULL);
+ g_object_unref (dialog);
}
@@ -160,19 +179,21 @@ write_state (char *filename)
* The user answered the question whether they really want their secret written
* to disk in cleartext.
*
- * @param dialog the warning dialog
- * @param response_id #GTK_RESPONSE_OK if the user confirmed
+ * @param source the warning dialog
+ * @param res the result of the user's choice
* @param user_data the file name to write to
*/
static void
-cleartext_warning_response_cb (GtkDialog *dialog,
- gint response_id,
- gpointer user_data)
+cleartext_warning_response_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
{
char *filename = user_data;
- gtk_window_destroy (GTK_WINDOW (dialog));
- if (GTK_RESPONSE_OK != response_id)
+ if (0 !=
+ gtk_alert_dialog_choose_finish (GTK_ALERT_DIALOG (source),
+ res,
+ NULL))
{
/* user aborted */
GNUNET_free (filename);
@@ -183,16 +204,16 @@ cleartext_warning_response_cb (GtkDialog *dialog,
/**
- * Function called from the save-file dialog upon completion.
+ * Function called once the user picked the file to save the state in.
*
- * @param dialog the file selection dialog
- * @param response_id response code from the dialog
- * @param user_data the builder of the dialog
+ * @param source the file dialog
+ * @param res the result of the user's choice
+ * @param user_data unused
*/
-void
-save_directory_dialog_response_cb (GtkDialog *dialog,
- gint response_id,
- gpointer user_data)
+static void
+save_state_ready_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
{
static const struct DispatchItem save_state[] = {
{ .state = "USER_ATTRIBUTES_COLLECTING",
@@ -200,20 +221,20 @@ save_directory_dialog_response_cb (GtkDialog *dialog,
{ .state = NULL,
.action = NULL }
};
- GtkBuilder *builder = GTK_BUILDER (user_data);
+ GFile *file;
char *filename;
- if (GTK_RESPONSE_ACCEPT != response_id)
- {
- gtk_window_destroy (GTK_WINDOW (dialog));
- g_object_unref (G_OBJECT (builder));
+ (void) user_data;
+ file = gtk_file_dialog_save_finish (GTK_FILE_DIALOG (source),
+ res,
+ NULL);
+ if (NULL == file)
+ return; /* user aborted */
+ filename = ANASTASIS_GTK_file_get_path_utf8 (file);
+ g_object_unref (file);
+ if (NULL == filename)
return;
- }
(void) AG_dispatch (save_state);
- filename =
- ANASTASIS_GTK_filechooser_get_filename_utf8 (GTK_FILE_CHOOSER (dialog));
- gtk_window_destroy (GTK_WINDOW (dialog));
- g_object_unref (G_OBJECT (builder));
/* check if we should warn the user about writing 'core_secret' to disk */
{
@@ -224,23 +245,32 @@ save_directory_dialog_response_cb (GtkDialog *dialog,
if ( (NULL != cs) &&
(! json_is_null (cs)) )
{
- GtkWidget *diag;
+ GtkAlertDialog *diag;
GtkRoot *toplevel;
+ const char *buttons[] = {
+ _ ("_OK"),
+ _ ("_Cancel"),
+ NULL
+ };
toplevel = gtk_widget_get_root (
GTK_WIDGET (GCG_get_main_window_object (
"anastasis_gtk_main_window")));
- diag = gtk_message_dialog_new (
- GTK_WINDOW (toplevel),
- GTK_DIALOG_MODAL,
- GTK_MESSAGE_QUESTION,
- GTK_BUTTONS_OK_CANCEL,
+ diag = gtk_alert_dialog_new (
+ "%s",
_ ("This will write your secret to disk in cleartext!"));
- g_signal_connect (diag,
- "response",
- G_CALLBACK (&cleartext_warning_response_cb),
- filename);
- gtk_window_present (GTK_WINDOW (diag));
+ gtk_alert_dialog_set_buttons (diag,
+ buttons);
+ gtk_alert_dialog_set_cancel_button (diag,
+ 1);
+ gtk_alert_dialog_set_default_button (diag,
+ 0);
+ gtk_alert_dialog_choose (diag,
+ GTK_WINDOW (toplevel),
+ NULL,
+ &cleartext_warning_response_cb,
+ filename);
+ g_object_unref (diag);
return;
}
}
@@ -260,27 +290,23 @@ void
anastasis_gtk_main_window_save_as_button_clicked_cb (GtkButton *button,
gpointer user_data)
{
- GtkWidget *ad;
- GtkBuilder *builder;
- GtkRoot *toplevel;
-
- toplevel = gtk_widget_get_root (GTK_WIDGET (button));
- builder = ANASTASIS_GTK_get_new_builder (
- ANASTASIS_GTK_project_data (),
- "anastasis_gtk_save_file_dialog.ui",
- NULL);
- if (NULL == builder)
- {
- GNUNET_break (0);
- return;
- }
- ad = GTK_WIDGET (gtk_builder_get_object (builder,
- "save_file_dialog"));
- gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (ad),
- "untitled.ana");
- {
- gtk_window_set_transient_for (GTK_WINDOW (ad),
- GTK_WINDOW (toplevel));
- gtk_window_present (GTK_WINDOW (ad));
- }
+ GtkFileDialog *dialog;
+ GListModel *filters;
+
+ (void) user_data;
+ dialog = gtk_file_dialog_new ();
+ gtk_file_dialog_set_title (dialog,
+ _ ("Save Anastasis state"));
+ filters = state_filters ();
+ gtk_file_dialog_set_filters (dialog,
+ filters);
+ g_object_unref (filters);
+ gtk_file_dialog_set_initial_name (dialog,
+ "untitled.ana");
+ gtk_file_dialog_save (dialog,
+ GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (button))),
+ NULL,
+ &save_state_ready_cb,
+ NULL);
+ g_object_unref (dialog);
}
diff --git a/src/anastasis/anastasis-gtk_pe-edit-policy.c b/src/anastasis/anastasis-gtk_pe-edit-policy.c
@@ -58,14 +58,14 @@ struct LineContext
struct EditDialogContext *edc;
/**
- * Our combo box.
+ * Our drop down.
*/
- GtkComboBox *cb;
+ GtkDropDown *cb;
/**
- * Model for our combo box.
+ * The provider URLs our drop down offers.
*/
- GtkTreeModel *model;
+ GtkStringList *model;
/**
* Challenge index for this line.
@@ -141,21 +141,16 @@ anastasis_gtk_policy_edit_dialog_response_cb (
NULL != lctx;
lctx = lctx->next)
{
- GtkTreeIter iter;
- gchar *url;
+ guint selected;
if (! lctx->on)
continue;
- if (! gtk_combo_box_get_active_iter (lctx->cb,
- &iter))
+ selected = gtk_drop_down_get_selected (lctx->cb);
+ if (GTK_INVALID_LIST_POSITION == selected)
{
GNUNET_break (0);
continue;
}
- gtk_tree_model_get (lctx->model,
- &iter,
- 0, &url,
- -1);
GNUNET_assert (0 ==
json_array_append_new (
policy,
@@ -163,8 +158,9 @@ anastasis_gtk_policy_edit_dialog_response_cb (
GNUNET_JSON_pack_uint64 ("authentication_method",
lctx->cindex),
GNUNET_JSON_pack_string ("provider",
- url))));
- g_free (url);
+ gtk_string_list_get_string (
+ lctx->model,
+ selected)))));
}
if (UINT_MAX == edc->pindex)
{
@@ -216,43 +212,40 @@ anastasis_gtk_policy_edit_dialog_response_cb (
/**
- * The user changed an entry in the combo boxy of an edit
+ * The user changed an entry in the drop down of an edit
* dialog. Update the ability to confirm the selection:
* if at least one authentication method is selected, the
* OK button should be sensitive.
*
- * @param widget the combo box that was changed
+ * @param widget the drop down that was changed
+ * @param pspec unused
* @param user_data the `struct EditDialogContext`
*/
static void
-combo_box_changed_cb (
- GtkComboBox *widget,
+drop_down_changed_cb (
+ GtkDropDown *widget,
+ GParamSpec *pspec,
gpointer user_data)
{
struct LineContext *lc = user_data;
struct EditDialogContext *edc = lc->edc;
+ (void) widget;
+ (void) pspec;
/* Update our line context's on/off flag */
{
- GtkTreeIter iter;
+ guint selected = gtk_drop_down_get_selected (lc->cb);
- if (! gtk_combo_box_get_active_iter (lc->cb,
- &iter))
+ if (GTK_INVALID_LIST_POSITION == selected)
{
GNUNET_break (0);
}
else
{
- gchar *url;
-
- gtk_tree_model_get (lc->model,
- &iter,
- 0, &url,
- -1);
lc->on = (0 !=
strcmp (_ ("<off>"),
- url));
- g_free (url);
+ gtk_string_list_get_string (lc->model,
+ selected)));
}
}
/* finally, update "OK" button sensitivity */
@@ -320,27 +313,22 @@ ap_matches (const char *type,
/**
- * Create a GtkListStore containing all of the URLs
- * of Anastasis providers offering @a type as an
- * authentication method.
+ * Create a list with all of the URLs of Anastasis providers offering
+ * @a type as an authentication method.
*
* @return the model
*/
-static GtkTreeModel *
+static GtkStringList *
make_model (const char *type)
{
- GtkListStore *ls;
+ GtkStringList *sl;
json_t *aps;
const char *url;
json_t *ap;
- ls = gtk_list_store_new (1,
- G_TYPE_STRING);
- gtk_list_store_insert_with_values (ls,
- NULL,
- -1,
- 0, _ ("<off>"),
- -1);
+ sl = gtk_string_list_new (NULL);
+ gtk_string_list_append (sl,
+ _ ("<off>"));
aps = json_object_get (AG_redux_state,
"authentication_providers");
GNUNET_break (NULL != aps);
@@ -348,15 +336,12 @@ make_model (const char *type)
if (ap_matches (type,
ap))
{
- gtk_list_store_insert_with_values (ls,
- NULL,
- -1,
- 0, url,
- -1);
+ gtk_string_list_append (sl,
+ url);
}
}
- return GTK_TREE_MODEL (ls);
+ return sl;
}
@@ -370,33 +355,20 @@ static void
select_by_url (const char *url,
struct LineContext *lctx)
{
- GtkTreeIter iter;
+ guint n = g_list_model_get_n_items (G_LIST_MODEL (lctx->model));
- if (! gtk_tree_model_get_iter_first (lctx->model,
- &iter))
+ for (guint i = 0; i < n; i++)
{
- GNUNET_break (0);
- return;
- }
- do {
- gchar *have;
-
- gtk_tree_model_get (lctx->model,
- &iter,
- 0, &have,
- -1);
- if (0 == strcmp (have,
+ if (0 == strcmp (gtk_string_list_get_string (lctx->model,
+ i),
url))
{
- gtk_combo_box_set_active_iter (lctx->cb,
- &iter);
+ gtk_drop_down_set_selected (lctx->cb,
+ i);
lctx->on = true;
- g_free (have);
return;
}
- g_free (have);
- } while (gtk_tree_model_iter_next (lctx->model,
- &iter));
+ }
GNUNET_break (0); /* not found */
}
@@ -414,16 +386,10 @@ select_by_policy (const json_t *methods,
{
size_t index;
json_t *method;
- GtkTreeIter iter;
- if (! gtk_tree_model_get_iter_first (lctx->model,
- &iter))
- {
- GNUNET_break (0);
- return;
- }
- gtk_combo_box_set_active_iter (lctx->cb,
- &iter);
+ /* default to the "<off>" entry we put first */
+ gtk_drop_down_set_selected (lctx->cb,
+ 0);
json_array_foreach (methods, index, method) {
json_int_t am = json_integer_value (json_object_get (method, \
"authentication_method"));
@@ -518,27 +484,17 @@ AG_edit_policy (guint pindex)
labels);
GNUNET_free (labels);
lctx->model = make_model (type);
- cb = gtk_combo_box_new_with_model (lctx->model);
- lctx->cb = GTK_COMBO_BOX (cb);
- {
- GtkCellRenderer *renderer;
-
- renderer = gtk_cell_renderer_text_new ();
- gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cb),
- renderer,
- true);
- gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (cb),
- renderer,
- "text",
- 0);
- }
+ /* the drop down takes over our reference to the model */
+ cb = gtk_drop_down_new (G_LIST_MODEL (lctx->model),
+ NULL);
+ lctx->cb = GTK_DROP_DOWN (cb);
lctx->edc = edc;
GNUNET_CONTAINER_DLL_insert (edc->lc_head,
edc->lc_tail,
lctx);
g_object_connect (cb,
- "signal::changed",
- &combo_box_changed_cb, lctx,
+ "signal::notify::selected",
+ &drop_down_changed_cb, lctx,
NULL);
if (NULL != methods)
select_by_policy (methods,
diff --git a/src/anastasis/anastasis-gtk_progress.c b/src/anastasis/anastasis-gtk_progress.c
@@ -19,21 +19,143 @@
*/
/**
* @file src/anastasis/anastasis-gtk_progress.c
- * @brief Functions dealing with the tree views used to show the user where we are in the process
+ * @brief Functions dealing with the lists that show the user where we are in the process
* @author Christian Grothoff
*/
#include <gnunet/gnunet_util_lib.h>
+#include "anastasis_gtk_util.h"
#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_rows.h"
#include <gdk-pixbuf/gdk-pixbuf.h>
/**
- * The progress tree views only report where we are in the process; the user
- * must not be able to change the selection by clicking. The gesture runs in
- * the capture phase, so claiming the sequence here keeps the click from ever
- * reaching the tree view.
+ * One step of one of the two processes we guide the user through.
+ */
+struct Step
+{
+ /**
+ * Name of the step, as shown to the user.
+ */
+ const char *description;
+
+ /**
+ * Regular expression matching the reducer states this step covers.
+ */
+ const char *regex;
+
+ /**
+ * Long explanation of the step, shown as a tooltip.
+ */
+ const char *tooltip;
+};
+
+
+/**
+ * The steps of making a backup.
+ */
+static const struct Step backup_steps[] = {
+ { .description = N_ ("1. Location & currency"),
+ .regex = "^(CONTINENT|COUNTRY)_SELECTING$",
+ .tooltip = N_ (
+ "Where you live will determine which personal information can be used to identify your backup.") },
+ { .description = N_ ("2. Personal information"),
+ .regex = "^USER_ATTRIBUTES_COLLECTING$",
+ .tooltip = N_ (
+ "Your personal information will not be shared with anyone. But you must provide the same information during recovery.\\") },
+ { .description = N_ ("3. Authorization methods"),
+ .regex = "^AUTHENTICATIONS_EDITING$",
+ .tooltip = N_ (
+ "Specify which methods could be used to authenticate you during recovery.") },
+ { .description = N_ ("4. Recovery policies"),
+ .regex = "^POLICIES_REVIEWING$",
+ .tooltip = N_ (
+ "These are possible ways how the secret could be recovered. Please review and possibly edit the policies.") },
+ { .description = N_ ("5. Enter secret"),
+ .regex = "^SECRET_EDITING$",
+ .tooltip = N_ ("Please specify the secret to be backed up.") },
+ { .description = N_ ("6. Payment (optional)"),
+ .regex = "^.*_PAYING$",
+ .tooltip = N_ (
+ "Some Anastasis providers require payment for their service. Please pay.") },
+ { .description = N_ ("7. Backup completed"),
+ .regex = "BACKUP_FINISHED",
+ .tooltip = N_ (
+ "Your backup is complete. Please do test the recovery on occasion.") },
+ { .description = NULL }
+};
+
+
+/**
+ * The steps of a recovery.
+ */
+static const struct Step recovery_steps[] = {
+ { .description = N_ ("1. Location & currency"),
+ .regex = "^(CONTINENT|COUNTRY)_SELECTING$",
+ .tooltip = N_ (
+ "Where you live will determine which personal information can be used to identify your backup.") },
+ { .description = N_ ("2. Personal information"),
+ .regex = "^USER_ATTRIBUTES_COLLECTING$",
+ .tooltip = N_ (
+ "Your personal information will not be shared with anyone. But you must provide the same information during recovery.") },
+ { .description = N_ ("3. Select secret"),
+ .regex = "^SECRET_SELECTING$",
+ .tooltip = N_ (
+ "Please select which secret to recover. You may switch to a different version or provider.") },
+ { .description = N_ ("4. Solve challenges"),
+ .regex = "^CHALLENGE_SELECTING$",
+ .tooltip = N_ (
+ "Please select an authentication challenge to pass to recover the secret.") },
+ { .description = N_ ("5. Secret recovered"),
+ .regex = "^RECOVERY_FINISHED$",
+ .tooltip = N_ ("Your secret was successfully recovered.") },
+ { .description = NULL }
+};
+
+
+/**
+ * Fill the list called @a name with the given @a steps.
*
- * @param gesture the click gesture of the tree view
+ * @param name name of the model in the UI file
+ * @param steps the steps to show
+ */
+static void
+fill_progress (const char *name,
+ const struct Step *steps)
+{
+ GListStore *list = AG_list (name);
+
+ if (NULL == list)
+ return;
+ for (unsigned int i = 0; NULL != steps[i].description; i++)
+ AG_list_append (list,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("description",
+ _ (steps[i].description)),
+ GNUNET_JSON_pack_string ("regex",
+ steps[i].regex),
+ GNUNET_JSON_pack_string ("tooltip",
+ _ (steps[i].tooltip))));
+}
+
+
+void
+AG_progress_setup (void)
+{
+ fill_progress ("anastasis_gtk_backup_progress_liststore",
+ backup_steps);
+ fill_progress ("anastasis_gtk_recovery_progress_liststore",
+ recovery_steps);
+}
+
+
+/**
+ * The progress lists only report where we are in the process; the user must
+ * not be able to change the selection by clicking. The gesture runs in the
+ * capture phase, so claiming the sequence here keeps the click from ever
+ * reaching the list.
+ *
+ * @param gesture the click gesture of the list
* @param n_press number of presses
* @param x horizontal position of the click
* @param y vertical position of the click
@@ -96,10 +218,11 @@ validate_regex (const char *input,
void
AG_progress_update (void)
{
- GtkTreeSelection *ts;
- GtkTreeModel *tm;
- GtkTreeIter iter;
+ const char *sel_name;
+ GtkSingleSelection *sel;
+ GListModel *steps;
const char *state;
+ guint n;
state = json_string_value (json_object_get (AG_redux_state,
"backup_state"));
@@ -112,45 +235,47 @@ AG_progress_update (void)
GNUNET_break (0);
return;
}
- ts = GTK_TREE_SELECTION (GCG_get_main_window_object (
- "anastasis_gtk_recovery_progress_tree_selection")
- );
+ sel_name = "anastasis_gtk_recovery_progress_tree_selection";
+ steps = G_LIST_MODEL (AG_list (
+ "anastasis_gtk_recovery_progress_liststore"));
}
else
{
- ts = GTK_TREE_SELECTION (GCG_get_main_window_object (
- "anastasis_gtk_backup_progress_tree_selection"));
+ sel_name = "anastasis_gtk_backup_progress_tree_selection";
+ steps = G_LIST_MODEL (AG_list (
+ "anastasis_gtk_backup_progress_liststore"));
}
- if (! gtk_tree_selection_get_selected (ts,
- &tm,
- &iter))
- {
+ if (NULL == steps)
return;
- }
- if (! gtk_tree_model_get_iter_first (tm,
- &iter))
+ sel = GTK_SINGLE_SELECTION (GCG_get_main_window_object (sel_name));
+ if (NULL == sel)
{
GNUNET_break (0);
return;
}
- do {
- char *regex;
-
- gtk_tree_model_get (tm,
- &iter,
- AG_PRGMC_REGEX, ®ex,
- -1);
- if (validate_regex (state,
- regex))
+ /* We only ever move an existing selection; as nothing selects a step to
+ begin with, the lists show no step as the current one. This is how the
+ GTK3 version behaved as well. */
+ if (GTK_INVALID_LIST_POSITION ==
+ gtk_single_selection_get_selected (sel))
+ return;
+ n = g_list_model_get_n_items (steps);
+ for (guint i = 0; i < n; i++)
+ {
+ AGRow *row = g_list_model_get_item (steps,
+ i);
+ bool match;
+
+ match = validate_regex (state,
+ AG_row_string (row,
+ "regex"));
+ g_object_unref (row);
+ if (match)
{
- g_free (regex);
- gtk_tree_selection_select_iter (ts,
- &iter);
+ AG_select_row (sel_name,
+ i);
return;
}
- g_free (regex);
- } while (gtk_tree_model_iter_next (tm,
- &iter));
+ }
GNUNET_break (0);
- return;
}
diff --git a/src/anastasis/anastasis-gtk_progress.h b/src/anastasis/anastasis-gtk_progress.h
@@ -27,6 +27,14 @@
/**
+ * Fill the two lists of steps. To be called once, after the main window
+ * was built.
+ */
+void
+AG_progress_setup (void);
+
+
+/**
* Update progress indicators.
*/
void
diff --git a/src/anastasis/anastasis-gtk_rows.c b/src/anastasis/anastasis-gtk_rows.c
@@ -0,0 +1,610 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2025 Anastasis SARL
+
+ Anastasis is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ Anastasis is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Anastasis; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/**
+ * @file src/anastasis/anastasis-gtk_rows.c
+ * @brief Rows of the GtkColumnViews and the models holding them
+ * @author Christian Grothoff
+ */
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_rows.h"
+
+
+/**
+ * Key under which the row a cell shows is attached to the widget of that
+ * cell. This is how #AG_row_at() finds the row that was clicked.
+ */
+#define ROW_KEY "anastasis-row"
+
+
+/**
+ * One row of one of our lists.
+ */
+struct _AGRow
+{
+ /**
+ * We are a GObject.
+ */
+ GObject parent_instance;
+
+ /**
+ * The values of this row.
+ */
+ json_t *fields;
+
+ /**
+ * Image to render this row with, or NULL.
+ */
+ GdkPaintable *image;
+
+ /**
+ * Rows nested under this one, or NULL if there are none.
+ */
+ GListStore *children;
+};
+
+G_DEFINE_TYPE (AGRow,
+ AG_row,
+ G_TYPE_OBJECT);
+
+
+static void
+AG_row_finalize (GObject *object)
+{
+ AGRow *row = AG_ROW (object);
+
+ json_decref (row->fields);
+ row->fields = NULL;
+ g_clear_object (&row->image);
+ g_clear_object (&row->children);
+ G_OBJECT_CLASS (AG_row_parent_class)->finalize (object);
+}
+
+
+static void
+AG_row_class_init (AGRowClass *klass)
+{
+ G_OBJECT_CLASS (klass)->finalize = &AG_row_finalize;
+}
+
+
+static void
+AG_row_init (AGRow *row)
+{
+ row->fields = json_object ();
+ GNUNET_assert (NULL != row->fields);
+}
+
+
+AGRow *
+AG_row_new (json_t *fields)
+{
+ AGRow *row;
+
+ GNUNET_assert (json_is_object (fields));
+ row = g_object_new (AG_TYPE_ROW,
+ NULL);
+ json_decref (row->fields);
+ row->fields = fields;
+ return row;
+}
+
+
+void
+AG_row_set (AGRow *row,
+ const char *field,
+ json_t *value)
+{
+ GNUNET_assert (0 ==
+ json_object_set_new (row->fields,
+ field,
+ value));
+}
+
+
+const char *
+AG_row_string (AGRow *row,
+ const char *field)
+{
+ return json_string_value (json_object_get (row->fields,
+ field));
+}
+
+
+bool
+AG_row_bool (AGRow *row,
+ const char *field)
+{
+ return json_boolean_value (json_object_get (row->fields,
+ field));
+}
+
+
+uint64_t
+AG_row_uint (AGRow *row,
+ const char *field)
+{
+ return (uint64_t) json_integer_value (json_object_get (row->fields,
+ field));
+}
+
+
+json_t *
+AG_row_json (AGRow *row,
+ const char *field)
+{
+ return json_object_get (row->fields,
+ field);
+}
+
+
+void
+AG_row_set_image (AGRow *row,
+ GdkPaintable *image)
+{
+ g_clear_object (&row->image);
+ row->image = image;
+}
+
+
+GdkPaintable *
+AG_row_image (AGRow *row)
+{
+ return row->image;
+}
+
+
+GListStore *
+AG_row_children (AGRow *row)
+{
+ if (NULL == row->children)
+ row->children = g_list_store_new (AG_TYPE_ROW);
+ return row->children;
+}
+
+
+AGRow *
+AG_row_of (gpointer item)
+{
+ if (NULL == item)
+ return NULL;
+ if (GTK_IS_TREE_LIST_ROW (item))
+ {
+ AGRow *row;
+
+ row = gtk_tree_list_row_get_item (GTK_TREE_LIST_ROW (item));
+ if (NULL == row)
+ return NULL;
+ /* we hand out a borrowed reference; the tree list row holds one of
+ its own for as long as it exists */
+ g_object_unref (row);
+ return AG_ROW (row);
+ }
+ return AG_ROW (item);
+}
+
+
+GListStore *
+AG_list_of (GtkBuilder *builder,
+ const char *name)
+{
+ GObject *model;
+
+ model = gtk_builder_get_object (builder,
+ name);
+ if (NULL == model)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ return G_LIST_STORE (model);
+}
+
+
+GListStore *
+AG_list (const char *name)
+{
+ GObject *model;
+
+ model = GCG_get_main_window_object (name);
+ if (NULL == model)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ return G_LIST_STORE (model);
+}
+
+
+void
+AG_list_clear (const char *name)
+{
+ GListStore *list = AG_list (name);
+
+ if (NULL == list)
+ return;
+ g_list_store_remove_all (list);
+}
+
+
+void
+AG_list_add_row (GListStore *list,
+ AGRow *row)
+{
+ g_list_store_append (list,
+ row);
+ g_object_unref (row);
+}
+
+
+AGRow *
+AG_list_append (GListStore *list,
+ json_t *fields)
+{
+ AGRow *row = AG_row_new (fields);
+
+ g_list_store_append (list,
+ row);
+ g_object_unref (row);
+ return row;
+}
+
+
+AGRow *
+AG_list_add (const char *name,
+ json_t *fields)
+{
+ GListStore *list = AG_list (name);
+
+ if (NULL == list)
+ {
+ json_decref (fields);
+ return NULL;
+ }
+ return AG_list_append (list,
+ fields);
+}
+
+
+AGRow *
+AG_selected (GtkSingleSelection *selection)
+{
+ return AG_row_of (gtk_single_selection_get_selected_item (selection));
+}
+
+
+/**
+ * Return the selection called @a name of the main window.
+ *
+ * @param name name of the selection in the UI file
+ * @return NULL on error
+ */
+static GtkSingleSelection *
+main_window_selection (const char *name)
+{
+ GObject *sel;
+
+ sel = GCG_get_main_window_object (name);
+ if (NULL == sel)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ return GTK_SINGLE_SELECTION (sel);
+}
+
+
+AGRow *
+AG_selected_row (const char *name)
+{
+ GtkSingleSelection *sel = main_window_selection (name);
+
+ if (NULL == sel)
+ return NULL;
+ return AG_selected (sel);
+}
+
+
+void
+AG_select_row (const char *name,
+ guint position)
+{
+ GtkSingleSelection *sel = main_window_selection (name);
+
+ if (NULL == sel)
+ return;
+ gtk_selection_model_select_item (GTK_SELECTION_MODEL (sel),
+ position,
+ TRUE);
+}
+
+
+/**
+ * Search the descendants of @a widget for the cell of a row that covers
+ * @a y in the coordinates of @a view.
+ *
+ * @param widget widget to search below
+ * @param view the column view @a y refers to
+ * @param y vertical position to look for
+ * @return NULL if no cell of any row covers @a y
+ */
+static AGRow *
+find_row_at (GtkWidget *widget,
+ GtkWidget *view,
+ double y)
+{
+ for (GtkWidget *child = gtk_widget_get_first_child (widget);
+ NULL != child;
+ child = gtk_widget_get_next_sibling (child))
+ {
+ AGRow *row;
+ GtkWidget *cell;
+ graphene_rect_t bounds;
+
+ row = g_object_get_data (G_OBJECT (child),
+ ROW_KEY);
+ if (NULL == row)
+ {
+ row = find_row_at (child,
+ view,
+ y);
+ if (NULL != row)
+ return row;
+ continue;
+ }
+ /* our widget does not fill the cell it was put into, so we measure
+ the cell, which does tile the list without gaps */
+ cell = gtk_widget_get_parent (child);
+ if (! gtk_widget_compute_bounds ((NULL != cell) ? cell : child,
+ view,
+ &bounds))
+ continue;
+ if ( (y >= bounds.origin.y) &&
+ (y < bounds.origin.y + bounds.size.height) )
+ return row;
+ }
+ return NULL;
+}
+
+
+AGRow *
+AG_row_at (GtkWidget *view,
+ double y)
+{
+ return find_row_at (view,
+ view,
+ y);
+}
+
+
+/**
+ * Return the @a field of @a row as a number, mapping true to 1.
+ *
+ * @param row the row to inspect
+ * @param field name of the field
+ * @return 0 if there is no such field
+ */
+static json_int_t
+row_number (AGRow *row,
+ const char *field)
+{
+ json_t *val = AG_row_json (row,
+ field);
+
+ if (json_is_boolean (val))
+ return json_is_true (val) ? 1 : 0;
+ return json_integer_value (val);
+}
+
+
+/**
+ * Compare two rows by the field named in @a cls.
+ *
+ * @param a first row
+ * @param b second row
+ * @param cls the `struct AG_SortSpec` of the column being sorted
+ * @return -1, 0 or 1
+ */
+static gint
+compare_rows (gconstpointer a,
+ gconstpointer b,
+ gpointer cls)
+{
+ const struct AG_SortSpec *spec = cls;
+ AGRow *ra = AG_row_of ((gpointer) a);
+ AGRow *rb = AG_row_of ((gpointer) b);
+
+ if (spec->numeric)
+ {
+ json_int_t na = row_number (ra,
+ spec->field);
+ json_int_t nb = row_number (rb,
+ spec->field);
+
+ if (na < nb)
+ return -1;
+ if (na > nb)
+ return 1;
+ return 0;
+ }
+ {
+ const char *sa = AG_row_string (ra,
+ spec->field);
+ const char *sb = AG_row_string (rb,
+ spec->field);
+
+ if (NULL == sa)
+ sa = "";
+ if (NULL == sb)
+ sb = "";
+ return g_utf8_collate (sa,
+ sb);
+ }
+}
+
+
+void
+AG_list_make_sortable (GtkBuilder *builder,
+ const char *view,
+ const char *sort_model,
+ const struct AG_SortSpec *specs)
+{
+ GtkColumnView *cv;
+ GtkSortListModel *slm;
+
+ cv = GTK_COLUMN_VIEW (gtk_builder_get_object (builder,
+ view));
+ slm = GTK_SORT_LIST_MODEL (gtk_builder_get_object (builder,
+ sort_model));
+ if ( (NULL == cv) ||
+ (NULL == slm) )
+ {
+ GNUNET_break (0);
+ return;
+ }
+ for (unsigned int i = 0; NULL != specs[i].column; i++)
+ {
+ GtkColumnViewColumn *col;
+ GtkSorter *sorter;
+
+ col = GTK_COLUMN_VIEW_COLUMN (gtk_builder_get_object (builder,
+ specs[i].column));
+ if (NULL == col)
+ {
+ GNUNET_break (0);
+ continue;
+ }
+ sorter = GTK_SORTER (gtk_custom_sorter_new (&compare_rows,
+ (gpointer) &specs[i],
+ NULL));
+ gtk_column_view_column_set_sorter (col,
+ sorter);
+ g_object_unref (sorter);
+ }
+ /* clicking a column header now sorts the rows the view shows */
+ gtk_sort_list_model_set_sorter (slm,
+ gtk_column_view_get_sorter (cv));
+}
+
+
+/**
+ * Return the rows nested under @a item, if any.
+ *
+ * @param item the parent row
+ * @param cls NULL
+ * @return NULL if @a item has no children
+ */
+static GListModel *
+create_child_model (gpointer item,
+ gpointer cls)
+{
+ AGRow *row = AG_ROW (item);
+
+ (void) cls;
+ if (NULL == row->children)
+ return NULL;
+ return G_LIST_MODEL (g_object_ref (row->children));
+}
+
+
+/**
+ * Columns of the list of authentication methods that can be sorted by
+ * clicking their header.
+ */
+static const struct AG_SortSpec auth_method_sorts[] = {
+ { .column = "auth_method_type",
+ .field = "type" },
+ { .column = NULL }
+};
+
+
+/**
+ * Columns of the list of secrets that can be sorted by clicking their
+ * header.
+ */
+static const struct AG_SortSpec secret_sorts[] = {
+ { .column = "secret_name_column",
+ .field = "secret_name" },
+ { .column = "backup_time_column",
+ .field = "date",
+ .numeric = true },
+ { .column = NULL }
+};
+
+
+void
+AG_lists_setup (void)
+{
+ GtkBuilder *builder = ANASTASIS_GTK_main_loop_get_builder (AG_ml);
+
+ /* the policy review shows a tree, which the UI file cannot express */
+ {
+ GListStore *policies;
+ GtkTreeListModel *tlm;
+ GtkSingleSelection *sel;
+
+ policies = AG_list ("policy_review_treestore");
+ sel = main_window_selection ("anastasis_gtk_review_policy_selection");
+ if ( (NULL == policies) ||
+ (NULL == sel) )
+ {
+ GNUNET_break (0);
+ return;
+ }
+ tlm = gtk_tree_list_model_new (G_LIST_MODEL (g_object_ref (policies)),
+ FALSE, /* we want the GtkTreeListRows */
+ TRUE, /* expand all */
+ &create_child_model,
+ NULL,
+ NULL);
+ gtk_single_selection_set_model (sel,
+ G_LIST_MODEL (tlm));
+ g_object_unref (tlm);
+ }
+ AG_list_make_sortable (builder,
+ "anastasis_gtk_authentication_methods_list",
+ "authentication_methods_sorted",
+ auth_method_sorts);
+ AG_list_make_sortable (builder,
+ "anastasis_secret_selection_treeview",
+ "secret_selection_sorted",
+ secret_sorts);
+}
+
+
+void
+AG_cell_set_row (GtkWidget *widget,
+ AGRow *row)
+{
+ g_object_set_data (G_OBJECT (widget),
+ ROW_KEY,
+ row);
+}
+
+
+AGRow *
+AG_cell_get_row (GtkWidget *widget)
+{
+ return g_object_get_data (G_OBJECT (widget),
+ ROW_KEY);
+}
+
+
+/* end of anastasis-gtk_rows.c */
diff --git a/src/anastasis/anastasis-gtk_rows.h b/src/anastasis/anastasis-gtk_rows.h
@@ -0,0 +1,354 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2025 Anastasis SARL
+
+ Anastasis is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ Anastasis is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Anastasis; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/**
+ * @file src/anastasis/anastasis-gtk_rows.h
+ * @brief Rows of the GtkColumnViews and the models holding them
+ * @author Christian Grothoff
+ */
+#ifndef ANASTASIS_GTK_ROWS_H
+#define ANASTASIS_GTK_ROWS_H
+
+#include <gtk/gtk.h>
+#include <jansson.h>
+
+
+/**
+ * One row of one of our lists. The values shown in the cells and the
+ * bookkeeping data that goes with them are kept as JSON, which is also
+ * what the reducer state we build the rows from is made of.
+ */
+#define AG_TYPE_ROW (AG_row_get_type ())
+
+G_DECLARE_FINAL_TYPE (AGRow,
+ AG_row,
+ AG,
+ ROW,
+ GObject);
+
+
+/**
+ * Create a row with the given @a fields.
+ *
+ * @param fields the values of the row, ownership is taken
+ * @return the new row
+ */
+AGRow *
+AG_row_new (json_t *fields);
+
+
+/**
+ * Change the @a field of @a row to @a value.
+ *
+ * @param row the row to update
+ * @param field name of the field
+ * @param value new value, ownership is taken
+ */
+void
+AG_row_set (AGRow *row,
+ const char *field,
+ json_t *value);
+
+
+/**
+ * Obtain the string @a field of @a row.
+ *
+ * @param row the row to inspect
+ * @param field name of the field
+ * @return NULL if there is no such string field
+ */
+const char *
+AG_row_string (AGRow *row,
+ const char *field);
+
+
+/**
+ * Obtain the boolean @a field of @a row.
+ *
+ * @param row the row to inspect
+ * @param field name of the field
+ * @return false if there is no such field
+ */
+bool
+AG_row_bool (AGRow *row,
+ const char *field);
+
+
+/**
+ * Obtain the numeric @a field of @a row.
+ *
+ * @param row the row to inspect
+ * @param field name of the field
+ * @return 0 if there is no such field
+ */
+uint64_t
+AG_row_uint (AGRow *row,
+ const char *field);
+
+
+/**
+ * Obtain the @a field of @a row as JSON.
+ *
+ * @param row the row to inspect
+ * @param field name of the field
+ * @return NULL if there is no such field, otherwise
+ * a value borrowed from @a row
+ */
+json_t *
+AG_row_json (AGRow *row,
+ const char *field);
+
+
+/**
+ * Set the image @a row is to be rendered with.
+ *
+ * @param row the row to update
+ * @param image the image, ownership is taken; may be NULL
+ */
+void
+AG_row_set_image (AGRow *row,
+ GdkPaintable *image);
+
+
+/**
+ * Return the image @a row is rendered with.
+ *
+ * @param row the row to inspect
+ * @return NULL if @a row has no image
+ */
+GdkPaintable *
+AG_row_image (AGRow *row);
+
+
+/**
+ * Return the rows nested under @a row, creating the list on first use.
+ * Only rows of a tree ever have children.
+ *
+ * @param row the parent row
+ * @return the children of @a row, owned by @a row
+ */
+GListStore *
+AG_row_children (AGRow *row);
+
+
+/* ***************** the lists of rows ****************** */
+
+
+/**
+ * Return the list of rows called @a name in the main window.
+ *
+ * @param name name of the model in the UI file
+ * @return the model, NULL on error
+ */
+GListStore *
+AG_list (const char *name);
+
+
+/**
+ * Return the list of rows called @a name in the window @a builder built.
+ *
+ * @param builder builder of the window holding the list
+ * @param name name of the model in the UI file
+ * @return the model, NULL on error
+ */
+GListStore *
+AG_list_of (GtkBuilder *builder,
+ const char *name);
+
+
+/**
+ * Remove all rows from the list called @a name in the main window.
+ *
+ * @param name name of the model in the UI file
+ */
+void
+AG_list_clear (const char *name);
+
+
+/**
+ * Append a row with the given @a fields to @a list.
+ *
+ * @param list list to append to
+ * @param fields the values of the row, ownership is taken
+ * @return the new row, owned by @a list
+ */
+AGRow *
+AG_list_append (GListStore *list,
+ json_t *fields);
+
+
+/**
+ * Append @a row to @a list. Rows that have children must be filled
+ * before they are added, as only then does the list learn that they can
+ * be expanded.
+ *
+ * @param list list to append to
+ * @param row the row to append, ownership is taken
+ */
+void
+AG_list_add_row (GListStore *list,
+ AGRow *row);
+
+
+/**
+ * Append a row with the given @a fields to the list called @a name in
+ * the main window.
+ *
+ * @param name name of the model in the UI file
+ * @param fields the values of the row, ownership is taken
+ * @return the new row, owned by the list
+ */
+AGRow *
+AG_list_add (const char *name,
+ json_t *fields);
+
+
+/**
+ * Return the row @a item stands for. Rows of a tree are wrapped into a
+ * `GtkTreeListRow` by the model, this unwraps them.
+ *
+ * @param item item of a list model, may be NULL
+ * @return NULL if @a item is NULL
+ */
+AGRow *
+AG_row_of (gpointer item);
+
+
+/**
+ * Attach the row a cell shows to the @a widget rendering it, so that
+ * #AG_row_at() can find the row a click landed on.
+ *
+ * @param widget widget rendering the cell
+ * @param row the row being rendered, may be NULL
+ */
+void
+AG_cell_set_row (GtkWidget *widget,
+ AGRow *row);
+
+
+/**
+ * Return the row the cell rendered by @a widget shows.
+ *
+ * @param widget widget rendering a cell
+ * @return NULL if @a widget is not bound to a row
+ */
+AGRow *
+AG_cell_get_row (GtkWidget *widget);
+
+
+/* ******************* selections ********************* */
+
+
+/**
+ * Return the row that is selected in @a selection.
+ *
+ * @param selection the selection to inspect
+ * @return NULL if nothing is selected
+ */
+AGRow *
+AG_selected (GtkSingleSelection *selection);
+
+
+/**
+ * Return the row selected in the selection called @a name of the main
+ * window.
+ *
+ * @param name name of the selection in the UI file
+ * @return NULL if nothing is selected
+ */
+AGRow *
+AG_selected_row (const char *name);
+
+
+/**
+ * Select the row at @a position in the selection called @a name of the
+ * main window.
+ *
+ * @param name name of the selection in the UI file
+ * @param position index of the row to select
+ */
+void
+AG_select_row (const char *name,
+ guint position);
+
+
+/**
+ * Return the row shown at @a y in @a view. Used to find the row a
+ * context menu was opened on.
+ *
+ * @param view the column view that was clicked
+ * @param y vertical position of the click, in widget coordinates
+ * @return NULL if there is no row at @a y
+ */
+AGRow *
+AG_row_at (GtkWidget *view,
+ double y);
+
+
+/* ****************** sorting ******************* */
+
+
+/**
+ * How a column of a list is to be sorted.
+ */
+struct AG_SortSpec
+{
+ /**
+ * Name of the `GtkColumnViewColumn` in the UI file.
+ */
+ const char *column;
+
+ /**
+ * Field of the rows to compare.
+ */
+ const char *field;
+
+ /**
+ * True to compare the field numerically instead of as text.
+ */
+ bool numeric;
+};
+
+
+/**
+ * Make the columns given in @a specs of the column view called @a view
+ * sort the rows of @a sort_model. Both objects are looked up in @a
+ * builder.
+ *
+ * @param builder builder of the window holding the list
+ * @param view name of the `GtkColumnView` in the UI file
+ * @param sort_model name of the `GtkSortListModel` feeding @a view
+ * @param specs the columns to make sortable, terminated by a NULL column
+ */
+void
+AG_list_make_sortable (GtkBuilder *builder,
+ const char *view,
+ const char *sort_model,
+ const struct AG_SortSpec *specs);
+
+
+/**
+ * Set up those models of the main window that cannot be described in
+ * the UI file. To be called once, after the main window was built.
+ */
+void
+AG_lists_setup (void);
+
+
+#endif
diff --git a/src/anastasis/meson.build b/src/anastasis/meson.build
@@ -4,6 +4,7 @@ anastasis_gtk_SOURCES = [
'anastasis-gtk_action.c',
'anastasis-gtk_attributes.c',
'anastasis-gtk_autocomplete.c',
+ 'anastasis-gtk_cells.c',
'anastasis-gtk_dispatch.c',
'anastasis-gtk_io.c',
'anastasis-gtk_handle-add-provider.c',
@@ -17,7 +18,6 @@ anastasis_gtk_SOURCES = [
'anastasis-gtk_handle-clear-secret-clicked.c',
'anastasis-gtk_handle-continent-selected.c',
'anastasis-gtk_handle-country-activated.c',
- 'anastasis-gtk_handle-country-unselected.c',
'anastasis-gtk_handle-core-secret-changed.c',
'anastasis-gtk_handle-core-secret-name-changed.c',
'anastasis-gtk_handle-expiration-change.c',
@@ -43,6 +43,7 @@ anastasis_gtk_SOURCES = [
'anastasis-gtk_pe-delete-policy.c',
'anastasis-gtk_pe-edit-policy.c',
'anastasis-gtk_progress.c',
+ 'anastasis-gtk_rows.c',
]
if haru_dep.found()
diff --git a/src/include/anastasis_gtk_util.h b/src/include/anastasis_gtk_util.h
@@ -29,6 +29,12 @@
#define _(String) dgettext (PACKAGE, String)
+/**
+ * Mark a string for translation without translating it right here, for
+ * strings in static tables. Translate them with #_() where they are used.
+ */
+#define N_(String) String
+
#define DIR_SEPARATOR_STR "/"
#define DIR_SEPARATOR '/'
@@ -117,13 +123,13 @@ ANASTASIS_GTK_from_loc_to_utf8 (const char *str_loc);
/**
- * Returns filename from a file chooser, encoded in UTF-8.
+ * Returns the path of @a file, encoded in UTF-8.
*
- * @param fc file chooser to inspect
- * @return selected filename as UTF-8, NULL on errors
+ * @param fc the file the user picked, may be NULL
+ * @return path of @a file as UTF-8, NULL on errors
*/
char *
-ANASTASIS_GTK_filechooser_get_filename_utf8 (GtkFileChooser *fc);
+ANASTASIS_GTK_file_get_path_utf8 (GFile *file);
/**
diff --git a/src/util/builder.c b/src/util/builder.c
@@ -115,20 +115,4 @@ ANASTASIS_GTK_get_new_builder (const struct GNUNET_OS_ProjectData *pd,
}
-void
-GNUNET_FS_GTK_remove_treestore_subtree (GtkTreeStore *ts,
- GtkTreeIter *root)
-{
- GtkTreeIter child;
-
- while (gtk_tree_model_iter_children (GTK_TREE_MODEL (ts),
- &child,
- root))
- GNUNET_FS_GTK_remove_treestore_subtree (ts,
- &child);
- gtk_tree_store_remove (ts,
- root);
-}
-
-
/* end of builder.c */
diff --git a/src/util/nls.c b/src/util/nls.c
@@ -134,17 +134,14 @@ from_filename_to_utf8 (gchar *filename)
char *
-ANASTASIS_GTK_filechooser_get_filename_utf8 (GtkFileChooser *fc)
+ANASTASIS_GTK_file_get_path_utf8 (GFile *file)
{
char *filename_utf8;
- GFile *file;
gchar *filename;
- file = gtk_file_chooser_get_file (fc);
if (NULL == file)
return NULL;
filename = g_file_get_path (file);
- g_object_unref (file);
if (NULL == filename)
return NULL;
filename_utf8 = from_filename_to_utf8 (filename);