[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
RFC: status icon support again
From: |
Tom Tromey |
Subject: |
RFC: status icon support again |
Date: |
Sat, 02 Feb 2008 23:35:18 -0700 |
User-agent: |
Gnus/5.11 (Gnus v5.11) Emacs/22.0.990 (gnu/linux) |
Here is a new revision of the status icon patch. I think I've read
all the responses and I tried to take them into account. Namely:
* I looked a little at rewriting so status icons would be part of
keymaps, but this looked complicated and I didn't see a benefit --
in particular this code has no relation to toolbars at all, and I
didn't think any code could be shared.
* The new patch does not introduce a new type. Instead, a status icon
is named by a symbol. The API changed slightly as a consequence of
this.
* I tried to make all the pre-function comments verbose and readable.
* Michael Olson's test cases should work ok now. At least, they used
to fail for me, and now they work. There is a hack here, in that
Gtk does not actually provide a reliable way to get the information
we want. (I am not sure whether this code will work if the user
doesn't actually have a notification area.)
I think it is a bit more robust now as well. I found a few problems
with the earlier patch that I've fixed.
Tom
ChangeLog:
2008-02-03 Tom Tromey <address@hidden>
* configure: Rebuild.
* configure.in: Check for GtkStatusIcon, libnotify.
doc/lispref/ChangeLog:
2008-02-03 Tom Tromey <address@hidden>
* frames.texi (Status Icons): New node.
(Frames): Update.
src/ChangeLog:
2008-02-03 Tom Tromey <address@hidden>
* alloc.c (Fgarbage_collect): Call mark_status_icons.
* xmenu.c: Include systray.h.
(Fx_popup_menu): Update documentation. Handle status-icon
position argument.
(create_and_show_popup_menu): Add status_icon and status_button
arguments. Update. Use gtk_get_current_event_time.
(xmenu_show): Add status_icon and status_button arguments.
Update.
* termhooks.h (enum event_kind) <STATUS_ICON_CLICK_EVENT>: New
constant.
* lisp.h (syms_of_systray, get_status_icon_for_menu): Declare.
* keyboard.c (Qstatus_icon_click_event): New global.
(kbd_buffer_get_event): Handle STATUS_ICON_CLICK_EVENT.
(make_lispy_event): Likewise.
(syms_of_keyboard): Initialize Qstatus_icon_click_event. Define
"status-icon-click-event" event.
* emacs.c (main): Call syms_of_systray.
* config.in: Rebuild.
* Makefile.in (LIBNOTIFY_CFLAGS): New variable.
(LIBNOTIFY_LIBS): Likewise.
(ALL_CFLAGS): Add LIBNOTIFY_CFLAGS.
(GTK_OBJ): Add systray.o
(LIBES): Add LIBNOTIFY_LIBS.
(systray.o): New target.
* systray.c: New file.
Index: configure.in
===================================================================
RCS file: /sources/emacs/emacs/configure.in,v
retrieving revision 1.506
diff -u -r1.506 configure.in
--- configure.in 2 Feb 2008 15:49:10 -0000 1.506
+++ configure.in 3 Feb 2008 06:54:40 -0000
@@ -148,6 +148,7 @@
OPTION_DEFAULT_ON([gpm],[don't use -lgpm for mouse support on a GNU/Linux
console])
OPTION_DEFAULT_OFF([dbus],[compile with D-Bus support])
+OPTION_DEFAULT_ON([libnotify],[don't use libnotify])
AC_ARG_WITH([pkg-config-prog],dnl
[AS_HELP_STRING([--with-pkg-config-prog=PATH],
@@ -1769,6 +1770,15 @@
[Define to 1 if GTK has both file selection and chooser dialog.])
fi
+ dnl Check if we have GtkStatusIcon.
+ HAVE_GTK_STATUS_ICON=no
+ AC_CHECK_FUNCS(gtk_status_icon_new, HAVE_GTK_STATUS_ICON=yes)
+
+ if test "$HAVE_GTK_STATUS_ICON" = yes; then
+ AC_DEFINE(HAVE_GTK_STATUS_ICON, 1,
+ [Define to 1 if GTK has GtkStatusIcon.])
+ fi
+
dnl Check if pthreads are available. Emacs only needs this when using
dnl gtk_file_chooser under Gnome.
if test "$HAVE_GTK_FILE_CHOOSER" = yes; then
@@ -1799,6 +1809,16 @@
fi
fi
+dnl Check if we have libnotify.
+if test "${with_libnotify}" = yes; then
+ PKG_CHECK_MODULES(LIBNOTIFY, libnotify >= 0.4.1, HAVE_LIBNOTIFY=yes,
+ HAVE_LIBNOTIFY=no)
+ if test "$HAVE_LIBNOTIFY" = yes; then
+ AC_DEFINE(HAVE_LIBNOTIFY, 1,
+ [Define to 1 if the system has libnotify.])
+ fi
+fi
+
dnl Do not put whitespace before the #include statements below.
dnl Older compilers (eg sunos4 cc) choke on it.
HAVE_XAW3D=no
Index: doc/lispref/frames.texi
===================================================================
RCS file: /sources/emacs/emacs/doc/lispref/frames.texi,v
retrieving revision 1.6
diff -u -r1.6 frames.texi
--- doc/lispref/frames.texi 8 Jan 2008 20:45:50 -0000 1.6
+++ doc/lispref/frames.texi 3 Feb 2008 06:54:41 -0000
@@ -75,6 +75,7 @@
* Text Terminal Colors:: Defining colors for text-only terminals.
* Resources:: Getting resource values from the server.
* Display Feature Testing:: Determining the features of a terminal.
+* Status Icons:: Status icons and notifications.
@end menu
@xref{Display}, for information about the related topic of
@@ -2218,6 +2219,126 @@
width and height of an X Window frame, measured in pixels.
@end ignore
address@hidden Status Icons
address@hidden Status Icons and Notifications
address@hidden status icons
address@hidden system tray
+
+Some platforms provide a ``system tray'', an area holding icons tied
+to particular applications. Applications use these icons to
+unobtrusively communicate information to the user. Status icons also
+support ``notifications'', which are pop-up windows displaying some
+text and perhaps having some buttons. A notification is less
+intrusive than a dialog because it does not steal the focus and need
+not be manually dismissed. Emacs can create and manipulate status
+icons, and send notifications.
+
+If status icons are available, the feature @code{systray} is provided.
+
address@hidden make-status-icon name &optional alist
+
+This function creates and returns a new status icon. @var{name} is a
+symbol which is used to refer to the status icon. It is an error if
address@hidden already names a status icon. The optional argument
address@hidden provides initial properties for the icon. Some properties
+are specially recognized; unrecognized properties are retained (for
+use by @code{status-icon-parameters}) but otherwise ignored.
+
+The recognized properties are:
address@hidden @code
address@hidden icon-name
+The value is the file name of the image to display on the icon.
address@hidden means that Emacs should display a default image.
+
address@hidden blinking
+If the value is address@hidden, the icon will blink. The default is
address@hidden
+
address@hidden visible
+If the value is @code{nil}, the icon will not be shown. The default
+is @code{t}.
+
address@hidden help-echo
+The value is a string which will displayed as a tooltip for the icon.
+
address@hidden click-callback
+The value is a no-argument function which will be called when the user
+clicks on the icon.
+
address@hidden menu
+The value is a menu (of any kind recognized by @code{popup-menu})
+which will be displayed when the user right-clicks on the icon.
address@hidden table
address@hidden defun
+
address@hidden delete-status-icon icon
+Destroy @var{icon}.
address@hidden defun
+
address@hidden modify-status-icon-parameters icon alist
+Modify the parameters of @var{icon} according. @var{alist} is an
+alist of parameters of the same kind accepted by
address@hidden
address@hidden defun
+
address@hidden status-iconp object
+Return address@hidden if @var{object} is a status icon.
address@hidden defun
+
address@hidden status-icon-list
+Return a list of all status icons.
address@hidden defun
+
address@hidden show-status-icon-message icon summary &rest args
+This function creates and displays a new notification. Note that this
+function may not be available on all platforms, and can be missing
+even if the basic status icon support is available.
+
address@hidden is the status icon to which the notification should attach.
+If @var{icon} is @code{nil}, then the notification will not be
+attached to an icon, but instead will display in some default
+location.
+
address@hidden is the summary text of the notification, a string.
+
address@hidden, if given, is a list of key-value pairs. The recognized
+keys and their meanings are:
address@hidden @code
address@hidden :body @var{string}
+Set the body text of the notification. This is ordinarily displayed
+in a different font than the summary text.
+
address@hidden :timeout @var{value}
+By default a notification will time out. The default timeout interval
+is system-specific. If @var{value} is @code{nil}, the default is
+used; if it is @code{t}, the notification will never time out.
+Otherwise, if @var{value} is an integer, then it specifies the amount
+of time in milliseconds.
+
address@hidden :urgency @var{value}
+Set the urgency level. @var{value} is a symbol, one of @samp{low},
address@hidden, @samp{critical}. The default is @samp{normal}. This
+setting may affect how the notification is displayed; for instance a
address@hidden notification may have a red background.
+
address@hidden :icon @var{value}
+Set the notification's icon. @samp{value} is a symbol, currently one
+of @samp{warning}, @samp{info}, @samp{question}, @samp{error}. The
+default is @samp{info}.
+
address@hidden :action (@var{label} . @var{function})
+Add an action to the notification, with label @var{label}. An action
+typically appears on the notification as a small button. Multiple
+actions can be added, to give the user a choice of how to react to a
+given event. @var{function} is called with no arguments if the action
+is chosen.
+
address@hidden :closed-callback @var{function}
+Call @var{function} with no arguments when the notification is closed.
+This is not called when the user clicks an action button.
address@hidden table
address@hidden defun
+
@ignore
arch-tag: 94977df6-3dca-4730-b57b-c6329e9282ba
@end ignore
Index: src/Makefile.in
===================================================================
RCS file: /sources/emacs/emacs/src/Makefile.in,v
retrieving revision 1.365
diff -u -r1.365 Makefile.in
--- src/Makefile.in 1 Feb 2008 23:10:21 -0000 1.365
+++ src/Makefile.in 3 Feb 2008 06:54:44 -0000
@@ -270,6 +270,11 @@
DBUS_OBJ = dbusbind.o
#endif
+#ifdef HAVE_LIBNOTIFY
+LIBNOTIFY_CFLAGS = @LIBNOTIFY_CFLAGS@
+LIBNOTIFY_LIBS = @LIBNOTIFY_LIBS@
+#endif
+
/* DO NOT use -R. There is a special hack described in lastfile.c
which is used instead. Some initialized data areas are modified
at initial startup, then labeled as part of the text area when
@@ -283,7 +288,7 @@
/* C_SWITCH_X_SITE must come before C_SWITCH_X_MACHINE and C_SWITCH_X_SYSTEM
since it may have -I options that should override those two. */
-ALL_CFLAGS=-Demacs -DHAVE_CONFIG_H $(TOOLKIT_DEFINES) $(MYCPPFLAGS) -I.
-I${srcdir} C_SWITCH_MACHINE C_SWITCH_SYSTEM C_SWITCH_SITE C_SWITCH_X_SITE
C_SWITCH_X_MACHINE C_SWITCH_X_SYSTEM C_SWITCH_SYSTEM_TEMACS ${CFLAGS_SOUND}
${RSVG_CFLAGS} ${DBUS_CFLAGS} ${CFLAGS} @FREETYPE_CFLAGS@ @FONTCONFIG_CFLAGS@
@LIBOTF_CFLAGS@ @M17N_FLT_CFLAGS@
+ALL_CFLAGS=-Demacs -DHAVE_CONFIG_H $(TOOLKIT_DEFINES) $(MYCPPFLAGS) -I.
-I${srcdir} C_SWITCH_MACHINE C_SWITCH_SYSTEM C_SWITCH_SITE C_SWITCH_X_SITE
C_SWITCH_X_MACHINE C_SWITCH_X_SYSTEM C_SWITCH_SYSTEM_TEMACS ${CFLAGS_SOUND}
${RSVG_CFLAGS} ${DBUS_CFLAGS} ${LIBNOTIFY_CFLAGS} ${CFLAGS} @FREETYPE_CFLAGS@
@FONTCONFIG_CFLAGS@ @LIBOTF_CFLAGS@ @M17N_FLT_CFLAGS@
.c.o:
$(CC) -c $(CPPFLAGS) $(ALL_CFLAGS) $<
@@ -311,7 +316,7 @@
#ifdef HAVE_MENUS
#ifdef USE_GTK
-GTK_OBJ= gtkutil.o
+GTK_OBJ= gtkutil.o systray.o
#endif
/* The X Menu stuff is present in the X10 distribution, but missing
@@ -957,6 +962,7 @@
duplicated symbols. If the standard libraries were compiled
with GCC, we might need gnulib again after them. */
LIBES = $(LOADLIBES) $(LIBS) $(LIBX) $(LIBSOUND) $(RSVG_LIBS) $(DBUS_LIBS) \
+ $(LIBNOTIFY_LIBS) \
LIBGPM LIBRESOLV LIBS_SYSTEM LIBS_MACHINE LIBS_TERMCAP \
LIBS_DEBUG $(GETLOADAVG_LIBS) \
@FREETYPE_LIBS@ @FONTCONFIG_LIBS@ @LIBOTF_LIBS@ @M17N_FLT_LIBS@ \
@@ -1221,6 +1227,7 @@
sysdep.o: sysdep.c syssignal.h systty.h systime.h syswait.h blockinput.h \
process.h dispextern.h termhooks.h termchar.h termopts.h \
frame.h atimer.h window.h msdos.h dosfns.h keyboard.h cm.h $(config_h)
+systray.o: systray.c $(config_h) lisp.h frame.h
term.o: term.c termchar.h termhooks.h termopts.h $(config_h) cm.h frame.h \
disptab.h dispextern.h keyboard.h character.h charset.h coding.h ccl.h \
msdos.h window.h keymap.h blockinput.h atimer.h systime.h
Index: src/alloc.c
===================================================================
RCS file: /sources/emacs/emacs/src/alloc.c,v
retrieving revision 1.435
diff -u -r1.435 alloc.c
--- src/alloc.c 1 Feb 2008 16:00:57 -0000 1.435
+++ src/alloc.c 3 Feb 2008 06:54:44 -0000
@@ -5087,6 +5087,13 @@
extern void xg_mark_data ();
xg_mark_data ();
}
+
+#ifdef HAVE_GTK_STATUS_ICON
+ {
+ extern void mark_status_icons ();
+ mark_status_icons ();
+ }
+#endif
#endif
#if (GC_MARK_STACK == GC_MAKE_GCPROS_NOOPS \
Index: src/emacs.c
===================================================================
RCS file: /sources/emacs/emacs/src/emacs.c,v
retrieving revision 1.416
diff -u -r1.416 emacs.c
--- src/emacs.c 1 Feb 2008 16:00:45 -0000 1.416
+++ src/emacs.c 3 Feb 2008 06:54:45 -0000
@@ -1656,6 +1656,10 @@
syms_of_dbusbind ();
#endif /* HAVE_DBUS */
+#ifdef HAVE_GTK
+ syms_of_systray ();
+#endif
+
#ifdef SYMS_SYSTEM
SYMS_SYSTEM;
#endif
Index: src/keyboard.c
===================================================================
RCS file: /sources/emacs/emacs/src/keyboard.c,v
retrieving revision 1.942
diff -u -r1.942 keyboard.c
--- src/keyboard.c 1 Feb 2008 16:00:40 -0000 1.942
+++ src/keyboard.c 3 Feb 2008 06:54:46 -0000
@@ -489,6 +489,8 @@
#ifdef HAVE_DBUS
Lisp_Object Qdbus_event;
#endif
+Lisp_Object Qstatus_icon_click_event;
+
/* Lisp_Object Qmouse_movement; - also an event header */
/* Properties of event headers. */
@@ -4298,6 +4300,11 @@
kbd_fetch_ptr = event + 1;
}
#endif
+ else if (event->kind == STATUS_ICON_CLICK_EVENT)
+ {
+ obj = make_lispy_event (event);
+ kbd_fetch_ptr = event + 1;
+ }
else
{
/* If this event is on a different frame, return a switch-frame this
@@ -6174,6 +6181,9 @@
}
#endif /* HAVE_DBUS */
+ case STATUS_ICON_CLICK_EVENT:
+ return Fcons (Qstatus_icon_click_event, event->arg);
+
#ifdef HAVE_GPM
case GPM_CLICK_EVENT:
{
@@ -11811,6 +11821,9 @@
staticpro (&Qdbus_event);
#endif
+ Qstatus_icon_click_event = intern ("status-icon-click-event");
+ staticpro (&Qstatus_icon_click_event);
+
Qmenu_enable = intern ("menu-enable");
staticpro (&Qmenu_enable);
Qmenu_alias = intern ("menu-alias");
@@ -12547,6 +12560,9 @@
initial_define_lispy_key (Vspecial_event_map, "dbus-event",
"dbus-handle-event");
#endif
+
+ initial_define_lispy_key (Vspecial_event_map, "status-icon-click-event",
+ "status-icon-handle-click-event");
}
/* Mark the pointers in the kboard objects.
Index: src/lisp.h
===================================================================
RCS file: /sources/emacs/emacs/src/lisp.h,v
retrieving revision 1.606
diff -u -r1.606 lisp.h
--- src/lisp.h 1 Feb 2008 16:00:44 -0000 1.606
+++ src/lisp.h 3 Feb 2008 06:54:46 -0000
@@ -3242,6 +3243,10 @@
extern void syms_of_sound P_ ((void));
extern void init_sound P_ ((void));
+/* Defined in systray.c */
+extern void syms_of_systray P_ ((void));
+extern void *get_status_icon_for_menu (Lisp_Object symbol);
+
/* Defined in category.c */
extern void init_category_once P_ ((void));
extern Lisp_Object char_category_set P_ ((int));
Index: src/systray.c
===================================================================
RCS file: src/systray.c
diff -N src/systray.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/systray.c 3 Feb 2008 06:54:46 -0000
@@ -0,0 +1,850 @@
+/* Elisp bindings system tray icons
+ Copyright (C) 2007, 2008 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GNU Emacs 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA. */
+
+#include "config.h"
+
+#include "lisp.h"
+#include "frame.h"
+#include "charset.h"
+#include "coding.h"
+#include "blockinput.h"
+#include "termhooks.h"
+#include "xterm.h"
+
+#include <gtk/gtk.h>
+
+#ifdef HAVE_LIBNOTIFY
+#include <libnotify/notify.h>
+#endif
+
+#ifdef HAVE_GTK_STATUS_ICON
+
+/* A linked list of notifications. This is used to associate a
+ notification with its corresponding status icon. */
+struct notification_list
+{
+ /* The notification. */
+ NotifyNotification *notification;
+
+ /* Lisp data associated with this notification, which must remain
+ live while the notification is active. */
+ Lisp_Object list;
+
+ /* Link to the next one. */
+ struct notification_list *next;
+};
+
+/* Representation of a status icon. */
+struct status_icon
+{
+ /* The symbol used when the icon was created. */
+ Lisp_Object name;
+
+ /* Parameter alist of this status icon. */
+ Lisp_Object param_alist;
+
+ /* The icon widget. */
+ GtkStatusIcon *icon;
+
+ /* Linked list of all notifications sent via this icon. */
+ struct notification_list *notifications;
+
+ /* The next icon in the list. */
+ struct status_icon *next;
+};
+
+/* All the status icons. */
+static struct status_icon *all_icons;
+
+/* List of notifications that were not issued against a status icon. */
+static struct notification_list *iconless_notifications;
+
+/* FIXME: should be in a header. */
+extern Lisp_Object Qhelp_echo;
+extern Lisp_Object Qmenu;
+extern Lisp_Object Qstatus_icon_click_event;
+
+Lisp_Object Qblinking;
+Lisp_Object Qclick_callback;
+Lisp_Object QCbody, QCtimeout, QCurgency, QCicon, QCaction, QCclosed_callback;
+Lisp_Object Qlow, Qcritical, Qwarning, Qquestion, Qerror;
+Lisp_Object Qpopup_menu;
+
+/* Return a status icon of the given name, or NULL if no such icon
+ exists. */
+static struct status_icon *
+find_status_icon (Lisp_Object name)
+{
+ struct status_icon *iter;
+ for (iter = all_icons; iter; iter = iter->next)
+ if (EQ (name, iter->name))
+ break;
+ return iter;
+}
+
+/* Return a status icon of the given name, or throw an error if no
+ such icon exists, or if NAME is not a symbol. */
+static struct status_icon *
+get_status_icon (Lisp_Object name)
+{
+ struct status_icon *result;
+
+ CHECK_SYMBOL (name);
+ result = find_status_icon (name);
+ if (! result)
+ error ("no status icon named `%s'", SDATA (SYMBOL_NAME (name)));
+ return result;
+}
+
+/* Like find_status_icon, but for use by the menu-handling code.
+ Returns the underlying GtkStatusIcon rather than status_icon
+ struct. */
+void *
+get_status_icon_for_menu (Lisp_Object name)
+{
+ struct status_icon *sicon = find_status_icon (name);
+ return sicon ? sicon->icon : NULL;
+}
+
+/* This callback is attached to the "activate" signal on the status
+ icon. It is called when the user clicks (usually the left button)
+ on the icon. WIDGET is the widget which is clicked, and DATA is
+ the data we passed to the signal; in our case, the icon
+ descriptor. */
+static void
+activate_icon (GtkWidget *widget, gpointer data)
+{
+ Lisp_Object icon = (Lisp_Object) data;
+ struct status_icon *sicon = find_status_icon (icon);
+
+ /* Just ignore garbage. */
+ if (sicon)
+ {
+ Lisp_Object elt = Fassq (Qmenu, sicon->param_alist);
+ if (CONSP (elt) && FUNCTIONP (XCDR (elt)))
+ {
+ struct input_event event;
+ EVENT_INIT (event);
+ event.kind = STATUS_ICON_CLICK_EVENT;
+ event.frame_or_window = Qnil;
+ event.arg = XCDR (elt);
+ kbd_buffer_store_event (&event);
+ }
+ }
+}
+
+/* This callback is attached to the "popup-menu" signal on the status
+ icon. It is called when the user clicks (usually the right button)
+ on the icon. The arguments are as specified by Gtk; in our case
+ USER_DATA is the icon descriptor. */
+static void
+pop_up_status_menu (GtkStatusIcon *gicon, guint button,
+ guint activate_time, gpointer user_data)
+{
+ Lisp_Object icon = (Lisp_Object) user_data;
+ struct status_icon *sicon = find_status_icon (icon);
+
+ /* Just ignore garbage. */
+ if (sicon)
+ {
+ Lisp_Object elt = Fassq (Qmenu, sicon->param_alist);
+ if (CONSP (elt) && ! NILP (XCDR (elt)))
+ {
+ struct gcpro gcpro1;
+ struct input_event event;
+ Lisp_Object function;
+
+ GCPRO1 (function);
+ /* Construct the 'position' list. */
+ function = Fcons (icon,
+ Fcons (make_number (button), Qnil));
+ function = Fcons (Qquote,
+ Fcons (function, Qnil));
+ /* Construct the call to `popup-menu'. */
+ function = Fcons (Qpopup_menu,
+ Fcons (Fcons (Qquote,
+ Fcons (XCDR (elt), Qnil)),
+ Fcons (function, Qnil)));
+ /* Construct the function. */
+ function = Fcons (Qlambda,
+ Fcons (Qnil,
+ Fcons (function, Qnil)));
+
+ EVENT_INIT (event);
+ event.kind = STATUS_ICON_CLICK_EVENT;
+ event.frame_or_window = Qnil;
+ event.arg = function;
+ kbd_buffer_store_event (&event);
+ }
+ }
+}
+
+/* Apply property KEY with value VALUE to icon SICON. The keys and
+ values are as documented in modify-status-icon-parameters.
+ Unrecognized keys are ignored here. This function modifies the
+ status icon as it works. */
+static void
+apply_one_icon_property (struct status_icon *sicon, Lisp_Object key,
+ Lisp_Object value)
+{
+ if (EQ (key, Qicon_name))
+ {
+ struct gcpro gcpro1;
+ Lisp_Object filename = Qnil;
+ GCPRO1 (filename);
+ if (NILP (value))
+ {
+ /* Default to the GNU. */
+ filename = build_string ("emacs_48.png");
+ }
+ else
+ {
+ CHECK_STRING (value);
+ filename = value;
+ }
+ /* Not clear if x_find_image_file returns a string with the
+ correct encoding for Gtk. However, it is already used this
+ way in gtkutil.c. */
+ filename = x_find_image_file (filename);
+ if (NILP (filename))
+ {
+ /* No good choice, so choose a semi-appropriate stock
+ icon. */
+ gtk_status_icon_set_from_stock (sicon->icon,
+ GTK_STOCK_DIALOG_QUESTION);
+ }
+ else
+ gtk_status_icon_set_from_file (sicon->icon,
+ (char *) SDATA (filename));
+ UNGCPRO;
+ }
+ else if (EQ (key, Qblinking))
+ gtk_status_icon_set_blinking (sicon->icon, ! NILP (value));
+ else if (EQ (key, Qvisible))
+ gtk_status_icon_set_visible (sicon->icon, ! NILP (value));
+ else if (EQ (key, Qhelp_echo))
+ {
+ struct gcpro gcpro1;
+ Lisp_Object tooltip = Qnil;
+ GCPRO1 (tooltip);
+ if (NILP (value))
+ gtk_status_icon_set_tooltip (sicon->icon, NULL);
+ else
+ {
+ /* FIXME: should accept a sexp to eval here. */
+ CHECK_STRING (value);
+ tooltip = ENCODE_UTF_8 (value);
+ gtk_status_icon_set_tooltip (sicon->icon,
+ (char *) SDATA (tooltip));
+ }
+ UNGCPRO;
+ }
+ else
+ {
+ /* Ignore things we don't understand. */
+ }
+}
+
+/* Apply the ALIST of properties to status icon ICON. The contents of
+ ALIST are as documented in modify-status-icon-parameters. */
+static void
+apply_status_icon_alist (struct status_icon *sicon, Lisp_Object alist)
+{
+ Lisp_Object tail;
+
+ for (tail = alist; CONSP (tail); tail = XCDR (tail))
+ {
+ Lisp_Object key, value;
+ Lisp_Object elt = XCAR (tail);
+ if (! CONSP (elt))
+ continue;
+ key = XCAR (elt);
+ value = XCDR (elt);
+
+ apply_one_icon_property (sicon, key, value);
+
+ /* Update parameters. */
+ elt = Fassq (key, sicon->param_alist);
+ if (NILP (elt))
+ sicon->param_alist = Fcons (Fcons (key, value), sicon->param_alist);
+ else
+ Fsetcdr (elt, value);
+ }
+}
+
+/* Flush the Gtk event queue. Must be called with input blocked. */
+static void
+flush_gtk_events ()
+{
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+}
+
+/* Helper function for make-status-icon. This is a Gtk callback that
+ is attached to the GtkStatusIcon 'size-changed' signal. */
+static gboolean
+set_size_changed (GtkStatusIcon *icon, gint size, gpointer user_data)
+{
+ int *valp = (int *) user_data;
+ *valp = 1;
+ return 0;
+}
+
+DEFUN ("make-status-icon", Fmake_status_icon, Smake_status_icon,
+ 1, 2, 0,
+ doc: /* Return a newly created status icon.
+NAME is a symbol used to identify the icon.
+Optional argument ALIST is an alist of parameters for the new frame.
+See `modify-status-icon-parameters' for a list of recognized parameters.*/)
+ (name, alist)
+ Lisp_Object name, alist;
+{
+ struct status_icon *sicon;
+ Lisp_Object deflist;
+ struct gcpro gcpro1;
+ int size_changed;
+ gulong handler;
+
+ check_x ();
+
+ CHECK_SYMBOL (name);
+ if (find_status_icon (name))
+ error ("status icon `%s' already exists", SDATA (SYMBOL_NAME (name)));
+
+ BLOCK_INPUT;
+
+ sicon = (struct status_icon *) xmalloc (sizeof (struct status_icon));
+ sicon->name = name;
+ sicon->param_alist = Qnil;
+ sicon->icon = gtk_status_icon_new ();
+ sicon->notifications = NULL;
+ sicon->next = all_icons;
+ all_icons = sicon;
+
+ size_changed = 0;
+ g_signal_connect (sicon->icon, "size-changed", G_CALLBACK (set_size_changed),
+ (gpointer) &size_changed);
+
+ g_signal_connect (sicon->icon, "activate", G_CALLBACK (activate_icon),
+ (gpointer) name);
+ handler = g_signal_connect (sicon->icon, "popup-menu",
+ G_CALLBACK (pop_up_status_menu),
+ (gpointer) name);
+
+ GCPRO1 (deflist);
+
+ /* Make some defaults. */
+ deflist = Fcons (Fcons (Qclick_callback, Qnil),
+ Fcons (Fcons (Qicon_name, Qnil),
+ Fcons (Fcons (Qblinking, Qnil),
+ Fcons (Fcons (Qvisible, Qt),
+ Fcons (Fcons (Qhelp_echo, Qnil),
+ alist)))));
+ apply_status_icon_alist (sicon, deflist);
+
+ /* This is a hack: if we wait for a size event, then an immediate
+ notification will show up at the correct location. We only try
+ to do this when we've requested that the icon be visible. */
+ if (! gtk_status_icon_get_visible (sicon->icon))
+ size_changed = 1;
+ do
+ {
+ flush_gtk_events ();
+ }
+ while (! size_changed);
+ g_signal_handler_disconnect (sicon->icon, handler);
+
+ UNBLOCK_INPUT;
+
+ RETURN_UNGCPRO (name);
+}
+
+DEFUN ("modify-status-icon-parameters", Fmodify_status_icon_parameters,
+ Smodify_status_icon_parameters,
+ 2, 2, 0,
+ doc: /* Modify the parameters of status icon ICON according to ALIST.
+ICON is a symbol naming a status icon.
+Each element of alist has the form (PARM . VALUE), where PARM is a symbol.
+Undefined PARMs are ignored, but stored in the frame's parameter list
+so that `status-icon-parameters' will return them.
+
+Currently defined parameters and their values are:
+
+ icon-name The file name of an icon to display.
+ blinking If non-nil, the status icon will blink.
+ visible If nil, status icon is invisible.
+ help-echo A string which will be displayed as a tooltip for the
+ status icon.
+ click-callback A no-argument function which will be called when the
+ user clicks on the icon.
+ menu A menu which will pop up when the user clicks on the
+ icon. May not be available on all platforms.*/)
+ (icon, alist)
+ Lisp_Object icon, alist;
+{
+ struct status_icon *sicon = get_status_icon (icon);
+ BLOCK_INPUT;
+ apply_status_icon_alist (sicon, alist);
+ flush_gtk_events ();
+ UNBLOCK_INPUT;
+ return Qnil;
+}
+
+DEFUN ("status-icon-parameters", Fstatus_icon_parameters,
+ Sstatus_icon_parameters,
+ 1, 1, 0,
+ doc: /* Return the parameters-alist of the status icon ICON.
+It is a list of elements of the form (PARM . VALUE), where PARM is a symbol.*/)
+ (icon)
+ Lisp_Object icon;
+{
+ struct status_icon *sicon = get_status_icon (icon);
+ return Fcopy_alist (sicon->param_alist);
+}
+
+DEFUN ("status-icon-list", Fstatus_icon_list, Sstatus_icon_list,
+ 0, 0, 0,
+ doc: /* Return a list of all status icons. */)
+ ()
+{
+ Lisp_Object result = Qnil;
+ struct status_icon *iter;
+ for (iter = all_icons; iter; iter = iter->next)
+ result = Fcons (iter->name, result);
+ return result;
+}
+
+DEFUN ("delete-status-icon", Fdelete_status_icon, Sdelete_status_icon,
+ 1, 1, 0,
+ doc: /* Delete the status icon ICON. */)
+ (icon)
+ Lisp_Object icon;
+{
+ struct status_icon *sicon = get_status_icon (icon), *iter;
+
+ BLOCK_INPUT;
+
+ while (sicon->notifications)
+ {
+ struct notification_list *next;
+ /* Apparently this does not call the 'close' callback, so we do
+ all the handling here rather relying on the callback. */
+ notify_notification_close (sicon->notifications->notification, NULL);
+ g_object_unref (sicon->notifications->notification);
+ next = sicon->notifications->next;
+ free (sicon->notifications);
+ sicon->notifications = next;
+ }
+
+ gtk_status_icon_set_visible (sicon->icon, 0);
+ g_object_unref (sicon->icon);
+ flush_gtk_events ();
+ UNBLOCK_INPUT;
+
+ if (all_icons == sicon)
+ all_icons = sicon->next;
+ else
+ {
+ struct status_icon *iter;
+ for (iter = all_icons; iter; iter = iter->next)
+ if (iter->next == sicon)
+ {
+ iter->next = sicon->next;
+ break;
+ }
+ }
+ free (sicon);
+
+ return Qnil;
+}
+
+#ifdef HAVE_LIBNOTIFY
+
+/* Called in response to notification action clicks. */
+static void
+handle_notification_event (NotifyNotification *notification,
+ int action_number, Lisp_Object callback_arg)
+{
+ struct notification_list *iter, **prev;
+ Lisp_Object icon = XCAR (callback_arg);
+ struct status_icon *sicon = find_status_icon (icon);
+ Lisp_Object callback = Fnth (make_number (action_number + 2), callback_arg);
+
+ if (FUNCTIONP (callback))
+ {
+ struct input_event event;
+ EVENT_INIT (event);
+ event.kind = STATUS_ICON_CLICK_EVENT;
+ event.frame_or_window = Qnil;
+ event.arg = callback;
+ kbd_buffer_store_event (&event);
+ }
+
+ if (sicon)
+ {
+ prev = &sicon->notifications;
+ iter = sicon->notifications;
+ }
+ else
+ {
+ prev = &iconless_notifications;
+ iter = iconless_notifications;
+ }
+ while (iter)
+ {
+ if (iter->notification == notification)
+ {
+ *prev = iter->next;
+ free (iter);
+ break;
+ }
+ prev = &iter->next;
+ iter = iter->next;
+ }
+
+ g_object_unref (notification);
+}
+
+static void
+action_click (NotifyNotification *notification, gchar *actionid,
+ gpointer user_data)
+{
+ int id = atoi (actionid);
+ Lisp_Object callback_arg = (Lisp_Object) user_data;
+ handle_notification_event (notification, id, callback_arg);
+}
+
+static void
+cleanup_notification (NotifyNotification *notification, gpointer user_data)
+{
+ Lisp_Object callback_arg = (Lisp_Object) user_data;
+ handle_notification_event (notification, -1, callback_arg);
+}
+
+DEFUN ("show-status-icon-message", Fshow_status_icon_message,
+ Sshow_status_icon_message,
+ 2, MANY, 0,
+ doc: /* Post a notification message attached to the icon ICON.
+ICON is a status icon created by `make-status-icon', or nil for a
+standalone notification.
+SUMMARY is the message to display, a string.
+
+The remaining arguments, if any, are a property list specifying
+additional parameters of the notification:
+
+ :body STRING Set the body text of the notification.
+ :timeout VALUE Set the timeout value.
+ nil means use the default.
+ t means never time out.
+ An integer specifies the timeout in milliseconds.
+ :urgency VALUE Set the urgency level. VALUE is a symbol, one of
+ 'low', 'normal', 'critical'. The default is 'normal'.
+ :icon VALUE Set the notification icon. VALUE is a symbol, currently
+ one of 'warning', 'info', 'question', 'error'. The
+ default is 'info'.
+ :action (LABEL . FUNCTION)
+ Add an action to the notification, with label LABEL.
+ FUNCTION is called with no arguments if the action
+ is chosen.
+ :closed-callback FUNCTION
+ Call FUNCTION with no arguments when the notification
+ is closed. This is not called when the user clicks
+ an action button.
+
+usage: (show-status-icon-message ICON SUMMARY &rest ARGS) */)
+ (nargs, args)
+ int nargs;
+ Lisp_Object *args;
+{
+ struct status_icon *sicon;
+ struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
+ Lisp_Object gc_temp = Qnil;
+ Lisp_Object callback_list = Qnil;
+ Lisp_Object closed_callback = Qnil;
+ Lisp_Object callback_arg = Qnil;
+ NotifyNotification *notification;
+ struct notification_list *entry;
+
+ int i;
+ int action_count = 0;
+ Lisp_Object icon = args[0];
+ Lisp_Object summary = args[1];
+
+ check_x ();
+ if ((nargs - 2) % 2 != 0)
+ error ("Invalid number of arguments");
+
+ if (NILP (icon))
+ sicon = NULL;
+ else
+ sicon = get_status_icon (icon);
+ CHECK_STRING (summary);
+
+ BLOCK_INPUT;
+
+ if (! notify_is_initted () && ! notify_init ("Emacs"))
+ {
+ UNBLOCK_INPUT;
+ error ("Couldn't connect to notification server");
+ }
+
+ GCPRO4 (gc_temp, callback_list, closed_callback, callback_arg);
+ gc_temp = ENCODE_UTF_8 (summary);
+
+ /* Pre-allocate the cons that will be passed to all callbacks. We
+ will fill it in later. */
+ callback_arg = Fcons (Qnil, Qnil);
+
+ if (sicon)
+ {
+ notification
+ = notify_notification_new_with_status_icon (SDATA (gc_temp), NULL,
+ GTK_STOCK_DIALOG_INFO,
+ sicon->icon);
+ }
+ else
+ {
+ notification
+ = notify_notification_new (SDATA (gc_temp), NULL, GTK_STOCK_DIALOG_INFO,
+ NULL);
+ }
+
+ for (i = 2; i < nargs; i += 2)
+ {
+ Lisp_Object key = args[i];
+ Lisp_Object value = args[i + 1];
+ /* FIXME: still no access to: setting the icon from a pixbuf,
+ maybe hints (what are they good for?), closing the
+ notification by caller. */
+ if (EQ (key, QCbody))
+ {
+ /* Ignore things we don't understand. */
+ if (STRINGP (value))
+ {
+ gc_temp = ENCODE_UTF_8 (value);
+ g_object_set (G_OBJECT (notification), "body", SDATA (gc_temp),
+ NULL);
+ }
+ }
+ else if (EQ (key, QCtimeout))
+ {
+ gint timeout = NOTIFY_EXPIRES_DEFAULT;
+ if (EQ (value, Qt))
+ timeout = NOTIFY_EXPIRES_NEVER;
+ else if (INTEGERP (value))
+ {
+ timeout = XINT (value);
+ if (timeout <= 0)
+ timeout = NOTIFY_EXPIRES_DEFAULT;
+ }
+ notify_notification_set_timeout (notification, timeout);
+ }
+ else if (EQ (key, QCurgency))
+ {
+ NotifyUrgency urgency = NOTIFY_URGENCY_NORMAL;
+ if (EQ (value, Qlow))
+ urgency = NOTIFY_URGENCY_LOW;
+ else if (EQ (value, Qcritical))
+ urgency = NOTIFY_URGENCY_CRITICAL;
+ notify_notification_set_urgency (notification, urgency);
+ }
+ else if (EQ (key, QCicon))
+ {
+ char *icon_name = GTK_STOCK_DIALOG_INFO;
+ if (EQ (value, Qwarning))
+ icon_name = GTK_STOCK_DIALOG_WARNING;
+ else if (EQ (value, Qquestion))
+ icon_name = GTK_STOCK_DIALOG_QUESTION;
+ else if (EQ (value, Qerror))
+ icon_name = GTK_STOCK_DIALOG_ERROR;
+ g_object_set (G_OBJECT (notification), "icon-name", icon_name, NULL);
+ }
+ else if (EQ (key, QCaction))
+ {
+ /* Ignore if we don't understand the value. */
+ if (CONSP (value) && STRINGP (XCAR (value)))
+ {
+ char actionid[20];
+ Lisp_Object label = XCAR (value);
+ gc_temp = ENCODE_UTF_8 (label);
+ /* Use a plain integer for the action id, because we
+ will use it later to look up the action. */
+ sprintf (actionid, "%d", action_count);
+ ++action_count;
+ notify_notification_add_action (notification, actionid,
+ SDATA (gc_temp),
+ action_click,
+ (gpointer) callback_arg, NULL);
+
+ /* We build the list from the back, the reverse it
+ later. */
+ callback_list = Fcons (XCDR (value), callback_list);
+ }
+ }
+ else if (EQ (key, QCclosed_callback))
+ closed_callback = value;
+ }
+
+ /* Put the callback list into the right order, then put the 'closed'
+ callback at the start of the list. */
+ callback_list = Fcons (closed_callback, Fnreverse (callback_list));
+
+ XSETCAR (callback_arg, icon);
+ XSETCDR (callback_arg, callback_list);
+
+ /* Arrange to clean up when this notification goes away. */
+ g_signal_connect (G_OBJECT (notification), "closed",
+ G_CALLBACK (cleanup_notification),
+ (gpointer) callback_arg);
+
+ entry = ((struct notification_list *)
+ xmalloc (sizeof (struct notification_list)));
+ entry->notification = notification;
+ entry->list = callback_arg;
+ if (sicon)
+ {
+ entry->next = sicon->notifications;
+ sicon->notifications = entry;
+ }
+ else
+ {
+ entry->next = iconless_notifications;
+ iconless_notifications = entry;
+ }
+
+ /* Ignore errors here for now. */
+ notify_notification_show (notification, NULL);
+
+ flush_gtk_events ();
+ UNBLOCK_INPUT;
+
+ RETURN_UNGCPRO (Qnil);
+}
+
+#endif /* HAVE_LIBNOTIFY */
+
+DEFUN ("status-iconp", Fstatus_iconp, Sstatus_iconp,
+ 1, 1, 0, doc: /* Return non-nil if OBJECT is a status icon. */)
+ (object)
+ Lisp_Object object;
+{
+ return SYMBOLP (object) && find_status_icon (object) ? Qt : Qnil;
+}
+
+DEFUN ("status-icon-handle-click-event", Fstatus_icon_handle_click_event,
+ Sstatus_icon_handle_click_event,
+ 1, 1, "e",
+ doc: /* Internal handler for status icon click events.
+A status icon click event is generated in response to the user clicking
+on a status icon. This handler calls the specified callback function,
+if any.*/)
+ (event)
+ Lisp_Object event;
+{
+ Lisp_Object fun;
+ struct gcpro gcpro1;
+
+ /* Just ignore garbage. */
+ if (! CONSP (event) || ! EQ (XCAR (event), Qstatus_icon_click_event)
+ || ! FUNCTIONP (XCDR (event)))
+ return Qnil;
+
+ GCPRO1 (fun);
+ fun = XCDR (event);
+ Ffuncall (1, &fun);
+ RETURN_UNGCPRO (Qnil);
+}
+
+void
+syms_of_systray ()
+{
+ struct status_icon *sicon;
+
+ Qblinking = intern ("blinking");
+ staticpro (&Qblinking);
+ Qclick_callback = intern ("click-callback");
+ staticpro (&Qclick_callback);
+ QCbody = intern (":body");
+ staticpro (&QCbody);
+ QCtimeout = intern (":timeout");
+ staticpro (&QCtimeout);
+ QCurgency = intern (":urgency");
+ staticpro (&QCurgency);
+ QCicon = intern (":icon");
+ staticpro (&QCicon);
+ QCaction = intern (":action");
+ staticpro (&QCaction);
+ QCclosed_callback = intern (":closed-callback");
+ staticpro (&QCclosed_callback);
+ Qlow = intern ("low");
+ staticpro (&Qlow);
+ Qcritical = intern ("critical");
+ staticpro (&Qcritical);
+ Qwarning = intern ("warning");
+ staticpro (&Qwarning);
+ Qquestion = intern ("question");
+ staticpro (&Qquestion);
+ Qerror = intern ("error");
+ staticpro (&Qerror);
+ Qpopup_menu = intern ("popup-menu");
+ staticpro (&Qpopup_menu);
+
+ defsubr (&Smake_status_icon);
+ defsubr (&Smodify_status_icon_parameters);
+ defsubr (&Sdelete_status_icon);
+ defsubr (&Sstatus_icon_parameters);
+ defsubr (&Sstatus_icon_list);
+#ifdef HAVE_LIBNOTIFY
+ defsubr (&Sshow_status_icon_message);
+#endif /* HAVE_LIBNOTIFY */
+ defsubr (&Sstatus_iconp);
+ defsubr (&Sstatus_icon_handle_click_event);
+
+ Fprovide (intern ("systray"), Qnil);
+}
+
+void
+mark_status_icons ()
+{
+ struct status_icon *iter;
+ struct notification_list *niter;
+
+ for (iter = all_icons; iter; iter = iter->next)
+ {
+ mark_object (iter->name);
+ mark_object (iter->param_alist);
+
+ for (niter = iter->notifications; niter; niter = niter->next)
+ mark_object (niter->list);
+ }
+
+ for (niter = iconless_notifications; niter; niter = niter->next)
+ mark_object (niter->list);
+}
+
+#else /* HAVE_GTK_STATUS_ICON */
+
+void
+syms_of_systray ()
+{
+ /* Nothing. */
+}
+
+#endif
Index: src/termhooks.h
===================================================================
RCS file: /sources/emacs/emacs/src/termhooks.h,v
retrieving revision 1.91
diff -u -r1.91 termhooks.h
--- src/termhooks.h 8 Jan 2008 20:44:20 -0000 1.91
+++ src/termhooks.h 3 Feb 2008 06:54:47 -0000
@@ -201,6 +201,8 @@
, DBUS_EVENT
#endif
+ , STATUS_ICON_CLICK_EVENT
+
#ifdef WINDOWSNT
/* Generated when an APPCOMMAND event is received, in response to
Multimedia or Internet buttons on some keyboards.
Index: src/xmenu.c
===================================================================
RCS file: /sources/emacs/emacs/src/xmenu.c,v
retrieving revision 1.326
diff -u -r1.326 xmenu.c
--- src/xmenu.c 1 Feb 2008 16:00:56 -0000 1.326
+++ src/xmenu.c 3 Feb 2008 06:54:47 -0000
@@ -156,7 +156,7 @@
Lisp_Object, Lisp_Object));
static int update_frame_menubar P_ ((struct frame *));
static Lisp_Object xmenu_show P_ ((struct frame *, int, int, int, int,
- Lisp_Object, char **));
+ Lisp_Object, char **, void *, int));
static void keymap_panes P_ ((Lisp_Object *, int, int));
static void single_keymap_panes P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
int, int));
@@ -780,6 +780,10 @@
corner of WINDOW. (WINDOW may be a window or a frame object.)
This controls the position of the top left of the menu as a whole.
If POSITION is t, it means to use the current mouse position.
+POSITION can also be a list (STATUS-ICON BUTTON-TIME),
+where STATUS-ICON is a symbol used when creating a status icon and
+BUTTON is an integer. In this case the menu is displayed over the
+indicated status icon.
MENU is a specifier for a menu. For the simplest case, MENU is a keymap.
The menu items come from key bindings that have a menu string as well as
@@ -829,6 +833,8 @@
int for_click = 0;
int specpdl_count = SPECPDL_INDEX ();
struct gcpro gcpro1;
+ void *status_icon = NULL;
+ int status_button = 0;
#ifdef HAVE_MENUS
if (! NILP (position))
@@ -843,6 +849,16 @@
{
get_current_pos_p = 1;
}
+ else if (CONSP (position)
+ && (status_icon = get_status_icon_for_menu (XCAR (position))))
+ {
+ status_button = XINT (Fcar (Fcdr (position)));
+ for_click = 1;
+ /* Placate later code. */
+ XSETINT (x, 0);
+ XSETINT (y, 0);
+ window = selected_window;
+ }
else
{
tem = Fcar (position);
@@ -1029,7 +1045,8 @@
BLOCK_INPUT;
selection = xmenu_show (f, xpos, ypos, for_click,
- keymaps, title, &error_name);
+ keymaps, title, &error_name,
+ status_icon, status_button);
UNBLOCK_INPUT;
discard_menu_items ();
@@ -2664,18 +2681,22 @@
menu pops down.
menu_item_selection will be set to the selection. */
static void
-create_and_show_popup_menu (f, first_wv, x, y, for_click)
+create_and_show_popup_menu (f, first_wv, x, y, for_click,
+ status_icon, status_button)
FRAME_PTR f;
widget_value *first_wv;
int x;
int y;
int for_click;
+ void *status_icon;
+ int status_button;
{
int i;
GtkWidget *menu;
GtkMenuPositionFunc pos_func = 0; /* Pop up at pointer. */
struct next_popup_x_y popup_x_y;
int specpdl_count = SPECPDL_INDEX ();
+ gpointer user_data = &popup_x_y;
if (! FRAME_X_P (f))
abort ();
@@ -2702,6 +2723,14 @@
i = 0; /* gtk_menu_popup needs this to be 0 for a non-button popup. */
}
+#ifdef HAVE_GTK_STATUS_ICON
+ else if (status_icon)
+ {
+ user_data = status_icon;
+ i = status_button;
+ pos_func = gtk_status_icon_position_menu;
+ }
+#endif /* HAVE_GTK_STATUS_ICON */
else
{
for (i = 0; i < 5; i++)
@@ -2711,7 +2740,8 @@
/* Display the menu. */
gtk_widget_show_all (menu);
- gtk_menu_popup (GTK_MENU (menu), 0, 0, pos_func, &popup_x_y, i, 0);
+ gtk_menu_popup (GTK_MENU (menu), 0, 0, pos_func, user_data, i,
+ gtk_get_current_event_time ());
record_unwind_protect (pop_down_menu, make_save_value (menu, 0));
@@ -2772,14 +2802,18 @@
/* Pop up the menu for frame F defined by FIRST_WV at X/Y and loop until the
menu pops down.
- menu_item_selection will be set to the selection. */
+ menu_item_selection will be set to the selection.
+ The status-icon-related arguments are ignore in this implementation. */
static void
-create_and_show_popup_menu (f, first_wv, x, y, for_click)
+create_and_show_popup_menu (f, first_wv, x, y, for_click,
+ status_icon, status_button)
FRAME_PTR f;
widget_value *first_wv;
int x;
int y;
int for_click;
+ void *status_icon;
+ int status_button;
{
int i;
Arg av[2];
@@ -2848,7 +2882,8 @@
#endif /* not USE_GTK */
static Lisp_Object
-xmenu_show (f, x, y, for_click, keymaps, title, error)
+xmenu_show (f, x, y, for_click, keymaps, title, error,
+ status_icon, status_button)
FRAME_PTR f;
int x;
int y;
@@ -2856,6 +2891,8 @@
int keymaps;
Lisp_Object title;
char **error;
+ void *status_icon;
+ int status_button;
{
int i;
widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
@@ -3062,7 +3099,8 @@
menu_item_selection = 0;
/* Actually create and show the menu until popped down. */
- create_and_show_popup_menu (f, first_wv, x, y, for_click);
+ create_and_show_popup_menu (f, first_wv, x, y, for_click,
+ status_icon, status_button);
/* Free the widget_value objects we used to specify the contents. */
free_menubar_widget_value_tree (first_wv);
@@ -3523,6 +3561,10 @@
int keymaps;
Lisp_Object title;
char **error;
+ /* Note that status-icon-related arguments are ignored in this
+ implementation */
+ void *status_icon;
+ int status_button;
{
Window root;
XMenu *menu;
- RFC: status icon support again,
Tom Tromey <=