/* gtktrayicon.c * Copyright (C) 2002 Anders Carlsson * Copyright (C) 2005 Hans Breuer * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* * This is an implementation of the gtktrayicon interface *not* * following the freedesktop.org "system tray" spec, * http://www.freedesktop.org/wiki/Standards/systemtray-spec */ #include #include #include #include "gtkintl.h" #include "gtkprivate.h" #include "gtktrayicon.h" #include "gtkimage.h" #include "gtkiconfactory.h" #include "win32/gdkwin32.h" #include "gtkalias.h" #define WIN32_MEAN_AND_LEAN #include #define WM_GTK_TRAY_NOTIFICATION (WM_USER+1) struct _GtkTrayIconPrivate { NOTIFYICONDATA nid; }; static void gtk_tray_icon_add (GtkContainer *container, GtkWidget *widget); static void gtk_tray_icon_size_request (GtkWidget *widget, GtkRequisition *requisition); static void gtk_tray_icon_size_allocate (GtkWidget *widget, GtkAllocation *allocation); static void gtk_tray_icon_finalize (GObject *object); G_DEFINE_TYPE (GtkTrayIcon, gtk_tray_icon, GTK_TYPE_PLUG) static void gtk_tray_icon_class_init (GtkTrayIconClass *class) { GObjectClass *gobject_class = G_OBJECT_CLASS (class); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class); gobject_class->finalize = gtk_tray_icon_finalize; widget_class->size_request = gtk_tray_icon_size_request; //widget_class->size_allocate = gtk_tray_icon_size_allocate; container_class->add = gtk_tray_icon_add; g_type_class_add_private (class, sizeof (GtkTrayIconPrivate)); } static LRESULT CALLBACK _win32_on_tray_change (HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { if (WM_GTK_TRAY_NOTIFICATION == message) { GdkEventButton e = {0, }; GtkTrayIcon *tray_icon = GTK_TRAY_ICON (wparam); switch (lparam) { case WM_MOUSEMOVE : g_print ("GtkTrayIcon::WM_MOUSEMOVE\n"); break; case WM_RBUTTONDBLCLK : case WM_LBUTTONDBLCLK : g_print ("GtkTrayIcon::WM_LBUTTONDBLCLK\n"); e.type = GDK_2BUTTON_PRESS; e.button = (WM_LBUTTONDBLCLK == lparam) ? 1 : 3; break; case WM_LBUTTONUP : case WM_RBUTTONUP : /* deliberately ignored */ break; case WM_LBUTTONDOWN : case WM_RBUTTONDOWN : g_print ("GtkTrayIcon::WM_RBUTTONUP\n"); e.type = GDK_BUTTON_PRESS; e.button = (WM_LBUTTONDOWN == lparam) ? 1 : 3; break; default : g_print ("GtkTrayIcon::%d\n", lparam); break; } g_signal_emit_by_name (tray_icon, "button-press-event", &e); return 0; } else { return DefWindowProc (hwnd, message, wparam, lparam); } } HWND _gdk_win32_create_tray_observer (void) { WNDCLASS wclass; static HWND hwnd = NULL; ATOM klass; HINSTANCE hmodule = GetModuleHandle (NULL); if (hwnd) return hwnd; memset (&wclass, 0, sizeof(WNDCLASS)); wclass.lpszClassName = "GtkTrayNotification"; wclass.lpfnWndProc = _win32_on_tray_change; wclass.hInstance = hmodule; klass = RegisterClass (&wclass); if (!klass) return NULL; hwnd = CreateWindow (MAKEINTRESOURCE(klass), NULL, WS_POPUP, 0, 0, 16, 16, NULL, NULL, hmodule, NULL); if (!hwnd) { UnregisterClass (MAKEINTRESOURCE(klass), hmodule); return NULL; } return hwnd; } static void gtk_tray_icon_init (GtkTrayIcon *icon) { icon->priv = G_TYPE_INSTANCE_GET_PRIVATE (icon, GTK_TYPE_TRAY_ICON, GtkTrayIconPrivate); memset (&icon->priv->nid, 0, sizeof (NOTIFYICONDATA)); icon->priv->nid.hWnd = _gdk_win32_create_tray_observer (); icon->priv->nid.uID = GPOINTER_TO_UINT (icon); icon->priv->nid.uCallbackMessage = WM_GTK_TRAY_NOTIFICATION; icon->priv->nid.uFlags = NIF_ICON|NIF_MESSAGE; //NIF_TIP } static void gtk_tray_icon_finalize (GObject *object) { GtkTrayIcon *icon = GTK_TRAY_ICON (object); Shell_NotifyIcon (NIM_DELETE, &icon->priv->nid); G_OBJECT_CLASS (gtk_tray_icon_parent_class)->finalize (object); } static void tray_image_changed (GObject *object, GParamSpec *pspec, gpointer data) { GtkImage *image; GtkWidget *widget; GdkPixbuf *pixbuf = NULL; GtkTrayIcon *icon = GTK_TRAY_ICON (data); g_return_if_fail (GTK_IS_IMAGE (object)); g_return_if_fail (GTK_IS_TRAY_ICON (data)); widget = GTK_WIDGET (object); image = GTK_IMAGE (object); /* * TODO: If nothing changed don't do nothing. * we get called three times for one change - for 'size', * 'storage-type', 'pixbuf' - could be cached. * But 'visible'(==FALSE) needs to be handled! */ switch (image->storage_type) { case GTK_IMAGE_PIXBUF : pixbuf = gtk_image_get_pixbuf (GTK_IMAGE (object)); g_object_ref (pixbuf); g_print ("GtkTrayIcon::image_changed size=%dx%d\n", pixbuf ? gdk_pixbuf_get_width (pixbuf) : 0, pixbuf ? gdk_pixbuf_get_height (pixbuf) : 0); break; case GTK_IMAGE_EMPTY : g_print ("GtkTrayIcon::image_changed EMPTY\n"); break; case GTK_IMAGE_ICON_NAME : { GtkIconSet *icon_set = NULL; const char* name = NULL; gtk_image_get_icon_name (image, &name, NULL); g_print ("GtkTrayIcon::image_changed '%s'\n", name); icon_set = gtk_style_lookup_icon_set (widget->style, name); pixbuf = gtk_icon_set_render_icon (icon_set, widget->style, gtk_widget_get_direction (GTK_WIDGET(icon)), GTK_STATE_NORMAL, GTK_ICON_SIZE_BUTTON, widget, NULL); } break; default : g_print ("GtkTrayIcon::image_changed %d\n", image->storage_type); break; } if (pixbuf) { HICON hIcon = icon->priv->nid.hIcon; icon->priv->nid.hIcon = gdk_win32_pixbuf_to_hicon_libgtk_only (pixbuf); Shell_NotifyIcon (hIcon ? NIM_MODIFY : NIM_ADD, &icon->priv->nid); if (hIcon) DestroyIcon (hIcon); g_object_unref (pixbuf); } } static void gtk_tray_icon_add (GtkContainer *container, GtkWidget *widget) { g_return_if_fail (GTK_IS_IMAGE (widget)); if (GTK_CONTAINER_CLASS (gtk_tray_icon_parent_class)->add) GTK_CONTAINER_CLASS (gtk_tray_icon_parent_class)->add (container, widget); g_signal_connect (widget, "notify", G_CALLBACK (tray_image_changed), container); } static void gtk_tray_icon_size_request (GtkWidget *widget, GtkRequisition *requisition) { requisition->width = 16; requisition->height = 16; } static void gtk_tray_icon_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { widget->allocation = *allocation; } GtkTrayIcon * _gtk_tray_icon_new (const gchar *name) { return g_object_new (GTK_TYPE_TRAY_ICON, "title", name, NULL); } GtkOrientation _gtk_tray_icon_get_orientation (GtkTrayIcon *icon) { g_return_val_if_fail (GTK_IS_TRAY_ICON (icon), GTK_ORIENTATION_HORIZONTAL); //FIXME: should we deliver the orientation of the tray ?? return GTK_ORIENTATION_VERTICAL; }