| /* |
| * Systray |
| * |
| * Copyright 1999 Kai Morich <kai.morich@bigfoot.de> |
| * |
| * Manage the systray window. That it actually appears in the docking |
| * area of KDE or GNOME is delegated to windows/x11drv/wnd.c, |
| * X11DRV_WND_DockWindow. |
| * |
| */ |
| |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "shellapi.h" |
| #include "shell32_main.h" |
| #include "windows.h" |
| #include "commctrl.h" |
| #include "debugtools.h" |
| #include "config.h" |
| |
| DEFAULT_DEBUG_CHANNEL(shell) |
| |
| |
| typedef struct SystrayItem { |
| NOTIFYICONDATAA notifyIcon; |
| int nitem; /* number of current element = tooltip id */ |
| struct SystrayItem *next; /* nextright systray item */ |
| } SystrayItem; |
| |
| |
| typedef struct SystrayData { |
| int hasCritSection; |
| CRITICAL_SECTION critSection; |
| HWND hWnd; |
| HWND hWndToolTip; |
| int nitems; /* number of elements in systray */ |
| SystrayItem *next; /* leftmost systray item */ |
| } SystrayData; |
| |
| |
| static SystrayData systray; |
| |
| static BOOL SYSTRAY_Delete(PNOTIFYICONDATAA pnid); |
| |
| |
| /************************************************************************** |
| * internal systray |
| * |
| */ |
| #define SMALL_ICON_SIZE GetSystemMetrics(SM_CXSMICON) |
| /* space between icons */ |
| #define SMALL_ICON_FILL 1 |
| /* space between icons and frame */ |
| #define IBORDER 3 |
| #define OBORDER 2 |
| #define TBORDER (OBORDER+1+IBORDER) |
| |
| #define ICON_TOP(i) (TBORDER) |
| #define ICON_LEFT(i) (TBORDER+(i)*(SMALL_ICON_SIZE+SMALL_ICON_FILL)) |
| #define ICON_RIGHT(i) (TBORDER+(i)*(SMALL_ICON_SIZE+SMALL_ICON_FILL)+SMALL_ICON_SIZE) |
| #define ICON_BOTTOM(i) (TBORDER+SMALL_ICON_SIZE) |
| |
| static LRESULT CALLBACK SYSTRAY_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) |
| { |
| HDC hdc; |
| PAINTSTRUCT ps; |
| |
| switch (message) { |
| case WM_PAINT: |
| { |
| RECT rc; |
| SystrayItem *trayItem = systray.next; |
| |
| hdc = BeginPaint(hWnd, &ps); |
| GetClientRect(hWnd, &rc); |
| /* |
| rc.top += OBORDER; |
| rc.bottom -= OBORDER; |
| rc.left += OBORDER; |
| rc.right -= OBORDER; |
| DrawEdge(hdc, &rc, EDGE_SUNKEN, BF_TOPLEFT|BF_BOTTOMRIGHT); |
| */ |
| rc.top = TBORDER; |
| rc.left = TBORDER; |
| while (trayItem) { |
| if (trayItem->notifyIcon.hIcon) |
| if (!DrawIconEx(hdc, rc.left, rc.top, trayItem->notifyIcon.hIcon, |
| SMALL_ICON_SIZE, SMALL_ICON_SIZE, 0, 0, DI_DEFAULTSIZE|DI_NORMAL)) |
| SYSTRAY_Delete(&trayItem->notifyIcon); |
| trayItem = trayItem->next; |
| rc.left += SMALL_ICON_SIZE+SMALL_ICON_FILL; |
| } |
| EndPaint(hWnd, &ps); |
| } break; |
| case WM_MOUSEMOVE: |
| case WM_LBUTTONDOWN: |
| case WM_LBUTTONUP: |
| case WM_RBUTTONDOWN: |
| case WM_RBUTTONUP: |
| case WM_MBUTTONDOWN: |
| case WM_MBUTTONUP: |
| { |
| MSG msg; |
| RECT rect; |
| GetWindowRect(hWnd, &rect); |
| msg.hwnd=hWnd; |
| msg.message=message; |
| msg.wParam=wParam; |
| msg.lParam=lParam; |
| msg.time = GetMessageTime (); |
| msg.pt.x = LOWORD(GetMessagePos ()); |
| msg.pt.y = HIWORD(GetMessagePos ()); |
| SendMessageA(systray.hWndToolTip, TTM_RELAYEVENT, 0, (LPARAM)&msg); |
| } |
| case WM_LBUTTONDBLCLK: |
| case WM_RBUTTONDBLCLK: |
| case WM_MBUTTONDBLCLK: |
| { |
| int xPos = LOWORD(lParam); |
| int xItem = TBORDER; |
| SystrayItem *trayItem = systray.next; |
| while (trayItem) { |
| if (xPos>=xItem && xPos<(xItem+SMALL_ICON_SIZE)) { |
| if (!PostMessageA(trayItem->notifyIcon.hWnd, trayItem->notifyIcon.uCallbackMessage, |
| (WPARAM)trayItem->notifyIcon.hIcon, (LPARAM)message)) |
| SYSTRAY_Delete(&trayItem->notifyIcon); |
| break; |
| } |
| trayItem = trayItem->next; |
| xItem += SMALL_ICON_SIZE+SMALL_ICON_FILL; |
| } |
| } break; |
| default: |
| return (DefWindowProcA(hWnd, message, wParam, lParam)); |
| } |
| return (0); |
| } |
| |
| BOOL SYSTRAY_Create(void) |
| { |
| WNDCLASSA wc; |
| RECT rect; |
| wc.style = CS_SAVEBITS; |
| wc.lpfnWndProc = (WNDPROC)SYSTRAY_WndProc; |
| wc.cbClsExtra = 0; |
| wc.cbWndExtra = 0; |
| wc.hInstance = 0; |
| wc.hIcon = 0; /* LoadIcon (NULL, IDI_EXCLAMATION); */ |
| wc.hCursor = LoadCursorA(0, IDC_ARROWA); |
| wc.hbrBackground = (HBRUSH)(COLOR_WINDOW); |
| wc.lpszMenuName = NULL; |
| wc.lpszClassName = "WineSystray"; |
| |
| TRACE("\n"); |
| if (!RegisterClassA(&wc)) { |
| ERR("RegisterClass(WineSystray) failed\n"); |
| return FALSE; |
| } |
| rect.left = 0; |
| rect.top = 0; |
| rect.right = SMALL_ICON_SIZE+2*TBORDER; |
| rect.bottom = SMALL_ICON_SIZE+2*TBORDER; |
| /* AdjustWindowRect(&rect, WS_CAPTION, FALSE);*/ |
| |
| systray.hWnd = CreateWindowExA( |
| WS_EX_TRAYWINDOW, |
| "WineSystray", "Wine-Systray", |
| WS_VISIBLE, |
| CW_USEDEFAULT, CW_USEDEFAULT, |
| rect.right-rect.left, rect.bottom-rect.top, |
| 0, 0, 0, 0); |
| if (!systray.hWnd) { |
| ERR("CreateWindow(WineSystray) failed\n"); |
| return FALSE; |
| } |
| systray.hWndToolTip = CreateWindowA(TOOLTIPS_CLASSA,NULL,TTS_ALWAYSTIP, |
| CW_USEDEFAULT, CW_USEDEFAULT, |
| CW_USEDEFAULT, CW_USEDEFAULT, |
| systray.hWnd, 0, 0, 0); |
| if (systray.hWndToolTip==0) { |
| ERR("CreateWindow(TOOLTIP) failed\n"); |
| return FALSE; |
| } |
| return TRUE; |
| } |
| |
| |
| static void SYSTRAY_RepaintItem(int nitem) |
| { |
| if(nitem<0) { /* repaint all items */ |
| RECT rc1, rc2; |
| GetWindowRect(systray.hWnd, &rc1); |
| rc2.left = 0; |
| rc2.top = 0; |
| rc2.right = systray.nitems*(SMALL_ICON_SIZE+SMALL_ICON_FILL)+2*TBORDER; |
| rc2.bottom = SMALL_ICON_SIZE+2*TBORDER; |
| /* TRACE("%d %d %d %d %d\n",systray.nitems, rc1.left, rc1.top, rc2.right-rc2.left, rc2.bottom-rc2.top); */ |
| /* AdjustWindowRect(&rc2, WS_CAPTION, FALSE);*/ |
| MoveWindow(systray.hWnd, rc1.left, rc1.top, rc2.right-rc2.left, rc2.bottom-rc2.top, TRUE); |
| InvalidateRect(systray.hWnd, NULL, TRUE); |
| /* TRACE("%d %d %d %d %d\n",systray.nitems, rc1.left, rc1.top, rc2.right-rc2.left, rc2.bottom-rc2.top); */ |
| } else { |
| RECT rc; |
| rc.left = ICON_LEFT(nitem); |
| rc.top = ICON_TOP(nitem); |
| rc.right = ICON_RIGHT(nitem); |
| rc.bottom = ICON_BOTTOM(nitem); |
| InvalidateRect(systray.hWnd, &rc, TRUE); |
| } |
| } |
| |
| void SYSTRAY_InitItem(SystrayItem *trayItem) |
| { |
| trayItem->nitem = systray.nitems++; |
| /* create window only if needed */ |
| if (systray.hWnd==0) |
| SYSTRAY_Create(); |
| } |
| |
| void SYSTRAY_SetIcon(SystrayItem *trayItem, HICON hIcon) |
| { |
| trayItem->notifyIcon.hIcon = hIcon; |
| SYSTRAY_RepaintItem(trayItem->nitem>systray.nitems?-1:trayItem->nitem); |
| } |
| |
| void SYSTRAY_SetTip2(SystrayItem *trayItem) |
| { |
| TTTOOLINFOA ti; |
| |
| ti.cbSize = sizeof(TTTOOLINFOA); |
| ti.uFlags = 0; |
| ti.hwnd = systray.hWnd; |
| ti.hinst = 0; |
| ti.uId = trayItem->nitem; |
| ti.lpszText = trayItem->notifyIcon.szTip; |
| ti.rect.left = ICON_LEFT(trayItem->nitem); |
| ti.rect.top = ICON_TOP(trayItem->nitem); |
| ti.rect.right = ICON_RIGHT(trayItem->nitem); |
| ti.rect.bottom = ICON_BOTTOM(trayItem->nitem); |
| SendMessageA(systray.hWndToolTip, TTM_ADDTOOLA, 0, (LPARAM)&ti); |
| } |
| |
| static void SYSTRAY_TermItem(SystrayItem *removeItem) |
| { |
| int nitem; |
| SystrayItem **trayItem; |
| TTTOOLINFOA ti; |
| ti.cbSize = sizeof(TTTOOLINFOA); |
| ti.uFlags = 0; |
| ti.hwnd = systray.hWnd; |
| ti.hinst = 0; |
| |
| /* delete all tooltips ...*/ |
| trayItem = &systray.next; |
| while (*trayItem) { |
| ti.uId = (*trayItem)->nitem; |
| SendMessageA(systray.hWndToolTip, TTM_DELTOOLA, 0, (LPARAM)&ti); |
| trayItem = &((*trayItem)->next); |
| } |
| /* ... and add them again, because uID may shift */ |
| nitem=0; |
| trayItem = &systray.next; |
| while (*trayItem) { |
| if (*trayItem != removeItem) { |
| (*trayItem)->nitem = nitem; |
| ti.uId = nitem; |
| ti.lpszText = (*trayItem)->notifyIcon.szTip; |
| ti.rect.left = ICON_LEFT(nitem); |
| ti.rect.top = ICON_TOP(nitem); |
| ti.rect.right = ICON_RIGHT(nitem); |
| ti.rect.bottom = ICON_BOTTOM(nitem); |
| SendMessageA(systray.hWndToolTip, TTM_ADDTOOLA, 0, (LPARAM)&ti); |
| nitem++; |
| } |
| trayItem = &((*trayItem)->next); |
| } |
| /* remove icon */ |
| SYSTRAY_RepaintItem(-1); |
| systray.nitems--; |
| } |
| |
| /************************************************************************** |
| * helperfunctions |
| * |
| */ |
| void SYSTRAY_SetMessage(SystrayItem *trayItem, UINT uCallbackMessage) |
| { |
| trayItem->notifyIcon.uCallbackMessage = uCallbackMessage; |
| } |
| |
| |
| void SYSTRAY_SetTip(SystrayItem *trayItem, CHAR* szTip) |
| { |
| /* char *s; */ |
| strncpy(trayItem->notifyIcon.szTip, szTip, sizeof(trayItem->notifyIcon.szTip)); |
| trayItem->notifyIcon.szTip[sizeof(trayItem->notifyIcon.szTip)-1]=0; |
| /* cut off trailing spaces */ |
| /* |
| s=trayItem->notifyIcon.szTip+strlen(trayItem->notifyIcon.szTip); |
| while (--s >= trayItem->notifyIcon.szTip && *s == ' ') |
| *s=0; |
| */ |
| SYSTRAY_SetTip2(trayItem); |
| } |
| |
| |
| static BOOL SYSTRAY_IsEqual(PNOTIFYICONDATAA pnid1, PNOTIFYICONDATAA pnid2) |
| { |
| if (pnid1->hWnd != pnid2->hWnd) return FALSE; |
| if (pnid1->uID != pnid2->uID) return FALSE; |
| return TRUE; |
| } |
| |
| |
| static BOOL SYSTRAY_Add(PNOTIFYICONDATAA pnid) |
| { |
| SystrayItem **trayItem = &systray.next; |
| while (*trayItem) { |
| if (SYSTRAY_IsEqual(pnid, &(*trayItem)->notifyIcon)) |
| return FALSE; |
| trayItem = &((*trayItem)->next); |
| } |
| (*trayItem) = (SystrayItem*)malloc(sizeof(SystrayItem)); |
| memcpy(&(*trayItem)->notifyIcon, pnid, sizeof(NOTIFYICONDATAA)); |
| SYSTRAY_InitItem(*trayItem); |
| SYSTRAY_SetIcon (*trayItem, (pnid->uFlags&NIF_ICON) ?pnid->hIcon :0); |
| SYSTRAY_SetMessage(*trayItem, (pnid->uFlags&NIF_MESSAGE)?pnid->uCallbackMessage:0); |
| SYSTRAY_SetTip (*trayItem, (pnid->uFlags&NIF_TIP) ?pnid->szTip :""); |
| (*trayItem)->next = NULL; |
| TRACE("%p: 0x%08x %d %s\n", *trayItem, (*trayItem)->notifyIcon.hWnd, |
| (*trayItem)->notifyIcon.uID, (*trayItem)->notifyIcon.szTip); |
| return TRUE; |
| } |
| |
| |
| static BOOL SYSTRAY_Modify(PNOTIFYICONDATAA pnid) |
| { |
| SystrayItem *trayItem = systray.next; |
| while (trayItem) { |
| if (SYSTRAY_IsEqual(pnid, &trayItem->notifyIcon)) { |
| if (pnid->uFlags & NIF_ICON) |
| SYSTRAY_SetIcon(trayItem, pnid->hIcon); |
| if (pnid->uFlags & NIF_MESSAGE) |
| SYSTRAY_SetMessage(trayItem, pnid->uCallbackMessage); |
| if (pnid->uFlags & NIF_TIP) |
| SYSTRAY_SetTip(trayItem, pnid->szTip); |
| TRACE("%p: 0x%08x %d %s\n", trayItem, trayItem->notifyIcon.hWnd, |
| trayItem->notifyIcon.uID, trayItem->notifyIcon.szTip); |
| return TRUE; |
| } |
| trayItem = trayItem->next; |
| } |
| return FALSE; /* not found */ |
| } |
| |
| |
| static BOOL SYSTRAY_Delete(PNOTIFYICONDATAA pnid) |
| { |
| SystrayItem **trayItem = &systray.next; |
| while (*trayItem) { |
| if (SYSTRAY_IsEqual(pnid, &(*trayItem)->notifyIcon)) { |
| SystrayItem *next = (*trayItem)->next; |
| TRACE("%p: 0x%08x %d %s\n", *trayItem, (*trayItem)->notifyIcon.hWnd, |
| (*trayItem)->notifyIcon.uID, (*trayItem)->notifyIcon.szTip); |
| SYSTRAY_TermItem(*trayItem); |
| free(*trayItem); |
| *trayItem = next; |
| |
| return TRUE; |
| } |
| trayItem = &((*trayItem)->next); |
| } |
| |
| return FALSE; /* not found */ |
| } |
| |
| |
| /************************************************************************* |
| * |
| */ |
| BOOL SYSTRAY_Init(void) |
| { |
| if (!systray.hasCritSection) { |
| systray.hasCritSection=1; |
| InitializeCriticalSection(&systray.critSection); |
| MakeCriticalSectionGlobal(&systray.critSection); |
| TRACE(" =%p\n", &systray.critSection); |
| } |
| return TRUE; |
| } |
| |
| |
| /************************************************************************* |
| * Shell_NotifyIconA [SHELL32.297] |
| */ |
| BOOL WINAPI Shell_NotifyIconA(DWORD dwMessage, PNOTIFYICONDATAA pnid ) |
| { |
| BOOL flag=FALSE; |
| TRACE("wait %d %d %ld\n", pnid->hWnd, pnid->uID, dwMessage); |
| /* must be serialized because all apps access same systray */ |
| EnterCriticalSection(&systray.critSection); |
| TRACE("enter %d %d %ld\n", pnid->hWnd, pnid->uID, dwMessage); |
| |
| switch(dwMessage) { |
| case NIM_ADD: |
| flag = SYSTRAY_Add(pnid); |
| break; |
| case NIM_MODIFY: |
| flag = SYSTRAY_Modify(pnid); |
| break; |
| case NIM_DELETE: |
| flag = SYSTRAY_Delete(pnid); |
| break; |
| } |
| |
| LeaveCriticalSection(&systray.critSection); |
| TRACE("leave %d %d %ld\n", pnid->hWnd, pnid->uID, dwMessage); |
| return flag; |
| } |
| |
| /************************************************************************* |
| * Shell_NotifyIcon [SHELL32.296] |
| */ |
| BOOL WINAPI Shell_NotifyIcon(DWORD dwMessage, PNOTIFYICONDATAA pnid) |
| { |
| return Shell_NotifyIconA(dwMessage, pnid); |
| } |