<commit_before>/* gtkstatusicon.c:
 *
 * Copyright (C) 2003 Sun Microsystems, Inc.
 * Copyright (C) 2005 <NAME> <<EMAIL>>
 * Copyright (C) 2005 Novell, Inc.
 * Copyright (C) 2006 Imendio AB
 *
 * 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:
 *	<NAME> <<EMAIL>>
 *	<NAME> <<EMAIL>>
 *	<NAME> <<EMAIL>>
 *	<NAME> <<EMAIL>>
 */

#include "config.h"
#include <string.h>

#include "gtkstatusicon.h"

#include "gtkintl.h"
#include "gtkiconfactory.h"
#include "gtkmain.h"
#include "gtkmarshalers.h"
#include "gtktrayicon.h"

#include "gtkprivate.h"
#include "gtkwidget.h"
#include "gtktooltip.h"

#ifdef GDK_WINDOWING_X11
#include "gdk/x11/gdkx.h"
#endif

#ifdef GDK_WINDOWING_WIN32
#include "gtkicontheme.h"
#include "gtklabel.h"

#include "win32/gdkwin32.h"
#define WM_GTK_TRAY_NOTIFICATION (WM_USER+1)
#endif

#ifdef GDK_WINDOWING_QUARTZ
#include "gtkicontheme.h"
#include "gtklabel.h"
#endif	

#include "gdkkeysyms.h"

#include "gtkalias.h"

#define BLINK_TIMEOUT 500

enum
{
  PROP_0,
  PROP_PIXBUF,
  PROP_FILE,
  PROP_STOCK,
  PROP_ICON_NAME,
  PROP_GICON,
  PROP_STORAGE_TYPE,
  PROP_SIZE,
  PROP_SCREEN,
  PROP_VISIBLE,
  PROP_ORIENTATION,
  PROP_EMBEDDED,
  PROP_BLINKING,
  PROP_HAS_TOOLTIP,
  PROP_TOOLTIP_TEXT,
  PROP_TOOLTIP_MARKUP,
  PROP_TITLE
};

enum 
{
  ACTIVATE_SIGNAL,
  POPUP_MENU_SIGNAL,
  SIZE_CHANGED_SIGNAL,
  BUTTON_PRESS_EVENT_SIGNAL,
  BUTTON_RELEASE_EVENT_SIGNAL,
  SCROLL_EVENT_SIGNAL,
  QUERY_TOOLTIP_SIGNAL,
  LAST_SIGNAL
};

static guint status_icon_signals [LAST_SIGNAL] = { 0 };

#ifdef GDK_WINDOWING_QUARTZ
#include "gtkstatusicon-quartz.c"
#endif

struct _GtkStatusIconPrivate
{
#ifdef GDK_WINDOWING_X11
  GtkWidget    *tray_icon;
  GtkWidget    *image;
#endif

#ifdef GDK_WINDOWING_WIN32
  GtkWidget     *dummy_widget;
  NOTIFYICONDATAW nid;
  gint          taskbar_top;
  gint		last_click_x, last_click_y;
  GtkOrientation orientation;
  gchar         *tooltip_text;
  gchar         *title;
#endif
	
#ifdef GDK_WINDOWING_QUARTZ
  GtkWidget     *dummy_widget;
  GtkQuartzStatusIcon *status_item;
  gchar         *tooltip_text;
  gchar         *title;
#endif

  gint          size;

  gint          image_width;
  gint          image_height;

  GtkImageType  storage_type;

  union
    {
      GdkPixbuf *pixbuf;
      gchar     *stock_id;
      gchar     *icon_name;
      GIcon     *gicon;
    } image_data;

  GdkPixbuf    *blank_icon;
  guint         blinking_timeout;

  guint         blinking : 1;
  guint         blink_off : 1;
  guint         visible : 1;
};

static GObject* gtk_status_icon_constructor      (GType                  type,
                                                  guint                  n_construct_properties,
                                                  GObjectConstructParam *construct_params);
static void     gtk_status_icon_finalize         (GObject        *object);
static void     gtk_status_icon_set_property     (GObject        *object,
						  guint           prop_id,
						  const GValue   *value,
						  GParamSpec     *pspec);
static void     gtk_status_icon_get_property     (GObject        *object,
						  guint           prop_id,
						  GValue         *value,
					          GParamSpec     *pspec);

#ifdef GDK_WINDOWING_X11
static void     gtk_status_icon_size_allocate    (GtkStatusIcon  *status_icon,
						  GtkAllocation  *allocation);
static void     gtk_status_icon_screen_changed   (GtkStatusIcon  *status_icon,
						  GdkScreen      *old_screen);
static void     gtk_status_icon_embedded_changed (GtkStatusIcon *status_icon);
static void     gtk_status_icon_orientation_changed (GtkStatusIcon *status_icon);
static gboolean gtk_status_icon_scroll           (GtkStatusIcon  *status_icon,
						  GdkEventScroll *event);
static gboolean gtk_status_icon_query_tooltip    (GtkStatusIcon *status_icon,
						  gint           x,
						  gint           y,
						  gboolean       keyboard_tip,
						  GtkTooltip    *tooltip);

static gboolean gtk_status_icon_key_press        (GtkStatusIcon  *status_icon,
						  GdkEventKey    *event);
static void     gtk_status_icon_popup_menu       (GtkStatusIcon  *status_icon);
#endif
static gboolean gtk_status_icon_button_press     (GtkStatusIcon  *status_icon,
						  GdkEventButton *event);
static gboolean gtk_status_icon_button_release   (GtkStatusIcon  *status_icon,
						  GdkEventButton *event);
static void     gtk_status_icon_disable_blinking (GtkStatusIcon  *status_icon);
static void     gtk_status_icon_reset_image_data (GtkStatusIcon  *status_icon);
static void     gtk_status_icon_update_image    (GtkStatusIcon *status_icon);

G_DEFINE_TYPE (GtkStatusIcon, gtk_status_icon, G_TYPE_OBJECT)

