[gnome-terminal/wip/text-objects: 1/4] Text-Objects: Add GUI for configuration of per-profile text-objects.



commit f6eb94ba96bd6988222c5ce7d139254d1a8bc9d3
Author: Rodolfo Granata <warlock cc gmail com>
Date:   Thu May 9 08:20:06 2019 -0400

    Text-Objects: Add GUI for configuration of per-profile text-objects.

 src/Makefile.am                    |   2 +
 src/org.gnome.Terminal.gschema.xml |  13 ++
 src/preferences.ui                 | 260 +++++++++++++++++++++++++++
 src/profile-editor.c               |   9 +-
 src/profile-editor.h               |   6 +
 src/profile-text-objects.c         | 359 +++++++++++++++++++++++++++++++++++++
 src/profile-text-objects.h         |  31 ++++
 7 files changed, 679 insertions(+), 1 deletion(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index b36cc8e3..ff1bae58 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -91,6 +91,8 @@ gnome_terminal_server_SOURCES = \
        terminal-version.h \
        terminal-window.c \
        terminal-window.h \
+       profile-text-objects.c \
+       profile-text-objects.h \
        $(NULL)
 
 nodist_gnome_terminal_server_SOURCES = $(gnome_terminal_server_built)
diff --git a/src/org.gnome.Terminal.gschema.xml b/src/org.gnome.Terminal.gschema.xml
index e4e0c003..9becfce7 100644
--- a/src/org.gnome.Terminal.gschema.xml
+++ b/src/org.gnome.Terminal.gschema.xml
@@ -395,6 +395,19 @@
       <default>'narrow'</default>
       <summary>Whether ambiguous-width characters are narrow or wide when using UTF-8 encoding</summary>
     </key>
+
+    <key name="text-objects" type="a{s(ssi)}">
+      <default>{
+        'GNOME issues': (
+            '((?i)gnome|community|world)/([-+./a-z]+)#([[:digit:]]+)',
+            'https://gitlab.gnome.org/$1/$2/issues/$3', 0),
+        'freedesktop issues': (
+            '((?i)xorg|wayland|PulseAudio)/([-+./[:alnum:]]+)#([[:digit:]]+)',
+            'https://gitlab.freedesktop.org/$1/$2/issues/$3', 0)
+        }
+      </default>
+      <description>A list of tuples describing: name, match, rewrite, priority</description>
+    </key>
   </schema>
 
   <!-- Keybinding settings -->
diff --git a/src/preferences.ui b/src/preferences.ui
index f42b652c..b0c3ca15 100644
--- a/src/preferences.ui
+++ b/src/preferences.ui
@@ -2089,6 +2089,92 @@
                             <property name="tab_fill">False</property>
                           </packing>
                         </child>
+                        <child>
+                          <object class="GtkBox">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="orientation">vertical</property>
+                            <property name="border_width">12</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <object class="GtkScrolledWindow" id="scroll-txt-obj-list">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="shadow_type">in</property>
+                                <property name="hexpand">True</property>
+                                <property name="vexpand">True</property>
+                                <child>
+                                  <object class="GtkTreeView" id="text-object-list">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <child internal-child="selection">
+                                      <object class="GtkTreeSelection"/>
+                                    </child>
+                                  </object>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkButtonBox" id="txt-obj-bottonbox">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="layout_style">end</property>
+                                <property name="margin_top">6</property>
+                                <property name="spacing">6</property>
+                                <child>
+                                  <object class="GtkButton" id="txt-obj-remove-button">
+                                    <property name="label" translatable="yes">Remove</property>
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="use_underline">True</property>
+                                    <property name="focus_on_click">False</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">False</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkButton" id="txt-obj-edit-button">
+                                    <property name="label" translatable="yes">Edit</property>
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="use_underline">True</property>
+                                    <property name="focus_on_click">False</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">False</property>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">1</property>
+                                <property name="pack_type">end</property>
+                              </packing>
+                            </child>
+                          </object>
+                        </child>
+                        <child type="tab">
+                          <object class="GtkLabel" id="text_objects">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="label" translatable="yes">Text Objects</property>
+                            <property name="use_underline">True</property>
+                            <property name="justify">center</property>
+                          </object>
+                          <packing>
+                            <property name="position">5</property>
+                            <property name="tab_fill">False</property>
+                          </packing>
+                        </child>
                       </object>
                     </child>
                   </object>
@@ -2289,4 +2375,178 @@
       </object>
     </child>
   </object>
+  <object class="GtkPopover" id="txt-obj-dialog">
+    <property name="can_focus">False</property>
+    <child>
+      <object class="GtkBox">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="border_width">12</property>
+        <property name="spacing">6</property>
+        <child>
+          <object class="GtkLabel" id="txt-obj-title">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="xalign">0</property>
+            <property name="margin_bottom">6</property>
+            <property name="label">Text Object</property>
+            <attributes>
+              <attribute name="weight" value="bold"/>
+            </attributes>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="txt-obj-name-lbl">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="xalign">0</property>
+            <property name="label">Rule Name</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkEntry" id="txt-obj-name">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="activates_default">True</property>
+            <!-- make the popover have some minimum width -->
+            <property name="width_chars">48</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="txt-obj-match-lbl">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="xalign">0</property>
+            <property name="label">Regex Match</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">3</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkEntry" id="txt-obj-match">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="activates_default">True</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">4</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="txt-obj-rewrite-lbl">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="xalign">0</property>
+            <property name="label">URL Template</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">5</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkEntry" id="txt-obj-rewrite">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="activates_default">True</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">6</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="txt-obj-prio-lbl">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="xalign">0</property>
+            <property name="label">Rule Priority</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">7</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkEntry" id="txt-obj-prio">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="activates_default">True</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">8</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButtonBox">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="layout_style">start</property>
+            <property name="margin_top">6</property>
+            <property name="spacing">6</property>
+            <child>
+              <object class="GtkButton" id="txt-obj-edit-cancel">
+                <property name="label" translatable="yes">Cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="pack_type">end</property>
+                <property name="position">0</property>
+                <property name="secondary">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="txt-obj-edit-save">
+                <property name="label" translatable="yes">Save</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="pack_type">end</property>
+                <property name="position">1</property>
+                <property name="secondary">True</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">9</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
 </interface>
diff --git a/src/profile-editor.c b/src/profile-editor.c
index 1912c8cf..d9f0926f 100644
--- a/src/profile-editor.c
+++ b/src/profile-editor.c
@@ -36,6 +36,7 @@
 #include "terminal-util.h"
 #include "terminal-profiles-list.h"
 #include "terminal-libgsystem.h"
+#include "profile-text-objects.h"
 
 
 /* Wrapper around g_signal_connect that maintains a list of the
@@ -55,7 +56,7 @@ profile_prefs_register_signal_handler (gpointer instance,
   g_array_append_val (the_pref_data->profile_signals, sig);
 }
 
-static gulong
+gulong
 profile_prefs_signal_connect (gpointer instance,
                               const gchar *detailed_signal,
                               GCallback c_handler,
@@ -834,6 +835,9 @@ profile_prefs_init (void)
   gtk_label_set_text ((GtkLabel *) gtk_builder_get_object (builder, "cell-height-scale-label"),
                       text);
   g_free (text);
+
+  /* Text-Objects: setup list */
+  profile_text_objects_init();
 }
 
 /* Called each time the user switches away from a profile, so it's no longer being edited */
