Fixed the displaying of the FOURCC codes in _dump_pixelformat.
[wine] / dlls / shell32 / systray.c
1 /*
2  *      Systray
3  *
4  *      Copyright 1999 Kai Morich       <kai.morich@bigfoot.de>
5  *
6  *  Manage the systray window. That it actually appears in the docking
7  *  area of KDE or GNOME is delegated to windows/x11drv/wnd.c,
8  *  X11DRV_WND_DockWindow.
9  *
10  */
11
12 #include <unistd.h>
13 #include <stdlib.h>
14 #include <string.h>
15
16 #include "heap.h"
17 #include "shellapi.h"
18 #include "shell32_main.h"
19 #include "windows.h"
20 #include "commctrl.h"
21 #include "debugtools.h"
22 #include "config.h"
23
24 DEFAULT_DEBUG_CHANNEL(shell)
25
26 typedef struct SystrayItem {
27   HWND                  hWnd;
28   HWND                  hWndToolTip;
29   NOTIFYICONDATAA       notifyIcon;
30   struct SystrayItem    *nextTrayItem;
31 } SystrayItem;
32
33 static SystrayItem *systray=NULL;
34 static int firstSystray=TRUE; /* defer creation of window class until first systray item is created */
35
36 static BOOL SYSTRAY_Delete(PNOTIFYICONDATAA pnid);
37
38
39 #define ICON_SIZE GetSystemMetrics(SM_CXSMICON)
40 /* space around icon (forces icon to center of KDE systray area) */
41 #define ICON_BORDER  4
42
43
44
45 static BOOL SYSTRAY_ItemIsEqual(PNOTIFYICONDATAA pnid1, PNOTIFYICONDATAA pnid2)
46 {
47   if (pnid1->hWnd != pnid2->hWnd) return FALSE;
48   if (pnid1->uID  != pnid2->uID)  return FALSE;
49   return TRUE;
50 }
51
52 static LRESULT CALLBACK SYSTRAY_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
53 {
54   HDC hdc;
55   PAINTSTRUCT ps;
56
57   switch (message) {
58   case WM_PAINT:
59   {
60     RECT rc;
61     SystrayItem  *ptrayItem = systray;
62
63     while (ptrayItem) {
64       if (ptrayItem->hWnd==hWnd) {
65         if (ptrayItem->notifyIcon.hIcon) {
66           hdc = BeginPaint(hWnd, &ps);
67           GetClientRect(hWnd, &rc);
68           if (!DrawIconEx(hdc, rc.left+ICON_BORDER, rc.top+ICON_BORDER, ptrayItem->notifyIcon.hIcon,
69                           ICON_SIZE, ICON_SIZE, 0, 0, DI_DEFAULTSIZE|DI_NORMAL)) {
70             ERR("Paint(SystrayWindow 0x%08x) failed -> removing SystrayItem %p\n", hWnd, ptrayItem);
71             SYSTRAY_Delete(&ptrayItem->notifyIcon);
72           }
73         }
74         break;
75       }
76       ptrayItem = ptrayItem->nextTrayItem;
77     }
78     EndPaint(hWnd, &ps);
79   }
80   break;
81
82   case WM_MOUSEMOVE:
83   case WM_LBUTTONDOWN:
84   case WM_LBUTTONUP:
85   case WM_RBUTTONDOWN:
86   case WM_RBUTTONUP:
87   case WM_MBUTTONDOWN:
88   case WM_MBUTTONUP:
89   {
90     MSG msg;
91     SystrayItem *ptrayItem = systray;
92
93     while ( ptrayItem ) {
94       if (ptrayItem->hWnd == hWnd) {
95         msg.hwnd=hWnd;
96         msg.message=message;
97         msg.wParam=wParam;
98         msg.lParam=lParam;
99         msg.time = GetMessageTime ();
100         msg.pt.x = LOWORD(GetMessagePos ());
101         msg.pt.y = HIWORD(GetMessagePos ());
102
103         SendMessageA(ptrayItem->hWndToolTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
104       }
105       ptrayItem = ptrayItem->nextTrayItem;
106     }
107   }
108   /* fallthru */
109
110   case WM_LBUTTONDBLCLK:
111   case WM_RBUTTONDBLCLK:
112   case WM_MBUTTONDBLCLK:
113   {
114     SystrayItem *ptrayItem = systray;
115
116     while (ptrayItem) {
117       if (ptrayItem->hWnd == hWnd) {
118         if (ptrayItem->notifyIcon.hWnd && ptrayItem->notifyIcon.uCallbackMessage) {
119           if (!PostMessageA(ptrayItem->notifyIcon.hWnd, ptrayItem->notifyIcon.uCallbackMessage,
120                             (WPARAM)ptrayItem->notifyIcon.uID, (LPARAM)message)) {
121               ERR("PostMessage(SystrayWindow 0x%08x) failed -> removing SystrayItem %p\n", hWnd, ptrayItem);
122               SYSTRAY_Delete(&ptrayItem->notifyIcon);
123             }
124         }
125         break;
126       }
127       ptrayItem = ptrayItem->nextTrayItem;
128     }
129   }
130   break;
131
132   default:
133     return (DefWindowProcA(hWnd, message, wParam, lParam));
134   }
135   return (0);
136
137 }
138
139
140 BOOL SYSTRAY_RegisterClass(void)
141 {
142   WNDCLASSA  wc;
143
144   wc.style         = CS_SAVEBITS;
145   wc.lpfnWndProc   = (WNDPROC)SYSTRAY_WndProc;
146   wc.cbClsExtra    = 0;
147   wc.cbWndExtra    = 0;
148   wc.hInstance     = 0;
149   wc.hIcon         = 0;
150   wc.hCursor       = LoadCursorA(0, IDC_ARROWA);
151   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW);
152   wc.lpszMenuName  = NULL;
153   wc.lpszClassName = "WineSystray";
154
155   if (!RegisterClassA(&wc)) {
156     ERR("RegisterClass(WineSystray) failed\n");
157     return FALSE;
158   }
159   return TRUE;
160 }
161
162
163 BOOL SYSTRAY_ItemInit(SystrayItem *ptrayItem)
164 {
165   RECT rect;
166
167   /* Register the class if this is our first tray item. */
168   if ( firstSystray ) {
169     firstSystray = FALSE;
170     if ( !SYSTRAY_RegisterClass() ) {
171       ERR( "RegisterClass(WineSystray) failed\n" );
172       return FALSE;
173     }
174   }
175
176   /* Initialize the window size. */
177   rect.left   = 0;
178   rect.top    = 0;
179   rect.right  = ICON_SIZE+2*ICON_BORDER;
180   rect.bottom = ICON_SIZE+2*ICON_BORDER;
181
182   ZeroMemory( ptrayItem, sizeof(SystrayItem) );
183   /* Create tray window for icon. */
184   ptrayItem->hWnd = CreateWindowExA( WS_EX_TRAYWINDOW,
185                                 "WineSystray", "Wine-Systray",
186                                 WS_VISIBLE,
187                                 CW_USEDEFAULT, CW_USEDEFAULT,
188                                 rect.right-rect.left, rect.bottom-rect.top,
189                                 0, 0, 0, 0 );
190   if ( !ptrayItem->hWnd ) {
191     ERR( "CreateWindow(WineSystray) failed\n" );
192     return FALSE;
193   }
194
195   /* Create tooltip for icon. */
196   ptrayItem->hWndToolTip = CreateWindowA( TOOLTIPS_CLASSA,NULL,TTS_ALWAYSTIP,
197                                      CW_USEDEFAULT, CW_USEDEFAULT,
198                                      CW_USEDEFAULT, CW_USEDEFAULT,
199                                      ptrayItem->hWnd, 0, 0, 0 );
200   if ( !ptrayItem->hWndToolTip ) {
201     ERR( "CreateWindow(TOOLTIP) failed\n" );
202     return FALSE;
203   }
204   return TRUE;
205 }
206
207
208 static void SYSTRAY_ItemTerm(SystrayItem *ptrayItem)
209 {
210   if(ptrayItem->hWndToolTip)
211       DestroyWindow(ptrayItem->hWndToolTip);
212   if(ptrayItem->hWnd)
213     DestroyWindow(ptrayItem->hWnd);
214   return;
215 }
216
217
218 void SYSTRAY_ItemSetMessage(SystrayItem *ptrayItem, UINT uCallbackMessage)
219 {
220   ptrayItem->notifyIcon.uCallbackMessage = uCallbackMessage;
221 }
222
223
224 void SYSTRAY_ItemSetIcon(SystrayItem *ptrayItem, HICON hIcon)
225 {
226   ptrayItem->notifyIcon.hIcon = hIcon; /* do we need a copy? */
227   InvalidateRect(ptrayItem->hWnd, NULL, TRUE);
228 }
229
230
231 void SYSTRAY_ItemSetTip(SystrayItem *ptrayItem, CHAR* szTip, int modify)
232 {
233   TTTOOLINFOA ti;
234
235   strncpy(ptrayItem->notifyIcon.szTip, szTip, sizeof(ptrayItem->notifyIcon.szTip));
236   ptrayItem->notifyIcon.szTip[sizeof(ptrayItem->notifyIcon.szTip)-1]=0;
237
238   ti.cbSize = sizeof(TTTOOLINFOA);
239   ti.uFlags = 0;
240   ti.hwnd = ptrayItem->hWnd;
241   ti.hinst = 0;
242   ti.uId = 0;
243   ti.lpszText = ptrayItem->notifyIcon.szTip;
244   ti.rect.left   = 0;
245   ti.rect.top    = 0;
246   ti.rect.right  = ICON_SIZE+2*ICON_BORDER;
247   ti.rect.bottom = ICON_SIZE+2*ICON_BORDER;
248
249   if(modify)
250     SendMessageA(ptrayItem->hWndToolTip, TTM_UPDATETIPTEXTA, 0, (LPARAM)&ti);
251   else
252     SendMessageA(ptrayItem->hWndToolTip, TTM_ADDTOOLA, 0, (LPARAM)&ti);
253 }
254
255
256 static BOOL SYSTRAY_Add(PNOTIFYICONDATAA pnid)
257 {
258   SystrayItem **ptrayItem = &systray;
259
260   /* Find last element. */
261   while( *ptrayItem ) {
262     if ( SYSTRAY_ItemIsEqual(pnid, &(*ptrayItem)->notifyIcon) )
263       return FALSE;
264     ptrayItem = &((*ptrayItem)->nextTrayItem);
265   }
266   /* Allocate SystrayItem for element and add to end of list. */
267   (*ptrayItem) = ( SystrayItem *)malloc( sizeof(SystrayItem) );
268
269   /* Initialize and set data for the tray element. */
270   SYSTRAY_ItemInit( (*ptrayItem) );
271   (*ptrayItem)->notifyIcon.uID = pnid->uID; /* only needed for callback message */
272   (*ptrayItem)->notifyIcon.hWnd = pnid->hWnd; /* only needed for callback message */
273   SYSTRAY_ItemSetIcon   (*ptrayItem, (pnid->uFlags&NIF_ICON)   ?pnid->hIcon           :0);
274   SYSTRAY_ItemSetMessage(*ptrayItem, (pnid->uFlags&NIF_MESSAGE)?pnid->uCallbackMessage:0);
275   SYSTRAY_ItemSetTip    (*ptrayItem, (pnid->uFlags&NIF_TIP)    ?pnid->szTip           :"", FALSE);
276
277   TRACE("%p: 0x%08x %s\n",  (*ptrayItem), (*ptrayItem)->notifyIcon.hWnd,
278                                           (*ptrayItem)->notifyIcon.szTip);
279   return TRUE;
280 }
281
282
283 static BOOL SYSTRAY_Modify(PNOTIFYICONDATAA pnid)
284 {
285   SystrayItem *ptrayItem = systray;
286
287   while ( ptrayItem ) {
288     if ( SYSTRAY_ItemIsEqual(pnid, &ptrayItem->notifyIcon) ) {
289       if (pnid->uFlags & NIF_ICON)
290         SYSTRAY_ItemSetIcon(ptrayItem, pnid->hIcon);
291       if (pnid->uFlags & NIF_MESSAGE)
292         SYSTRAY_ItemSetMessage(ptrayItem, pnid->uCallbackMessage);
293       if (pnid->uFlags & NIF_TIP)
294         SYSTRAY_ItemSetTip(ptrayItem, pnid->szTip, TRUE);
295
296       TRACE("%p: 0x%08x %s\n", ptrayItem, ptrayItem->notifyIcon.hWnd, ptrayItem->notifyIcon.szTip);
297       return TRUE;
298     }
299     ptrayItem = ptrayItem->nextTrayItem;
300   }
301   return FALSE; /* not found */
302 }
303
304
305 static BOOL SYSTRAY_Delete(PNOTIFYICONDATAA pnid)
306 {
307   SystrayItem **ptrayItem = &systray;
308
309   while (*ptrayItem) {
310     if (SYSTRAY_ItemIsEqual(pnid, &(*ptrayItem)->notifyIcon)) {
311       SystrayItem *next = (*ptrayItem)->nextTrayItem;
312       TRACE("%p: 0x%08x %s\n", *ptrayItem, (*ptrayItem)->notifyIcon.hWnd, (*ptrayItem)->notifyIcon.szTip);
313       SYSTRAY_ItemTerm(*ptrayItem);
314
315       free(*ptrayItem);
316       *ptrayItem = next;
317
318       return TRUE;
319     }
320     ptrayItem = &((*ptrayItem)->nextTrayItem);
321   }
322
323   return FALSE; /* not found */
324 }
325
326 /*************************************************************************
327  *
328  */
329 BOOL SYSTRAY_Init(void)
330 {
331   return TRUE;
332 }
333
334 /*************************************************************************
335  * Shell_NotifyIconA                    [SHELL32.297]
336  */
337 BOOL WINAPI Shell_NotifyIconA(DWORD dwMessage, PNOTIFYICONDATAA pnid )
338 {
339   BOOL flag=FALSE;
340   TRACE("enter %d %d %ld\n", pnid->hWnd, pnid->uID, dwMessage);
341   switch(dwMessage) {
342   case NIM_ADD:
343     flag = SYSTRAY_Add(pnid);
344     break;
345   case NIM_MODIFY:
346     flag = SYSTRAY_Modify(pnid);
347     break;
348   case NIM_DELETE:
349     flag = SYSTRAY_Delete(pnid);
350     break;
351   }
352   TRACE("leave %d %d %ld=%d\n", pnid->hWnd, pnid->uID, dwMessage, flag);
353   return flag;
354 }
355
356 /*************************************************************************
357  * Shell_NotifyIconW                    [SHELL32.297]
358  */
359 BOOL WINAPI Shell_NotifyIconW (DWORD dwMessage, PNOTIFYICONDATAW pnid )
360 {
361         BOOL ret;
362
363         PNOTIFYICONDATAA p = HeapAlloc(GetProcessHeap(),0,sizeof(NOTIFYICONDATAA));
364         memcpy(p, pnid, sizeof(NOTIFYICONDATAA));
365         if (*(pnid->szTip))
366           lstrcpynWtoA (p->szTip, pnid->szTip, 64 );
367
368         ret = Shell_NotifyIconA(dwMessage, p );
369
370         HeapFree(GetProcessHeap(),0,p);
371         return ret;
372 }
373
374 /*************************************************************************
375  * Shell_NotifyIcon                     [SHELL32.296]
376  */
377 BOOL WINAPI Shell_NotifyIcon(DWORD dwMessage, PNOTIFYICONDATAA pnid)
378 {
379   return Shell_NotifyIconA(dwMessage, pnid);
380 }