static void
gtk_status_icon_class_init (GtkStatusIconClass *class)
{
  GObjectClass *gobject_class = (GObjectClass *) class;

  gobject_class->constructor  = gtk_status_icon_constructor;
  gobject_class->finalize     = gtk_status_icon_finalize;
  gobject_class->set_property = gtk_status_icon_set_property;
  gobject_class->get_property = gtk_status_icon_get_property;

  class->button_press_event   = NULL;
  class->button_release_event = NULL;
  class->scroll_event         = NULL;
  class->query_tooltip        = NULL;

  g_object_class_install_property (gobject_class,
				   PROP_PIXBUF,
				   g_param_spec_object ("pixbuf",
							P_("Pixbuf"),
							P_("A GdkPixbuf to display"),
							GDK_TYPE_PIXBUF,
							GTK_PARAM_READWRITE));

  g_object_class_install_property (gobject_class,
				   PROP_FILE,
				   g_param_spec_string ("file",
							P_("Filename"),
							P_("Filename to load and display"),
							NULL,
							GTK_PARAM_WRITABLE));

  g_object_class_install_property (gobject_class,
				   PROP_STOCK,
				   g_param_spec_string ("stock",
							P_("Stock ID"),
							P_("Stock ID for a stock image to display"),
							NULL,
							GTK_PARAM_READWRITE));
  
  g_object_class_install_property (gobject_class,
                                   PROP_ICON_NAME,
                                   g_param_spec_string ("icon-name",
                                                        P_("Icon Name"),
                                                        P_("The name of the icon from the icon theme"),
                                                        NULL,
                                                        GTK_PARAM_READWRITE));

  /**
   * GtkStatusIcon:gicon:
   *
   * The #GIcon displayed in the #GtkStatusIcon. For themed icons,
   * the image will be updated automatically if the theme changes.
   *
   * Since: 2.14
   */
  g_object_class_install_property (gobject_class,
                                   PROP_GICON,
                                   g_param_spec_object ("gicon",
                                                        P_("GIcon"),
                                                        P_("The GIcon being displayed"),
                                                        G_TYPE_ICON,
                                                        GTK_PARAM_READWRITE));

  g_object_class_install_property (gobject_class,
				   PROP_STORAGE_TYPE,
				   g_param_spec_enum ("storage-type",
						      P_("Storage type"),
						      P_("The representation being used for image data"),
						      GTK_TYPE_IMAGE_TYPE,
						      GTK_IMAGE_EMPTY,
						      GTK_PARAM_READABLE));

  g_object_class_install_property (gobject_class,
				   PROP_SIZE,
				   g_param_spec_int ("size",
						     P_("Size"),
						     P_("The size of the icon"),
						     0,
						     G_MAXINT,
						     0,
						     GTK_PARAM_READABLE));

  g_object_class_install_property (gobject_class,
				   PROP_SCREEN,
				   g_param_spec_object ("screen",
 							P_("Screen"),
 							P_("The screen where this status icon will be displayed"),
							GDK_TYPE_SCREEN,
 							GTK_PARAM_READWRITE));

  /**
   * GtkStatusIcon:blinking:
   *
   * Whether or not the status icon is blinking.
   *
   * Deprecated: 2.22: This property will be removed in GTK+ 3
   */
  g_object_class_install_property (gobject_class,
				   PROP_BLINKING,
				   g_param_spec_boolean ("blinking",
							 P_("Blinking"),
							 P_("Whether or not the status icon is blinking"),
							 FALSE,
							 GTK_PARAM_READWRITE | G_PARAM_DEPRECATED));

  g_object_class_install_property (gobject_class,
				   PROP_VISIBLE,
				   g_param_spec_boolean ("visible",
							 P_("Visible"),
							 P_("Whether or not the status icon is visible"),
							 TRUE,
							 GTK_PARAM_READWRITE));


  /**
   * GtkStatusIcon:embedded: 
   *
   * %TRUE if the statusicon is embedded in a notification area.
   *
   * Since: 2.12
   */
  g_object_class_install_property (gobject_class,
				   PROP_EMBEDDED,
				   g_param_spec_boolean ("embedded",
							 P_("Embedded"),
							 P_("Whether or not the status icon is embedded"),
							 FALSE,
							 GTK_PARAM_READABLE));

  /**
   * GtkStatusIcon:orientation:
   *
   * The orientation of the tray in which the statusicon 
   * is embedded. 
   *
   * Since: 2.12
   */
  g_object_class_install_property (gobject_class,
				   PROP_ORIENTATION,
				   g_param_spec_enum ("orientation",
						      P_("Orientation"),
						      P_("The orientation of the tray"),
						      GTK_TYPE_ORIENTATION,
						      GTK_ORIENTATION_HORIZONTAL,
						      GTK_PARAM_READABLE));

/**
 * GtkStatusIcon:has-tooltip:
 *
 * Enables or disables the emission of #GtkStatusIcon::query-tooltip on
 * @status_icon.  A value of %TRUE indicates that @status_icon can have a
 * tooltip, in this case the status icon will be queried using
 * #GtkStatusIcon::query-tooltip to determine whether it will provide a
 * tooltip or not.
 *
 * Note that setting this property to %TRUE for the first time will change
 * the event masks of the windows of this status icon to include leave-notify
 * and motion-notify events. This will not be undone when the property is set
 * to %FALSE again.
 *
 * Whether this property is respected is platform dependent.
 * For plain text tooltips, use #GtkStatusIcon:tooltip-text in preference.
 *
 * Since: 2.16
 */
  g_object_class_install_property (gobject_class,
				   PROP_HAS_TOOLTIP,
				   g_param_spec_boolean ("has-tooltip",
 							 P_("Has tooltip"),
 							 P_("Whether this tray icon has a tooltip"),
 							 FALSE,
 							 GTK_PARAM_READWRITE));
  /**
   * GtkStatusIcon:tooltip-text:
   *
   * Sets the text of tooltip to be the given string.
   *
   * Also see gtk_tooltip_set_text().
   *
   * This is a convenience property which will take care of getting the
   * tooltip shown if the given string is not %NULL.
   * #GtkStatusIcon:has-tooltip will automatically be set to %TRUE and
   * the default handler for the #GtkStatusIcon::query-tooltip signal
   * will take care of displaying the tooltip.
   *
   * Note that some platforms have limitations on the length of tooltips
   * that they allow on status icons, e.g. Windows only shows the first
   * 64 characters.
   *
   * Since: 2.16
   */
  g_object_class_install_property (gobject_class,
                                   PROP_TOOLTIP_TEXT,
                                   g_param_spec_string ("tooltip-text",
                                                        P_("Tooltip Text"),
                                                        P_("The contents of the tooltip for this widget"),
                                                        NULL,
                                                        GTK_PARAM_READWRITE));
  /**
   * GtkStatusIcon:tooltip-markup:
   *
   * Sets the text of tooltip to be the given string, which is marked up
   * with the <link linkend="PangoMarkupFormat">Pango text markup 
   * language</link>. Also see gtk_tooltip_set_markup().
   *
   * This is a convenience property which will take care of getting the
   * tooltip shown if the given string is not %NULL.
   * #GtkStatusIcon:has-tooltip will automatically be set to %TRUE and
   * the default handler for the #GtkStatusIcon::query-tooltip signal
   * will take care of displaying the tooltip.
   *
   * On some platforms, embedded markup will be ignored.
   *
   * Since: 2.16
   */
  g_object_class_install_property (gobject_class,
				   PROP_TOOLTIP_MARKUP,
				   g_param_spec_string ("tooltip-markup",
 							P_("Tooltip markup"),
							P_("The contents of the tooltip for this tray icon"),
							NULL,
							GTK_PARAM_READWRITE));


  /**
   * GtkStatusIcon:title:
   *
   * The title of this tray icon. This should be a short, human-readable,
   * localized string describing the tray icon. It may be used by tools
   * like screen readers to render the tray icon.
   *
   * Since: 2.18
   */
  g_object_class_install_property (gobject_class,
                                   PROP_TITLE,
                                   g_param_spec_string ("title",
                                                        P_("Title"),
                                                        P_("The title of this tray icon"),
                                                        NULL,
                                                        GTK_PARAM_READWRITE));

  /**
   * GtkStatusIcon::activate:
   * @status_icon: the object which received the signal
   *
   * Gets emitted when the user activates the status icon. 
   * If and how status icons can activated is platform-dependent.
   *
   * Unlike most G_SIGNAL_ACTION signals, this signal is meant to 
   * be used by applications and should be wrapped by language bindings.
   *
   * Since: 2.10
   */
  status_icon_signals [ACTIVATE_SIGNAL] =
    g_signal_new (I_("activate"),
		  G_TYPE_FROM_CLASS (gobject_class),
		  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
		  G_STRUCT_OFFSET (GtkStatusIconClass, activate),
		  NULL,
		  NULL,
		  g_cclosure_marshal_VOID__VOID,
		  G_TYPE_NONE,
		  0);

  /**
   * GtkStatusIcon::popup-menu:
   * @status_icon: the object which received the signal
   * @button: the button that was pressed, or 0 if the 
   *   signal is not emitted in response to a button press event
   * @activate_time: the timestamp of the event that
   *   triggered the signal emission
   *
   * Gets emitted when the user brings up the context menu
   * of the status icon. Whether status icons can have context 
   * menus and how these are activated is platform-dependent.
   *
   * The @button and @activate_time parameters should be 
   * passed as the last to arguments to gtk_menu_popup().
   *
   * Unlike most G_SIGNAL_ACTION signals, this signal is meant to 
   * be used by applications and should be wrapped by language bindings.
   *
   * Since: 2.10
   */
  status_icon_signals [POPUP_MENU_SIGNAL] =
    g_signal_new (I_("popup-menu"),
		  G_TYPE_FROM_CLASS (gobject_class),
		  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
		  G_STRUCT_OFFSET (GtkStatusIconClass, popup_menu),
		  NULL,
		  NULL,
		  _gtk_marshal_VOID__UINT_UINT,
		  G_TYPE_NONE,
		  2,
		  G_TYPE_UINT,
		  G_TYPE_UINT);

  /**
   * GtkStatusIcon::size-changed:
   * @status_icon: the object which received the signal
   * @size: the new size
   *
   * Gets emitted when the size available for the image
   * changes, e.g. because the notification area got resized.
   *
   * Return value: %TRUE if the icon was updated for the new
   * size. Otherwise, GTK+ will scale the icon as necessary.
   *
   * Since: 2.10
   */
  status_icon_signals [SIZE_CHANGED_SIGNAL] =
    g_signal_new (I_("size-changed"),
		  G_TYPE_FROM_CLASS (gobject_class),
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkStatusIconClass, size_changed),
		  g_signal_accumulator_true_handled,
		  NULL,
		  _gtk_marshal_BOOLEAN__INT,
		  G_TYPE_BOOLEAN,
		  1,
		  G_TYPE_INT);

  /**
   * GtkStatusIcon::button-press-event:
   * @status_icon: the object which received the signal
   * @event: the #GdkEventButton which triggered this signal
   *
   * The ::button-press-event signal will be emitted when a button
   * (typically from a mouse) is pressed.
   *
   * Whether this event is emitted is platform-dependent.  Use the ::activate
   * and ::popup-menu signals in preference.
   *
   * Return value: %TRUE to stop other handlers from being invoked
   * for the event. %FALSE to propagate the event further.
   *
   * Since: 2.14
   */
  status_icon_signals [BUTTON_PRESS_EVENT_SIGNAL] =
    g_signal_new (I_("button_press_event"),
		  G_TYPE_FROM_CLASS (gobject_class),
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkStatusIconClass, button_press_event),
		  g_signal_accumulator_true_handled, NULL,
		  _gtk_marshal_BOOLEAN__BOXED,
		  G_TYPE_BOOLEAN, 1,
		  GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);

  /**
   * GtkStatusIcon::button-release-event:
   * @status_icon: the object which received the signal
   * @event: the #GdkEventButton which triggered this signal
   *
   * The ::button-release-event signal will be emitted when a button
   * (typically from a mouse) is released.
   *
   * Whether this event is emitted is platform-dependent.  Use the ::activate
   * and ::popup-menu signals in preference.
   *
   * Return value: %TRUE to stop other handlers from being invoked
   * for the event. %FALSE to propagate the event further.
   *
   * Since: 2.14
   */
  status_icon_signals [BUTTON_RELEASE_EVENT_SIGNAL] =
    g_signal_new (I_("button_release_event"),
		  G_TYPE_FROM_CLASS (gobject_class),
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkStatusIconClass, button_release_event),
		  g_signal_accumulator_true_handled, NULL,
		  _gtk_marshal_BOOLEAN__BOXED,
		  G_TYPE_BOOLEAN, 1,
		  GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);

  /**
   * GtkStatusIcon::scroll-event:
   * @status_icon: the object which received the signal.
   * @event: the #GdkEventScroll which triggered this signal
   *
   * The ::scroll-event signal is emitted when a button in the 4 to 7
   * range is pressed. Wheel mice are usually configured to generate 
   * button press events for buttons 4 and 5 when the wheel is turned.
   *
   * Whether this event is emitted is platform-dependent.
   *
   * Returns: %TRUE to stop other handlers from being invoked for the event. 
   *   %FALSE to propagate the event further.
   */
  status_icon_signals[SCROLL_EVENT_SIGNAL] =
    g_signal_new (I_("scroll_event"),
		  G_TYPE_FROM_CLASS (gobject_class),
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkStatusIconClass, scroll_event),
		  g_signal_accumulator_true_handled, NULL,
		  _gtk_marshal_BOOLEAN__BOXED,
		  G_TYPE_BOOLEAN, 1,
		  GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);

  /**
   * GtkStatusIcon::query-tooltip:
   * @status_icon: the object which received the signal
   * @x: the x coordinate of the cursor position where the request has been
   *     emitted, relative to @status_icon
   * @y: the y coordinate of the cursor position where the request has been
   *     emitted, relative to @status_icon
   * @keyboard_mode: %TRUE if the tooltip was trigged using the keyboard
   * @tooltip: a #GtkTooltip
   *
   * Emitted when the #GtkSettings:gtk-tooltip-timeout has expired with the
   * cursor hovering above @status_icon; or emitted when @status_icon got
   * focus in keyboard mode.
   *
   * Using the given coordinates, the signal handler should determine
   * whether a tooltip should be shown for @status_icon. If this is
   * the case %TRUE should be returned, %FALSE otherwise. Note that if
   * @keyboard_mode is %TRUE, the values of @x and @y are undefined and
   * should not be used.
   *
   * The signal handler is free to manipulate @tooltip with the therefore
   * destined function calls.
   *
   * Whether this signal is emitted is platform-dependent.
   * For plain text tooltips, use #GtkStatusIcon:tooltip-text in preference.
   *
   * Returns: %TRUE if @tooltip should be shown right now, %FALSE otherwise.
   *
   * Since: 2.16
   */
  status_icon_signals [QUERY_TOOLTIP_SIGNAL] =
    g_signal_new (I_("query_tooltip"),
		  G_TYPE_FROM_CLASS (gobject_class),
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkStatusIconClass, query_tooltip),
		  g_signal_accumulator_true_handled, NULL,
		  _gtk_marshal_BOOLEAN__INT_INT_BOOLEAN_OBJECT,
		  G_TYPE_BOOLEAN, 4,
		  G_TYPE_INT,
		  G_TYPE_INT,
		  G_TYPE_BOOLEAN,
		  GTK_TYPE_TOOLTIP);

  g_type_class_add_private (class, sizeof (GtkStatusIconPrivate));
}