@@ -1254,6 +1258,9 @@ profile_prefs_load (const char *uuid, GSettings *profile)
                                w,
                                "active-id",
                                G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET);
+
+  /* Text-Object options */
+  profile_text_objects_load(profile);
 }
 
 /* Called once per Preferences window, to destroy stuff that doesn't depend on the profile being edited */
diff --git a/src/profile-editor.h b/src/profile-editor.h
index e83d3931..ffc07650 100644
--- a/src/profile-editor.h
+++ b/src/profile-editor.h
@@ -32,6 +32,12 @@ void profile_prefs_unload (void);
 void profile_prefs_load (const char *uuid,
                          GSettings *profile);
 
+gulong
+profile_prefs_signal_connect (gpointer instance,
+                              const gchar *detailed_signal,
+                              GCallback c_handler,
+                              gpointer data);
+
 G_END_DECLS
 
 #endif /* TERMINAL_PROFILE_EDITOR_H */
diff --git a/src/profile-text-objects.c b/src/profile-text-objects.c
new file mode 100644
index 00000000..23e42f4d
--- /dev/null
+++ b/src/profile-text-objects.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright © 2019 Rodolfo Granata
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "profile-editor.h"
+#include "profile-text-objects.h"
+#include "terminal-libgsystem.h"
+#include "terminal-prefs.h"
+
+#include <glib/gi18n.h>
+
+enum {
+  TEXT_OBJ_NAME = 0,
+  TEXT_OBJ_MATCH,
+  TEXT_OBJ_REWRITE,
+  TEXT_OBJ_PRIO,
+  TEXT_OBJ_N_COLS,
+};
+
+/* setup the the profile editor's text-object tab */
+void
+profile_text_objects_init(void)
+{
+  GtkBuilder *builder = the_pref_data->builder;
+  GtkTreeView *tree_view =
+    (GtkTreeView *) gtk_builder_get_object (builder, "text-object-list");
+  /* setup tree */
+  GtkTreeSelection *select = gtk_tree_view_get_selection (tree_view);
+  gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE);
+
+  /* Setup cell renderers */
+  GtkCellRenderer *renderer;
+  GtkTreeViewColumn *column;
+
+  renderer = gtk_cell_renderer_text_new ();
+  column = gtk_tree_view_column_new_with_attributes (
+      _("Name"), renderer, "text", TEXT_OBJ_NAME, NULL);
+  gtk_tree_view_append_column (tree_view, column);
+
+  renderer = gtk_cell_renderer_text_new ();
+  column = gtk_tree_view_column_new_with_attributes (
+      _("Regex Match"), renderer, "text", TEXT_OBJ_MATCH, NULL);
+  gtk_tree_view_append_column (tree_view, column);
+
+  renderer = gtk_cell_renderer_text_new ();
+  column = gtk_tree_view_column_new_with_attributes (
+      _("URL Template"), renderer, "text", TEXT_OBJ_REWRITE, NULL);
+  gtk_tree_view_append_column (tree_view, column);
+
+  renderer = gtk_cell_renderer_text_new ();
+  column = gtk_tree_view_column_new_with_attributes (
+      _("Rank"), renderer, "text", TEXT_OBJ_PRIO, NULL);
+  gtk_tree_view_append_column (tree_view, column);
+}
+
+static void
+profile_save_text_objects(GSettings *profile, GtkListStore *store) {
+  /* setup gvariant dict */
+  GVariantBuilder txt_objs;
+  g_variant_builder_init (&txt_objs, G_VARIANT_TYPE ("a{s(ssi)}"));
+
+  GtkTreeIter list_iter;
+  /* iterate over the store and write values into variant */
+  gboolean more_rows =
+    gtk_tree_model_get_iter_first ((GtkTreeModel *) store, &list_iter);
+  while (more_rows) {
+    gs_free gchar *name, *match, *rewrite;
+    gint prio;
+    gtk_tree_model_get ((GtkTreeModel *) store, &list_iter,
+        TEXT_OBJ_NAME, &name,
+        TEXT_OBJ_MATCH, &match,
+        TEXT_OBJ_REWRITE, &rewrite,
+        TEXT_OBJ_PRIO, &prio,
+        -1);
+    /* encode text-object into variant dict */
+    g_variant_builder_add (&txt_objs, "{s(ssi)}", name, match, rewrite, prio);
+    more_rows = gtk_tree_model_iter_next ((GtkTreeModel *) store, &list_iter);
+  }
+
+  GVariant *text_objects = g_variant_builder_end (&txt_objs);
+  g_settings_set_value (profile, "text-objects", text_objects);
+}
+
+/* callback triggered for opening the dialog to edit/add a text-object */
+static void
+edit_text_object_cb (GtkWidget *button, gpointer user_data)
+{
+  GtkBuilder *builder = the_pref_data->builder;
+  GtkPopover *txtobj_dialog =
+    (GtkPopover *) gtk_builder_get_object (builder, "txt-obj-dialog");
+
+  gtk_popover_set_relative_to (txtobj_dialog, button);
+  gtk_popover_set_position (txtobj_dialog, GTK_POS_BOTTOM);
+  gtk_popover_set_default_widget (txtobj_dialog,
+      GTK_WIDGET (gtk_builder_get_object (builder, "txt-obj-edit-save")));
+
+#if GTK_CHECK_VERSION (3, 22, 0)
+  gtk_popover_popup (txtobj_dialog);
+#else
+  gtk_widget_show (GTK_WIDGET (txtobj_dialog));
+#endif
+}
+
+/* callback for removing a text-object in the profile editor */
+static void
+remove_text_object_cb (GtkWidget *button, GSettings *profile)
+{
+  GtkBuilder *builder = the_pref_data->builder;
+  GtkTreeView *tree_view =
+    (GtkTreeView *) gtk_builder_get_object (builder, "text-object-list");
+  GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view);
+
+  GtkTreeIter list_iter;
+  GtkListStore *store;
+  /* check if there's an item selected */
+  if (gtk_tree_selection_get_selected (selection, (GtkTreeModel **) &store, &list_iter)) {
+    gtk_list_store_remove (store, &list_iter);
+    profile_save_text_objects (profile, store);
+  }
+}
+
+/* callback to hide the text-object editor dialog */
+static void
+edit_text_object_hide_cb (GtkButton *button, gpointer user_data)
+{
+  GtkBuilder *builder = the_pref_data->builder;
+  GtkPopover *txtobj_dialog =
+    (GtkPopover *) gtk_builder_get_object (builder, "txt-obj-dialog");
+
+#if GTK_CHECK_VERSION (3, 22, 0)
+  gtk_popover_popdown (txtobj_dialog);
+#else
+  gtk_widget_hide (GTK_WIDGET (txtobj_dialog));
+#endif
+}
+
+/* callback for saving contents to text-object editor dialog */
+static void
+edit_text_object_save_cb (GtkButton *button, GSettings *profile)
+{
+  GtkBuilder *builder = the_pref_data->builder;
+
+  /* get contents of the dialog text fields */
+  const char *name = gtk_entry_get_text (
+      GTK_ENTRY (gtk_builder_get_object (builder, "txt-obj-name")));
+  const char *match = gtk_entry_get_text (
+      GTK_ENTRY (gtk_builder_get_object (builder, "txt-obj-match")));
+  const char *rewrite = gtk_entry_get_text (
+      GTK_ENTRY (gtk_builder_get_object (builder, "txt-obj-rewrite")));
+  const char *sprio = gtk_entry_get_text (
+      GTK_ENTRY (gtk_builder_get_object (builder, "txt-obj-prio")));
+  gint64 prio = g_ascii_strtoll (sprio, NULL, 10);
+
+  GtkTreeView *tree_view =
+    (GtkTreeView *) gtk_builder_get_object (builder, "text-object-list");
+  GtkListStore *store = (GtkListStore *) gtk_tree_view_get_model(tree_view);
+
+  /* search the profile's text-objects by rule-name to check if we're
+   * adding a new text-object or we're replacing an existing one */
+  GtkTreeIter list_iter;
+  gboolean more_rows =
+    gtk_tree_model_get_iter_first ((GtkTreeModel *) store, &list_iter);
+  gboolean existing = FALSE;
+  while (more_rows && !existing) {
+    gs_free gchar *_name;
+    gtk_tree_model_get ((GtkTreeModel *) store, &list_iter,
+                        TEXT_OBJ_NAME, &_name, -1);
+    existing = (g_strcmp0 (name, _name) == 0);
+    /* found an existing text-object with the same rule-name, we'll replace it */
+    if (existing) {
+      break;
+    }
+    more_rows = gtk_tree_model_iter_next ((GtkTreeModel *) store, &list_iter);
+  }
+
+  /* if no existing rule found we'll append a new one */
+  if (!existing) {
+    gtk_list_store_append (store, &list_iter);
+  }
+
+  /* update/add the text-object */
+  gtk_list_store_set (store, &list_iter,
+      TEXT_OBJ_NAME, name,
+      TEXT_OBJ_MATCH, match,
+      TEXT_OBJ_REWRITE, rewrite,
+      TEXT_OBJ_PRIO, prio,
+      -1);
+
+  profile_save_text_objects (profile, store);
+
+  /* Hide the popover */
+  edit_text_object_hide_cb (button, NULL);
+}
+
+/* callback to pre-populate text-object edition dialog with current selection */
+static void
+toggle_text_object_buttons_cb (GtkTreeSelection *selection, gpointer user_data)
+{
+  GtkBuilder *builder = the_pref_data->builder;
+  GtkTreeIter list_iter;
+  GtkListStore *store;
+  /* get active selection */
+  gboolean selected = gtk_tree_selection_get_selected (
+      selection, (GtkTreeModel **) &store, &list_iter);
+
+  /* disable the 'Remove' button if there's no active selection */
+  gtk_widget_set_sensitive (
+      GTK_WIDGET (gtk_builder_get_object (builder, "txt-obj-remove-button")), selected);
+
+  /* copy selected values into edit dialog for later use */
+  if (selected) {
+    gs_free gchar *name, *match, *rewrite, *sprio;
+    gint prio;
+    gtk_tree_model_get ((GtkTreeModel *) store, &list_iter,
+        TEXT_OBJ_NAME, &name,
+        TEXT_OBJ_MATCH, &match,
+        TEXT_OBJ_REWRITE, &rewrite,
+        TEXT_OBJ_PRIO, &prio,
+        -1);
+    sprio = g_strdup_printf("%d", prio);
+    gtk_entry_set_text (
+        GTK_ENTRY (gtk_builder_get_object (builder, "txt-obj-name")), name);
+    gtk_entry_set_text (
+        GTK_ENTRY (gtk_builder_get_object (builder, "txt-obj-match")), match);
+    gtk_entry_set_text (
+        GTK_ENTRY (gtk_builder_get_object (builder, "txt-obj-rewrite")), rewrite);
+    gtk_entry_set_text (
+        GTK_ENTRY (gtk_builder_get_object (builder, "txt-obj-prio")), sprio);
+  }
+}
+
+static void
+validate_text_object_cb (GtkEntry *entry, gpointer user_data)
+{
+  GtkBuilder *builder = the_pref_data->builder;
+  gboolean valid = TRUE;
+
+  /* check that all fields have some text */
+  const char *text = gtk_entry_get_text (entry);
+  valid &= (strlen(text) > 0);
+
+  /* Check that Priority/Rank is an integer */
+  GtkEntry *prio = GTK_ENTRY (gtk_builder_get_object (builder, "txt-obj-prio"));
+  if (entry == prio) {
+    gchar *end_ptr = NULL;
+    g_ascii_strtoll (text, &end_ptr, 10);
+    /* check that end_ptr actually advanced */
+    valid &= (end_ptr == text + strlen(text));
+  }
+
+  /* react to input being valid: set warning icon and toggle Save button */
+  gtk_entry_set_icon_from_icon_name (
+      entry,
+      GTK_ENTRY_ICON_PRIMARY, valid ? NULL : "dialog-warning");
+  gtk_widget_set_sensitive (
+      (GtkWidget *) gtk_builder_get_object (builder, "txt-obj-edit-save"), valid);
+}
+
+/* bind the text-object GUI elements to callbacks */
+static void
+profile_text_objects_bind(GSettings *profile)
+{
+  GtkBuilder *builder = the_pref_data->builder;
+  profile_prefs_signal_connect (
+      gtk_builder_get_object (builder, "txt-obj-remove-button"),
+      "clicked", G_CALLBACK (remove_text_object_cb), profile);
+
+  profile_prefs_signal_connect (
+      gtk_builder_get_object (builder, "txt-obj-edit-button"),
+      "clicked", G_CALLBACK (edit_text_object_cb), NULL);
+
+  profile_prefs_signal_connect (
+      gtk_builder_get_object (builder, "txt-obj-edit-cancel"),
+      "clicked", G_CALLBACK (edit_text_object_hide_cb), NULL);
+
+  profile_prefs_signal_connect (
+      gtk_builder_get_object (builder, "txt-obj-edit-save"),
+      "clicked", G_CALLBACK (edit_text_object_save_cb), profile);
+
+  profile_prefs_signal_connect (
+      (GtkEntry *) gtk_builder_get_object (builder, "txt-obj-match"),
+      "changed", G_CALLBACK (validate_text_object_cb), NULL);
+
+  profile_prefs_signal_connect (
+      (GtkEntry *) gtk_builder_get_object (builder, "txt-obj-rewrite"),
+      "changed", G_CALLBACK (validate_text_object_cb), NULL);
+
+  profile_prefs_signal_connect (
+      (GtkEntry *) gtk_builder_get_object (builder, "txt-obj-prio"),
+      "changed", G_CALLBACK (validate_text_object_cb), NULL);
+
+  /* disable edit buttons if we can't write settings */
+  if (g_settings_is_writable (profile, "text-objects")) {
+    /* react to selection changes in the text-object list */
+    GtkTreeSelection *selection = gtk_tree_view_get_selection (
+        (GtkTreeView *) gtk_builder_get_object (builder, "text-object-list"));
+    profile_prefs_signal_connect (
+        selection,
+        "changed", G_CALLBACK (toggle_text_object_buttons_cb), NULL);
+  } else {
+    gtk_widget_set_sensitive (
+        GTK_WIDGET (gtk_builder_get_object (builder, "txt-obj-remove-button")), FALSE);
+    gtk_widget_set_sensitive (
+        GTK_WIDGET (gtk_builder_get_object (builder, "txt-obj-edit-button")), FALSE);
+  }
+}
+
+void
+profile_text_objects_load(GSettings *profile)
+{
+  /* Create the model */
+  GtkListStore *store = gtk_list_store_new (
+      TEXT_OBJ_N_COLS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);
+
+  /* Load text-objects config from profile gsettings */
+  gs_unref_variant GVariant *text_objects =
+    g_settings_get_value(profile, "text-objects");
+
+  GVariantIter viter;
+  const gchar *name, *match, *rewrite;
+  gint prio;
+
+  /* populate the text-object table */
+  g_variant_iter_init (&viter, text_objects);
+  while (g_variant_iter_next (&viter, "{&s(&s&si)}", &name, &match, &rewrite, &prio)) {
+    GtkTreeIter list_iter;
+    gtk_list_store_append (store, &list_iter);
+    gtk_list_store_set (store, &list_iter,
+        TEXT_OBJ_NAME, name,
+        TEXT_OBJ_MATCH, match,
+        TEXT_OBJ_REWRITE, rewrite,
+        TEXT_OBJ_PRIO, prio,
+        -1);
+  }
+
+  /* link the data to the view */
+  GtkBuilder *builder = the_pref_data->builder;
+  GtkTreeView *tree_view =
+    (GtkTreeView *) gtk_builder_get_object (builder, "text-object-list");
+  gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL(store));
+
+  /* bind buttons and actions */
+  profile_text_objects_bind (profile);
+}
diff --git a/src/profile-text-objects.h b/src/profile-text-objects.h
new file mode 100644
index 00000000..ae4f328b
--- /dev/null
+++ b/src/profile-text-objects.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright © 2019 Rodolfo Granata
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TEXT_OBJECTS_H
+#define TEXT_OBJECTS_H
+
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+void profile_text_objects_init(void);
+void profile_text_objects_load(GSettings *profile);
+
+G_END_DECLS
+
+#endif /* TEXT_OBJECTS_H */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]