/* * Systray * * Copyright 1999 Kai Morich * * 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 #include #include #include "heap.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_NotifyIconA [SHELL32.297] */ BOOL WINAPI Shell_NotifyIconW (DWORD dwMessage, PNOTIFYICONDATAW pnid ) { BOOL ret; PNOTIFYICONDATAA p = HeapAlloc(GetProcessHeap(),0,sizeof(NOTIFYICONDATAA)); memcpy(p, pnid, sizeof(NOTIFYICONDATAA)); if (*(pnid->szTip)) lstrcpynWtoA (p->szTip, pnid->szTip, 64 ); ret = Shell_NotifyIconA(dwMessage, p ); HeapFree(GetProcessHeap(),0,p); return ret; } /************************************************************************* * Shell_NotifyIcon [SHELL32.296] */ BOOL WINAPI Shell_NotifyIcon(DWORD dwMessage, PNOTIFYICONDATAA pnid) { return Shell_NotifyIconA(dwMessage, pnid); }