#ifdef GDK_WINDOWING_WIN32

static void
build_button_event (GtkStatusIconPrivate *priv,
		    GdkEventButton       *e,
		    guint                 button)
{
  POINT pos;
  GdkRectangle monitor0;

  /* We know that gdk/win32 puts the primary monitor at index 0 */
  gdk_screen_get_monitor_geometry (gdk_screen_get_default (), 0, &monitor0);
  e->window = g_object_ref (gdk_get_default_root_window ());
  e->send_event = TRUE;
  e->time = GetTickCount ();
  GetCursorPos (&pos);
  priv->last_click_x = e->x = pos.x + monitor0.x;
  priv->last_click_y = e->y = pos.y + monitor0.y;
  e->axes = NULL;
  e->state = 0;
  e->button = button;
  e->device = gdk_display_get_default ()->core_pointer;
  e->x_root = e->x;
  e->y_root = e->y;
}

typedef struct
{
  GtkStatusIcon *status_icon;
  GdkEventButton *event;
} ButtonCallbackData;

static gboolean
button_callback (gpointer data)
{
  ButtonCallbackData *bc = (ButtonCallbackData *) data;

  if (bc->event->type == GDK_BUTTON_PRESS)
    gtk_status_icon_button_press (bc->status_icon, bc->event);
  else
    gtk_status_icon_button_release (bc->status_icon, bc->event);

  gdk_event_free ((GdkEvent *) bc->event);
  g_free (data);

  return FALSE;
}

static UINT taskbar_created_msg = 0;
static GSList *status_icons = NULL;

