/* Miranda IM: the free IM client for Microsoft* Windows* Copyright 2000-2009 Miranda ICQ/IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "..\..\core\commonheaders.h" #include "clc.h" #define TOOLTIP_TOLERANCE 5 extern HIMAGELIST hCListImages; extern BOOL(WINAPI * MySetProcessWorkingSetSize) (HANDLE, SIZE_T, SIZE_T); int GetAverageMode(int* pNetProtoCount = NULL); static UINT WM_TASKBARCREATED; static UINT WM_TASKBARBUTTONCREATED; static BOOL mToolTipTrayTips = FALSE; static UINT_PTR cycleTimerId = 0; static int cycleStep = 0; static UINT_PTR RefreshTimerId=0; /////by FYR static CRITICAL_SECTION trayLockCS; // don't move to win2k.h, need new and old versions to work on 9x/2000/XP #define NIF_STATE 0x00000008 #define NIF_INFO 0x00000010 #define lock cli.pfnLockTray() #define ulock cli.pfnUnlockTray() #define initcheck if (!fTrayInited) return static BOOL fTrayInited=FALSE; static TCHAR* sttGetXStatus( const char* szProto ) { TCHAR* result = NULL; if ( CallProtoService( szProto, PS_GETSTATUS, 0, 0 ) > ID_STATUS_OFFLINE ) { char str[MAXMODULELABELLENGTH]; mir_snprintf( str, sizeof(str), "%s/GetXStatus", szProto ); if ( ServiceExists( str )) { char* dbTitle = "XStatusName"; char* dbTitle2 = NULL; int xstatus = CallProtoService( szProto, "/GetXStatus", ( WPARAM )&dbTitle, ( LPARAM )&dbTitle2 ); if ( dbTitle && xstatus ) { DBVARIANT dbv={0}; if ( !DBGetContactSettingTString(NULL, szProto, dbTitle, &dbv )) { if ( dbv.ptszVal[0] != 0 ) result = mir_tstrdup(dbv.ptszVal); DBFreeVariant(&dbv); } } } } return result; } static HICON lastTaskBarIcon; static void SetTaskBarIcon(const HICON hIcon, const TCHAR *szNewTip) { if (pTaskbarInterface) { wchar_t *szTip = mir_t2u(szNewTip); pTaskbarInterface->SetOverlayIcon(cli.hwndContactList, hIcon, szTip); mir_free(szTip); lastTaskBarIcon = hIcon; } } TCHAR* fnTrayIconMakeTooltip( const TCHAR *szPrefix, const char *szProto ) { TCHAR *szStatus, *szSeparator; TCHAR *ProtoXStatus=NULL; int t; PROTOACCOUNT* pa; initcheck NULL; lock; if ( !mToolTipTrayTips ) szSeparator = (IsWinVerMEPlus()) ? szSeparator = _T("\n") : _T(" | "); else szSeparator = _T("\n"); if (szProto == NULL) { if (accounts.getCount() == 0) { ulock; return NULL; } if (accounts.getCount() == 1) { ulock; return cli.pfnTrayIconMakeTooltip(szPrefix, accounts[0]->szModuleName); } if (szPrefix && szPrefix[0]) { lstrcpyn(cli.szTip, szPrefix, MAX_TIP_SIZE); if (!DBGetContactSettingByte(NULL, "CList", "AlwaysStatus", SETTING_ALWAYSSTATUS_DEFAULT)) { ulock; return cli.szTip; } } else cli.szTip[0] = '\0'; cli.szTip[ MAX_TIP_SIZE-1 ] = '\0'; for ( t = 0; t < accounts.getCount(); t++ ) { int i = cli.pfnGetAccountIndexByPos( t ); if ( i == -1 ) continue; pa = accounts[i]; if ( !cli.pfnGetProtocolVisibility( pa->szModuleName )) continue; szStatus = cli.pfnGetStatusModeDescription( CallProtoService( pa->szModuleName, PS_GETSTATUS, 0, 0), 0); if ( !szStatus ) continue; ProtoXStatus = sttGetXStatus( pa->szModuleName ); if ( mToolTipTrayTips ) { TCHAR tipline[256]; mir_sntprintf(tipline, SIZEOF(tipline), _T("%-12.12s\t%s"), pa->tszAccountName, szStatus); if ( cli.szTip[0] ) _tcsncat(cli.szTip, szSeparator, MAX_TIP_SIZE - _tcslen(cli.szTip)); _tcsncat(cli.szTip, tipline, MAX_TIP_SIZE - _tcslen(cli.szTip)); if (ProtoXStatus) { mir_sntprintf(tipline, SIZEOF(tipline), _T("%-24.24s\n"), ProtoXStatus); if ( cli.szTip[0] ) _tcsncat(cli.szTip, szSeparator, MAX_TIP_SIZE - _tcslen(cli.szTip)); _tcsncat(cli.szTip, tipline, MAX_TIP_SIZE - _tcslen(cli.szTip)); } } else { if (cli.szTip[0]) _tcsncat(cli.szTip, szSeparator, MAX_TIP_SIZE - _tcslen(cli.szTip)); _tcsncat(cli.szTip, pa->tszAccountName, MAX_TIP_SIZE - _tcslen(cli.szTip)); _tcsncat(cli.szTip, _T(" "), MAX_TIP_SIZE - _tcslen(cli.szTip)); _tcsncat(cli.szTip, szStatus, MAX_TIP_SIZE - _tcslen(cli.szTip)); } mir_free( ProtoXStatus ); } } else { if (( pa = Proto_GetAccount( szProto )) != NULL ) { ProtoXStatus = sttGetXStatus( szProto ); szStatus = cli.pfnGetStatusModeDescription(CallProtoService(szProto, PS_GETSTATUS, 0, 0), 0); if ( szPrefix && szPrefix[0] ) { if ( DBGetContactSettingByte( NULL, "CList", "AlwaysStatus", SETTING_ALWAYSSTATUS_DEFAULT )) { if ( mToolTipTrayTips ) { if ( ProtoXStatus ) mir_sntprintf(cli.szTip, MAX_TIP_SIZE, _T("%s%s%-12.12s\t%s%s%-24.24s"), szPrefix, szSeparator, pa->tszAccountName, szStatus, szSeparator, ProtoXStatus); else mir_sntprintf(cli.szTip, MAX_TIP_SIZE, _T("%s%s%-12.12s\t%s"), szPrefix, szSeparator, pa->tszAccountName, szStatus); } else mir_sntprintf(cli.szTip, MAX_TIP_SIZE, _T("%s%s%s %s"), szPrefix, szSeparator, pa->tszAccountName, szStatus); } else lstrcpyn(cli.szTip, szPrefix, MAX_TIP_SIZE); } else { if ( mToolTipTrayTips ) { if ( ProtoXStatus ) mir_sntprintf( cli.szTip, MAX_TIP_SIZE, _T("%-12.12s\t%s\n%-24.24s"), pa->tszAccountName, szStatus, ProtoXStatus); else mir_sntprintf( cli.szTip, MAX_TIP_SIZE, _T("%-12.12s\t%s"), pa->tszAccountName, szStatus); } else mir_sntprintf(cli.szTip, MAX_TIP_SIZE, _T("%s %s"), pa->tszAccountName, szStatus); } mir_free(ProtoXStatus); } } ulock; return cli.szTip; } int fnTrayIconAdd(HWND hwnd, const char *szProto, const char *szIconProto, int status) { NOTIFYICONDATA nid = { 0 }; int i; initcheck 0; lock; for (i = 0; i < cli.trayIconCount; i++) if (cli.trayIcon[i].id == 0) break; cli.trayIcon[i].id = TRAYICON_ID_BASE + i; cli.trayIcon[i].szProto = (char *) szProto; cli.trayIcon[i].hBaseIcon = cli.pfnGetIconFromStatusMode( NULL, szIconProto ? szIconProto : cli.trayIcon[i].szProto, status ); nid.cbSize = ( cli.shellVersion >= 5 ) ? sizeof(nid) : NOTIFYICONDATA_V1_SIZE; nid.hWnd = hwnd; nid.uID = cli.trayIcon[i].id; nid.uFlags = mToolTipTrayTips ? NIF_ICON | NIF_MESSAGE : NIF_ICON | NIF_MESSAGE | NIF_TIP; nid.uCallbackMessage = TIM_CALLBACK; nid.hIcon = cli.trayIcon[i].hBaseIcon; if (cli.shellVersion >= 5) nid.uFlags |= NIF_INFO; cli.pfnTrayIconMakeTooltip( NULL, cli.trayIcon[i].szProto ); if ( !mToolTipTrayTips ) lstrcpyn( nid.szTip, cli.szTip, SIZEOF( nid.szTip )); cli.trayIcon[i].ptszToolTip = mir_tstrdup( cli.szTip ); Shell_NotifyIcon(NIM_ADD, &nid); cli.trayIcon[i].isBase = 1; if (cli.trayIconCount == 1) SetTaskBarIcon(cli.trayIcon[0].hBaseIcon, cli.szTip); ulock; return i; } void fnTrayIconRemove(HWND hwnd, const char *szProto) { int i; initcheck; lock; for ( i = 0; i < cli.trayIconCount; i++ ) { struct trayIconInfo_t* pii = &cli.trayIcon[i]; if ( pii->id != 0 && !lstrcmpA( szProto, pii->szProto )) { NOTIFYICONDATA nid = { 0 }; nid.cbSize = ( cli.shellVersion >= 5 ) ? sizeof(nid) : NOTIFYICONDATA_V1_SIZE; nid.hWnd = hwnd; nid.uID = pii->id; Shell_NotifyIcon(NIM_DELETE, &nid); DestroyIcon(pii->hBaseIcon); mir_free(pii->ptszToolTip); pii->ptszToolTip = NULL; pii->id = 0; break; } } if (cli.trayIconCount == 1) SetTaskBarIcon(NULL, NULL); ulock; } int fnTrayIconInit(HWND hwnd) { int netProtoCount = 0; initcheck 0; lock; int averageMode = GetAverageMode(&netProtoCount); mToolTipTrayTips = ServiceExists("mToolTip/ShowTip") != 0; if ( cli.cycleTimerId ) { KillTimer(NULL, cli.cycleTimerId); cli.cycleTimerId = 0; } cli.trayIconCount = 1; if (netProtoCount) { cli.trayIcon = (trayIconInfo_t *) mir_calloc(sizeof(trayIconInfo_t) * accounts.getCount()); int trayIconSetting = DBGetContactSettingByte(NULL, "CList", "TrayIcon", SETTING_TRAYICON_DEFAULT); if (trayIconSetting == SETTING_TRAYICON_SINGLE) { DBVARIANT dbv = { DBVT_DELETED }; char *szProto; if (!DBGetContactSettingString(NULL, "CList", "PrimaryStatus", &dbv) && (averageMode < 0 || DBGetContactSettingByte(NULL, "CList", "AlwaysPrimary", 0) )) szProto = dbv.pszVal; else szProto = NULL; cli.pfnTrayIconAdd(hwnd, NULL, szProto, szProto ? CallProtoService(szProto, PS_GETSTATUS, 0, 0) : CallService(MS_CLIST_GETSTATUSMODE, 0, 0)); DBFreeVariant(&dbv); } else if (trayIconSetting == SETTING_TRAYICON_MULTI && (averageMode < 0 || DBGetContactSettingByte(NULL, "CList", "AlwaysMulti", SETTING_ALWAYSMULTI_DEFAULT ))) { cli.trayIconCount = netProtoCount; for (int i = 0; i < accounts.getCount(); ++i) { int j = cli.pfnGetAccountIndexByPos(i); if (j >= 0) { PROTOACCOUNT* pa = accounts[j]; if (cli.pfnGetProtocolVisibility(pa->szModuleName)) cli.pfnTrayIconAdd(hwnd, pa->szModuleName, NULL, CallProtoService(pa->szModuleName, PS_GETSTATUS, 0, 0)); } } } else { cli.pfnTrayIconAdd(hwnd, NULL, NULL, averageMode); if (trayIconSetting == SETTING_TRAYICON_CYCLE && averageMode < 0) cli.cycleTimerId = SetTimer(NULL, 0, DBGetContactSettingWord(NULL, "CList", "CycleTime", SETTING_CYCLETIME_DEFAULT) * 1000, cli.pfnTrayCycleTimerProc); } } else { cli.trayIcon = (trayIconInfo_t *) mir_calloc(sizeof(trayIconInfo_t)); cli.pfnTrayIconAdd(hwnd, NULL, NULL, CallService(MS_CLIST_GETSTATUSMODE, 0, 0)); } ulock; return 0; } int fnTrayIconDestroy(HWND hwnd) { NOTIFYICONDATA nid = { 0 }; int i; initcheck 0; lock; if (cli.trayIconCount == 1) SetTaskBarIcon(NULL, NULL); nid.cbSize = ( cli.shellVersion >= 5 ) ? sizeof(nid) : NOTIFYICONDATA_V1_SIZE; nid.hWnd = hwnd; for ( i = 0; i < cli.trayIconCount; i++ ) { if ( cli.trayIcon[i].id == 0 ) continue; nid.uID = cli.trayIcon[i].id; Shell_NotifyIcon( NIM_DELETE, &nid ); DestroyIcon( cli.trayIcon[i].hBaseIcon ); mir_free( cli.trayIcon[i].ptszToolTip ); } mir_free(cli.trayIcon); cli.trayIcon = NULL; cli.trayIconCount = 0; ulock; return 0; } //called when Explorer crashes and the taskbar is remade void fnTrayIconTaskbarCreated(HWND hwnd) { initcheck; cli.pfnTrayIconDestroy(hwnd); cli.pfnTrayIconInit(hwnd); } static VOID CALLBACK RefreshTimerProc(HWND, UINT, UINT_PTR, DWORD) { int i; if ( RefreshTimerId ) { KillTimer(NULL, RefreshTimerId); RefreshTimerId=0; } for (i=0; i < accounts.getCount(); i++) { cli.pfnTrayIconUpdateBase( accounts[i]->szModuleName ); } } int fnTrayIconUpdate(HICON hNewIcon, const TCHAR *szNewTip, const char *szPreferredProto, int isBase) { NOTIFYICONDATA nid = { 0 }; int i; initcheck -1; lock; nid.cbSize = ( cli.shellVersion >= 5 ) ? sizeof(nid) : NOTIFYICONDATA_V1_SIZE; nid.hWnd = cli.hwndContactList; nid.uFlags = mToolTipTrayTips ? NIF_ICON : NIF_ICON | NIF_TIP; nid.hIcon = hNewIcon; if (!hNewIcon) { ulock; return -1; } for (i = 0; i < cli.trayIconCount; i++) { if (cli.trayIcon[i].id == 0) continue; if (lstrcmpA(cli.trayIcon[i].szProto, szPreferredProto)) continue; nid.uID = cli.trayIcon[i].id; cli.pfnTrayIconMakeTooltip(szNewTip, cli.trayIcon[i].szProto); mir_free( cli.trayIcon[i].ptszToolTip ); cli.trayIcon[i].ptszToolTip = mir_tstrdup( cli.szTip ); if (!mToolTipTrayTips) lstrcpyn(nid.szTip, cli.szTip, SIZEOF(nid.szTip)); Shell_NotifyIcon(NIM_MODIFY, &nid); if (cli.trayIconCount == 1) SetTaskBarIcon(hNewIcon, cli.szTip); else SetTaskBarIcon(NULL, NULL); cli.trayIcon[i].isBase = isBase; { ulock; return i; } } //if there wasn't a suitable icon, change all the icons { for (i = 0; i < cli.trayIconCount; i++) { if (cli.trayIcon[i].id == 0) continue; nid.uID = cli.trayIcon[i].id; cli.pfnTrayIconMakeTooltip(szNewTip, cli.trayIcon[i].szProto); mir_free( cli.trayIcon[i].ptszToolTip ); cli.trayIcon[i].ptszToolTip = mir_tstrdup( cli.szTip ); if (!mToolTipTrayTips) lstrcpyn(nid.szTip, cli.szTip, SIZEOF(nid.szTip)); Shell_NotifyIcon(NIM_MODIFY, &nid); if (cli.trayIconCount == 1) SetTaskBarIcon(hNewIcon, cli.szTip); else SetTaskBarIcon(NULL, NULL); cli.trayIcon[i].isBase = isBase; if (DBGetContactSettingByte(NULL, "CList", "TrayIcon", SETTING_TRAYICON_DEFAULT) == SETTING_TRAYICON_MULTI) { DWORD time1=DBGetContactSettingWord(NULL, "CList", "CycleTime", SETTING_CYCLETIME_DEFAULT)*200; DWORD time2=DBGetContactSettingWord(NULL, "CList", "IconFlashTime", 550)+1000; DWORD time=max(max(2000, time1), time2); if (RefreshTimerId) {KillTimer(NULL, RefreshTimerId); RefreshTimerId=0;} RefreshTimerId=SetTimer(NULL, 0, time, RefreshTimerProc); // if unknown base was changed - than show preffered proto icon for 2 sec and reset it to original one after timeout } { ulock; return i; } } } { ulock; return -1; } } int fnTrayIconSetBaseInfo(HICON hIcon, const char *szPreferredProto) { int i; initcheck -1; lock; if (szPreferredProto) { for (i = 0; i < cli.trayIconCount; i++) { if (cli.trayIcon[i].id == 0) continue; if (lstrcmpA(cli.trayIcon[i].szProto, szPreferredProto)) continue; DestroyIcon(cli.trayIcon[i].hBaseIcon); cli.trayIcon[i].hBaseIcon = hIcon; ulock; return i; } if ((cli.pfnGetProtocolVisibility(szPreferredProto)) && (GetAverageMode() == -1) && (DBGetContactSettingByte(NULL, "CList", "TrayIcon", SETTING_TRAYICON_DEFAULT) == SETTING_TRAYICON_MULTI) && !(DBGetContactSettingByte(NULL, "CList", "AlwaysMulti", SETTING_ALWAYSMULTI_DEFAULT))) goto LBL_Error; } //if there wasn't a specific icon, there will only be one suitable for (i = 0; i < cli.trayIconCount; i++) { if (cli.trayIcon[i].id == 0) continue; DestroyIcon(cli.trayIcon[i].hBaseIcon); cli.trayIcon[i].hBaseIcon = hIcon; ulock; return i; } LBL_Error: DestroyIcon(hIcon); ulock; return -1; } void fnTrayIconUpdateWithImageList(int iImage, const TCHAR *szNewTip, char *szPreferredProto) { HICON hIcon = ImageList_GetIcon(hCListImages, iImage, ILD_NORMAL); cli.pfnTrayIconUpdate(hIcon, szNewTip, szPreferredProto, 0); DestroyIcon(hIcon); } VOID CALLBACK fnTrayCycleTimerProc(HWND, UINT, UINT_PTR, DWORD) { initcheck; lock; int i; for (i = accounts.getCount() + 1; --i;) { cycleStep = (cycleStep + 1) % accounts.getCount(); if ( cli.pfnGetProtocolVisibility( accounts[cycleStep]->szModuleName )) break; } if (i) { DestroyIcon(cli.trayIcon[0].hBaseIcon); cli.trayIcon[0].hBaseIcon = cli.pfnGetIconFromStatusMode(NULL, accounts[cycleStep]->szModuleName, CallProtoService( accounts[cycleStep]->szModuleName, PS_GETSTATUS, 0, 0 )); if (cli.trayIcon[0].isBase) cli.pfnTrayIconUpdate(cli.trayIcon[0].hBaseIcon, NULL, NULL, 1); } ulock; } void fnTrayIconUpdateBase(const char *szChangedProto) { if ( !cli.pfnGetProtocolVisibility( szChangedProto )) return; int i, netProtoCount, changed = -1; HWND hwnd = cli.hwndContactList; initcheck; lock; int averageMode = GetAverageMode(&netProtoCount); if (cli.cycleTimerId) { KillTimer(NULL, cli.cycleTimerId); cli.cycleTimerId = 0; } for (i = 0; i < accounts.getCount(); i++) { if (!lstrcmpA(szChangedProto, accounts[i]->szModuleName )) cycleStep = i - 1; } if (netProtoCount > 0) { int trayIconSetting = DBGetContactSettingByte(NULL, "CList", "TrayIcon", SETTING_TRAYICON_DEFAULT); if (averageMode > 0) { if (trayIconSetting == SETTING_TRAYICON_MULTI) { if (DBGetContactSettingByte(NULL, "CList", "AlwaysMulti", SETTING_ALWAYSMULTI_DEFAULT)) //changed = cli.pfnTrayIconSetBaseInfo( cli.pfnGetIconFromStatusMode((char*)szChangedProto, NULL, averageMode), (char*)szChangedProto); changed = cli.pfnTrayIconSetBaseInfo( cli.pfnGetIconFromStatusMode( NULL, szChangedProto, CallProtoService(szChangedProto, PS_GETSTATUS, 0, 0)), (char*)szChangedProto ); else if (cli.trayIcon && cli.trayIcon[0].szProto != NULL) { cli.pfnTrayIconDestroy(hwnd); cli.pfnTrayIconInit(hwnd); } else changed = cli.pfnTrayIconSetBaseInfo( cli.pfnGetIconFromStatusMode(NULL, NULL, averageMode), NULL ); } else changed = cli.pfnTrayIconSetBaseInfo( cli.pfnGetIconFromStatusMode(NULL, NULL, averageMode), NULL); } else { switch (trayIconSetting) { case SETTING_TRAYICON_SINGLE: { DBVARIANT dbv = { DBVT_DELETED }; char *szProto; if (DBGetContactSettingString(NULL, "CList", "PrimaryStatus", &dbv)) szProto = NULL; else szProto = dbv.pszVal; changed = cli.pfnTrayIconSetBaseInfo( cli.pfnGetIconFromStatusMode( NULL, szProto, szProto ? CallProtoService(szProto, PS_GETSTATUS, 0, 0) : CallService(MS_CLIST_GETSTATUSMODE, 0, 0)), szProto ); DBFreeVariant(&dbv); break; } case SETTING_TRAYICON_CYCLE: cli.cycleTimerId = SetTimer(NULL, 0, DBGetContactSettingWord(NULL, "CList", "CycleTime", SETTING_CYCLETIME_DEFAULT) * 1000, cli.pfnTrayCycleTimerProc); changed = cli.pfnTrayIconSetBaseInfo(ImageList_GetIcon (hCListImages, cli.pfnIconFromStatusMode(szChangedProto, CallProtoService(szChangedProto, PS_GETSTATUS, 0, 0), NULL), ILD_NORMAL), NULL); break; case SETTING_TRAYICON_MULTI: if (!cli.trayIcon) { cli.pfnTrayIconRemove(NULL, NULL); } else if ((cli.trayIconCount > 1 || netProtoCount == 1) || DBGetContactSettingByte( NULL, "CList", "AlwaysMulti", SETTING_ALWAYSMULTI_DEFAULT )) changed = cli.pfnTrayIconSetBaseInfo( cli.pfnGetIconFromStatusMode( NULL, szChangedProto, CallProtoService(szChangedProto, PS_GETSTATUS, 0, 0)), (char*)szChangedProto ); else { cli.pfnTrayIconDestroy(hwnd); cli.pfnTrayIconInit(hwnd); } break; } } } else changed = cli.pfnTrayIconSetBaseInfo(ImageList_GetIcon(hCListImages, cli.pfnIconFromStatusMode(NULL, averageMode, NULL), ILD_NORMAL), NULL); if (changed != -1 && cli.trayIcon[changed].isBase) cli.pfnTrayIconUpdate(cli.trayIcon[changed].hBaseIcon, NULL, cli.trayIcon[changed].szProto, 1); ulock; } void fnTrayIconSetToBase(char *szPreferredProto) { int i; initcheck; lock; for (i = 0; i < cli.trayIconCount; i++) { if ( cli.trayIcon[i].id == 0 ) continue; if ( lstrcmpA( cli.trayIcon[i].szProto, szPreferredProto )) continue; cli.pfnTrayIconUpdate( cli.trayIcon[i].hBaseIcon, NULL, szPreferredProto, 1); ulock; return; } //if there wasn't a specific icon, there will only be one suitable for ( i = 0; i < cli.trayIconCount; i++) { if ( cli.trayIcon[i].id == 0 ) continue; cli.pfnTrayIconUpdate( cli.trayIcon[i].hBaseIcon, NULL, szPreferredProto, 1); ulock; return; } ulock; return; } void fnTrayIconIconsChanged(void) { initcheck; lock; cli.pfnTrayIconDestroy(cli.hwndContactList); cli.pfnTrayIconInit(cli.hwndContactList); ulock; } static UINT_PTR autoHideTimerId; static VOID CALLBACK TrayIconAutoHideTimer(HWND hwnd, UINT, UINT_PTR idEvent, DWORD) { HWND hwndClui; initcheck; lock; KillTimer(hwnd, idEvent); hwndClui = cli.hwndContactList; if (GetActiveWindow() != hwndClui) { ShowWindow(hwndClui, SW_HIDE); if (MySetProcessWorkingSetSize != NULL) MySetProcessWorkingSetSize(GetCurrentProcess(), -1, -1); } ulock; return; } int fnTrayIconPauseAutoHide(WPARAM, LPARAM) { initcheck 0; lock; if (DBGetContactSettingByte(NULL, "CList", "AutoHide", SETTING_AUTOHIDE_DEFAULT)) { if ( GetActiveWindow() != cli.hwndContactList ) { KillTimer(NULL, autoHideTimerId); autoHideTimerId = SetTimer(NULL, 0, 1000 * DBGetContactSettingWord(NULL, "CList", "HideTime", SETTING_HIDETIME_DEFAULT), TrayIconAutoHideTimer); } } ulock; return 0; } ///////////////////////////////////////////////////////////////////////////////////////// // processes tray icon's messages static BYTE s_LastHoverIconID = 0; static BOOL g_trayTooltipActive = FALSE; static POINT tray_hover_pos = {0}; static void CALLBACK TrayHideToolTipTimerProc(HWND hwnd, UINT, UINT_PTR, DWORD) { if ( g_trayTooltipActive ) { POINT pt; GetCursorPos(&pt); if ( abs(pt.x - tray_hover_pos.x) > TOOLTIP_TOLERANCE || abs(pt.y - tray_hover_pos.y) > TOOLTIP_TOLERANCE ) { CallService("mToolTip/HideTip", 0, 0); g_trayTooltipActive = FALSE; KillTimer( hwnd, TIMERID_TRAYHOVER_2 ); } } else KillTimer( hwnd, TIMERID_TRAYHOVER_2 ); } static void CALLBACK TrayToolTipTimerProc(HWND hwnd, UINT, UINT_PTR id, DWORD) { if ( !g_trayTooltipActive && !cli.bTrayMenuOnScreen ) { CLCINFOTIP ti = {0}; POINT pt; GetCursorPos( &pt ); if ( abs(pt.x - tray_hover_pos.x) <= TOOLTIP_TOLERANCE && abs(pt.y - tray_hover_pos.y) <= TOOLTIP_TOLERANCE ) { TCHAR* szTipCur = cli.szTip; { int n = s_LastHoverIconID-100; if ( n >= 0 && n < cli.trayIconCount ) szTipCur = cli.trayIcon[n].ptszToolTip; } ti.rcItem.left = pt.x - 10; ti.rcItem.right = pt.x + 10; ti.rcItem.top = pt.y - 10; ti.rcItem.bottom = pt.y + 10; ti.cbSize = sizeof( ti ); ti.isTreeFocused = GetFocus() == cli.hwndContactList ? 1 : 0; #if defined( _UNICODE ) if (CallService( "mToolTip/ShowTipW", (WPARAM)szTipCur, (LPARAM)&ti ) == CALLSERVICE_NOTFOUND) { char* p = mir_u2a( szTipCur ); CallService( "mToolTip/ShowTip", (WPARAM)p, (LPARAM)&ti ); mir_free( p ); } #else CallService( "mToolTip/ShowTip", (WPARAM)szTipCur, (LPARAM)&ti ); #endif GetCursorPos( &tray_hover_pos ); SetTimer( cli.hwndContactList, TIMERID_TRAYHOVER_2, 600, TrayHideToolTipTimerProc ); g_trayTooltipActive = TRUE; } } KillTimer(hwnd, id); } INT_PTR fnTrayIconProcessMessage(WPARAM wParam, LPARAM lParam) { MSG *msg = (MSG *) wParam; switch (msg->message) { case WM_CREATE: { WM_TASKBARCREATED = RegisterWindowMessage( _T("TaskbarCreated")); WM_TASKBARBUTTONCREATED = RegisterWindowMessage( _T("TaskbarButtonCreated")); PostMessage(msg->hwnd, TIM_CREATE, 0, 0); break; } case TIM_CREATE: cli.pfnTrayIconInit(msg->hwnd); break; case WM_ACTIVATE: if (DBGetContactSettingByte(NULL, "CList", "AutoHide", SETTING_AUTOHIDE_DEFAULT)) { if (LOWORD(msg->wParam) == WA_INACTIVE) autoHideTimerId = SetTimer(NULL, 0, 1000 * DBGetContactSettingWord(NULL, "CList", "HideTime", SETTING_HIDETIME_DEFAULT), TrayIconAutoHideTimer); else KillTimer(NULL, autoHideTimerId); } break; case WM_DESTROY: cli.pfnTrayIconDestroy(msg->hwnd); cli.pfnUninitTray(); break; case TIM_CALLBACK: if ( msg->lParam == WM_RBUTTONDOWN || msg->lParam == WM_LBUTTONDOWN || msg->lParam == WM_RBUTTONDOWN && g_trayTooltipActive ) { CallService("mToolTip/HideTip", 0, 0); g_trayTooltipActive = FALSE; } if ( msg->lParam == WM_MBUTTONUP ) cli.pfnShowHide(0, 0); else if (msg->lParam == (DBGetContactSettingByte(NULL, "CList", "Tray1Click", SETTING_TRAY1CLICK_DEFAULT) ? WM_LBUTTONUP : WM_LBUTTONDBLCLK)) { if ((GetAsyncKeyState(VK_CONTROL) & 0x8000)) { POINT pt; HMENU hMenu = (HMENU)CallService(MS_CLIST_MENUGETSTATUS, 0, 0); for (int i = 0; i < cli.trayIconCount; ++i) { if ((unsigned)cli.trayIcon[i].id == msg->wParam) { if (!cli.trayIcon[i].szProto) break; int ind = 0; for (int j = 0; j < accounts.getCount(); ++j) { int k = cli.pfnGetAccountIndexByPos(j); if (k >= 0) { if (!strcmp(cli.trayIcon[i].szProto, accounts[k]->szModuleName)) { HMENU hm = GetSubMenu(hMenu, ind); if (hm) hMenu = hm; break; } if (cli.pfnGetProtocolVisibility(accounts[k]->szModuleName)) ++ind; } } break; } } SetForegroundWindow(msg->hwnd); SetFocus(msg->hwnd); GetCursorPos(&pt); TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x, pt.y, 0, msg->hwnd, NULL); } else if (cli.pfnEventsProcessTrayDoubleClick(msg->wParam)) cli.pfnShowHide(0, 0); } else if (msg->lParam == WM_RBUTTONUP) { MENUITEMINFO mi; POINT pt; HMENU hMainMenu = LoadMenu(cli.hInst, MAKEINTRESOURCE(IDR_CONTEXT)); HMENU hMenu = GetSubMenu(hMainMenu, 0); CallService(MS_LANGPACK_TRANSLATEMENU, (WPARAM) hMenu, 0); ZeroMemory(&mi, sizeof(mi)); mi.cbSize = MENUITEMINFO_V4_SIZE; mi.fMask = MIIM_SUBMENU | MIIM_TYPE; mi.fType = MFT_STRING; mi.hSubMenu = (HMENU) CallService(MS_CLIST_MENUGETMAIN, 0, 0); mi.dwTypeData = TranslateT("&Main Menu"); InsertMenuItem(hMenu, 1, TRUE, &mi); mi.hSubMenu = (HMENU) CallService(MS_CLIST_MENUGETSTATUS, 0, 0); mi.dwTypeData = TranslateT("&Status"); InsertMenuItem(hMenu, 2, TRUE, &mi); SetMenuDefaultItem(hMenu, ID_TRAY_HIDE, FALSE); SetForegroundWindow(msg->hwnd); SetFocus(msg->hwnd); GetCursorPos(&pt); TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_LEFTALIGN, pt.x, pt.y, 0, msg->hwnd, NULL); RemoveMenu(hMenu, 1, MF_BYPOSITION); RemoveMenu(hMenu, 1, MF_BYPOSITION); DestroyMenu(hMainMenu); } else if ( msg->lParam == WM_MOUSEMOVE ) { s_LastHoverIconID = msg->wParam; if ( g_trayTooltipActive ) { POINT pt; GetCursorPos( &pt ); if ( abs(pt.x - tray_hover_pos.x) > TOOLTIP_TOLERANCE || abs(pt.y - tray_hover_pos.y) > TOOLTIP_TOLERANCE ) { CallService("mToolTip/HideTip", 0, 0); g_trayTooltipActive = FALSE; ReleaseCapture(); } } else { GetCursorPos(&tray_hover_pos); SetTimer(cli.hwndContactList, TIMERID_TRAYHOVER, 600, TrayToolTipTimerProc); } break; } *((LRESULT *) lParam) = 0; return TRUE; default: if (msg->message == WM_TASKBARCREATED) { cli.pfnTrayIconTaskbarCreated(msg->hwnd); *((LRESULT *) lParam) = 0; return TRUE; } else if (msg->message == WM_TASKBARBUTTONCREATED) { SetTaskBarIcon(lastTaskBarIcon, NULL); *((LRESULT *) lParam) = 0; return TRUE; } } return FALSE; } ///////////////////////////////////////////////////////////////////////////////////////// // processes tray icon's notifications int fnCListTrayNotify( MIRANDASYSTRAYNOTIFY* msn ) { UINT iconId = 0; if ( msn == NULL ) return 1; if ( msn->cbSize != sizeof(MIRANDASYSTRAYNOTIFY) || msn->szInfo == NULL || msn->szInfoTitle == NULL ) return 1; if ( cli.trayIcon == NULL ) return 2; if ( msn->szProto ) { int j; for ( j = 0; j < cli.trayIconCount; j++ ) { if ( cli.trayIcon[j].szProto != NULL ) { if ( !strcmp( msn->szProto, cli.trayIcon[j].szProto )) { iconId = cli.trayIcon[j].id; break; } } else if ( cli.trayIcon[j].isBase ) { iconId = cli.trayIcon[j].id; break; } } } else iconId = cli.trayIcon[0].id; #if defined(_UNICODE) if ( msn->dwInfoFlags & NIIF_INTERN_UNICODE ) { NOTIFYICONDATAW nid = {0}; nid.cbSize = ( cli.shellVersion >= 5 ) ? sizeof(nid) : NOTIFYICONDATAW_V1_SIZE; nid.hWnd = cli.hwndContactList; nid.uID = iconId; nid.uFlags = NIF_INFO; lstrcpynW( nid.szInfo, msn->tszInfo, SIZEOF( nid.szInfo )); lstrcpynW( nid.szInfoTitle, msn->tszInfoTitle, SIZEOF( nid.szInfoTitle )); nid.szInfo[ SIZEOF(nid.szInfo)-1 ] = 0; nid.szInfoTitle[ SIZEOF(nid.szInfoTitle)-1 ] = 0; nid.uTimeout = msn->uTimeout; nid.dwInfoFlags = (msn->dwInfoFlags & ~NIIF_INTERN_UNICODE); return Shell_NotifyIconW( NIM_MODIFY, &nid ) == 0; } else #endif { NOTIFYICONDATAA nid = { 0 }; nid.cbSize = ( cli.shellVersion >= 5 ) ? sizeof(nid) : NOTIFYICONDATAA_V1_SIZE; nid.hWnd = cli.hwndContactList; nid.uID = iconId; nid.uFlags = NIF_INFO; lstrcpynA( nid.szInfo, msn->szInfo, sizeof( nid.szInfo )); lstrcpynA( nid.szInfoTitle, msn->szInfoTitle, sizeof( nid.szInfoTitle )); nid.uTimeout = msn->uTimeout; nid.dwInfoFlags = msn->dwInfoFlags; return Shell_NotifyIconA( NIM_MODIFY, &nid ) == 0; } } ///////////////////////////////////////////////////////////////////////////////////////// typedef struct _DllVersionInfo { DWORD cbSize; DWORD dwMajorVersion; // Major version DWORD dwMinorVersion; // Minor version DWORD dwBuildNumber; // Build number DWORD dwPlatformID; // DLLVER_PLATFORM_* } DLLVERSIONINFO; typedef HRESULT(CALLBACK * DLLGETVERSIONPROC) (DLLVERSIONINFO *); static DLLVERSIONINFO dviShell; static INT_PTR pfnCListTrayNotifyStub(WPARAM, LPARAM lParam ) { return cli.pfnCListTrayNotify(( MIRANDASYSTRAYNOTIFY* )lParam ); } void fnInitTray( void ) { HMODULE hLib = GetModuleHandleA("shell32"); if ( hLib ) { DLLGETVERSIONPROC proc; dviShell.cbSize = sizeof(dviShell); proc = ( DLLGETVERSIONPROC )GetProcAddress( hLib, "DllGetVersion" ); if (proc) { proc( &dviShell ); cli.shellVersion = dviShell.dwMajorVersion; } FreeLibrary(hLib); } InitializeCriticalSection(&trayLockCS); if ( cli.shellVersion >= 5 ) CreateServiceFunction(MS_CLIST_SYSTRAY_NOTIFY, pfnCListTrayNotifyStub ); fTrayInited=TRUE; } void fnUninitTray( void ) { fTrayInited=FALSE; DeleteCriticalSection( &trayLockCS ); } void fnLockTray( void ) { // return; //stub to be removed initcheck; EnterCriticalSection( &trayLockCS ); } void fnUnlockTray( void ) { // return; //stub to be removed initcheck; #ifdef _DEBUG if (trayLockCS.RecursionCount == 0) DebugBreak(); //try to unlock already #endif LeaveCriticalSection( &trayLockCS ); } #undef lock #undef ulock #undef initcheck