1
0
Fork 0
mirror of https://gitlab.gnome.org/GNOME/calls.git synced 2024-06-24 12:49:30 +00:00
Purism-Calls/src/calls-new-call-box.c

431 lines
12 KiB
C
Raw Normal View History

2018-08-01 09:41:11 +00:00
/*
* Copyright (C) 2018 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: Adrien Plazas <adrien.plazas@puri.sm>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#define G_LOG_DOMAIN "CallsNewCallBox"
#include "calls-application.h"
2018-08-01 09:41:11 +00:00
#include "calls-new-call-box.h"
2020-07-07 12:42:23 +00:00
#include "calls-ussd.h"
#include "calls-main-window.h"
#include "calls-manager.h"
2018-08-01 09:41:11 +00:00
#include <glib/gi18n.h>
#include <handy.h>
enum {
PROP_0,
PROP_NUMERIC_INPUT_ONLY,
PROP_LAST_PROP
};
static GParamSpec *props[PROP_LAST_PROP];
2018-08-01 09:41:11 +00:00
struct _CallsNewCallBox
{
GtkBox parent_instance;
GtkListBox *origin_list_box;
HdyComboRow *origin_list;
GtkButton *backspace;
Replace HdyDialer with HdyKeypad and bump libhandy to 0.0.12 HdyDialer was deprecated and therefore is replaced with HdyKeypad. HdyDialer used a `gchr *` to store the entered phone number. On every button press the entire text of the display entry was replaced with the new string, which messed up selection and cursor position. HdyKeypad connects directly to a GtkEntry and inserts each button press the same way as a keyboard stroke would do. In the case of the `call display` entry every new digit is appended to the end of the input and therefore it also moves the cursor to the end of the entry. Instead of making the Entry not editable, only the events which would remove text form the entry are blocked, via the `delete-text` signal. And the signal `insert-text` is used to block unwanted chars from beeing inserted. Same as for the `call display` entry also the `new call box` entry is made editable and the signal `insert-text` is used to block unwanted chars. All other user action possible on a entry arn't blocked e.g. repositioning the cursor. The advantage of making the Entry editable is that we can show the cursor position. It also allows the user to select the position where new digits are inserted in the `new call box`. On a button press the focus is set to the Entry to give the correct feedback to the user. This centers the text on the entry, as required by the design. This also makes the delete button remove only one char at the time, to move closer to the desired UX. Related: https://source.puri.sm/Librem5/calls/issues/58 Fixes: https://source.puri.sm/Librem5/calls/issues/82
2019-09-17 12:09:33 +00:00
HdyKeypad *keypad;
GtkButton *dial;
GtkEntry *address_entry;
GtkButton *dial_result;
2020-12-27 11:01:35 +00:00
GtkGestureLongPress *long_press_back_gesture;
GList *dial_queue;
gboolean numeric_input_only;
2018-08-01 09:41:11 +00:00
};
G_DEFINE_TYPE (CallsNewCallBox, calls_new_call_box, GTK_TYPE_BOX);
static CallsOrigin *
get_selected_origin (CallsNewCallBox *self)
{
g_autoptr (CallsOrigin) origin = NULL;
GListModel *model = hdy_combo_row_get_model (self->origin_list);
gint index = -1;
if (model)
index = hdy_combo_row_get_selected_index (self->origin_list);
if (model && index >= 0)
origin = g_list_model_get_item (model, index);
return origin;
}
static CallsOrigin *
get_origin (CallsNewCallBox *self,
const char *target)
{
CallsApplication *app = CALLS_APPLICATION (g_application_get_default ());
2021-04-16 02:18:15 +00:00
g_autoptr (CallsOrigin) origin = NULL;
GListModel *model;
gboolean auto_use_def_origin =
calls_application_get_use_default_origins_setting (app);
if (auto_use_def_origin) {
model = calls_manager_get_suitable_origins (calls_manager_get_default (),
target);
if (g_list_model_get_n_items (model) == 0)
return NULL;
origin = g_list_model_get_item (model, 0);
return origin;
} else {
return get_selected_origin (self);
}
}
static void
set_numeric (CallsNewCallBox *self,
gboolean enable)
{
if (enable == self->numeric_input_only)
return;
g_debug ("Numeric input %sabled", enable ? "en" : "dis");
self->numeric_input_only = enable;
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_NUMERIC_INPUT_ONLY]);
}
static void
notify_selected_index_cb (CallsNewCallBox *self)
{
CallsOrigin *origin = get_selected_origin (self);
gboolean numeric_input = TRUE;
if (origin)
g_object_get (origin, "numeric-addresses", &numeric_input, NULL);
set_numeric (self, numeric_input);
}
2020-12-27 11:01:35 +00:00
static void
long_press_back_cb (CallsNewCallBox *self)
{
GtkEntry *entry = hdy_keypad_get_entry (self->keypad);
gtk_editable_delete_text (GTK_EDITABLE (entry), 0, -1);
}
2018-08-01 09:41:11 +00:00
static void
backspace_clicked_cb (CallsNewCallBox *self)
2018-08-01 09:41:11 +00:00
{
GtkEntry *entry = hdy_keypad_get_entry (self->keypad);
Replace HdyDialer with HdyKeypad and bump libhandy to 0.0.12 HdyDialer was deprecated and therefore is replaced with HdyKeypad. HdyDialer used a `gchr *` to store the entered phone number. On every button press the entire text of the display entry was replaced with the new string, which messed up selection and cursor position. HdyKeypad connects directly to a GtkEntry and inserts each button press the same way as a keyboard stroke would do. In the case of the `call display` entry every new digit is appended to the end of the input and therefore it also moves the cursor to the end of the entry. Instead of making the Entry not editable, only the events which would remove text form the entry are blocked, via the `delete-text` signal. And the signal `insert-text` is used to block unwanted chars from beeing inserted. Same as for the `call display` entry also the `new call box` entry is made editable and the signal `insert-text` is used to block unwanted chars. All other user action possible on a entry arn't blocked e.g. repositioning the cursor. The advantage of making the Entry editable is that we can show the cursor position. It also allows the user to select the position where new digits are inserted in the `new call box`. On a button press the focus is set to the Entry to give the correct feedback to the user. This centers the text on the entry, as required by the design. This also makes the delete button remove only one char at the time, to move closer to the desired UX. Related: https://source.puri.sm/Librem5/calls/issues/58 Fixes: https://source.puri.sm/Librem5/calls/issues/82
2019-09-17 12:09:33 +00:00
g_signal_emit_by_name (entry, "backspace", NULL);
2018-08-01 09:41:11 +00:00
}
2020-07-07 12:42:23 +00:00
static void
ussd_send_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
CallsNewCallBox *self;
CallsUssd *ussd = (CallsUssd *)object;
g_autoptr (GTask) task = user_data;
2020-07-07 12:42:23 +00:00
GError *error = NULL;
char *response;
g_assert (G_IS_TASK (task));
self = g_task_get_source_object (task);
g_assert (CALLS_IS_NEW_CALL_BOX (self));
g_assert (CALLS_IS_USSD (ussd));
response = calls_ussd_initiate_finish (ussd, result, &error);
g_task_set_task_data (task, g_object_ref (ussd), g_object_unref);
if (error)
g_task_return_error (task, error);
else
g_task_return_pointer (task, response, g_free);
}
2018-08-01 09:41:11 +00:00
static void
dial_clicked_cb (CallsNewCallBox *self)
2018-08-01 09:41:11 +00:00
{
GtkEntry *entry = hdy_keypad_get_entry (self->keypad);
2020-07-07 12:42:23 +00:00
GtkWidget *window;
const char *text;
window = gtk_widget_get_toplevel (GTK_WIDGET (self));
text = gtk_entry_get_text (entry);
if (CALLS_IS_MAIN_WINDOW (window))
calls_main_window_dial (CALLS_MAIN_WINDOW (window), text);
else
calls_new_call_box_dial (self, text);
2018-08-01 09:41:11 +00:00
}
static void
dial_result_clicked_cb (CallsNewCallBox *self)
{
CallsOrigin *origin = get_selected_origin (self);
const char *address = gtk_entry_get_text (self->address_entry);
if (origin)
calls_origin_dial (origin, address);
else
g_warning ("No suitable origin found. How was this even clicked?");
}
2018-08-01 09:41:11 +00:00
static void
dial_queued_cb (gchar *target,
CallsNewCallBox *self)
{
CallsOrigin *origin = NULL;
g_debug ("Try dialing queued target `%s'", target);
origin = get_origin (self,
target);
if (origin) {
calls_origin_dial (origin, target);
self->dial_queue = g_list_remove (self->dial_queue, target);
}
else
g_debug ("No suitable origin found");
}
static void
clear_dial_queue (CallsNewCallBox *self)
{
g_list_free_full (self->dial_queue, g_free);
self->dial_queue = NULL;
}
static void
dial_queued (CallsNewCallBox *self)
{
if (!self->dial_queue)
2021-04-16 02:18:15 +00:00
return;
g_debug ("Try dialing %u queued targets",
g_list_length (self->dial_queue));
g_list_foreach (self->dial_queue,
2021-04-16 02:18:15 +00:00
(GFunc) dial_queued_cb,
self);
}
static char *
get_origin_name (gpointer item,
gpointer user_data)
2018-08-01 09:41:11 +00:00
{
g_assert (CALLS_IS_ORIGIN (item));
2018-08-01 09:41:11 +00:00
return calls_origin_get_name (item);
2018-08-01 09:41:11 +00:00
}
static void
origin_count_changed_cb (CallsNewCallBox *self)
2018-08-01 09:41:11 +00:00
{
GListModel *origins;
guint n_items = 0;
2018-08-01 09:41:11 +00:00
g_assert (CALLS_IS_NEW_CALL_BOX (self));
2018-08-01 09:41:11 +00:00
origins = calls_manager_get_origins (calls_manager_get_default ());
n_items = g_list_model_get_n_items (origins);
2018-08-01 09:41:11 +00:00
gtk_widget_set_visible (GTK_WIDGET (self->origin_list_box), n_items > 1);
gtk_widget_set_sensitive (GTK_WIDGET (self->dial), n_items > 0);
if (n_items)
dial_queued (self);
}
static void
calls_new_call_box_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
CallsNewCallBox *self = CALLS_NEW_CALL_BOX (object);
switch (property_id) {
case PROP_NUMERIC_INPUT_ONLY:
g_value_set_boolean (value, self->numeric_input_only);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
calls_new_call_box_init (CallsNewCallBox *self)
{
GListModel *origins;
gtk_widget_init_template (GTK_WIDGET (self));
origins = calls_manager_get_origins (calls_manager_get_default ());
hdy_combo_row_bind_name_model (self->origin_list, origins,
get_origin_name, self, NULL);
2018-08-01 09:41:11 +00:00
g_signal_connect_swapped (origins, "items-changed",
G_CALLBACK (origin_count_changed_cb), self);
origin_count_changed_cb (self);
}
static void
calls_new_call_box_dispose (GObject *object)
{
2018-08-01 09:41:11 +00:00
CallsNewCallBox *self = CALLS_NEW_CALL_BOX (object);
clear_dial_queue (self);
2020-12-27 11:01:35 +00:00
if (self->long_press_back_gesture != NULL)
g_object_unref (self->long_press_back_gesture);
G_OBJECT_CLASS (calls_new_call_box_parent_class)->dispose (object);
2018-08-01 09:41:11 +00:00
}
static void
calls_new_call_box_class_init (CallsNewCallBoxClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->get_property = calls_new_call_box_get_property;
object_class->dispose = calls_new_call_box_dispose;
2018-08-01 09:41:11 +00:00
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Calls/ui/new-call-box.ui");
gtk_widget_class_bind_template_child (widget_class, CallsNewCallBox, origin_list_box);
gtk_widget_class_bind_template_child (widget_class, CallsNewCallBox, origin_list);
gtk_widget_class_bind_template_child (widget_class, CallsNewCallBox, backspace);
2020-12-27 11:01:35 +00:00
gtk_widget_class_bind_template_child (widget_class, CallsNewCallBox, long_press_back_gesture);
Replace HdyDialer with HdyKeypad and bump libhandy to 0.0.12 HdyDialer was deprecated and therefore is replaced with HdyKeypad. HdyDialer used a `gchr *` to store the entered phone number. On every button press the entire text of the display entry was replaced with the new string, which messed up selection and cursor position. HdyKeypad connects directly to a GtkEntry and inserts each button press the same way as a keyboard stroke would do. In the case of the `call display` entry every new digit is appended to the end of the input and therefore it also moves the cursor to the end of the entry. Instead of making the Entry not editable, only the events which would remove text form the entry are blocked, via the `delete-text` signal. And the signal `insert-text` is used to block unwanted chars from beeing inserted. Same as for the `call display` entry also the `new call box` entry is made editable and the signal `insert-text` is used to block unwanted chars. All other user action possible on a entry arn't blocked e.g. repositioning the cursor. The advantage of making the Entry editable is that we can show the cursor position. It also allows the user to select the position where new digits are inserted in the `new call box`. On a button press the focus is set to the Entry to give the correct feedback to the user. This centers the text on the entry, as required by the design. This also makes the delete button remove only one char at the time, to move closer to the desired UX. Related: https://source.puri.sm/Librem5/calls/issues/58 Fixes: https://source.puri.sm/Librem5/calls/issues/82
2019-09-17 12:09:33 +00:00
gtk_widget_class_bind_template_child (widget_class, CallsNewCallBox, keypad);
gtk_widget_class_bind_template_child (widget_class, CallsNewCallBox, dial);
gtk_widget_class_bind_template_child (widget_class, CallsNewCallBox, address_entry);
2018-08-01 09:41:11 +00:00
gtk_widget_class_bind_template_callback (widget_class, dial_clicked_cb);
gtk_widget_class_bind_template_callback (widget_class, dial_result_clicked_cb);
gtk_widget_class_bind_template_callback (widget_class, backspace_clicked_cb);
2020-12-27 11:01:35 +00:00
gtk_widget_class_bind_template_callback (widget_class, long_press_back_cb);
gtk_widget_class_bind_template_callback (widget_class, notify_selected_index_cb);
props[PROP_NUMERIC_INPUT_ONLY] =
g_param_spec_boolean ("numeric-input-only",
"Numeric input only",
"Whether only numeric input is allowed (for the selected origin)",
TRUE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
2018-08-01 09:41:11 +00:00
}
CallsNewCallBox *
calls_new_call_box_new (void)
{
return g_object_new (CALLS_TYPE_NEW_CALL_BOX, NULL);
}
void
calls_new_call_box_dial (CallsNewCallBox *self,
const gchar *target)
{
CallsOrigin *origin;
g_return_if_fail (CALLS_IS_NEW_CALL_BOX (self));
g_return_if_fail (target != NULL);
origin = get_origin (self, target);
2021-04-16 02:18:15 +00:00
if (!origin) {
// Queue for dialing when an origin appears
g_debug ("Can't submit call with no origin, queuing for later");
self->dial_queue = g_list_append (self->dial_queue,
g_strdup (target));
return;
}
calls_origin_dial (origin, target);
}
2020-07-07 12:42:23 +00:00
void
calls_new_call_box_send_ussd_async (CallsNewCallBox *self,
const char *target,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr (CallsOrigin) origin = NULL;
g_autoptr (GTask) task = NULL;
2020-07-07 12:42:23 +00:00
GtkEntry *entry;
g_return_if_fail (CALLS_IS_NEW_CALL_BOX (self));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (target && *target);
origin = get_origin (self, target);
2020-07-07 12:42:23 +00:00
task = g_task_new (self, cancellable, callback, user_data);
2021-04-16 02:18:15 +00:00
if (!CALLS_IS_USSD (origin)) {
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
"No origin with USSD available");
return;
}
if (!calls_number_is_ussd (target)) {
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
"%s is not a valid USSD code", target);
return;
}
2020-07-07 12:42:23 +00:00
calls_ussd_initiate_async (CALLS_USSD (origin), target, cancellable,
ussd_send_cb, g_steal_pointer (&task));
entry = hdy_keypad_get_entry (self->keypad);
gtk_editable_delete_text (GTK_EDITABLE (entry), 0, -1);
}
char *
calls_new_call_box_send_ussd_finish (CallsNewCallBox *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (CALLS_IS_NEW_CALL_BOX (self), NULL);
g_return_val_if_fail (G_IS_TASK (result), NULL);
return g_task_propagate_pointer (G_TASK (result), error);
}