static LRESULT CALLBACK
wndproc (HWND   hwnd,
	 UINT   message,
	 WPARAM wparam,
	 LPARAM lparam)
{
  if (message == taskbar_created_msg)
    {
      GSList *rover;

      for (rover = status_icons; rover != NULL; rover = rover->next)
	{
	  GtkStatusIcon *status_icon = GTK_STATUS_ICON (rover->data);
	  GtkStatusIconPrivate *priv = status_icon->priv;

	  priv->nid.hWnd = hwnd;
	  priv->nid.uID = GPOINTER_TO_UINT (status_icon);
	  priv->nid.uCallbackMessage = WM_GTK_TRAY_NOTIFICATION;
	  priv->nid.uFlags = NIF_MESSAGE;

	  if (!Shell_NotifyIconW (NIM_ADD, &priv->nid))
	    {
	      g_warning (G_STRLOC ": Shell_NotifyIcon(NIM_ADD) failed");
	      priv->nid.hWnd = NULL;
	      continue;
	    }

	  gtk_status_icon_update_image (status_icon);
	}
      return 0;
    }

  if (message == WM_GTK_TRAY_NOTIFICATION)
    {
      ButtonCallbackData *bc;
      guint button;
      
      switch (lparam)
	{
	case WM_LBUTTONDOWN:
	  button = 1;
	  goto buttondown0;

	case WM_MBUTTONDOWN:
	  button = 2;
	  goto buttondown0;

	case WM_RBUTTONDOWN:
	  button = 3;
	  goto buttondown0;

	case WM_XBUTTONDOWN:
	  if (HIWORD (wparam) == XBUTTON1)
	    button = 4;
	  else
	    button = 5;

	buttondown0:
	  bc = g_new (ButtonCallbackData, 1);
	  bc->event = (GdkEventButton *) gdk_event_new (GDK_BUTTON_PRESS);
	  bc->status_icon = GTK_STATUS_ICON (wparam);
	  build_button_event (bc->status_icon->priv, bc->event, button);
	  g_idle_add (button_callback, bc);
	  break;

	case WM_LBUTTONUP:
	  button = 1;
	  goto buttonup0;

	case WM_MBUTTONUP:
	  button = 2;
	  goto buttonup0;

	case WM_RBUTTONUP:
	  button = 3;
	  goto buttonup0;

	case WM_XBUTTONUP:
	  if (HIWORD (wparam) == XBUTTON1)
	    button = 4;
	  else
	    button = 5;

	buttonup0:
	  bc = g_new (ButtonCallbackData, 1);
	  bc->event = (GdkEventButton *) gdk_event_new (GDK_BUTTON_RELEASE);
	  bc->status_icon = GTK_STATUS_ICON (wparam);
	  build_button_event (bc->status_icon->priv, bc->event, button);
	  g_idle_add (button_callback, bc);
	  break;

	default :
	  break;
	}
	return 0;
    }
  else
    {
      return DefWindowProc (hwnd, message, wparam, lparam);
    }
}

static HWND
create_tray_observer (void)
{
  WNDCLASS    wclass;
  static HWND hwnd = NULL;
  ATOM        klass;
  HINSTANCE   hmodule = GetModuleHandle (NULL);

  if (hwnd)
    return hwnd;

  taskbar_created_msg = RegisterWindowMessage("TaskbarCreated");

  memset (&wclass, 0, sizeof(WNDCLASS));
  wclass.lpszClassName = "gtkstatusicon-observer";
  wclass.lpfnWndProc   = wndproc;
  wclass.hInstance     = hmodule;

  klass = RegisterClass (&wclass);
  if (!klass)
    return NULL;

  hwnd = CreateWindow (MAKEINTRESOURCE (klass),
                       NULL, WS_POPUP,
                       0, 0, 1, 1, NULL, NULL,
                       hmodule, NULL);
  if (!hwnd)
    {
      UnregisterClass (MAKEINTRESOURCE(klass), hmodule);
      return NULL;
    }

  return hwnd;
}

#endif

