From 4bf5cd52321c60b6bbc4ac0b5bb633d157685a0b Mon Sep 17 00:00:00 2001 From: Evangelos Ribeiro Tzaras Date: Mon, 8 Jun 2020 15:09:57 +0200 Subject: [PATCH] Implement delete call with context menu on longpress * src/ui/call-record-row.ui: Add menu, GtkPopover. Surround existing elements with GtkEventBox to capture longpress/rightclicks * src/calls-call-record-row.c: Provide functions emiting "call-delete" signal, add widgets from ui file * src/calls-record.c: Add "call-delete" signal * src/calls-history-box.c: Add callback for "call-delete" signal * src/calls-record-store.c: Add callback for "call-delete" signal * src/util.c: Add convenience function calls_find_in_store for finding items in ListModel * src/util.h: Add declaration of calls_find_in_store --- src/calls-call-record-row.c | 92 ++++++++++++++++++++ src/calls-call-record.c | 15 ++++ src/calls-history-box.c | 42 ++++++++- src/calls-record-store.c | 55 ++++++++++++ src/ui/call-record-row.ui | 166 ++++++++++++++++++++---------------- src/util.c | 33 +++++++ src/util.h | 4 + 7 files changed, 330 insertions(+), 77 deletions(-) diff --git a/src/calls-call-record-row.c b/src/calls-call-record-row.c index 3e0495e..2bd3731 100644 --- a/src/calls-call-record-row.c +++ b/src/calls-call-record-row.c @@ -44,6 +44,13 @@ struct _CallsCallRecordRow GtkLabel *target; GtkLabel *time; GtkButton *button; + GtkPopover *popover; + GtkGesture *gesture; + GtkEventBox *event_box; + + GMenu *context_menu; + + GActionMap *action_map; CallsCallRecord *record; gulong answered_notify_handler_id; @@ -392,6 +399,45 @@ setup_contact (CallsCallRecordRow *self) } +static void +context_menu (GtkWidget *self, + GdkEvent *event) +{ + gtk_popover_popup (CALLS_CALL_RECORD_ROW (self)->popover); +} + + +static gboolean +calls_call_record_row_popup_menu (GtkWidget *self) +{ + context_menu (self, NULL); + return TRUE; +} + + +static void +long_pressed (GtkGestureLongPress *gesture, + gdouble x, + gdouble y, + GtkWidget *self) +{ + context_menu (self, NULL); +} + + +static gboolean +calls_call_record_row_button_press_event (GtkWidget *self, + GdkEventButton *event) +{ + if (gdk_event_triggers_context_menu ((GdkEvent *) event)) + { + context_menu (self, (GdkEvent *) event); + return TRUE; + } + return GTK_WIDGET_CLASS (calls_call_record_row_parent_class)->button_press_event (self, event); +} + + static void set_property (GObject *object, guint property_id, @@ -480,6 +526,8 @@ dispose (GObject *object) g_clear_object (&self->contact); g_clear_object (&self->contacts); + g_clear_object (&self->action_map); + g_clear_object (&self->gesture); calls_clear_source (&self->date_change_timeout); calls_clear_signal (self->record, &self->answered_notify_handler_id); @@ -501,6 +549,9 @@ calls_call_record_row_class_init (CallsCallRecordRowClass *klass) object_class->get_property = get_property; object_class->dispose = dispose; + widget_class->popup_menu = calls_call_record_row_popup_menu; + widget_class->button_press_event = calls_call_record_row_button_press_event; + props[PROP_RECORD] = g_param_spec_object ("record", "Record", @@ -524,18 +575,59 @@ calls_call_record_row_class_init (CallsCallRecordRowClass *klass) 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_child (widget_class, CallsCallRecordRow, button); + + gtk_widget_class_bind_template_child (widget_class, CallsCallRecordRow, event_box); + gtk_widget_class_bind_template_child (widget_class, CallsCallRecordRow, popover); + gtk_widget_class_bind_template_child (widget_class, CallsCallRecordRow, context_menu); } +static void +delete_call_activated (GSimpleAction *action, + GVariant *parameter, + gpointer data) +{ + GtkWidget *self = GTK_WIDGET (data); + g_signal_emit_by_name (CALLS_CALL_RECORD_ROW (self)->record, "call-delete"); +} + + +static GActionEntry entries[] = +{ + { "delete-call", delete_call_activated, NULL, NULL, NULL}, +}; + + static void calls_call_record_row_init (CallsCallRecordRow *self) { + GAction *act; gtk_widget_init_template (GTK_WIDGET (self)); g_signal_connect (self->avatar, "notify::text", G_CALLBACK (avatar_text_changed_cb), NULL); + + self->action_map = G_ACTION_MAP (g_simple_action_group_new ()); + g_action_map_add_action_entries (self->action_map, + entries, + G_N_ELEMENTS (entries), + self); + gtk_widget_insert_action_group (GTK_WIDGET (self), + "row-history", + G_ACTION_GROUP (self->action_map)); + + act = g_action_map_lookup_action (self->action_map, "delete-call"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (act), TRUE); + + self->gesture = gtk_gesture_long_press_new (GTK_WIDGET (self->event_box)); + gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (self->gesture), TRUE); + g_signal_connect (self->gesture, "pressed", G_CALLBACK (long_pressed), self); + + gtk_popover_bind_model (self->popover, + G_MENU_MODEL (self->context_menu), + "row-history"); } diff --git a/src/calls-call-record.c b/src/calls-call-record.c index d9d904f..7ca5dc5 100644 --- a/src/calls-call-record.c +++ b/src/calls-call-record.c @@ -55,6 +55,12 @@ enum { }; static GParamSpec *props[PROP_LAST_PROP]; +enum { + SIGNAL_CALL_DELETE, + SIGNAL_LAST_SIGNAL, +}; +static guint signals [SIGNAL_LAST_SIGNAL]; + static void get_property (GObject *object, @@ -167,6 +173,15 @@ calls_call_record_class_init (CallsCallRecordClass *klass) object_class->get_property = get_property; object_class->set_property = set_property; + signals[SIGNAL_CALL_DELETE] = + g_signal_new ("call-delete", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, + 0, NULL); + gom_resource_class_set_table (resource_class, "calls"); diff --git a/src/calls-history-box.c b/src/calls-history-box.c index 85569e0..4d6bac6 100644 --- a/src/calls-history-box.c +++ b/src/calls-history-box.c @@ -102,13 +102,51 @@ header_cb (GtkListBoxRow *row, } } +static void +delete_call_cb (CallsCallRecord *record, + CallsHistoryBox *self) +{ + guint position; + guint id; + gboolean ok; + + g_return_if_fail (CALLS_IS_CALL_RECORD (record)); + + ok = calls_find_in_store (self->model, + record, + &position); + + g_object_get (G_OBJECT (record), + "id", + &id, + NULL); + + if (!ok) + { + g_warning ("Could not find record with id %u in model", + id); + return; + } + + g_list_store_remove ((GListStore *) self->model, position); + + update(self); +} + static GtkWidget * create_row_cb (CallsCallRecord *record, CallsHistoryBox *self) { - return GTK_WIDGET (calls_call_record_row_new (record, - self->contacts)); + GtkWidget *row_widget; + row_widget = GTK_WIDGET (calls_call_record_row_new (record, + self->contacts)); + + g_signal_connect (record, + "call-delete", + G_CALLBACK (delete_call_cb), + self); + return row_widget; } diff --git a/src/calls-record-store.c b/src/calls-record-store.c index d9e5e3d..d8bd93f 100644 --- a/src/calls-record-store.c +++ b/src/calls-record-store.c @@ -81,6 +81,56 @@ struct _CallsRecordStore G_DEFINE_TYPE (CallsRecordStore, calls_record_store, G_TYPE_LIST_STORE); +static void +delete_record_cb (GomResource *resource, + GAsyncResult *res, + CallsRecordStore *self) +{ + g_autoptr (GError) error = NULL; + gboolean ok; + guint id; + + ok = gom_resource_delete_finish (resource, + res, + &error); + + g_object_get (G_OBJECT (resource), + "id", + &id, + NULL); + + if (!ok) + { + if (error) + { + g_warning ("Error deleting call record with id %u from database %s", + id, error->message); + return; + } + else + { + g_warning ("Unknown error deleting call record with id %u from database", + id); + } + } + else { + g_debug ("Successfully deleted call record with id %u from database", + id); + } + +} + + +static void +delete_call_cb (CallsCallRecord *record, + CallsRecordStore *self) +{ + gom_resource_delete_async (GOM_RESOURCE (record), + (GAsyncReadyCallback) delete_record_cb, + self); +} + + static void load_calls_fetch_cb (GomResourceGroup *group, GAsyncResult *res, @@ -127,6 +177,11 @@ load_calls_fetch_cb (GomResourceGroup *group, { g_date_time_unref (end); } + + g_signal_connect (record, + "call-delete", + G_CALLBACK (delete_call_cb), + self); } g_list_store_splice (G_LIST_STORE (self), diff --git a/src/ui/call-record-row.ui b/src/ui/call-record-row.ui index 484a11e..6ac37aa 100644 --- a/src/ui/call-record-row.ui +++ b/src/ui/call-record-row.ui @@ -8,94 +8,110 @@ False False - + True - False - + True False - 8 - 8 - 8 - 48 - - True - - - 0 - - - - - True - False - 8 - - - 1 - - - - - True - False - 10 - middle - - - 2 - - - - - True - 12 - 8 - 8 - 8 - center - center - app.dial - - - - Call the party + + + True + False + 8 + 8 + 8 + 48 + + True + + 0 + - + True - call-start-symbolic + False + 8 + + 1 + + + + + True + False + 10 + middle + + + 2 + + + + + True + 12 + 8 + 8 + 8 + center + center + app.dial + + + + Call the party + + + + + True + call-start-symbolic + + + + + end + 3 + + + + + True + False + 8 + center + + + + + + + end + 4 + - - end - 3 - - - - - True - False - 8 - center - - - - - - - end - 4 - + + CallsCallRecordRow + + +
+ + _Delete Call + delete-call + +
+
diff --git a/src/util.c b/src/util.c index 32f5e57..69749b4 100644 --- a/src/util.c +++ b/src/util.c @@ -142,3 +142,36 @@ calls_date_time_is_same_year (GDateTime *a, g_date_time_get_year (a) == g_date_time_get_year (b); } + + +gboolean +calls_find_in_store (GListModel *list, + gpointer item, + guint *position) +{ +#if GLIB_CHECK_VERSION(2, 64, 0) + return g_list_store_find ((GListStore *) list, + item, + position); +#else + guint count; + + g_return_val_if_fail (G_IS_LIST_MODEL (list), FALSE); + + count = g_list_model_get_n_items (list); + + for (guint i = 0; i < count; i++) + { + g_autoptr (GObject) object = NULL; + + object = g_list_model_get_item (list, i); + + if (object == item) + { + *position = i; + return TRUE; + } + } + return FALSE; +#endif +} diff --git a/src/util.h b/src/util.h index 5af7be7..bb34f32 100644 --- a/src/util.h +++ b/src/util.h @@ -138,6 +138,10 @@ gboolean calls_date_time_is_yesterday (GDateTime *now, gboolean calls_date_time_is_same_year (GDateTime *a, GDateTime *b); +gboolean calls_find_in_store (GListModel *list, + gpointer item, + guint *position); + G_END_DECLS #endif /* CALLS__UTIL_H__ */