mirror of
https://gitlab.gnome.org/GNOME/calls.git
synced 2024-12-04 18:57:37 +00:00
Hook up Recent Calls list to database
Closes use-cases#113 Closes use-cases#115
This commit is contained in:
parent
8862b03a13
commit
3c22bc9154
22 changed files with 1312 additions and 82 deletions
43
data/call-arrow-incoming-missed-symbolic.svg
Normal file
43
data/call-arrow-incoming-missed-symbolic.svg
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="16"
|
||||
id="svg7384"
|
||||
version="1.1"
|
||||
width="16">
|
||||
<metadata
|
||||
id="metadata90">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>Gnome Symbolic Icon Theme</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<title
|
||||
id="title9167">Gnome Symbolic Icon Theme</title>
|
||||
<defs
|
||||
id="defs7386">
|
||||
<linearGradient
|
||||
id="linearGradient7212"
|
||||
osb:paint="solid">
|
||||
<stop
|
||||
id="stop7214"
|
||||
offset="0"
|
||||
style="stop-color:#000000;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path
|
||||
class="error"
|
||||
id="path14066"
|
||||
d="M 2 4 L 2 10 L 3 10 C 3.5522848 10 4 9.5522848 4 9 L 4 7.4140625 L 9 12.414062 L 13.697266 7.7167969 A 1 1 0 0 0 14 7 L 14 6 L 13 6 A 1 1 0 0 0 12.292969 6.2929688 L 9 9.5859375 L 5.4160156 6 L 7 6 C 7.5522848 6 8 5.5522848 8 5 L 8 4 L 2 4 z "
|
||||
style="opacity:1;vector-effect:none;fill:#ed333b;fill-opacity:1;stroke:none;stroke-width:1.99999988;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
42
data/call-arrow-incoming-symbolic.svg
Normal file
42
data/call-arrow-incoming-symbolic.svg
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="16"
|
||||
id="svg7384"
|
||||
version="1.1"
|
||||
width="16">
|
||||
<metadata
|
||||
id="metadata90">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>Gnome Symbolic Icon Theme</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<title
|
||||
id="title9167">Gnome Symbolic Icon Theme</title>
|
||||
<defs
|
||||
id="defs7386">
|
||||
<linearGradient
|
||||
id="linearGradient7212"
|
||||
osb:paint="solid">
|
||||
<stop
|
||||
id="stop7214"
|
||||
offset="0"
|
||||
style="stop-color:#000000;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path
|
||||
id="path819"
|
||||
d="M 9,13 V 12 C 9,11.44772 8.552285,11 8,11 H 6.414062 l 6.289063,-6.2910203 c 1.027617,-0.93764 -0.463493,-2.43908 -1.408203,-1.41796 L 5,9.5937497 v -1.59375 c -0.0011,-0.55152 -0.448476,-0.99805 -1,-0.99805 v -0.00195 H 3 V 13 Z"
|
||||
style="opacity:1;vector-effect:none;fill:#3584e4;fill-opacity:1;stroke:none;stroke-width:1.99999988;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
43
data/call-arrow-outgoing-missed-symbolic.svg
Normal file
43
data/call-arrow-outgoing-missed-symbolic.svg
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="16"
|
||||
id="svg7384"
|
||||
version="1.1"
|
||||
width="16">
|
||||
<metadata
|
||||
id="metadata90">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>Gnome Symbolic Icon Theme</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<title
|
||||
id="title9167">Gnome Symbolic Icon Theme</title>
|
||||
<defs
|
||||
id="defs7386">
|
||||
<linearGradient
|
||||
id="linearGradient7212"
|
||||
osb:paint="solid">
|
||||
<stop
|
||||
id="stop7214"
|
||||
offset="0"
|
||||
style="stop-color:#000000;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path
|
||||
class="error"
|
||||
style="opacity:1;vector-effect:none;fill:#ed333b;fill-opacity:1;stroke:none;stroke-width:1.99999988;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="M 14,12 V 6 H 13 C 12.447715,6 12,6.4477152 12,7 V 8.5859375 L 7,3.585938 2.302734,8.2832031 A 1,1 0 0 0 2,9 v 1 H 3 A 1,1 0 0 0 3.707031,9.707031 L 7,6.4140625 10.583984,10 H 9 c -0.5522848,0 -1,0.447715 -1,1 v 1 z"
|
||||
id="path949" />
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
42
data/call-arrow-outgoing-symbolic.svg
Normal file
42
data/call-arrow-outgoing-symbolic.svg
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="16"
|
||||
id="svg7384"
|
||||
version="1.1"
|
||||
width="16">
|
||||
<metadata
|
||||
id="metadata90">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>Gnome Symbolic Icon Theme</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<title
|
||||
id="title9167">Gnome Symbolic Icon Theme</title>
|
||||
<defs
|
||||
id="defs7386">
|
||||
<linearGradient
|
||||
id="linearGradient7212"
|
||||
osb:paint="solid">
|
||||
<stop
|
||||
id="stop7214"
|
||||
offset="0"
|
||||
style="stop-color:#000000;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path
|
||||
style="opacity:1;vector-effect:none;fill:#2ec27e;fill-opacity:1;stroke:none;stroke-width:1.99999988;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="m 7.000377,3.00001 v 1 c 0,0.55228 0.447715,1 1,1 h 1.585938 l -6.289063,6.29102 c -1.027617,0.93764 0.463493,2.43908 1.408203,1.41796 l 6.294922,-6.30273 v 1.59375 c 0.0011,0.55152 0.448476,0.99805 1,0.99805 v 0.00195 h 1 v -6 z"
|
||||
id="path14090" />
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -1,6 +1,6 @@
|
|||
/* calls-application.c
|
||||
*
|
||||
* Copyright (C) 2018 Purism SPC
|
||||
* Copyright (C) 2018, 2019 Purism SPC
|
||||
* Copyright (C) 2018 Mohammed Sadiq <sadiq@sadiqpk.org>
|
||||
*
|
||||
* This file is part of Calls.
|
||||
|
@ -126,11 +126,16 @@ static const GActionEntry actions[] =
|
|||
static void
|
||||
startup (GApplication *application)
|
||||
{
|
||||
GtkIconTheme *icon_theme;
|
||||
|
||||
G_APPLICATION_CLASS (calls_application_parent_class)->startup (application);
|
||||
|
||||
g_set_prgname (APP_ID);
|
||||
g_set_application_name (_("Calls"));
|
||||
|
||||
icon_theme = gtk_icon_theme_get_default ();
|
||||
gtk_icon_theme_add_resource_path (icon_theme, "/sm/puri/calls/");
|
||||
|
||||
g_action_map_add_action_entries (G_ACTION_MAP (application),
|
||||
actions,
|
||||
G_N_ELEMENTS (actions),
|
||||
|
@ -253,7 +258,9 @@ activate (GApplication *application)
|
|||
* But we assume that the application is closed by closing the
|
||||
* window. In that case, GTK+ frees the resources right.
|
||||
*/
|
||||
window = GTK_WINDOW (calls_main_window_new (gtk_app, self->provider));
|
||||
window = GTK_WINDOW
|
||||
(calls_main_window_new (gtk_app, self->provider,
|
||||
G_LIST_MODEL (self->record_store)));
|
||||
calls_call_window_new (gtk_app, self->provider);
|
||||
}
|
||||
|
||||
|
@ -282,6 +289,7 @@ dispose (GObject *object)
|
|||
{
|
||||
CallsApplication *self = (CallsApplication *)object;
|
||||
|
||||
g_clear_object (&self->record_store);
|
||||
g_clear_object (&self->ringer);
|
||||
g_clear_object (&self->provider);
|
||||
|
||||
|
|
458
src/calls-call-record-row.c
Normal file
458
src/calls-call-record-row.c
Normal file
|
@ -0,0 +1,458 @@
|
|||
/*
|
||||
* Copyright (C) 2018, 2019 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: Bob Ham <bob.ham@puri.sm>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
*/
|
||||
|
||||
#include "calls-call-record-row.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <glib-object.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
struct _CallsCallRecordRow
|
||||
{
|
||||
GtkOverlay parent_instance;
|
||||
|
||||
GtkImage *avatar;
|
||||
GtkImage *type;
|
||||
GtkLabel *target;
|
||||
GtkLabel *time;
|
||||
|
||||
CallsCallRecord *record;
|
||||
gulong answered_notify_handler_id;
|
||||
gulong end_notify_handler_id;
|
||||
guint date_change_timeout;
|
||||
|
||||
CallsNewCallBox *new_call;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (CallsCallRecordRow, calls_call_record_row, GTK_TYPE_BOX);
|
||||
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_RECORD,
|
||||
PROP_NEW_CALL,
|
||||
PROP_LAST_PROP,
|
||||
};
|
||||
static GParamSpec *props[PROP_LAST_PROP];
|
||||
|
||||
|
||||
static void
|
||||
redial_clicked_cb (CallsCallRecordRow *self)
|
||||
{
|
||||
gchar *target;
|
||||
|
||||
g_object_get (self->record,
|
||||
"target", &target,
|
||||
NULL);
|
||||
g_assert (target != NULL);
|
||||
|
||||
calls_new_call_box_dial (self->new_call, target);
|
||||
g_free (target);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
nice_time (GDateTime *t,
|
||||
gchar **nice,
|
||||
gboolean *final)
|
||||
{
|
||||
GDateTime *now = g_date_time_new_now_local ();
|
||||
const gboolean today =
|
||||
calls_date_time_is_same_day (now, t);
|
||||
const gboolean yesterday =
|
||||
(!today && calls_date_time_is_yesterday (now, t));
|
||||
|
||||
g_assert (nice != NULL);
|
||||
g_assert (final != NULL);
|
||||
|
||||
if (today || yesterday)
|
||||
{
|
||||
gchar *n = g_date_time_format (t, "%R");
|
||||
|
||||
if (yesterday)
|
||||
{
|
||||
gchar *s;
|
||||
s = g_strdup_printf (_("%s\nyesterday"), n);
|
||||
g_free (n);
|
||||
n = s;
|
||||
}
|
||||
|
||||
*nice = n;
|
||||
*final = FALSE;
|
||||
}
|
||||
else if (calls_date_time_is_same_year (now, t))
|
||||
{
|
||||
*nice = g_date_time_format (t, "%b %-d");
|
||||
*final = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
*nice = g_date_time_format (t, "%Y");
|
||||
*final = TRUE;
|
||||
}
|
||||
|
||||
g_date_time_unref (now);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
update_time (CallsCallRecordRow *self,
|
||||
GDateTime *end,
|
||||
gboolean *final)
|
||||
{
|
||||
gchar *nice;
|
||||
nice_time (end, &nice, final);
|
||||
gtk_label_set_text (self->time, nice);
|
||||
g_free (nice);
|
||||
}
|
||||
|
||||
|
||||
static gboolean date_change_cb (CallsCallRecordRow *self);
|
||||
|
||||
|
||||
static void
|
||||
setup_date_change_timeout (CallsCallRecordRow *self)
|
||||
{
|
||||
GDateTime *gnow, *gnextday, *gtomorrow;
|
||||
struct timeval now, tomorrow, delta;
|
||||
int err;
|
||||
guint interval;
|
||||
|
||||
// Get the time now
|
||||
gnow = g_date_time_new_now_local ();
|
||||
|
||||
// Get the next day
|
||||
gnextday = g_date_time_add_days (gnow, 1);
|
||||
g_date_time_unref (gnow);
|
||||
|
||||
// Get the start of the next day
|
||||
gtomorrow =
|
||||
g_date_time_new (g_date_time_get_timezone (gnextday),
|
||||
g_date_time_get_year (gnextday),
|
||||
g_date_time_get_month (gnextday),
|
||||
g_date_time_get_day_of_month (gnextday),
|
||||
0,
|
||||
0,
|
||||
0.0);
|
||||
g_date_time_unref (gnextday);
|
||||
|
||||
// Convert to a timeval
|
||||
tomorrow.tv_sec = g_date_time_to_unix (gtomorrow);
|
||||
tomorrow.tv_usec = 0;
|
||||
g_date_time_unref (gtomorrow);
|
||||
|
||||
// Get the precise time now
|
||||
err = gettimeofday (&now, NULL);
|
||||
if (err == -1)
|
||||
{
|
||||
g_warning ("Error getting time to set date change timeout: %s",
|
||||
g_strerror (errno));
|
||||
return;
|
||||
}
|
||||
|
||||
// Find how long from now until the start of the next day
|
||||
timersub (&tomorrow, &now, &delta);
|
||||
|
||||
// Convert to milliseconds
|
||||
interval =
|
||||
(delta.tv_sec * 1000)
|
||||
+
|
||||
(delta.tv_usec / 1000);
|
||||
|
||||
// Add the timeout
|
||||
self->date_change_timeout =
|
||||
g_timeout_add (interval,
|
||||
(GSourceFunc)date_change_cb,
|
||||
self);
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
date_change_cb (CallsCallRecordRow *self)
|
||||
{
|
||||
GDateTime *end;
|
||||
gboolean final;
|
||||
|
||||
g_object_get (G_OBJECT (self->record),
|
||||
"end", &end,
|
||||
NULL);
|
||||
g_assert (end != NULL);
|
||||
|
||||
update_time (self, end, &final);
|
||||
g_date_time_unref (end);
|
||||
|
||||
if (final)
|
||||
{
|
||||
self->date_change_timeout = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
setup_date_change_timeout (self);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
update (CallsCallRecordRow *self,
|
||||
gboolean inbound,
|
||||
GDateTime *answered,
|
||||
GDateTime *end)
|
||||
{
|
||||
gboolean missed = FALSE;
|
||||
gchar *type_icon_name;
|
||||
|
||||
if (end)
|
||||
{
|
||||
gboolean time_final;
|
||||
|
||||
update_time (self, end, &time_final);
|
||||
|
||||
if (!time_final && !self->date_change_timeout)
|
||||
{
|
||||
setup_date_change_timeout (self);
|
||||
}
|
||||
|
||||
if (!answered)
|
||||
{
|
||||
missed = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
type_icon_name = g_strdup_printf
|
||||
("call-arrow-%s%s-symbolic",
|
||||
inbound ? "incoming" : "outgoing",
|
||||
missed ? "-missed" : "");
|
||||
gtk_image_set_from_icon_name (self->type, type_icon_name,
|
||||
GTK_ICON_SIZE_MENU);
|
||||
|
||||
g_free (type_icon_name);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
notify_cb (CallsCallRecordRow *self,
|
||||
GParamSpec *pspec,
|
||||
CallsCallRecord *record)
|
||||
{
|
||||
gboolean inbound;
|
||||
GDateTime *answered;
|
||||
GDateTime *end;
|
||||
|
||||
g_object_get (G_OBJECT (self->record),
|
||||
"inbound", &inbound,
|
||||
"answered", &answered,
|
||||
"end", &end,
|
||||
NULL);
|
||||
|
||||
update (self, inbound, answered, end);
|
||||
|
||||
if (answered)
|
||||
{
|
||||
g_date_time_unref (answered);
|
||||
calls_clear_signal (record, &self->answered_notify_handler_id);
|
||||
}
|
||||
if (end)
|
||||
{
|
||||
g_date_time_unref (end);
|
||||
calls_clear_signal (record, &self->end_notify_handler_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
CallsCallRecordRow *self = CALLS_CALL_RECORD_ROW (object);
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_RECORD:
|
||||
g_set_object (&self->record,
|
||||
CALLS_CALL_RECORD (g_value_get_object (value)));
|
||||
break;
|
||||
|
||||
case PROP_NEW_CALL:
|
||||
g_set_object (&self->new_call,
|
||||
CALLS_NEW_CALL_BOX (g_value_get_object (value)));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
constructed (GObject *object)
|
||||
{
|
||||
GObjectClass *obj_class = g_type_class_peek (G_TYPE_OBJECT);
|
||||
CallsCallRecordRow *self = CALLS_CALL_RECORD_ROW (object);
|
||||
gchar *target;
|
||||
gboolean inbound;
|
||||
GDateTime *answered;
|
||||
GDateTime *end;
|
||||
|
||||
g_object_get (G_OBJECT (self->record),
|
||||
"target", &target,
|
||||
"inbound", &inbound,
|
||||
"answered", &answered,
|
||||
"end", &end,
|
||||
NULL);
|
||||
|
||||
gtk_label_set_text (self->target, target);
|
||||
g_free (target);
|
||||
|
||||
if (!end)
|
||||
{
|
||||
self->end_notify_handler_id =
|
||||
g_signal_connect_swapped (self->record,
|
||||
"notify::end",
|
||||
G_CALLBACK (notify_cb),
|
||||
self);
|
||||
|
||||
if (!answered)
|
||||
{
|
||||
self->answered_notify_handler_id =
|
||||
g_signal_connect_swapped (self->record,
|
||||
"notify::answered",
|
||||
G_CALLBACK (notify_cb),
|
||||
self);
|
||||
}
|
||||
}
|
||||
|
||||
update (self, inbound, answered, end);
|
||||
calls_date_time_unref (answered);
|
||||
calls_date_time_unref (end);
|
||||
|
||||
obj_class->constructed (object);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
CallsCallRecordRow *self = CALLS_CALL_RECORD_ROW (object);
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_RECORD:
|
||||
g_value_set_object (value, self->record);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
dispose (GObject *object)
|
||||
{
|
||||
GObjectClass *obj_class = g_type_class_peek (G_TYPE_OBJECT);
|
||||
CallsCallRecordRow *self = CALLS_CALL_RECORD_ROW (object);
|
||||
|
||||
g_clear_object (&self->new_call);
|
||||
|
||||
calls_clear_source (&self->date_change_timeout);
|
||||
calls_clear_signal (self->record, &self->answered_notify_handler_id);
|
||||
calls_clear_signal (self->record, &self->end_notify_handler_id);
|
||||
g_clear_object (&self->record);
|
||||
|
||||
obj_class->dispose (object);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
calls_call_record_row_class_init (CallsCallRecordRowClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
|
||||
object_class->set_property = set_property;
|
||||
object_class->constructed = constructed;
|
||||
object_class->get_property = get_property;
|
||||
object_class->dispose = dispose;
|
||||
|
||||
props[PROP_RECORD] =
|
||||
g_param_spec_object ("record",
|
||||
_("Record"),
|
||||
_("The call record for this row"),
|
||||
CALLS_TYPE_CALL_RECORD,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
||||
|
||||
props[PROP_NEW_CALL] =
|
||||
g_param_spec_object ("new-call",
|
||||
_("New call"),
|
||||
_("The UI box for making calls"),
|
||||
CALLS_TYPE_NEW_CALL_BOX,
|
||||
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
|
||||
|
||||
g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
|
||||
|
||||
|
||||
gtk_widget_class_set_template_from_resource (widget_class, "/sm/puri/calls/ui/call-record-row.ui");
|
||||
gtk_widget_class_bind_template_child (widget_class, CallsCallRecordRow, avatar);
|
||||
gtk_widget_class_bind_template_child (widget_class, CallsCallRecordRow, type);
|
||||
gtk_widget_class_bind_template_child (widget_class, CallsCallRecordRow, target);
|
||||
gtk_widget_class_bind_template_child (widget_class, CallsCallRecordRow, time);
|
||||
gtk_widget_class_bind_template_callback (widget_class, redial_clicked_cb);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
calls_call_record_row_init (CallsCallRecordRow *self)
|
||||
{
|
||||
gtk_widget_init_template (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
|
||||
CallsCallRecordRow *
|
||||
calls_call_record_row_new (CallsCallRecord *record,
|
||||
CallsNewCallBox *new_call)
|
||||
{
|
||||
return g_object_new (CALLS_TYPE_CALL_RECORD_ROW,
|
||||
"record", record,
|
||||
"new-call", new_call,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
CallsCallRecord *
|
||||
calls_call_record_row_get_record (CallsCallRecordRow *self)
|
||||
{
|
||||
return self->record;
|
||||
}
|
46
src/calls-call-record-row.h
Normal file
46
src/calls-call-record-row.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (C) 2018, 2019 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: Bob Ham <bob.ham@puri.sm>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CALLS_CALL_RECORD_ROW_H__
|
||||
#define CALLS_CALL_RECORD_ROW_H__
|
||||
|
||||
#include "calls-call-record.h"
|
||||
#include "calls-new-call-box.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define CALLS_TYPE_CALL_RECORD_ROW (calls_call_record_row_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (CallsCallRecordRow, calls_call_record_row,
|
||||
CALLS, CALL_RECORD_ROW, GtkBox);
|
||||
|
||||
CallsCallRecordRow *calls_call_record_row_new (CallsCallRecord *record,
|
||||
CallsNewCallBox *new_call);
|
||||
CallsCallRecord * calls_call_record_row_get_record (CallsCallRecordRow *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* CALLS_CALL_RECORD_ROW_H__ */
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2018 Purism SPC
|
||||
* Copyright (C) 2018, 2019 Purism SPC
|
||||
*
|
||||
* This file is part of Calls.
|
||||
*
|
||||
|
@ -23,9 +23,8 @@
|
|||
*/
|
||||
|
||||
#include "calls-history-box.h"
|
||||
#include "calls-origin.h"
|
||||
#include "calls-call-holder.h"
|
||||
#include "calls-call-selector-item.h"
|
||||
#include "calls-call-record.h"
|
||||
#include "calls-call-record-row.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
|
@ -39,10 +38,179 @@ struct _CallsHistoryBox
|
|||
{
|
||||
GtkStack parent_instance;
|
||||
|
||||
GtkListStore *history_store;
|
||||
GtkListBox *history;
|
||||
|
||||
GListModel *model;
|
||||
gulong model_changed_handler_id;
|
||||
|
||||
CallsNewCallBox *new_call;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (CallsHistoryBox, calls_history_box, GTK_TYPE_STACK)
|
||||
G_DEFINE_TYPE (CallsHistoryBox, calls_history_box, GTK_TYPE_STACK);
|
||||
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_MODEL,
|
||||
PROP_NEW_CALL,
|
||||
PROP_LAST_PROP,
|
||||
};
|
||||
static GParamSpec *props[PROP_LAST_PROP];
|
||||
|
||||
|
||||
static void
|
||||
update (CallsHistoryBox *self)
|
||||
{
|
||||
gchar *child_name;
|
||||
|
||||
if (g_list_model_get_n_items (self->model) == 0)
|
||||
{
|
||||
child_name = "empty";
|
||||
}
|
||||
else
|
||||
{
|
||||
child_name = "history";
|
||||
|
||||
/* Transition should only ever be from empty to non-empty */
|
||||
if (self->model_changed_handler_id != 0)
|
||||
{
|
||||
calls_clear_signal (self->model,
|
||||
&self->model_changed_handler_id);
|
||||
}
|
||||
}
|
||||
|
||||
gtk_stack_set_visible_child_name (GTK_STACK (self), child_name);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
header_cb (GtkListBoxRow *row,
|
||||
GtkListBoxRow *before,
|
||||
CallsHistoryBox *self)
|
||||
{
|
||||
if (!before)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gtk_list_box_row_get_header (row))
|
||||
{
|
||||
GtkWidget *header =
|
||||
gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
|
||||
|
||||
gtk_list_box_row_set_header (row, header);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static GtkWidget *
|
||||
create_row_cb (CallsCallRecord *record,
|
||||
CallsHistoryBox *self)
|
||||
{
|
||||
return GTK_WIDGET (calls_call_record_row_new (record, self->new_call));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
CallsHistoryBox *self = CALLS_HISTORY_BOX (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_MODEL:
|
||||
g_set_object (&self->model,
|
||||
G_LIST_MODEL (g_value_get_object (value)));
|
||||
break;
|
||||
|
||||
case PROP_NEW_CALL:
|
||||
g_set_object (&self->new_call,
|
||||
CALLS_NEW_CALL_BOX (g_value_get_object (value)));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
constructed (GObject *object)
|
||||
{
|
||||
GObjectClass *parent_class = g_type_class_peek (G_TYPE_OBJECT);
|
||||
CallsHistoryBox *self = CALLS_HISTORY_BOX (object);
|
||||
|
||||
g_assert (self->model != NULL);
|
||||
|
||||
self->model_changed_handler_id =
|
||||
g_signal_connect_swapped
|
||||
(self->model, "items-changed", G_CALLBACK (update), self);
|
||||
g_assert (self->model_changed_handler_id != 0);
|
||||
|
||||
gtk_list_box_set_header_func (self->history,
|
||||
(GtkListBoxUpdateHeaderFunc)header_cb,
|
||||
self,
|
||||
NULL);
|
||||
|
||||
gtk_list_box_bind_model (self->history,
|
||||
self->model,
|
||||
(GtkListBoxCreateWidgetFunc)create_row_cb,
|
||||
self,
|
||||
NULL);
|
||||
|
||||
update (self);
|
||||
|
||||
parent_class->constructed (object);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
dispose (GObject *object)
|
||||
{
|
||||
GObjectClass *parent_class = g_type_class_peek (G_TYPE_OBJECT);
|
||||
CallsHistoryBox *self = CALLS_HISTORY_BOX (object);
|
||||
|
||||
g_clear_object (&self->new_call);
|
||||
g_clear_object (&self->model);
|
||||
|
||||
parent_class->dispose (object);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
calls_history_box_class_init (CallsHistoryBoxClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
|
||||
object_class->set_property = set_property;
|
||||
object_class->constructed = constructed;
|
||||
object_class->dispose = dispose;
|
||||
|
||||
props[PROP_MODEL] =
|
||||
g_param_spec_object ("model",
|
||||
_("model"),
|
||||
_("The data store containing call records"),
|
||||
G_TYPE_LIST_MODEL,
|
||||
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
|
||||
|
||||
props[PROP_NEW_CALL] =
|
||||
g_param_spec_object ("new-call",
|
||||
_("New call"),
|
||||
_("The UI box for making calls"),
|
||||
CALLS_TYPE_NEW_CALL_BOX,
|
||||
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
|
||||
|
||||
g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
|
||||
|
||||
|
||||
gtk_widget_class_set_template_from_resource (widget_class, "/sm/puri/calls/ui/history-box.ui");
|
||||
gtk_widget_class_bind_template_child (widget_class, CallsHistoryBox, history);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
|
@ -52,12 +220,12 @@ calls_history_box_init (CallsHistoryBox *self)
|
|||
}
|
||||
|
||||
|
||||
static void
|
||||
calls_history_box_class_init (CallsHistoryBoxClass *klass)
|
||||
CallsHistoryBox *
|
||||
calls_history_box_new (GListModel *model,
|
||||
CallsNewCallBox *new_call)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
|
||||
|
||||
gtk_widget_class_set_template_from_resource (widget_class, "/sm/puri/calls/ui/history-box.ui");
|
||||
gtk_widget_class_bind_template_child (widget_class, CallsHistoryBox, history_store);
|
||||
return g_object_new (CALLS_TYPE_HISTORY_BOX,
|
||||
"model", model,
|
||||
"new-call", new_call,
|
||||
NULL);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#ifndef CALLS_HISTORY_BOX_H__
|
||||
#define CALLS_HISTORY_BOX_H__
|
||||
|
||||
#include "calls-new-call-box.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define HANDY_USE_UNSTABLE_API
|
||||
|
@ -34,7 +36,10 @@ G_BEGIN_DECLS
|
|||
|
||||
#define CALLS_TYPE_HISTORY_BOX (calls_history_box_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (CallsHistoryBox, calls_history_box, CALLS, HISTORY_BOX, GtkStack)
|
||||
G_DECLARE_FINAL_TYPE (CallsHistoryBox, calls_history_box, CALLS, HISTORY_BOX, GtkStack);
|
||||
|
||||
CallsHistoryBox * calls_history_box_new (GListModel *model,
|
||||
CallsNewCallBox *new_call);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "calls-call-holder.h"
|
||||
#include "calls-call-selector-item.h"
|
||||
#include "calls-new-call-box.h"
|
||||
#include "calls-history-box.h"
|
||||
#include "calls-enumerate.h"
|
||||
#include "config.h"
|
||||
#include "util.h"
|
||||
|
@ -43,6 +44,7 @@ struct _CallsMainWindow
|
|||
GtkApplicationWindow parent_instance;
|
||||
|
||||
CallsProvider *provider;
|
||||
GListModel *record_store;
|
||||
|
||||
GtkRevealer *info_revealer;
|
||||
guint info_timeout;
|
||||
|
@ -55,6 +57,8 @@ struct _CallsMainWindow
|
|||
HdyViewSwitcher *narrow_switcher;
|
||||
HdyViewSwitcherBar *switcher_bar;
|
||||
GtkStack *main_stack;
|
||||
|
||||
CallsNewCallBox *new_call;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (CallsMainWindow, calls_main_window, GTK_TYPE_APPLICATION_WINDOW);
|
||||
|
@ -62,6 +66,7 @@ G_DEFINE_TYPE (CallsMainWindow, calls_main_window, GTK_TYPE_APPLICATION_WINDOW);
|
|||
enum {
|
||||
PROP_0,
|
||||
PROP_PROVIDER,
|
||||
PROP_RECORD_STORE,
|
||||
PROP_LAST_PROP,
|
||||
};
|
||||
static GParamSpec *props[PROP_LAST_PROP];
|
||||
|
@ -194,7 +199,13 @@ set_property (GObject *object,
|
|||
|
||||
switch (property_id) {
|
||||
case PROP_PROVIDER:
|
||||
g_set_object (&self->provider, CALLS_PROVIDER (g_value_get_object (value)));
|
||||
g_set_object (&self->provider,
|
||||
CALLS_PROVIDER (g_value_get_object (value)));
|
||||
break;
|
||||
|
||||
case PROP_RECORD_STORE:
|
||||
g_set_object (&self->record_store,
|
||||
G_LIST_MODEL (g_value_get_object (value)));
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -236,24 +247,39 @@ set_up_provider (CallsMainWindow *self)
|
|||
static void
|
||||
constructed (GObject *object)
|
||||
{
|
||||
GObjectClass *parent_class = g_type_class_peek (GTK_TYPE_APPLICATION_WINDOW);
|
||||
GObjectClass *parent_class = g_type_class_peek (G_TYPE_OBJECT);
|
||||
CallsMainWindow *self = CALLS_MAIN_WINDOW (object);
|
||||
GSimpleActionGroup *simple_action_group;
|
||||
CallsNewCallBox *new_call_box;
|
||||
GtkContainer *main_stack = GTK_CONTAINER (self->main_stack);
|
||||
GtkWidget *widget;
|
||||
CallsHistoryBox *history;
|
||||
|
||||
set_up_provider (self);
|
||||
|
||||
/* Add new call box */
|
||||
new_call_box = calls_new_call_box_new (self->provider);
|
||||
gtk_stack_add_titled (self->main_stack, GTK_WIDGET (new_call_box),
|
||||
// Add new call box
|
||||
self->new_call = calls_new_call_box_new (self->provider);
|
||||
widget = GTK_WIDGET (self->new_call);
|
||||
gtk_stack_add_titled (self->main_stack, widget,
|
||||
"dial-pad", _("Dial Pad"));
|
||||
gtk_container_child_set (GTK_CONTAINER (self->main_stack),
|
||||
GTK_WIDGET (new_call_box),
|
||||
gtk_container_child_set (main_stack, widget,
|
||||
"icon-name", "input-dialpad-symbolic",
|
||||
NULL);
|
||||
gtk_stack_set_visible_child_name (self->main_stack, "dial-pad");
|
||||
|
||||
/* Add actions */
|
||||
// Add call records
|
||||
history = calls_history_box_new (self->record_store,
|
||||
self->new_call);
|
||||
widget = GTK_WIDGET (history);
|
||||
gtk_stack_add_titled (self->main_stack, widget,
|
||||
"recent", _("Recent"));
|
||||
gtk_container_child_set
|
||||
(main_stack, widget,
|
||||
"icon-name", "document-open-recent-symbolic",
|
||||
"position", 0,
|
||||
NULL);
|
||||
gtk_widget_set_visible (widget, TRUE);
|
||||
gtk_stack_set_visible_child_name (self->main_stack, "recent");
|
||||
|
||||
// Add actions
|
||||
simple_action_group = g_simple_action_group_new ();
|
||||
g_action_map_add_action_entries (G_ACTION_MAP (simple_action_group),
|
||||
window_entries,
|
||||
|
@ -279,13 +305,15 @@ constructed (GObject *object)
|
|||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
dispose (GObject *object)
|
||||
{
|
||||
GObjectClass *parent_class = g_type_class_peek (GTK_TYPE_APPLICATION_WINDOW);
|
||||
GObjectClass *parent_class = g_type_class_peek (G_TYPE_OBJECT);
|
||||
CallsMainWindow *self = CALLS_MAIN_WINDOW (object);
|
||||
|
||||
stop_info_timeout (self);
|
||||
g_clear_object (&self->record_store);
|
||||
g_clear_object (&self->provider);
|
||||
|
||||
parent_class->dispose (object);
|
||||
|
@ -327,6 +355,13 @@ calls_main_window_class_init (CallsMainWindowClass *klass)
|
|||
CALLS_TYPE_PROVIDER,
|
||||
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
|
||||
|
||||
props[PROP_RECORD_STORE] =
|
||||
g_param_spec_object ("record-store",
|
||||
_("Record store"),
|
||||
_("The store of call records"),
|
||||
G_TYPE_LIST_MODEL,
|
||||
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
|
||||
|
||||
g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
|
||||
|
||||
|
||||
|
@ -354,13 +389,17 @@ calls_main_window_init (CallsMainWindow *self)
|
|||
|
||||
|
||||
CallsMainWindow *
|
||||
calls_main_window_new (GtkApplication *application, CallsProvider *provider)
|
||||
calls_main_window_new (GtkApplication *application,
|
||||
CallsProvider *provider,
|
||||
GListModel *record_store)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_APPLICATION (application), NULL);
|
||||
g_return_val_if_fail (CALLS_IS_PROVIDER (provider), NULL);
|
||||
g_return_val_if_fail (G_IS_LIST_MODEL (record_store), NULL);
|
||||
|
||||
return g_object_new (CALLS_TYPE_MAIN_WINDOW,
|
||||
"application", application,
|
||||
"provider", provider,
|
||||
"record-store", record_store,
|
||||
NULL);
|
||||
}
|
||||
|
|
|
@ -35,8 +35,9 @@ G_BEGIN_DECLS
|
|||
|
||||
G_DECLARE_FINAL_TYPE (CallsMainWindow, calls_main_window, CALLS, MAIN_WINDOW, GtkApplicationWindow);
|
||||
|
||||
CallsMainWindow *calls_main_window_new (GtkApplication *application,
|
||||
CallsProvider *provider);
|
||||
CallsMainWindow *calls_main_window_new (GtkApplication *application,
|
||||
CallsProvider *provider,
|
||||
GListModel *record_store);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
|
|
@ -79,30 +79,11 @@ dial_pad_deleted_cb (CallsNewCallBox *self,
|
|||
|
||||
|
||||
static void
|
||||
dial_clicked_cb (CallsNewCallBox *self,
|
||||
const gchar *unused,
|
||||
GtkButton *button)
|
||||
dial_clicked_cb (CallsNewCallBox *self)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
gboolean ok;
|
||||
CallsOrigin *origin;
|
||||
const gchar *number;
|
||||
|
||||
ok = gtk_combo_box_get_active_iter (self->origin_box, &iter);
|
||||
if (!ok)
|
||||
{
|
||||
g_debug ("Can't submit call with no origin");
|
||||
return;
|
||||
}
|
||||
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (self->origin_store), &iter,
|
||||
ORIGIN_STORE_COLUMN_ORIGIN, &origin,
|
||||
-1);
|
||||
g_assert (CALLS_IS_ORIGIN (origin));
|
||||
|
||||
number = gtk_entry_get_text (GTK_ENTRY (self->number_entry));
|
||||
|
||||
calls_origin_dial (origin, number);
|
||||
calls_new_call_box_dial
|
||||
(self,
|
||||
gtk_entry_get_text (GTK_ENTRY (self->number_entry)));
|
||||
}
|
||||
|
||||
|
||||
|
@ -311,3 +292,29 @@ calls_new_call_box_new (CallsProvider *provider)
|
|||
"provider", provider,
|
||||
NULL);
|
||||
}
|
||||
|
||||
void
|
||||
calls_new_call_box_dial (CallsNewCallBox *self,
|
||||
const gchar *target)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
gboolean ok;
|
||||
CallsOrigin *origin;
|
||||
|
||||
g_return_if_fail (CALLS_IS_NEW_CALL_BOX (self));
|
||||
g_return_if_fail (target != NULL);
|
||||
|
||||
ok = gtk_combo_box_get_active_iter (self->origin_box, &iter);
|
||||
if (!ok)
|
||||
{
|
||||
g_debug ("Can't submit call with no origin");
|
||||
return;
|
||||
}
|
||||
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (self->origin_store), &iter,
|
||||
ORIGIN_STORE_COLUMN_ORIGIN, &origin,
|
||||
-1);
|
||||
g_assert (CALLS_IS_ORIGIN (origin));
|
||||
|
||||
calls_origin_dial (origin, target);
|
||||
}
|
||||
|
|
|
@ -35,7 +35,9 @@ G_BEGIN_DECLS
|
|||
|
||||
G_DECLARE_FINAL_TYPE (CallsNewCallBox, calls_new_call_box, CALLS, NEW_CALL_BOX, GtkBox);
|
||||
|
||||
CallsNewCallBox * calls_new_call_box_new (CallsProvider *provider);
|
||||
CallsNewCallBox * calls_new_call_box_new (CallsProvider *provider);
|
||||
void calls_new_call_box_dial (CallsNewCallBox *self,
|
||||
const gchar *target);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ struct _CallsRecordStore
|
|||
GomRepository *repository;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (CallsRecordStore, calls_record_store, G_TYPE_OBJECT);
|
||||
G_DEFINE_TYPE (CallsRecordStore, calls_record_store, G_TYPE_LIST_STORE);
|
||||
|
||||
|
||||
enum {
|
||||
|
@ -90,6 +90,133 @@ enum {
|
|||
static GParamSpec *props[PROP_LAST_PROP];
|
||||
|
||||
|
||||
static void
|
||||
load_calls_fetch_cb (GomResourceGroup *group,
|
||||
GAsyncResult *res,
|
||||
CallsRecordStore *self)
|
||||
{
|
||||
gboolean ok;
|
||||
GError *error = NULL;
|
||||
guint count, i;
|
||||
gpointer *records;
|
||||
|
||||
ok = gom_resource_group_fetch_finish (group,
|
||||
res,
|
||||
&error);
|
||||
if (error)
|
||||
{
|
||||
g_debug ("Error fetching call records: %s",
|
||||
error->message);
|
||||
g_error_free (error);
|
||||
return;
|
||||
}
|
||||
g_assert (ok);
|
||||
|
||||
count = gom_resource_group_get_count (group);
|
||||
g_debug ("Fetched %u call records from database `%s'",
|
||||
count, self->filename);
|
||||
records = g_new (gpointer, count);
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
GomResource *resource;
|
||||
CallsCallRecord *record;
|
||||
GDateTime *end = NULL;
|
||||
|
||||
resource = gom_resource_group_get_index (group, i);
|
||||
g_assert (resource != NULL);
|
||||
g_assert (CALLS_IS_CALL_RECORD (resource));
|
||||
record = CALLS_CALL_RECORD (resource);
|
||||
|
||||
records[i] = record;
|
||||
|
||||
g_object_get (G_OBJECT (record),
|
||||
"end", &end,
|
||||
NULL);
|
||||
if (end)
|
||||
{
|
||||
g_date_time_unref (end);
|
||||
}
|
||||
}
|
||||
|
||||
g_list_store_splice (G_LIST_STORE (self),
|
||||
0,
|
||||
0,
|
||||
records,
|
||||
count);
|
||||
|
||||
g_free (records);
|
||||
g_object_unref (group);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
load_calls_find_cb (GomRepository *repository,
|
||||
GAsyncResult *res,
|
||||
CallsRecordStore *self)
|
||||
{
|
||||
GomResourceGroup *group;
|
||||
GError *error = NULL;
|
||||
guint count;
|
||||
|
||||
group = gom_repository_find_finish (repository,
|
||||
res,
|
||||
&error);
|
||||
if (error)
|
||||
{
|
||||
g_debug ("Error finding call records in database `%s': %s",
|
||||
self->filename, error->message);
|
||||
g_error_free (error);
|
||||
return;
|
||||
}
|
||||
g_assert (group != NULL);
|
||||
|
||||
count = gom_resource_group_get_count (group);
|
||||
if (count == 0)
|
||||
{
|
||||
g_debug ("No call records found in database `%s'",
|
||||
self->filename);
|
||||
return;
|
||||
}
|
||||
|
||||
g_debug ("Found %u call records in database `%s', fetching",
|
||||
count, self->filename);
|
||||
gom_resource_group_fetch_async
|
||||
(group,
|
||||
0,
|
||||
count,
|
||||
(GAsyncReadyCallback)load_calls_fetch_cb,
|
||||
self);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
load_calls (CallsRecordStore *self)
|
||||
{
|
||||
GomFilter *filter;
|
||||
GomSorting *sorting;
|
||||
|
||||
filter = gom_filter_new_is_not_null
|
||||
(CALLS_TYPE_CALL_RECORD, "start");
|
||||
|
||||
sorting = gom_sorting_new (CALLS_TYPE_CALL_RECORD,
|
||||
"start",
|
||||
GOM_SORTING_DESCENDING,
|
||||
NULL);
|
||||
|
||||
g_debug ("Finding records in call record database `%s'",
|
||||
self->filename);
|
||||
gom_repository_find_sorted_async (self->repository,
|
||||
CALLS_TYPE_CALL_RECORD,
|
||||
filter,
|
||||
sorting,
|
||||
(GAsyncReadyCallback)load_calls_find_cb,
|
||||
self);
|
||||
|
||||
g_object_unref (G_OBJECT (filter));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
set_up_repo_migrate_cb (GomRepository *repo,
|
||||
GAsyncResult *res,
|
||||
|
@ -120,6 +247,7 @@ set_up_repo_migrate_cb (GomRepository *repo,
|
|||
{
|
||||
g_debug ("Successfully migrated call record database `%s'",
|
||||
self->filename);
|
||||
load_calls (self);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -256,12 +384,19 @@ open_repo (CallsRecordStore *self)
|
|||
}
|
||||
|
||||
|
||||
static void
|
||||
record_call_save_cb (GomResource *resource,
|
||||
GAsyncResult *res,
|
||||
CallsCall *call)
|
||||
struct CallsRecordCallData
|
||||
{
|
||||
GObject * const call_obj = G_OBJECT (call);
|
||||
CallsRecordStore *self;
|
||||
CallsCall *call;
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
record_call_save_cb (GomResource *resource,
|
||||
GAsyncResult *res,
|
||||
struct CallsRecordCallData *data)
|
||||
{
|
||||
GObject * const call_obj = G_OBJECT (data->call);
|
||||
GError *error = NULL;
|
||||
gboolean ok;
|
||||
|
||||
|
@ -284,8 +419,15 @@ record_call_save_cb (GomResource *resource,
|
|||
else
|
||||
{
|
||||
g_debug ("Successfully saved new call record to database");
|
||||
g_list_store_insert (G_LIST_STORE (data->self),
|
||||
0,
|
||||
CALLS_CALL_RECORD (resource));
|
||||
g_object_set_data (call_obj, "calls-call-start", NULL);
|
||||
}
|
||||
|
||||
g_object_unref (data->call);
|
||||
g_object_unref (data->self);
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
|
||||
|
@ -296,6 +438,7 @@ record_call (CallsRecordStore *self,
|
|||
GObject * const call_obj = G_OBJECT (call);
|
||||
GDateTime *start;
|
||||
CallsCallRecord *record;
|
||||
struct CallsRecordCallData *data;
|
||||
|
||||
g_assert (g_object_get_data (call_obj, "calls-call-record") == NULL);
|
||||
|
||||
|
@ -312,10 +455,15 @@ record_call (CallsRecordStore *self,
|
|||
g_object_set_data_full (call_obj, "calls-call-record",
|
||||
record, g_object_unref);
|
||||
|
||||
data = g_new (struct CallsRecordCallData, 1);
|
||||
g_object_ref (self);
|
||||
g_object_ref (call);
|
||||
data->self = self;
|
||||
data->call = call;
|
||||
|
||||
gom_resource_save_async (GOM_RESOURCE (record),
|
||||
(GAsyncReadyCallback)record_call_save_cb,
|
||||
call);
|
||||
data);
|
||||
}
|
||||
|
||||
|
||||
|
@ -550,6 +698,8 @@ dispose (GObject *object)
|
|||
|
||||
g_clear_object (&self->provider);
|
||||
|
||||
g_list_store_remove_all (G_LIST_STORE (self));
|
||||
|
||||
g_clear_object (&self->repository);
|
||||
close_adapter (self);
|
||||
|
||||
|
@ -604,6 +754,7 @@ CallsRecordStore *
|
|||
calls_record_store_new (CallsProvider *provider)
|
||||
{
|
||||
return g_object_new (CALLS_TYPE_RECORD_STORE,
|
||||
"item-type", CALLS_TYPE_CALL_RECORD,
|
||||
"provider", provider,
|
||||
NULL);
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ G_BEGIN_DECLS
|
|||
|
||||
#define CALLS_TYPE_RECORD_STORE (calls_record_store_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (CallsRecordStore, calls_record_store, CALLS, RECORD_STORE, GObject);
|
||||
G_DECLARE_FINAL_TYPE (CallsRecordStore, calls_record_store, CALLS, RECORD_STORE, GListStore);
|
||||
|
||||
CallsRecordStore *calls_record_store_new (CallsProvider *provider);
|
||||
|
||||
|
|
|
@ -10,8 +10,13 @@
|
|||
<file preprocess="xml-stripblanks">history-header-bar.ui</file>
|
||||
<file preprocess="xml-stripblanks">new-call-box.ui</file>
|
||||
<file preprocess="xml-stripblanks">new-call-header-bar.ui</file>
|
||||
<file preprocess="xml-stripblanks">call-record-row.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/sm/puri/calls/">
|
||||
<file>new-call-symbolic.svg</file>
|
||||
<file>call-arrow-incoming-symbolic.svg</file>
|
||||
<file>call-arrow-incoming-missed-symbolic.svg</file>
|
||||
<file>call-arrow-outgoing-symbolic.svg</file>
|
||||
<file>call-arrow-outgoing-missed-symbolic.svg</file>
|
||||
</gresource>
|
||||
</gresources>
|
||||
|
|
|
@ -54,6 +54,7 @@ calls_sources = files(['calls-message-source.c', 'calls-message-source.h',
|
|||
'util.c', 'util.h',
|
||||
'calls-call-record.c', 'calls-call-record.h',
|
||||
'calls-record-store.c', 'calls-record-store.h',
|
||||
'calls-call-record-row.c', 'calls-call-record-row.h',
|
||||
])
|
||||
|
||||
calls_config_data = config_data
|
||||
|
|
91
src/ui/call-record-row.ui
Normal file
91
src/ui/call-record-row.ui
Normal file
|
@ -0,0 +1,91 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.22.1 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.20"/>
|
||||
<template class="CallsCallRecordRow" parent="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="avatar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">8</property>
|
||||
<property name="margin_top">8</property>
|
||||
<property name="margin_bottom">8</property>
|
||||
<property name="icon-name">avatar-default-symbolic</property>
|
||||
<property name="icon-size">6</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImage" id="type">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">8</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="target">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">10</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="redial">
|
||||
<property name="visible">True</property>
|
||||
<property name="margin_left">12</property>
|
||||
<property name="margin_right">8</property>
|
||||
<property name="margin_top">8</property>
|
||||
<property name="margin_bottom">8</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<signal name="clicked" handler="redial_clicked_cb" swapped="yes"/>
|
||||
<style>
|
||||
<class name="image-button"/>
|
||||
</style>
|
||||
<child internal-child="accessible">
|
||||
<object class="AtkObject" id="a11y-hide-dial-pad">
|
||||
<property name="accessible-name" translatable="yes">Call the party</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="icon-name">call-start-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="time">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">8</property>
|
||||
<property name="justify">center</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
<attributes>
|
||||
<attribute name="scale" value="0.7"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
|
@ -2,11 +2,9 @@
|
|||
<!-- Generated with glade 3.22.0 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.22"/>
|
||||
<requires lib="libhandy" version="0.0"/>
|
||||
<object class="GtkListStore" id="history_store"/>
|
||||
<template class="CallsHistoryBox" parent="GtkStack">
|
||||
<child>
|
||||
<object class="GtkBox" id="empty_view">
|
||||
<object class="GtkBox" id="empty">
|
||||
<property name="visible">1</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
|
@ -34,17 +32,33 @@
|
|||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">empty</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<object class="GtkTreeView" id="history_view">
|
||||
<object class="HdyColumn">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="model">history_store</property>
|
||||
<property name="visible">True</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection"/>
|
||||
<property name="maximum-width">720</property>
|
||||
<property name="linear-growth-width">720</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<object class="GtkListBox" id="history">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">history</property>
|
||||
</packing>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
|
|
|
@ -76,17 +76,6 @@
|
|||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="expand">True</property>
|
||||
<child>
|
||||
<object class="CallsHistoryBox" id="history_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">recent</property>
|
||||
<property name="title" translatable="yes">Recent</property>
|
||||
<property name="icon_name">document-open-recent-symbolic</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="contacts">
|
||||
<property name="visible">True</property>
|
||||
|
|
48
src/util.c
48
src/util.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2018 Purism SPC
|
||||
* Copyright (C) 2018, 2019 Purism SPC
|
||||
*
|
||||
* This file is part of Calls.
|
||||
*
|
||||
|
@ -85,3 +85,49 @@ calls_entry_append (GtkEntry *entry,
|
|||
|
||||
gtk_entry_buffer_insert_text (buf, len, str, 1);
|
||||
}
|
||||
|
||||
|
||||
gboolean
|
||||
calls_date_time_is_same_day (GDateTime *a,
|
||||
GDateTime *b)
|
||||
{
|
||||
#define eq(member) \
|
||||
(g_date_time_get_##member (a) == \
|
||||
g_date_time_get_##member (b))
|
||||
|
||||
return
|
||||
eq (year)
|
||||
&&
|
||||
eq (month)
|
||||
&&
|
||||
eq (day_of_month);
|
||||
|
||||
#undef eq
|
||||
}
|
||||
|
||||
|
||||
gboolean
|
||||
calls_date_time_is_yesterday (GDateTime *now,
|
||||
GDateTime *t)
|
||||
{
|
||||
GDateTime *yesterday;
|
||||
gboolean same_day;
|
||||
|
||||
yesterday = g_date_time_add_days (now, -1);
|
||||
|
||||
same_day = calls_date_time_is_same_day (yesterday, t);
|
||||
|
||||
g_date_time_unref (yesterday);
|
||||
|
||||
return same_day;
|
||||
}
|
||||
|
||||
|
||||
gboolean
|
||||
calls_date_time_is_same_year (GDateTime *a,
|
||||
GDateTime *b)
|
||||
{
|
||||
return
|
||||
g_date_time_get_year (a) ==
|
||||
g_date_time_get_year (b);
|
||||
}
|
||||
|
|
31
src/util.h
31
src/util.h
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2018 Purism SPC
|
||||
* Copyright (C) 2018, 2019 Purism SPC
|
||||
*
|
||||
* This file is part of Calls.
|
||||
*
|
||||
|
@ -76,6 +76,27 @@ G_BEGIN_DECLS
|
|||
ptr = new_value;
|
||||
|
||||
|
||||
#define calls_clear_source(source_id_ptr) \
|
||||
if (*source_id_ptr != 0) \
|
||||
{ \
|
||||
g_source_remove (*source_id_ptr); \
|
||||
*source_id_ptr = 0; \
|
||||
}
|
||||
|
||||
#define calls_clear_signal(object,handler_id_ptr) \
|
||||
if (*handler_id_ptr != 0) \
|
||||
{ \
|
||||
g_signal_handler_disconnect (object, *handler_id_ptr); \
|
||||
*handler_id_ptr = 0; \
|
||||
}
|
||||
|
||||
#define calls_date_time_unref(date_time) \
|
||||
if (date_time) \
|
||||
{ \
|
||||
g_date_time_unref (date_time); \
|
||||
}
|
||||
|
||||
|
||||
/** Find a particular pointer value in a GtkListStore */
|
||||
gboolean
|
||||
calls_list_store_find (GtkListStore *store,
|
||||
|
@ -88,6 +109,14 @@ void
|
|||
calls_entry_append (GtkEntry *entry,
|
||||
gchar character);
|
||||
|
||||
|
||||
gboolean calls_date_time_is_same_day (GDateTime *a,
|
||||
GDateTime *b);
|
||||
gboolean calls_date_time_is_yesterday (GDateTime *now,
|
||||
GDateTime *t);
|
||||
gboolean calls_date_time_is_same_year (GDateTime *a,
|
||||
GDateTime *b);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* CALLS__UTIL_H__ */
|
||||
|
|
Loading…
Reference in a new issue