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
parent b15c2876
......@@ -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");
}
......
......@@ -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");
......
......@@ -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;
}
......
......@@ -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),
......
......@@ -8,94 +8,110 @@
<property name="activatable">False</property>
<property name="selectable">False</property>
<child>
<object class="GtkBox">
<object class="GtkEventBox" id="event_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="HdyAvatar" id="avatar">
<object class="GtkBox">
<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="size">48</property>
<property name="text" bind-source="target" bind-property="label" bind-flags="sync-create"></property>
<property name="show-initials">True</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>
<property name="ellipsize">middle</property>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button">
<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>
<property name="action-name">app.dial</property>
<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>
<child>
<object class="HdyAvatar" 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="size">48</property>
<property name="text" bind-source="target" bind-property="label" bind-flags="sync-create"></property>
<property name="show-initials">True</property>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkImage">
<object class="GtkImage" id="type">
<property name="visible">True</property>
<property name="icon-name">call-start-symbolic</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>
<property name="ellipsize">middle</property>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button">
<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>
<property name="action-name">app.dial</property>
<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>
</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>
</object>
</child>
</template>
<object class="GtkPopover" id="popover">
<property name="relative-to">CallsCallRecordRow</property>
</object>
<menu id="context_menu">
<section>
<item>
<attribute name="label" translatable="yes">_Delete Call</attribute>
<attribute name="action">delete-call</attribute>
</item>
</section>
</menu>
</interface>
......@@ -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
}
......@@ -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__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment