Replace functions which called once with their bodies
[pidgin-git.git] / pidgin / win32 / gtkdocklet-win32.c
blob33d219059841c99a181ed73846b8bad1ac927186
1 /*
2 * System tray icon (aka docklet) plugin for Winpidgin
4 * Copyright (C) 2002-3 Robert McQueen <robot101@debian.org>
5 * Copyright (C) 2003 Herman Bloggs <hermanator12002@yahoo.com>
6 * Inspired by a similar plugin by:
7 * John (J5) Palmieri <johnp@martianrock.com>
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 * 02111-1301, USA.
25 #include <config.h>
27 #include <windows.h>
28 #include <gdk/gdkwin32.h>
29 #include <gdk/gdk.h>
31 #include "internal.h"
32 #include "gtkblist.h"
33 #include "debug.h"
35 #include "resource.h"
36 #include "MinimizeToTray.h"
37 #include "gtkwin32dep.h"
38 #include "gtkdocklet.h"
39 #include "pidginicon.h"
42 * DEFINES, MACROS & DATA TYPES
44 #define WM_TRAYMESSAGE WM_USER /* User defined WM Message */
47 * LOCALS
49 static HWND systray_hwnd = NULL;
50 /* additional two cached_icons entries for pending and connecting icons */
51 static HICON cached_icons[PURPLE_STATUS_NUM_PRIMITIVES + 3];
52 static GtkWidget *image = NULL;
53 /* This is used to trigger click events on so they appear to GTK+ as if they are triggered by input */
54 static GtkWidget *dummy_button = NULL;
55 static GtkWidget *dummy_window = NULL;
56 static NOTIFYICONDATAW _nicon_data;
58 static gboolean dummy_button_cb(GtkWidget *widget, GdkEventButton *event, gpointer user_data) {
59 pidgin_docklet_clicked(event->button);
60 return TRUE;
63 static LRESULT CALLBACK systray_mainmsg_handler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
64 static UINT taskbarRestartMsg; /* static here means value is kept across multiple calls to this func */
66 switch(msg) {
67 case WM_CREATE:
68 purple_debug_info("docklet", "WM_CREATE\n");
69 taskbarRestartMsg = RegisterWindowMessageW(L"TaskbarCreated");
70 break;
72 case WM_TIMER:
73 purple_debug_info("docklet", "WM_TIMER\n");
74 break;
76 case WM_DESTROY:
77 purple_debug_info("docklet", "WM_DESTROY\n");
78 break;
80 case WM_TRAYMESSAGE:
82 int type = 0;
83 GdkEvent *event;
84 GdkEventButton *event_btn;
86 /* We'll use Double Click - Single click over on linux */
87 if(lparam == WM_LBUTTONDBLCLK)
88 type = GDK_BUTTOM_PRIMARY;
89 else if(lparam == WM_MBUTTONUP)
90 type = GDK_BUTTON_MIDDLE;
91 else if(lparam == WM_RBUTTONUP)
92 type = GDK_BUTTON_SECONDARY;
93 else
94 break;
96 gtk_widget_show_all(dummy_window);
97 event = gdk_event_new(GDK_BUTTON_PRESS);
98 event_btn = (GdkEventButton *) event;
99 event_btn->window = g_object_ref (gdk_get_default_root_window());
100 event_btn->send_event = TRUE;
101 event_btn->time = GetTickCount();
102 event_btn->axes = NULL;
103 event_btn->state = 0;
104 event_btn->button = type;
105 event_btn->device = gdk_display_get_default ()->core_pointer;
106 event->any.window = g_object_ref(dummy_window->window);
107 gdk_window_set_user_data(event->any.window, dummy_button);
109 gtk_main_do_event((GdkEvent *)event);
110 gtk_widget_hide(dummy_window);
111 gdk_event_free((GdkEvent *)event);
113 break;
115 default:
116 if (msg == taskbarRestartMsg) {
117 /* explorer crashed and left us hanging...
118 This will put the systray icon back in it's place, when it restarts */
119 Shell_NotifyIconW(NIM_ADD, &_nicon_data);
121 break;
122 }/* end switch */
124 return DefWindowProc(hwnd, msg, wparam, lparam);
127 /* Create hidden window to process systray messages */
128 static HWND systray_create_hiddenwin() {
129 WNDCLASSEXW wcex;
130 wchar_t *wname;
132 wname = L"WinpidginSystrayWinCls";
134 wcex.cbSize = sizeof(wcex);
135 wcex.style = 0;
136 wcex.lpfnWndProc = systray_mainmsg_handler;
137 wcex.cbClsExtra = 0;
138 wcex.cbWndExtra = 0;
139 wcex.hInstance = winpidgin_exe_hinstance();
140 wcex.hIcon = NULL;
141 wcex.hCursor = NULL,
142 wcex.hbrBackground = NULL;
143 wcex.lpszMenuName = NULL;
144 wcex.lpszClassName = wname;
145 wcex.hIconSm = NULL;
147 RegisterClassExW(&wcex);
149 /* Create the window */
150 return (CreateWindowW(wname, L"", 0, 0, 0, 0, 0, GetDesktopWindow(), NULL, winpidgin_exe_hinstance(), 0));
153 static void systray_init_icon(HWND hWnd) {
154 wchar_t *w;
155 ZeroMemory(&_nicon_data, sizeof(_nicon_data));
156 _nicon_data.cbSize = sizeof(NOTIFYICONDATAW);
157 _nicon_data.hWnd = hWnd;
158 _nicon_data.uID = 0;
159 _nicon_data.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
160 _nicon_data.uCallbackMessage = WM_TRAYMESSAGE;
161 _nicon_data.hIcon = NULL;
162 w = g_utf8_to_utf16(PIDGIN_NAME, -1, NULL, NULL, NULL);
163 wcsncpy(_nicon_data.szTip, w, sizeof(_nicon_data.szTip) / sizeof(wchar_t));
164 g_free(w);
165 Shell_NotifyIconW(NIM_ADD, &_nicon_data);
166 pidgin_docklet_embedded();
169 /* This is ganked from GTK+.
170 * When we can use GTK+ 2.10 and the GtkStatusIcon stuff, this will no longer be necesary */
171 #define WIN32_GDI_FAILED(api) printf("GDI FAILED %s\n", api)
173 static gboolean
174 _gdk_win32_pixbuf_to_hicon_supports_alpha (void)
176 static gboolean is_win_xp=FALSE, is_win_xp_checked=FALSE;
178 if (!is_win_xp_checked)
180 is_win_xp_checked = TRUE;
182 if (!G_WIN32_IS_NT_BASED ())
183 is_win_xp = FALSE;
184 else
186 OSVERSIONINFO version;
188 memset (&version, 0, sizeof (version));
189 version.dwOSVersionInfoSize = sizeof (version);
190 is_win_xp = GetVersionEx (&version)
191 && version.dwPlatformId == VER_PLATFORM_WIN32_NT
192 && (version.dwMajorVersion > 5
193 || (version.dwMajorVersion == 5 && version.dwMinorVersion >= 1));
196 return is_win_xp;
199 static HBITMAP
200 create_alpha_bitmap (gint size,
201 guchar **outdata)
203 BITMAPV5HEADER bi;
204 HDC hdc;
205 HBITMAP hBitmap;
207 ZeroMemory (&bi, sizeof (BITMAPV5HEADER));
208 bi.bV5Size = sizeof (BITMAPV5HEADER);
209 bi.bV5Height = bi.bV5Width = size;
210 bi.bV5Planes = 1;
211 bi.bV5BitCount = 32;
212 bi.bV5Compression = BI_BITFIELDS;
213 /* The following mask specification specifies a supported 32 BPP
214 * alpha format for Windows XP (BGRA format).
216 bi.bV5RedMask = 0x00FF0000;
217 bi.bV5GreenMask = 0x0000FF00;
218 bi.bV5BlueMask = 0x000000FF;
219 bi.bV5AlphaMask = 0xFF000000;
221 /* Create the DIB section with an alpha channel. */
222 hdc = GetDC (NULL);
223 if (!hdc)
225 WIN32_GDI_FAILED ("GetDC");
226 return NULL;
228 hBitmap = CreateDIBSection (hdc, (BITMAPINFO *)&bi, DIB_RGB_COLORS,
229 (PVOID *) outdata, NULL, (DWORD)0);
230 if (hBitmap == NULL)
231 WIN32_GDI_FAILED ("CreateDIBSection");
232 ReleaseDC (NULL, hdc);
234 return hBitmap;
237 static HBITMAP
238 create_color_bitmap (gint size,
239 guchar **outdata,
240 gint bits)
242 struct {
243 BITMAPV4HEADER bmiHeader;
244 RGBQUAD bmiColors[2];
245 } bmi;
246 HDC hdc;
247 HBITMAP hBitmap;
249 ZeroMemory (&bmi, sizeof (bmi));
250 bmi.bmiHeader.bV4Size = sizeof (BITMAPV4HEADER);
251 bmi.bmiHeader.bV4Height = bmi.bmiHeader.bV4Width = size;
252 bmi.bmiHeader.bV4Planes = 1;
253 bmi.bmiHeader.bV4BitCount = bits;
254 bmi.bmiHeader.bV4V4Compression = BI_RGB;
256 /* when bits is 1, these will be used.
257 * bmiColors[0] already zeroed from ZeroMemory()
259 bmi.bmiColors[1].rgbBlue = 0xFF;
260 bmi.bmiColors[1].rgbGreen = 0xFF;
261 bmi.bmiColors[1].rgbRed = 0xFF;
263 hdc = GetDC (NULL);
264 if (!hdc)
266 WIN32_GDI_FAILED ("GetDC");
267 return NULL;
269 hBitmap = CreateDIBSection (hdc, (BITMAPINFO *)&bmi, DIB_RGB_COLORS,
270 (PVOID *) outdata, NULL, (DWORD)0);
271 if (hBitmap == NULL)
272 WIN32_GDI_FAILED ("CreateDIBSection");
273 ReleaseDC (NULL, hdc);
275 return hBitmap;
278 static gboolean
279 pixbuf_to_hbitmaps_alpha_winxp (GdkPixbuf *pixbuf,
280 HBITMAP *color,
281 HBITMAP *mask)
283 /* Based on code from
284 * http://www.dotnet247.com/247reference/msgs/13/66301.aspx
286 HBITMAP hColorBitmap, hMaskBitmap;
287 guchar *indata, *inrow;
288 guchar *colordata, *colorrow, *maskdata, *maskbyte;
289 gint width, height, size, i, i_offset, j, j_offset, rowstride;
290 guint maskstride, mask_bit;
292 width = gdk_pixbuf_get_width (pixbuf); /* width of icon */
293 height = gdk_pixbuf_get_height (pixbuf); /* height of icon */
295 /* The bitmaps are created square */
296 size = MAX (width, height);
298 hColorBitmap = create_alpha_bitmap (size, &colordata);
299 if (!hColorBitmap)
300 return FALSE;
301 hMaskBitmap = create_color_bitmap (size, &maskdata, 1);
302 if (!hMaskBitmap)
304 DeleteObject (hColorBitmap);
305 return FALSE;
308 /* MSDN says mask rows are aligned to "LONG" boundaries */
309 maskstride = (((size + 31) & ~31) >> 3);
311 indata = gdk_pixbuf_get_pixels (pixbuf);
312 rowstride = gdk_pixbuf_get_rowstride (pixbuf);
314 if (width > height)
316 i_offset = 0;
317 j_offset = (width - height) / 2;
319 else
321 i_offset = (height - width) / 2;
322 j_offset = 0;
325 for (j = 0; j < height; j++)
327 colorrow = colordata + 4*(j+j_offset)*size + 4*i_offset;
328 maskbyte = maskdata + (j+j_offset)*maskstride + i_offset/8;
329 mask_bit = (0x80 >> (i_offset % 8));
330 inrow = indata + (height-j-1)*rowstride;
331 for (i = 0; i < width; i++)
333 colorrow[4*i+0] = inrow[4*i+2];
334 colorrow[4*i+1] = inrow[4*i+1];
335 colorrow[4*i+2] = inrow[4*i+0];
336 colorrow[4*i+3] = inrow[4*i+3];
337 if (inrow[4*i+3] == 0)
338 maskbyte[0] |= mask_bit; /* turn ON bit */
339 else
340 maskbyte[0] &= ~mask_bit; /* turn OFF bit */
341 mask_bit >>= 1;
342 if (mask_bit == 0)
344 mask_bit = 0x80;
345 maskbyte++;
350 *color = hColorBitmap;
351 *mask = hMaskBitmap;
353 return TRUE;
356 static gboolean
357 pixbuf_to_hbitmaps_normal (GdkPixbuf *pixbuf,
358 HBITMAP *color,
359 HBITMAP *mask)
361 /* Based on code from
362 * http://www.dotnet247.com/247reference/msgs/13/66301.aspx
364 HBITMAP hColorBitmap, hMaskBitmap;
365 guchar *indata, *inrow;
366 guchar *colordata, *colorrow, *maskdata, *maskbyte;
367 gint width, height, size, i, i_offset, j, j_offset, rowstride, nc, bmstride;
368 gboolean has_alpha;
369 guint maskstride, mask_bit;
371 width = gdk_pixbuf_get_width (pixbuf); /* width of icon */
372 height = gdk_pixbuf_get_height (pixbuf); /* height of icon */
374 /* The bitmaps are created square */
375 size = MAX (width, height);
377 hColorBitmap = create_color_bitmap (size, &colordata, 24);
378 if (!hColorBitmap)
379 return FALSE;
380 hMaskBitmap = create_color_bitmap (size, &maskdata, 1);
381 if (!hMaskBitmap)
383 DeleteObject (hColorBitmap);
384 return FALSE;
387 /* rows are always aligned on 4-byte boundarys */
388 bmstride = size * 3;
389 if (bmstride % 4 != 0)
390 bmstride += 4 - (bmstride % 4);
392 /* MSDN says mask rows are aligned to "LONG" boundaries */
393 maskstride = (((size + 31) & ~31) >> 3);
395 indata = gdk_pixbuf_get_pixels (pixbuf);
396 rowstride = gdk_pixbuf_get_rowstride (pixbuf);
397 nc = gdk_pixbuf_get_n_channels (pixbuf);
398 has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
400 if (width > height)
402 i_offset = 0;
403 j_offset = (width - height) / 2;
405 else
407 i_offset = (height - width) / 2;
408 j_offset = 0;
411 for (j = 0; j < height; j++)
413 colorrow = colordata + (j+j_offset)*bmstride + 3*i_offset;
414 maskbyte = maskdata + (j+j_offset)*maskstride + i_offset/8;
415 mask_bit = (0x80 >> (i_offset % 8));
416 inrow = indata + (height-j-1)*rowstride;
417 for (i = 0; i < width; i++)
419 if (has_alpha && inrow[nc*i+3] < 128)
421 colorrow[3*i+0] = colorrow[3*i+1] = colorrow[3*i+2] = 0;
422 maskbyte[0] |= mask_bit; /* turn ON bit */
424 else
426 colorrow[3*i+0] = inrow[nc*i+2];
427 colorrow[3*i+1] = inrow[nc*i+1];
428 colorrow[3*i+2] = inrow[nc*i+0];
429 maskbyte[0] &= ~mask_bit; /* turn OFF bit */
431 mask_bit >>= 1;
432 if (mask_bit == 0)
434 mask_bit = 0x80;
435 maskbyte++;
440 *color = hColorBitmap;
441 *mask = hMaskBitmap;
443 return TRUE;
446 static HICON
447 pixbuf_to_hicon (GdkPixbuf *pixbuf)
449 gint x = 0, y = 0;
450 gboolean is_icon = TRUE;
451 ICONINFO ii;
452 HICON icon;
453 gboolean success;
455 if (pixbuf == NULL)
456 return NULL;
458 if (_gdk_win32_pixbuf_to_hicon_supports_alpha() && gdk_pixbuf_get_has_alpha (pixbuf))
459 success = pixbuf_to_hbitmaps_alpha_winxp (pixbuf, &ii.hbmColor, &ii.hbmMask);
460 else
461 success = pixbuf_to_hbitmaps_normal (pixbuf, &ii.hbmColor, &ii.hbmMask);
463 if (!success)
464 return NULL;
466 ii.fIcon = is_icon;
467 ii.xHotspot = x;
468 ii.yHotspot = y;
469 icon = CreateIconIndirect (&ii);
470 DeleteObject (ii.hbmColor);
471 DeleteObject (ii.hbmMask);
472 return icon;
475 static HICON load_hicon_from_stock(const char *stock) {
476 HICON hicon = NULL;
477 GdkPixbuf *pixbuf = gtk_widget_render_icon(image, stock,
478 gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL), NULL);
480 if (pixbuf) {
481 hicon = pixbuf_to_hicon(pixbuf);
482 g_object_unref(pixbuf);
483 } else
484 purple_debug_error("docklet", "Unable to load pixbuf for %s.\n", stock);
486 return hicon;
490 static void systray_change_icon(HICON hicon) {
491 g_return_if_fail(hicon != NULL);
493 _nicon_data.hIcon = hicon;
494 Shell_NotifyIconW(NIM_MODIFY, &_nicon_data);
497 static void systray_remove_nid(void) {
498 Shell_NotifyIconW(NIM_DELETE, &_nicon_data);
501 static void winpidgin_tray_update_icon(PurpleStatusPrimitive status,
502 PidginDockletFlag flags) {
504 int icon_index;
505 g_return_if_fail(image != NULL);
507 if(flags & PIDGIN_DOCKLET_CONNECTING)
508 icon_index = PURPLE_STATUS_NUM_PRIMITIVES;
509 else if(flags & PIDGIN_DOCKLET_EMAIL_PENDING)
510 icon_index = PURPLE_STATUS_NUM_PRIMITIVES+2;
511 else if(flags & PIDGIN_DOCKLET_CONV_PENDING)
512 icon_index = PURPLE_STATUS_NUM_PRIMITIVES+1;
513 else
514 icon_index = status;
516 g_return_if_fail(icon_index < (sizeof(cached_icons) / sizeof(HICON)));
518 /* Look up and cache the HICON if we don't already have it */
519 if (cached_icons[icon_index] == NULL) {
520 const gchar *icon_name = NULL;
522 icon_name = pidgin_status_icon_from_primitive(status);
524 if (flags & PIDGIN_DOCKLET_EMAIL_PENDING)
525 icon_name = PIDGIN_ICON_MAIL_NEW;
526 else if (flags & PIDGIN_DOCKLET_CONV_PENDING)
527 icon_name = PIDGIN_ICON_MESSAGE_NEW;
528 else if (flags & PIDGIN_DOCKLET_CONNECTING)
529 icon_name = PIDGIN_ICON_CONNECT;
531 g_return_if_fail(icon_name != NULL);
533 cached_icons[icon_index] = load_hicon_from_stock(icon_name);
536 systray_change_icon(cached_icons[icon_index]);
539 static void winpidgin_tray_blank_icon() {
540 _nicon_data.hIcon = NULL;
541 Shell_NotifyIconW(NIM_MODIFY, &_nicon_data);
544 static void winpidgin_tray_set_tooltip(gchar *tooltip) {
545 const char *value = tooltip;
546 wchar_t *w;
547 if (value == NULL) {
548 value = PIDGIN_NAME;
550 w = g_utf8_to_utf16(value, -1, NULL, NULL, NULL);
551 wcsncpy(_nicon_data.szTip, w, sizeof(_nicon_data.szTip) / sizeof(wchar_t));
552 g_free(w);
553 Shell_NotifyIconW(NIM_MODIFY, &_nicon_data);
556 static void winpidgin_tray_minimize(PidginBuddyList *gtkblist) {
557 MinimizeWndToTray(GDK_WINDOW_HWND(gtkblist->window->window));
560 static void winpidgin_tray_maximize(PidginBuddyList *gtkblist) {
561 RestoreWndFromTray(GDK_WINDOW_HWND(gtkblist->window->window));
565 static void winpidgin_tray_create() {
566 OSVERSIONINFO osinfo;
567 /* dummy window to process systray messages */
568 systray_hwnd = systray_create_hiddenwin();
570 dummy_window = gtk_window_new(GTK_WINDOW_POPUP);
571 dummy_button = gtk_button_new();
572 gtk_container_add(GTK_CONTAINER(dummy_window), dummy_button);
574 /* We trigger the click event indirectly so that gtk_get_current_event_state() is TRUE when the event is handled */
575 g_signal_connect(G_OBJECT(dummy_button), "button-press-event",
576 G_CALLBACK(dummy_button_cb), NULL);
578 image = gtk_image_new();
579 g_object_ref_sink(image);
581 osinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
582 GetVersionEx(&osinfo);
584 /* Load icons, and init systray notify icon
585 * NOTE: Windows < XP only supports displaying 4-bit images in the Systray,
586 * 2K and ME will use the highest color depth that the desktop will support,
587 * but will scale it back to 4-bits for display.
588 * That is why we use custom 4-bit icons for pre XP Windowses */
589 if (osinfo.dwMajorVersion < 5 || (osinfo.dwMajorVersion == 5 && osinfo.dwMinorVersion == 0))
591 cached_icons[PURPLE_STATUS_OFFLINE] = (HICON) LoadImage(winpidgin_dll_hinstance(),
592 MAKEINTRESOURCE(PIDGIN_TRAY_OFFLINE_4BIT), IMAGE_ICON, 16, 16, LR_CREATEDIBSECTION);
593 cached_icons[PURPLE_STATUS_AVAILABLE] = (HICON) LoadImage(winpidgin_dll_hinstance(),
594 MAKEINTRESOURCE(PIDGIN_TRAY_AVAILABLE_4BIT), IMAGE_ICON, 16, 16, LR_CREATEDIBSECTION);
595 cached_icons[PURPLE_STATUS_AWAY] = (HICON) LoadImage(winpidgin_dll_hinstance(),
596 MAKEINTRESOURCE(PIDGIN_TRAY_AWAY_4BIT), IMAGE_ICON, 16, 16, LR_CREATEDIBSECTION);
597 cached_icons[PURPLE_STATUS_EXTENDED_AWAY] = (HICON) LoadImage(winpidgin_dll_hinstance(),
598 MAKEINTRESOURCE(PIDGIN_TRAY_XA_4BIT), IMAGE_ICON, 16, 16, LR_CREATEDIBSECTION);
599 cached_icons[PURPLE_STATUS_UNAVAILABLE] = (HICON) LoadImage(winpidgin_dll_hinstance(),
600 MAKEINTRESOURCE(PIDGIN_TRAY_BUSY_4BIT), IMAGE_ICON, 16, 16, LR_CREATEDIBSECTION);
601 cached_icons[PURPLE_STATUS_NUM_PRIMITIVES] = (HICON) LoadImage(winpidgin_dll_hinstance(),
602 MAKEINTRESOURCE(PIDGIN_TRAY_CONNECTING_4BIT), IMAGE_ICON, 16, 16, LR_CREATEDIBSECTION);
603 cached_icons[PURPLE_STATUS_NUM_PRIMITIVES+1] = (HICON) LoadImage(winpidgin_dll_hinstance(),
604 MAKEINTRESOURCE(PIDGIN_TRAY_PENDING_4BIT), IMAGE_ICON, 16, 16, LR_CREATEDIBSECTION);
605 cached_icons[PURPLE_STATUS_INVISIBLE] = (HICON) LoadImage(winpidgin_dll_hinstance(),
606 MAKEINTRESOURCE(PIDGIN_TRAY_INVISIBLE_4BIT), IMAGE_ICON, 16, 16, LR_CREATEDIBSECTION);
609 /* Create icon in systray */
610 systray_init_icon(systray_hwnd);
612 purple_signal_connect(pidgin_blist_get_handle(), "gtkblist-hiding",
613 pidgin_docklet_get_handle(), PURPLE_CALLBACK(winpidgin_tray_minimize), NULL);
614 purple_signal_connect(pidgin_blist_get_handle(), "gtkblist-unhiding",
615 pidgin_docklet_get_handle(), PURPLE_CALLBACK(winpidgin_tray_maximize), NULL);
617 purple_debug_info("docklet", "created\n");
620 static void winpidgin_tray_destroy() {
621 int cached_cnt = sizeof(cached_icons) / sizeof(HICON);
622 systray_remove_nid();
624 purple_signals_disconnect_by_handle(pidgin_docklet_get_handle());
626 DestroyWindow(systray_hwnd);
627 pidgin_docklet_remove();
629 while (--cached_cnt >= 0) {
630 if (cached_icons[cached_cnt] != NULL)
631 DestroyIcon(cached_icons[cached_cnt]);
632 cached_icons[cached_cnt] = NULL;
635 g_object_unref(image);
636 image = NULL;
638 gtk_widget_destroy(dummy_window);
639 dummy_button = NULL;
640 dummy_window = NULL;
643 static struct docklet_ui_ops winpidgin_tray_ops =
645 winpidgin_tray_create,
646 winpidgin_tray_destroy,
647 winpidgin_tray_update_icon,
648 winpidgin_tray_blank_icon,
649 winpidgin_tray_set_tooltip,
650 NULL
653 /* Used by docklet's plugin load func */
654 void docklet_ui_init() {
655 /* Initialize the cached icons to NULL */
656 ZeroMemory(cached_icons, sizeof(cached_icons));
658 pidgin_docklet_set_ui_ops(&winpidgin_tray_ops);