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

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

systray heap corruption fix

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