source: trunk/src/shell32/systray.cpp@ 3653

Last change on this file since 3653 was 3653, checked in by sandervl, 25 years ago

systray bugfix

File size: 14.0 KB
Line 
1/* $Id: systray.cpp,v 1.3 2000-06-03 07:18:54 sandervl Exp $ */
2/*
3 * Systray
4 *
5 * Copyright 2000 Christoph Bratschi (cbratschi@datacomm.ch)
6 *
7 * Copyright 1999 Kai Morich <kai.morich@bigfoot.de>
8 *
9 * Manage the systray window. That it actually appears in the docking
10 * area of KDE or GNOME is delegated to windows/x11drv/wnd.c,
11 * X11DRV_WND_DockWindow.
12 *
13 */
14
15//#include <unistd.h>
16#include <stdlib.h>
17#include <string.h>
18
19#define ICOM_CINTERFACE 1
20#define CINTERFACE 1
21
22#include "shellapi.h"
23#include "shell32_main.h"
24#include "windows.h"
25#include "commctrl.h"
26#include "debugtools.h"
27#include "config.h"
28#include "heapstring.h"
29#include "winversion.h"
30
31DEFAULT_DEBUG_CHANNEL(shell)
32
33typedef struct SystrayData {
34 HWND hWnd;
35 HWND hWndToolTip;
36 NOTIFYICONDATAW notifyIcon;
37 int nitem; /* number of current element = tooltip id */
38 struct SystrayData *nextTrayItem;
39} SystrayData;
40
41typedef struct Systray {
42 int hasCritSection;
43 CRITICAL_SECTION critSection;
44 SystrayData *systrayItemList;
45} Systray;
46
47static Systray systray;
48static int nNumberTrayElements;
49
50#define SYSTRAY_CLASS "OdinSysTray"
51
52static BOOL SYSTRAY_Delete(PNOTIFYICONDATAW pnid,BOOL unicode);
53
54
55/**************************************************************************
56* internal systray
57*
58*/
59
60#define SMALL_ICON_SIZE GetSystemMetrics(SM_CXSMICON)
61
62/* space between icons and frame */
63#define IBORDER 3
64#define OBORDER 2
65#define TBORDER (OBORDER+1+IBORDER)
66
67static LRESULT CALLBACK SYSTRAY_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
68{
69 HDC hdc;
70 PAINTSTRUCT ps;
71
72 switch (message) {
73 case WM_PAINT:
74 {
75 RECT rc;
76 SystrayData *ptrayItem = systray.systrayItemList;
77
78 while (ptrayItem)
79 {
80 if (ptrayItem->hWnd == hWnd)
81 {
82 hdc = BeginPaint(hWnd, &ps);
83 GetClientRect(hWnd, &rc);
84
85 if (!DrawIconEx(hdc, rc.left, rc.top, ptrayItem->notifyIcon.hIcon,
86 SMALL_ICON_SIZE, SMALL_ICON_SIZE, 0, 0, DI_DEFAULTSIZE|DI_NORMAL))
87 SYSTRAY_Delete(&ptrayItem->notifyIcon,TRUE);
88 }
89 ptrayItem = ptrayItem->nextTrayItem;
90 }
91 EndPaint(hWnd, &ps);
92 }
93 break;
94
95 case WM_MOUSEMOVE:
96 case WM_LBUTTONDOWN:
97 case WM_LBUTTONUP:
98 case WM_RBUTTONDOWN:
99 case WM_RBUTTONUP:
100 case WM_MBUTTONDOWN:
101 case WM_MBUTTONUP:
102 {
103 MSG msg;
104 SystrayData *ptrayItem = systray.systrayItemList;
105
106 while ( ptrayItem )
107 {
108 if (ptrayItem->hWnd == hWnd)
109 {
110 msg.hwnd=hWnd;
111 msg.message=message;
112 msg.wParam=wParam;
113 msg.lParam=lParam;
114 msg.time = GetMessageTime ();
115 msg.pt.x = LOWORD(GetMessagePos ());
116 msg.pt.y = HIWORD(GetMessagePos ());
117
118 SendMessageA(ptrayItem->hWndToolTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
119 }
120 ptrayItem = ptrayItem->nextTrayItem;
121 }
122 }
123
124 case WM_LBUTTONDBLCLK:
125 case WM_RBUTTONDBLCLK:
126 case WM_MBUTTONDBLCLK:
127 {
128 int xPos;
129 SystrayData *ptrayItem = systray.systrayItemList;
130
131 while (ptrayItem)
132 {
133 if (ptrayItem->hWnd == hWnd)
134 {
135 xPos = LOWORD(lParam);
136 if( (xPos >= TBORDER) &&
137 (xPos < (TBORDER+SMALL_ICON_SIZE)) )
138 {
139 if (!PostMessageA(ptrayItem->notifyIcon.hWnd, ptrayItem->notifyIcon.uCallbackMessage,
140 (WPARAM)ptrayItem->notifyIcon.uID, (LPARAM)message))
141 SYSTRAY_Delete(&ptrayItem->notifyIcon,TRUE);
142 break;
143 }
144 }
145 ptrayItem = ptrayItem->nextTrayItem;
146 }
147 }
148 break;
149
150 default:
151 return (DefWindowProcA(hWnd, message, wParam, lParam));
152 }
153 return (0);
154
155}
156
157BOOL SYSTRAY_RegisterClass(void)
158{
159 WNDCLASSA wc;
160
161 wc.style = CS_SAVEBITS;
162 wc.lpfnWndProc = (WNDPROC)SYSTRAY_WndProc;
163 wc.cbClsExtra = 0;
164 wc.cbWndExtra = 0;
165 wc.hInstance = 0;
166 wc.hIcon = 0; /* LoadIcon (NULL, IDI_EXCLAMATION); */
167 wc.hCursor = LoadCursorA(0, IDC_ARROWA);
168 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW);
169 wc.lpszMenuName = NULL;
170 wc.lpszClassName = SYSTRAY_CLASS;
171
172 if (!RegisterClassA(&wc)) {
173 ERR("RegisterClass(WineSystray) failed\n");
174 return FALSE;
175 }
176 return TRUE;
177}
178
179
180BOOL SYSTRAY_Create(SystrayData *ptrayItem)
181{
182 RECT rect;
183
184 /* Register the class if this is our first tray item. */
185 if ( nNumberTrayElements == 1 )
186 {
187 if ( !SYSTRAY_RegisterClass() )
188 {
189 ERR( "RegisterClass(WineSystray) failed\n" );
190 return FALSE;
191 }
192 }
193
194 /* Initialize the window size. */
195 rect.left = 0;
196 rect.top = 0;
197 rect.right = SMALL_ICON_SIZE+2*TBORDER;
198 rect.bottom = SMALL_ICON_SIZE+2*TBORDER;
199
200 /* Create tray window for icon. */
201 ptrayItem->hWnd = CreateWindowExA( WS_EX_TRAYWINDOW,
202 SYSTRAY_CLASS, "Odin-Systray",
203 WS_VISIBLE | WS_POPUP,
204 CW_USEDEFAULT, CW_USEDEFAULT,
205 rect.right-rect.left, rect.bottom-rect.top,
206 0, 0, 0, 0 );
207 if ( !ptrayItem->hWnd )
208 {
209 ERR( "CreateWindow(OdinSystray) failed\n" );
210 return FALSE;
211 }
212
213 /* Create tooltip for icon. */
214 ptrayItem->hWndToolTip = CreateWindowA( TOOLTIPS_CLASSA,NULL,TTS_ALWAYSTIP,
215 CW_USEDEFAULT, CW_USEDEFAULT,
216 CW_USEDEFAULT, CW_USEDEFAULT,
217 ptrayItem->hWnd, 0, 0, 0 );
218 if ( ptrayItem->hWndToolTip==0 )
219 {
220 ERR( "CreateWindow(TOOLTIP) failed\n" );
221 return FALSE;
222 }
223 return TRUE;
224}
225
226static void SYSTRAY_RepaintAll(void)
227{
228 SystrayData *ptrayItem = systray.systrayItemList;
229
230 while(ptrayItem)
231 {
232 InvalidateRect(ptrayItem->hWnd, NULL, TRUE);
233 ptrayItem = ptrayItem->nextTrayItem;
234 }
235
236}
237
238static void SYSTRAY_RepaintItem(int nitem)
239{
240
241 SystrayData *ptrayItem = systray.systrayItemList;
242
243 while(ptrayItem)
244 {
245 if (ptrayItem->nitem == nitem)
246 InvalidateRect(ptrayItem->hWnd, NULL, TRUE);
247
248 ptrayItem = ptrayItem->nextTrayItem;
249 }
250}
251
252void SYSTRAY_InitItem(SystrayData *ptrayItem)
253{
254 ptrayItem->nitem = nNumberTrayElements++;
255 SYSTRAY_Create(ptrayItem);
256}
257
258void SYSTRAY_SetIcon(SystrayData *ptrayItem, HICON hIcon)
259{
260 ptrayItem->notifyIcon.hIcon = hIcon;
261}
262
263void SYSTRAY_SetTip(SystrayData *ptrayItem,WCHAR* szTip,BOOL unicode)
264{
265 TTTOOLINFOW ti;
266
267 if (unicode)
268 lstrcpynW(ptrayItem->notifyIcon.szTip, szTip, sizeof(ptrayItem->notifyIcon.szTip)/sizeof(WCHAR));
269 else
270 lstrcpynAtoW(ptrayItem->notifyIcon.szTip,(LPSTR)szTip,sizeof(ptrayItem->notifyIcon.szTip)/sizeof(WCHAR));
271 ptrayItem->notifyIcon.szTip[sizeof(ptrayItem->notifyIcon.szTip)/sizeof(WCHAR)-1]=0;
272
273 ti.cbSize = sizeof(TTTOOLINFOW);
274 ti.uFlags = 0;
275 ti.hwnd = ptrayItem->hWnd;
276 ti.hinst = 0;
277 ti.uId = ptrayItem->nitem;
278 ti.lpszText = ptrayItem->notifyIcon.szTip;
279 ti.rect.left = 0;
280 ti.rect.top = 0;
281 ti.rect.right = SMALL_ICON_SIZE+2*TBORDER;
282 ti.rect.bottom = SMALL_ICON_SIZE+2*TBORDER;
283
284 SendMessageA(ptrayItem->hWndToolTip, TTM_ADDTOOLW, 0, (LPARAM)&ti);
285}
286
287static void SYSTRAY_ModifyTip(SystrayData *ptrayItem,WCHAR* szTip,BOOL unicode)
288{
289 TTTOOLINFOW ti;
290
291 if (unicode)
292 lstrcpynW(ptrayItem->notifyIcon.szTip, szTip, sizeof(ptrayItem->notifyIcon.szTip)/sizeof(WCHAR));
293 else
294 lstrcpynAtoW(ptrayItem->notifyIcon.szTip,(LPSTR)szTip,sizeof(ptrayItem->notifyIcon.szTip)/sizeof(WCHAR));
295 ptrayItem->notifyIcon.szTip[sizeof(ptrayItem->notifyIcon.szTip)/sizeof(WCHAR)-1]=0;
296
297 ti.cbSize = sizeof(TTTOOLINFOW);
298 ti.uFlags = 0;
299 ti.hwnd = ptrayItem->hWnd;
300 ti.hinst = 0;
301 ti.uId = ptrayItem->nitem;
302 ti.lpszText = ptrayItem->notifyIcon.szTip;
303 ti.rect.left = 0;
304 ti.rect.top = 0;
305 ti.rect.right = SMALL_ICON_SIZE+2*TBORDER;
306 ti.rect.bottom = SMALL_ICON_SIZE+2*TBORDER;
307
308 SendMessageA(ptrayItem->hWndToolTip, TTM_UPDATETIPTEXTW, 0, (LPARAM)&ti);
309}
310
311static void SYSTRAY_TermItem(SystrayData *removeItem)
312{
313 int nitem;
314 SystrayData **trayItem;
315 TTTOOLINFOW ti;
316 ti.cbSize = sizeof(TTTOOLINFOA);
317 ti.uFlags = 0;
318 ti.hinst = 0;
319
320 /* delete all tooltips ...*/
321 trayItem = &systray.systrayItemList;
322 while (*trayItem) {
323 ti.uId = (*trayItem)->nitem;
324 ti.hwnd = (*trayItem)->hWnd;
325 SendMessageA((*trayItem)->hWndToolTip, TTM_DELTOOLW, 0, (LPARAM)&ti);
326 trayItem = &((*trayItem)->nextTrayItem);
327 }
328 /* ... and add them again, because uID may shift */
329 nitem=0;
330 trayItem = &systray.systrayItemList;
331 while (*trayItem)
332 {
333 if (*trayItem != removeItem)
334 {
335 (*trayItem)->nitem = nitem;
336 ti.uId = nitem;
337 ti.hwnd = (*trayItem)->hWnd;
338 ti.lpszText = (*trayItem)->notifyIcon.szTip;
339 ti.rect.left = 0;
340 ti.rect.top = 0;
341 ti.rect.right = SMALL_ICON_SIZE+2*TBORDER;
342 ti.rect.bottom = SMALL_ICON_SIZE+2*TBORDER;
343 SendMessageA((*trayItem)->hWndToolTip, TTM_ADDTOOLW, 0, (LPARAM)&ti);
344 nitem++;
345 }
346 trayItem = &((*trayItem)->nextTrayItem);
347 }
348 nNumberTrayElements--;
349}
350
351
352/**************************************************************************
353* helperfunctions
354*
355*/
356void SYSTRAY_SetMessage(SystrayData *ptrayItem, UINT uCallbackMessage)
357{
358 ptrayItem->notifyIcon.uCallbackMessage = uCallbackMessage;
359}
360
361static BOOL SYSTRAY_IsEqual(PNOTIFYICONDATAW pnid1, PNOTIFYICONDATAW pnid2)
362{
363 if (pnid1->hWnd != pnid2->hWnd) return FALSE;
364 if (pnid1->uID != pnid2->uID) return FALSE;
365 return TRUE;
366}
367
368static BOOL SYSTRAY_Add(PNOTIFYICONDATAW pnid,BOOL unicode)
369{
370 SystrayData **ptrayItem = &systray.systrayItemList;
371
372 /* Find empty space for new element. */
373 while( *ptrayItem )
374 {
375 if ( SYSTRAY_IsEqual(pnid, &(*ptrayItem)->notifyIcon) )
376 return FALSE;
377 ptrayItem = &((*ptrayItem)->nextTrayItem);
378 }
379
380 /* Allocate SystrayData for element and zero memory. */
381 (*ptrayItem) = ( SystrayData *)malloc( sizeof(SystrayData) );
382 ZeroMemory( (*ptrayItem), sizeof(SystrayData) );
383
384 /* Copy notification data */
385 memcpy( &(*ptrayItem)->notifyIcon, pnid, sizeof(NOTIFYICONDATAW) );
386
387 /* Initialize and set data for the tray element. */
388 SYSTRAY_InitItem( (*ptrayItem) );
389
390 SYSTRAY_SetIcon (*ptrayItem, (pnid->uFlags&NIF_ICON) ?pnid->hIcon :0);
391 SYSTRAY_SetMessage(*ptrayItem, (pnid->uFlags&NIF_MESSAGE)?pnid->uCallbackMessage:0);
392 SYSTRAY_SetTip (*ptrayItem, (pnid->uFlags & NIF_TIP) ? ((WCHAR*)pnid->szTip):((WCHAR*)L""),unicode && pnid->szTip);
393
394 (*ptrayItem)->nextTrayItem = NULL; /* may be overkill after the ZeroMemory call. */
395
396 /* Repaint all system tray icons as we have added one. */
397 SYSTRAY_RepaintAll();
398
399 TRACE("%p: 0x%08x %d %s\n", (*ptrayItem), (*ptrayItem)->notifyIcon.hWnd,
400 (*ptrayItem)->notifyIcon.uID,
401 (*ptrayItem)->notifyIcon.szTip);
402 return TRUE;
403}
404
405static BOOL SYSTRAY_Modify(PNOTIFYICONDATAW pnid,BOOL unicode)
406{
407 SystrayData *ptrayItem = systray.systrayItemList;
408
409 while ( ptrayItem )
410 {
411 if ( SYSTRAY_IsEqual(pnid, &ptrayItem->notifyIcon) )
412 {
413 if (pnid->uFlags & NIF_ICON)
414 {
415 SYSTRAY_SetIcon(ptrayItem, pnid->hIcon);
416 SYSTRAY_RepaintItem(ptrayItem->nitem);
417 }
418
419 if (pnid->uFlags & NIF_MESSAGE)
420 SYSTRAY_SetMessage(ptrayItem, pnid->uCallbackMessage);
421
422 if (pnid->uFlags & NIF_TIP)
423 SYSTRAY_ModifyTip(ptrayItem,pnid->szTip,unicode);
424
425 TRACE("%p: 0x%08x %d %s\n", ptrayItem, ptrayItem->notifyIcon.hWnd,
426 ptrayItem->notifyIcon.uID, ptrayItem->notifyIcon.szTip);
427 return TRUE;
428 }
429 ptrayItem = ptrayItem->nextTrayItem;
430 }
431 return FALSE; /* not found */
432}
433
434static BOOL SYSTRAY_Delete(PNOTIFYICONDATAW pnid,BOOL unicode)
435{
436 SystrayData **ptrayItem = &systray.systrayItemList;
437
438 while (*ptrayItem)
439 {
440 if (SYSTRAY_IsEqual(pnid, &(*ptrayItem)->notifyIcon))
441 {
442 SystrayData *next = (*ptrayItem)->nextTrayItem;
443 TRACE("%p: 0x%08x %d %s\n", *ptrayItem, (*ptrayItem)->notifyIcon.hWnd,
444 (*ptrayItem)->notifyIcon.uID, (*ptrayItem)->notifyIcon.szTip);
445 SYSTRAY_TermItem(*ptrayItem);
446
447 DestroyWindow((*ptrayItem)->hWndToolTip);
448 DestroyWindow((*ptrayItem)->hWnd);
449
450 free(*ptrayItem);
451 *ptrayItem = next;
452
453 SYSTRAY_RepaintAll();
454
455 return TRUE;
456 }
457 ptrayItem = &((*ptrayItem)->nextTrayItem);
458 }
459
460 return FALSE; /* not found */
461}
462
463/*************************************************************************
464 *
465 */
466BOOL SYSTRAY_Init(void)
467{
468 if (!systray.hasCritSection)
469 {
470 systray.hasCritSection=1;
471 InitializeCriticalSection(&systray.critSection);
472 MakeCriticalSectionGlobal(&systray.critSection);
473 //RegisterDebugptr(&systray.critSection, "SYSTRAY");
474 TRACE(" =%p\n", &systray.critSection);
475 }
476 return TRUE;
477}
478
479BOOL InternalNotifyIcon(DWORD dwMessage,PNOTIFYICONDATAW pnid,BOOL unicode)
480{
481 BOOL flag=FALSE;
482 TRACE("wait %d %d %ld\n", pnid->hWnd, pnid->uID, dwMessage);
483 /* must be serialized because all apps access same systray */
484 EnterCriticalSection(&systray.critSection);
485 TRACE("enter %d %d %ld\n", pnid->hWnd, pnid->uID, dwMessage);
486
487
488 switch(dwMessage) {
489 case NIM_ADD:
490 TRACE("Calling systray add\n");
491 flag = SYSTRAY_Add(pnid,unicode);
492 break;
493 case NIM_MODIFY:
494 flag = SYSTRAY_Modify(pnid,unicode);
495 break;
496 case NIM_DELETE:
497 flag = SYSTRAY_Delete(pnid,unicode);
498 break;
499 }
500
501 LeaveCriticalSection(&systray.critSection);
502 TRACE("leave %d %d %ld\n", pnid->hWnd, pnid->uID, dwMessage);
503 return flag;
504}
505
506/*************************************************************************
507 * Shell_NotifyIconA [SHELL32.297]
508 */
509ODINFUNCTION2(BOOL,Shell_NotifyIconA,DWORD,dwMessage,PNOTIFYICONDATAA,pnid )
510{
511 return InternalNotifyIcon(dwMessage,(PNOTIFYICONDATAW)pnid,FALSE);
512}
513
514/*************************************************************************
515 * Shell_NotifyIconW [SHELL32.???]
516 */
517ODINFUNCTION2(BOOL,Shell_NotifyIconW,DWORD,dwMessage,PNOTIFYICONDATAW,pnid )
518{
519 return InternalNotifyIcon(dwMessage,pnid,TRUE);
520}
521
522/*************************************************************************
523 * Shell_NotifyIcon [SHELL32.296]
524 */
525ODINFUNCTION2(BOOL,Shell_NotifyIcon,DWORD,dwMessage,PNOTIFYICONDATAW,pnid)
526{
527 return InternalNotifyIcon(dwMessage,pnid,VERSION_OsIsUnicode());
528}
Note: See TracBrowser for help on using the repository browser.