aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJason Conti <jason.conti@gmail.com>2016-08-25 16:28:00 -0400
committerJason Conti <jason.conti@gmail.com>2016-08-25 16:28:00 -0400
commit9cb6e6e7bea6e7cded5212d99083ff948cb100bb (patch)
tree934d3f5e0efc3c3396f92f21a081fc523df07908 /src
parentb67ee91bf305ef3b0cb072af715a0a8914e267cc (diff)
downloadayatana-indicator-notifications-9cb6e6e7bea6e7cded5212d99083ff948cb100bb.tar.gz
ayatana-indicator-notifications-9cb6e6e7bea6e7cded5212d99083ff948cb100bb.tar.bz2
ayatana-indicator-notifications-9cb6e6e7bea6e7cded5212d99083ff948cb100bb.zip
* Added support for visiting links within notifications
- Currently inserting two test links into each message (no link parsing) - Changes how notifications are highlighted and closed + Removed prelight on the menuitem so that the links render correctly + Must click the X to close a notification now
Diffstat (limited to 'src')
-rw-r--r--src/notification-menuitem.c176
-rw-r--r--src/notification-menuitem.h3
2 files changed, 155 insertions, 24 deletions
diff --git a/src/notification-menuitem.c b/src/notification-menuitem.c
index ecf760c..f1986f4 100644
--- a/src/notification-menuitem.c
+++ b/src/notification-menuitem.c
@@ -19,9 +19,17 @@ enum {
static void notification_menuitem_class_init(NotificationMenuItemClass *klass);
static void notification_menuitem_init(NotificationMenuItem *self);
-static void notification_activated_cb(GtkMenuItem *menuitem, gpointer user_data);
-static gboolean notification_button_press_cb(GtkWidget *widget, GdkEventButton *event, gpointer user_data);
-static gboolean notification_button_release_cb(GtkWidget *widget, GdkEventButton *event, gpointer user_data);
+static void notification_menuitem_activate(GtkMenuItem *menuitem);
+static gboolean notification_menuitem_motion(GtkWidget *widget, GdkEventMotion *event);
+static gboolean notification_menuitem_leave(GtkWidget *widget, GdkEventCrossing *event);
+static gboolean notification_menuitem_button_press(GtkWidget *widget, GdkEventButton *event);
+static gboolean notification_menuitem_button_release(GtkWidget *widget, GdkEventButton *event);
+static void notification_menuitem_select(GtkMenuItem *item);
+static void notification_menuitem_deselect(GtkMenuItem *item);
+
+static gboolean notification_menuitem_activate_link_cb(GtkLabel *label, gchar *uri, gpointer user_data);
+
+static gboolean widget_contains_event(GtkWidget *widget, GdkEventButton *event);
static guint notification_menuitem_signals[LAST_SIGNAL] = { 0 };
@@ -30,11 +38,19 @@ G_DEFINE_TYPE (NotificationMenuItem, notification_menuitem, GTK_TYPE_MENU_ITEM);
static void
notification_menuitem_class_init(NotificationMenuItemClass *klass)
{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
GtkMenuItemClass *menu_item_class = GTK_MENU_ITEM_CLASS(klass);
+ widget_class->leave_notify_event = notification_menuitem_leave;
+ widget_class->motion_notify_event = notification_menuitem_motion;
+ widget_class->button_press_event = notification_menuitem_button_press;
+ widget_class->button_release_event = notification_menuitem_button_release;
+
g_type_class_add_private(klass, sizeof(NotificationMenuItemPrivate));
- menu_item_class->hide_on_activate = FALSE;
+ menu_item_class->activate = notification_menuitem_activate;
+ menu_item_class->select = notification_menuitem_select;
+ menu_item_class->deselect = notification_menuitem_deselect;
notification_menuitem_signals[CLICKED] =
g_signal_new(NOTIFICATION_MENUITEM_SIGNAL_CLICKED,
@@ -51,7 +67,7 @@ notification_menuitem_init(NotificationMenuItem *self)
{
self->priv = NOTIFICATION_MENUITEM_GET_PRIVATE(self);
- GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+ self->priv->hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
self->priv->label = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(self->priv->label), 0, 0);
@@ -59,20 +75,19 @@ notification_menuitem_init(NotificationMenuItem *self)
gtk_label_set_line_wrap(GTK_LABEL(self->priv->label), TRUE);
gtk_label_set_line_wrap_mode(GTK_LABEL(self->priv->label), PANGO_WRAP_WORD_CHAR);
gtk_label_set_max_width_chars(GTK_LABEL(self->priv->label), NOTIFICATION_MENUITEM_MAX_CHARS);
+ gtk_label_set_track_visited_links(GTK_LABEL(self->priv->label), TRUE);
+
+ g_signal_connect(self->priv->label, "activate-link", G_CALLBACK(notification_menuitem_activate_link_cb), self);
- gtk_box_pack_start(GTK_BOX(hbox), self->priv->label, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(self->priv->hbox), self->priv->label, TRUE, TRUE, 0);
gtk_widget_show(self->priv->label);
self->priv->close_image = gtk_image_new_from_icon_name("gtk-close", GTK_ICON_SIZE_MENU);
gtk_widget_show(self->priv->close_image);
- gtk_box_pack_start(GTK_BOX(hbox), self->priv->close_image, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(self->priv->hbox), self->priv->close_image, FALSE, FALSE, 0);
- gtk_container_add(GTK_CONTAINER(self), hbox);
- gtk_widget_show(hbox);
-
- g_signal_connect(self, "activate", G_CALLBACK(notification_activated_cb), NULL);
- g_signal_connect(self, "button-press-event", G_CALLBACK(notification_button_press_cb), NULL);
- g_signal_connect(self, "button-release-event", G_CALLBACK(notification_button_release_cb), NULL);
+ gtk_container_add(GTK_CONTAINER(self), self->priv->hbox);
+ gtk_widget_show(self->priv->hbox);
}
GtkWidget *
@@ -92,7 +107,7 @@ notification_menuitem_set_from_notification(NotificationMenuItem *self, Notifica
gchar *body = g_markup_escape_text(notification_get_body(note), -1);
gchar *timestamp_string = g_markup_escape_text(unescaped_timestamp_string, -1);
- gchar *markup = g_strdup_printf("<b>%s</b>\n%s\n<small><i>%s %s <b>%s</b></i></small>",
+ gchar *markup = g_strdup_printf("<b>%s</b>\n%s\n<a href=\"http://www.google.com/\">Google</a> <a href=\"http://www.yahoo.com/\">Yahoo</a> \n<small><i>%s %s <b>%s</b></i></small>",
summary, body, timestamp_string, _("from"), app_name);
g_free(app_name);
@@ -107,39 +122,83 @@ notification_menuitem_set_from_notification(NotificationMenuItem *self, Notifica
}
/**
- * notification_activated_cb:
+ * notification_menuitem_activate:
* @menuitem: the menuitem
* @user_data: not used
*
* Emit a clicked event for the case where a keyboard activates a menuitem.
**/
static void
-notification_activated_cb(GtkMenuItem *menuitem, gpointer user_data)
+notification_menuitem_activate(GtkMenuItem *menuitem)
{
g_return_if_fail(IS_NOTIFICATION_MENUITEM(menuitem));
g_signal_emit(NOTIFICATION_MENUITEM(menuitem), notification_menuitem_signals[CLICKED], 0);
}
+static gboolean
+notification_menuitem_leave(GtkWidget *widget, GdkEventCrossing *event)
+{
+ g_return_val_if_fail(IS_NOTIFICATION_MENUITEM(widget), FALSE);
+
+ NotificationMenuItem *self = NOTIFICATION_MENUITEM(widget);
+
+ gtk_widget_event(self->priv->label, (GdkEvent *)event);
+ return FALSE;
+}
+
+static gboolean
+notification_menuitem_motion(GtkWidget *widget, GdkEventMotion *event)
+{
+ g_return_val_if_fail(IS_NOTIFICATION_MENUITEM(widget), FALSE);
+
+ NotificationMenuItem *self = NOTIFICATION_MENUITEM(widget);
+
+ GtkAllocation self_alloc;
+ GtkAllocation label_alloc;
+
+ gtk_widget_get_allocation(GTK_WIDGET(self), &self_alloc);
+ gtk_widget_get_allocation(self->priv->label, &label_alloc);
+
+ /* The event is mapped to the menu item's allocation, so we need to shift it
+ * to the label's allocation so that links are probably selected.
+ */
+ GdkEventMotion *e = (GdkEventMotion *)gdk_event_copy((GdkEvent *)event);
+ e->x = event->x - (label_alloc.x - self_alloc.x);
+ e->y = event->y - (label_alloc.y - self_alloc.y);
+
+ gtk_widget_event(self->priv->label, (GdkEvent *)e);
+
+ gdk_event_free((GdkEvent *)e);
+ return FALSE;
+}
+
/**
- * notification_button_press_cb:
+ * notification_menuitem_button_press:
* @widget: the menuitem
* @event: the button press event
- * @user_data: not used
*
* Override the menuitem button-press-event.
**/
static gboolean
-notification_button_press_cb(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
+notification_menuitem_button_press(GtkWidget *widget, GdkEventButton *event)
{
+ g_return_val_if_fail(IS_NOTIFICATION_MENUITEM(widget), FALSE);
+
+ NotificationMenuItem *self = NOTIFICATION_MENUITEM(widget);
+ GtkWidget *label = self->priv->label;
+
+ /* The context menu breaks everything so disable it for now */
+ if (event->button == GDK_BUTTON_PRIMARY && widget_contains_event(label, event)) {
+ gtk_widget_event(label, (GdkEvent *)event);
+ }
return TRUE;
}
/**
- * notification_button_release_cb:
+ * notification_menuitem_button_release:
* @widget: the menuitem
* @event: the button release event
- * @user_data: not used
*
* Override the menuitem button-release-event so that the menu isn't hidden when the
* item is removed.
@@ -147,10 +206,81 @@ notification_button_press_cb(GtkWidget *widget, GdkEventButton *event, gpointer
* FIXME: Only remove the item when the close image is clicked.
**/
static gboolean
-notification_button_release_cb(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
+notification_menuitem_button_release(GtkWidget *widget, GdkEventButton *event)
{
g_return_val_if_fail(IS_NOTIFICATION_MENUITEM(widget), FALSE);
- g_signal_emit(NOTIFICATION_MENUITEM(widget), notification_menuitem_signals[CLICKED], 0, event->button);
+ NotificationMenuItem *self = NOTIFICATION_MENUITEM(widget);
+
+ if (widget_contains_event(self->priv->close_image, event)) {
+ g_signal_emit(NOTIFICATION_MENUITEM(widget), notification_menuitem_signals[CLICKED], 0, event->button);
+ }
+ else {
+ /* The context menu breaks everything so disable it for now */
+ if (event->button == GDK_BUTTON_PRIMARY) {
+ gtk_widget_event(self->priv->label, (GdkEvent *)event);
+ }
+ }
return TRUE;
}
+
+static void
+notification_menuitem_select(GtkMenuItem *menuitem)
+{
+}
+
+static void
+notification_menuitem_deselect(GtkMenuItem *menuitem)
+{
+}
+
+static gboolean
+notification_menuitem_activate_link_cb(GtkLabel *label, gchar *uri, gpointer user_data)
+{
+ g_return_val_if_fail(IS_NOTIFICATION_MENUITEM(user_data), FALSE);
+
+ NotificationMenuItem *self = NOTIFICATION_MENUITEM(user_data);
+
+ /* Show the link */
+ GError *error = NULL;
+
+ if (!gtk_show_uri(gtk_widget_get_screen(GTK_WIDGET(label)),
+ uri, gtk_get_current_event_time(), &error)) {
+ g_warning("Unable to show '%s': %s", uri, error->message);
+ g_error_free(error);
+ }
+
+ /* Deactivate the menu shell so it doesn't block the screen */
+ GtkWidget *parent = gtk_widget_get_parent(GTK_WIDGET(self));
+ if (GTK_IS_MENU_SHELL(parent)) {
+ gtk_menu_shell_deactivate(GTK_MENU_SHELL(parent));
+ }
+
+ return TRUE;
+}
+
+static gboolean
+widget_contains_event(GtkWidget *widget, GdkEventButton *event)
+{
+ if (gtk_widget_get_window(widget) == NULL)
+ return FALSE;
+
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation(widget, &allocation);
+
+ GdkWindow *window = gtk_widget_get_window(widget);
+
+ int xwin, ywin;
+
+ gdk_window_get_origin(window, &xwin, &ywin);
+
+ int xmin = allocation.x;
+ int xmax = allocation.x + allocation.width;
+ int ymin = allocation.y;
+ int ymax = allocation.y + allocation.height;
+ int x = event->x_root - xwin;
+ int y = event->y_root - ywin;
+
+ return x >= xmin && x <= xmax && y >= ymin && y <= ymax;
+}
diff --git a/src/notification-menuitem.h b/src/notification-menuitem.h
index b13d8aa..c44c105 100644
--- a/src/notification-menuitem.h
+++ b/src/notification-menuitem.h
@@ -34,8 +34,9 @@ struct _NotificationMenuItemClass
};
struct _NotificationMenuItemPrivate {
- GtkWidget *label;
GtkWidget *close_image;
+ GtkWidget *hbox;
+ GtkWidget *label;
};
#define NOTIFICATION_MENUITEM_GET_PRIVATE(o) \