Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

810 lines
26 KiB

  1. #include "cabinet.h"
  2. #include "traycmn.h"
  3. #include "trayreg.h"
  4. #include "trayitem.h"
  5. #include "shellapi.h"
  6. #include "util.h"
  7. BOOL CTrayItemRegistry::_DestroyIconInfoCB(TNPersistStreamData * pData, LPVOID pData2)
  8. {
  9. delete [] pData;
  10. return TRUE;
  11. }
  12. void CTrayItemRegistry::_QueryRegValue(HKEY hkey, LPTSTR pszValue, ULONG* puVal, ULONG uDefault)
  13. {
  14. if (hkey)
  15. {
  16. DWORD dwSize = sizeof(*puVal);
  17. if (ERROR_SUCCESS != RegQueryValueEx(hkey, pszValue, NULL, NULL, (LPBYTE) puVal, &dwSize))
  18. *puVal = uDefault;
  19. }
  20. }
  21. void CTrayItemRegistry::IncChevronInfoTipShownInRegistry(BOOL bUserClickedInfoTip/*=FALSE*/)
  22. {
  23. HKEY hKey = NULL;
  24. if (_bShowChevronInfoTip)
  25. {
  26. if (bUserClickedInfoTip)
  27. {
  28. // If the user has clicked the info tip, do not show it in subsequent
  29. // sessions...
  30. _dwTimesChevronInfoTipShown = MAX_CHEVRON_INFOTIP_SHOWN;
  31. }
  32. else
  33. {
  34. _dwTimesChevronInfoTipShown ++;
  35. }
  36. if ( (_dwTimesChevronInfoTipShown <= MAX_CHEVRON_INFOTIP_SHOWN) &&
  37. (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, SZ_TRAYNOTIFY_REGKEY, 0, KEY_WRITE, &hKey)) )
  38. {
  39. RegSetValueEx(hKey, SZ_INFOTIP_REGVALUE, 0, REG_DWORD,
  40. (LPBYTE) &_dwTimesChevronInfoTipShown, sizeof(DWORD));
  41. RegCloseKey(hKey);
  42. }
  43. }
  44. // The chevron infotip can be shown only once per session...
  45. _bShowChevronInfoTip = FALSE;
  46. }
  47. void CTrayItemRegistry::InitRegistryValues(UINT uIconListFlags)
  48. {
  49. HKEY hkeyTrayNotify = NULL;
  50. RegOpenKeyEx(HKEY_CURRENT_USER, SZ_TRAYNOTIFY_REGKEY, 0, KEY_QUERY_VALUE, &hkeyTrayNotify);
  51. // Load the countdown interval for the items when added to the tray
  52. _QueryRegValue(hkeyTrayNotify, SZ_ICON_COUNTDOWN_VALUE, &_uPrimaryCountdown, TT_ICON_COUNTDOWN_INTERVAL);
  53. // The length of time for which an item can reside in the past items tray, from
  54. // when it was last used...
  55. _QueryRegValue(hkeyTrayNotify, SZ_ICONCLEANUP_VALUE, &_uValidLastUseTimePeriod, TT_ICONCLEANUP_INTERVAL);
  56. // The number of times the chevron info tip has been shown...
  57. // - assume that it hasnt been shown before...
  58. _QueryRegValue(hkeyTrayNotify, SZ_INFOTIP_REGVALUE, &_dwTimesChevronInfoTipShown, 0);
  59. if (_dwTimesChevronInfoTipShown < MAX_CHEVRON_INFOTIP_SHOWN)
  60. _bShowChevronInfoTip = TRUE;
  61. else
  62. _bShowChevronInfoTip = FALSE;
  63. // The ticking interval for the internal timers for CUserEventTimer, when the
  64. // CUserEventTimer counts the time for which the item is resident in the tray
  65. _QueryRegValue(hkeyTrayNotify, SZ_ICONDEMOTE_TIMER_TICK_VALUE, &_uIconDemoteTimerTickInterval,
  66. UET_ICONDEMOTE_TIMER_TICK_INTERVAL);
  67. // The ticking interval for the internal timers for CUserEventTimer, when the
  68. // CUserEventTimer counts the time for which the balloon tips show on an item in
  69. // the tray
  70. _QueryRegValue(hkeyTrayNotify, SZ_INFOTIP_TIMER_TICK_VALUE, &_uInfoTipTimerTickInterval,
  71. UET_INFOTIP_TIMER_TICK_INTERVAL);
  72. RegCloseKey(hkeyTrayNotify);
  73. // Is the automatic tray (the new Whistler tray feature) enabled ?
  74. _fNoAutoTrayPolicyEnabled = (SHRestricted(REST_NOAUTOTRAYNOTIFY) != 0);
  75. _fAutoTrayEnabledByUser = _IsAutoTrayEnabledInRegistry();
  76. // Load the icon info from the previous session...
  77. InitTrayItemStream(STGM_READ, NULL, NULL);
  78. if (!_himlPastItemsIconList)
  79. {
  80. _himlPastItemsIconList = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
  81. uIconListFlags, 0, 1);
  82. }
  83. }
  84. UINT CTrayItemRegistry::GetTimerTickInterval(int nTimerFlag)
  85. {
  86. switch(nTimerFlag)
  87. {
  88. case TF_ICONDEMOTE_TIMER:
  89. return (UINT) _uIconDemoteTimerTickInterval;
  90. case TF_INFOTIP_TIMER:
  91. return (UINT) _uInfoTipTimerTickInterval;
  92. }
  93. ASSERT(FALSE);
  94. return 0;
  95. }
  96. void CTrayItemRegistry::InitTrayItemStream(DWORD dwStreamMode,
  97. PFNTRAYNOTIFYCALLBACK pfnTrayNotifyCB, void *pCBData)
  98. {
  99. BOOL bDeleteIconStreamRegValue = FALSE;
  100. IStream * pStream = SHOpenRegStream( HKEY_CURRENT_USER,
  101. SZ_TRAYNOTIFY_REGKEY,
  102. SZ_ITEMSTREAMS_REGVALUE,
  103. dwStreamMode );
  104. if (pStream && SUCCEEDED(IStream_Reset(pStream)))
  105. {
  106. if (dwStreamMode == STGM_READ)
  107. {
  108. _LoadTrayItemStream(pStream, pfnTrayNotifyCB, pCBData);
  109. }
  110. else
  111. {
  112. ASSERT(dwStreamMode == STGM_WRITE);
  113. if (FAILED(_SaveTrayItemStream(pStream, pfnTrayNotifyCB, pCBData)))
  114. bDeleteIconStreamRegValue = TRUE;
  115. }
  116. pStream->Release();
  117. }
  118. if (bDeleteIconStreamRegValue)
  119. {
  120. HKEY hKey;
  121. if (RegOpenKeyEx(HKEY_CURRENT_USER, SZ_TRAYNOTIFY_REGKEY, 0, KEY_WRITE, &hKey) == ERROR_SUCCESS)
  122. {
  123. ASSERT(hKey);
  124. RegDeleteValue(hKey, SZ_ITEMSTREAMS_REGVALUE);
  125. RegCloseKey(hKey);
  126. }
  127. }
  128. }
  129. BOOL CTrayItemRegistry::_LoadIconsFromRegStream(DWORD dwItemStreamSignature)
  130. {
  131. ASSERT(_himlPastItemsIconList == NULL);
  132. IStream * pStream = SHOpenRegStream( HKEY_CURRENT_USER,
  133. SZ_TRAYNOTIFY_REGKEY,
  134. SZ_ICONSTREAMS_REGVALUE,
  135. STGM_READ );
  136. if (pStream)
  137. {
  138. TNPersistentIconStreamHeader tnPISH = {0};
  139. if (SUCCEEDED(IStream_Read(pStream, &tnPISH, sizeof(TNPersistentIconStreamHeader))))
  140. {
  141. if ( (tnPISH.dwSize == sizeof(TNPersistentIconStreamHeader)) &&
  142. (_IsValidStreamHeaderVersion(tnPISH.dwVersion)) &&
  143. (tnPISH.dwSignature == dwItemStreamSignature) &&
  144. (tnPISH.cIcons > 0) )
  145. {
  146. LARGE_INTEGER c_li0 = { 0, 0 };
  147. c_li0.LowPart = tnPISH.dwOffset;
  148. if (S_OK == pStream->Seek(c_li0, STREAM_SEEK_SET, NULL))
  149. {
  150. _himlPastItemsIconList = ImageList_Read(pStream);
  151. }
  152. }
  153. }
  154. pStream->Release();
  155. }
  156. return (_himlPastItemsIconList != NULL);
  157. }
  158. HRESULT CTrayItemRegistry::_LoadTrayItemStream(IStream *pstm, PFNTRAYNOTIFYCALLBACK pfnTrayNotifyCB,
  159. void *pCBData)
  160. {
  161. HRESULT hr;
  162. TNPersistStreamHeader persStmHeader = {0};
  163. ASSERT(pstm);
  164. hr = IStream_Read(pstm, &persStmHeader, sizeof(persStmHeader));
  165. if (SUCCEEDED(hr))
  166. {
  167. if ( (persStmHeader.dwSize != sizeof(TNPersistStreamHeader)) ||
  168. (!_IsValidStreamHeaderVersion(persStmHeader.dwVersion)) ||
  169. (persStmHeader.dwSignature != TNH_SIGNATURE) ||
  170. (persStmHeader.cIcons <= 0) )
  171. {
  172. return E_FAIL;
  173. }
  174. LARGE_INTEGER c_li0 = { 0, 0 };
  175. c_li0.LowPart = persStmHeader.dwOffset;
  176. if (S_OK == (hr = pstm->Seek(c_li0, STREAM_SEEK_SET, NULL)))
  177. {
  178. if (!_dpaPersistentItemInfo)
  179. {
  180. if (!_dpaPersistentItemInfo.Create(10))
  181. return E_FAIL;
  182. }
  183. ASSERT( (persStmHeader.dwVersion != TNH_VERSION_ONE) &&
  184. (persStmHeader.dwVersion != TNH_VERSION_TWO) &&
  185. (persStmHeader.dwVersion != TNH_VERSION_THREE) );
  186. for (int i = 0; i < (int)(persStmHeader.cIcons); ++i)
  187. {
  188. TNPersistStreamData * ptnpd = new TNPersistStreamData;
  189. if (ptnpd)
  190. {
  191. hr = IStream_Read(pstm, ptnpd, _SizeOfPersistStreamData(persStmHeader.dwVersion));
  192. if (SUCCEEDED(hr))
  193. {
  194. if (persStmHeader.dwVersion == TNH_VERSION_FOUR)
  195. {
  196. ptnpd->guidItem = GUID_NULL;
  197. }
  198. }
  199. if (FAILED(hr) || (_dpaPersistentItemInfo.AppendPtr(ptnpd) == -1))
  200. {
  201. delete (ptnpd);
  202. _dpaPersistentItemInfo.DestroyCallback(_DestroyIconInfoCB, NULL);
  203. _DestroyPastItemsIconList();
  204. hr = E_FAIL;
  205. break;
  206. }
  207. }
  208. else
  209. {
  210. _dpaPersistentItemInfo.DestroyCallback(_DestroyIconInfoCB, NULL);
  211. _DestroyPastItemsIconList();
  212. hr = E_OUTOFMEMORY;
  213. break;
  214. }
  215. }
  216. if (SUCCEEDED(hr))
  217. {
  218. if (!_LoadIconsFromRegStream(persStmHeader.dwSignature))
  219. {
  220. if (_dpaPersistentItemInfo)
  221. {
  222. for (int i = _dpaPersistentItemInfo.GetPtrCount()-1; i >= 0; i--)
  223. {
  224. (_dpaPersistentItemInfo.GetPtr(i))->nImageIndex = INVALID_IMAGE_INDEX;
  225. }
  226. }
  227. }
  228. }
  229. }
  230. }
  231. return hr;
  232. }
  233. // TO DO : 1. Maybe we can avoid 2 stream writes of the header, maybe a seek will work directly
  234. // 2. If failed, dont store anything, esp. avoid 2 writes
  235. HRESULT CTrayItemRegistry::_SaveTrayItemStream(IStream *pstm, PFNTRAYNOTIFYCALLBACK pfnTrayNotifyCB,
  236. void *pCBData)
  237. {
  238. HRESULT hr;
  239. DWORD nWrittenIcons = 0;
  240. TNPersistStreamHeader persStmHeader;
  241. persStmHeader.dwSize = sizeof(TNPersistStreamHeader);
  242. persStmHeader.dwVersion = TNH_VERSION_FIVE;
  243. persStmHeader.dwSignature = TNH_SIGNATURE;
  244. // The Bang Icon(s) dont count...
  245. persStmHeader.cIcons = 0;
  246. persStmHeader.dwOffset = persStmHeader.dwSize;
  247. hr = IStream_Write(pstm, &persStmHeader, sizeof(persStmHeader));
  248. if (SUCCEEDED(hr))
  249. {
  250. // Write the icons in the current session...
  251. // Since the icons are added to the front of the tray toolbar, the icons
  252. // in the front of the tray are the ones that have been added last. So
  253. // maintain this order in writing the icon data into the stream.
  254. INT_PTR i = 0;
  255. CTrayItem * pti = NULL;
  256. HICON hIcon = NULL;
  257. do
  258. {
  259. TRAYCBRET trayCBRet = {0};
  260. pti = NULL;
  261. hIcon = NULL;
  262. if (pfnTrayNotifyCB(i, pCBData, TRAYCBARG_ALL, &trayCBRet))
  263. {
  264. pti = trayCBRet.pti;
  265. hIcon = trayCBRet.hIcon;
  266. if (pti && pti->ShouldSaveIcon())
  267. {
  268. int nPastSessionIndex = DoesIconExistFromPreviousSession(pti,
  269. pti->szIconText, hIcon);
  270. if (nPastSessionIndex != -1)
  271. {
  272. DeletePastItem(nPastSessionIndex);
  273. }
  274. TNPersistStreamData tnPersistData = {0};
  275. if (_FillPersistData(&tnPersistData, pti, trayCBRet.hIcon))
  276. {
  277. if (SUCCEEDED(hr = IStream_Write(pstm, &tnPersistData, sizeof(tnPersistData))))
  278. {
  279. nWrittenIcons ++;
  280. }
  281. else
  282. {
  283. // If we failed to store the item, then remove its corresponding icon
  284. // from the icon image list...
  285. // Since this icon was appended to the end of the list, and we remove it
  286. // we dont need to update all the other item image indices, as they will
  287. // not be affected...
  288. ImageList_Remove(_himlPastItemsIconList, (INT) i);
  289. }
  290. }
  291. }
  292. if (hIcon)
  293. DestroyIcon(hIcon);
  294. }
  295. else
  296. {
  297. break;
  298. }
  299. i++;
  300. }
  301. while (TRUE);
  302. // Write out the icons from the previous sessions..
  303. if (_dpaPersistentItemInfo)
  304. {
  305. INT_PTR nIcons = _dpaPersistentItemInfo.GetPtrCount();
  306. for (i = 0; i < nIcons; i++)
  307. {
  308. TNPersistStreamData * ptnpd = _dpaPersistentItemInfo.GetPtr(i);
  309. ASSERT(ptnpd);
  310. BOOL bWritten = FALSE;
  311. if (_IsIconLastUseValid(ptnpd->wYear, ptnpd->wMonth))
  312. {
  313. if (SUCCEEDED(hr = IStream_Write(pstm, ptnpd, sizeof(TNPersistStreamData))))
  314. {
  315. nWrittenIcons++;
  316. bWritten = TRUE;
  317. }
  318. }
  319. if (!bWritten)
  320. {
  321. if (ImageList_Remove(_himlPastItemsIconList, (INT) i))
  322. UpdateImageIndices(i);
  323. }
  324. }
  325. }
  326. }
  327. if (nWrittenIcons <= 0)
  328. return E_FAIL;
  329. else
  330. {
  331. _SaveIconsToRegStream();
  332. }
  333. if (FAILED(hr) || nWrittenIcons > 0)
  334. {
  335. persStmHeader.cIcons = nWrittenIcons;
  336. if (SUCCEEDED(hr = IStream_Reset(pstm)))
  337. hr = IStream_Write(pstm, &persStmHeader, sizeof(persStmHeader));
  338. }
  339. return hr;
  340. }
  341. void CTrayItemRegistry::UpdateImageIndices(INT_PTR nDeletedImageIndex)
  342. {
  343. if (!_dpaPersistentItemInfo)
  344. return;
  345. INT_PTR nPastItemCount = _dpaPersistentItemInfo.GetPtrCount();
  346. for (INT_PTR i = 0; i < nPastItemCount; i++)
  347. {
  348. TNPersistStreamData * ptnpd = _dpaPersistentItemInfo.GetPtr(i);
  349. if (ptnpd)
  350. {
  351. if (ptnpd->nImageIndex > nDeletedImageIndex)
  352. {
  353. ptnpd->nImageIndex --;
  354. }
  355. else if (ptnpd->nImageIndex == nDeletedImageIndex)
  356. {
  357. ptnpd->nImageIndex = INVALID_IMAGE_INDEX;
  358. }
  359. }
  360. }
  361. }
  362. BOOL CTrayItemRegistry::_SaveIconsToRegStream()
  363. {
  364. BOOL bStreamWritten = FALSE;
  365. if (_himlPastItemsIconList)
  366. {
  367. IStream * pStream = SHOpenRegStream( HKEY_CURRENT_USER,
  368. SZ_TRAYNOTIFY_REGKEY,
  369. SZ_ICONSTREAMS_REGVALUE,
  370. STGM_WRITE );
  371. if (pStream)
  372. {
  373. TNPersistentIconStreamHeader tnPISH = {0};
  374. tnPISH.dwSize = sizeof(TNPersistentIconStreamHeader);
  375. tnPISH.dwVersion = TNH_VERSION_FOUR;
  376. tnPISH.dwSignature = TNH_SIGNATURE;
  377. tnPISH.cIcons = ImageList_GetImageCount(_himlPastItemsIconList);
  378. tnPISH.dwOffset = tnPISH.dwSize;
  379. if (tnPISH.cIcons > 0)
  380. {
  381. if (SUCCEEDED(IStream_Write(pStream, &tnPISH, sizeof(TNPersistentIconStreamHeader))))
  382. {
  383. if (ImageList_Write(_himlPastItemsIconList, pStream))
  384. {
  385. bStreamWritten = TRUE;
  386. }
  387. }
  388. }
  389. pStream->Release();
  390. }
  391. }
  392. if (!bStreamWritten)
  393. {
  394. HKEY hKey;
  395. if (RegOpenKeyEx(HKEY_CURRENT_USER, SZ_TRAYNOTIFY_REGKEY, 0, KEY_WRITE, &hKey) == ERROR_SUCCESS)
  396. {
  397. ASSERT(hKey);
  398. RegDeleteValue(hKey, SZ_ICONSTREAMS_REGVALUE);
  399. RegCloseKey(hKey);
  400. }
  401. }
  402. return bStreamWritten;
  403. }
  404. BOOL CTrayItemRegistry::_IsIconLastUseValid(WORD wYear, WORD wMonth)
  405. {
  406. SYSTEMTIME currentTime = {0};
  407. GetLocalTime(&currentTime);
  408. ULONG nCount = 0;
  409. // wYear/wMonth CANNOT be greater than currentTime.wYear/currentTime.wMonth
  410. while (nCount < _uValidLastUseTimePeriod)
  411. {
  412. if (wYear == currentTime.wYear && wMonth == currentTime.wMonth)
  413. break;
  414. wMonth++;
  415. if (wMonth > 12)
  416. {
  417. wYear ++;
  418. wMonth = 1;
  419. }
  420. nCount++;
  421. }
  422. return (nCount < _uValidLastUseTimePeriod);
  423. }
  424. BOOL CTrayItemRegistry::_IsAutoTrayEnabledInRegistry()
  425. {
  426. return (SHRegGetBoolUSValue(SZ_EXPLORER_REGKEY, SZ_AUTOTRAY_REGVALUE, FALSE, TRUE));
  427. }
  428. BOOL CTrayItemRegistry::SetIsAutoTrayEnabledInRegistry(BOOL bAutoTray)
  429. {
  430. HKEY hKey;
  431. ASSERT(!_fNoAutoTrayPolicyEnabled);
  432. if (_fAutoTrayEnabledByUser != bAutoTray)
  433. {
  434. _fAutoTrayEnabledByUser = bAutoTray;
  435. DWORD dwAutoTray = (bAutoTray ? 1 : 0);
  436. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, SZ_EXPLORER_REGKEY, 0, KEY_ALL_ACCESS, &hKey))
  437. {
  438. RegSetValueEx(hKey, SZ_AUTOTRAY_REGVALUE, 0, REG_DWORD, (LPBYTE) &dwAutoTray, sizeof(DWORD));
  439. RegCloseKey(hKey);
  440. }
  441. return TRUE;
  442. }
  443. return FALSE;
  444. }
  445. INT_PTR CTrayItemRegistry::CheckAndRestorePersistentIconSettings (
  446. CTrayItem * pti,
  447. LPTSTR pszIconToolTip,
  448. HICON hIcon
  449. )
  450. {
  451. // If we have icon information from the previous session..
  452. int i = -1;
  453. if (_dpaPersistentItemInfo)
  454. {
  455. i = DoesIconExistFromPreviousSession(pti, pszIconToolTip, hIcon);
  456. if (i != -1)
  457. {
  458. ASSERT(i >= 0 && i < _dpaPersistentItemInfo.GetPtrCount());
  459. TNPersistStreamData * ptnpd = _dpaPersistentItemInfo.GetPtr(i);
  460. ASSERT(ptnpd);
  461. _RestorePersistentIconSettings(ptnpd, pti);
  462. return i;
  463. }
  464. }
  465. return (-1);
  466. }
  467. //
  468. // Since we have already taken the previous-session info for this icon,
  469. // there is no need to hold it in our HDPA array...
  470. //
  471. void CTrayItemRegistry::DeletePastItem(INT_PTR nIndex)
  472. {
  473. if (nIndex != -1)
  474. {
  475. ASSERT((nIndex >= 0) && (nIndex < _dpaPersistentItemInfo.GetPtrCount()));
  476. TNPersistStreamData * ptnpd = _dpaPersistentItemInfo.GetPtr(nIndex);
  477. if (ptnpd)
  478. {
  479. if (_himlPastItemsIconList && (ptnpd->nImageIndex != INVALID_IMAGE_INDEX))
  480. {
  481. if (ImageList_Remove(_himlPastItemsIconList, ptnpd->nImageIndex))
  482. UpdateImageIndices(ptnpd->nImageIndex);
  483. }
  484. delete(ptnpd);
  485. }
  486. _dpaPersistentItemInfo.DeletePtr((int)nIndex);
  487. }
  488. }
  489. void CTrayItemRegistry::_RestorePersistentIconSettings(TNPersistStreamData * ptnpd, CTrayItem * pti)
  490. {
  491. ASSERT(ptnpd);
  492. pti->SetDemoted(ptnpd->bDemoted);
  493. pti->dwUserPref = ptnpd->dwUserPref;
  494. // if (NULL == lstrcpyn(pti->szExeName, ptnpd->szExeName, lstrlen(ptnpd->szExeName)+1))
  495. // pti->szExeName[0] = '\0';
  496. if (pti->IsStartupIcon())
  497. {
  498. if (ptnpd->bStartupIcon)
  499. {
  500. pti->uNumSeconds = ptnpd->uNumSeconds;
  501. #define MAX_NUM_SECONDS_VALUE TT_ICON_COUNTDOWN_INTERVAL/1000
  502. #define NUM_SECONDS_THRESHOLD 30
  503. if (ptnpd->uNumSeconds > MAX_NUM_SECONDS_VALUE - NUM_SECONDS_THRESHOLD)
  504. pti->uNumSeconds = MAX_NUM_SECONDS_VALUE - NUM_SECONDS_THRESHOLD;
  505. }
  506. else
  507. // If it wasnt a startup icon in the previous session, then dont take the acculumated time
  508. pti->uNumSeconds = 0;
  509. }
  510. // If it is not a startup icon, the accumulated time doesnt matter
  511. }
  512. int CTrayItemRegistry::DoesIconExistFromPreviousSession (
  513. CTrayItem * pti,
  514. LPTSTR pszIconToolTip,
  515. HICON hIcon
  516. )
  517. {
  518. ASSERT(pti);
  519. if (!_dpaPersistentItemInfo)
  520. return -1;
  521. if (pti->szExeName)
  522. {
  523. for (int i = 0; i < _dpaPersistentItemInfo.GetPtrCount(); i++)
  524. {
  525. TNPersistStreamData * ptnpd = _dpaPersistentItemInfo.GetPtr(i);
  526. ASSERT(ptnpd);
  527. if (lstrcmpi(pti->szExeName, ptnpd->szExeName) == 0)
  528. {
  529. if (ptnpd->uID == pti->uID)
  530. return i;
  531. if (hIcon)
  532. {
  533. HICON hIconNew = DuplicateIcon(NULL, hIcon);
  534. HICON hIconOld = NULL;
  535. if (ptnpd->nImageIndex != INVALID_IMAGE_INDEX)
  536. hIconOld = ImageList_GetIcon(_himlPastItemsIconList, ptnpd->nImageIndex, ILD_NORMAL);
  537. BOOL bRet = FALSE;
  538. if (hIconNew && hIconOld)
  539. {
  540. bRet = SHAreIconsEqual(hIconNew, hIconOld);
  541. }
  542. if (hIconNew)
  543. DestroyIcon(hIconNew);
  544. if (hIconOld)
  545. DestroyIcon(hIconOld);
  546. if (bRet)
  547. return i;
  548. }
  549. // We need to check this case for animating icons. We do not know
  550. // which icon is showing at the moment the item was deleted from the
  551. // tray.
  552. // For instance, in the "Network Connections" item, any of 3
  553. // "animating" icons could be showing when the item was deleted from
  554. // the tray. In this case, the SHAreIconsEqual check (between the
  555. // Past icon and the current icon) will fail, still the icons
  556. // represent the same item.
  557. // There is *no* sure way to catch this case. Adding a tooltip check
  558. // would strengthen our check. If the two icons have the same tooltip
  559. // text (till the '\n'), then they will be eliminated.
  560. // Of course, if an exe placed two icons in the tray, and gave them
  561. // different IDs but the same tooltip, then one of them will be deemed
  562. // to be a dupe of the other. But an app shouldnt be placing two icons
  563. // on the tray if their tips are different.
  564. if (pszIconToolTip)
  565. {
  566. TCHAR szToolTip[MAX_PATH];
  567. LPTSTR szTemp = NULL;
  568. int nCharToCompare = lstrlen(pszIconToolTip);
  569. if ((szTemp = StrChr(pszIconToolTip, (TCHAR)'\n')) != NULL)
  570. {
  571. nCharToCompare = szTemp-pszIconToolTip;
  572. StrCpyN(szToolTip, pszIconToolTip, nCharToCompare+1);
  573. }
  574. if (StrCmpNI( ((szTemp == NULL) ? pszIconToolTip : szToolTip),
  575. ptnpd->szIconText, nCharToCompare ) == 0)
  576. return i;
  577. }
  578. }
  579. }
  580. }
  581. return -1;
  582. }
  583. // Returns TRUE to indicate the function succeeded, fails only if the index is invalid
  584. // *pbStat is set to TRUE if pni is filled in, FALSE if pni is not filled in. pni might
  585. // not be filled in, if the item specified by index doesnt meet specific criteria.
  586. BOOL CTrayItemRegistry::GetTrayItem(INT_PTR nIndex, CNotificationItem * pni, BOOL * pbStat)
  587. {
  588. if (!_dpaPersistentItemInfo || (nIndex < 0) || (nIndex >= _dpaPersistentItemInfo.GetPtrCount()))
  589. {
  590. *pbStat = FALSE;
  591. return FALSE;
  592. }
  593. TNPersistStreamData * ptnpd = _dpaPersistentItemInfo.GetPtr(nIndex);
  594. if (ptnpd && _IsIconLastUseValid(ptnpd->wYear, ptnpd->wMonth))
  595. {
  596. *pni = ptnpd; // C++ magic...
  597. if (ptnpd->nImageIndex != INVALID_IMAGE_INDEX)
  598. pni->hIcon = ImageList_GetIcon(_himlPastItemsIconList, ptnpd->nImageIndex, ILD_NORMAL);
  599. *pbStat = TRUE;
  600. return TRUE;
  601. }
  602. *pbStat = FALSE;
  603. return TRUE;
  604. }
  605. BOOL CTrayItemRegistry::SetPastItemPreference(LPNOTIFYITEM pNotifyItem)
  606. {
  607. if (_dpaPersistentItemInfo && pNotifyItem->pszExeName[0] != 0)
  608. {
  609. for (INT_PTR i = _dpaPersistentItemInfo.GetPtrCount()-1; i >= 0; --i)
  610. {
  611. TNPersistStreamData * ptnpd = _dpaPersistentItemInfo.GetPtr(i);
  612. if (ptnpd && ptnpd->uID == pNotifyItem->uID &&
  613. lstrcmpi(ptnpd->szExeName, pNotifyItem->pszExeName) == 0)
  614. {
  615. ptnpd->dwUserPref = pNotifyItem->dwUserPref;
  616. return TRUE;
  617. }
  618. }
  619. }
  620. return FALSE;
  621. }
  622. BOOL CTrayItemRegistry::AddToPastItems(CTrayItem * pti, HICON hIcon)
  623. {
  624. if (!_dpaPersistentItemInfo)
  625. _dpaPersistentItemInfo.Create(10);
  626. if (_dpaPersistentItemInfo)
  627. {
  628. TNPersistStreamData * ptnPersistData = new TNPersistStreamData;
  629. if (ptnPersistData)
  630. {
  631. if (_FillPersistData(ptnPersistData, pti, hIcon))
  632. {
  633. if (_dpaPersistentItemInfo.InsertPtr(0, ptnPersistData) != -1)
  634. {
  635. return TRUE;
  636. }
  637. }
  638. delete(ptnPersistData);
  639. }
  640. }
  641. return FALSE;
  642. }
  643. BOOL CTrayItemRegistry::_FillPersistData(TNPersistStreamData * ptnPersistData, CTrayItem * pti, HICON hIcon)
  644. {
  645. SYSTEMTIME currentTime = {0};
  646. GetLocalTime(&currentTime);
  647. if (NULL != lstrcpyn(ptnPersistData->szExeName, pti->szExeName, lstrlen(pti->szExeName)+1))
  648. {
  649. ptnPersistData->uID = pti->uID;
  650. ptnPersistData->bDemoted = pti->IsDemoted();
  651. ptnPersistData->dwUserPref = pti->dwUserPref;
  652. ptnPersistData->wYear = currentTime.wYear;
  653. ptnPersistData->wMonth = currentTime.wMonth;
  654. ptnPersistData->bStartupIcon = pti->IsStartupIcon();
  655. ptnPersistData->nImageIndex = _AddPastIcon(-1, hIcon);
  656. memcpy(&(ptnPersistData->guidItem), &(pti->guidItem), sizeof(pti->guidItem));
  657. lstrcpyn(ptnPersistData->szIconText, pti->szIconText, lstrlen(pti->szIconText) + 1);
  658. if (pti->IsStartupIcon())
  659. {
  660. ptnPersistData->uNumSeconds = pti->uNumSeconds;
  661. }
  662. return TRUE;
  663. }
  664. return FALSE;
  665. }
  666. UINT_PTR CTrayItemRegistry::_SizeOfPersistStreamData(DWORD dwVersion)
  667. {
  668. ASSERT((dwVersion == TNH_VERSION_FOUR) || (dwVersion == TNH_VERSION_FIVE));
  669. if (dwVersion == TNH_VERSION_FOUR)
  670. return FIELD_OFFSET(TNPersistStreamData, guidItem);
  671. return sizeof(TNPersistStreamData);
  672. }