static void
gtk_status_icon_init (GtkStatusIcon *status_icon)
{
  GtkStatusIconPrivate *priv;

  priv = G_TYPE_INSTANCE_GET_PRIVATE (status_icon, GTK_TYPE_STATUS_ICON,
				      GtkStatusIconPrivate);
  status_icon->priv = priv;
  
  priv->storage_type = GTK_IMAGE_EMPTY;
  priv->visible      = TRUE;

#ifdef GDK_WINDOWING_X11
  priv->size         = 0;
  priv->image_width  = 0;
  priv->image_height = 0;

  priv->tray_icon = GTK_WIDGET (_gtk_tray_icon_new (NULL));

  gtk_widget_add_events (GTK_WIDGET (priv->tray_icon),
			 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
			 GDK_SCROLL_MASK);

  g_signal_connect_swapped (priv->tray_icon, "key-press-event",
			    G_CALLBACK (gtk_status_icon_key_press), status_icon);
  g_signal_connect_swapped (priv->tray_icon, "popup-menu",
			    G_CALLBACK (gtk_status_icon_popup_menu), status_icon);
  g_signal_connect_swapped (priv->tray_icon, "notify::embedded",
			    G_CALLBACK (gtk_status_icon_embedded_changed), status_icon);
  g_signal_connect_swapped (priv->tray_icon, "notify::orientation",
			    G_CALLBACK (gtk_status_icon_orientation_changed), status_icon);
  g_signal_connect_swapped (priv->tray_icon, "button-press-event",
			    G_CALLBACK (gtk_status_icon_button_press), status_icon);
  g_signal_connect_swapped (priv->tray_icon, "button-release-event",
			    G_CALLBACK (gtk_status_icon_button_release), status_icon);
  g_signal_connect_swapped (priv->tray_icon, "scroll-event",
			    G_CALLBACK (gtk_status_icon_scroll), status_icon);
  g_signal_connect_swapped (priv->tray_icon, "query-tooltip",
			    G_CALLBACK (gtk_status_icon_query_tooltip), status_icon);
  g_signal_connect_swapped (priv->tray_icon, "screen-changed",
		    	    G_CALLBACK (gtk_status_icon_screen_changed), status_icon);
  priv->image = gtk_image_new ();
  gtk_widget_set_can_focus (priv->image, TRUE);
  gtk_container_add (GTK_CONTAINER (priv->tray_icon), priv->image);
  gtk_widget_show (priv->image);

  g_signal_connect_swapped (priv->image, "size-allocate",
			    G_CALLBACK (gtk_status_icon_size_allocate), status_icon);

#endif

#ifdef GDK_WINDOWING_WIN32

  /* Get position and orientation of Windows taskbar. */
  {
    APPBARDATA abd;
    
    abd.cbSize = sizeof (abd);
    SHAppBarMessage (ABM_GETTASKBARPOS, &abd);
    if (abd.rc.bottom - abd.rc.top > abd.rc.right - abd.rc.left)
      priv->orientation = GTK_ORIENTATION_VERTICAL;
    else
      priv->orientation = GTK_ORIENTATION_HORIZONTAL;

    priv->taskbar_top = abd.rc.top;
  }

  priv->last_click_x = priv->last_click_y = 0;

  /* Are the system tray icons always 16 pixels square? */
  priv->size         = 16;
  priv->image_width  = 16;
  priv->image_height = 16;

  priv->dummy_widget = gtk_label_new ("");

  memset (&priv->nid, 0, sizeof (priv->nid));

  priv->nid.hWnd = create_tray_observer ();
  priv->nid.uID = GPOINTER_TO_UINT (status_icon);
  priv->nid.uCallbackMessage = WM_GTK_TRAY_NOTIFICATION;
  priv->nid.uFlags = NIF_MESSAGE;

  /* To help win7 identify the icon create it with an application "unique" tip */
  if (g_get_prgname ())
  {
    WCHAR *wcs = g_utf8_to_utf16 (g_get_prgname (), -1, NULL, NULL, NULL);

    priv->nid.uFlags |= NIF_TIP;
    wcsncpy (priv->nid.szTip, wcs, G_N_ELEMENTS (priv->nid.szTip) - 1);
    priv->nid.szTip[G_N_ELEMENTS (priv->nid.szTip) - 1] = 0;
    g_free (wcs);
  }

  if (!Shell_NotifyIconW (NIM_ADD, &priv->nid))
    {
      g_warning (G_STRLOC ": Shell_NotifyIcon(NIM_ADD) failed");
      priv->nid.hWnd = NULL;
    }

  status_icons = g_slist_append (status_icons, status_icon);

#endif
	
#ifdef GDK_WINDOWING_QUARTZ
  priv->dummy_widget = gtk_label_new ("");<commit_msg>statusicon: fix crash storing pointer in UINT<commit_after>/* gtkstatusicon.c:
 *
 * Copyright (C) 2003 Sun Microsystems, Inc.
 * Copyright (C) 2005 <NAME> <<EMAIL>>
 * Copyright (C) 2005 Novell, Inc.
 * Copyright (C) 2006 Imendio AB
 *
 * 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:
 *	<NAME> <<EMAIL>>
 *	<NAME> <<EMAIL>>
 *	<NAME> <<EMAIL>>
 *	<NAME> <<EMAIL>>
 */

#include "config.h"
#include <string.h>

#include "gtkstatusicon.h"

#include "gtkintl.h"
#include "gtkiconfactory.h"
#include "gtkmain.h"
#include "gtkmarshalers.h"
#include "gtktrayicon.h"

#include "gtkprivate.h"
#include "gtkwidget.h"
#include "gtktooltip.h"

#ifdef GDK_WINDOWING_X11
#include "gdk/x11/gdkx.h"
#endif

#ifdef GDK_WINDOWING_WIN32
#include "gtkicontheme.h"
#include "gtklabel.h"

#include "win32/gdkwin32.h"
#define WM_GTK_TRAY_NOTIFICATION (WM_USER+1)
#endif

#ifdef GDK_WINDOWING_QUARTZ
#include "gtkicontheme.h"
#include "gtklabel.h"
#endif	

#include "gdkkeysyms.h"

#include "gtkalias.h"

#define BLINK_TIMEOUT 500

enum
{
  PROP_0,
  PROP_PIXBUF,
  PROP_FILE,
  PROP_STOCK,
  PROP_ICON_NAME,
  PROP_GICON,
  PROP_STORAGE_TYPE,
  PROP_SIZE,
  PROP_SCREEN,
  PROP_VISIBLE,
  PROP_ORIENTATION,
  PROP_EMBEDDED,
  PROP_BLINKING,
  PROP_HAS_TOOLTIP,
  PROP_TOOLTIP_TEXT,
  PROP_TOOLTIP_MARKUP,
  PROP_TITLE
};

enum 
{
  ACTIVATE_SIGNAL,
  POPUP_MENU_SIGNAL,
  SIZE_CHANGED_SIGNAL,
  BUTTON_PRESS_EVENT_SIGNAL,
  BUTTON_RELEASE_EVENT_SIGNAL,
  SCROLL_EVENT_SIGNAL,
  QUERY_TOOLTIP_SIGNAL,
  LAST_SIGNAL
};

static guint status_icon_signals [LAST_SIGNAL] = { 0 };

#ifdef GDK_WINDOWING_QUARTZ
#include "gtkstatusicon-quartz.c"
#endif

struct _GtkStatusIconPrivate
{
#ifdef GDK_WINDOWING_X11
  GtkWidget    *tray_icon;
  GtkWidget    *image;
#endif

#ifdef GDK_WINDOWING_WIN32
  GtkWidget     *dummy_widget;
  NOTIFYICONDATAW nid;
  gint          taskbar_top;
  gint		last_click_x, last_click_y;
  GtkOrientation orientation;
  gchar         *tooltip_text;
  gchar         *title;
#endif
	
#ifdef GDK_WINDOWING_QUARTZ
  GtkWidget     *dummy_widget;
  GtkQuartzStatusIcon *status_item;
  gchar         *tooltip_text;
  gchar         *title;
#endif

  gint          size;

  gint          image_width;
  gint          image_height;

  GtkImageType  storage_type;

  union
    {
      GdkPixbuf *pixbuf;
      gchar     *stock_id;
      gchar     *icon_name;
      GIcon     *gicon;
    } image_data;

  GdkPixbuf    *blank_icon;
  guint         blinking_timeout;

  guint         blinking : 1;
  guint         blink_off : 1;
  guint         visible : 1;
};

static GObject* gtk_status_icon_constructor      (GType                  type,
                                                  guint                  n_construct_properties,
                                                  GObjectConstructParam *construct_params);
static void     gtk_status_icon_finalize         (GObject        *object);
static void     gtk_status_icon_set_property     (GObject        *object,
						  guint           prop_id,
						  const GValue   *value,
						  GParamSpec     *pspec);
static void     gtk_status_icon_get_property     (GObject        *object,
						  guint           prop_id,
						  GValue         *value,
					          GParamSpec     *pspec);

#ifdef GDK_WINDOWING_X11
static void     gtk_status_icon_size_allocate    (GtkStatusIcon  *status_icon,
						  GtkAllocation  *allocation);
static void     gtk_status_icon_screen_changed   (GtkStatusIcon  *status_icon,
						  GdkScreen      *old_screen);
static void     gtk_status_icon_embedded_changed (GtkStatusIcon *status_icon);
static void     gtk_status_icon_orientation_changed (GtkStatusIcon *status_icon);
static gboolean gtk_status_icon_scroll           (GtkStatusIcon  *status_icon,
						  GdkEventScroll *event);
static gboolean gtk_status_icon_query_tooltip    (GtkStatusIcon *status_icon,
						  gint           x,
						  gint           y,
						  gboolean       keyboard_tip,
						  GtkTooltip    *tooltip);

static gboolean gtk_status_icon_key_press        (GtkStatusIcon  *status_icon,
						  GdkEventKey    *event);
static void     gtk_status_icon_popup_menu       (GtkStatusIcon  *status_icon);
#endif
static gboolean gtk_status_icon_button_press     (GtkStatusIcon  *status_icon,
						  GdkEventButton *event);
static gboolean gtk_status_icon_button_release   (GtkStatusIcon  *status_icon,
						  GdkEventButton *event);
static void     gtk_status_icon_disable_blinking (GtkStatusIcon  *status_icon);
static void     gtk_status_icon_reset_image_data (GtkStatusIcon  *status_icon);
static void     gtk_status_icon_update_image    (GtkStatusIcon *status_icon);

G_DEFINE_TYPE (GtkStatusIcon, gtk_status_icon, G_TYPE_OBJECT)

static void
gtk_status_icon_class_init (GtkStatusIconClass *class)
{
  GObjectClass *gobject_class = (GObjectClass *) class;

  gobject_class->constructor  = gtk_status_icon_constructor;
  gobject_class->finalize     = gtk_status_icon_finalize;
  gobject_class->set_property = gtk_status_icon_set_property;
  gobject_class->get_property = gtk_status_icon_get_property;

  class->button_press_event   = NULL;
  class->button_release_event = NULL;
  class->scroll_event         = NULL;
  class->query_tooltip        = NULL;

  g_object_class_install_property (gobject_class,
				   PROP_PIXBUF,
				   g_param_spec_object ("pixbuf",
							P_("Pixbuf"),
							P_("A GdkPixbuf to display"),
							GDK_TYPE_PIXBUF,
							GTK_PARAM_READWRITE));

  g_object_class_install_property (gobject_class,
				   PROP_FILE,
				   g_param_spec_string ("file",
							P_("Filename"),
							P_("Filename to load and display"),
							NULL,
							GTK_PARAM_WRITABLE));

  g_object_class_install_property (gobject_class,
				   PROP_STOCK,
				   g_param_spec_string ("stock",
							P_("Stock ID"),
							P_("Stock ID for a stock image to display"),
							NULL,
							GTK_PARAM_READWRITE));
  
  g_object_class_install_property (gobject_class,
                                   PROP_ICON_NAME,
                                   g_param_spec_string ("icon-name",
                                                        P_("Icon Name"),
                                                        P_("The name of the icon from the icon theme"),
                                                        NULL,
                                                        GTK_PARAM_READWRITE));

  /**
   * GtkStatusIcon:gicon:
   *
   * The #GIcon displayed in the #GtkStatusIcon. For themed icons,
   * the image will be updated automatically if the theme changes.
   *
   * Since: 2.14
   */
  g_object_class_install_property (gobject_class,
                                   PROP_GICON,
                                   g_param_spec_object ("gicon",
                                                        P_("GIcon"),
                                                        P_("The GIcon being displayed"),
                                                        G_TYPE_ICON,
                                                        GTK_PARAM_READWRITE));

  g_object_class_install_property (gobject_class,
				   PROP_STORAGE_TYPE,
				   g_param_spec_enum ("storage-type",
						      P_("Storage type"),
						      P_("The representation being used for image data"),
						      GTK_TYPE_IMAGE_TYPE,
						      GTK_IMAGE_EMPTY,
						      GTK_PARAM_READABLE));

  g_object_class_install_property (gobject_class,
				   PROP_SIZE,
				   g_param_spec_int ("size",
						     P_("Size"),
						     P_("The size of the icon"),
						     0,
						     G_MAXINT,
						     0,
						     GTK_PARAM_READABLE));

  g_object_class_install_property (gobject_class,
				   PROP_SCREEN,
				   g_param_spec_object ("screen",
 							P_("Screen"),
 							P_("The screen where this status icon will be displayed"),
							GDK_TYPE_SCREEN,
 							GTK_PARAM_READWRITE));

  /**
   * GtkStatusIcon:blinking:
   *
   * Whether or not the status icon is blinking.
   *
   * Deprecated: 2.22: This property will be removed in GTK+ 3
   */
  g_object_class_install_property (gobject_class,
				   PROP_BLINKING,
				   g_param_spec_boolean ("blinking",
							 P_("Blinking"),
							 P_("Whether or not the status icon is blinking"),
							 FALSE,
							 GTK_PARAM_READWRITE | G_PARAM_DEPRECATED));

  g_object_class_install_property (gobject_class,
				   PROP_VISIBLE,
				   g_param_spec_boolean ("visible",
							 P_("Visible"),
							 P_("Whether or not the status icon is visible"),
							 TRUE,
							 GTK_PARAM_READWRITE));


  /**
   * GtkStatusIcon:embedded: 
   *
   * %TRUE if the statusicon is embedded in a notification area.
   *
   * Since: 2.12
   */
  g_object_class_install_property (gobject_class,
				   PROP_EMBEDDED,
				   g_param_spec_boolean ("embedded",
							 P_("Embedded"),
							 P_("Whether or not the status icon is embedded"),
							 FALSE,
							 GTK_PARAM_READABLE));

  /**
   * GtkStatusIcon:orientation:
   *
   * The orientation of the tray in which the statusicon 
   * is embedded. 
   *
   * Since: 2.12
   */
  g_object_class_install_property (gobject_class,
				   PROP_ORIENTATION,
				   g_param_spec_enum ("orientation",
						      P_("Orientation"),
						      P_("The orientation of the tray"),
						      GTK_TYPE_ORIENTATION,
						      GTK_ORIENTATION_HORIZONTAL,
						      GTK_PARAM_READABLE));

/**
 * GtkStatusIcon:has-tooltip:
 *
 * Enables or disables the emission of #GtkStatusIcon::query-tooltip on
 * @status_icon.  A value of %TRUE indicates that @status_icon can have a
 * tooltip, in this case the status icon will be queried using
 * #GtkStatusIcon::query-tooltip to determine whether it will provide a
 * tooltip or not.
 *
 * Note that setting this property to %TRUE for the first time will change
 * the event masks of the windows of this status icon to include leave-notify
 * and motion-notify events. This will not be undone when the property is set
 * to %FALSE again.
 *
 * Whether this property is respected is platform dependent.
 * For plain text tooltips, use #GtkStatusIcon:tooltip-text in preference.
 *
 * Since: 2.16
 */
  g_object_class_install_property (gobject_class,
				   PROP_HAS_TOOLTIP,
				   g_param_spec_boolean ("has-tooltip",
 							 P_("Has tooltip"),
 							 P_("Whether this tray icon has a tooltip"),
 							 FALSE,
 							 GTK_PARAM_READWRITE));
  /**
   * GtkStatusIcon:tooltip-text:
   *
   * Sets the text of tooltip to be the given string.
   *
   * Also see gtk_tooltip_set_text().
   *
   * This is a convenience property which will take care of getting the
   * tooltip shown if the given string is not %NULL.
   * #GtkStatusIcon:has-tooltip will automatically be set to %TRUE and
   * the default handler for the #GtkStatusIcon::query-tooltip signal
   * will take care of displaying the tooltip.
   *
   * Note that some platforms have limitations on the length of tooltips
   * that they allow on status icons, e.g. Windows only shows the first
   * 64 characters.
   *
   * Since: 2.16
   */
  g_object_class_install_property (gobject_class,
                                   PROP_TOOLTIP_TEXT,
                                   g_param_spec_string ("tooltip-text",
                                                        P_("Tooltip Text"),
                                                        P_("The contents of the tooltip for this widget"),
                                                        NULL,
                                                        GTK_PARAM_READWRITE));
  /**
   * GtkStatusIcon:tooltip-markup:
   *
   * Sets the text of tooltip to be the given string, which is marked up
   * with the <link linkend="PangoMarkupFormat">Pango text markup 
   * language</link>. Also see gtk_tooltip_set_markup().
   *
   * This is a convenience property which will take care of getting the
   * tooltip shown if the given string is not %NULL.
   * #GtkStatusIcon:has-tooltip will automatically be set to %TRUE and
   * the default handler for the #GtkStatusIcon::query-tooltip signal
   * will take care of displaying the tooltip.
   *
   * On some platforms, embedded markup will be ignored.
   *
   * Since: 2.16
   */
  g_object_class_install_property (gobject_class,
				   PROP_TOOLTIP_MARKUP,
				   g_param_spec_string ("tooltip-markup",
 							P_("Tooltip markup"),
							P_("The contents of the tooltip for this tray icon"),
							NULL,
							GTK_PARAM_READWRITE));


  /**
   * GtkStatusIcon:title:
   *
   * The title of this tray icon. This should be a short, human-readable,
   * localized string describing the tray icon. It may be used by tools
   * like screen readers to render the tray icon.
   *
   * Since: 2.18
   */
  g_object_class_install_property (gobject_class,
                                   PROP_TITLE,
                                   g_param_spec_string ("title",
                                                        P_("Title"),
                                                        P_("The title of this tray icon"),
                                                        NULL,
                                                        GTK_PARAM_READWRITE));

  /**
   * GtkStatusIcon::activate:
   * @status_icon: the object which received the signal
   *
   * Gets emitted when the user activates the status icon. 
   * If and how status icons can activated is platform-dependent.
   *
   * Unlike most G_SIGNAL_ACTION signals, this signal is meant to 
   * be used by applications and should be wrapped by language bindings.
   *
   * Since: 2.10
   */
  status_icon_signals [ACTIVATE_SIGNAL] =
    g_signal_new (I_("activate"),
		  G_TYPE_FROM_CLASS (gobject_class),
		  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
		  G_STRUCT_OFFSET (GtkStatusIconClass, activate),
		  NULL,
		  NULL,
		  g_cclosure_marshal_VOID__VOID,
		  G_TYPE_NONE,
		  0);

  /**
   * GtkStatusIcon::popup-menu:
   * @status_icon: the object which received the signal
   * @button: the button that was pressed, or 0 if the 
   *   signal is not emitted in response to a button press event
   * @activate_time: the timestamp of the event that
   *   triggered the signal emission
   *
   * Gets emitted when the user brings up the context menu
   * of the status icon. Whether status icons can have context 
   * menus and how these are activated is platform-dependent.
   *
   * The @button and @activate_time parameters should be 
   * passed as the last to arguments to gtk_menu_popup().
   *
   * Unlike most G_SIGNAL_ACTION signals, this signal is meant to 
   * be used by applications and should be wrapped by language bindings.
   *
   * Since: 2.10
   */
  status_icon_signals [POPUP_MENU_SIGNAL] =
    g_signal_new (I_("popup-menu"),
		  G_TYPE_FROM_CLASS (gobject_class),
		  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
		  G_STRUCT_OFFSET (GtkStatusIconClass, popup_menu),
		  NULL,
		  NULL,
		  _gtk_marshal_VOID__UINT_UINT,
		  G_TYPE_NONE,
		  2,
		  G_TYPE_UINT,
		  G_TYPE_UINT);

  /**
   * GtkStatusIcon::size-changed:
   * @status_icon: the object which received the signal
   * @size: the new size
   *
   * Gets emitted when the size available for the image
   * changes, e.g. because the notification area got resized.
   *
   * Return value: %TRUE if the icon was updated for the new
   * size. Otherwise, GTK+ will scale the icon as necessary.
   *
   * Since: 2.10
   */
  status_icon_signals [SIZE_CHANGED_SIGNAL] =
    g_signal_new (I_("size-changed"),
		  G_TYPE_FROM_CLASS (gobject_class),
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkStatusIconClass, size_changed),
		  g_signal_accumulator_true_handled,
		  NULL,
		  _gtk_marshal_BOOLEAN__INT,
		  G_TYPE_BOOLEAN,
		  1,
		  G_TYPE_INT);

  /**
   * GtkStatusIcon::button-press-event:
   * @status_icon: the object which received the signal
   * @event: the #GdkEventButton which triggered this signal
   *
   * The ::button-press-event signal will be emitted when a button
   * (typically from a mouse) is pressed.
   *
   * Whether this event is emitted is platform-dependent.  Use the ::activate
   * and ::popup-menu signals in preference.
   *
   * Return value: %TRUE to stop other handlers from being invoked
   * for the event. %FALSE to propagate the event further.
   *
   * Since: 2.14
   */
  status_icon_signals [BUTTON_PRESS_EVENT_SIGNAL] =
    g_signal_new (I_("button_press_event"),
		  G_TYPE_FROM_CLASS (gobject_class),
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkStatusIconClass, button_press_event),
		  g_signal_accumulator_true_handled, NULL,
		  _gtk_marshal_BOOLEAN__BOXED,
		  G_TYPE_BOOLEAN, 1,
		  GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);

  /**
   * GtkStatusIcon::button-release-event:
   * @status_icon: the object which received the signal
   * @event: the #GdkEventButton which triggered this signal
   *
   * The ::button-release-event signal will be emitted when a button
   * (typically from a mouse) is released.
   *
   * Whether this event is emitted is platform-dependent.  Use the ::activate
   * and ::popup-menu signals in preference.
   *
   * Return value: %TRUE to stop other handlers from being invoked
   * for the event. %FALSE to propagate the event further.
   *
   * Since: 2.14
   */
  status_icon_signals [BUTTON_RELEASE_EVENT_SIGNAL] =
    g_signal_new (I_("button_release_event"),
		  G_TYPE_FROM_CLASS (gobject_class),
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkStatusIconClass, button_release_event),
		  g_signal_accumulator_true_handled, NULL,
		  _gtk_marshal_BOOLEAN__BOXED,
		  G_TYPE_BOOLEAN, 1,
		  GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);

  /**
   * GtkStatusIcon::scroll-event:
   * @status_icon: the object which received the signal.
   * @event: the #GdkEventScroll which triggered this signal
   *
   * The ::scroll-event signal is emitted when a button in the 4 to 7
   * range is pressed. Wheel mice are usually configured to generate 
   * button press events for buttons 4 and 5 when the wheel is turned.
   *
   * Whether this event is emitted is platform-dependent.
   *
   * Returns: %TRUE to stop other handlers from being invoked for the event. 
   *   %FALSE to propagate the event further.
   */
  status_icon_signals[SCROLL_EVENT_SIGNAL] =
    g_signal_new (I_("scroll_event"),
		  G_TYPE_FROM_CLASS (gobject_class),
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkStatusIconClass, scroll_event),
		  g_signal_accumulator_true_handled, NULL,
		  _gtk_marshal_BOOLEAN__BOXED,
		  G_TYPE_BOOLEAN, 1,
		  GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);

  /**
   * GtkStatusIcon::query-tooltip:
   * @status_icon: the object which received the signal
   * @x: the x coordinate of the cursor position where the request has been
   *     emitted, relative to @status_icon
   * @y: the y coordinate of the cursor position where the request has been
   *     emitted, relative to @status_icon
   * @keyboard_mode: %TRUE if the tooltip was trigged using the keyboard
   * @tooltip: a #GtkTooltip
   *
   * Emitted when the #GtkSettings:gtk-tooltip-timeout has expired with the
   * cursor hovering above @status_icon; or emitted when @status_icon got
   * focus in keyboard mode.
   *
   * Using the given coordinates, the signal handler should determine
   * whether a tooltip should be shown for @status_icon. If this is
   * the case %TRUE should be returned, %FALSE otherwise. Note that if
   * @keyboard_mode is %TRUE, the values of @x and @y are undefined and
   * should not be used.
   *
   * The signal handler is free to manipulate @tooltip with the therefore
   * destined function calls.
   *
   * Whether this signal is emitted is platform-dependent.
   * For plain text tooltips, use #GtkStatusIcon:tooltip-text in preference.
   *
   * Returns: %TRUE if @tooltip should be shown right now, %FALSE otherwise.
   *
   * Since: 2.16
   */
  status_icon_signals [QUERY_TOOLTIP_SIGNAL] =
    g_signal_new (I_("query_tooltip"),
		  G_TYPE_FROM_CLASS (gobject_class),
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkStatusIconClass, query_tooltip),
		  g_signal_accumulator_true_handled, NULL,
		  _gtk_marshal_BOOLEAN__INT_INT_BOOLEAN_OBJECT,
		  G_TYPE_BOOLEAN, 4,
		  G_TYPE_INT,
		  G_TYPE_INT,
		  G_TYPE_BOOLEAN,
		  GTK_TYPE_TOOLTIP);

  g_type_class_add_private (class, sizeof (GtkStatusIconPrivate));
}

