diff --git a/ChangeLog b/ChangeLog index 3801b06..aaa250b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2004-09-13 Glynn Foster + + * data/Makefile.am, data/zenity-notification.png: Add new + notification icon. + * src/Makefile.am: Update for new files. + * src/about.c, src/calendar.c, src/entry.c, src/fileselection.c, + src/progress.c, src/text.c, src/tree.c, src/msg.c: Restructure code a + little bit for new utility functions for setting window icons. + * src/eggtrayicon.c, src/eggtrayicon.h: New files for notification area + support. + * src/main.c, src/notification.c, src/util.c, src/util.h, src/zenity.h: + Add support for notification area. + * data/zenity.1, help/*: Update docs for notification and new file + selection changes. + 2004-09-13 Glynn Foster * THANKS: Update diff --git a/data/Makefile.am b/data/Makefile.am index aade652..fd71b4e 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -14,7 +14,8 @@ images_DATA = \ zenity-file.png \ zenity-progress.png \ zenity-text.png \ - zenity-entry.png + zenity-entry.png \ + zenity-notification.png man_MANS = zenity.1 diff --git a/data/zenity-notification.png b/data/zenity-notification.png new file mode 100644 index 0000000..6b44ef9 Binary files /dev/null and b/data/zenity-notification.png differ diff --git a/data/zenity.1 b/data/zenity.1 index 17f6eab..3949899 100644 --- a/data/zenity.1 +++ b/data/zenity.1 @@ -43,6 +43,9 @@ Display info dialog .B \-\-list Display list dialog .TP +.B \-\-notification +Display notification icon +.TP .B \-\-progress Display progress indication dialog .TP @@ -152,6 +155,13 @@ Allow changes to text Specify what column to print to standard output. The default is to return the first column. 'ALL' may be used to print all columns. +.PP +Notification options + +.TP +.B \-\-text=STRING +Set the notification text + .PP Progress options @@ -230,6 +240,10 @@ and the text \fIFinding all header files...\fP. .IP find . -name '*.h' | zenity --title "Search Results" --text "Finding all header files.." --column "Files" .PP +Show an icon in the notification area +.IP +zenity --notification --window-icon=update.png --text "System update necessary!" +.PP Display a weekly shopping list in a check list dialog with \fIApples\fP and \fIOranges\fP pre selected .IP zenity --list --checklist --column "Buy" --column "Item" TRUE Apples TRUE Oranges FALSE Pears FALSE Toothpaste diff --git a/help/C/figures/zenity-notification-screenshot.png b/help/C/figures/zenity-notification-screenshot.png new file mode 100644 index 0000000..c1b2c9a Binary files /dev/null and b/help/C/figures/zenity-notification-screenshot.png differ diff --git a/help/C/l10n_scripts/notification.sh b/help/C/l10n_scripts/notification.sh new file mode 100755 index 0000000..b7de9ab --- /dev/null +++ b/help/C/l10n_scripts/notification.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +/gnome/head/cvs/zenity/src/zenity --notification \ +--window-icon="info" \ +--text="There are system updates necessary!" diff --git a/help/C/zenity.xml b/help/C/zenity.xml index 9d51f70..749a741 100644 --- a/help/C/zenity.xml +++ b/help/C/zenity.xml @@ -115,6 +115,7 @@ Calendar File selection List + Notification Message Error @@ -248,7 +249,10 @@ =icon_path - Specifies the icon that is displayed in the window frame of the dialog. + Specifies the icon that is displayed in the window frame of the dialog. There are + 4 stock icons also available by providing the following keywords - 'info', 'warning', 'question' and + 'error'. + @@ -435,7 +439,8 @@ File Selection Dialog - Use the option to create a file selection dialog. &app; returns the selected files or directories to standard error. + Use the option to create a file selection dialog. &app; returns the selected files or directories to standard + error. The default mode of the file selection dialog is open. The file selection dialog supports the following options: @@ -457,6 +462,20 @@ + + + + Allows only selection of directories in the file selection dialog. + + + + + + + Set the file selection dialog into save mode. + + + =separator @@ -501,6 +520,53 @@ + + + Notification Icon + + + + + + + + =text + + Specifies the text that is displayed in the notification area. + + + + + + + + The following example script shows how to create a notification icon: + + #!/bin/sh + + zenity --notification\ + --window-icon="info" \ + --text="There are system updates necessary!" + + + + +
+ Notification Icon Example + + + + + + + &app; notification icon example + + + +
+ +
+ diff --git a/src/Makefile.am b/src/Makefile.am index c5c9fcd..310cb56 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -12,6 +12,9 @@ zenity_SOURCES = \ text.c \ progress.c \ tree.c \ + notification.c \ + eggtrayicon.c \ + eggtrayicon.h \ about.c \ util.h \ util.c diff --git a/src/about.c b/src/about.c index 60cb570..d6fc908 100644 --- a/src/about.c +++ b/src/about.c @@ -300,7 +300,7 @@ zenity_create_boutique (void) window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - zenity_util_set_window_icon (window, ZENITY_IMAGE_FULLPATH ("zenity.png")); + zenity_util_set_window_icon (window, NULL, ZENITY_IMAGE_FULLPATH ("zenity.png")); canvas = zenity_create_monk (); gtk_container_add (GTK_CONTAINER (window), canvas); @@ -383,7 +383,7 @@ zenity_about (ZenityData *data) g_signal_connect (G_OBJECT (dialog), "key_press_event", G_CALLBACK (zenity_zen_wisdom), glade_dialog); - zenity_util_set_window_icon (dialog, ZENITY_IMAGE_FULLPATH ("zenity.png")); + zenity_util_set_window_icon (dialog, NULL, ZENITY_IMAGE_FULLPATH ("zenity.png")); image = glade_xml_get_widget (glade_dialog, "zenity_about_image"); diff --git a/src/calendar.c b/src/calendar.c index 5936aef..9da633e 100644 --- a/src/calendar.c +++ b/src/calendar.c @@ -58,10 +58,7 @@ zenity_calendar (ZenityData *data, ZenityCalendarData *cal_data) if (data->dialog_title) gtk_window_set_title (GTK_WINDOW (dialog), data->dialog_title); - if (data->window_icon) - zenity_util_set_window_icon (dialog, data->window_icon); - else - zenity_util_set_window_icon (dialog, ZENITY_IMAGE_FULLPATH ("zenity-calendar.png")); + zenity_util_set_window_icon (dialog, data->window_icon, ZENITY_IMAGE_FULLPATH ("zenity-calendar.png")); if (data->width > -1 || data->height > -1) gtk_window_set_default_size (GTK_WINDOW (dialog), data->width, data->height); diff --git a/src/eggtrayicon.c b/src/eggtrayicon.c new file mode 100644 index 0000000..c4aa3e6 --- /dev/null +++ b/src/eggtrayicon.c @@ -0,0 +1,468 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* eggtrayicon.c + * Copyright (C) 2002 Anders Carlsson + * + * 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 +#include +#include + +#include "eggtrayicon.h" + +#include +#include + +#ifndef EGG_COMPILATION +#ifndef _ +#define _(x) dgettext (GETTEXT_PACKAGE, x) +#define N_(x) x +#endif +#else +#define _(x) x +#define N_(x) x +#endif + +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 + +#define SYSTEM_TRAY_ORIENTATION_HORZ 0 +#define SYSTEM_TRAY_ORIENTATION_VERT 1 + +enum { + PROP_0, + PROP_ORIENTATION +}; + +static GtkPlugClass *parent_class = NULL; + +static void egg_tray_icon_init (EggTrayIcon *icon); +static void egg_tray_icon_class_init (EggTrayIconClass *klass); + +static void egg_tray_icon_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static void egg_tray_icon_realize (GtkWidget *widget); +static void egg_tray_icon_unrealize (GtkWidget *widget); + +static void egg_tray_icon_update_manager_window (EggTrayIcon *icon); + +GType +egg_tray_icon_get_type (void) +{ + static GType our_type = 0; + + if (our_type == 0) + { + static const GTypeInfo our_info = + { + sizeof (EggTrayIconClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) egg_tray_icon_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (EggTrayIcon), + 0, /* n_preallocs */ + (GInstanceInitFunc) egg_tray_icon_init + }; + + our_type = g_type_register_static (GTK_TYPE_PLUG, "EggTrayIcon", &our_info, 0); + } + + return our_type; +} + +static void +egg_tray_icon_init (EggTrayIcon *icon) +{ + icon->stamp = 1; + icon->orientation = GTK_ORIENTATION_HORIZONTAL; + + gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK); +} + +static void +egg_tray_icon_class_init (EggTrayIconClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass *)klass; + GtkWidgetClass *widget_class = (GtkWidgetClass *)klass; + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->get_property = egg_tray_icon_get_property; + + widget_class->realize = egg_tray_icon_realize; + widget_class->unrealize = egg_tray_icon_unrealize; + + g_object_class_install_property (gobject_class, + PROP_ORIENTATION, + g_param_spec_enum ("orientation", + _("Orientation"), + _("The orientation of the tray."), + GTK_TYPE_ORIENTATION, + GTK_ORIENTATION_HORIZONTAL, + G_PARAM_READABLE)); +} + +static void +egg_tray_icon_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + EggTrayIcon *icon = EGG_TRAY_ICON (object); + + switch (prop_id) + { + case PROP_ORIENTATION: + g_value_set_enum (value, icon->orientation); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +egg_tray_icon_get_orientation_property (EggTrayIcon *icon) +{ + Display *xdisplay; + Atom type; + int format; + union { + gulong *prop; + guchar *prop_ch; + } prop = { NULL }; + gulong nitems; + gulong bytes_after; + int error, result; + + g_assert (icon->manager_window != None); + + xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon))); + + gdk_error_trap_push (); + type = None; + result = XGetWindowProperty (xdisplay, + icon->manager_window, + icon->orientation_atom, + 0, G_MAXLONG, FALSE, + XA_CARDINAL, + &type, &format, &nitems, + &bytes_after, &(prop.prop_ch)); + error = gdk_error_trap_pop (); + + if (error || result != Success) + return; + + if (type == XA_CARDINAL) + { + GtkOrientation orientation; + + orientation = (prop.prop [0] == SYSTEM_TRAY_ORIENTATION_HORZ) ? + GTK_ORIENTATION_HORIZONTAL : + GTK_ORIENTATION_VERTICAL; + + if (icon->orientation != orientation) + { + icon->orientation = orientation; + + g_object_notify (G_OBJECT (icon), "orientation"); + } + } + + if (prop.prop) + XFree (prop.prop); +} + +static GdkFilterReturn +egg_tray_icon_manager_filter (GdkXEvent *xevent, GdkEvent *event, gpointer user_data) +{ + EggTrayIcon *icon = user_data; + XEvent *xev = (XEvent *)xevent; + + if (xev->xany.type == ClientMessage && + xev->xclient.message_type == icon->manager_atom && + xev->xclient.data.l[1] == icon->selection_atom) + { + egg_tray_icon_update_manager_window (icon); + } + else if (xev->xany.window == icon->manager_window) + { + if (xev->xany.type == PropertyNotify && + xev->xproperty.atom == icon->orientation_atom) + { + egg_tray_icon_get_orientation_property (icon); + } + if (xev->xany.type == DestroyNotify) + { + egg_tray_icon_update_manager_window (icon); + } + } + + return GDK_FILTER_CONTINUE; +} + +static void +egg_tray_icon_unrealize (GtkWidget *widget) +{ + EggTrayIcon *icon = EGG_TRAY_ICON (widget); + GdkWindow *root_window; + + if (icon->manager_window != None) + { + GdkWindow *gdkwin; + + gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (widget), + icon->manager_window); + + gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon); + } + + root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget)); + + gdk_window_remove_filter (root_window, egg_tray_icon_manager_filter, icon); + + if (GTK_WIDGET_CLASS (parent_class)->unrealize) + (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget); +} + +static void +egg_tray_icon_send_manager_message (EggTrayIcon *icon, + long message, + Window window, + long data1, + long data2, + long data3) +{ + XClientMessageEvent ev; + Display *display; + + ev.type = ClientMessage; + ev.window = window; + ev.message_type = icon->system_tray_opcode_atom; + ev.format = 32; + ev.data.l[0] = gdk_x11_get_server_time (GTK_WIDGET (icon)->window); + ev.data.l[1] = message; + ev.data.l[2] = data1; + ev.data.l[3] = data2; + ev.data.l[4] = data3; + + display = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon))); + + gdk_error_trap_push (); + XSendEvent (display, + icon->manager_window, False, NoEventMask, (XEvent *)&ev); + XSync (display, False); + gdk_error_trap_pop (); +} + +static void +egg_tray_icon_send_dock_request (EggTrayIcon *icon) +{ + egg_tray_icon_send_manager_message (icon, + SYSTEM_TRAY_REQUEST_DOCK, + icon->manager_window, + gtk_plug_get_id (GTK_PLUG (icon)), + 0, 0); +} + +static void +egg_tray_icon_update_manager_window (EggTrayIcon *icon) +{ + Display *xdisplay; + + xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon))); + + if (icon->manager_window != None) + { + GdkWindow *gdkwin; + + gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)), + icon->manager_window); + + gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon); + } + + XGrabServer (xdisplay); + + icon->manager_window = XGetSelectionOwner (xdisplay, + icon->selection_atom); + + if (icon->manager_window != None) + XSelectInput (xdisplay, + icon->manager_window, StructureNotifyMask|PropertyChangeMask); + + XUngrabServer (xdisplay); + XFlush (xdisplay); + + if (icon->manager_window != None) + { + GdkWindow *gdkwin; + + gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)), + icon->manager_window); + + gdk_window_add_filter (gdkwin, egg_tray_icon_manager_filter, icon); + + /* Send a request that we'd like to dock */ + egg_tray_icon_send_dock_request (icon); + + egg_tray_icon_get_orientation_property (icon); + } +} + +static void +egg_tray_icon_realize (GtkWidget *widget) +{ + EggTrayIcon *icon = EGG_TRAY_ICON (widget); + GdkScreen *screen; + GdkDisplay *display; + Display *xdisplay; + char buffer[256]; + GdkWindow *root_window; + + if (GTK_WIDGET_CLASS (parent_class)->realize) + GTK_WIDGET_CLASS (parent_class)->realize (widget); + + screen = gtk_widget_get_screen (widget); + display = gdk_screen_get_display (screen); + xdisplay = gdk_x11_display_get_xdisplay (display); + + /* Now see if there's a manager window around */ + g_snprintf (buffer, sizeof (buffer), + "_NET_SYSTEM_TRAY_S%d", + gdk_screen_get_number (screen)); + + icon->selection_atom = XInternAtom (xdisplay, buffer, False); + + icon->manager_atom = XInternAtom (xdisplay, "MANAGER", False); + + icon->system_tray_opcode_atom = XInternAtom (xdisplay, + "_NET_SYSTEM_TRAY_OPCODE", + False); + + icon->orientation_atom = XInternAtom (xdisplay, + "_NET_SYSTEM_TRAY_ORIENTATION", + False); + + egg_tray_icon_update_manager_window (icon); + + root_window = gdk_screen_get_root_window (screen); + + /* Add a root window filter so that we get changes on MANAGER */ + gdk_window_add_filter (root_window, + egg_tray_icon_manager_filter, icon); +} + +EggTrayIcon * +egg_tray_icon_new_for_screen (GdkScreen *screen, const char *name) +{ + g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL); + + return g_object_new (EGG_TYPE_TRAY_ICON, "screen", screen, "title", name, NULL); +} + +EggTrayIcon* +egg_tray_icon_new (const gchar *name) +{ + return g_object_new (EGG_TYPE_TRAY_ICON, "title", name, NULL); +} + +guint +egg_tray_icon_send_message (EggTrayIcon *icon, + gint timeout, + const gchar *message, + gint len) +{ + guint stamp; + + g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), 0); + g_return_val_if_fail (timeout >= 0, 0); + g_return_val_if_fail (message != NULL, 0); + + if (icon->manager_window == None) + return 0; + + if (len < 0) + len = strlen (message); + + stamp = icon->stamp++; + + /* Get ready to send the message */ + egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_BEGIN_MESSAGE, + (Window)gtk_plug_get_id (GTK_PLUG (icon)), + timeout, len, stamp); + + /* Now to send the actual message */ + gdk_error_trap_push (); + while (len > 0) + { + XClientMessageEvent ev; + Display *xdisplay; + + xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon))); + + ev.type = ClientMessage; + ev.window = (Window)gtk_plug_get_id (GTK_PLUG (icon)); + ev.format = 8; + ev.message_type = XInternAtom (xdisplay, + "_NET_SYSTEM_TRAY_MESSAGE_DATA", False); + if (len > 20) + { + memcpy (&ev.data, message, 20); + len -= 20; + message += 20; + } + else + { + memcpy (&ev.data, message, len); + len = 0; + } + + XSendEvent (xdisplay, + icon->manager_window, False, StructureNotifyMask, (XEvent *)&ev); + XSync (xdisplay, False); + } + gdk_error_trap_pop (); + + return stamp; +} + +void +egg_tray_icon_cancel_message (EggTrayIcon *icon, + guint id) +{ + g_return_if_fail (EGG_IS_TRAY_ICON (icon)); + g_return_if_fail (id > 0); + + egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_CANCEL_MESSAGE, + (Window)gtk_plug_get_id (GTK_PLUG (icon)), + id, 0, 0); +} + +GtkOrientation +egg_tray_icon_get_orientation (EggTrayIcon *icon) +{ + g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), GTK_ORIENTATION_HORIZONTAL); + + return icon->orientation; +} diff --git a/src/eggtrayicon.h b/src/eggtrayicon.h new file mode 100644 index 0000000..007f4c1 --- /dev/null +++ b/src/eggtrayicon.h @@ -0,0 +1,77 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* eggtrayicon.h + * Copyright (C) 2002 Anders Carlsson + * + * 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_TRAY_ICON_H__ +#define __EGG_TRAY_ICON_H__ + +#include +#include + +G_BEGIN_DECLS + +#define EGG_TYPE_TRAY_ICON (egg_tray_icon_get_type ()) +#define EGG_TRAY_ICON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_TRAY_ICON, EggTrayIcon)) +#define EGG_TRAY_ICON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_TRAY_ICON, EggTrayIconClass)) +#define EGG_IS_TRAY_ICON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_TRAY_ICON)) +#define EGG_IS_TRAY_ICON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_TRAY_ICON)) +#define EGG_TRAY_ICON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_TRAY_ICON, EggTrayIconClass)) + +typedef struct _EggTrayIcon EggTrayIcon; +typedef struct _EggTrayIconClass EggTrayIconClass; + +struct _EggTrayIcon +{ + GtkPlug parent_instance; + + guint stamp; + + Atom selection_atom; + Atom manager_atom; + Atom system_tray_opcode_atom; + Atom orientation_atom; + Window manager_window; + + GtkOrientation orientation; +}; + +struct _EggTrayIconClass +{ + GtkPlugClass parent_class; +}; + +GType egg_tray_icon_get_type (void); + +EggTrayIcon *egg_tray_icon_new_for_screen (GdkScreen *screen, + const gchar *name); + +EggTrayIcon *egg_tray_icon_new (const gchar *name); + +guint egg_tray_icon_send_message (EggTrayIcon *icon, + gint timeout, + const char *message, + gint len); +void egg_tray_icon_cancel_message (EggTrayIcon *icon, + guint id); + +GtkOrientation egg_tray_icon_get_orientation (EggTrayIcon *icon); + +G_END_DECLS + +#endif /* __EGG_TRAY_ICON_H__ */ diff --git a/src/entry.c b/src/entry.c index 4d24216..46672f8 100644 --- a/src/entry.c +++ b/src/entry.c @@ -54,10 +54,7 @@ zenity_entry (ZenityData *data, ZenityEntryData *entry_data) if (data->dialog_title) gtk_window_set_title (GTK_WINDOW (dialog), data->dialog_title); - if (data->window_icon) - zenity_util_set_window_icon (dialog, data->window_icon); - else - zenity_util_set_window_icon (dialog, ZENITY_IMAGE_FULLPATH ("zenity-entry.png")); + zenity_util_set_window_icon (dialog, data->window_icon, ZENITY_IMAGE_FULLPATH ("zenity-entry.png")); if (data->width > -1 || data->height > -1) gtk_window_set_default_size (GTK_WINDOW (dialog), data->width, data->height); diff --git a/src/fileselection.c b/src/fileselection.c index fdd40fd..4d50fb7 100644 --- a/src/fileselection.c +++ b/src/fileselection.c @@ -60,10 +60,7 @@ void zenity_fileselection (ZenityData *data, ZenityFileData *file_data) if (data->dialog_title) gtk_window_set_title (GTK_WINDOW (dialog), data->dialog_title); - if (data->window_icon) - zenity_util_set_window_icon (dialog, data->window_icon); - else - zenity_util_set_window_icon (dialog, ZENITY_IMAGE_FULLPATH ("zenity-file.png")); + zenity_util_set_window_icon (dialog, data->window_icon, ZENITY_IMAGE_FULLPATH ("zenity-file.png")); if (file_data->uri) { dir = g_path_get_dirname (file_data->uri); diff --git a/src/main.c b/src/main.c index 09b1519..4d8c607 100644 --- a/src/main.c +++ b/src/main.c @@ -42,6 +42,7 @@ typedef enum { MODE_TEXTINFO, MODE_WARNING, MODE_INFO, + MODE_NOTIFICATION, MODE_ABOUT, MODE_LAST } ZenityDialogMode; @@ -64,6 +65,7 @@ typedef struct { ZenityProgressData *progress_data; ZenityTextData *text_data; ZenityTreeData *tree_data; + ZenityNotificationData *notification_data; } ZenityParsingOptions; enum { @@ -79,6 +81,7 @@ enum { OPTION_TEXTINFO, OPTION_TEXTEDIT, OPTION_WARNING, + OPTION_NOTIFICATION, OPTION_TITLE, OPTION_ICON, OPTION_WIDTH, @@ -110,6 +113,8 @@ enum { OPTION_PRINTCOLUMN, OPTION_QUESTIONTEXT, OPTION_WARNINGTEXT, + OPTION_NOTIFICATIONICON, + OPTION_NOTIFICATIONTEXT, OPTION_ABOUT, OPTION_VERSION, OPTION_LAST, @@ -185,6 +190,15 @@ struct poptOption options[] = { N_("Display list dialog"), NULL }, + { + "notification", + '\0', + POPT_ARG_NONE, + NULL, + OPTION_NOTIFICATION, + N_("Display notification"), + NULL + }, { "progress", '\0', @@ -548,6 +562,28 @@ struct poptOption list_options[] = { POPT_TABLEEND }; +struct poptOption notification_options[] = { + { + NULL, + '\0', + POPT_ARG_CALLBACK | POPT_CBFLAG_POST, + zenity_parse_options_callback, + 0, + NULL, + NULL + }, + { + "text", + '\0', + POPT_ARG_STRING, + NULL, + OPTION_NOTIFICATIONTEXT, + N_("Set the notification text"), + NULL + }, + POPT_TABLEEND +}; + struct poptOption progress_options[] = { { NULL, @@ -896,6 +932,15 @@ struct poptOption application_options[] = { N_("List options"), NULL }, + { + NULL, + '\0', + POPT_ARG_INCLUDE_TABLE, + notification_options, + 0, + N_("Notication options"), + NULL + }, { NULL, '\0', @@ -979,6 +1024,7 @@ zenity_init_parsing_options (void) { results->progress_data = g_new0 (ZenityProgressData, 1); results->text_data = g_new0 (ZenityTextData, 1); results->tree_data = g_new0 (ZenityTreeData, 1); + results->notification_data = g_new0 (ZenityNotificationData, 1); /* Give some sensible defaults */ results->data->width = -1; @@ -1056,6 +1102,10 @@ zenity_free_parsing_options (void) { if (results->tree_data->print_column) g_free (results->tree_data->print_column); break; + case MODE_NOTIFICATION: + if (results->notification_data->notification_text) + g_free (results->notification_data->notification_text); + break; default: break; } @@ -1125,6 +1175,9 @@ main (gint argc, gchar **argv) { results->tree_data->data = poptGetArgs (ctx); zenity_tree (results->data, results->tree_data); break; + case MODE_NOTIFICATION: + zenity_notification (results->data, results->notification_data); + break; case MODE_PROGRESS: zenity_progress (results->data, results->progress_data); break; @@ -1229,6 +1282,12 @@ zenity_parse_options_callback (poptContext ctx, results->mode = MODE_LIST; break; + case OPTION_NOTIFICATION: + if (results->mode != MODE_LAST) + zenity_error (NULL, ERROR_DIALOG); + + results->mode = MODE_NOTIFICATION; + break; case OPTION_PROGRESS: if (results->mode != MODE_LAST) zenity_error (NULL, ERROR_DIALOG); @@ -1286,13 +1345,13 @@ zenity_parse_options_callback (poptContext ctx, case OPTION_PROGRESSTEXT: case OPTION_LISTTEXT: case OPTION_WARNINGTEXT: - + case OPTION_NOTIFICATIONTEXT: /* FIXME: This is an ugly hack because of the way the poptOptions are * ordered above. When you try and use an --option more than once * parse_options_callback gets called for each option. Suckage */ - if (parse_option_text > 7) + if (parse_option_text > 8) zenity_error ("--text", ERROR_DUPLICATE); switch (results->mode) { @@ -1319,6 +1378,10 @@ zenity_parse_options_callback (poptContext ctx, results->tree_data->dialog_text = g_locale_to_utf8 (g_strcompress (arg), -1, NULL, NULL, NULL); break; + case MODE_NOTIFICATION: + results->notification_data->notification_text = g_locale_to_utf8 (g_strcompress (arg), + -1, NULL, NULL, NULL); + break; default: zenity_error ("--text", ERROR_SUPPORT); } diff --git a/src/msg.c b/src/msg.c index 2728447..ffb2e53 100644 --- a/src/msg.c +++ b/src/msg.c @@ -83,29 +83,25 @@ zenity_msg (ZenityData *data, ZenityMsgData *msg_data) if (data->dialog_title) gtk_window_set_title (GTK_WINDOW (dialog), data->dialog_title); - if (data->window_icon) - zenity_util_set_window_icon (dialog, data->window_icon); - else { - switch (msg_data->mode) { - case ZENITY_MSG_WARNING: - zenity_util_set_window_icon_from_stock (dialog, GTK_STOCK_DIALOG_WARNING); - break; + switch (msg_data->mode) { + case ZENITY_MSG_WARNING: + zenity_util_set_window_icon_from_stock (dialog, data->window_icon, GTK_STOCK_DIALOG_WARNING); + break; - case ZENITY_MSG_QUESTION: - zenity_util_set_window_icon_from_stock (dialog, GTK_STOCK_DIALOG_QUESTION); - break; + case ZENITY_MSG_QUESTION: + zenity_util_set_window_icon_from_stock (dialog, data->window_icon, GTK_STOCK_DIALOG_QUESTION); + break; - case ZENITY_MSG_ERROR: - zenity_util_set_window_icon_from_stock (dialog, GTK_STOCK_DIALOG_ERROR); - break; + case ZENITY_MSG_ERROR: + zenity_util_set_window_icon_from_stock (dialog, data->window_icon, GTK_STOCK_DIALOG_ERROR); + break; - case ZENITY_MSG_INFO: - zenity_util_set_window_icon_from_stock (dialog, GTK_STOCK_DIALOG_INFO); - break; + case ZENITY_MSG_INFO: + zenity_util_set_window_icon_from_stock (dialog, data->window_icon, GTK_STOCK_DIALOG_INFO); + break; - default: - break; - } + default: + break; } if (data->width > -1 || data->height > -1) diff --git a/src/notification.c b/src/notification.c new file mode 100644 index 0000000..42f72c5 --- /dev/null +++ b/src/notification.c @@ -0,0 +1,129 @@ +/* + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Glynn Foster + */ + +#include +#include +#include "zenity.h" +#include "eggtrayicon.h" +#include "util.h" + +EggTrayIcon *tray_icon; + +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 (); +} + +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 gboolean +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 (); +} + +void +zenity_notification (ZenityData *data, ZenityNotificationData *notification_data) +{ + GtkWidget *icon_image; + GtkWidget *icon_event_box; + GtkTooltips *tooltips; + 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 (); + + if (pixbuf) { + icon_image = gtk_image_new_from_pixbuf (pixbuf); + gdk_pixbuf_unref (pixbuf); + } 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, "button_press_event", + G_CALLBACK (zenity_notification_icon_press_callback), data); + + 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); + + gtk_widget_show_all (GTK_WIDGET (tray_icon)); + + /* Does nothing at the moment */ + gtk_main (); +} diff --git a/src/progress.c b/src/progress.c index 373c5b8..ed727ca 100644 --- a/src/progress.c +++ b/src/progress.c @@ -191,10 +191,7 @@ zenity_progress (ZenityData *data, ZenityProgressData *progress_data) if (data->dialog_title) gtk_window_set_title (GTK_WINDOW (dialog), data->dialog_title); - if (data->window_icon) - zenity_util_set_window_icon (dialog, data->window_icon); - else - zenity_util_set_window_icon (dialog, ZENITY_IMAGE_FULLPATH ("zenity-progress.png")); + zenity_util_set_window_icon (dialog, data->window_icon, ZENITY_IMAGE_FULLPATH ("zenity-progress.png")); if (data->width > -1 || data->height > -1) gtk_window_set_default_size (GTK_WINDOW (dialog), data->width, data->height); diff --git a/src/text.c b/src/text.c index b70bed0..acd1739 100644 --- a/src/text.c +++ b/src/text.c @@ -128,10 +128,7 @@ zenity_text (ZenityData *data, ZenityTextData *text_data) if (data->dialog_title) gtk_window_set_title (GTK_WINDOW (dialog), data->dialog_title); - if (data->window_icon) - zenity_util_set_window_icon (dialog, data->window_icon); - else - zenity_util_set_window_icon (dialog, ZENITY_IMAGE_FULLPATH ("zenity-text.png")); + zenity_util_set_window_icon (dialog, data->window_icon, ZENITY_IMAGE_FULLPATH ("zenity-text.png")); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE); diff --git a/src/tree.c b/src/tree.c index fed58e2..436f632 100644 --- a/src/tree.c +++ b/src/tree.c @@ -320,10 +320,7 @@ zenity_tree (ZenityData *data, ZenityTreeData *tree_data) if (tree_data->dialog_text) gtk_label_set_text (GTK_LABEL (text), tree_data->dialog_text); - if (data->window_icon) - zenity_util_set_window_icon (dialog, data->window_icon); - else - zenity_util_set_window_icon (dialog, ZENITY_IMAGE_FULLPATH ("zenity-list.png")); + zenity_util_set_window_icon (dialog, data->window_icon, ZENITY_IMAGE_FULLPATH ("zenity-list.png")); if (data->width > -1 || data->height > -1) gtk_window_set_default_size (GTK_WINDOW (dialog), data->width, data->height); diff --git a/src/util.c b/src/util.c index 3364e30..b839d42 100644 --- a/src/util.c +++ b/src/util.c @@ -143,56 +143,53 @@ zenity_util_fill_file_buffer (GtkTextBuffer *buffer, const gchar *filename) return TRUE; } -static GList * -zenity_util_list_from_char_array (const char **s) +GdkPixbuf * +zenity_util_pixbuf_new_from_file (GtkWidget *widget, gchar *filename) { - GList *list = NULL; - gint i; - - for (i = 0; s[i]; i++) { - GdkPixbuf *pixbuf; - - pixbuf = gdk_pixbuf_new_from_file (s[i], NULL); - if (pixbuf) - list = g_list_prepend (list, pixbuf); - } - - return list; -} - -static void -zenity_util_free_list (GList *list) -{ - g_list_foreach (list, (GFunc) g_object_unref, NULL); - g_list_free (list); + if (!strcmp (g_strdown (filename), "warning")) + return gtk_widget_render_icon (widget, GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_BUTTON, NULL); + if (!strcmp (g_strdown (filename), "info")) + return gtk_widget_render_icon (widget, GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_BUTTON, NULL); + if (!strcmp (g_strdown (filename), "question")) + return gtk_widget_render_icon (widget, GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_BUTTON, NULL); + if (!strcmp (g_strdown (filename), "error")) + return gtk_widget_render_icon (widget, GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_BUTTON, NULL); + else + return gdk_pixbuf_new_from_file (filename, NULL); } void -zenity_util_set_window_icon (GtkWidget *widget, const gchar *filename) +zenity_util_set_window_icon (GtkWidget *widget, const gchar *filename, const gchar *default_file) { - const gchar *filenames[2] = { NULL}; - GList *list; + GdkPixbuf *pixbuf; - g_return_if_fail (widget != NULL); - g_return_if_fail (GTK_IS_WINDOW (widget)); + if (filename != NULL) + pixbuf = zenity_util_pixbuf_new_from_file (widget, (gchar *) filename); + else + pixbuf = gdk_pixbuf_new_from_file (default_file, NULL); - if (filename == NULL) - return; - - filenames[0] = filename; - list = zenity_util_list_from_char_array (filenames); - gtk_window_set_icon_list (GTK_WINDOW (widget), list); - zenity_util_free_list (list); + if (pixbuf != NULL) { + gtk_window_set_icon (GTK_WINDOW (widget), pixbuf); + g_object_unref (pixbuf); + } } void -zenity_util_set_window_icon_from_stock (GtkWidget *widget, const gchar *stock_id) +zenity_util_set_window_icon_from_stock (GtkWidget *widget, const gchar *filename, const gchar *default_stock_id) { GdkPixbuf *pixbuf; - - pixbuf = gtk_widget_render_icon (widget, stock_id, (GtkIconSize) -1, NULL); - gtk_window_set_icon (GTK_WINDOW (widget), pixbuf); - g_object_unref (pixbuf); + + if (filename != NULL) { + pixbuf = zenity_util_pixbuf_new_from_file (widget, (gchar *) filename); + } + else { + pixbuf = gtk_widget_render_icon (widget, default_stock_id, GTK_ICON_SIZE_BUTTON, NULL); + } + + if (pixbuf != NULL) { + gtk_window_set_icon (GTK_WINDOW (widget), pixbuf); + g_object_unref (pixbuf); + } } void diff --git a/src/util.h b/src/util.h index 69ec912..4e3c214 100644 --- a/src/util.h +++ b/src/util.h @@ -15,10 +15,14 @@ GladeXML* zenity_util_load_glade_file (const gchar *widge gchar * zenity_util_strip_newline (gchar *string); gboolean zenity_util_fill_file_buffer (GtkTextBuffer *buffer, const gchar *filename); -void zenity_util_set_window_icon (GtkWidget *widget, - const gchar *filename); +void zenity_util_set_window_icon (GtkWidget *widget, + const gchar *filename, + const gchar *default_file); void zenity_util_set_window_icon_from_stock (GtkWidget *widget, - const gchar *stock_id); + const gchar *filename, + const gchar *default_stock_id); +GdkPixbuf * zenity_util_pixbuf_new_from_file (GtkWidget *widget, + gchar *filename); void zenity_util_show_help (GError **error); gint zenity_util_return_exit_code (ZenityExitCode value); void zenity_util_show_dialog (GtkWidget *widget); diff --git a/src/zenity.h b/src/zenity.h index d9aff43..3d4eeea 100644 --- a/src/zenity.h +++ b/src/zenity.h @@ -98,6 +98,10 @@ typedef struct { const gchar **data; } ZenityTreeData; +typedef struct { + gchar *notification_text; +} ZenityNotificationData; + void zenity_calendar (ZenityData *data, ZenityCalendarData *calendar_data); void zenity_msg (ZenityData *data, @@ -112,6 +116,8 @@ void zenity_text (ZenityData *data, ZenityTextData *text_data); void zenity_tree (ZenityData *data, ZenityTreeData *tree_data); +void zenity_notification (ZenityData *data, + ZenityNotificationData *notification_data); void zenity_about (ZenityData *data); G_END_DECLS