zenity/src/notification.c

283 lines
8.8 KiB
C

/*
* notification.c
*
* Copyright (C) 2002 Sun Microsystems, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
* Authors: Glynn Foster <glynn.foster@sun.com>
*/
#include <glade/glade.h>
#include <time.h>
#include "zenity.h"
#include "eggtrayicon.h"
#include "util.h"
static EggTrayIcon *tray_icon;
static GtkWidget *icon_image;
static GtkWidget *icon_event_box;
static GtkTooltips *tooltips;
static void
set_scaled_pixbuf (GtkImage *image, GdkPixbuf *pixbuf, GtkIconSize icon_size)
{
GdkScreen *screen;
GtkSettings *settings;
int width, height, desired_width, desired_height;
GdkPixbuf *new_pixbuf;
screen = gtk_widget_get_screen (GTK_WIDGET (image));
settings = gtk_settings_get_for_screen (screen);
if (!gtk_icon_size_lookup_for_settings (settings, icon_size,
&desired_width, &desired_height))
return;
width = gdk_pixbuf_get_width (pixbuf);
height = gdk_pixbuf_get_height (pixbuf);
if (height > desired_height || width > desired_width) {
if (width * desired_height / height > desired_width)
desired_height = height * desired_width / width;
else
desired_width = width * desired_height / height;
new_pixbuf = gdk_pixbuf_scale_simple (pixbuf,
desired_width,
desired_height,
GDK_INTERP_BILINEAR);
gtk_image_set_from_pixbuf (image, new_pixbuf);
g_object_unref (new_pixbuf);
} else {
gtk_image_set_from_pixbuf (image, pixbuf);
}
}
static gboolean
zenity_notification_icon_press_callback (GtkWidget *widget, GdkEventButton *event, gpointer data)
{
ZenityData *zen_data;
zen_data = data;
zen_data->exit_code = zenity_util_return_exit_code (ZENITY_OK);
gtk_main_quit ();
return TRUE;
}
static gboolean
zenity_notification_icon_expose_callback (GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
if (GTK_WIDGET_HAS_FOCUS (widget)) {
gint focus_width, focus_pad;
gint x, y, width, height;
gtk_widget_style_get (widget,
"focus-line-width", &focus_width,
"focus-padding", &focus_pad,
NULL);
x = widget->allocation.x + focus_pad;
y = widget->allocation.y + focus_pad;
width = widget->allocation.width - 2 * focus_pad;
height = widget->allocation.height - 2 * focus_pad;
gtk_paint_focus (widget->style, widget->window,
GTK_WIDGET_STATE (widget),
&event->area, widget, "button",
x, y, width, height);
}
return FALSE;
}
static void
zenity_notification_icon_destroy_callback (GtkWidget *widget, gpointer data)
{
ZenityData *zen_data;
zen_data = data;
gtk_widget_destroy (GTK_WIDGET (tray_icon));
zen_data->exit_code = zenity_util_return_exit_code (ZENITY_ESC);
gtk_main_quit ();
}
static gboolean
zenity_notification_handle_stdin (GIOChannel *channel,
GIOCondition condition,
gpointer user_data)
{
ZenityData *zen_data;
zen_data = (ZenityData *)user_data;
if ((condition & G_IO_IN) != 0) {
GString *string;
GError *error = NULL;
string = g_string_new (NULL);
while (channel->is_readable != TRUE)
;
do {
gint status;
gchar *command, *value, *colon;
do {
status = g_io_channel_read_line_string (channel, string, NULL, &error);
while (gdk_events_pending ())
gtk_main_iteration ();
} while (status == G_IO_STATUS_AGAIN);
if (status != G_IO_STATUS_NORMAL) {
if (error) {
g_warning ("zenity_notification_handle_stdin () : %s",
error->message);
g_error_free (error);
error = NULL;
}
continue;
}
zenity_util_strip_newline (string->str);
colon = strchr(string->str, ':');
if (colon == NULL) {
g_printerr (_("could not parse command from stdin\n"));
continue;
}
/* split off the command and value */
command = g_strndup (string->str, colon - string->str);
command = g_strstrip (command);
g_strdown (command);
value = colon + 1;
while (*value && g_ascii_isspace (*value)) value++;
if (!strcmp (command, "icon")) {
GdkPixbuf *pixbuf;
pixbuf = zenity_util_pixbuf_new_from_file (GTK_WIDGET (tray_icon),
value);
if (pixbuf != NULL) {
set_scaled_pixbuf (GTK_IMAGE (icon_image), pixbuf,
GTK_ICON_SIZE_BUTTON);
gdk_pixbuf_unref (pixbuf);
} else {
g_warning ("Could not load notification icon : %s", value);
}
} else if (!strcmp (command, "message")) {
g_warning ("haven't implemented message support yet");
} else if (!strcmp (command, "tooltip")) {
gtk_tooltips_set_tip (tooltips, icon_event_box, value, value);
} else if (!strcmp (command, "visible")) {
if (!strcasecmp (value, "false")) {
/* We need to get the parent, because just hiding the tray_icon
* doesn't save on space. See #161539 for details
*/
gtk_widget_hide (gtk_widget_get_parent (GTK_WIDGET (tray_icon)));
} else {
gtk_widget_show (gtk_widget_get_parent (GTK_WIDGET (tray_icon)));
}
} else {
g_warning ("Unknown command '%s'", command);
}
g_free (command);
} while (g_io_channel_get_buffer_condition (channel) == G_IO_IN);
g_string_free (string, TRUE);
}
if ((condition & G_IO_HUP) != 0) {
g_io_channel_shutdown (channel, TRUE, NULL);
zen_data->exit_code = zenity_util_return_exit_code (ZENITY_OK);
gtk_main_quit ();
return FALSE;
}
return TRUE;
}
static void
zenity_notification_listen_on_stdin (ZenityData *data)
{
GIOChannel *channel;
channel = g_io_channel_unix_new (0);
g_io_channel_set_encoding (channel, NULL, NULL);
g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL);
g_io_add_watch (channel, G_IO_IN | G_IO_HUP,
zenity_notification_handle_stdin, data);
}
void
zenity_notification (ZenityData *data, ZenityNotificationData *notification_data)
{
GdkPixbuf *pixbuf = NULL;
tray_icon = egg_tray_icon_new (_("Zenity notification"));
tooltips = gtk_tooltips_new ();
if (data->window_icon != NULL)
pixbuf = zenity_util_pixbuf_new_from_file (GTK_WIDGET (tray_icon), data->window_icon);
else
pixbuf = gdk_pixbuf_new_from_file (ZENITY_IMAGE_FULLPATH ("zenity-notification.png"), NULL);
icon_event_box = gtk_event_box_new ();
icon_image = gtk_image_new ();
if (pixbuf) {
set_scaled_pixbuf (GTK_IMAGE (icon_image), pixbuf,
GTK_ICON_SIZE_BUTTON);
gdk_pixbuf_unref (pixbuf);
} else {
if (data->window_icon != NULL) {
g_warning ("Could not load notification icon : %s", data->window_icon);
}
else
g_warning ("Could not load notification icon : %s", ZENITY_IMAGE_FULLPATH ("zenity-notification.png"));
return;
}
gtk_container_add (GTK_CONTAINER (icon_event_box), icon_image);
if (notification_data->notification_text)
gtk_tooltips_set_tip (tooltips, icon_event_box, notification_data->notification_text, notification_data->notification_text);
else
gtk_tooltips_set_tip (tooltips, icon_event_box, _("Zenity notification"), _("Zenity notification"));
gtk_widget_add_events (GTK_WIDGET (tray_icon), GDK_BUTTON_PRESS_MASK | GDK_FOCUS_CHANGE_MASK);
gtk_container_add (GTK_CONTAINER (tray_icon), icon_event_box);
g_signal_connect (tray_icon, "destroy",
G_CALLBACK (zenity_notification_icon_destroy_callback), data);
g_signal_connect (tray_icon, "expose_event",
G_CALLBACK (zenity_notification_icon_expose_callback), data);
if (notification_data->listen) {
zenity_notification_listen_on_stdin (data);
} else {
/* if we aren't listening for changes, then close on button_press */
g_signal_connect (tray_icon, "button_press_event",
G_CALLBACK (zenity_notification_icon_press_callback), data);
}
gtk_widget_show_all (GTK_WIDGET (tray_icon));
/* Does nothing at the moment */
gtk_main ();
}