#ifdef GDK_WINDOWING_WIN32

static void
build_button_event (GtkStatusIconPrivate *priv,
		    GdkEventButton       *e,
		    guint                 button)
{
  POINT pos;
  GdkRectangle monitor0;

  /* We know that gdk/win32 puts the primary monitor at index 0 */
  gdk_screen_get_monitor_geometry (gdk_screen_get_default (), 0, &monitor0);
  e->window = g_object_ref (gdk_get_default_root_window ());
  e->send_event = TRUE;
  e->time = GetTickCount ();
  GetCursorPos (&pos);
  priv->last_click_x = e->x = pos.x + monitor0.x;
  priv->last_click_y = e->y = pos.y + monitor0.y;
  e->axes = NULL;
  e->state = 0;
  e->button = button;
  e->device = gdk_display_get_default ()->core_pointer;
  e->x_root = e->x;
  e->y_root = e->y;
}

typedef struct
{
  GtkStatusIcon *status_icon;
  GdkEventButton *event;
} ButtonCallbackData;

static gboolean
button_callback (gpointer data)
{
  ButtonCallbackData *bc = (ButtonCallbackData *) data;

  if (bc->event->type == GDK_BUTTON_PRESS)
    gtk_status_icon_button_press (bc->status_icon, bc->event);
  else
    gtk_status_icon_button_release (bc->status_icon, bc->event);

  gdk_event_free ((GdkEvent *) bc->event);
  g_free (data);

  return FALSE;
}

