[calls] sip: Introduce CallsSipAccountWidget



commit 23cb050f612fb44e9c007038e7875e0c917f43c5
Author: Evangelos Ribeiro Tzaras <evangelos tzaras puri sm>
Date:   Mon Jul 5 11:55:44 2021 +0200

    sip: Introduce CallsSipAccountWidget
    
    This widget can be used to add new accounts or edit existing ones.
    
    First part of #264.

 plugins/sip/calls-sip-account-widget.c | 458 +++++++++++++++++++++++++++++++++
 plugins/sip/calls-sip-account-widget.h |  42 +++
 plugins/sip/meson.build                |  11 +
 plugins/sip/sip-account-widget.ui      | 193 ++++++++++++++
 plugins/sip/sip.gresources.xml         |   6 +
 5 files changed, 710 insertions(+)
---
diff --git a/plugins/sip/calls-sip-account-widget.c b/plugins/sip/calls-sip-account-widget.c
new file mode 100644
index 00000000..c7d4fb40
--- /dev/null
+++ b/plugins/sip/calls-sip-account-widget.c
@@ -0,0 +1,458 @@
+/*
+ * Copyright (C) 2021 Purism SPC
+ *
+ * This file is part of Calls.
+ *
+ * Calls 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.
+ *
+ * Calls 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 Calls.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Evangelos Ribeiro Tzaras <evangelos tzaras puri sm>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+#define G_LOG_DOMAIN "CallsSipAccountWidget"
+
+#include "calls-sip-account-widget.h"
+#include "calls-sip-provider.h"
+#include "calls-sip-origin.h"
+
+/**
+ * Section:calls-sip-account-widget
+ * short_description: A #GtkWidget to edit or add SIP accounts
+ * @Title: CallsSipAccountWidget
+ *
+ * This #GtkWidget allows the user to add a new or edit an existing SIP account.
+ */
+
+
+enum {
+  PROP_0,
+  PROP_PROVIDER,
+  PROP_ORIGIN,
+  PROP_LAST_PROP
+};
+static GParamSpec *props[PROP_LAST_PROP];
+
+
+struct _CallsSipAccountWidget {
+  GtkBox            parent;
+
+  /* Header bar */
+  GtkWidget        *header_add;
+  GtkSpinner       *spinner_add;
+  GtkWidget        *header_edit;
+  GtkSpinner       *spinner_edit;
+  GtkWidget        *login_btn;
+  GtkWidget        *apply_btn;
+  GtkWidget        *delete_btn;
+
+  /* widgets for editing account credentials */
+  GtkEntry         *host;
+  GtkEntry         *display_name;
+  GtkEntry         *user;
+  GtkEntry         *password;
+  GtkEntry         *port;
+  HdyComboRow      *protocol;
+  GListStore       *protocols_store; /* bound model for protocol HdyComboRow */
+
+
+  /* properties */
+  CallsSipProvider *provider;
+  CallsSipOrigin   *origin; /* nullable to add a new account */
+
+  /* misc */
+  gboolean          connecting;
+};
+
+G_DEFINE_TYPE (CallsSipAccountWidget, calls_sip_account_widget, GTK_TYPE_BOX)
+
+
+static gboolean
+is_form_valid (CallsSipAccountWidget *self)
+{
+  g_assert (CALLS_IS_SIP_ACCOUNT_WIDGET (self));
+
+  /* TODO perform some sanity checks */
+  return TRUE;
+}
+
+static gboolean
+is_form_filled (CallsSipAccountWidget *self)
+{
+  g_assert (CALLS_IS_SIP_ACCOUNT_WIDGET (self));
+
+  return
+    g_strcmp0 (gtk_entry_get_text (GTK_ENTRY (self->host)), "") != 0 &&
+    g_strcmp0 (gtk_entry_get_text (GTK_ENTRY (self->user)), "") != 0 &&
+    g_strcmp0 (gtk_entry_get_text (GTK_ENTRY (self->password)), "") != 0 &&
+    g_strcmp0 (gtk_entry_get_text (GTK_ENTRY (self->port)), "") != 0;
+}
+
+
+static void
+on_text_changed (CallsSipAccountWidget *self)
+{
+  g_assert (CALLS_IS_SIP_ACCOUNT_WIDGET (self));
+
+  gtk_widget_set_sensitive (self->login_btn,
+                            is_form_filled (self) &&
+                            is_form_valid (self));
+
+  gtk_widget_set_sensitive (self->apply_btn,
+                            is_form_filled (self) &&
+                            is_form_valid (self));
+}
+
+
+static void
+update_header (CallsSipAccountWidget *self)
+{
+  g_assert (CALLS_IS_SIP_ACCOUNT_WIDGET (self));
+
+  if (self->origin) {
+    gtk_widget_show (self->header_edit);
+    gtk_widget_hide (self->header_add);
+
+  } else {
+    gtk_widget_show (self->header_add);
+    gtk_widget_hide (self->header_edit);
+  }
+
+  if (self->connecting) {
+    gtk_spinner_start (self->spinner_add);
+    gtk_spinner_start (self->spinner_edit);
+  } else {
+    gtk_spinner_stop (self->spinner_add);
+    gtk_spinner_stop (self->spinner_edit);
+  }
+}
+
+
+static gboolean
+find_protocol (CallsSipAccountWidget *self,
+               const char            *protocol,
+               guint                 *index)
+{
+  guint len;
+  g_assert (CALLS_IS_SIP_ACCOUNT_WIDGET (self));
+
+  len = g_list_model_get_n_items (G_LIST_MODEL (self->protocols_store));
+  for (guint i = 0; i < len; i++) {
+    g_autoptr (HdyValueObject) obj =
+      g_list_model_get_item (G_LIST_MODEL (self->protocols_store), i);
+    const char *prot = hdy_value_object_get_string (obj);
+
+    if (g_strcmp0 (protocol, prot) == 0) {
+      if (index)
+        *index = i;
+      return TRUE;
+    }
+  }
+
+  g_debug ("Could not find protocol '%s'", protocol);
+  return FALSE;
+}
+
+
+static void
+clear_form (CallsSipAccountWidget *self)
+{
+  g_assert (CALLS_IS_SIP_ACCOUNT_WIDGET (self));
+
+  gtk_entry_set_text (self->host, "");
+  gtk_entry_set_text (self->display_name, "");
+  gtk_entry_set_text (self->user, "");
+  gtk_entry_set_text (self->password, "");
+  gtk_entry_set_text (self->port, "0");
+  hdy_combo_row_set_selected_index (self->protocol, 0);
+
+  self->origin = NULL;
+
+  update_header (self);
+
+  if (gtk_widget_get_can_focus (GTK_WIDGET (self->host)))
+    gtk_widget_grab_focus (GTK_WIDGET (self->host));
+}
+
+
+static void
+edit_form (CallsSipAccountWidget *self,
+           CallsSipOrigin        *origin)
+{
+  g_autofree char *host = NULL;
+  g_autofree char *display_name = NULL;
+  g_autofree char *user = NULL;
+  g_autofree char *password = NULL;
+  g_autofree char *port_str = NULL;
+  g_autofree char *protocol = NULL;
+  gint port;
+  guint protocol_index;
+
+  g_assert (CALLS_IS_SIP_ACCOUNT_WIDGET (self));
+
+  if (!origin) {
+    clear_form (self);
+    return;
+  }
+
+  g_assert (CALLS_IS_SIP_ORIGIN (origin));
+
+  self->origin = origin;
+
+  g_object_get (origin,
+                "host", &host,
+                "display-name", &display_name,
+                "user", &user,
+                "password", &password,
+                "port", &port,
+                "transport-protocol", &protocol,
+                NULL);
+
+  port_str = g_strdup_printf ("%d", port);
+
+  /* The following should always succeed,
+     TODO inform user in the error case
+     related issue #275 https://source.puri.sm/Librem5/calls/-/issues/275
+  */
+  if (!find_protocol (self, protocol, &protocol_index))
+    protocol_index = 0;
+
+  /* set UI elements */
+  gtk_entry_set_text (self->host, host);
+  gtk_entry_set_text (self->display_name, display_name ?: "");
+  gtk_entry_set_text (self->user, user);
+  gtk_entry_set_text (self->password, password);
+  gtk_entry_set_text (self->port, port_str);
+  hdy_combo_row_set_selected_index (self->protocol, protocol_index);
+
+  gtk_widget_set_sensitive (self->apply_btn, FALSE);
+
+  update_header (self);
+
+  if (gtk_widget_get_can_focus (GTK_WIDGET (self->host)))
+    gtk_widget_grab_focus (GTK_WIDGET (self->host));
+}
+
+
+static void
+on_login_clicked (CallsSipAccountWidget *self)
+{
+  CallsSipOrigin *origin;
+
+  g_debug ("Logging into newly created account");
+
+  origin = calls_sip_provider_add_origin (self->provider,
+                                          gtk_entry_get_text (GTK_ENTRY (self->host)),
+                                          gtk_entry_get_text (GTK_ENTRY (self->user)),
+                                          gtk_entry_get_text (GTK_ENTRY (self->password)),
+                                          gtk_entry_get_text (GTK_ENTRY (self->display_name)),
+                                          "UDP",
+                                          0);
+
+  self->origin = origin;
+  update_header (self);
+}
+
+
+static void
+on_delete_clicked (CallsSipAccountWidget *self)
+{
+  g_debug ("Deleting account");
+
+  calls_sip_provider_remove_origin (self->provider, self->origin);
+  self->origin = NULL;
+
+  update_header (self);
+}
+
+
+static void
+on_apply_clicked (CallsSipAccountWidget *self)
+{
+  g_debug ("Applying changes to the account");
+
+  calls_sip_origin_set_credentials (self->origin,
+                                    gtk_entry_get_text (self->host),
+                                    gtk_entry_get_text (self->user),
+                                    gtk_entry_get_text (self->password),
+                                    gtk_entry_get_text (self->display_name),
+                                    "UDP",
+                                    0,
+                                    TRUE);
+
+  update_header (self);
+}
+
+
+static void
+calls_sip_account_widget_set_property (GObject      *object,
+                                       guint         property_id,
+                                       const GValue *value,
+                                       GParamSpec   *pspec)
+{
+  CallsSipAccountWidget *self = CALLS_SIP_ACCOUNT_WIDGET (object);
+
+  switch (property_id) {
+  case PROP_PROVIDER:
+    self->provider = g_value_get_object (value);
+    break;
+
+  case PROP_ORIGIN:
+    calls_sip_account_widget_set_origin (self, g_value_get_object (value));
+    break;
+
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    break;
+  }
+}
+
+
+static void
+calls_sip_account_widget_get_property (GObject    *object,
+                                       guint       property_id,
+                                       GValue     *value,
+                                       GParamSpec *pspec)
+{
+  CallsSipAccountWidget *self = CALLS_SIP_ACCOUNT_WIDGET (object);
+
+  switch (property_id) {
+  case PROP_ORIGIN:
+    g_value_set_object (value, calls_sip_account_widget_get_origin (self));
+    break;
+
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    break;
+  }
+}
+
+static void
+calls_sip_account_widget_dispose (GObject *object)
+{
+  CallsSipAccountWidget *self = CALLS_SIP_ACCOUNT_WIDGET (object);
+
+  g_clear_object (&self->protocols_store);
+
+  G_OBJECT_CLASS (calls_sip_account_widget_parent_class)->dispose (object);
+}
+
+
+static void
+calls_sip_account_widget_class_init (CallsSipAccountWidgetClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->set_property = calls_sip_account_widget_set_property;
+  object_class->get_property = calls_sip_account_widget_get_property;
+  object_class->dispose = calls_sip_account_widget_dispose;
+
+  props[PROP_PROVIDER] =
+    g_param_spec_object ("provider",
+                         "Provider",
+                         "The SIP provider",
+                         CALLS_TYPE_SIP_PROVIDER,
+                         G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+  props[PROP_ORIGIN] =
+    g_param_spec_object ("origin",
+                         "Origin",
+                         "The origin to edit",
+                         CALLS_TYPE_SIP_ORIGIN,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
+
+  gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Calls/ui/sip-account-widget.ui");
+  gtk_widget_class_bind_template_child (widget_class, CallsSipAccountWidget, header_add);
+  gtk_widget_class_bind_template_child (widget_class, CallsSipAccountWidget, spinner_add);
+  gtk_widget_class_bind_template_child (widget_class, CallsSipAccountWidget, header_edit);
+  gtk_widget_class_bind_template_child (widget_class, CallsSipAccountWidget, spinner_edit);
+
+  gtk_widget_class_bind_template_child (widget_class, CallsSipAccountWidget, login_btn);
+  gtk_widget_class_bind_template_child (widget_class, CallsSipAccountWidget, apply_btn);
+
+  gtk_widget_class_bind_template_child (widget_class, CallsSipAccountWidget, host);
+  gtk_widget_class_bind_template_child (widget_class, CallsSipAccountWidget, display_name);
+  gtk_widget_class_bind_template_child (widget_class, CallsSipAccountWidget, user);
+  gtk_widget_class_bind_template_child (widget_class, CallsSipAccountWidget, password);
+  gtk_widget_class_bind_template_child (widget_class, CallsSipAccountWidget, port);
+  gtk_widget_class_bind_template_child (widget_class, CallsSipAccountWidget, protocol);
+
+  gtk_widget_class_bind_template_callback (widget_class, on_login_clicked);
+  gtk_widget_class_bind_template_callback (widget_class, on_delete_clicked);
+  gtk_widget_class_bind_template_callback (widget_class, on_apply_clicked);
+  gtk_widget_class_bind_template_callback (widget_class, on_text_changed);
+}
+
+
+static void
+calls_sip_account_widget_init (CallsSipAccountWidget *self)
+{
+  HdyValueObject *obj;
+
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  self->protocols_store = g_list_store_new (HDY_TYPE_VALUE_OBJECT);
+
+  obj = hdy_value_object_new_string ("UDP");
+  g_list_store_insert (self->protocols_store, 0, obj);
+  g_clear_object (&obj);
+
+  obj = hdy_value_object_new_string ("TCP");
+  g_list_store_insert (self->protocols_store, 1, obj);
+  g_clear_object (&obj);
+
+  obj = hdy_value_object_new_string ("TLS");
+  g_list_store_insert (self->protocols_store, 2, obj);
+  g_clear_object (&obj);
+
+  hdy_combo_row_bind_name_model (self->protocol,
+                                 G_LIST_MODEL (self->protocols_store),
+                                 (HdyComboRowGetNameFunc) hdy_value_object_dup_string,
+                                 NULL, NULL);
+}
+
+
+CallsSipAccountWidget *
+calls_sip_account_widget_new (CallsSipProvider *provider)
+{
+  g_return_val_if_fail (CALLS_IS_SIP_PROVIDER (provider), NULL);
+
+  return g_object_new (CALLS_TYPE_SIP_ACCOUNT_WIDGET,
+                       "provider", provider,
+                       NULL);
+}
+
+
+CallsSipOrigin *
+calls_sip_account_widget_get_origin (CallsSipAccountWidget *self)
+{
+  g_return_val_if_fail (CALLS_IS_SIP_ACCOUNT_WIDGET (self), NULL);
+
+  return self->origin;
+}
+
+
+void
+calls_sip_account_widget_set_origin (CallsSipAccountWidget *self,
+                                     CallsSipOrigin        *origin)
+{
+  g_return_if_fail (CALLS_IS_SIP_ACCOUNT_WIDGET (self));
+  g_return_if_fail (!origin || CALLS_IS_SIP_ORIGIN (origin));
+
+  edit_form (self, origin);
+}
diff --git a/plugins/sip/calls-sip-account-widget.h b/plugins/sip/calls-sip-account-widget.h
new file mode 100644
index 00000000..1f310e5d
--- /dev/null
+++ b/plugins/sip/calls-sip-account-widget.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 Purism SPC
+ *
+ * This file is part of Calls.
+ *
+ * Calls 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.
+ *
+ * Calls 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 Calls.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Evangelos Ribeiro Tzaras <evangelos tzaras puri sm>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+#pragma once
+
+#include "calls-sip-provider.h"
+
+#include <handy.h>
+
+G_BEGIN_DECLS
+
+#define CALLS_TYPE_SIP_ACCOUNT_WIDGET (calls_sip_account_widget_get_type ())
+
+G_DECLARE_FINAL_TYPE (CallsSipAccountWidget, calls_sip_account_widget, CALLS, SIP_ACCOUNT_WIDGET, GtkBox)
+
+CallsSipAccountWidget *calls_sip_account_widget_new           (CallsSipProvider      *provider);
+void                   calls_sip_account_widget_set_origin    (CallsSipAccountWidget *self,
+                                                               CallsSipOrigin        *origin);
+CallsSipOrigin        *calls_sip_account_widget_get_origin    (CallsSipAccountWidget *self);
+
+G_END_DECLS
diff --git a/plugins/sip/meson.build b/plugins/sip/meson.build
index 722cdef1..69115798 100644
--- a/plugins/sip/meson.build
+++ b/plugins/sip/meson.build
@@ -36,6 +36,7 @@ sip_deps = [
   dependency('gobject-2.0'),
   dependency('gstreamer-1.0'),
   dependency('gtk+-3.0'),
+  dependency('libhandy-1', version: '>= 1.1.90'),
   dependency('libpeas-1.0'),
   dependency('sofia-sip-ua-glib'),
 ]
@@ -48,6 +49,7 @@ sip_sources = files(
     'calls-sip-util.c', 'calls-sip-util.h',
     'calls-sip-media-manager.c', 'calls-sip-media-manager.h',
     'calls-sip-media-pipeline.c', 'calls-sip-media-pipeline.h',
+    'calls-sip-account-widget.c', 'calls-sip-account-widget.h',
     'gst-rfc3551.c', 'gst-rfc3551.h',
   ]
 )
@@ -64,6 +66,15 @@ sip_enums = gnome.mkenums('calls-sip-enums',
 
 sip_sources += sip_enums
 
+sip_resources = gnome.compile_resources(
+  'sip-resources',
+  'sip.gresources.xml',
+  source_dir: '.',
+  c_name: 'call',
+)
+
+sip_sources += sip_resources
+
 calls_sip = shared_module(
   'sip',
   sip_sources,
diff --git a/plugins/sip/sip-account-widget.ui b/plugins/sip/sip-account-widget.ui
new file mode 100644
index 00000000..f4b0478c
--- /dev/null
+++ b/plugins/sip/sip-account-widget.ui
@@ -0,0 +1,193 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.24"/>
+  <requires lib="libhandy" version="1.0"/>
+  <template class="CallsSipAccountWidget" parent="GtkBox">
+    <property name="visible">True</property>
+    <property name="orientation">vertical</property>
+    
+    <child>
+      <object class="HdyHeaderBar" id="header_add">
+        <property name="title" translatable="yes">Add Account</property>
+        <property name="show-close-button">True</property>
+        <child>
+          <object class="GtkButton" id="login_btn">
+            <property name="visible">True</property>
+            <property name="use-underline">True</property>
+            <property name="label" translatable="yes">_Log In</property>
+            <signal name="clicked" handler="on_login_clicked" swapped="yes"/>
+            <style>
+              <class name="suggested-action"/>
+            </style>
+          </object>
+          <packing>
+            <property name="pack_type">end</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkSpinner" id="spinner_add">
+            <property name="visible">True</property>
+          </object>
+          <packing>
+            <property name="pack_type">end</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    
+    <child>
+      <object class="HdyHeaderBar" id="header_edit">
+        <property name="visible">True</property>
+        <property name="show-close-button">True</property>
+        <property name="title" translatable="yes">Manage Account</property>
+        <child>
+          <object class="GtkButton" id="apply_btn">
+            <property name="visible">True</property>
+            <property name="use-underline">True</property>
+            <property name="label" translatable="yes">_Apply</property>
+            <signal name="clicked" handler="on_apply_clicked" swapped="yes"/>
+            <style>
+              <class name="suggested-action"/>
+            </style>
+          </object>
+          <packing>
+            <property name="pack_type">end</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="delete_btn">
+            <property name="visible">True</property>
+            <property name="use-underline">True</property>
+            <property name="label" translatable="yes">_Delete</property>
+            <signal name="clicked" handler="on_delete_clicked" swapped="yes"/>
+            <style>
+              <class name="destructive-action"/>
+            </style>
+          </object>
+          <packing>
+            <property name="pack_type">end</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkSpinner" id="spinner_edit">
+            <property name="visible">True</property>
+          </object>
+          <packing>
+            <property name="pack_type">end</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    
+    <child>
+      <object class="HdyPreferencesPage">
+        <property name="visible">True</property>
+        <child>
+          <object class="HdyPreferencesGroup">
+            <property name="visible">True</property>
+            <child>
+              <object class="HdyActionRow">
+                <property name="visible">True</property>
+                <property name="title" translatable="yes">Server</property>
+                <child>
+                  <object class="GtkEntry" id="host">
+                    <property name="visible">True</property>
+                    <property name="valign">center</property>
+                    <signal name="changed" handler="on_text_changed" swapped="yes"/>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="HdyPreferencesGroup">
+            <property name="visible">True</property>
+            <child>
+              <object class="HdyActionRow">
+                <property name="visible">True</property>
+                <property name="title" translatable="yes">Display Name</property>
+                <property name="subtitle" translatable="yes">Optional</property>
+                <child>
+                  <object class="GtkEntry" id="display_name">
+                    <property name="visible">True</property>
+                    <property name="valign">center</property>
+                    <signal name="changed" handler="on_text_changed" swapped="yes"/>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="HdyPreferencesGroup">
+            <property name="visible">True</property>
+            <child>
+              <object class="HdyActionRow">
+                <property name="visible">True</property>
+                <property name="title" translatable="yes">User ID</property>
+                <child>
+                  <object class="GtkEntry" id="user">
+                    <property name="visible">True</property>
+                    <property name="valign">center</property>
+                    <signal name="changed" handler="on_text_changed" swapped="yes"/>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="HdyActionRow">
+                <property name="visible">True</property>
+                <property name="title" translatable="yes">Password</property>
+                <child>
+                  <object class="GtkEntry" id="password">
+                    <property name="visible">True</property>
+                    <property name="valign">center</property>
+                    <property name="input-purpose">password</property>
+                    <property name="visibility">False</property>
+                    <signal name="changed" handler="on_text_changed" swapped="yes"/>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="HdyPreferencesGroup">
+            <property name="visible">True</property>
+            <child>
+              <object class="HdyActionRow">
+                <property name="visible">True</property>
+                <property name="title" translatable="yes">Port</property>
+                <child>
+                  <object class="GtkEntry" id="port">
+                    <property name="visible">True</property>
+                    <property name="valign">center</property>
+                    <property name="input-purpose">digits</property>
+                    <signal name="changed" handler="on_text_changed" swapped="yes"/>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="HdyComboRow" id="protocol">
+                <property name="visible">True</property>
+                <property name="title" translatable="yes">Transport</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+  <object class="GtkSizeGroup">
+    <widgets>
+      <widget name="host"/>
+      <widget name="display_name"/>
+      <widget name="user"/>
+      <widget name="password"/>
+      <widget name="port"/>
+    </widgets>
+  </object>
+</interface>
+
diff --git a/plugins/sip/sip.gresources.xml b/plugins/sip/sip.gresources.xml
new file mode 100644
index 00000000..a6400158
--- /dev/null
+++ b/plugins/sip/sip.gresources.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/Calls/ui">
+    <file preprocess="xml-stripblanks">sip-account-widget.ui</file>
+  </gresource>
+</gresources>


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