Leaked source code of windows server 2003
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.

2072 lines
62 KiB

  1. #include "priv.h"
  2. #include <iehelpid.h>
  3. #include <pstore.h>
  4. #include "hlframe.h"
  5. #include "shldisp.h"
  6. #include "opsprof.h"
  7. #include "resource.h"
  8. #include <mluisupp.h>
  9. #include "htmlstr.h"
  10. #include "mypics.h"
  11. #include "mshtmcid.h"
  12. #include "util.h"
  13. #include "winuser.h"
  14. //////////////////////////////////////////////////////////////////////////////////
  15. //
  16. // filename: mypics.cpp
  17. //
  18. // description: implements the my pictures exposure hoverbar thingie
  19. //
  20. // notes: lots of stuff is stolen from iforms.cpp and iformsp.h
  21. //
  22. // history: 06.15.2000 created by t-jdavis
  23. //
  24. //////////////////////////////////////////////////////////////////////////////////
  25. extern HINSTANCE g_hinst;
  26. #define TF_MYPICS TF_CUSTOM2
  27. // we don't actually use all of these, but we COULD, you know, if we wanted too.
  28. CMyPicsEventSinkCallback::EventSinkEntry CMyPicsEventSinkCallback::EventsToSink[] =
  29. {
  30. { EVENT_MOUSEOVER, L"onmouseover", L"mouseover" },
  31. { EVENT_MOUSEOUT, L"onmouseout", L"mouseout" },
  32. { EVENT_SCROLL, L"onscroll", L"scroll" },
  33. { EVENT_RESIZE, L"onresize", L"resize" }
  34. };
  35. // image toolbar states
  36. enum
  37. {
  38. HOVERSTATE_HIDING = 0,
  39. HOVERSTATE_SHOWING,
  40. HOVERSTATE_LOCKED,
  41. HOVERSTATE_SCROLLING,
  42. HOVERSTATE_WAITINGTOSHOW
  43. };
  44. //
  45. // CMyPics
  46. //
  47. // set some stuff
  48. CMyPics::CMyPics()
  49. {
  50. TraceMsg(TF_MYPICS, "+CMyPics::CMyPics");
  51. m_Hwnd = NULL;
  52. m_hWndMyPicsToolBar = NULL;
  53. m_hWndHover = NULL;
  54. m_pEleCurr = NULL;
  55. m_pSink = NULL;
  56. m_bIsOffForSession = FALSE;
  57. m_cRef = 1;
  58. m_bGalleryMeta = TRUE;
  59. TraceMsg(TF_MYPICS, "-CMyPics::CMyPics");
  60. }
  61. // destroy whatever needs destroying....
  62. CMyPics::~CMyPics()
  63. {
  64. TraceMsg(TF_MYPICS, "+CMyPics::~CMyPics");
  65. DestroyHover();
  66. ATOMICRELEASE(m_pEleCurr);
  67. if (m_hWndMyPicsToolBar)
  68. DestroyWindow(m_hWndMyPicsToolBar);
  69. if (m_hWndHover)
  70. {
  71. SetWindowPtr(m_hWndHover, GWLP_USERDATA, NULL);
  72. DestroyWindow(m_hWndHover);
  73. }
  74. TraceMsg(TF_MYPICS, "-CMyPics::~CMyPics");
  75. }
  76. // did the user turn this feature off?
  77. BOOL CMyPics::IsOff()
  78. {
  79. return (m_bIsOffForSession);
  80. }
  81. void CMyPics::IsGalleryMeta(BOOL bFlag)
  82. {
  83. m_bGalleryMeta = bFlag;
  84. }
  85. HRESULT CMyPics::Init(IHTMLDocument2 *pDoc2)
  86. {
  87. HRESULT hr = S_OK;
  88. TraceMsg(TF_MYPICS, "+CMyPics::Init");
  89. ASSERT(pDoc2);
  90. //sink things
  91. IHTMLElement2 *pEle2 = NULL;
  92. IHTMLElementCollection *pCollect = NULL;
  93. IHTMLElementCollection *pSubCollect = NULL;
  94. IDispatch *pDisp = NULL;
  95. VARIANT TagName;
  96. ULONG ulCount = 0;
  97. VARIANTARG va1;
  98. VARIANTARG va2;
  99. IHTMLWindow3 *pWin3 = NULL;
  100. // ...remember this...
  101. m_pDoc2 = pDoc2;
  102. pDoc2->AddRef();
  103. // setup variant for finding all the IMG tags...
  104. TagName.vt = VT_BSTR;
  105. TagName.bstrVal = (BSTR)c_bstr_IMG;
  106. //get all tags
  107. hr = pDoc2->get_all(&pCollect);
  108. if (FAILED(hr))
  109. goto Cleanup;
  110. //get all IMG tags
  111. hr = pCollect->tags(TagName, &pDisp);
  112. if (FAILED(hr))
  113. goto Cleanup;
  114. if (pDisp)
  115. {
  116. hr = pDisp->QueryInterface(IID_IHTMLElementCollection,(void **)&pSubCollect);
  117. ATOMICRELEASE(pDisp);
  118. }
  119. if (FAILED(hr))
  120. goto Cleanup;
  121. //get IMG tag count
  122. hr = pSubCollect->get_length((LONG *)&ulCount);
  123. if (FAILED(hr))
  124. goto Cleanup;
  125. va1.vt = VT_I4;
  126. va2.vt = VT_EMPTY;
  127. //iterate through tags sinking events to elements
  128. for (int i=0; i<(LONG)ulCount; i++)
  129. {
  130. pDisp = NULL;
  131. va1.lVal = (LONG)i;
  132. pSubCollect->item(va1, va2, &pDisp);
  133. // only create a new CEventSink once
  134. if (!m_pSink && pDisp)
  135. m_pSink = new CEventSink(this);
  136. if (pDisp)
  137. {
  138. hr = pDisp->QueryInterface(IID_IHTMLElement2, (void **)&pEle2);
  139. if (FAILED(hr))
  140. goto Cleanup;
  141. ASSERT(m_pSink);
  142. if (m_pSink && pEle2)
  143. {
  144. EVENTS events[] = { EVENT_MOUSEOVER, EVENT_MOUSEOUT, EVENT_RESIZE };
  145. m_pSink->SinkEvents(pEle2, ARRAYSIZE(events), events);
  146. }
  147. ATOMICRELEASE(pEle2);
  148. ATOMICRELEASE(pDisp);
  149. }
  150. }
  151. // sink scroll event from the window, because it doesn't come from elements.
  152. if (m_pSink)
  153. {
  154. Win3FromDoc2(m_pDoc2, &pWin3);
  155. if (pWin3)
  156. {
  157. m_pWin3 = pWin3;
  158. m_pWin3->AddRef();
  159. EVENTS eventScroll[] = { EVENT_SCROLL };
  160. m_pSink->SinkEvents(pWin3, ARRAYSIZE(eventScroll), eventScroll);
  161. }
  162. }
  163. //end sinking things
  164. Cleanup:
  165. ATOMICRELEASE(pCollect);
  166. ATOMICRELEASE(pSubCollect);
  167. ATOMICRELEASE(pWin3);
  168. ATOMICRELEASE(pDisp);
  169. ATOMICRELEASE(pEle2);
  170. TraceMsg(TF_MYPICS, "-CMyPics::Init");
  171. return hr;
  172. }
  173. HRESULT CMyPics::UnInit()
  174. {
  175. // Unhook regular event sink
  176. TraceMsg(TF_MYPICS, "+CMyPics::UnInit");
  177. if (m_pSink)
  178. {
  179. if (m_pWin3)
  180. {
  181. EVENTS events[] = { EVENT_SCROLL };
  182. m_pSink->UnSinkEvents(m_pWin3, ARRAYSIZE(events), events);
  183. SAFERELEASE(m_pWin3);
  184. }
  185. m_pSink->SetParent(NULL);
  186. ATOMICRELEASE(m_pSink);
  187. }
  188. SAFERELEASE(m_pEleCurr);
  189. SAFERELEASE(m_pDoc2);
  190. TraceMsg(TF_MYPICS, "-CMyPics::UnInit");
  191. return S_OK;
  192. }
  193. ULONG CMyPics::AddRef(void)
  194. {
  195. return ++m_cRef;
  196. }
  197. ULONG CMyPics::Release(void)
  198. {
  199. if (--m_cRef == 0)
  200. {
  201. delete this;
  202. return 0;
  203. }
  204. return m_cRef;
  205. }
  206. // has this been disabled by some administrator or something via IEAK?
  207. BOOL MP_IsEnabledInIEAK()
  208. {
  209. DWORD dwType = REG_DWORD;
  210. DWORD dwSize;
  211. DWORD dwEnabled; // dsheldon - should this var be called dwDisabled since we say (dwEnabled != 1) == enabled?
  212. DWORD dwRet;
  213. const TCHAR c_szSPMIEPS[] = TEXT("Software\\Policies\\Microsoft\\Internet Explorer\\PhotoSupport");
  214. const TCHAR c_szVal[] = TEXT("MyPics_Hoverbar");
  215. dwSize = sizeof(dwEnabled);
  216. dwRet = SHGetValue(HKEY_CURRENT_USER, c_szSPMIEPS, c_szVal, &dwType, &dwEnabled, &dwSize);
  217. if ((dwType == REG_DWORD) && (dwRet == ERROR_SUCCESS))
  218. {
  219. if (dwEnabled!=1)
  220. return TRUE; // enabled
  221. else
  222. return FALSE; // disabled
  223. }
  224. // value not found...
  225. return TRUE;
  226. }
  227. // has the user explicitly disabled this feature for now and all time via intern control panel?
  228. BOOL MP_IsEnabledInRegistry()
  229. {
  230. DWORD dwType = REG_SZ;
  231. DWORD dwSize;
  232. TCHAR szEnabled[16];
  233. DWORD dwRet;
  234. const TCHAR c_szSMIEM[] = TEXT("Software\\Microsoft\\Internet Explorer\\Main");
  235. const TCHAR c_szVal[] = TEXT("Enable_MyPics_Hoverbar");
  236. dwSize = sizeof(szEnabled);
  237. dwRet = SHGetValue(HKEY_CURRENT_USER, c_szSMIEM, c_szVal, &dwType, szEnabled, &dwSize);
  238. if (dwRet == ERROR_INSUFFICIENT_BUFFER)
  239. {
  240. ASSERT(dwRet == ERROR_SUCCESS); // this is wacky...
  241. return FALSE;
  242. }
  243. if ((dwType == REG_SZ) && (dwRet == ERROR_SUCCESS))
  244. {
  245. if (!StrCmp(szEnabled, TEXT("yes")))
  246. return TRUE; // enabled
  247. else
  248. return FALSE; // disabled
  249. }
  250. // value not found...
  251. return TRUE;
  252. }
  253. DWORD MP_GetFilterInfoFromRegistry()
  254. {
  255. const TCHAR c_szSMIEAOMM[] = TEXT("Software\\Microsoft\\Internet Explorer\\Main");
  256. const TCHAR c_szVal[] = TEXT("Image_Filter");
  257. DWORD dwType, dwSize, dwFilter, dwRet;
  258. dwSize = sizeof(dwFilter);
  259. dwRet = SHGetValue(HKEY_CURRENT_USER, c_szSMIEAOMM, c_szVal, &dwType, &dwFilter, &dwSize);
  260. if ((dwRet != ERROR_SUCCESS) || (dwType != REG_DWORD))
  261. {
  262. dwFilter = MP_MIN_SIZE;
  263. }
  264. return dwFilter;
  265. }
  266. DWORD MP_GetOffsetInfoFromRegistry()
  267. {
  268. const TCHAR c_szSMIEAOMM[] = TEXT("Software\\Microsoft\\Internet Explorer\\Main");
  269. const TCHAR c_szVal[] = TEXT("Offset");
  270. DWORD dwType, dwSize, dwOffset, dwRet;
  271. dwSize = sizeof(dwOffset);
  272. dwRet = SHGetValue(HKEY_CURRENT_USER, c_szSMIEAOMM, c_szVal, &dwType, &dwOffset, &dwSize);
  273. if ((dwRet != ERROR_SUCCESS) || (dwType != REG_DWORD))
  274. {
  275. dwOffset = MP_HOVER_OFFSET;
  276. }
  277. return dwOffset;
  278. }
  279. BOOL_PTR CALLBACK DisableMPDialogProc(HWND hDlg, UINT uMsg, WPARAM wparam, LPARAM lparam)
  280. {
  281. BOOL bMsgHandled = FALSE;
  282. switch (uMsg)
  283. {
  284. case WM_INITDIALOG:
  285. {
  286. // center dialog... yay msdn...
  287. RECT rc;
  288. GetWindowRect(hDlg, &rc);
  289. SetWindowPos(hDlg, HWND_TOP,
  290. ((GetSystemMetrics(SM_CXSCREEN) - (rc.right - rc.left)) / 2),
  291. ((GetSystemMetrics(SM_CYSCREEN) - (rc.bottom - rc.top)) / 2),
  292. 0, 0, SWP_NOSIZE);
  293. }
  294. break;
  295. case WM_COMMAND:
  296. switch (LOWORD(wparam))
  297. {
  298. case IDC_MP_ALWAYS:
  299. EndDialog(hDlg, IDC_MP_ALWAYS);
  300. break;
  301. case IDC_MP_THISSESSION:
  302. EndDialog(hDlg, IDC_MP_THISSESSION);
  303. break;
  304. case IDC_MP_CANCEL:
  305. EndDialog(hDlg, IDC_MP_CANCEL);
  306. break;
  307. }
  308. break;
  309. case WM_CLOSE:
  310. EndDialog(hDlg, IDC_MP_CANCEL);
  311. break;
  312. default:
  313. break;
  314. }
  315. return(bMsgHandled);
  316. }
  317. LRESULT CALLBACK CMyPics::s_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  318. {
  319. CMyPics* pThis = (CMyPics*)GetWindowPtr(hWnd, GWLP_USERDATA);
  320. TraceMsg(TF_MYPICS, "+CMyPics::s_WndProc hWnd=%x, pThis=%p", hWnd, pThis);
  321. HRESULT hr = S_OK;
  322. IOleCommandTarget *pOleCommandTarget = NULL;
  323. switch (uMsg)
  324. {
  325. case WM_SIZE:
  326. if (!pThis)
  327. break;
  328. SetWindowPos(pThis->m_hWndMyPicsToolBar, NULL, 0, 0, LOWORD(lParam), HIWORD(lParam), SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
  329. break;
  330. case WM_ERASEBKGND:
  331. if (!pThis)
  332. break;
  333. {
  334. RECT rc;
  335. HBRUSH hb = GetSysColorBrush(COLOR_3DFACE);
  336. GetClientRect(pThis->m_hWndMyPicsToolBar, &rc);
  337. FillRect((HDC)wParam, &rc, hb);
  338. return TRUE;
  339. }
  340. case WM_COMMAND:
  341. if (!pThis)
  342. break;
  343. switch(LOWORD(wParam))
  344. {
  345. case IDM_MYPICS_SAVE: //Save As... dialogue
  346. ASSERT(pThis->m_pEleCurr);
  347. // the evil QI call...
  348. hr = pThis->m_pEleCurr->QueryInterface(IID_IOleCommandTarget, (void **)&pOleCommandTarget);
  349. if (FAILED(hr))
  350. return(hr);
  351. // hide the hoverthing so it doesn't cause us any nasty problems
  352. pThis->HideHover();
  353. // launch the Save As dialogue thingie...
  354. pOleCommandTarget->Exec(&CGID_MSHTML, IDM_SAVEPICTURE, 0, 0, NULL);
  355. ATOMICRELEASE(pOleCommandTarget);
  356. break;
  357. case IDM_MYPICS_PRINT:
  358. {
  359. // get the cmd target
  360. hr = pThis->m_pEleCurr->QueryInterface(IID_IOleCommandTarget, (void **)&pOleCommandTarget);
  361. if (FAILED(hr))
  362. return(hr);
  363. pThis->HideHover();
  364. //pThis->m_hoverState = HOVERSTATE_SHOWING; // kludge to keep hover from appearing under print dialogue
  365. pOleCommandTarget->Exec(&CGID_MSHTML, IDM_MP_PRINTPICTURE, 0, 0, NULL);
  366. ATOMICRELEASE(pOleCommandTarget);
  367. //pThis->m_hoverState = HOVERSTATE_HIDING;
  368. }
  369. break;
  370. case IDM_MYPICS_EMAIL:
  371. {
  372. // get the cmd target...
  373. hr = pThis->m_pEleCurr->QueryInterface(IID_IOleCommandTarget, (void **)&pOleCommandTarget);
  374. if (FAILED(hr))
  375. return(hr);
  376. // ... and then hide the hover bar...
  377. pThis->HideHover();
  378. //pThis->m_hoverState = HOVERSTATE_SHOWING; // kludge to keep hover from appearing under print dialogue
  379. // ... and pray this works...
  380. pOleCommandTarget->Exec(&CGID_MSHTML, IDM_MP_EMAILPICTURE, 0, 0, NULL);
  381. ATOMICRELEASE(pOleCommandTarget);
  382. // ... and cleanup
  383. //pThis->m_hoverState = HOVERSTATE_HIDING;
  384. }
  385. break;
  386. case IDM_MYPICS_MYPICS: // Open My Pictures folder
  387. // get the cmd target
  388. hr = pThis->m_pEleCurr->QueryInterface(IID_IOleCommandTarget, (void **)&pOleCommandTarget);
  389. if (FAILED(hr))
  390. return(hr);
  391. pOleCommandTarget->Exec(&CGID_MSHTML, IDM_MP_MYPICS, 0, 0, NULL);
  392. ATOMICRELEASE(pOleCommandTarget);
  393. hr = S_OK;
  394. pThis->HideHover();
  395. break;
  396. default:
  397. break;
  398. }
  399. break;
  400. case WM_NOTIFY: // tooltips...
  401. if (!pThis)
  402. break;
  403. switch (((LPNMHDR)lParam)->code)
  404. {
  405. case TTN_NEEDTEXT:
  406. {
  407. LPTOOLTIPTEXT lpToolTipText;
  408. TCHAR szBuf[MAX_PATH];
  409. lpToolTipText = (LPTOOLTIPTEXT)lParam;
  410. hr = MLLoadString((UINT)lpToolTipText->hdr.idFrom,
  411. szBuf,
  412. ARRAYSIZE(szBuf));
  413. lpToolTipText->lpszText = szBuf;
  414. break;
  415. }
  416. }
  417. break;
  418. case WM_SETTINGCHANGE:
  419. if (!pThis)
  420. break;
  421. {
  422. pThis->DestroyHover(); // to stop wierd window distortion
  423. break;
  424. }
  425. case WM_CONTEXTMENU:
  426. if (!pThis)
  427. break;
  428. {
  429. // load the menu
  430. HMENU hMenu0 = LoadMenu(MLGetHinst(), MAKEINTRESOURCE(IDR_MYPICS_CONTEXT_MENU));
  431. HMENU hMenu1 = GetSubMenu(hMenu0, 0);
  432. if(!hMenu1)
  433. break;
  434. POINT point;
  435. point.x = (LONG)GET_X_LPARAM(lParam);
  436. point.y = (LONG)GET_Y_LPARAM(lParam);
  437. ASSERT(pThis->m_hoverState=HOVERSTATE_SHOWING);
  438. // lock against mouseouts
  439. pThis->m_hoverState = HOVERSTATE_LOCKED;
  440. // display it, get choice (if any)
  441. int iPick = TrackPopupMenu(hMenu1,
  442. TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD,
  443. point.x,
  444. point.y,
  445. 0,
  446. hWnd,
  447. (RECT *)NULL);
  448. DestroyMenu(hMenu0);
  449. DestroyMenu(hMenu1);
  450. pThis->m_hoverState = HOVERSTATE_SHOWING;
  451. if (iPick)
  452. {
  453. switch(iPick)
  454. {
  455. case IDM_DISABLE_MYPICS:
  456. {
  457. pThis->HideHover();
  458. // create dialog to ask user if they want to turn this stuff off...
  459. // (explicit cast to make Win64 builds happy)
  460. int iResult = (int)DialogBoxParam(MLGetHinst(),
  461. MAKEINTRESOURCE(DLG_DISABLE_MYPICS),
  462. pThis->m_Hwnd,
  463. DisableMPDialogProc,
  464. NULL);
  465. // deal with their choice...
  466. if (iResult)
  467. {
  468. switch (iResult)
  469. {
  470. case IDC_MP_ALWAYS:
  471. {
  472. pThis->m_bIsOffForSession = TRUE;
  473. DWORD dwType = REG_SZ;
  474. DWORD dwSize;
  475. TCHAR szEnabled[16] = TEXT("no");
  476. DWORD dwRet;
  477. const TCHAR c_szSMIEM[] =
  478. TEXT("Software\\Microsoft\\Internet Explorer\\Main");
  479. const TCHAR c_szVal[] = TEXT("Enable_MyPics_Hoverbar");
  480. dwSize = sizeof(szEnabled);
  481. dwRet = SHSetValue(HKEY_CURRENT_USER,
  482. c_szSMIEM,
  483. c_szVal,
  484. dwType,
  485. szEnabled,
  486. dwSize);
  487. }
  488. break;
  489. case IDC_MP_THISSESSION:
  490. // twiddle member var flag
  491. // this is propagated back up to COmWindow via ReleaseMyPics() function.
  492. pThis->m_bIsOffForSession = TRUE;
  493. break;
  494. default:
  495. break;
  496. }
  497. }
  498. }
  499. break;
  500. case IDM_HELP_MYPICS:
  501. pThis->HideHover();
  502. SHHtmlHelpOnDemandWrap(hWnd, TEXT("iexplore.chm > iedefault"), 0, (DWORD_PTR) TEXT("pic_tb_ovr.htm"), ML_CROSSCODEPAGE);
  503. break;
  504. default:
  505. // um, do nothing
  506. break;
  507. }
  508. }
  509. }
  510. break;
  511. default:
  512. return (DefWindowProc(hWnd, uMsg, wParam, lParam));
  513. }
  514. TraceMsg(TF_MYPICS, "-CMyPics::s_WndProc hWnd=%x, pThis=%p", hWnd, pThis);
  515. return (hr);
  516. }
  517. VOID CALLBACK CMyPics::s_TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
  518. {
  519. TraceMsg(TF_MYPICS, "+CMyPics::TimerProc");
  520. CMyPics* pThis = (CMyPics*)GetWindowPtr(hwnd, GWLP_USERDATA);
  521. switch (uMsg)
  522. {
  523. case WM_TIMER:
  524. KillTimer(hwnd, IDT_MP_TIMER);
  525. if (pThis && (pThis->m_hoverState == HOVERSTATE_WAITINGTOSHOW))
  526. {
  527. // Our hover bar is waiting to be shown.
  528. if (pThis->m_pEleCurr)
  529. {
  530. // We still have an element. Show it.
  531. pThis->m_hoverState = HOVERSTATE_SHOWING;
  532. pThis->ShowHover();
  533. }
  534. else
  535. {
  536. // Our timer popped, but we don't have an element.
  537. pThis->HideHover();
  538. }
  539. }
  540. break;
  541. default:
  542. break;
  543. }
  544. TraceMsg(TF_MYPICS, "-CMyPics::TimerProc");
  545. }
  546. BOOL CMyPics::ShouldAppearOnThisElement(IHTMLElement *pEle)
  547. {
  548. BOOL bRet = TRUE; // appear by default
  549. VARIANT varVal = {0};
  550. BSTR bstrAttribute = NULL; // to check img tags for expando
  551. IHTMLRect *pRect = NULL; // to get screen coords
  552. IHTMLElement2 *pEle2 = NULL;
  553. IHTMLElement3 *pEle3 = NULL; // to check for contenteditable mode
  554. VARIANT_BOOL bEdit = FALSE;// becomes true if contenteditable mode is true
  555. LONG lLeft; // these are the screen coords
  556. LONG lRight; // we get right and bottom to det size of image
  557. LONG lTop;
  558. LONG lBottom;
  559. DWORD dwFilter;
  560. IOleCommandTarget *pOleCommandTarget = NULL;
  561. TraceMsg(TF_MYPICS, "+CMyPics::ShouldAppearOnThisElement");
  562. // don't create it if it already exists. thats bad.
  563. if ((HOVERSTATE_SHOWING == m_hoverState) || (HOVERSTATE_LOCKED == m_hoverState))
  564. {
  565. bRet = FALSE;
  566. goto Cleanup;
  567. }
  568. m_bGalleryImg = FALSE;
  569. if (!pEle)
  570. {
  571. bRet = FALSE;
  572. goto Cleanup;
  573. }
  574. // find out if the image didn't load or is unrenderable
  575. if (SUCCEEDED(pEle->QueryInterface(IID_IOleCommandTarget, (void **)&pOleCommandTarget)))
  576. {
  577. OLECMD rgCmd;
  578. rgCmd.cmdID = IDM_SAVEPICTURE; // this is the same check the context menu uses
  579. rgCmd.cmdf = 0;
  580. pOleCommandTarget->QueryStatus(&CGID_MSHTML, 1, &rgCmd, NULL);
  581. if (!(OLECMDF_ENABLED & rgCmd.cmdf))
  582. {
  583. bRet = FALSE;
  584. goto Cleanup;
  585. }
  586. }
  587. // check for explicit enable/disable attribute in img tag...
  588. bstrAttribute=SysAllocString(L"galleryimg");
  589. if (!bstrAttribute)
  590. goto Cleanup;
  591. if (SUCCEEDED(pEle->getAttribute(bstrAttribute, 0, &varVal)))
  592. {
  593. if (varVal.vt == VT_BSTR)
  594. {
  595. if (StrCmpIW(varVal.bstrVal, L"true") == 0
  596. || StrCmpIW(varVal.bstrVal, L"on") == 0
  597. || StrCmpIW(varVal.bstrVal, L"yes") == 0
  598. )
  599. {
  600. // Explicitly turned on. Honor it and leave.
  601. bRet = TRUE;
  602. m_bGalleryImg = TRUE;
  603. goto Cleanup;
  604. }
  605. if (StrCmpIW(varVal.bstrVal, L"false") == 0
  606. || StrCmpIW(varVal.bstrVal, L"off") == 0
  607. || StrCmpIW(varVal.bstrVal, L"no") == 0
  608. )
  609. {
  610. // Explicitly turned off. Honor it and leave.
  611. bRet = FALSE;
  612. goto Cleanup;
  613. }
  614. }
  615. else if (varVal.vt == VT_BOOL)
  616. {
  617. if (varVal.boolVal == VARIANT_TRUE)
  618. {
  619. bRet = TRUE;
  620. m_bGalleryImg = TRUE;
  621. goto Cleanup;
  622. }
  623. else
  624. {
  625. bRet = FALSE;
  626. goto Cleanup;
  627. }
  628. }
  629. }
  630. VariantClear(&varVal);
  631. SysFreeString(bstrAttribute);
  632. // After checking "galleryimg" tag, check to see if turned off by the META tag
  633. if (m_bGalleryMeta == FALSE)
  634. return FALSE;
  635. // check for mappings on the image...
  636. bstrAttribute=SysAllocString(L"usemap");
  637. if (!bstrAttribute)
  638. return (bRet);
  639. if (SUCCEEDED(pEle->getAttribute(bstrAttribute, 0, &varVal)))
  640. {
  641. if (varVal.vt == VT_BSTR)
  642. {
  643. // What do we do here?
  644. bRet = (varVal.bstrVal == NULL);
  645. if (!bRet)
  646. goto Cleanup;
  647. }
  648. }
  649. VariantClear(&varVal);
  650. SysFreeString(bstrAttribute);
  651. // check for mappings on the image...
  652. bstrAttribute=SysAllocString(L"ismap");
  653. if (!bstrAttribute)
  654. return (bRet);
  655. if (SUCCEEDED(pEle->getAttribute(bstrAttribute, 0, &varVal)))
  656. {
  657. // If the attribute exists, then we need to return FALSE *unless* we see a value of FALSE
  658. bRet = FALSE;
  659. if (varVal.vt == VT_BOOL
  660. && varVal.boolVal == VARIANT_FALSE)
  661. {
  662. // "ismap" is false, so we can show the hover bar over this image.
  663. bRet = TRUE;
  664. }
  665. }
  666. if (!bRet)
  667. goto Cleanup;
  668. bRet = FALSE; // If any of the calls below fail, we'll exit with "FALSE".
  669. // Now check to see if we pass the size filter.
  670. // get an IHTMLElement2 from the IHTMLElement passed in...
  671. if (FAILED(pEle->QueryInterface(IID_IHTMLElement2, (void **)&pEle2) ))
  672. goto Cleanup;
  673. // get coords...
  674. if (FAILED(pEle2->getBoundingClientRect(&pRect) ))
  675. goto Cleanup;
  676. if (FAILED(pRect->get_left(&lLeft) ))
  677. goto Cleanup;
  678. if (FAILED(pRect->get_right(&lRight) ))
  679. goto Cleanup;
  680. if (FAILED(pRect->get_top(&lTop) ))
  681. goto Cleanup;
  682. if (FAILED(pRect->get_bottom(&lBottom) ))
  683. goto Cleanup;
  684. dwFilter = MP_GetFilterInfoFromRegistry();
  685. // see if this picture is big enough to qualify as a "Photo"...
  686. // TODO: decide if we like checking aspect ratio or not
  687. if ( (lRight - lLeft >= (LONG)dwFilter && lBottom - lTop >= (LONG)dwFilter)
  688. /*&& !(2*(min(lRight-lLeft,lBottom-lTop)) < max(lRight-lLeft,lBottom-lTop)) */)
  689. bRet = TRUE;
  690. if (FAILED(pEle2->QueryInterface(IID_IHTMLElement3, (void **)&pEle3) ))
  691. goto Cleanup;
  692. if (FAILED(pEle3->get_isContentEditable(&bEdit) ))
  693. goto Cleanup;
  694. if (bEdit)
  695. bRet = FALSE;
  696. Cleanup:
  697. VariantClear(&varVal);
  698. if (bstrAttribute)
  699. SysFreeString(bstrAttribute);
  700. SAFERELEASE(pOleCommandTarget);
  701. SAFERELEASE(pEle3);
  702. SAFERELEASE(pRect);
  703. SAFERELEASE(pEle2);
  704. TraceMsg(TF_MYPICS, "-CMyPics::ShouldAppearOnThisElement");
  705. return bRet;
  706. }
  707. HRESULT CMyPics::CreateHover()
  708. {
  709. HRESULT hr = S_OK;
  710. SIZE size = {0,0};
  711. WORD wImage;
  712. HBITMAP hbmp = NULL;
  713. HBITMAP hbmpHot = NULL;
  714. TraceMsg(TF_MYPICS, "+CMyPics::CreateHover, this=%p, m_hoverState=%d", this, m_hoverState);
  715. InitCommonControls();
  716. WNDCLASS wc = {0};
  717. wc.style = CS_HREDRAW | CS_VREDRAW;
  718. wc.lpszClassName = TEXT("MyPicturesHost");
  719. wc.lpfnWndProc = s_WndProc;
  720. wc.hInstance = g_hinst;
  721. wc.hbrBackground = HBRUSH(COLOR_BTNFACE);
  722. RegisterClass(&wc);
  723. // create the rebar to hold the toolbar...
  724. if (!m_hWndHover)
  725. {
  726. m_hWndHover = CreateWindow(TEXT("MyPicturesHost"), TEXT(""), WS_DLGFRAME | WS_VISIBLE | WS_CHILD,
  727. 0, 0, 0, 0, m_Hwnd, NULL, g_hinst, NULL);
  728. if (!m_hWndHover)
  729. {
  730. TraceMsg(TF_MYPICS | TF_WARNING, "CMyPics::CreateHover, unable to create m_hWndHover");
  731. hr = E_FAIL;
  732. goto Cleanup;
  733. }
  734. ASSERT(GetWindowPtr(m_hWndHover, GWLP_USERDATA) == NULL);
  735. SetWindowPtr(m_hWndHover, GWLP_USERDATA, this);
  736. // set cc version
  737. SendMessage(m_hWndHover, CCM_SETVERSION, COMCTL32_VERSION, 0);
  738. }
  739. // create the toolbar...
  740. if (!m_hWndMyPicsToolBar)
  741. {
  742. m_hWndMyPicsToolBar = CreateWindow(TOOLBARCLASSNAME, TEXT(""), TBSTYLE_TOOLTIPS | CCS_NODIVIDER | TBSTYLE_FLAT | WS_VISIBLE | WS_CHILD,
  743. 0,0,0,0, m_hWndHover, NULL, g_hinst, NULL);
  744. if (!m_hWndMyPicsToolBar)
  745. {
  746. TraceMsg(TF_MYPICS | TF_WARNING, "CMyPics::CreateHover, unable to create m_hWndMyPicsToolBar");
  747. hr = E_FAIL;
  748. goto Cleanup;
  749. }
  750. SetWindowPtr(m_hWndMyPicsToolBar, GWLP_USERDATA, this); // for the timer proc
  751. // set cc version for this too, and the sizeof tbbutton struct...
  752. SendMessage(m_hWndMyPicsToolBar, CCM_SETVERSION, COMCTL32_VERSION, 0);
  753. SendMessage(m_hWndMyPicsToolBar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
  754. }
  755. // create image lists...
  756. wImage = ((IsOS(OS_WHISTLERORGREATER)) ? IDB_MYPICS_TOOLBARGW : IDB_MYPICS_TOOLBARG);
  757. if (!m_himlHover)
  758. {
  759. m_himlHover = ImageList_LoadImage(HINST_THISDLL, MAKEINTRESOURCE(wImage), 16, 0, CLR_DEFAULT, IMAGE_BITMAP, LR_CREATEDIBSECTION);
  760. if (!m_himlHover)
  761. {
  762. TraceMsg(TF_MYPICS | TF_WARNING, "CMyPics::CreateHover, unable to create m_himlHover");
  763. }
  764. }
  765. wImage = ((IsOS(OS_WHISTLERORGREATER)) ? IDB_MYPICS_TOOLBARW : IDB_MYPICS_TOOLBAR);
  766. if (!m_himlHoverHot)
  767. {
  768. m_himlHoverHot = ImageList_LoadImage(HINST_THISDLL, MAKEINTRESOURCE(wImage) , 16, 0, CLR_DEFAULT, IMAGE_BITMAP, LR_CREATEDIBSECTION);
  769. if (!m_himlHoverHot)
  770. {
  771. TraceMsg(TF_MYPICS | TF_WARNING, "CMyPics::CreateHover, unable to create m_himlHoverHot");
  772. }
  773. }
  774. // set image list and hot image list
  775. SendMessage(m_hWndMyPicsToolBar, TB_SETIMAGELIST, 0, (LPARAM)m_himlHover );
  776. SendMessage(m_hWndMyPicsToolBar, TB_SETHOTIMAGELIST, 0, (LPARAM)m_himlHoverHot);
  777. TBBUTTON tbButton;
  778. // set bitmap indexes in tbbutton structure (this may not be necessary)
  779. for (int i=0;i<MP_NUM_TBBITMAPS;i++)
  780. {
  781. tbButton.iBitmap = MAKELONG(i,0);
  782. tbButton.fsState = TBSTATE_ENABLED;
  783. tbButton.fsStyle = TBSTYLE_BUTTON;
  784. tbButton.dwData = 0;
  785. tbButton.iString = 0;
  786. switch(i)
  787. {
  788. case 0: tbButton.idCommand = IDM_MYPICS_SAVE; break;
  789. case 1: tbButton.idCommand = IDM_MYPICS_PRINT; break;
  790. case 2: tbButton.idCommand = IDM_MYPICS_EMAIL; break;
  791. case 3: tbButton.idCommand = IDM_MYPICS_MYPICS; break;
  792. }
  793. SendMessage(m_hWndMyPicsToolBar, TB_INSERTBUTTON, i, (LPARAM)&tbButton);
  794. }
  795. Cleanup:
  796. TraceMsg(TF_MYPICS, "-CMyPics::CreateHover, this=%p, m_hoverState=%d", this, m_hoverState);
  797. return hr;
  798. }
  799. HRESULT CMyPics::DestroyHover()
  800. {
  801. HRESULT hr = S_OK;
  802. TraceMsg(TF_MYPICS, "+CMyPics::DestroyHover, this=%p, m_hoverState=%d", this, m_hoverState);
  803. // If we have a MyPicsToolBar...
  804. if (m_hWndMyPicsToolBar)
  805. {
  806. // first destroy the toolbar
  807. if (!DestroyWindow(m_hWndMyPicsToolBar))
  808. {
  809. TraceMsg(TF_MYPICS, "In CMyPics::DestroyHover, DestroyWindow(m_hWndMyPicsToolBar) failed");
  810. hr = E_FAIL;
  811. }
  812. m_hWndMyPicsToolBar=NULL;
  813. }
  814. // If we have a hover window...
  815. if (m_hWndHover)
  816. {
  817. // Clear the window word
  818. SetWindowPtr(m_hWndHover, GWLP_USERDATA, NULL);
  819. // then destroy the rebar
  820. if (!DestroyWindow(m_hWndHover))
  821. {
  822. hr = E_FAIL;
  823. goto Cleanup;
  824. }
  825. m_hWndHover = NULL;
  826. }
  827. // and destroy the image lists...
  828. if (m_himlHover)
  829. {
  830. ImageList_Destroy(m_himlHover);
  831. m_himlHover = NULL;
  832. }
  833. if (m_himlHoverHot)
  834. {
  835. ImageList_Destroy(m_himlHoverHot);
  836. m_himlHoverHot = NULL;
  837. }
  838. Cleanup:
  839. TraceMsg(TF_MYPICS, "-CMyPics::DestroyHover, this=%p, hr=%x", this, hr);
  840. return hr;
  841. }
  842. HRESULT CMyPics::HideHover()
  843. {
  844. HRESULT hr = S_OK;
  845. TraceMsg(TF_MYPICS, "+CMyPics::HideHover, this=%p, m_hoverState=%d", this, m_hoverState);
  846. if (m_hWndHover)
  847. {
  848. ShowWindow(m_hWndHover, SW_HIDE);
  849. m_hoverState = HOVERSTATE_HIDING;
  850. }
  851. else
  852. hr = E_FAIL;
  853. TraceMsg(TF_MYPICS, "-CMyPics::HideHover, this=%p, m_hoverState=%d", this, m_hoverState);
  854. return hr;
  855. }
  856. IHTMLElement *CMyPics::GetIMGFromArea(IHTMLElement *pEleIn, POINT ptEvent)
  857. {
  858. // someone got an IHTMLElement and decided it was an area tag
  859. // so find the img tag associated and return it as an IHTMLElement
  860. BSTR bstrName = NULL;
  861. BSTR bstrUseMap = NULL;
  862. IHTMLElement *pEleParent = NULL;
  863. IHTMLElement *pEleMisc = NULL;
  864. IHTMLElement2 *pEle2Misc = NULL;
  865. IHTMLElement *pEleMiscPar = NULL;
  866. IHTMLMapElement *pEleMap = NULL;
  867. IHTMLImgElement *pEleImg = NULL;
  868. IHTMLElement *pEleOut = NULL;
  869. IHTMLElementCollection *pCollect = NULL;
  870. IHTMLElementCollection *pSubCollect = NULL;
  871. IDispatch *pDisp = NULL;
  872. VARIANT TagName;
  873. ULONG ulCount = 0;
  874. VARIANTARG va1;
  875. VARIANTARG va2;
  876. HRESULT hr;
  877. POINT ptMouse,
  878. ptScr;
  879. LONG xInIMG = 0,
  880. yInIMG = 0,
  881. lOffset = 0,
  882. lOffsetLeft = 0,
  883. lOffsetTop = 0,
  884. lScrollLeft = 0,
  885. lScrollTop = 0,
  886. lOffsetW = 0,
  887. lOffsetH = 0;
  888. TagName.vt = VT_BSTR;
  889. TagName.bstrVal = (BSTR)c_bstr_IMG;
  890. // first get the map element
  891. if (SUCCEEDED(pEleIn->get_offsetParent(&pEleParent)))
  892. {
  893. // get the map element
  894. hr=pEleParent->QueryInterface(IID_IHTMLMapElement, (void **)&pEleMap);
  895. if (FAILED(hr))
  896. goto Cleanup;
  897. // next get the name of the map
  898. if (SUCCEEDED(pEleMap->get_name(&bstrName)))
  899. {
  900. //next get all tags
  901. hr = m_pDoc2->get_all(&pCollect);
  902. if (FAILED(hr))
  903. goto Cleanup;
  904. //get all IMG tags
  905. hr = pCollect->tags(TagName, &pDisp);
  906. if (FAILED(hr))
  907. goto Cleanup;
  908. if (pDisp)
  909. {
  910. hr = pDisp->QueryInterface(IID_IHTMLElementCollection,(void **)&pSubCollect);
  911. ATOMICRELEASE(pDisp);
  912. }
  913. if (FAILED(hr))
  914. goto Cleanup;
  915. //get IMG tag count
  916. hr = pSubCollect->get_length((LONG *)&ulCount);
  917. if (FAILED(hr))
  918. goto Cleanup;
  919. va1.vt = VT_I4;
  920. va2.vt = VT_EMPTY;
  921. ASSERT(pDisp==NULL);
  922. //iterate through tags looking for images that have the right usemap set
  923. for (int i=0; i<(LONG)ulCount; i++)
  924. {
  925. ATOMICRELEASE(pEleImg);
  926. ATOMICRELEASE(pDisp);
  927. pDisp = NULL;
  928. bstrUseMap = NULL;
  929. xInIMG = 0;
  930. yInIMG = 0;
  931. lOffset = 0;
  932. lOffsetLeft = 0;
  933. lOffsetTop = 0;
  934. lScrollLeft = 0;
  935. lScrollTop = 0;
  936. lOffsetW = 0;
  937. lOffsetH = 0;
  938. va1.lVal = (LONG)i;
  939. pSubCollect->item(va1, va2, &pDisp);
  940. if (pDisp)
  941. {
  942. hr = pDisp->QueryInterface(IID_IHTMLImgElement, (void **)&pEleImg);
  943. if (FAILED(hr))
  944. goto Cleanup;
  945. hr = pEleImg->get_useMap(&bstrUseMap);
  946. if (FAILED(hr))
  947. goto Cleanup;
  948. // this will be non-null if set for this IMG element...
  949. if (bstrUseMap){
  950. // skip the prepended '#' and see if this is what we're looking for...
  951. if (StrCmp(bstrUseMap + 1, bstrName) == 0)
  952. {
  953. m_pWin3->get_screenLeft(&ptScr.x);
  954. m_pWin3->get_screenTop (&ptScr.y);
  955. //Ok, we found a candidate. See if the mouse is here...
  956. ptMouse.x = ptEvent.x - ptScr.x;
  957. ptMouse.y = ptEvent.y - ptScr.y;
  958. hr = pDisp->QueryInterface(IID_IHTMLElement, (void **)&pEleMisc);
  959. if (FAILED(hr))
  960. goto Cleanup;
  961. while (pEleMisc)
  962. {
  963. hr = pEleMisc->QueryInterface(IID_IHTMLElement2, (void **)&pEle2Misc);
  964. if (FAILED(hr))
  965. goto Cleanup;
  966. pEleMisc->get_offsetLeft(&lOffsetLeft);
  967. pEle2Misc->get_scrollLeft(&lScrollLeft);
  968. lOffset += lOffsetLeft - lScrollLeft;
  969. pEleMisc->get_offsetParent(&pEleMiscPar);
  970. ATOMICRELEASE(pEleMisc);
  971. ATOMICRELEASE(pEle2Misc);
  972. pEleMisc=pEleMiscPar;
  973. }
  974. ATOMICRELEASE(pEleMiscPar);
  975. hr = pDisp->QueryInterface(IID_IHTMLElement, (void **)&pEleMisc);
  976. if (FAILED(hr))
  977. goto Cleanup;
  978. xInIMG = ptMouse.x - lOffset;
  979. pEleMisc->get_offsetWidth(&lOffsetW);
  980. if ((xInIMG < 0) || (xInIMG > lOffsetW))
  981. continue;
  982. lOffset = 0;
  983. while (pEleMisc)
  984. {
  985. hr = pEleMisc->QueryInterface(IID_IHTMLElement2, (void **)&pEle2Misc);
  986. if (FAILED(hr))
  987. goto Cleanup;
  988. pEleMisc->get_offsetTop(&lOffsetTop);
  989. pEle2Misc->get_scrollTop(&lScrollTop);
  990. lOffset += lOffsetTop - lScrollTop;
  991. pEleMisc->get_offsetParent(&pEleMiscPar);
  992. ATOMICRELEASE(pEleMisc);
  993. ATOMICRELEASE(pEle2Misc);
  994. pEleMisc=pEleMiscPar;
  995. }
  996. ATOMICRELEASE(pEleMiscPar);
  997. hr = pDisp->QueryInterface(IID_IHTMLElement, (void **)&pEleMisc);
  998. if (FAILED(hr))
  999. goto Cleanup;
  1000. yInIMG = ptMouse.y - lOffset;
  1001. pEleMisc->get_offsetHeight(&lOffsetH);
  1002. ATOMICRELEASE(pEleMisc);
  1003. if ((yInIMG < 0) || (yInIMG > lOffsetH))
  1004. continue;
  1005. // if we get to this point we found our IMG element so...
  1006. // ...do the QI...
  1007. pEleImg->QueryInterface(IID_IHTMLElement, (void **)&pEleOut);
  1008. // ...and we're done.
  1009. break;
  1010. }
  1011. SysFreeString(bstrUseMap);
  1012. bstrUseMap = NULL;
  1013. }
  1014. }
  1015. }
  1016. }
  1017. }
  1018. Cleanup:
  1019. ATOMICRELEASE(pCollect);
  1020. ATOMICRELEASE(pSubCollect);
  1021. ATOMICRELEASE(pEleMap);
  1022. ATOMICRELEASE(pEleParent);
  1023. ATOMICRELEASE(pDisp);
  1024. ATOMICRELEASE(pEleImg);
  1025. ATOMICRELEASE(pEleMisc);
  1026. ATOMICRELEASE(pEle2Misc);
  1027. ATOMICRELEASE(pEleMiscPar);
  1028. SysFreeString(bstrUseMap);
  1029. SysFreeString(bstrName);
  1030. return (pEleOut);
  1031. }
  1032. // sometimes coordinates are relative to a parent object, like in frames, etc. so this gets their real position relative
  1033. // to the browser window...
  1034. HRESULT CMyPics::GetRealCoords(IHTMLElement2 *pEle2, HWND hwnd, LONG *plLeft, LONG *plTop, LONG *plRight, LONG *plBottom)
  1035. {
  1036. LONG lScreenLeft = 0,
  1037. lScreenTop = 0;
  1038. HRESULT hr = E_FAIL;
  1039. IHTMLRect *pRect = NULL;
  1040. *plLeft = *plTop = *plRight = *plBottom = 0;
  1041. if (!pEle2)
  1042. return hr;
  1043. if (SUCCEEDED(pEle2->getBoundingClientRect(&pRect)) && pRect)
  1044. {
  1045. LONG lLeft, lRight, lTop, lBottom;
  1046. pRect->get_left(&lLeft);
  1047. pRect->get_right(&lRight);
  1048. pRect->get_top(&lTop);
  1049. pRect->get_bottom(&lBottom);
  1050. // if its an iframe and it scrolls past the top of the frame, we should correct a bit.
  1051. if (lTop <= 0)
  1052. lTop = 0;
  1053. // dito for left side
  1054. if (lLeft <= 0)
  1055. lLeft = 0;
  1056. POINT pointTL, pointBR; // TL=Top,Left BR=Bottom,Right
  1057. ASSERT(m_pWin3);
  1058. m_pWin3->get_screenLeft(&lScreenLeft);
  1059. m_pWin3->get_screenTop(&lScreenTop);
  1060. // convert coords relative to the frame window to screen coords
  1061. pointTL.x = lScreenLeft + lLeft;
  1062. pointTL.y = lScreenTop + lTop;
  1063. pointBR.x = lScreenLeft + lRight;
  1064. pointBR.y = lScreenTop + lBottom;
  1065. // now convert from screen coords to client coords and assign...
  1066. if (ScreenToClient(hwnd, &pointTL) && ScreenToClient(hwnd, &pointBR))
  1067. {
  1068. *plLeft = pointTL.x;
  1069. *plRight = pointBR.x;
  1070. *plTop = pointTL.y;
  1071. *plBottom = pointBR.y;
  1072. hr = S_OK;
  1073. }
  1074. pRect->Release();
  1075. }
  1076. return hr;
  1077. }
  1078. HRESULT CMyPics::ShowHover()
  1079. {
  1080. HRESULT hr = S_OK;
  1081. IHTMLElement2 *pEle2 = NULL; // cause we need an ele2 to get screen coords
  1082. IHTMLRect *pRect = NULL; // to get screen coords
  1083. LONG lLeft; // these are the screen coords
  1084. LONG lRight; // we get right and bottom to det size of image
  1085. LONG lTop;
  1086. LONG lBottom;
  1087. DWORD dwOffset;
  1088. DWORD dw;
  1089. SIZE sz;
  1090. RECT rc;
  1091. TraceMsg(TF_MYPICS, "+CMyPics::ShowHover, this=%p, m_hoverState=%d", this, m_hoverState);
  1092. ASSERT(m_pEleCurr);
  1093. ASSERT(m_Hwnd);
  1094. // get an IHTMLElement2 from the IHTMLElement cached...
  1095. hr = m_pEleCurr->QueryInterface(IID_IHTMLElement2, (void **)&pEle2);
  1096. if (FAILED(hr))
  1097. goto Cleanup;
  1098. // get correct coords...
  1099. hr = GetRealCoords(pEle2, m_Hwnd, &lLeft, &lTop, &lRight, &lBottom);
  1100. if (FAILED(hr))
  1101. goto Cleanup;
  1102. // adjust for offset...
  1103. dwOffset = MP_GetOffsetInfoFromRegistry();
  1104. lLeft += dwOffset;
  1105. lTop += dwOffset;
  1106. // need to do some sanity checks to make sure the hover bar appears in a visible location...
  1107. RECT rcBrowserWnd;
  1108. if (GetClientRect(m_Hwnd, &rcBrowserWnd))
  1109. {
  1110. // check to make sure it'll appear somewhere we'll see it...
  1111. if (lLeft < rcBrowserWnd.left)
  1112. lLeft = rcBrowserWnd.left + dwOffset;
  1113. if (lTop < rcBrowserWnd.top)
  1114. lTop = rcBrowserWnd.top + dwOffset;
  1115. // check to make sure the entire hoverbar is over the image (so the user
  1116. // doesn't mouseout trying to get to the buttons!)
  1117. // If "galleryimg" was explicitly turned on, then bypass this code, which ensures that the entire
  1118. // toolbar will fit within the image.
  1119. if (!m_bGalleryImg)
  1120. {
  1121. if (lRight - lLeft < MP_MIN_CX + 10 - (LONG)dwOffset)
  1122. goto Cleanup;
  1123. if (lBottom - lTop < MP_MIN_CY + 10)
  1124. goto Cleanup;
  1125. // now check to make sure there is enough horiz and vert room for it to appear...
  1126. // if there isn't enough room, we just don't display it...
  1127. if ((rcBrowserWnd.right - MP_SCROLLBAR_SIZE) - lLeft < MP_MIN_CX)
  1128. goto Cleanup;
  1129. if ((rcBrowserWnd.bottom - (MP_SCROLLBAR_SIZE+2)) - lTop < MP_MIN_CY)
  1130. goto Cleanup;
  1131. }
  1132. }
  1133. dw = (DWORD)SendMessage(m_hWndMyPicsToolBar, TB_GETBUTTONSIZE, 0, 0);
  1134. sz.cx = LOWORD(dw); sz.cy = HIWORD(dw);
  1135. rc.left = rc.top = 0;
  1136. SendMessage(m_hWndMyPicsToolBar, TB_GETIDEALSIZE, FALSE, (LPARAM)&sz);
  1137. rc.right = sz.cx;
  1138. rc.bottom = sz.cy;
  1139. AdjustWindowRectEx(&rc, GetWindowLong(m_hWndHover, GWL_STYLE), FALSE, GetWindowLong(m_hWndHover, GWL_EXSTYLE));
  1140. if (SetWindowPos(m_hWndHover, NULL, lLeft, lTop, rc.right-rc.left, rc.bottom-rc.top, SWP_NOZORDER | SWP_SHOWWINDOW))
  1141. {
  1142. m_hoverState = HOVERSTATE_SHOWING;
  1143. }
  1144. Cleanup:
  1145. ATOMICRELEASE(pRect);
  1146. ATOMICRELEASE(pEle2);
  1147. TraceMsg(TF_MYPICS, "-CMyPics::ShowHover, this=%p, m_hoverState=%d", this, m_hoverState);
  1148. return hr;
  1149. }
  1150. HRESULT CMyPics::HandleScroll()
  1151. {
  1152. TraceMsg(TF_MYPICS, "+CMyPics::HandleScroll, this=%p, m_hoverState=%d", this, m_hoverState);
  1153. HRESULT hr = S_OK;
  1154. switch(m_hoverState)
  1155. {
  1156. // I don't think we need to do anything in these cases.
  1157. //
  1158. case HOVERSTATE_HIDING:
  1159. case HOVERSTATE_LOCKED:
  1160. case HOVERSTATE_WAITINGTOSHOW:
  1161. break;
  1162. case HOVERSTATE_SHOWING:
  1163. {
  1164. IHTMLElement2 *pEle2=NULL;
  1165. IHTMLRect *pRect=NULL;
  1166. RECT rect;
  1167. ASSERT(m_pEleCurr);
  1168. ASSERT(m_Hwnd);
  1169. ASSERT(m_hWndHover); // Ensure we do have a window
  1170. HideHover();
  1171. ShowHover();
  1172. // Redraw client area to get rid of window droppings scrolling causes.
  1173. // Try to redraw just the part where its likely to need it.
  1174. if (FAILED(m_pEleCurr->QueryInterface(IID_IHTMLElement2, (void **)&pEle2)))
  1175. {
  1176. goto CleanUp;
  1177. }
  1178. if (FAILED(pEle2->getBoundingClientRect(&pRect)))
  1179. {
  1180. goto CleanUp;
  1181. }
  1182. pRect->get_left(&rect.left);
  1183. pRect->get_right(&rect.right);
  1184. pRect->get_top(&rect.top);
  1185. pRect->get_bottom(&rect.bottom);
  1186. rect.top -= 2*MP_MIN_CY;
  1187. if (rect.top < 0)
  1188. rect.top = 0;
  1189. rect.left -= 2*MP_MIN_CX;
  1190. if (rect.left <0)
  1191. rect.left = 0;
  1192. rect.bottom *= 2; rect.right *= 2;
  1193. RedrawWindow(m_Hwnd, &rect, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
  1194. CleanUp:
  1195. SAFERELEASE(pRect);
  1196. SAFERELEASE(pEle2);
  1197. }
  1198. break;
  1199. }
  1200. TraceMsg(TF_MYPICS, "-CMyPics::HandleScroll, this=%p, m_hoverState=%d", this, m_hoverState);
  1201. return hr;
  1202. }
  1203. HRESULT CMyPics::HandleMouseover(IHTMLElement *pEle)
  1204. {
  1205. HRESULT hr = S_OK;
  1206. IOleWindow *pOleWindow;
  1207. TraceMsg(TF_MYPICS, "+CMyPics::HandleMouseover");
  1208. if (m_hoverState != HOVERSTATE_HIDING)
  1209. {
  1210. // Ensure we really have a hover window
  1211. ASSERT(m_hWndHover);
  1212. return (S_OK);
  1213. }
  1214. else
  1215. {
  1216. // No bar. Release current element, if any.
  1217. ATOMICRELEASE(m_pEleCurr);
  1218. if (ShouldAppearOnThisElement(pEle))
  1219. {
  1220. m_pEleCurr = pEle;
  1221. pEle->AddRef();
  1222. // set m_Hwnd once...
  1223. if (!m_Hwnd)
  1224. {
  1225. // Get the Hwnd for the document...
  1226. hr = m_pDoc2->QueryInterface(IID_IOleWindow,(void **)&pOleWindow);
  1227. if (FAILED(hr))
  1228. return hr;
  1229. pOleWindow->GetWindow(&m_Hwnd);
  1230. pOleWindow->Release();
  1231. }
  1232. if (!m_hWndHover)
  1233. {
  1234. // We need a hover window now to conveniently set a timer.
  1235. hr = CreateHover(); // review: do we need to pass member variables as params?
  1236. }
  1237. // We're all set up. Set the state and start the timer.
  1238. m_hoverState=HOVERSTATE_WAITINGTOSHOW;
  1239. SetTimer(m_hWndMyPicsToolBar, IDT_MP_TIMER, MP_TIMER, s_TimerProc);
  1240. }
  1241. }
  1242. TraceMsg(TF_MYPICS, "-CMyPics::HandleMouseover");
  1243. return hr;
  1244. }
  1245. HRESULT CMyPics::HandleMouseout()
  1246. {
  1247. TraceMsg(TF_MYPICS, "+CMyPics::HandleMouseout");
  1248. switch(m_hoverState)
  1249. {
  1250. case HOVERSTATE_HIDING:
  1251. // Nothing to do
  1252. break;
  1253. case HOVERSTATE_SHOWING:
  1254. // Hide it
  1255. HideHover();
  1256. break;
  1257. case HOVERSTATE_LOCKED:
  1258. // Noop
  1259. break;
  1260. case HOVERSTATE_WAITINGTOSHOW:
  1261. m_hoverState = HOVERSTATE_HIDING;
  1262. KillTimer(m_hWndMyPicsToolBar, IDT_MP_TIMER);
  1263. break;
  1264. }
  1265. TraceMsg(TF_MYPICS, "-CMyPics::HandleMouseout");
  1266. return S_OK;
  1267. }
  1268. HRESULT CMyPics::HandleResize()
  1269. {
  1270. HRESULT hr = S_OK;
  1271. if (m_pEleCurr && (HOVERSTATE_SHOWING == m_hoverState))
  1272. {
  1273. HideHover();
  1274. ShowHover();
  1275. }
  1276. return hr;
  1277. }
  1278. HRESULT CMyPics::HandleEvent(IHTMLElement *pEle, EVENTS Event, IHTMLEventObj *pEventObj)
  1279. {
  1280. TraceMsg(TF_MYPICS, "CMyPics::HandleEvent Event=%ws", EventsToSink[Event].pwszEventName);
  1281. HRESULT hr = S_OK;
  1282. BSTR bstrTagName = NULL;
  1283. IHTMLElement *pEleUse = NULL;
  1284. BOOL fWasArea = FALSE;
  1285. // if this is an area tag we need to find the IMG tag that corresponds
  1286. if (pEle && SUCCEEDED(pEle->get_tagName(&bstrTagName)))
  1287. {
  1288. // if its an area tag, we need to find the img tag associated with it...
  1289. if (StrCmpNI(bstrTagName, TEXT("area"), 4)==0)
  1290. {
  1291. POINT ptEvent;
  1292. if (FAILED(pEventObj->get_screenX(&ptEvent.x)) ||
  1293. FAILED(pEventObj->get_screenY(&ptEvent.y)))
  1294. {
  1295. hr = E_FAIL;
  1296. goto Cleanup;
  1297. }
  1298. fWasArea = TRUE;
  1299. pEleUse = GetIMGFromArea(pEle, ptEvent);
  1300. }
  1301. }
  1302. // has the user turned this off?
  1303. if (m_bIsOffForSession)
  1304. goto Cleanup;
  1305. switch(Event)
  1306. {
  1307. case EVENT_SCROLL:
  1308. HandleScroll();
  1309. break;
  1310. case EVENT_MOUSEOVER:
  1311. hr = HandleMouseover(fWasArea ? pEleUse : pEle);
  1312. break;
  1313. case EVENT_MOUSEOUT:
  1314. hr = HandleMouseout();
  1315. break;
  1316. case EVENT_RESIZE:
  1317. hr = HandleResize();
  1318. break;
  1319. default:
  1320. //do nothing?
  1321. break;
  1322. }
  1323. Cleanup:
  1324. if (pEleUse)
  1325. ATOMICRELEASE(pEleUse);
  1326. if (bstrTagName)
  1327. SysFreeString(bstrTagName);
  1328. return (hr);
  1329. }
  1330. ////////////////////////////////////////////////////////////////////////////////////////////////
  1331. ////////////////////////////////////////////////////////////////////////////////////////////////
  1332. // this is stolen from iforms.cpp:
  1333. //=========================================================================
  1334. //
  1335. // Event sinking class
  1336. //
  1337. // We simply implement IDispatch and make a call into our parent when
  1338. // we receive a sinked event.
  1339. //
  1340. //=========================================================================
  1341. CMyPics::CEventSink::CEventSink(CMyPicsEventSinkCallback *pParent)
  1342. {
  1343. TraceMsg(TF_MYPICS, "CMyPics::CEventSink::CEventSink");
  1344. DllAddRef();
  1345. m_cRef = 1;
  1346. m_pParent = pParent;
  1347. }
  1348. CMyPics::CEventSink::~CEventSink()
  1349. {
  1350. TraceMsg(TF_MYPICS, "CMyPics::CEventSink::~CEventSink");
  1351. ASSERT( m_cRef == 0 );
  1352. DllRelease();
  1353. }
  1354. STDMETHODIMP CMyPics::CEventSink::QueryInterface(REFIID riid, void **ppv)
  1355. {
  1356. *ppv = NULL;
  1357. if ((IID_IDispatch == riid) ||
  1358. (IID_IUnknown == riid))
  1359. {
  1360. *ppv = (IDispatch *)this;
  1361. }
  1362. if (NULL != *ppv)
  1363. {
  1364. ((IUnknown *)*ppv)->AddRef();
  1365. return S_OK;
  1366. }
  1367. return E_NOINTERFACE;
  1368. }
  1369. STDMETHODIMP_(ULONG) CMyPics::CEventSink::AddRef(void)
  1370. {
  1371. return ++m_cRef;
  1372. }
  1373. STDMETHODIMP_(ULONG) CMyPics::CEventSink::Release(void)
  1374. {
  1375. if (--m_cRef == 0)
  1376. {
  1377. delete this;
  1378. return 0;
  1379. }
  1380. return m_cRef;
  1381. }
  1382. HRESULT CMyPics::CEventSink::SinkEvents(IHTMLElement2 *pEle2, int iNum, EVENTS *pEvents)
  1383. {
  1384. VARIANT_BOOL bSuccess = VARIANT_TRUE;
  1385. for (int i=0; i<iNum; i++)
  1386. {
  1387. BSTR bstrEvent = SysAllocString(CMyPicsEventSinkCallback::EventsToSink[(int)(pEvents[i])].pwszEventSubscribe);
  1388. if (bstrEvent)
  1389. {
  1390. pEle2->attachEvent(bstrEvent, (IDispatch *)this, &bSuccess);
  1391. SysFreeString(bstrEvent);
  1392. }
  1393. else
  1394. {
  1395. bSuccess = VARIANT_FALSE;
  1396. }
  1397. if (!bSuccess)
  1398. break;
  1399. }
  1400. return (bSuccess) ? S_OK : E_FAIL;
  1401. }
  1402. HRESULT CMyPics::CEventSink::SinkEvents(IHTMLWindow3 *pWin3, int iNum, EVENTS *pEvents)
  1403. {
  1404. VARIANT_BOOL bSuccess = VARIANT_TRUE;
  1405. for (int i=0; i<iNum; i++)
  1406. {
  1407. BSTR bstrEvent = SysAllocString(CMyPicsEventSinkCallback::EventsToSink[(int)(pEvents[i])].pwszEventSubscribe);
  1408. if (bstrEvent)
  1409. {
  1410. pWin3->attachEvent(bstrEvent, (IDispatch *)this, &bSuccess);
  1411. SysFreeString(bstrEvent);
  1412. }
  1413. else
  1414. {
  1415. bSuccess = VARIANT_FALSE;
  1416. }
  1417. if (!bSuccess)
  1418. break;
  1419. }
  1420. return (bSuccess) ? S_OK : E_FAIL;
  1421. }
  1422. HRESULT CMyPics::CEventSink::UnSinkEvents(IHTMLElement2 *pEle2, int iNum, EVENTS *pEvents)
  1423. {
  1424. for (int i=0; i<iNum; i++)
  1425. {
  1426. BSTR bstrEvent = SysAllocString(CMyPicsEventSinkCallback::EventsToSink[(int)(pEvents[i])].pwszEventSubscribe);
  1427. if (bstrEvent)
  1428. {
  1429. pEle2->detachEvent(bstrEvent, (IDispatch *)this);
  1430. SysFreeString(bstrEvent);
  1431. }
  1432. }
  1433. return S_OK;
  1434. }
  1435. HRESULT CMyPics::CEventSink::UnSinkEvents(IHTMLWindow3 *pWin3, int iNum, EVENTS *pEvents)
  1436. {
  1437. for (int i=0; i<iNum; i++)
  1438. {
  1439. BSTR bstrEvent = SysAllocString(CMyPicsEventSinkCallback::EventsToSink[(int)(pEvents[i])].pwszEventSubscribe);
  1440. if (bstrEvent)
  1441. {
  1442. pWin3->detachEvent(bstrEvent, (IDispatch *)this);
  1443. SysFreeString(bstrEvent);
  1444. }
  1445. }
  1446. return S_OK;
  1447. }
  1448. // IDispatch
  1449. STDMETHODIMP CMyPics::CEventSink::GetTypeInfoCount(UINT* /*pctinfo*/)
  1450. {
  1451. return E_NOTIMPL;
  1452. }
  1453. STDMETHODIMP CMyPics::CEventSink::GetTypeInfo(/* [in] */ UINT /*iTInfo*/,
  1454. /* [in] */ LCID /*lcid*/,
  1455. /* [out] */ ITypeInfo** /*ppTInfo*/)
  1456. {
  1457. return E_NOTIMPL;
  1458. }
  1459. STDMETHODIMP CMyPics::CEventSink::GetIDsOfNames(
  1460. REFIID riid,
  1461. OLECHAR** rgszNames,
  1462. UINT cNames,
  1463. LCID lcid,
  1464. DISPID* rgDispId)
  1465. {
  1466. return E_NOTIMPL;
  1467. }
  1468. STDMETHODIMP CMyPics::CEventSink::Invoke(
  1469. DISPID dispIdMember,
  1470. REFIID, LCID,
  1471. WORD wFlags,
  1472. DISPPARAMS* pDispParams,
  1473. VARIANT* pVarResult,
  1474. EXCEPINFO*,
  1475. UINT* puArgErr)
  1476. {
  1477. if (m_pParent && pDispParams && pDispParams->cArgs>=1)
  1478. {
  1479. if (pDispParams->rgvarg[0].vt == VT_DISPATCH)
  1480. {
  1481. IHTMLEventObj *pObj=NULL;
  1482. if (SUCCEEDED(pDispParams->rgvarg[0].pdispVal->QueryInterface(IID_IHTMLEventObj, (void **)&pObj) && pObj))
  1483. {
  1484. EVENTS Event=EVENT_BOGUS;
  1485. BSTR bstrEvent=NULL;
  1486. pObj->get_type(&bstrEvent);
  1487. if (bstrEvent)
  1488. {
  1489. for (int i=0; i<ARRAYSIZE(CMyPicsEventSinkCallback::EventsToSink); i++)
  1490. {
  1491. if (!StrCmpCW(bstrEvent, CMyPicsEventSinkCallback::EventsToSink[i].pwszEventName))
  1492. {
  1493. Event = (EVENTS) i;
  1494. break;
  1495. }
  1496. }
  1497. SysFreeString(bstrEvent);
  1498. }
  1499. if (Event != EVENT_BOGUS)
  1500. {
  1501. IHTMLElement *pEle=NULL;
  1502. pObj->get_srcElement(&pEle);
  1503. // EVENT_SCROLL comes from our window so we won't have an
  1504. // element for it
  1505. if (pEle || (Event == EVENT_SCROLL))
  1506. {
  1507. // Call the event handler here
  1508. m_pParent->HandleEvent(pEle, Event, pObj);
  1509. if (pEle)
  1510. {
  1511. pEle->Release();
  1512. }
  1513. }
  1514. }
  1515. pObj->Release();
  1516. }
  1517. }
  1518. }
  1519. return S_OK;
  1520. }
  1521. //////////////////////////////////////////////////////////////////////////////
  1522. // {9E56BE60-C50F-11CF-9A2C-00A0C90A90CE}
  1523. EXTERN_C const GUID MP_CLSID_MailRecipient = {0x9E56BE60L, 0xC50F, 0x11CF, 0x9A, 0x2C, 0x00, 0xA0, 0xC9, 0x0A, 0x90, 0xCE};
  1524. HRESULT DropPicOnMailRecipient(IDataObject *pdtobj, DWORD grfKeyState)
  1525. {
  1526. IDropTarget *pdrop;
  1527. HRESULT hres = CoCreateInstance(MP_CLSID_MailRecipient,
  1528. NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
  1529. IID_PPV_ARG(IDropTarget, &pdrop));
  1530. if (SUCCEEDED(hres))
  1531. {
  1532. hres = SHSimulateDrop(pdrop, pdtobj, grfKeyState, NULL, NULL);
  1533. pdrop->Release();
  1534. }
  1535. return hres;
  1536. }
  1537. //
  1538. // This function cannot return Non -NULL pointers if
  1539. // it returns a FAILED(hr)
  1540. //
  1541. HRESULT CreateShortcutSetSiteAndGetDataObjectIfPIDLIsNetUrl(
  1542. LPCITEMIDLIST pidl,
  1543. IUnknown *pUnkSite,
  1544. IUniformResourceLocator **ppUrlOut,
  1545. IDataObject **ppdtobj
  1546. )
  1547. {
  1548. HRESULT hr;
  1549. TCHAR szUrl[MAX_URL_STRING];
  1550. TCHAR *szTemp = NULL;
  1551. ASSERT(ppUrlOut);
  1552. ASSERT(ppdtobj);
  1553. *ppUrlOut = NULL;
  1554. *ppdtobj = NULL;
  1555. szUrl[0] = TEXT('\0');
  1556. hr = IEGetNameAndFlags(pidl, SHGDN_FORPARSING, szUrl, SIZECHARS(szUrl), NULL);
  1557. if ((S_OK == hr) && (*szUrl))
  1558. {
  1559. BOOL fIsHTML = FALSE;
  1560. BOOL fHitsNet = UrlHitsNetW(szUrl);
  1561. if (!fHitsNet)
  1562. {
  1563. if (URL_SCHEME_FILE == GetUrlScheme(szUrl))
  1564. {
  1565. TCHAR *szExt = PathFindExtension(szUrl);
  1566. if (szExt)
  1567. {
  1568. fIsHTML = ((0 == StrCmpNI(szExt, TEXT(".htm"),4)) ||
  1569. (0 == StrCmpNI(szExt, TEXT(".html"),5)));
  1570. }
  1571. }
  1572. }
  1573. if (fHitsNet || fIsHTML)
  1574. {
  1575. // Create a shortcut object and
  1576. HRESULT hr = CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER,
  1577. IID_PPV_ARG(IUniformResourceLocator, ppUrlOut));
  1578. if (SUCCEEDED(hr))
  1579. {
  1580. hr = (*ppUrlOut)->SetURL(szUrl, 0);
  1581. if (S_OK == hr)
  1582. {
  1583. // Get the IDataObject and send that back for the Drag Drop
  1584. hr = (*ppUrlOut)->QueryInterface(IID_PPV_ARG(IDataObject, ppdtobj));
  1585. if (SUCCEEDED(hr))
  1586. {
  1587. IUnknown_SetSite(*ppUrlOut, pUnkSite); // Only set the site if we're sure of
  1588. // returning SUCCESS
  1589. }
  1590. }
  1591. }
  1592. }
  1593. else
  1594. {
  1595. hr = E_FAIL;
  1596. }
  1597. }
  1598. if (FAILED(hr))
  1599. {
  1600. SAFERELEASE(*ppUrlOut);
  1601. SAFERELEASE(*ppdtobj);
  1602. }
  1603. return hr;
  1604. }
  1605. HRESULT SendDocToMailRecipient(LPCITEMIDLIST pidl, UINT uiCodePage, DWORD grfKeyState, IUnknown *pUnkSite)
  1606. {
  1607. IDataObject *pdtobj = NULL;
  1608. IUniformResourceLocator *purl = NULL;
  1609. HRESULT hr = CreateShortcutSetSiteAndGetDataObjectIfPIDLIsNetUrl(pidl, pUnkSite, &purl, &pdtobj);
  1610. if (FAILED(hr))
  1611. {
  1612. ASSERT(NULL == pdtobj);
  1613. ASSERT(NULL == purl);
  1614. hr = GetDataObjectForPidl(pidl, &pdtobj);
  1615. }
  1616. if (SUCCEEDED(hr))
  1617. {
  1618. IQueryCodePage * pQcp;
  1619. if (SUCCEEDED(pdtobj->QueryInterface(IID_PPV_ARG(IQueryCodePage, &pQcp))))
  1620. {
  1621. pQcp->SetCodePage(uiCodePage);
  1622. pQcp->Release();
  1623. }
  1624. hr = DropPicOnMailRecipient(pdtobj, grfKeyState);
  1625. pdtobj->Release();
  1626. }
  1627. if (purl)
  1628. {
  1629. IUnknown_SetSite(purl, NULL);
  1630. purl->Release();
  1631. }
  1632. return hr;
  1633. }
  1634. //////////////////////////////////////////////////////////////////////////////
  1635. #undef TF_MYPICS