static UINT taskbar_created_msg = 0;
static GSList *status_icons = NULL;
static UINT status_icon_id = 0;

static GtkStatusIcon *
find_status_icon (UINT id)
{
  GSList *rover;

  for (rover = status_icons; rover != NULL; rover = rover->next)
    {
      GtkStatusIcon *status_icon = GTK_STATUS_ICON (rover->data);
      GtkStatusIconPrivate *priv = status_icon->priv;

      if (priv->nid.uID == id)
        return status_icon;
    }

  return NULL;
}

static LRESULT CALLBACK
wndproc (HWND   hwnd,
	 UINT   message,
	 WPARAM wparam,
	 LPARAM lparam)
{
  if (message == taskbar_created_msg)
    {
      GSList *rover;

      for (rover = status_icons; rover != NULL; rover = rover->next)
	{
	  GtkStatusIcon *status_icon = GTK_STATUS_ICON (rover->data);
	  GtkStatusIconPrivate *priv = status_icon->priv;

	  priv->nid.hWnd = hwnd;
	  priv->nid.uID = status_icon_id++;
	  priv->nid.uCallbackMessage = WM_GTK_TRAY_NOTIFICATION;
	  priv->nid.uFlags = NIF_MESSAGE;

	  if (!Shell_NotifyIconW (NIM_ADD, &priv->nid))
	    {
	      g_warning (G_STRLOC ": Shell_NotifyIcon(NIM_ADD) failed");
	      priv->nid.hWnd = NULL;
	      continue;
	    }

	  gtk_status_icon_update_image (status_icon);
	}
      return 0;
    }

  if (message == WM_GTK_TRAY_NOTIFICATION)
    {
      ButtonCallbackData *bc;
      guint button;
      
      switch (lparam)
	{
	case WM_LBUTTONDOWN:
	  button = 1;
	  goto buttondown0;

	case WM_MBUTTONDOWN:
	  button = 2;
	  goto buttondown0;

	case WM_RBUTTONDOWN:
	  button = 3;
	  goto buttondown0;

	case WM_XBUTTONDOWN:
	  if (HIWORD (wparam) == XBUTTON1)
	    button = 4;
	  else
	    button = 5;

	buttondown0:
	  bc = g_new (ButtonCallbackData, 1);
	  bc->event = (GdkEventButton *) gdk_event_new (GDK_BUTTON_PRESS);
	  bc->status_icon = find_status_icon (wparam);
	  build_button_event (bc->status_icon->priv, bc->event, button);
	  g_idle_add (button_callback, bc);
	  break;

	case WM_LBUTTONUP:
	  button = 1;
	  goto buttonup0;

	case WM_MBUTTONUP:
	  button = 2;
	  goto buttonup0;

	case WM_RBUTTONUP:
	  button = 3;
	  goto buttonup0;

	case WM_XBUTTONUP:
	  if (HIWORD (wparam) == XBUTTON1)
	    button = 4;
	  else
	    button = 5;

	buttonup0:
	  bc = g_new (ButtonCallbackData, 1);
	  bc->event = (GdkEventButton *) gdk_event_new (GDK_BUTTON_RELEASE);
	  bc->status_icon = find_status_icon (wparam);
	  build_button_event (bc->status_icon->priv, bc->event, button);
	  g_idle_add (button_callback, bc);
	  break;

	default :
	  break;
	}
	return 0;
    }
  else
    {
      return DefWindowProc (hwnd, message, wparam, lparam);
    }
}

