[Galago-commits] r2421 - in trunk/notification-daemon: . src themes
themes/bubble
galago-commits at freedesktop.org
galago-commits at freedesktop.org
Wed Jan 11 01:53:27 PST 2006
Author: chipx86
Date: 2006-01-11 01:53:24 -0800 (Wed, 11 Jan 2006)
New Revision: 2421
Added:
trunk/notification-daemon/src/engines.c
trunk/notification-daemon/src/engines.h
trunk/notification-daemon/themes/
trunk/notification-daemon/themes/Makefile.am
trunk/notification-daemon/themes/bubble/
trunk/notification-daemon/themes/bubble/Makefile.am
trunk/notification-daemon/themes/bubble/eggnotificationbubblewidget.c
trunk/notification-daemon/themes/bubble/eggnotificationbubblewidget.h
trunk/notification-daemon/themes/bubble/theme.c
trunk/notification-daemon/themes/standard/
Removed:
trunk/notification-daemon/src/eggnotificationbubblewidget.c
trunk/notification-daemon/src/eggnotificationbubblewidget.h
Modified:
trunk/notification-daemon/ChangeLog
trunk/notification-daemon/Makefile.am
trunk/notification-daemon/configure.ac
trunk/notification-daemon/src/Makefile.am
trunk/notification-daemon/src/daemon.c
Log:
Big change. We're moving to a theme engine model for displaying notifications. This is something I've wanted for a long time. For now, the "bubble" theme, as I call it (which we can definitely change) is the default. We'll make modules configurable by the theme and/or by the user later. Make sure you do a make install in themes!
Modified: trunk/notification-daemon/ChangeLog
===================================================================
--- trunk/notification-daemon/ChangeLog 2006-01-11 09:13:36 UTC (rev 2420)
+++ trunk/notification-daemon/ChangeLog 2006-01-11 09:53:24 UTC (rev 2421)
@@ -1,3 +1,24 @@
+Wed Jan 11 01:51:12 PST 2006 Christian Hammond <chipx86 at chipx86.com>
+
+ * src/Makefile.am:
+ * src/daemon.c:
+ D src/eggnotificationbubblewidget.c:
+ D src/eggnotificationbubblewidget.h:
+ A src/engines.c:
+ A src/engines.h:
+ A themes/bubble/Makefile.am:
+ A themes/bubble/eggnotificationbubblewidget.c:
+ A themes/bubble/eggnotificationbubblewidget.h:
+ A themes/bubble/theme.c:
+ A themes/Makefile.am:
+ * Makefile.am:
+ * configure.ac:
+ - Big change. We're moving to a theme engine model for displaying
+ notifications. This is something I've wanted for a long time. For now,
+ the "bubble" theme, as I call it (which we can definitely change) is
+ the default. We'll make modules configurable by the theme and/or by
+ the user later. Make sure you do a make install in themes!
+
Tue Jan 10 23:56:01 PST 2006 Christian Hammond <chipx86 at chipx86.com>
* src/Makefile.am:
Modified: trunk/notification-daemon/Makefile.am
===================================================================
--- trunk/notification-daemon/Makefile.am 2006-01-11 09:13:36 UTC (rev 2420)
+++ trunk/notification-daemon/Makefile.am 2006-01-11 09:53:24 UTC (rev 2421)
@@ -1,4 +1,4 @@
-SUBDIRS = src
+SUBDIRS = src themes
servicedir = $(DBUS_SERVICES_DIR)
service_DATA = notification-daemon.service
Modified: trunk/notification-daemon/configure.ac
===================================================================
--- trunk/notification-daemon/configure.ac 2006-01-11 09:13:36 UTC (rev 2420)
+++ trunk/notification-daemon/configure.ac 2006-01-11 09:53:24 UTC (rev 2421)
@@ -143,6 +143,8 @@
Makefile
notification-daemon.service
src/Makefile
+themes/Makefile
+themes/bubble/Makefile
])
AC_OUTPUT
Modified: trunk/notification-daemon/src/Makefile.am
===================================================================
--- trunk/notification-daemon/src/Makefile.am 2006-01-11 09:13:36 UTC (rev 2420)
+++ trunk/notification-daemon/src/Makefile.am 2006-01-11 09:53:24 UTC (rev 2421)
@@ -1,13 +1,11 @@
-INCLUDES = -I$(top_srcdir) $(NOTIFICATION_DAEMON_CFLAGS)
-
EXTRA_DIST = notificationdaemon.xml
libexec_PROGRAMS = notification-daemon
-notification_daemon_SOURCES = \
- eggnotificationbubblewidget.c \
- eggnotificationbubblewidget.h \
- daemon.c \
+notification_daemon_SOURCES = \
+ engines.c \
+ engines.h \
+ daemon.c \
daemon.h
notification_daemon_LDADD = $(NOTIFICATION_DAEMON_LIBS)
@@ -16,3 +14,9 @@
notificationdaemon-dbus-glue.h: notificationdaemon.xml
dbus-binding-tool --mode=glib-server $(srcdir)/notificationdaemon.xml > notificationdaemon-dbus-glue.h
+
+INCLUDES = \
+ -I$(top_srcdir) \
+ $(NOTIFICATION_DAEMON_CFLAGS) \
+ -DENGINES_DIR=\"$(LIBDIR)/notification-daemon/engines\"
+
Modified: trunk/notification-daemon/src/daemon.c
===================================================================
--- trunk/notification-daemon/src/daemon.c 2006-01-11 09:13:36 UTC (rev 2420)
+++ trunk/notification-daemon/src/daemon.c 2006-01-11 09:53:24 UTC (rev 2421)
@@ -20,7 +20,7 @@
#include "config.h"
#include "daemon.h"
-#include "eggnotificationbubblewidget.h"
+#include "engines.h"
#include "notificationdaemon-dbus-glue.h"
#include <stdlib.h>
@@ -41,7 +41,7 @@
gboolean has_timeout;
guint id;
- EggNotificationBubbleWidget *widget;
+ GtkWindow *nw;
};
typedef struct _NotifyTimeout NotifyTimeout;
@@ -89,8 +89,8 @@
static void
_notify_timeout_destroy (NotifyTimeout *nt)
{
- gtk_widget_destroy ((GtkWidget *)nt->widget);
- g_free (nt);
+ gtk_widget_destroy(GTK_WIDGET(nt->nw));
+ g_free(nt);
}
static void
@@ -135,6 +135,10 @@
return daemon;
}
+/*
+ * XXX The notify_widget thing needs to be replaced with some struct.
+ */
+#if 0
static void
_emit_action_invoked_signal (GObject *notify_widget, gchar *action)
{
@@ -179,8 +183,15 @@
dbus_connection_unref (con);
}
}
+#endif
static void
+_action_invoked_cb(const char *key)
+{
+ g_message("'%s' invoked", key);
+}
+
+static void
_emit_closed_signal (GObject *notify_widget)
{
DBusConnection *con;
@@ -239,9 +250,9 @@
if (nt)
{
- _emit_closed_signal (G_OBJECT (nt->widget));
+ _emit_closed_signal(G_OBJECT(nt->nw));
- egg_notification_bubble_widget_hide (nt->widget);
+ theme_hide_notification(nt->nw);
g_hash_table_remove (priv->notification_hash, &id);
}
}
@@ -268,14 +279,14 @@
if (now.tv_sec > expiration.tv_sec)
{
- _emit_closed_signal (G_OBJECT (nt->widget));
+ _emit_closed_signal(G_OBJECT(nt->nw));
return TRUE;
}
else if (now.tv_sec == expiration.tv_sec)
{
if (now.tv_usec > expiration.tv_usec)
{
- _emit_closed_signal (G_OBJECT (nt->widget));
+ _emit_closed_signal (G_OBJECT (nt->nw));
return TRUE;
}
}
@@ -333,9 +344,7 @@
}
static guint
-_store_notification (NotifyDaemon *daemon,
- EggNotificationBubbleWidget *bw,
- int timeout)
+_store_notification(NotifyDaemon *daemon, GtkWindow *nw, int timeout)
{
NotifyDaemonPrivate *priv;
NotifyTimeout *nt;
@@ -360,7 +369,7 @@
nt = (NotifyTimeout *) g_new0(NotifyTimeout, 1);
nt->id = id;
- nt->widget = bw;
+ nt->nw = nw;
_calculate_timeout (daemon, nt, timeout);
@@ -372,9 +381,8 @@
}
static gboolean
-_notify_daemon_process_icon_data (NotifyDaemon *daemon,
- EggNotificationBubbleWidget *bw,
- GValue *icon_data)
+_notify_daemon_process_icon_data(NotifyDaemon *daemon, GtkWindow *nw,
+ GValue *icon_data)
{
const guchar *data;
gboolean has_alpha;
@@ -384,6 +392,7 @@
int rowstride;
int n_channels;
gsize expected_len;
+ GdkPixbuf *pixbuf;
GValueArray *image_struct;
GValue *value;
@@ -513,38 +522,38 @@
}
data = (guchar *)g_memdup (tmp_array->data, tmp_array->len);
+ pixbuf = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, has_alpha,
+ bits_per_sample, width, height,
+ rowstride,
+ (GdkPixbufDestroyNotify)g_free, NULL);
+ theme_set_notification_icon(nw, pixbuf);
+ g_object_unref(G_OBJECT(pixbuf));
- egg_notification_bubble_widget_set_icon_from_data (bw,
- data,
- has_alpha,
- bits_per_sample,
- width,
- height,
- rowstride);
return TRUE;
}
+#if 0
static void
-_notification_daemon_handle_bubble_widget_action (GtkWidget *b,
- EggNotificationBubbleWidget *bw)
+_notification_daemon_handle_bubble_widget_action(GtkWidget *b, GtkWindow *nw)
{
gchar *action;
action = (gchar *) g_object_get_data (G_OBJECT (b), "_notify_action");
- _emit_action_invoked_signal (G_OBJECT (bw), action);
+ _emit_action_invoked_signal (G_OBJECT (nw), action);
}
+#endif
static void
-_notification_daemon_handle_bubble_widget_default (EggNotificationBubbleWidget *bw,
- NotifyDaemon *daemon)
+_notification_daemon_handle_bubble_widget_default(GtkWindow *nw,
+ NotifyDaemon *daemon)
{
- _close_notification (daemon, GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (bw), "_notify_id")));
+ _close_notification(daemon,
+ GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(nw), "_notify_id")));
}
static void
-_remove_bubble_from_poptart_stack (EggNotificationBubbleWidget *bw,
- NotifyDaemon *daemon)
+_remove_bubble_from_poptart_stack(GtkWindow *nw, NotifyDaemon *daemon)
{
NotifyDaemonPrivate *priv;
GdkRectangle workarea;
@@ -566,26 +575,27 @@
y = workarea.y + workarea.height;
x = 0;
while (link)
- {
- EggNotificationBubbleWidget *b;
+ {
+ GtkWindow *nw2;
GtkRequisition req;
- b = EGG_NOTIFICATION_BUBBLE_WIDGET (link->data);
- if (b != bw)
- {
- printf ("dude\n");
+ nw2 = link->data;
- gtk_widget_size_request (GTK_WIDGET (b), &req);
+ if (nw2 != nw)
+ {
+ printf ("dude\n");
+ gtk_widget_size_request(GTK_WIDGET(nw2), &req);
+
x = workarea.x + workarea.width - req.width;
y = y - req.height;
- egg_notification_bubble_widget_set_pos (b, x, y);
- }
+ theme_move_notification(nw2, x, y);
+ }
else
- {
- remove_link = link;
- }
+ {
+ remove_link = link;
+ }
link = link->next;
}
@@ -595,8 +605,7 @@
}
static void
-_notify_daemon_add_bubble_to_poptart_stack (NotifyDaemon *daemon,
- EggNotificationBubbleWidget *bw)
+_notify_daemon_add_bubble_to_poptart_stack(NotifyDaemon *daemon, GtkWindow *nw)
{
NotifyDaemonPrivate *priv;
GtkRequisition req;
@@ -606,7 +615,7 @@
priv = daemon->priv;
- gtk_widget_size_request (GTK_WIDGET (bw), &req);
+ gtk_widget_size_request(GTK_WIDGET(nw), &req);
workarea.x = 0;
workarea.y = 0;
@@ -618,27 +627,27 @@
g_message ("x %i y %i width %i height %i", x, y, req.width, req.height);
- egg_notification_bubble_widget_set_pos (bw, x, y);
+ theme_move_notification(nw, x, y);
link = priv->poptart_stack;
while (link)
- {
- EggNotificationBubbleWidget *b;
+ {
+ GtkWindow *nw2;
- b = EGG_NOTIFICATION_BUBBLE_WIDGET (link->data);
- gtk_widget_size_request (GTK_WIDGET (b), &req);
+ nw2 = GTK_WINDOW(link->data);
+ gtk_widget_size_request(GTK_WIDGET(nw2), &req);
- x = workarea.x + workarea.width - req.width;
- y = y - req.height;
- g_message ("x %i y %i width %i height %i", x, y, req.width, req.height);
- egg_notification_bubble_widget_set_pos (b, x, y);
+ x = workarea.x + workarea.width - req.width;
+ y = y - req.height;
+ g_message ("x %i y %i width %i height %i", x, y, req.width, req.height);
+ theme_move_notification(nw2, x, y);
- link = link->next;
- }
+ link = link->next;
+ }
- g_signal_connect(G_OBJECT(bw), "destroy",
- G_CALLBACK(_remove_bubble_from_poptart_stack), daemon);
- priv->poptart_stack = g_slist_prepend (priv->poptart_stack, bw);
+ g_signal_connect(G_OBJECT(nw), "destroy",
+ G_CALLBACK(_remove_bubble_from_poptart_stack), daemon);
+ priv->poptart_stack = g_slist_prepend (priv->poptart_stack, nw);
}
@@ -657,7 +666,7 @@
{
NotifyDaemonPrivate *priv;
NotifyTimeout *nt;
- EggNotificationBubbleWidget *bw;
+ GtkWindow *nw;
GValue *data;
gboolean use_pos_data;
gint x, y;
@@ -671,22 +680,26 @@
nt = NULL;
priv = daemon->priv;
- bw = NULL;
+ nw = NULL;
if (id > 0)
nt = (NotifyTimeout *)
g_hash_table_lookup (priv->notification_hash, &id);
if (!nt)
- {
- bw = egg_notification_bubble_widget_new ();
+ {
+ nw = theme_create_notification();
id = 0;
- }
+ }
else
- bw = nt->widget;
+ nw = nt->nw;
use_pos_data = FALSE;
- egg_notification_bubble_widget_set (bw, summary, icon, body);
+ theme_set_notification_text(nw, summary, body);
+ /*
+ * XXX This needs to handle file URIs and all that.
+ */
+ /* set_icon_from_data(nw, icon); */
/* deal with x, and y hints */
data = (GValue *) (g_hash_table_lookup (hints, "x"));
@@ -706,10 +719,8 @@
i = 0;
while (actions[i] != NULL)
{
- gchar *l;
- GtkWidget *b;
+ gchar *l = actions[i + 1];
- l = actions[i + 1];
if (l == NULL)
{
g_warning ("Label not found for action %s. "
@@ -719,8 +730,12 @@
break;
}
- b = egg_notification_bubble_widget_create_button (bw, l);
+ theme_add_notification_action(nw, l, actions[i],
+ G_CALLBACK(_action_invoked_cb));
+#if 0
+ b = egg_notification_bubble_widget_create_button (nw, l);
+
g_object_set_data_full (G_OBJECT (b),
"_notify_action",
g_strdup (actions[i]),
@@ -729,36 +744,38 @@
g_signal_connect (b,
"clicked",
(GCallback)_notification_daemon_handle_bubble_widget_action,
- bw);
+ nw);
+#endif
i = i + 2;
}
if (use_pos_data)
- {
- egg_notification_bubble_widget_set_draw_arrow (bw, TRUE);
- egg_notification_bubble_widget_set_pos (bw, x, y);
- }
+ {
+ theme_set_notification_arrow(nw, TRUE, 0, 0);
+ theme_move_notification(nw, x, y);
+ }
else
- {
- egg_notification_bubble_widget_set_draw_arrow (bw, FALSE);
- _notify_daemon_add_bubble_to_poptart_stack (daemon, bw);
- }
+ {
+ theme_set_notification_arrow(nw, FALSE, 0, 0);
+ _notify_daemon_add_bubble_to_poptart_stack (daemon, nw);
+ }
/* check for icon_data if icon == "" */
if (strcmp ("", icon) == 0)
{
data = (GValue *) (g_hash_table_lookup (hints, "icon_data"));
if (data)
- _notify_daemon_process_icon_data (daemon, bw, data);
+ _notify_daemon_process_icon_data(daemon, nw, data);
}
- g_signal_connect (bw, "clicked", (GCallback)_notification_daemon_handle_bubble_widget_default, daemon);
+ g_signal_connect(G_OBJECT(nw), "clicked",
+ G_CALLBACK(_notification_daemon_handle_bubble_widget_default), daemon);
- egg_notification_bubble_widget_show (bw);
+ theme_show_notification(nw);
if (id == 0)
- return_id = _store_notification (daemon, bw, timeout);
+ return_id = _store_notification (daemon, nw, timeout);
else
return_id = id;
@@ -769,11 +786,9 @@
dbus_g_message_get_message(context->message)));
#endif
- g_object_set_data (G_OBJECT (bw), "_notify_id", GUINT_TO_POINTER (return_id));
- g_object_set_data_full (G_OBJECT (bw),
- "_notify_sender",
- sender,
- (GDestroyNotify) g_free);
+ g_object_set_data(G_OBJECT(nw), "_notify_id", GUINT_TO_POINTER(return_id));
+ g_object_set_data_full(G_OBJECT(nw),
+ "_notify_sender", sender, (GDestroyNotify)g_free);
if (nt)
_calculate_timeout (daemon, nt, timeout);
Deleted: trunk/notification-daemon/src/eggnotificationbubblewidget.c
===================================================================
--- trunk/notification-daemon/src/eggnotificationbubblewidget.c 2006-01-11 09:13:36 UTC (rev 2420)
+++ trunk/notification-daemon/src/eggnotificationbubblewidget.c 2006-01-11 09:53:24 UTC (rev 2421)
@@ -1,1204 +0,0 @@
-/* EggNotificationBubbleWidget
- * Copyright (C) 2005 Colin Walters <walters at verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <math.h>
-
-#include <gtk/gtk.h>
-#include "eggnotificationbubblewidget.h"
-
-#define BORDER_SIZE 30
-#define BORDER_LINE_WIDTH 2
-#define CURVE_LENGTH 25
-#define TRIANGLE_START 45
-#define TRIANGLE_WIDTH 60
-#define TEXT_WIDTH_THRESHOLD 100
-
-
-static void egg_notification_bubble_widget_class_init (EggNotificationBubbleWidgetClass *klass);
-static void egg_notification_bubble_widget_init (EggNotificationBubbleWidget *bubble_widget);
-static void egg_notification_bubble_widget_finalize (GObject *object);
-static void egg_notification_bubble_widget_event_handler (GtkWidget *widget,
- GdkEvent *event,
- gpointer user_data);
-static gboolean egg_notification_bubble_widget_expose (GtkWidget *widget,
- GdkEventExpose *event);
-
-static gboolean egg_notification_bubble_widget_body_label_expose_handler (GtkWidget *widget,
- GdkEventExpose *event,
- EggNotificationBubbleWidget *bw);
-
-static void egg_notification_bubble_widget_context_changed_handler (EggNotificationBubbleWidget *bubble_widget);
-
-static void egg_notification_bubble_widget_realize (GtkWidget *widget);
-
-static void egg_notification_bubble_widget_screen_changed (GtkWidget *widget,
- GdkScreen *old_screen);
-
-static void _populate_window (EggNotificationBubbleWidget *bubble_widget);
-static void draw_bubble_widget (EggNotificationBubbleWidget *bubble_widget);
-static void _stencil_bubble (EggNotificationBubbleWidget *bw);
-
-
-static GtkWindowClass *parent_class;
-
-#define BEVEL_ALPHA_LIGHT 0.2
-#define BEVEL_ALPHA_MEDIUM 0.5
-#define BEVEL_ALPHA_DARK 0.8
-
-enum
-{
- DRAW_MOVE = 0,
- DRAW_LINE = 1,
- DRAW_CAP = 2,
- DRAW_CLOSE = 3
-};
-
-typedef struct _DrawingInstruction
-{
- gint type;
-
- gint end_x, end_y;
- gint corner_x, corner_y;
-} DrawingInstruction;
-
-enum
-{
- ORIENT_TOP = 0,
- ORIENT_BOTTOM = 1,
- ORIENT_LEFT = 2,
- ORIENT_RIGHT = 3
-};
-
-enum {
- TRIANGLE_LEFT = 0,
- TRIANGLE_RIGHT = 1
-};
-
-enum
-{
- NOTIFICATION_CLICKED,
- LAST_SIGNAL
-};
-
-static guint egg_notification_bubble_widget_signals[LAST_SIGNAL] = { 0 };
-
-GType
-egg_notification_bubble_widget_get_type (void)
-{
- static GType bubble_widget_type = 0;
-
- if (!bubble_widget_type)
- {
- static const GTypeInfo bubble_widget_info =
- {
- sizeof (EggNotificationBubbleWidgetClass),
- NULL, /* base_init */
- NULL, /* base_finalize */
- (GClassInitFunc) egg_notification_bubble_widget_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (EggNotificationBubbleWidget),
- 0, /* n_preallocs */
- (GInstanceInitFunc) egg_notification_bubble_widget_init,
- };
-
- bubble_widget_type = g_type_register_static (GTK_TYPE_WINDOW, "EggNotificationBubbleWidget",
- &bubble_widget_info, 0);
- }
-
- return bubble_widget_type;
-}
-
-static void
-egg_notification_bubble_widget_class_init (EggNotificationBubbleWidgetClass *class)
-{
- GObjectClass *object_class;
- GtkWidgetClass *widget_class;
-
- object_class = (GObjectClass *) class;
- widget_class = (GtkWidgetClass *) class;
-
- parent_class = g_type_class_peek_parent (class);
-
- object_class->finalize = egg_notification_bubble_widget_finalize;
- widget_class->expose_event = egg_notification_bubble_widget_expose;
- widget_class->realize = egg_notification_bubble_widget_realize;
- widget_class->screen_changed = egg_notification_bubble_widget_screen_changed;
-
- egg_notification_bubble_widget_signals[NOTIFICATION_CLICKED] =
- g_signal_new ("clicked",
- EGG_TYPE_NOTIFICATION_BUBBLE_WIDGET,
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (EggNotificationBubbleWidgetClass, clicked),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE,
- 0);
-}
-
-static void
-egg_notification_bubble_widget_init (EggNotificationBubbleWidget *bubble_widget)
-{
- GtkWindow *win;
-
- win = GTK_WINDOW (bubble_widget);
-
- bubble_widget->can_composite = FALSE;
-
- egg_notification_bubble_widget_screen_changed (GTK_WIDGET (bubble_widget),
- NULL);
-
- bubble_widget->dp.is_clear = TRUE;
- bubble_widget->dp.pipeline = NULL;
- bubble_widget->draw_arrow = FALSE;
-
- _populate_window (bubble_widget);
-
-}
-
-static void
-egg_notification_bubble_widget_finalize (GObject *object)
-{
- EggNotificationBubbleWidget *bubble_widget = EGG_NOTIFICATION_BUBBLE_WIDGET (object);
-
- g_return_if_fail (bubble_widget != NULL);
-
- if (G_OBJECT_CLASS (parent_class)->finalize)
- G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-static void
-_layout_window (EggNotificationBubbleWidget *bubble_widget,
- int alignment)
-{
- if (bubble_widget->draw_arrow)
- gtk_container_set_border_width (GTK_CONTAINER (bubble_widget), BORDER_SIZE + 5);
- else
- gtk_container_set_border_width (GTK_CONTAINER (bubble_widget), 10);
-
- if (gtk_widget_get_parent (bubble_widget->icon))
- gtk_container_remove (GTK_CONTAINER (bubble_widget->table),
- bubble_widget->icon);
-
- if (gtk_widget_get_parent (bubble_widget->bubble_widget_header_label))
- gtk_container_remove (GTK_CONTAINER (bubble_widget->table),
- bubble_widget->bubble_widget_header_label);
-
- if (gtk_widget_get_parent (bubble_widget->bubble_widget_body_label))
- gtk_container_remove (GTK_CONTAINER (bubble_widget->table),
- bubble_widget->bubble_widget_body_label);
-
- if (bubble_widget->button_hbox != NULL &&
- gtk_widget_get_parent (bubble_widget->button_hbox) != NULL)
- gtk_container_remove (GTK_CONTAINER (bubble_widget->table),
- bubble_widget->button_hbox);
-
- if (alignment == TRIANGLE_LEFT)
- {
- gtk_table_attach (GTK_TABLE (bubble_widget->table),
- bubble_widget->icon,
- 0, 1, 1, 2,
- GTK_FILL, GTK_FILL,
- 0, 0);
-
- gtk_table_attach (GTK_TABLE (bubble_widget->table),
- bubble_widget->bubble_widget_header_label,
- 1, 2, 0, 1,
- GTK_FILL, GTK_FILL,
- 0, 0);
-
- gtk_table_attach (GTK_TABLE (bubble_widget->table),
- bubble_widget->bubble_widget_body_label,
- 1, 2, 1, 2,
- GTK_FILL, GTK_FILL,
- 0, 0);
- }
- else
- {
- gtk_table_attach (GTK_TABLE (bubble_widget->table),
- bubble_widget->icon,
- 1, 2, 1, 2,
- GTK_FILL, GTK_FILL,
- 0, 0);
-
- gtk_table_attach (GTK_TABLE (bubble_widget->table),
- bubble_widget->bubble_widget_header_label,
- 0, 1, 0, 1,
- GTK_FILL, GTK_FILL,
- 0, 0);
-
- gtk_table_attach (GTK_TABLE (bubble_widget->table),
- bubble_widget->bubble_widget_body_label,
- 0, 1, 1, 2,
- GTK_FILL, GTK_FILL,
- 0, 0);
-
- }
-
- if (bubble_widget->button_hbox != NULL)
- {
- gtk_table_attach (GTK_TABLE (bubble_widget->table),
- bubble_widget->button_hbox,
- 0, 2, 2, 3,
- GTK_FILL, GTK_FILL,
- 0, 0);
-
- }
-
- gtk_widget_show_all (bubble_widget->table);
-}
-
-static void
-_drawing_instruction_internal_add (GList **pipeline,
- guint type,
- gint end_x, gint end_y,
- gint corner_x, gint corner_y)
-{
- DrawingInstruction *di;
-
- di = g_new0 (DrawingInstruction, 1);
- di->type = type;
- di->end_x = end_x;
- di->end_y = end_y;
- di->corner_x = corner_x;
- di->corner_y = corner_y;
-
- *pipeline = g_list_append (*pipeline, di);
-}
-
-static void
-_drawing_instruction_move (GList **pipeline,
- gint x, gint y)
-{
- _drawing_instruction_internal_add (pipeline, DRAW_MOVE, x, y, 0, 0);
-}
-
-static void
-_drawing_instruction_line (GList **pipeline,
- gint x, gint y)
-{
- _drawing_instruction_internal_add (pipeline, DRAW_LINE, x, y, 0, 0);
-}
-
-static void
-_drawing_instruction_cap (GList **pipeline,
- gint x, gint y,
- gint corner_x, gint corner_y)
-{
- _drawing_instruction_internal_add (pipeline, DRAW_CAP, x, y, corner_x, corner_y);
-}
-
-static void
-_drawing_instruction_close (GList **pipeline)
-{
- _drawing_instruction_internal_add (pipeline, DRAW_CLOSE, 0, 0, 0, 0);
-}
-
-/* given a distance from start point x1, y1
- calculate the point dist units away */
-static GdkPoint
-_calc_point_on_line (x1, y1, x2, y2, dist)
-{
- GdkPoint result;
- gint dx, dy;
- gdouble d, vx, vy;
-
- dx = x2 - x1;
- dy = y2 - y1;
-
- d = sqrt (dx * dx + dy * dy);
- vx = dx / d;
- vy = dy / d;
-
- result.x = x1 + dist * vx;
- result.y = y1 + dist * vy;
-
- return result;
-}
-
-static void
-_edge_line_to (EggNotificationBubbleWidget *bw,
- gint x, gint y,
- gint corner_radius)
-{
- if (bw->dp.is_clear == TRUE)
- {
- bw->dp.start_x = x;
- bw->dp.start_y = y;
- bw->dp.start_corner_radius = corner_radius;
- bw->dp.is_clear = FALSE;
- }
- else
- {
- GdkPoint start_p;
- GdkPoint end_p;
-
- start_p = _calc_point_on_line (bw->dp.last_x,
- bw->dp.last_y,
- x,
- y,
- bw->dp.last_corner_radius);
- end_p = _calc_point_on_line (x,
- y,
- bw->dp.last_x,
- bw->dp.last_y,
- corner_radius);
-
- if (bw->dp.last_x == bw->dp.start_x &&
- bw->dp.last_y == bw->dp.start_y)
- _drawing_instruction_move (&bw->dp.pipeline, start_p.x, start_p.y);
- else
- _drawing_instruction_cap (&bw->dp.pipeline,
- start_p.x,
- start_p.y,
- bw->dp.last_x,
- bw->dp.last_y);
-
- _drawing_instruction_line (&bw->dp.pipeline, end_p.x, end_p.y);
-
- }
-
- bw->dp.last_x = x;
- bw->dp.last_y = y;
- bw->dp.last_corner_radius = corner_radius;
-}
-
-static void
-_close_path (EggNotificationBubbleWidget *bw)
-{
- GdkPoint start_p;
- GdkPoint end_p;
- DrawingInstruction *di;
-
- start_p = _calc_point_on_line (bw->dp.last_x,
- bw->dp.last_y,
- bw->dp.start_x,
- bw->dp.start_y,
- bw->dp.last_corner_radius);
-
- end_p = _calc_point_on_line (bw->dp.start_x,
- bw->dp.start_y,
- bw->dp.last_x,
- bw->dp.last_y,
- bw->dp.start_corner_radius);
-
-
- _drawing_instruction_cap (&bw->dp.pipeline,
- start_p.x,
- start_p.y,
- bw->dp.last_x,
- bw->dp.last_y);
-
- _drawing_instruction_line (&bw->dp.pipeline,
- end_p.x,
- end_p.y);
-
- di = (DrawingInstruction *) bw->dp.pipeline->data;
- _drawing_instruction_cap (&bw->dp.pipeline,
- di->end_x,
- di->end_y,
- bw->dp.start_x,
- bw->dp.start_y);
-
- _drawing_instruction_close (&bw->dp.pipeline);
-
-}
-
-static void
-_drawing_instruction_draw (DrawingInstruction *di, cairo_t *cr)
-{
- switch (di->type)
- {
- case DRAW_MOVE:
- cairo_move_to (cr, di->end_x, di->end_y);
- break;
- case DRAW_LINE:
- cairo_line_to (cr, di->end_x, di->end_y);
- break;
- case DRAW_CAP:
- cairo_curve_to (cr,
- di->corner_x, di->corner_y,
- di->corner_x, di->corner_y,
- di->end_x, di->end_y);
- break;
- case DRAW_CLOSE:
- cairo_close_path (cr);
- break;
- }
-}
-
-static void
-_drawing_pipeline_clear (EggNotificationBubbleWidget *bw)
-{
- bw->dp.is_clear = TRUE;
-
- g_list_foreach (bw->dp.pipeline, (GFunc) g_free, NULL);
- g_list_free (bw->dp.pipeline);
- bw->dp.pipeline = NULL;
-}
-
-static void
-_populate_window (EggNotificationBubbleWidget *bubble_widget)
-{
- GtkWidget *widget;
-
- g_return_if_fail (EGG_IS_NOTIFICATION_BUBBLE_WIDGET (bubble_widget));
-
- widget = GTK_WIDGET (bubble_widget);
-
- gtk_widget_add_events (widget, GDK_BUTTON_PRESS_MASK);
- gtk_widget_set_app_paintable (widget, TRUE);
- gtk_window_set_resizable (GTK_WINDOW (bubble_widget), FALSE);
-
- bubble_widget->bubble_widget_header_label = gtk_label_new (NULL);
-
- //use placeholder so we can use pango/cairo to draw body
- bubble_widget->bubble_widget_body_label = gtk_frame_new("");
- gtk_frame_set_shadow_type (GTK_FRAME (bubble_widget->bubble_widget_body_label), GTK_SHADOW_NONE);
- bubble_widget->icon = gtk_image_new_from_stock (GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_BUTTON);
- gtk_widget_ref (bubble_widget->bubble_widget_header_label);
- gtk_widget_ref (bubble_widget->bubble_widget_body_label);
- gtk_widget_ref (bubble_widget->icon);
-
- gtk_label_set_line_wrap (GTK_LABEL (bubble_widget->bubble_widget_header_label), TRUE);
- gtk_misc_set_alignment (GTK_MISC (bubble_widget->bubble_widget_header_label), 0.0, 0.0);
-
- gtk_misc_set_alignment (GTK_MISC (bubble_widget->icon), 0.0, 0.0);
- gtk_widget_show (bubble_widget->icon);
- gtk_widget_show (bubble_widget->bubble_widget_header_label);
- gtk_widget_show (bubble_widget->bubble_widget_body_label);
-
- bubble_widget->table = gtk_table_new (3, 2, FALSE);
- gtk_table_set_col_spacings (GTK_TABLE (bubble_widget->table), 5);
- gtk_table_set_row_spacings (GTK_TABLE (bubble_widget->table), 5);
-
- gtk_container_add (GTK_CONTAINER (bubble_widget), bubble_widget->table);
-
- bubble_widget->body_layout = pango_layout_new (
- gtk_widget_get_pango_context (
- GTK_WIDGET (bubble_widget)));
-
- /* do a fake layout for now so we can calculate
- height and width */
- _layout_window (bubble_widget, TRIANGLE_RIGHT);
-
- g_signal_connect (bubble_widget, "style-set",
- G_CALLBACK (egg_notification_bubble_widget_context_changed_handler),
- NULL);
-
- g_signal_connect (bubble_widget, "direction-changed",
- G_CALLBACK (egg_notification_bubble_widget_context_changed_handler),
- NULL);
-
- g_signal_connect_after (bubble_widget, "event-after",
- G_CALLBACK (egg_notification_bubble_widget_event_handler),
- bubble_widget);
-
- g_signal_connect (bubble_widget->bubble_widget_body_label, "expose-event",
- G_CALLBACK (egg_notification_bubble_widget_body_label_expose_handler),
- bubble_widget);
-}
-
-static void
-_destroy_pixmap_data_func (guchar *pixels,
- gpointer data)
-{
- g_free (pixels);
-}
-
-void
-egg_notification_bubble_widget_set_icon_from_data (EggNotificationBubbleWidget *bubble_widget,
- const guchar *data,
- gboolean has_alpha,
- int bits_per_sample,
- int width,
- int height,
- int rowstride)
-{
- GdkPixbuf *pixbuf;
-
- pixbuf = gdk_pixbuf_new_from_data (data,
- GDK_COLORSPACE_RGB,
- has_alpha,
- bits_per_sample,
- width,
- height,
- rowstride,
- _destroy_pixmap_data_func,
- NULL);
-
- gtk_image_set_from_pixbuf (GTK_IMAGE (bubble_widget->icon), pixbuf);
- gdk_pixbuf_unref (pixbuf);
-}
-
-static void
-_calculate_pango_layout_from_aspect (PangoLayout *layout,
- const char *text,
- double factor)
-{
- gint len;
- gint w, h;
- double x;
-
- len = strlen (text);
- pango_layout_set_width(layout, -1);
- pango_layout_set_markup(layout, text, len);
-
- pango_layout_get_pixel_size (layout, &w, &h);
-
- if (w > TEXT_WIDTH_THRESHOLD)
- {
- double f;
-
- pango_layout_get_size (layout, &w, &h);
-
- x = sqrt (factor * w / h);
- if (x == 0)
- x = 1;
-
- f = (double) w / x;
-
- w = (gint) (f + 0.5);
-
- pango_layout_set_width(layout, w);
- }
-}
-
-void
-egg_notification_bubble_widget_set (EggNotificationBubbleWidget *bubble_widget,
- const gchar *bubble_widget_header_text,
- const gchar *icon,
- const gchar *bubble_widget_body_text)
-{
- gchar *markupquoted;
- gchar *markuptext;
- gint w, h;
-
- g_return_if_fail (EGG_IS_NOTIFICATION_BUBBLE_WIDGET (bubble_widget));
-
- g_free (bubble_widget->bubble_widget_header_text);
- g_free (bubble_widget->bubble_widget_body_text);
- bubble_widget->bubble_widget_header_text = g_strdup (bubble_widget_header_text);
- bubble_widget->bubble_widget_body_text = g_strdup (bubble_widget_body_text);
-
- if (icon != NULL || strcmp (icon, "") != 0)
- {
- if (g_str_has_prefix (icon, "file://"))
- {
- gchar *icon_path = (gchar *) icon + (7 * sizeof (gchar));
- gtk_image_set_from_file (GTK_IMAGE (bubble_widget->icon), icon_path);
- }
- else
- {
- gtk_image_set_from_icon_name (GTK_IMAGE (bubble_widget->icon), icon, GTK_ICON_SIZE_DIALOG);
- }
- }
-
- markupquoted = g_markup_escape_text (bubble_widget->bubble_widget_header_text, -1);
- markuptext = g_strdup_printf ("<span size=\"larger\" weight=\"ultrabold\">%s</span>", markupquoted);
- gtk_label_set_markup (GTK_LABEL (bubble_widget->bubble_widget_header_label), markuptext);
- g_free (markuptext);
- g_free (markupquoted);
-
- _calculate_pango_layout_from_aspect (bubble_widget->body_layout,
- bubble_widget->bubble_widget_body_text,
- 0.25);
-
- pango_layout_get_pixel_size (bubble_widget->body_layout, &w, &h);
- gtk_widget_set_size_request (bubble_widget->bubble_widget_body_label, w, h);
-
-}
-
-
-void
-egg_notification_bubble_widget_set_pos (EggNotificationBubbleWidget *bubble_widget,
- gint x, gint y)
-{
- GdkScreen *screen;
- gint monitor_num;
- GdkRectangle monitor;
-
- bubble_widget->x = x;
- bubble_widget->y = y;
-
- screen = gtk_window_get_screen (GTK_WINDOW(bubble_widget));
- monitor_num = gdk_screen_get_monitor_at_point (screen, x, y);
- gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
-
- /* TODO: This is wrong - if elements are added before
- set_pos is called the layout become wrong */
- if (x < (monitor.x + monitor.width) / 2)
- _layout_window (bubble_widget, TRIANGLE_LEFT);
- else
- _layout_window (bubble_widget, TRIANGLE_RIGHT);
-
- _stencil_bubble (bubble_widget);
-
- gtk_window_move (GTK_WINDOW (bubble_widget),
- x - bubble_widget->offset_x,
- y - bubble_widget->offset_y);
-}
-
-static void
-egg_notification_bubble_widget_realize (GtkWidget *widget)
-{
- GtkWidgetClass *widget_parent_class;
-
- widget_parent_class = (GtkWidgetClass *)parent_class;
- if (widget_parent_class->realize)
- widget_parent_class->realize (widget);
-
- draw_bubble_widget (EGG_NOTIFICATION_BUBBLE_WIDGET(widget));
-}
-
-static void
-egg_notification_bubble_widget_screen_changed (GtkWidget *widget,
- GdkScreen *old_screen)
-{
- GtkWidgetClass *widget_parent_class;
- EggNotificationBubbleWidget *bw;
- GdkScreen *screen;
- GdkColormap *colormap;
- gboolean can_composite;
-
- bw = EGG_NOTIFICATION_BUBBLE_WIDGET (widget);
-
- widget_parent_class = (GtkWidgetClass *)parent_class;
- if (widget_parent_class->screen_changed)
- widget_parent_class->screen_changed (widget, old_screen);
-
- can_composite = TRUE;
-
- screen = gtk_widget_get_screen (widget);
- colormap = gdk_screen_get_rgba_colormap (screen);
-
- if (!colormap)
- {
- colormap = gdk_screen_get_rgb_colormap (screen);
- can_composite = FALSE;
- }
-
- gtk_widget_set_colormap (widget, colormap);
-
- bw->can_composite = can_composite;
-}
-
-
-static gboolean
-egg_notification_bubble_widget_body_label_expose_handler (GtkWidget *widget,
- GdkEventExpose *event,
- EggNotificationBubbleWidget *bw)
-{
- cairo_t *cr;
- cr = gdk_cairo_create (GTK_WIDGET(bw)->window);
-
- cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
-
- cairo_set_source_rgba (cr, bw->body_text_color.red,
- bw->body_text_color.green,
- bw->body_text_color.blue,
- 0.60);
- cairo_move_to (cr, event->area.x, event->area.y);
-
- pango_cairo_layout_path (cr, bw->body_layout);
-
- cairo_fill (cr);
- cairo_destroy (cr);
-
- return TRUE;
-}
-
-static gboolean
-egg_notification_bubble_widget_expose (GtkWidget *widget, GdkEventExpose *event)
-{
- GtkWidgetClass *widget_parent_class;
- EggNotificationBubbleWidget *bw;
-
- bw = EGG_NOTIFICATION_BUBBLE_WIDGET (widget);
-
- draw_bubble_widget (bw);
-
- widget_parent_class = (GtkWidgetClass *)parent_class;
- if (widget_parent_class->expose_event)
- widget_parent_class->expose_event (widget, event);
-
- return TRUE;
-}
-
-static void
-_stencil_bubble_top_right (EggNotificationBubbleWidget *bw,
- GdkRectangle *rect)
-{
- GdkPoint triangle[3];
-
- triangle[0].y = rect->y;
- triangle[0].x = rect->x + rect->width - (TRIANGLE_START + TRIANGLE_WIDTH);
- triangle[2].x = triangle[0].x + TRIANGLE_WIDTH;
- triangle[2].y = rect->y;
- triangle[1].x = (triangle[2].x - triangle[0].x) / 2 + triangle[0].x;
- triangle[1].y = rect->y - BORDER_SIZE + 5;
-
-
- bw->offset_x = triangle[1].x;
- bw->offset_y = triangle[1].y;
-
- _edge_line_to (bw, triangle[0].x, triangle[0].y, 0);
- _edge_line_to (bw, triangle[1].x, triangle[1].y, 0);
- _edge_line_to (bw, triangle[2].x, triangle[2].y, 0);
-
- _edge_line_to (bw, rect->x + rect->width, rect->y, CURVE_LENGTH);
- _edge_line_to (bw, rect->x + rect->width, rect->y + rect->height, CURVE_LENGTH);
-
- _edge_line_to (bw, rect->x, rect->y + rect->height + BORDER_SIZE - 5, CURVE_LENGTH);
-
- _edge_line_to (bw, rect->x, rect->y, CURVE_LENGTH);
-
- _close_path (bw);
-}
-
-static void
-_stencil_bubble_top_left (EggNotificationBubbleWidget *bw,
- GdkRectangle *rect)
-{
- GdkPoint triangle[3];
-
- triangle[0].x = rect->x + TRIANGLE_START;
- triangle[0].y = rect->y;
- triangle[2].x = triangle[0].x + TRIANGLE_WIDTH;
- triangle[2].y = rect->y;
- triangle[1].x = (triangle[2].x - triangle[0].x) / 2 + triangle[0].x;
- triangle[1].y = rect->y - BORDER_SIZE + 5;
-
- bw->offset_x = triangle[1].x;
- bw->offset_y = triangle[1].y;
-
- _edge_line_to (bw, triangle[0].x, triangle[0].y, 0);
- _edge_line_to (bw, triangle[1].x, triangle[1].y, 0);
- _edge_line_to (bw, triangle[2].x, triangle[2].y, 0);
-
- _edge_line_to (bw, rect->x + rect->width, rect->y, CURVE_LENGTH);
- _edge_line_to (bw, rect->x + rect->width, rect->y + rect->height + BORDER_SIZE - 5, CURVE_LENGTH);
-
- _edge_line_to (bw, rect->x, rect->y + rect->height, CURVE_LENGTH);
-
- _edge_line_to (bw, rect->x, rect->y, CURVE_LENGTH);
-
- _close_path (bw);
-
-}
-
-static void
-_stencil_bubble_bottom_right (EggNotificationBubbleWidget *bw,
- GdkRectangle *rect)
-{
- GdkPoint triangle[3];
-
- triangle[2].x = rect->x + rect->width - TRIANGLE_START;
- triangle[2].y = rect->y + rect->height;
-
- triangle[0].x = triangle[2].x - TRIANGLE_WIDTH;
- triangle[0].y = rect->y + rect->height;
- triangle[1].x = (triangle[2].x - triangle[0].x) / 2 + triangle[0].x;
- triangle[1].y = rect->y + rect->height + BORDER_SIZE - 5;
-
- bw->offset_x = triangle[1].x;
- bw->offset_y = triangle[1].y;
-
- _edge_line_to (bw, triangle[2].x, triangle[2].y, 0);
- _edge_line_to (bw, triangle[1].x, triangle[1].y, 0);
- _edge_line_to (bw, triangle[0].x, triangle[0].y, 0);
-
-
- _edge_line_to (bw, rect->x, rect->y + rect->height, CURVE_LENGTH);
- _edge_line_to (bw, rect->x, rect->y - BORDER_SIZE + 5, CURVE_LENGTH);
- _edge_line_to (bw, rect->x + rect->width, rect->y, CURVE_LENGTH);
- _edge_line_to (bw, rect->x + rect->width, rect->y + rect->height, CURVE_LENGTH);
-
- _close_path (bw);
-}
-
-static void
-_stencil_bubble_bottom_left (EggNotificationBubbleWidget *bw,
- GdkRectangle *rect)
-{
- GdkPoint triangle[3];
-
- triangle[0].x = rect->x + TRIANGLE_START;
- triangle[0].y = rect->y + rect->height;
- triangle[2].x = triangle[0].x + TRIANGLE_WIDTH;
- triangle[2].y = rect->y + rect->height;
- triangle[1].x = (triangle[2].x - triangle[0].x) / 2 + triangle[0].x;
- triangle[1].y = rect->y + rect->height + BORDER_SIZE - 5;
-
- bw->offset_x = triangle[1].x;
- bw->offset_y = triangle[1].y;
-
- _edge_line_to (bw, triangle[2].x, triangle[2].y, 0);
- _edge_line_to (bw, triangle[1].x, triangle[1].y, 0);
- _edge_line_to (bw, triangle[0].x, triangle[0].y, 0);
-
- _edge_line_to (bw, rect->x, rect->y + rect->height, CURVE_LENGTH);
- _edge_line_to (bw, rect->x, rect->y, CURVE_LENGTH);
- _edge_line_to (bw, rect->x + rect->width, rect->y - BORDER_SIZE + 5, CURVE_LENGTH);
- _edge_line_to (bw, rect->x + rect->width, rect->y + rect->height, CURVE_LENGTH);
-
- _close_path (bw);
-}
-
-static void
-_stencil_bubble_no_arrow (EggNotificationBubbleWidget *bw,
- GdkRectangle *rect)
-{
- bw->offset_x = 0;
- bw->offset_y = 0;
-
- _edge_line_to (bw, rect->x + rect->width, rect->y, CURVE_LENGTH);
- _edge_line_to (bw, rect->x + rect->width, rect->y + rect->height, CURVE_LENGTH);
-
- _edge_line_to (bw, rect->x, rect->y + rect->height, CURVE_LENGTH);
-
- _edge_line_to (bw, rect->x, rect->y, CURVE_LENGTH);
-
- _close_path (bw);
-
-}
-
-static void
-_stencil_bubble (EggNotificationBubbleWidget *bw)
-{
- GdkRectangle rect;
- GtkRequisition req;
- gint rect_border;
-
- gtk_widget_size_request (GTK_WIDGET (bw), &req);
-
- if (bw->draw_arrow)
- rect_border = BORDER_SIZE - BORDER_LINE_WIDTH;
- else
- rect_border = BORDER_LINE_WIDTH;
-
- rect.x = rect_border;
- rect.y = rect_border;
- rect.width = req.width - (rect_border * 2);
- rect.height = req.height - (rect_border * 2);
-
- _drawing_pipeline_clear (bw);
-
- if (bw->draw_arrow)
- {
- GdkScreen *screen;
- GdkRectangle monitor;
- gint monitor_num;
- gint orient;
- gint orient_triangle;
- gint x, y;
-
- x = bw->x;
- y = bw->y;
- screen = gtk_window_get_screen (GTK_WINDOW(bw));
- monitor_num = gdk_screen_get_monitor_at_point (screen, x, y);
- gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
-
- /* TODO: draw corner cases */
- if (x < (monitor.x + monitor.width) / 2)
- orient_triangle = TRIANGLE_LEFT;
- else
- orient_triangle = TRIANGLE_RIGHT;
-
- orient = ORIENT_TOP;
-
- if ((y + req.height) > monitor.y + monitor.height)
- orient = ORIENT_BOTTOM;
-
- if (orient == ORIENT_TOP)
- {
- if (orient_triangle == TRIANGLE_RIGHT)
- _stencil_bubble_top_right (bw, &rect);
- else if (orient_triangle == TRIANGLE_LEFT)
- _stencil_bubble_top_left (bw, &rect);
- }
- else if (orient == ORIENT_BOTTOM)
- {
- if (orient_triangle == TRIANGLE_RIGHT)
- _stencil_bubble_bottom_right (bw, &rect);
- else if (orient_triangle == TRIANGLE_LEFT)
- _stencil_bubble_bottom_left (bw, &rect);
- }
- }
- else
- _stencil_bubble_no_arrow (bw, &rect);
-}
-
-static GdkColor
-_blend_colors (GdkColor color1, GdkColor color2, gdouble factor)
-{
- GdkColor result;
- gint channel_intensity;
-
- channel_intensity = color1.red * factor + color2.red * (1 - factor);
- result.red = CLAMP (channel_intensity, 0, 65535);
-
- channel_intensity = color1.green * factor + color2.green * (1 - factor);
- result.green = CLAMP (channel_intensity, 0, 65535);
-
- channel_intensity = color1.blue * factor + color2.blue * (1 - factor);
- result.blue = CLAMP (channel_intensity, 0, 65535);
-
- result.pixel = 0xFF; /* This is never really used. */
-
- return result;
-}
-
-static void
-_calculate_colors_from_style (EggNotificationBubbleWidget *bw)
-{
- GtkStyle *style;
- GtkWidget *widget;
- GdkColor header_text_color;
- GdkColor body_text_color;
- GdkColor bg_end_gradient;
- GdkColor border_color;
- GdkColor bg_start_gradient;
-
- widget = GTK_WIDGET (bw);
-
- gtk_widget_ensure_style (widget);
- style = widget->style;
-
- header_text_color = style->text[GTK_STATE_NORMAL];
- body_text_color = style->text[GTK_STATE_NORMAL];
- bg_start_gradient = style->base[GTK_STATE_NORMAL];
- bg_end_gradient = style->bg[GTK_STATE_SELECTED];
- border_color = style->mid[GTK_STATE_NORMAL];
-
- bg_end_gradient = _blend_colors (bg_start_gradient, bg_end_gradient, 0.25);
-
- bw->header_text_color = header_text_color;
- bw->body_text_color = body_text_color;
- bw->bg_start_gradient = bg_start_gradient;
- bw->bg_end_gradient = bg_end_gradient;
- bw->border_color = border_color;
-}
-
-
-static void
-draw_bubble_widget (EggNotificationBubbleWidget *bubble_widget)
-{
- GtkRequisition requisition;
- gint w, h;
- cairo_pattern_t *pat;
- GdkPixmap *mask;
- GdkPoint arrow_pos;
-
- GtkWidget *widget;
- cairo_t *cairo_context;
- cairo_t *mask_cr;
- gboolean can_composite;
-
- mask_cr = NULL;
- mask = NULL;
-
- arrow_pos.x = 0;
- arrow_pos.y = 0;
-
- widget = GTK_WIDGET(bubble_widget);
- cairo_context = gdk_cairo_create (widget->window);
-
- can_composite = bubble_widget->can_composite;
-
- _calculate_colors_from_style (bubble_widget);
-
- gtk_widget_size_request (widget, &requisition);
- w = requisition.width;
- h = requisition.height;
-
- if (!can_composite)
- {
- mask = gdk_pixmap_new (NULL, w, h, 1);
- mask_cr = gdk_cairo_create ((GdkDrawable *) mask);
- }
-
- g_list_foreach (bubble_widget->dp.pipeline, (GFunc) _drawing_instruction_draw, cairo_context);
- if (!can_composite)
- g_list_foreach (bubble_widget->dp.pipeline, (GFunc) _drawing_instruction_draw, mask_cr);
-
- if (can_composite)
- cairo_set_source_rgba (cairo_context, 1, 1, 1, 0);
- else
- cairo_set_source_rgba (cairo_context,
- bubble_widget->border_color.red / 65535.0,
- bubble_widget->border_color.green / 65535.0,
- bubble_widget->border_color.blue / 65535.0, 1);
-
- cairo_set_operator (cairo_context, CAIRO_OPERATOR_SOURCE);
- cairo_paint (cairo_context);
-
- cairo_set_operator (cairo_context, CAIRO_OPERATOR_OVER);
-
- pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, h);
- //cairo_pattern_add_color_stop_rgba (pat, 1, 0.59, 0.76, 0.93, 1);
- cairo_pattern_add_color_stop_rgba (pat, 0,
- bubble_widget->bg_start_gradient.red /
- 65535.0,
- bubble_widget->bg_start_gradient.green /
- 65535.0,
- bubble_widget->bg_start_gradient.blue /
- 65535.0, 1);
-
- cairo_pattern_add_color_stop_rgba (pat, 1,
- bubble_widget->bg_end_gradient.red /
- 65535.0,
- bubble_widget->bg_end_gradient.green /
- 65535.0,
- bubble_widget->bg_end_gradient.blue /
- 65535.0, 1);
-
- cairo_set_source (cairo_context, pat);
- cairo_fill_preserve (cairo_context);
- cairo_pattern_destroy (pat);
-
- cairo_set_line_width (cairo_context, 3.5);
- cairo_set_source_rgba (cairo_context, 0.43, 0.49, 0.55, 1);
- cairo_stroke (cairo_context);
-
- if (!can_composite)
- {
- cairo_set_operator (mask_cr, CAIRO_OPERATOR_CLEAR);
- cairo_paint (mask_cr);
-
- cairo_set_operator (mask_cr, CAIRO_OPERATOR_OVER);
- cairo_set_line_width (mask_cr, 3.5);
- cairo_set_source_rgba (mask_cr, 1, 1, 1, 1);
- cairo_fill_preserve (mask_cr);
- cairo_stroke (mask_cr);
-
- gdk_window_shape_combine_mask (widget->window,
- (GdkBitmap *) mask,
- 0,
- 0);
- gdk_pixmap_unref (mask);
- cairo_destroy (mask_cr);
- }
-
- cairo_destroy (cairo_context);
-
- bubble_widget->active = TRUE;
-}
-
-void
-egg_notification_bubble_widget_show (EggNotificationBubbleWidget *bubble_widget)
-{
- gtk_widget_show_all (GTK_WIDGET (bubble_widget));
-}
-
-void
-egg_notification_bubble_widget_hide (EggNotificationBubbleWidget *bubble_widget)
-{
- if (bubble_widget)
- gtk_widget_hide (GTK_WIDGET (bubble_widget));
-}
-
-EggNotificationBubbleWidget*
-egg_notification_bubble_widget_new (void)
-{
- return g_object_new (EGG_TYPE_NOTIFICATION_BUBBLE_WIDGET,
- "type", GTK_WINDOW_POPUP,
- NULL);
-}
-
-static void
-egg_notification_bubble_widget_context_changed_handler (EggNotificationBubbleWidget *bubble_widget)
-{
- pango_layout_context_changed (bubble_widget->body_layout);
-}
-
-
-static void
-egg_notification_bubble_widget_event_handler (GtkWidget *widget,
- GdkEvent *event,
- gpointer user_data)
-{
- EggNotificationBubbleWidget *bubble_widget;
-
- bubble_widget = EGG_NOTIFICATION_BUBBLE_WIDGET (user_data);
-
- switch (event->type)
- {
- case GDK_BUTTON_PRESS:
- g_signal_emit (bubble_widget, egg_notification_bubble_widget_signals[NOTIFICATION_CLICKED], 0);
- break;
- default:
- break;
- }
-}
-
-GtkWidget *
-egg_notification_bubble_widget_create_button (EggNotificationBubbleWidget *bubble_widget,
- const gchar *label)
-{
- GtkWidget *b;
- GtkWidget *l;
- gchar *label_markup;
-
- b = gtk_button_new ();
- gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
- gtk_container_set_border_width (GTK_CONTAINER (b), 0);
-
- label_markup = g_markup_printf_escaped ("<span weight=\"bold\" underline=\"single\" foreground=\"blue\">%s</span>", label);
-
- l = gtk_label_new (label_markup);
- gtk_label_set_use_markup (GTK_LABEL (l), TRUE);
-
- g_free (label_markup);
-
- gtk_container_add (GTK_CONTAINER (b), l);
-
- gtk_widget_show_all (b);
-
- if (bubble_widget->button_hbox == NULL)
- bubble_widget->button_hbox = gtk_hbox_new (FALSE, 0);
-
- gtk_box_pack_end (GTK_BOX (bubble_widget->button_hbox),
- b,
- FALSE, FALSE,
- 0);
-
- return (b);
-}
-
-void
-egg_notification_bubble_widget_clear_buttons (EggNotificationBubbleWidget *bubble_widget)
-{
- if (bubble_widget->button_hbox != NULL)
- gtk_widget_destroy (bubble_widget->button_hbox);
-
- bubble_widget->button_hbox = NULL;
-}
-
-void
-egg_notification_bubble_widget_set_draw_arrow (EggNotificationBubbleWidget *bubble_widget,
- gboolean value)
-{
- bubble_widget->draw_arrow = value;
-}
-
-
Deleted: trunk/notification-daemon/src/eggnotificationbubblewidget.h
===================================================================
--- trunk/notification-daemon/src/eggnotificationbubblewidget.h 2006-01-11 09:13:36 UTC (rev 2420)
+++ trunk/notification-daemon/src/eggnotificationbubblewidget.h 2006-01-11 09:53:24 UTC (rev 2421)
@@ -1,129 +0,0 @@
-/* EggNotificationBubbleWidget
- * Copyright (C) 2005 Colin Walters <walters at verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-#ifndef __EGG_NOTIFICATION_BUBBLE_WIDGET_H__
-#define __EGG_NOTIFICATION_BUBBLE_WIDGET_H__
-
-#include <gtk/gtk.h>
-
-G_BEGIN_DECLS
-
-#define EGG_TYPE_NOTIFICATION_BUBBLE_WIDGET (egg_notification_bubble_widget_get_type ())
-#define EGG_NOTIFICATION_BUBBLE_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_NOTIFICATION_BUBBLE_WIDGET, EggNotificationBubbleWidget))
-#define EGG_NOTIFICATION_BUBBLE_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_NOTIFICATION_BUBBLE_WIDGET, EggNotificationBubbleWidgetClass))
-#define EGG_IS_NOTIFICATION_BUBBLE_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_NOTIFICATION_BUBBLE_WIDGET))
-#define EGG_IS_NOTIFICATION_BUBBLE_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_NOTIFICATION_BUBBLE_WIDGET))
-#define EGG_NOTIFICATION_BUBBLE_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_NOTIFICATION_BUBBLE_WIDGET, EggNotificationBubbleWidgetClass))
-
-
-typedef struct _EggNotificationBubbleWidget EggNotificationBubbleWidget;
-typedef struct _EggNotificationBubbleWidgetClass EggNotificationBubbleWidgetClass;
-typedef struct _EggNotificationBubbleWidgetData EggNotificationBubbleWidgetData;
-
-struct _DrawingPipeline
-{
- gint start_x;
- gint start_y;
- gint start_corner_radius;
-
- gint last_x;
- gint last_y;
- gint last_corner_radius;
-
- gboolean is_clear;
-
- GList *pipeline;
-};
-
-struct _EggNotificationBubbleWidget
-{
- GtkWindow parent_instance;
-
- char *bubble_widget_header_text;
- char *bubble_widget_body_text;
- GtkWidget *icon;
-
- gboolean active;
- GtkWidget *table;
- GtkWidget *bubble_widget_header_label;
- GtkWidget *bubble_widget_body_label;
- GtkWidget *button_hbox;
- PangoLayout *body_layout;
-
- gint x, y;
- gint offset_x, offset_y;
- gboolean can_composite;
- gboolean draw_arrow;
-
- GdkColor header_text_color;
- GdkColor body_text_color;
- GdkColor bg_start_gradient;
- GdkColor bg_end_gradient;
- GdkColor border_color;
-
- /* drawing instructions */
- struct _DrawingPipeline dp;
-};
-
-struct _EggNotificationBubbleWidgetClass
-{
- GtkWindowClass parent_class;
-
- void (*clicked) (void);
-
- /* Padding for future expansion */
- void (*_gtk_reserved1) (void);
- void (*_gtk_reserved2) (void);
- void (*_gtk_reserved3) (void);
- void (*_gtk_reserved4) (void);
-};
-
-GType egg_notification_bubble_widget_get_type (void) G_GNUC_CONST;
-EggNotificationBubbleWidget* egg_notification_bubble_widget_new (void);
-
-void egg_notification_bubble_widget_set (EggNotificationBubbleWidget *bubble_widget,
- const gchar *notification_header,
- const gchar *icon,
- const gchar *notification_body);
-
-void egg_notification_bubble_widget_show (EggNotificationBubbleWidget *bubble_widget);
-void egg_notification_bubble_widget_hide (EggNotificationBubbleWidget *bubble_widget);
-
-
-void egg_notification_bubble_widget_set_pos (EggNotificationBubbleWidget *bubble_widget,
- gint x, gint y);
-
-void egg_notification_bubble_widget_set_icon_from_data (EggNotificationBubbleWidget *bubble_widget,
- const guchar *data,
- gboolean has_alpha,
- int bits_per_sample,
- int width,
- int height,
- int rowstride);
-
-
-GtkWidget * egg_notification_bubble_widget_create_button (EggNotificationBubbleWidget *bubble_widget,
- const gchar *label);
-
-void egg_notification_bubble_widget_clear_buttons (EggNotificationBubbleWidget *bubble_widget);
-
-void egg_notification_bubble_widget_set_draw_arrow (EggNotificationBubbleWidget *bubble_widget, gboolean value);
-
-G_END_DECLS
-
-#endif /* __EGG_NOTIFICATION_BUBBLE_WIDGET_H__ */
Added: trunk/notification-daemon/src/engines.c
===================================================================
--- trunk/notification-daemon/src/engines.c 2006-01-11 09:13:36 UTC (rev 2420)
+++ trunk/notification-daemon/src/engines.c 2006-01-11 09:53:24 UTC (rev 2421)
@@ -0,0 +1,133 @@
+#include "engines.h"
+
+typedef struct
+{
+ GModule *module;
+ guint ref_count;
+
+ gpointer (*create_notification)(void);
+ void (*destroy_notification)(GtkWindow *nw);
+ void (*show_notification)(GtkWindow *nw);
+ void (*hide_notification)(GtkWindow *nw);
+ void (*set_notification_text)(GtkWindow *nw, const char *summary,
+ const char *body);
+ void (*set_notification_icon)(GtkWindow *nw, GdkPixbuf *pixbuf);
+ void (*set_notification_arrow)(GtkWindow *nw, gboolean visible,
+ int x, int y);
+ void (*add_notification_action)(GtkWindow *nw, const char *label,
+ const char *key, GCallback cb);
+ void (*move_notification)(GtkWindow *nw, int x, int y);
+
+} ThemeEngine;
+
+static ThemeEngine *active_engine = NULL;
+/* static GList *engines = NULL; */
+
+static ThemeEngine *
+load_theme_engine(const char *filename)
+{
+ ThemeEngine *engine = g_new0(ThemeEngine, 1);
+
+ engine->ref_count = 1;
+ engine->module = g_module_open(filename, G_MODULE_BIND_LAZY);
+
+ if (engine->module == NULL)
+ {
+ g_free(engine);
+ g_error("The default theme engine doesn't exist. Your install "
+ "likely isn't complete.");
+ return NULL;
+ }
+
+#define BIND_REQUIRED_FUNC(name) \
+ if (!g_module_symbol(engine->module, #name, (gpointer *)&engine->name)) \
+ { \
+ /* Too harsh! Fall back to default. */ \
+ g_error("Theme doesn't provide the required function '%s'", #name); \
+ if (!g_module_close(engine->module)) \
+ g_warning("%s: %s", filename, g_module_error()); \
+ \
+ g_free(engine); \
+ }
+
+ BIND_REQUIRED_FUNC(create_notification);
+ BIND_REQUIRED_FUNC(destroy_notification);
+ BIND_REQUIRED_FUNC(show_notification);
+ BIND_REQUIRED_FUNC(hide_notification);
+ BIND_REQUIRED_FUNC(set_notification_text);
+ BIND_REQUIRED_FUNC(set_notification_icon);
+ BIND_REQUIRED_FUNC(set_notification_arrow);
+ BIND_REQUIRED_FUNC(add_notification_action);
+ BIND_REQUIRED_FUNC(move_notification);
+
+ return engine;
+}
+
+static ThemeEngine *
+get_theme_engine(void)
+{
+ if (active_engine == NULL)
+ {
+ /* XXX */
+ active_engine = load_theme_engine(ENGINES_DIR"/libbubble.so");
+ g_assert(active_engine != NULL);
+ }
+
+ return active_engine;
+}
+
+gpointer
+theme_create_notification(void)
+{
+ return get_theme_engine()->create_notification();
+}
+
+void
+theme_destroy_notification(GtkWindow *nw)
+{
+ get_theme_engine()->destroy_notification(nw);
+}
+
+void
+theme_show_notification(GtkWindow *nw)
+{
+ get_theme_engine()->show_notification(nw);
+}
+
+void
+theme_hide_notification(GtkWindow *nw)
+{
+ get_theme_engine()->hide_notification(nw);
+}
+
+void
+theme_set_notification_text(GtkWindow *nw, const char *summary,
+ const char *body)
+{
+ get_theme_engine()->set_notification_text(nw, summary, body);
+}
+
+void
+theme_set_notification_icon(GtkWindow *nw, GdkPixbuf *pixbuf)
+{
+ get_theme_engine()->set_notification_icon(nw, pixbuf);
+}
+
+void
+theme_set_notification_arrow(GtkWindow *nw, gboolean visible, int x, int y)
+{
+ get_theme_engine()->set_notification_arrow(nw, visible, x, y);
+}
+
+void
+theme_add_notification_action(GtkWindow *nw, const char *label,
+ const char *key, GCallback cb)
+{
+ get_theme_engine()->add_notification_action(nw, label, key, cb);
+}
+
+void
+theme_move_notification(GtkWindow *nw, int x, int y)
+{
+ get_theme_engine()->move_notification(nw, x, y);
+}
Added: trunk/notification-daemon/src/engines.h
===================================================================
--- trunk/notification-daemon/src/engines.h 2006-01-11 09:13:36 UTC (rev 2420)
+++ trunk/notification-daemon/src/engines.h 2006-01-11 09:53:24 UTC (rev 2421)
@@ -0,0 +1,19 @@
+#ifndef _ENGINES_H_
+#define _ENGINES_H_
+
+#include <gtk/gtk.h>
+
+gpointer theme_create_notification(void);
+void theme_destroy_notification(GtkWindow *nw);
+void theme_show_notification(GtkWindow *nw);
+void theme_hide_notification(GtkWindow *nw);
+void theme_set_notification_text(GtkWindow *nw, const char *summary,
+ const char *body);
+void theme_set_notification_icon(GtkWindow *nw, GdkPixbuf *pixbuf);
+void theme_set_notification_arrow(GtkWindow *nw, gboolean visible,
+ int x, int y);
+void theme_add_notification_action(GtkWindow *nw, const char *label,
+ const char *key, GCallback cb);
+void theme_move_notification(GtkWindow *nw, int x, int y);
+
+#endif /* _ENGINES_H_ */
Property changes on: trunk/notification-daemon/themes
___________________________________________________________________
Name: svn:ignore
+ Makefile
Makefile.in
Added: trunk/notification-daemon/themes/Makefile.am
===================================================================
--- trunk/notification-daemon/themes/Makefile.am 2006-01-11 09:13:36 UTC (rev 2420)
+++ trunk/notification-daemon/themes/Makefile.am 2006-01-11 09:53:24 UTC (rev 2421)
@@ -0,0 +1,2 @@
+#SUBDIRS = standard bubble
+SUBDIRS = bubble
Property changes on: trunk/notification-daemon/themes/bubble
___________________________________________________________________
Name: svn:ignore
+ Makefile
Makefile.in
.*.swp
.deps
.libs
Added: trunk/notification-daemon/themes/bubble/Makefile.am
===================================================================
--- trunk/notification-daemon/themes/bubble/Makefile.am 2006-01-11 09:13:36 UTC (rev 2420)
+++ trunk/notification-daemon/themes/bubble/Makefile.am 2006-01-11 09:53:24 UTC (rev 2421)
@@ -0,0 +1,13 @@
+enginedir = $(libdir)/notification-daemon/engines
+
+engine_LTLIBRARIES = libbubble.la
+
+noinst_HEADERS = \
+ eggnotificationbubblewidget.h
+
+libbubble_la_SOURCES = \
+ eggnotificationbubblewidget.c \
+ theme.c
+
+INCLUDES = \
+ $(NOTIFICATION_DAEMON_CFLAGS)
Copied: trunk/notification-daemon/themes/bubble/eggnotificationbubblewidget.c (from rev 2418, trunk/notification-daemon/src/eggnotificationbubblewidget.c)
===================================================================
--- trunk/notification-daemon/src/eggnotificationbubblewidget.c 2006-01-11 08:01:57 UTC (rev 2418)
+++ trunk/notification-daemon/themes/bubble/eggnotificationbubblewidget.c 2006-01-11 09:53:24 UTC (rev 2421)
@@ -0,0 +1,1204 @@
+/* EggNotificationBubbleWidget
+ * Copyright (C) 2005 Colin Walters <walters at verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+
+#include <gtk/gtk.h>
+#include "eggnotificationbubblewidget.h"
+
+#define BORDER_SIZE 30
+#define BORDER_LINE_WIDTH 2
+#define CURVE_LENGTH 25
+#define TRIANGLE_START 45
+#define TRIANGLE_WIDTH 60
+#define TEXT_WIDTH_THRESHOLD 100
+
+
+static void egg_notification_bubble_widget_class_init (EggNotificationBubbleWidgetClass *klass);
+static void egg_notification_bubble_widget_init (EggNotificationBubbleWidget *bubble_widget);
+static void egg_notification_bubble_widget_finalize (GObject *object);
+static void egg_notification_bubble_widget_event_handler (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data);
+static gboolean egg_notification_bubble_widget_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+
+static gboolean egg_notification_bubble_widget_body_label_expose_handler (GtkWidget *widget,
+ GdkEventExpose *event,
+ EggNotificationBubbleWidget *bw);
+
+static void egg_notification_bubble_widget_context_changed_handler (EggNotificationBubbleWidget *bubble_widget);
+
+static void egg_notification_bubble_widget_realize (GtkWidget *widget);
+
+static void egg_notification_bubble_widget_screen_changed (GtkWidget *widget,
+ GdkScreen *old_screen);
+
+static void _populate_window (EggNotificationBubbleWidget *bubble_widget);
+static void draw_bubble_widget (EggNotificationBubbleWidget *bubble_widget);
+static void _stencil_bubble (EggNotificationBubbleWidget *bw);
+
+
+static GtkWindowClass *parent_class;
+
+#define BEVEL_ALPHA_LIGHT 0.2
+#define BEVEL_ALPHA_MEDIUM 0.5
+#define BEVEL_ALPHA_DARK 0.8
+
+enum
+{
+ DRAW_MOVE = 0,
+ DRAW_LINE = 1,
+ DRAW_CAP = 2,
+ DRAW_CLOSE = 3
+};
+
+typedef struct _DrawingInstruction
+{
+ gint type;
+
+ gint end_x, end_y;
+ gint corner_x, corner_y;
+} DrawingInstruction;
+
+enum
+{
+ ORIENT_TOP = 0,
+ ORIENT_BOTTOM = 1,
+ ORIENT_LEFT = 2,
+ ORIENT_RIGHT = 3
+};
+
+enum {
+ TRIANGLE_LEFT = 0,
+ TRIANGLE_RIGHT = 1
+};
+
+enum
+{
+ NOTIFICATION_CLICKED,
+ LAST_SIGNAL
+};
+
+static guint egg_notification_bubble_widget_signals[LAST_SIGNAL] = { 0 };
+
+GType
+egg_notification_bubble_widget_get_type (void)
+{
+ static GType bubble_widget_type = 0;
+
+ if (!bubble_widget_type)
+ {
+ static const GTypeInfo bubble_widget_info =
+ {
+ sizeof (EggNotificationBubbleWidgetClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) egg_notification_bubble_widget_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (EggNotificationBubbleWidget),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) egg_notification_bubble_widget_init,
+ };
+
+ bubble_widget_type = g_type_register_static (GTK_TYPE_WINDOW, "EggNotificationBubbleWidget",
+ &bubble_widget_info, 0);
+ }
+
+ return bubble_widget_type;
+}
+
+static void
+egg_notification_bubble_widget_class_init (EggNotificationBubbleWidgetClass *class)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GObjectClass *) class;
+ widget_class = (GtkWidgetClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->finalize = egg_notification_bubble_widget_finalize;
+ widget_class->expose_event = egg_notification_bubble_widget_expose;
+ widget_class->realize = egg_notification_bubble_widget_realize;
+ widget_class->screen_changed = egg_notification_bubble_widget_screen_changed;
+
+ egg_notification_bubble_widget_signals[NOTIFICATION_CLICKED] =
+ g_signal_new ("clicked",
+ EGG_TYPE_NOTIFICATION_BUBBLE_WIDGET,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EggNotificationBubbleWidgetClass, clicked),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+}
+
+static void
+egg_notification_bubble_widget_init (EggNotificationBubbleWidget *bubble_widget)
+{
+ GtkWindow *win;
+
+ win = GTK_WINDOW (bubble_widget);
+
+ bubble_widget->can_composite = FALSE;
+
+ egg_notification_bubble_widget_screen_changed (GTK_WIDGET (bubble_widget),
+ NULL);
+
+ bubble_widget->dp.is_clear = TRUE;
+ bubble_widget->dp.pipeline = NULL;
+ bubble_widget->draw_arrow = FALSE;
+
+ _populate_window (bubble_widget);
+
+}
+
+static void
+egg_notification_bubble_widget_finalize (GObject *object)
+{
+ EggNotificationBubbleWidget *bubble_widget = EGG_NOTIFICATION_BUBBLE_WIDGET (object);
+
+ g_return_if_fail (bubble_widget != NULL);
+
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+_layout_window (EggNotificationBubbleWidget *bubble_widget,
+ int alignment)
+{
+ if (bubble_widget->draw_arrow)
+ gtk_container_set_border_width (GTK_CONTAINER (bubble_widget), BORDER_SIZE + 5);
+ else
+ gtk_container_set_border_width (GTK_CONTAINER (bubble_widget), 10);
+
+ if (gtk_widget_get_parent (bubble_widget->icon))
+ gtk_container_remove (GTK_CONTAINER (bubble_widget->table),
+ bubble_widget->icon);
+
+ if (gtk_widget_get_parent (bubble_widget->bubble_widget_header_label))
+ gtk_container_remove (GTK_CONTAINER (bubble_widget->table),
+ bubble_widget->bubble_widget_header_label);
+
+ if (gtk_widget_get_parent (bubble_widget->bubble_widget_body_label))
+ gtk_container_remove (GTK_CONTAINER (bubble_widget->table),
+ bubble_widget->bubble_widget_body_label);
+
+ if (bubble_widget->button_hbox != NULL &&
+ gtk_widget_get_parent (bubble_widget->button_hbox) != NULL)
+ gtk_container_remove (GTK_CONTAINER (bubble_widget->table),
+ bubble_widget->button_hbox);
+
+ if (alignment == TRIANGLE_LEFT)
+ {
+ gtk_table_attach (GTK_TABLE (bubble_widget->table),
+ bubble_widget->icon,
+ 0, 1, 1, 2,
+ GTK_FILL, GTK_FILL,
+ 0, 0);
+
+ gtk_table_attach (GTK_TABLE (bubble_widget->table),
+ bubble_widget->bubble_widget_header_label,
+ 1, 2, 0, 1,
+ GTK_FILL, GTK_FILL,
+ 0, 0);
+
+ gtk_table_attach (GTK_TABLE (bubble_widget->table),
+ bubble_widget->bubble_widget_body_label,
+ 1, 2, 1, 2,
+ GTK_FILL, GTK_FILL,
+ 0, 0);
+ }
+ else
+ {
+ gtk_table_attach (GTK_TABLE (bubble_widget->table),
+ bubble_widget->icon,
+ 1, 2, 1, 2,
+ GTK_FILL, GTK_FILL,
+ 0, 0);
+
+ gtk_table_attach (GTK_TABLE (bubble_widget->table),
+ bubble_widget->bubble_widget_header_label,
+ 0, 1, 0, 1,
+ GTK_FILL, GTK_FILL,
+ 0, 0);
+
+ gtk_table_attach (GTK_TABLE (bubble_widget->table),
+ bubble_widget->bubble_widget_body_label,
+ 0, 1, 1, 2,
+ GTK_FILL, GTK_FILL,
+ 0, 0);
+
+ }
+
+ if (bubble_widget->button_hbox != NULL)
+ {
+ gtk_table_attach (GTK_TABLE (bubble_widget->table),
+ bubble_widget->button_hbox,
+ 0, 2, 2, 3,
+ GTK_FILL, GTK_FILL,
+ 0, 0);
+
+ }
+
+ gtk_widget_show_all (bubble_widget->table);
+}
+
+static void
+_drawing_instruction_internal_add (GList **pipeline,
+ guint type,
+ gint end_x, gint end_y,
+ gint corner_x, gint corner_y)
+{
+ DrawingInstruction *di;
+
+ di = g_new0 (DrawingInstruction, 1);
+ di->type = type;
+ di->end_x = end_x;
+ di->end_y = end_y;
+ di->corner_x = corner_x;
+ di->corner_y = corner_y;
+
+ *pipeline = g_list_append (*pipeline, di);
+}
+
+static void
+_drawing_instruction_move (GList **pipeline,
+ gint x, gint y)
+{
+ _drawing_instruction_internal_add (pipeline, DRAW_MOVE, x, y, 0, 0);
+}
+
+static void
+_drawing_instruction_line (GList **pipeline,
+ gint x, gint y)
+{
+ _drawing_instruction_internal_add (pipeline, DRAW_LINE, x, y, 0, 0);
+}
+
+static void
+_drawing_instruction_cap (GList **pipeline,
+ gint x, gint y,
+ gint corner_x, gint corner_y)
+{
+ _drawing_instruction_internal_add (pipeline, DRAW_CAP, x, y, corner_x, corner_y);
+}
+
+static void
+_drawing_instruction_close (GList **pipeline)
+{
+ _drawing_instruction_internal_add (pipeline, DRAW_CLOSE, 0, 0, 0, 0);
+}
+
+/* given a distance from start point x1, y1
+ calculate the point dist units away */
+static GdkPoint
+_calc_point_on_line (x1, y1, x2, y2, dist)
+{
+ GdkPoint result;
+ gint dx, dy;
+ gdouble d, vx, vy;
+
+ dx = x2 - x1;
+ dy = y2 - y1;
+
+ d = sqrt (dx * dx + dy * dy);
+ vx = dx / d;
+ vy = dy / d;
+
+ result.x = x1 + dist * vx;
+ result.y = y1 + dist * vy;
+
+ return result;
+}
+
+static void
+_edge_line_to (EggNotificationBubbleWidget *bw,
+ gint x, gint y,
+ gint corner_radius)
+{
+ if (bw->dp.is_clear == TRUE)
+ {
+ bw->dp.start_x = x;
+ bw->dp.start_y = y;
+ bw->dp.start_corner_radius = corner_radius;
+ bw->dp.is_clear = FALSE;
+ }
+ else
+ {
+ GdkPoint start_p;
+ GdkPoint end_p;
+
+ start_p = _calc_point_on_line (bw->dp.last_x,
+ bw->dp.last_y,
+ x,
+ y,
+ bw->dp.last_corner_radius);
+ end_p = _calc_point_on_line (x,
+ y,
+ bw->dp.last_x,
+ bw->dp.last_y,
+ corner_radius);
+
+ if (bw->dp.last_x == bw->dp.start_x &&
+ bw->dp.last_y == bw->dp.start_y)
+ _drawing_instruction_move (&bw->dp.pipeline, start_p.x, start_p.y);
+ else
+ _drawing_instruction_cap (&bw->dp.pipeline,
+ start_p.x,
+ start_p.y,
+ bw->dp.last_x,
+ bw->dp.last_y);
+
+ _drawing_instruction_line (&bw->dp.pipeline, end_p.x, end_p.y);
+
+ }
+
+ bw->dp.last_x = x;
+ bw->dp.last_y = y;
+ bw->dp.last_corner_radius = corner_radius;
+}
+
+static void
+_close_path (EggNotificationBubbleWidget *bw)
+{
+ GdkPoint start_p;
+ GdkPoint end_p;
+ DrawingInstruction *di;
+
+ start_p = _calc_point_on_line (bw->dp.last_x,
+ bw->dp.last_y,
+ bw->dp.start_x,
+ bw->dp.start_y,
+ bw->dp.last_corner_radius);
+
+ end_p = _calc_point_on_line (bw->dp.start_x,
+ bw->dp.start_y,
+ bw->dp.last_x,
+ bw->dp.last_y,
+ bw->dp.start_corner_radius);
+
+
+ _drawing_instruction_cap (&bw->dp.pipeline,
+ start_p.x,
+ start_p.y,
+ bw->dp.last_x,
+ bw->dp.last_y);
+
+ _drawing_instruction_line (&bw->dp.pipeline,
+ end_p.x,
+ end_p.y);
+
+ di = (DrawingInstruction *) bw->dp.pipeline->data;
+ _drawing_instruction_cap (&bw->dp.pipeline,
+ di->end_x,
+ di->end_y,
+ bw->dp.start_x,
+ bw->dp.start_y);
+
+ _drawing_instruction_close (&bw->dp.pipeline);
+
+}
+
+static void
+_drawing_instruction_draw (DrawingInstruction *di, cairo_t *cr)
+{
+ switch (di->type)
+ {
+ case DRAW_MOVE:
+ cairo_move_to (cr, di->end_x, di->end_y);
+ break;
+ case DRAW_LINE:
+ cairo_line_to (cr, di->end_x, di->end_y);
+ break;
+ case DRAW_CAP:
+ cairo_curve_to (cr,
+ di->corner_x, di->corner_y,
+ di->corner_x, di->corner_y,
+ di->end_x, di->end_y);
+ break;
+ case DRAW_CLOSE:
+ cairo_close_path (cr);
+ break;
+ }
+}
+
+static void
+_drawing_pipeline_clear (EggNotificationBubbleWidget *bw)
+{
+ bw->dp.is_clear = TRUE;
+
+ g_list_foreach (bw->dp.pipeline, (GFunc) g_free, NULL);
+ g_list_free (bw->dp.pipeline);
+ bw->dp.pipeline = NULL;
+}
+
+static void
+_populate_window (EggNotificationBubbleWidget *bubble_widget)
+{
+ GtkWidget *widget;
+
+ g_return_if_fail (EGG_IS_NOTIFICATION_BUBBLE_WIDGET (bubble_widget));
+
+ widget = GTK_WIDGET (bubble_widget);
+
+ gtk_widget_add_events (widget, GDK_BUTTON_PRESS_MASK);
+ gtk_widget_set_app_paintable (widget, TRUE);
+ gtk_window_set_resizable (GTK_WINDOW (bubble_widget), FALSE);
+
+ bubble_widget->bubble_widget_header_label = gtk_label_new (NULL);
+
+ //use placeholder so we can use pango/cairo to draw body
+ bubble_widget->bubble_widget_body_label = gtk_frame_new("");
+ gtk_frame_set_shadow_type (GTK_FRAME (bubble_widget->bubble_widget_body_label), GTK_SHADOW_NONE);
+ bubble_widget->icon = gtk_image_new_from_stock (GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_BUTTON);
+ gtk_widget_ref (bubble_widget->bubble_widget_header_label);
+ gtk_widget_ref (bubble_widget->bubble_widget_body_label);
+ gtk_widget_ref (bubble_widget->icon);
+
+ gtk_label_set_line_wrap (GTK_LABEL (bubble_widget->bubble_widget_header_label), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (bubble_widget->bubble_widget_header_label), 0.0, 0.0);
+
+ gtk_misc_set_alignment (GTK_MISC (bubble_widget->icon), 0.0, 0.0);
+ gtk_widget_show (bubble_widget->icon);
+ gtk_widget_show (bubble_widget->bubble_widget_header_label);
+ gtk_widget_show (bubble_widget->bubble_widget_body_label);
+
+ bubble_widget->table = gtk_table_new (3, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (bubble_widget->table), 5);
+ gtk_table_set_row_spacings (GTK_TABLE (bubble_widget->table), 5);
+
+ gtk_container_add (GTK_CONTAINER (bubble_widget), bubble_widget->table);
+
+ bubble_widget->body_layout = pango_layout_new (
+ gtk_widget_get_pango_context (
+ GTK_WIDGET (bubble_widget)));
+
+ /* do a fake layout for now so we can calculate
+ height and width */
+ _layout_window (bubble_widget, TRIANGLE_RIGHT);
+
+ g_signal_connect (bubble_widget, "style-set",
+ G_CALLBACK (egg_notification_bubble_widget_context_changed_handler),
+ NULL);
+
+ g_signal_connect (bubble_widget, "direction-changed",
+ G_CALLBACK (egg_notification_bubble_widget_context_changed_handler),
+ NULL);
+
+ g_signal_connect_after (bubble_widget, "event-after",
+ G_CALLBACK (egg_notification_bubble_widget_event_handler),
+ bubble_widget);
+
+ g_signal_connect (bubble_widget->bubble_widget_body_label, "expose-event",
+ G_CALLBACK (egg_notification_bubble_widget_body_label_expose_handler),
+ bubble_widget);
+}
+
+static void
+_destroy_pixmap_data_func (guchar *pixels,
+ gpointer data)
+{
+ g_free (pixels);
+}
+
+void
+egg_notification_bubble_widget_set_icon_from_data (EggNotificationBubbleWidget *bubble_widget,
+ const guchar *data,
+ gboolean has_alpha,
+ int bits_per_sample,
+ int width,
+ int height,
+ int rowstride)
+{
+ GdkPixbuf *pixbuf;
+
+ pixbuf = gdk_pixbuf_new_from_data (data,
+ GDK_COLORSPACE_RGB,
+ has_alpha,
+ bits_per_sample,
+ width,
+ height,
+ rowstride,
+ _destroy_pixmap_data_func,
+ NULL);
+
+ gtk_image_set_from_pixbuf (GTK_IMAGE (bubble_widget->icon), pixbuf);
+ gdk_pixbuf_unref (pixbuf);
+}
+
+static void
+_calculate_pango_layout_from_aspect (PangoLayout *layout,
+ const char *text,
+ double factor)
+{
+ gint len;
+ gint w, h;
+ double x;
+
+ len = strlen (text);
+ pango_layout_set_width(layout, -1);
+ pango_layout_set_markup(layout, text, len);
+
+ pango_layout_get_pixel_size (layout, &w, &h);
+
+ if (w > TEXT_WIDTH_THRESHOLD)
+ {
+ double f;
+
+ pango_layout_get_size (layout, &w, &h);
+
+ x = sqrt (factor * w / h);
+ if (x == 0)
+ x = 1;
+
+ f = (double) w / x;
+
+ w = (gint) (f + 0.5);
+
+ pango_layout_set_width(layout, w);
+ }
+}
+
+void
+egg_notification_bubble_widget_set (EggNotificationBubbleWidget *bubble_widget,
+ const gchar *bubble_widget_header_text,
+ const gchar *icon,
+ const gchar *bubble_widget_body_text)
+{
+ gchar *markupquoted;
+ gchar *markuptext;
+ gint w, h;
+
+ g_return_if_fail (EGG_IS_NOTIFICATION_BUBBLE_WIDGET (bubble_widget));
+
+ g_free (bubble_widget->bubble_widget_header_text);
+ g_free (bubble_widget->bubble_widget_body_text);
+ bubble_widget->bubble_widget_header_text = g_strdup (bubble_widget_header_text);
+ bubble_widget->bubble_widget_body_text = g_strdup (bubble_widget_body_text);
+
+ if (icon != NULL && strcmp (icon, "") != 0)
+ {
+ if (g_str_has_prefix (icon, "file://"))
+ {
+ gchar *icon_path = (gchar *) icon + (7 * sizeof (gchar));
+ gtk_image_set_from_file (GTK_IMAGE (bubble_widget->icon), icon_path);
+ }
+ else
+ {
+ gtk_image_set_from_icon_name (GTK_IMAGE (bubble_widget->icon), icon, GTK_ICON_SIZE_DIALOG);
+ }
+ }
+
+ markupquoted = g_markup_escape_text (bubble_widget->bubble_widget_header_text, -1);
+ markuptext = g_strdup_printf ("<span size=\"larger\" weight=\"ultrabold\">%s</span>", markupquoted);
+ gtk_label_set_markup (GTK_LABEL (bubble_widget->bubble_widget_header_label), markuptext);
+ g_free (markuptext);
+ g_free (markupquoted);
+
+ _calculate_pango_layout_from_aspect (bubble_widget->body_layout,
+ bubble_widget->bubble_widget_body_text,
+ 0.25);
+
+ pango_layout_get_pixel_size (bubble_widget->body_layout, &w, &h);
+ gtk_widget_set_size_request (bubble_widget->bubble_widget_body_label, w, h);
+
+}
+
+
+void
+egg_notification_bubble_widget_set_pos (EggNotificationBubbleWidget *bubble_widget,
+ gint x, gint y)
+{
+ GdkScreen *screen;
+ gint monitor_num;
+ GdkRectangle monitor;
+
+ bubble_widget->x = x;
+ bubble_widget->y = y;
+
+ screen = gtk_window_get_screen (GTK_WINDOW(bubble_widget));
+ monitor_num = gdk_screen_get_monitor_at_point (screen, x, y);
+ gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+ /* TODO: This is wrong - if elements are added before
+ set_pos is called the layout become wrong */
+ if (x < (monitor.x + monitor.width) / 2)
+ _layout_window (bubble_widget, TRIANGLE_LEFT);
+ else
+ _layout_window (bubble_widget, TRIANGLE_RIGHT);
+
+ _stencil_bubble (bubble_widget);
+
+ gtk_window_move (GTK_WINDOW (bubble_widget),
+ x - bubble_widget->offset_x,
+ y - bubble_widget->offset_y);
+}
+
+static void
+egg_notification_bubble_widget_realize (GtkWidget *widget)
+{
+ GtkWidgetClass *widget_parent_class;
+
+ widget_parent_class = (GtkWidgetClass *)parent_class;
+ if (widget_parent_class->realize)
+ widget_parent_class->realize (widget);
+
+ draw_bubble_widget (EGG_NOTIFICATION_BUBBLE_WIDGET(widget));
+}
+
+static void
+egg_notification_bubble_widget_screen_changed (GtkWidget *widget,
+ GdkScreen *old_screen)
+{
+ GtkWidgetClass *widget_parent_class;
+ EggNotificationBubbleWidget *bw;
+ GdkScreen *screen;
+ GdkColormap *colormap;
+ gboolean can_composite;
+
+ bw = EGG_NOTIFICATION_BUBBLE_WIDGET (widget);
+
+ widget_parent_class = (GtkWidgetClass *)parent_class;
+ if (widget_parent_class->screen_changed)
+ widget_parent_class->screen_changed (widget, old_screen);
+
+ can_composite = TRUE;
+
+ screen = gtk_widget_get_screen (widget);
+ colormap = gdk_screen_get_rgba_colormap (screen);
+
+ if (!colormap)
+ {
+ colormap = gdk_screen_get_rgb_colormap (screen);
+ can_composite = FALSE;
+ }
+
+ gtk_widget_set_colormap (widget, colormap);
+
+ bw->can_composite = can_composite;
+}
+
+
+static gboolean
+egg_notification_bubble_widget_body_label_expose_handler (GtkWidget *widget,
+ GdkEventExpose *event,
+ EggNotificationBubbleWidget *bw)
+{
+ cairo_t *cr;
+ cr = gdk_cairo_create (GTK_WIDGET(bw)->window);
+
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+
+ cairo_set_source_rgba (cr, bw->body_text_color.red,
+ bw->body_text_color.green,
+ bw->body_text_color.blue,
+ 0.60);
+ cairo_move_to (cr, event->area.x, event->area.y);
+
+ pango_cairo_layout_path (cr, bw->body_layout);
+
+ cairo_fill (cr);
+ cairo_destroy (cr);
+
+ return TRUE;
+}
+
+static gboolean
+egg_notification_bubble_widget_expose (GtkWidget *widget, GdkEventExpose *event)
+{
+ GtkWidgetClass *widget_parent_class;
+ EggNotificationBubbleWidget *bw;
+
+ bw = EGG_NOTIFICATION_BUBBLE_WIDGET (widget);
+
+ draw_bubble_widget (bw);
+
+ widget_parent_class = (GtkWidgetClass *)parent_class;
+ if (widget_parent_class->expose_event)
+ widget_parent_class->expose_event (widget, event);
+
+ return TRUE;
+}
+
+static void
+_stencil_bubble_top_right (EggNotificationBubbleWidget *bw,
+ GdkRectangle *rect)
+{
+ GdkPoint triangle[3];
+
+ triangle[0].y = rect->y;
+ triangle[0].x = rect->x + rect->width - (TRIANGLE_START + TRIANGLE_WIDTH);
+ triangle[2].x = triangle[0].x + TRIANGLE_WIDTH;
+ triangle[2].y = rect->y;
+ triangle[1].x = (triangle[2].x - triangle[0].x) / 2 + triangle[0].x;
+ triangle[1].y = rect->y - BORDER_SIZE + 5;
+
+
+ bw->offset_x = triangle[1].x;
+ bw->offset_y = triangle[1].y;
+
+ _edge_line_to (bw, triangle[0].x, triangle[0].y, 0);
+ _edge_line_to (bw, triangle[1].x, triangle[1].y, 0);
+ _edge_line_to (bw, triangle[2].x, triangle[2].y, 0);
+
+ _edge_line_to (bw, rect->x + rect->width, rect->y, CURVE_LENGTH);
+ _edge_line_to (bw, rect->x + rect->width, rect->y + rect->height, CURVE_LENGTH);
+
+ _edge_line_to (bw, rect->x, rect->y + rect->height + BORDER_SIZE - 5, CURVE_LENGTH);
+
+ _edge_line_to (bw, rect->x, rect->y, CURVE_LENGTH);
+
+ _close_path (bw);
+}
+
+static void
+_stencil_bubble_top_left (EggNotificationBubbleWidget *bw,
+ GdkRectangle *rect)
+{
+ GdkPoint triangle[3];
+
+ triangle[0].x = rect->x + TRIANGLE_START;
+ triangle[0].y = rect->y;
+ triangle[2].x = triangle[0].x + TRIANGLE_WIDTH;
+ triangle[2].y = rect->y;
+ triangle[1].x = (triangle[2].x - triangle[0].x) / 2 + triangle[0].x;
+ triangle[1].y = rect->y - BORDER_SIZE + 5;
+
+ bw->offset_x = triangle[1].x;
+ bw->offset_y = triangle[1].y;
+
+ _edge_line_to (bw, triangle[0].x, triangle[0].y, 0);
+ _edge_line_to (bw, triangle[1].x, triangle[1].y, 0);
+ _edge_line_to (bw, triangle[2].x, triangle[2].y, 0);
+
+ _edge_line_to (bw, rect->x + rect->width, rect->y, CURVE_LENGTH);
+ _edge_line_to (bw, rect->x + rect->width, rect->y + rect->height + BORDER_SIZE - 5, CURVE_LENGTH);
+
+ _edge_line_to (bw, rect->x, rect->y + rect->height, CURVE_LENGTH);
+
+ _edge_line_to (bw, rect->x, rect->y, CURVE_LENGTH);
+
+ _close_path (bw);
+
+}
+
+static void
+_stencil_bubble_bottom_right (EggNotificationBubbleWidget *bw,
+ GdkRectangle *rect)
+{
+ GdkPoint triangle[3];
+
+ triangle[2].x = rect->x + rect->width - TRIANGLE_START;
+ triangle[2].y = rect->y + rect->height;
+
+ triangle[0].x = triangle[2].x - TRIANGLE_WIDTH;
+ triangle[0].y = rect->y + rect->height;
+ triangle[1].x = (triangle[2].x - triangle[0].x) / 2 + triangle[0].x;
+ triangle[1].y = rect->y + rect->height + BORDER_SIZE - 5;
+
+ bw->offset_x = triangle[1].x;
+ bw->offset_y = triangle[1].y;
+
+ _edge_line_to (bw, triangle[2].x, triangle[2].y, 0);
+ _edge_line_to (bw, triangle[1].x, triangle[1].y, 0);
+ _edge_line_to (bw, triangle[0].x, triangle[0].y, 0);
+
+
+ _edge_line_to (bw, rect->x, rect->y + rect->height, CURVE_LENGTH);
+ _edge_line_to (bw, rect->x, rect->y - BORDER_SIZE + 5, CURVE_LENGTH);
+ _edge_line_to (bw, rect->x + rect->width, rect->y, CURVE_LENGTH);
+ _edge_line_to (bw, rect->x + rect->width, rect->y + rect->height, CURVE_LENGTH);
+
+ _close_path (bw);
+}
+
+static void
+_stencil_bubble_bottom_left (EggNotificationBubbleWidget *bw,
+ GdkRectangle *rect)
+{
+ GdkPoint triangle[3];
+
+ triangle[0].x = rect->x + TRIANGLE_START;
+ triangle[0].y = rect->y + rect->height;
+ triangle[2].x = triangle[0].x + TRIANGLE_WIDTH;
+ triangle[2].y = rect->y + rect->height;
+ triangle[1].x = (triangle[2].x - triangle[0].x) / 2 + triangle[0].x;
+ triangle[1].y = rect->y + rect->height + BORDER_SIZE - 5;
+
+ bw->offset_x = triangle[1].x;
+ bw->offset_y = triangle[1].y;
+
+ _edge_line_to (bw, triangle[2].x, triangle[2].y, 0);
+ _edge_line_to (bw, triangle[1].x, triangle[1].y, 0);
+ _edge_line_to (bw, triangle[0].x, triangle[0].y, 0);
+
+ _edge_line_to (bw, rect->x, rect->y + rect->height, CURVE_LENGTH);
+ _edge_line_to (bw, rect->x, rect->y, CURVE_LENGTH);
+ _edge_line_to (bw, rect->x + rect->width, rect->y - BORDER_SIZE + 5, CURVE_LENGTH);
+ _edge_line_to (bw, rect->x + rect->width, rect->y + rect->height, CURVE_LENGTH);
+
+ _close_path (bw);
+}
+
+static void
+_stencil_bubble_no_arrow (EggNotificationBubbleWidget *bw,
+ GdkRectangle *rect)
+{
+ bw->offset_x = 0;
+ bw->offset_y = 0;
+
+ _edge_line_to (bw, rect->x + rect->width, rect->y, CURVE_LENGTH);
+ _edge_line_to (bw, rect->x + rect->width, rect->y + rect->height, CURVE_LENGTH);
+
+ _edge_line_to (bw, rect->x, rect->y + rect->height, CURVE_LENGTH);
+
+ _edge_line_to (bw, rect->x, rect->y, CURVE_LENGTH);
+
+ _close_path (bw);
+
+}
+
+static void
+_stencil_bubble (EggNotificationBubbleWidget *bw)
+{
+ GdkRectangle rect;
+ GtkRequisition req;
+ gint rect_border;
+
+ gtk_widget_size_request (GTK_WIDGET (bw), &req);
+
+ if (bw->draw_arrow)
+ rect_border = BORDER_SIZE - BORDER_LINE_WIDTH;
+ else
+ rect_border = BORDER_LINE_WIDTH;
+
+ rect.x = rect_border;
+ rect.y = rect_border;
+ rect.width = req.width - (rect_border * 2);
+ rect.height = req.height - (rect_border * 2);
+
+ _drawing_pipeline_clear (bw);
+
+ if (bw->draw_arrow)
+ {
+ GdkScreen *screen;
+ GdkRectangle monitor;
+ gint monitor_num;
+ gint orient;
+ gint orient_triangle;
+ gint x, y;
+
+ x = bw->x;
+ y = bw->y;
+ screen = gtk_window_get_screen (GTK_WINDOW(bw));
+ monitor_num = gdk_screen_get_monitor_at_point (screen, x, y);
+ gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+ /* TODO: draw corner cases */
+ if (x < (monitor.x + monitor.width) / 2)
+ orient_triangle = TRIANGLE_LEFT;
+ else
+ orient_triangle = TRIANGLE_RIGHT;
+
+ orient = ORIENT_TOP;
+
+ if ((y + req.height) > monitor.y + monitor.height)
+ orient = ORIENT_BOTTOM;
+
+ if (orient == ORIENT_TOP)
+ {
+ if (orient_triangle == TRIANGLE_RIGHT)
+ _stencil_bubble_top_right (bw, &rect);
+ else if (orient_triangle == TRIANGLE_LEFT)
+ _stencil_bubble_top_left (bw, &rect);
+ }
+ else if (orient == ORIENT_BOTTOM)
+ {
+ if (orient_triangle == TRIANGLE_RIGHT)
+ _stencil_bubble_bottom_right (bw, &rect);
+ else if (orient_triangle == TRIANGLE_LEFT)
+ _stencil_bubble_bottom_left (bw, &rect);
+ }
+ }
+ else
+ _stencil_bubble_no_arrow (bw, &rect);
+}
+
+static GdkColor
+_blend_colors (GdkColor color1, GdkColor color2, gdouble factor)
+{
+ GdkColor result;
+ gint channel_intensity;
+
+ channel_intensity = color1.red * factor + color2.red * (1 - factor);
+ result.red = CLAMP (channel_intensity, 0, 65535);
+
+ channel_intensity = color1.green * factor + color2.green * (1 - factor);
+ result.green = CLAMP (channel_intensity, 0, 65535);
+
+ channel_intensity = color1.blue * factor + color2.blue * (1 - factor);
+ result.blue = CLAMP (channel_intensity, 0, 65535);
+
+ result.pixel = 0xFF; /* This is never really used. */
+
+ return result;
+}
+
+static void
+_calculate_colors_from_style (EggNotificationBubbleWidget *bw)
+{
+ GtkStyle *style;
+ GtkWidget *widget;
+ GdkColor header_text_color;
+ GdkColor body_text_color;
+ GdkColor bg_end_gradient;
+ GdkColor border_color;
+ GdkColor bg_start_gradient;
+
+ widget = GTK_WIDGET (bw);
+
+ gtk_widget_ensure_style (widget);
+ style = widget->style;
+
+ header_text_color = style->text[GTK_STATE_NORMAL];
+ body_text_color = style->text[GTK_STATE_NORMAL];
+ bg_start_gradient = style->base[GTK_STATE_NORMAL];
+ bg_end_gradient = style->bg[GTK_STATE_SELECTED];
+ border_color = style->mid[GTK_STATE_NORMAL];
+
+ bg_end_gradient = _blend_colors (bg_start_gradient, bg_end_gradient, 0.25);
+
+ bw->header_text_color = header_text_color;
+ bw->body_text_color = body_text_color;
+ bw->bg_start_gradient = bg_start_gradient;
+ bw->bg_end_gradient = bg_end_gradient;
+ bw->border_color = border_color;
+}
+
+
+static void
+draw_bubble_widget (EggNotificationBubbleWidget *bubble_widget)
+{
+ GtkRequisition requisition;
+ gint w, h;
+ cairo_pattern_t *pat;
+ GdkPixmap *mask;
+ GdkPoint arrow_pos;
+
+ GtkWidget *widget;
+ cairo_t *cairo_context;
+ cairo_t *mask_cr;
+ gboolean can_composite;
+
+ mask_cr = NULL;
+ mask = NULL;
+
+ arrow_pos.x = 0;
+ arrow_pos.y = 0;
+
+ widget = GTK_WIDGET(bubble_widget);
+ cairo_context = gdk_cairo_create (widget->window);
+
+ can_composite = bubble_widget->can_composite;
+
+ _calculate_colors_from_style (bubble_widget);
+
+ gtk_widget_size_request (widget, &requisition);
+ w = requisition.width;
+ h = requisition.height;
+
+ if (!can_composite)
+ {
+ mask = gdk_pixmap_new (NULL, w, h, 1);
+ mask_cr = gdk_cairo_create ((GdkDrawable *) mask);
+ }
+
+ g_list_foreach (bubble_widget->dp.pipeline, (GFunc) _drawing_instruction_draw, cairo_context);
+ if (!can_composite)
+ g_list_foreach (bubble_widget->dp.pipeline, (GFunc) _drawing_instruction_draw, mask_cr);
+
+ if (can_composite)
+ cairo_set_source_rgba (cairo_context, 1, 1, 1, 0);
+ else
+ cairo_set_source_rgba (cairo_context,
+ bubble_widget->border_color.red / 65535.0,
+ bubble_widget->border_color.green / 65535.0,
+ bubble_widget->border_color.blue / 65535.0, 1);
+
+ cairo_set_operator (cairo_context, CAIRO_OPERATOR_SOURCE);
+ cairo_paint (cairo_context);
+
+ cairo_set_operator (cairo_context, CAIRO_OPERATOR_OVER);
+
+ pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, h);
+ //cairo_pattern_add_color_stop_rgba (pat, 1, 0.59, 0.76, 0.93, 1);
+ cairo_pattern_add_color_stop_rgba (pat, 0,
+ bubble_widget->bg_start_gradient.red /
+ 65535.0,
+ bubble_widget->bg_start_gradient.green /
+ 65535.0,
+ bubble_widget->bg_start_gradient.blue /
+ 65535.0, 1);
+
+ cairo_pattern_add_color_stop_rgba (pat, 1,
+ bubble_widget->bg_end_gradient.red /
+ 65535.0,
+ bubble_widget->bg_end_gradient.green /
+ 65535.0,
+ bubble_widget->bg_end_gradient.blue /
+ 65535.0, 1);
+
+ cairo_set_source (cairo_context, pat);
+ cairo_fill_preserve (cairo_context);
+ cairo_pattern_destroy (pat);
+
+ cairo_set_line_width (cairo_context, 3.5);
+ cairo_set_source_rgba (cairo_context, 0.43, 0.49, 0.55, 1);
+ cairo_stroke (cairo_context);
+
+ if (!can_composite)
+ {
+ cairo_set_operator (mask_cr, CAIRO_OPERATOR_CLEAR);
+ cairo_paint (mask_cr);
+
+ cairo_set_operator (mask_cr, CAIRO_OPERATOR_OVER);
+ cairo_set_line_width (mask_cr, 3.5);
+ cairo_set_source_rgba (mask_cr, 1, 1, 1, 1);
+ cairo_fill_preserve (mask_cr);
+ cairo_stroke (mask_cr);
+
+ gdk_window_shape_combine_mask (widget->window,
+ (GdkBitmap *) mask,
+ 0,
+ 0);
+ gdk_pixmap_unref (mask);
+ cairo_destroy (mask_cr);
+ }
+
+ cairo_destroy (cairo_context);
+
+ bubble_widget->active = TRUE;
+}
+
+void
+egg_notification_bubble_widget_show (EggNotificationBubbleWidget *bubble_widget)
+{
+ gtk_widget_show_all (GTK_WIDGET (bubble_widget));
+}
+
+void
+egg_notification_bubble_widget_hide (EggNotificationBubbleWidget *bubble_widget)
+{
+ if (bubble_widget)
+ gtk_widget_hide (GTK_WIDGET (bubble_widget));
+}
+
+EggNotificationBubbleWidget*
+egg_notification_bubble_widget_new (void)
+{
+ return g_object_new (EGG_TYPE_NOTIFICATION_BUBBLE_WIDGET,
+ "type", GTK_WINDOW_POPUP,
+ NULL);
+}
+
+static void
+egg_notification_bubble_widget_context_changed_handler (EggNotificationBubbleWidget *bubble_widget)
+{
+ pango_layout_context_changed (bubble_widget->body_layout);
+}
+
+
+static void
+egg_notification_bubble_widget_event_handler (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ EggNotificationBubbleWidget *bubble_widget;
+
+ bubble_widget = EGG_NOTIFICATION_BUBBLE_WIDGET (user_data);
+
+ switch (event->type)
+ {
+ case GDK_BUTTON_PRESS:
+ g_signal_emit (bubble_widget, egg_notification_bubble_widget_signals[NOTIFICATION_CLICKED], 0);
+ break;
+ default:
+ break;
+ }
+}
+
+GtkWidget *
+egg_notification_bubble_widget_create_button (EggNotificationBubbleWidget *bubble_widget,
+ const gchar *label)
+{
+ GtkWidget *b;
+ GtkWidget *l;
+ gchar *label_markup;
+
+ b = gtk_button_new ();
+ gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
+ gtk_container_set_border_width (GTK_CONTAINER (b), 0);
+
+ label_markup = g_markup_printf_escaped ("<span weight=\"bold\" underline=\"single\" foreground=\"blue\">%s</span>", label);
+
+ l = gtk_label_new (label_markup);
+ gtk_label_set_use_markup (GTK_LABEL (l), TRUE);
+
+ g_free (label_markup);
+
+ gtk_container_add (GTK_CONTAINER (b), l);
+
+ gtk_widget_show_all (b);
+
+ if (bubble_widget->button_hbox == NULL)
+ bubble_widget->button_hbox = gtk_hbox_new (FALSE, 0);
+
+ gtk_box_pack_end (GTK_BOX (bubble_widget->button_hbox),
+ b,
+ FALSE, FALSE,
+ 0);
+
+ return (b);
+}
+
+void
+egg_notification_bubble_widget_clear_buttons (EggNotificationBubbleWidget *bubble_widget)
+{
+ if (bubble_widget->button_hbox != NULL)
+ gtk_widget_destroy (bubble_widget->button_hbox);
+
+ bubble_widget->button_hbox = NULL;
+}
+
+void
+egg_notification_bubble_widget_set_draw_arrow (EggNotificationBubbleWidget *bubble_widget,
+ gboolean value)
+{
+ bubble_widget->draw_arrow = value;
+}
+
+
Copied: trunk/notification-daemon/themes/bubble/eggnotificationbubblewidget.h (from rev 2418, trunk/notification-daemon/src/eggnotificationbubblewidget.h)
Added: trunk/notification-daemon/themes/bubble/theme.c
===================================================================
--- trunk/notification-daemon/themes/bubble/theme.c 2006-01-11 09:13:36 UTC (rev 2420)
+++ trunk/notification-daemon/themes/bubble/theme.c 2006-01-11 09:53:24 UTC (rev 2421)
@@ -0,0 +1,65 @@
+#include <gtk/gtk.h>
+#include "eggnotificationbubblewidget.h"
+
+GtkWindow *
+create_notification(void)
+{
+ return GTK_WINDOW(egg_notification_bubble_widget_new());
+}
+
+void
+destroy_notification(GtkWindow *nw)
+{
+ gtk_widget_destroy(GTK_WIDGET(nw));
+}
+
+void
+show_notification(GtkWindow *nw)
+{
+ egg_notification_bubble_widget_show(EGG_NOTIFICATION_BUBBLE_WIDGET(nw));
+}
+
+void
+hide_notification(GtkWindow *nw)
+{
+ egg_notification_bubble_widget_hide(EGG_NOTIFICATION_BUBBLE_WIDGET(nw));
+}
+
+void
+set_notification_text(GtkWindow *nw, const char *summary, const char *body)
+{
+ egg_notification_bubble_widget_set(EGG_NOTIFICATION_BUBBLE_WIDGET(nw),
+ summary, NULL, body);
+}
+
+void
+set_notification_icon(GtkWindow *nw, GdkPixbuf *pixbuf)
+{
+ EggNotificationBubbleWidget *bubble = EGG_NOTIFICATION_BUBBLE_WIDGET(nw);
+
+ gtk_image_set_from_pixbuf(GTK_IMAGE(bubble->icon), pixbuf);
+}
+
+void
+set_notification_arrow(GtkWindow *nw, gboolean visible, int x, int y)
+{
+ egg_notification_bubble_widget_set_draw_arrow(
+ EGG_NOTIFICATION_BUBBLE_WIDGET(nw), visible);
+}
+
+void
+add_notification_action(GtkWindow *nw, const char *label, const char *key,
+ GCallback cb)
+{
+ GtkWidget *b = egg_notification_bubble_widget_create_button(
+ EGG_NOTIFICATION_BUBBLE_WIDGET(nw), label);
+
+ g_signal_connect_swapped(G_OBJECT(b), "clicked", cb, (GtkWindow *)key);
+}
+
+void
+move_notification(GtkWindow *nw, int x, int y)
+{
+ egg_notification_bubble_widget_set_pos(EGG_NOTIFICATION_BUBBLE_WIDGET(nw),
+ x, y);
+}
More information about the galago-commits
mailing list