static HWND
create_tray_observer (void)
{
  WNDCLASS    wclass;
  static HWND hwnd = NULL;
  ATOM        klass;
  HINSTANCE   hmodule = GetModuleHandle (NULL);

  if (hwnd)
    return hwnd;

  taskbar_created_msg = RegisterWindowMessage("TaskbarCreated");

  memset (&wclass, 0, sizeof(WNDCLASS));
  wclass.lpszClassName = "gtkstatusicon-observer";
  wclass.lpfnWndProc   = wndproc;
  wclass.hInstance     = hmodule;

  klass = RegisterClass (&wclass);
  if (!klass)
    return NULL;

  hwnd = CreateWindow (MAKEINTRESOURCE (klass),
                       NULL, WS_POPUP,
                       0, 0, 1, 1, NULL, NULL,
                       hmodule, NULL);
  if (!hwnd)
    {
      UnregisterClass (MAKEINTRESOURCE(klass), hmodule);
      return NULL;
    }

  return hwnd;
}

#endif

static void
gtk_status_icon_init (GtkStatusIcon *status_icon)
{
  GtkStatusIconPrivate *priv;

  priv = G_TYPE_INSTANCE_GET_PRIVATE (status_icon, GTK_TYPE_STATUS_ICON,
				      GtkStatusIconPrivate);
  status_icon->priv = priv;
  
  priv->storage_type = GTK_IMAGE_EMPTY;
  priv->visible      = TRUE;

#ifdef GDK_WINDOWING_X11
  priv->size         = 0;
  priv->image_width  = 0;
  priv->image_height = 0;

  priv->tray_icon = GTK_WIDGET (_gtk_tray_icon_new (NULL));

  gtk_widget_add_events (GTK_WIDGET (priv->tray_icon),
			 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
			 GDK_SCROLL_MASK);

  g_signal_connect_swapped (priv->tray_icon, "key-press-event",
			    G_CALLBACK (gtk_status_icon_key_press), status_icon);
  g_signal_connect_swapped (priv->tray_icon, "popup-menu",
			    G_CALLBACK (gtk_status_icon_popup_menu), status_icon);
  g_signal_connect_swapped (priv->tray_icon, "notify::embedded",
			    G_CALLBACK (gtk_status_icon_embedded_changed), status_icon);
  g_signal_connect_swapped (priv->tray_icon, "notify::orientation",
			    G_CALLBACK (gtk_status_icon_orientation_changed), status_icon);
  g_signal_connect_swapped (priv->tray_icon, "button-press-event",
			    G_CALLBACK (gtk_status_icon_button_press), status_icon);
  g_signal_connect_swapped (priv->tray_icon, "button-release-event",
			    G_CALLBACK (gtk_status_icon_button_release), status_icon);
  g_signal_connect_swapped (priv->tray_icon, "scroll-event",
			    G_CALLBACK (gtk_status_icon_scroll), status_icon);
  g_signal_connect_swapped (priv->tray_icon, "query-tooltip",
			    G_CALLBACK (gtk_status_icon_query_tooltip), status_icon);
  g_signal_connect_swapped (priv->tray_icon, "screen-changed",
		    	    G_CALLBACK (gtk_status_icon_screen_changed), status_icon);
  priv->image = gtk_image_new ();
  gtk_widget_set_can_focus (priv->image, TRUE);
  gtk_container_add (GTK_CONTAINER (priv->tray_icon), priv->image);
  gtk_widget_show (priv->image);

  g_signal_connect_swapped (priv->image, "size-allocate",
			    G_CALLBACK (gtk_status_icon_size_allocate), status_icon);

#endif

#ifdef GDK_WINDOWING_WIN32

  /* Get position and orientation of Windows taskbar. */
  {
    APPBARDATA abd;
    
    abd.cbSize = sizeof (abd);
    SHAppBarMessage (ABM_GETTASKBARPOS, &abd);
    if (abd.rc.bottom - abd.rc.top > abd.rc.right - abd.rc.left)
      priv->orientation = GTK_ORIENTATION_VERTICAL;
    else
      priv->orientation = GTK_ORIENTATION_HORIZONTAL;

    priv->taskbar_top = abd.rc.top;
  }

  priv->last_click_x = priv->last_click_y = 0;

  /* Are the system tray icons always 16 pixels square? */
  priv->size         = 16;
  priv->image_width  = 16;
  priv->image_height = 16;

  priv->dummy_widget = gtk_label_new ("");

  memset (&priv->nid, 0, sizeof (priv->nid));

  priv->nid.hWnd = create_tray_observer ();
  priv->nid.uID = status_icon_id++;
  priv->nid.uCallbackMessage = WM_GTK_TRAY_NOTIFICATION;
  priv->nid.uFlags = NIF_MESSAGE;

  /* To help win7 identify the icon create it with an application "unique" tip */
  if (g_get_prgname ())
  {
    WCHAR *wcs = g_utf8_to_utf16 (g_get_prgname (), -1, NULL, NULL, NULL);

    priv->nid.uFlags |= NIF_TIP;
    wcsncpy (priv->nid.szTip, wcs, G_N_ELEMENTS (priv->nid.szTip) - 1);
    priv->nid.szTip[G_N_ELEMENTS (priv->nid.szTip) - 1] = 0;
    g_free (wcs);
  }

  if (!Shell_NotifyIconW (NIM_ADD, &priv->nid))
    {
      g_warning (G_STRLOC ": Shell_NotifyIcon(NIM_ADD) failed");
      priv->nid.hWnd = NULL;
    }

  status_icons = g_slist_append (status_icons, status_icon);

#endif
	
#ifdef GDK_WINDOWING_QUARTZ
  priv->dummy_widget = gtk_label_new ("");
{'fraction_non_alphanumeric': 0.08543800166066981, 'fraction_numerical': 0.006279407694436756, 'mean_word_length': 2.797228060956385, 'pattern_counts': {'":': 4, '<': 39, '<?xml version=': 0, '>': 237, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 88, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2441948', 'n_tokens_mistral': 21048, 'n_tokens_neox': 18827, 'n_words': 4718}