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.

996 lines
29 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 2000
  6. //
  7. // File: cplnkele.cpp
  8. //
  9. // This module implements a 'link' element in the Control Panel's DUI
  10. // view. Link elements have a title, infotip, icon and an associated
  11. // command that is invoked when the link is selected. The CLinkElement
  12. // object is an extension of the DUI::Button class. Direct UI automatically
  13. // creates an instance of CLinkElement when a 'linkelement' item from
  14. // cpview.ui is instantiated.
  15. //
  16. //--------------------------------------------------------------------------
  17. #include "shellprv.h"
  18. #include "cpviewp.h"
  19. #include "cpaction.h"
  20. #include "cpduihlp.h"
  21. #include "cpguids.h"
  22. #include "cpuiele.h"
  23. #include "cplnkele.h"
  24. #include "cputil.h"
  25. #include "defviewp.h"
  26. #include "dobjutil.h"
  27. #include "ids.h"
  28. using namespace CPL;
  29. CLinkElement::CLinkElement(
  30. void
  31. ) : m_pUiCommand(NULL),
  32. m_eIconSize(eCPIMGSIZE(-1)),
  33. m_hwndInfotip(NULL),
  34. m_idTitle(0),
  35. m_idIcon(0),
  36. m_iDragState(DRAG_IDLE)
  37. {
  38. TraceMsg(TF_LIFE, "CLinkElement::CLinkElement, this = 0x%x", this);
  39. SetRect(&m_rcDragBegin, 0, 0, 0, 0);
  40. }
  41. CLinkElement::~CLinkElement(
  42. void
  43. )
  44. {
  45. TraceMsg(TF_LIFE, "CLinkElement::~CLinkElement, this = 0x%x", this);
  46. _Destroy();
  47. }
  48. //
  49. // This is called by the DUI engine when the link element is
  50. // created.
  51. //
  52. HRESULT
  53. CLinkElement::Create( // [static]
  54. DUI::Element **ppElement
  55. )
  56. {
  57. HRESULT hr = E_OUTOFMEMORY;
  58. CLinkElement *ple = DUI::HNewAndZero<CLinkElement>();
  59. if (NULL != ple)
  60. {
  61. hr = ple->_Initialize();
  62. if (FAILED(hr))
  63. {
  64. ple->Destroy();
  65. ple = NULL;
  66. }
  67. }
  68. *ppElement = ple;
  69. return THR(hr);
  70. }
  71. //
  72. // This is called by the Control Panel UI code creating
  73. // the link element.
  74. //
  75. HRESULT
  76. CLinkElement::Initialize(
  77. IUICommand *pUiCommand,
  78. eCPIMGSIZE eIconSize
  79. )
  80. {
  81. ASSERT(NULL == m_pUiCommand);
  82. ASSERT(NULL != pUiCommand);
  83. (m_pUiCommand = pUiCommand)->AddRef();
  84. m_eIconSize = eIconSize;
  85. HRESULT hr = _CreateElementTitle();
  86. if (SUCCEEDED(hr))
  87. {
  88. //
  89. // We don't fail element creation if the icon
  90. // cannot be created. We want to display the
  91. // title without an icon so that we know there's
  92. // a problem retrieving the icon.
  93. //
  94. THR(_CreateElementIcon());
  95. }
  96. //
  97. // Note that we don't fail element creation if accessibility
  98. // initialization fails.
  99. //
  100. THR(_InitializeAccessibility());
  101. if (FAILED(hr))
  102. {
  103. ATOMICRELEASE(m_pUiCommand);
  104. }
  105. return THR(hr);
  106. }
  107. HRESULT
  108. CLinkElement::_InitializeAccessibility(
  109. void
  110. )
  111. {
  112. HRESULT hr = THR(SetAccessible(true));
  113. if (SUCCEEDED(hr))
  114. {
  115. hr = THR(SetAccRole(ROLE_SYSTEM_LINK));
  116. if (SUCCEEDED(hr))
  117. {
  118. LPWSTR pszTitle;
  119. hr = THR(_GetTitleText(&pszTitle));
  120. if (SUCCEEDED(hr))
  121. {
  122. hr = THR(SetAccName(pszTitle));
  123. CoTaskMemFree(pszTitle);
  124. pszTitle = NULL;
  125. if (SUCCEEDED(hr))
  126. {
  127. LPWSTR pszInfotip;
  128. hr = THR(_GetInfotipText(&pszInfotip));
  129. if (SUCCEEDED(hr))
  130. {
  131. hr = THR(SetAccDesc(pszInfotip));
  132. CoTaskMemFree(pszInfotip);
  133. pszInfotip = NULL;
  134. if (SUCCEEDED(hr))
  135. {
  136. TCHAR szDefAction[80];
  137. if (0 < LoadString(HINST_THISDLL,
  138. IDS_CP_LINK_ACCDEFACTION,
  139. szDefAction,
  140. ARRAYSIZE(szDefAction)))
  141. {
  142. hr = THR(SetAccDefAction(szDefAction));
  143. }
  144. else
  145. {
  146. hr = THR(ResultFromLastError());
  147. }
  148. }
  149. }
  150. }
  151. }
  152. }
  153. }
  154. return THR(hr);
  155. }
  156. void
  157. CLinkElement::OnDestroy(
  158. void
  159. )
  160. {
  161. _Destroy();
  162. DUI::Button::OnDestroy();
  163. }
  164. void
  165. CLinkElement::OnInput(
  166. DUI::InputEvent *pev
  167. )
  168. {
  169. if (GINPUT_MOUSE == pev->nDevice)
  170. {
  171. //
  172. // Use a set of states to control our handling of
  173. // the mouse inputs for drag/drop.
  174. //
  175. // DRAG_IDLE - We have not yet detected any drag activity.
  176. // DRAG_HITTESTING - Waiting to see if user drags cursor a minimum distance.
  177. // DRAG_DRAGGING - User did drag cursor a minimum distance and we're now
  178. // inside the drag loop.
  179. //
  180. //
  181. // START -+-> DRAG_IDLE --> [ GMOUSE_DRAG ] --> DRAG_HITTESTING --+
  182. // | |
  183. // | [ GMOUSE_DRAG + |
  184. // | moved SM_CXDRAG |
  185. // | or SM_CYDRAG ] |
  186. // | |
  187. // +-<--------------- [ GMOUSE_UP ] <--- DRAG_DRAGGING <---+
  188. //
  189. DUI::MouseEvent *pmev = (DUI::MouseEvent *)pev;
  190. switch(pev->nCode)
  191. {
  192. case GMOUSE_UP:
  193. m_iDragState = DRAG_IDLE;
  194. break;
  195. case GMOUSE_DRAG:
  196. switch(m_iDragState)
  197. {
  198. case DRAG_IDLE:
  199. {
  200. //
  201. // This is the same way comctl's listview calculates
  202. // the begin-drag rect.
  203. //
  204. int dxClickRect = GetSystemMetrics(SM_CXDRAG);
  205. int dyClickRect = GetSystemMetrics(SM_CYDRAG);
  206. if (4 > dxClickRect)
  207. {
  208. dxClickRect = dyClickRect = 4;
  209. }
  210. //
  211. // Remember where the mouse pointer is on our first
  212. // indication that a drag operation is starting.
  213. //
  214. SetRect(&m_rcDragBegin,
  215. pmev->ptClientPxl.x - dxClickRect,
  216. pmev->ptClientPxl.y - dyClickRect,
  217. pmev->ptClientPxl.x + dxClickRect,
  218. pmev->ptClientPxl.y + dyClickRect);
  219. m_iDragState = DRAG_HITTESTING;
  220. break;
  221. }
  222. case DRAG_HITTESTING:
  223. if (!PtInRect(&m_rcDragBegin, pmev->ptClientPxl))
  224. {
  225. //
  226. // Begin the drag/drop operation only if we've moved the mouse
  227. // outside the "drag begin" rectangle. This prevents us from
  228. // confusing a normal click with a drag/drop operation.
  229. //
  230. m_iDragState = DRAG_DRAGGING;
  231. //
  232. // Position the drag point at the middle of the item's image.
  233. //
  234. UINT cxIcon = 32;
  235. UINT cyIcon = 32;
  236. CPL::ImageDimensionsFromDesiredSize(m_eIconSize, &cxIcon, &cyIcon);
  237. _BeginDrag(cxIcon / 2, cyIcon / 2);
  238. }
  239. break;
  240. case DRAG_DRAGGING:
  241. break;
  242. }
  243. break;
  244. default:
  245. break;
  246. }
  247. }
  248. Button::OnInput(pev);
  249. }
  250. void
  251. CLinkElement::OnEvent(
  252. DUI::Event *pev
  253. )
  254. {
  255. if (DUI::Button::Click == pev->uidType)
  256. {
  257. pev->fHandled = true;
  258. DUI::ButtonClickEvent * pbe = (DUI::ButtonClickEvent *) pev;
  259. if (1 != pbe->nCount)
  260. {
  261. return; // ingore additional clicks - don't forward.
  262. }
  263. _OnSelected();
  264. }
  265. else if (DUI::Button::Context == pev->uidType)
  266. {
  267. DUI::ButtonContextEvent *peButton = reinterpret_cast<DUI::ButtonContextEvent *>(pev);
  268. _OnContextMenu(peButton);
  269. pev->fHandled = true;
  270. }
  271. Button::OnEvent(pev);
  272. }
  273. void
  274. CLinkElement::OnPropertyChanged(
  275. DUI::PropertyInfo *ppi,
  276. int iIndex,
  277. DUI::Value *pvOld,
  278. DUI::Value *pvNew
  279. )
  280. {
  281. //
  282. // Don't trace this function. It's called very often.
  283. //
  284. // Perform default processing.
  285. //
  286. Button::OnPropertyChanged(ppi, iIndex, pvOld, pvNew);
  287. if (IsProp(MouseWithin))
  288. {
  289. _OnMouseOver(pvNew);
  290. }
  291. }
  292. //
  293. // Called to begin a drag-drop operation from the control panel.
  294. // This is used for dragging CPL applet icons to shell folders
  295. // for shortcut creation.
  296. //
  297. HRESULT
  298. CLinkElement::_BeginDrag(
  299. int iClickPosX,
  300. int iClickPosY
  301. )
  302. {
  303. DBG_ENTER(FTF_CPANEL, "CLinkElement::_BeginDrag");
  304. HRESULT hr = E_FAIL;
  305. HRESULT hrCoInit = SHCoInitialize();
  306. if (SUCCEEDED(hrCoInit))
  307. {
  308. hr = hrCoInit;
  309. IDataObject *pdtobj;
  310. hr = _GetDragDropData(&pdtobj);
  311. if (SUCCEEDED(hr))
  312. {
  313. //
  314. // Ignore any failure to set the drag image. Drag images
  315. // are not supported on some video configurations.
  316. // In these cases, we still want to be able to create a shortcut.
  317. //
  318. THR(_SetDragImage(pdtobj, iClickPosX, iClickPosY));
  319. HWND hwndRoot;
  320. hr = THR(Dui_GetElementRootHWND(this, &hwndRoot));
  321. if (SUCCEEDED(hr))
  322. {
  323. DWORD dwEffect = DROPEFFECT_LINK;
  324. hr = THR(SHDoDragDrop(hwndRoot, pdtobj, NULL, dwEffect, &dwEffect));
  325. }
  326. pdtobj->Release();
  327. }
  328. SHCoUninitialize(hrCoInit);
  329. }
  330. DBG_EXIT_HRES(FTF_CPANEL, "CLinkElement::_BeginDrag", hr);
  331. return THR(hr);
  332. }
  333. //
  334. // Get and prepare the data object used in a drag-drop operation.
  335. // The returned data object is suitable for use by SHDoDragDrop.
  336. //
  337. HRESULT
  338. CLinkElement::_GetDragDropData(
  339. IDataObject **ppdtobj
  340. )
  341. {
  342. DBG_ENTER(FTF_CPANEL, "CLinkElement::_GetDragDropData");
  343. ASSERT(NULL != ppdtobj);
  344. ASSERT(!IsBadWritePtr(ppdtobj, sizeof(*ppdtobj)));
  345. ASSERT(NULL != m_pUiCommand);
  346. *ppdtobj = NULL;
  347. ICpUiCommand *puic;
  348. HRESULT hr = m_pUiCommand->QueryInterface(IID_PPV_ARG(ICpUiCommand, &puic));
  349. if (SUCCEEDED(hr))
  350. {
  351. //
  352. // Note that this call will fail with E_NOTIMPL for links that don't
  353. // provide drag-drop data. Only CPL applet links provide data.
  354. // This is how we limit drag-drop to only CPL applets.
  355. //
  356. IDataObject *pdtobj;
  357. hr = THR(puic->GetDataObject(&pdtobj));
  358. if (SUCCEEDED(hr))
  359. {
  360. hr = _SetPreferredDropEffect(pdtobj, DROPEFFECT_LINK);
  361. if (SUCCEEDED(hr))
  362. {
  363. (*ppdtobj = pdtobj)->AddRef();
  364. }
  365. pdtobj->Release();
  366. }
  367. puic->Release();
  368. }
  369. DBG_EXIT_HRES(FTF_CPANEL, "CLinkElement::_GetDragDropData", hr);
  370. return THR(hr);
  371. }
  372. HRESULT
  373. CLinkElement::_SetPreferredDropEffect(
  374. IDataObject *pdtobj,
  375. DWORD dwEffect
  376. )
  377. {
  378. DBG_ENTER(FTF_CPANEL, "CLinkElement::_SetPreferredDropEffect");
  379. HRESULT hr = S_OK;
  380. static CLIPFORMAT cf;
  381. if ((CLIPFORMAT)0 == cf)
  382. {
  383. cf = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT);
  384. if ((CLIPFORMAT)0 == cf)
  385. {
  386. hr = THR(ResultFromLastError());
  387. }
  388. }
  389. if (SUCCEEDED(hr))
  390. {
  391. hr = THR(DataObj_SetDWORD(pdtobj, cf, dwEffect));
  392. }
  393. DBG_EXIT_HRES(FTF_CPANEL, "CLinkElement::_SetPreferredDropEffect", hr);
  394. return THR(hr);
  395. }
  396. //
  397. // Set up the drag image in the data object so that our icon is
  398. // displayed during the drag operation.
  399. //
  400. // I took this code from the old webvw project's fldricon.cpp
  401. // implementation (shell\ext\webvw\fldricon.cpp). It seems to
  402. // work just fine.
  403. //
  404. HRESULT
  405. CLinkElement::_SetDragImage(
  406. IDataObject *pdtobj,
  407. int iClickPosX,
  408. int iClickPosY
  409. )
  410. {
  411. DBG_ENTER(FTF_CPANEL, "CLinkElement::_SetDragImage");
  412. ASSERT(NULL != pdtobj);
  413. HRESULT hr = S_OK;
  414. HDC hdc = CreateCompatibleDC(NULL);
  415. if (NULL == hdc)
  416. {
  417. hr = THR(ResultFromLastError());
  418. }
  419. else
  420. {
  421. HBITMAP hbm;
  422. LONG lBitmapWidth;
  423. LONG lBitmapHeight;
  424. hr = _GetDragImageBitmap(&hbm, &lBitmapWidth, &lBitmapHeight);
  425. if (SUCCEEDED(hr))
  426. {
  427. IDragSourceHelper *pdsh;
  428. hr = CoCreateInstance(CLSID_DragDropHelper,
  429. NULL,
  430. CLSCTX_INPROC_SERVER,
  431. IID_PPV_ARG(IDragSourceHelper, &pdsh));
  432. if (SUCCEEDED(hr))
  433. {
  434. BITMAPINFOHEADER bmi = {0};
  435. BITMAP bm = {0};
  436. UINT uBufferOffset = 0;
  437. //
  438. // This is a screwy procedure to use GetDIBits.
  439. // See knowledge base Q80080
  440. //
  441. if (GetObject(hbm, sizeof(BITMAP), &bm))
  442. {
  443. bmi.biSize = sizeof(BITMAPINFOHEADER);
  444. bmi.biWidth = bm.bmWidth;
  445. bmi.biHeight = bm.bmHeight;
  446. bmi.biPlanes = 1;
  447. bmi.biBitCount = bm.bmPlanes * bm.bmBitsPixel;
  448. //
  449. // This needs to be one of these 4 values
  450. //
  451. if (bmi.biBitCount <= 1)
  452. bmi.biBitCount = 1;
  453. else if (bmi.biBitCount <= 4)
  454. bmi.biBitCount = 4;
  455. else if (bmi.biBitCount <= 8)
  456. bmi.biBitCount = 8;
  457. else
  458. bmi.biBitCount = 24;
  459. bmi.biCompression = BI_RGB;
  460. //
  461. // Total size of buffer for info struct and color table
  462. //
  463. uBufferOffset = sizeof(BITMAPINFOHEADER) +
  464. ((bmi.biBitCount == 24) ? 0 : ((1 << bmi.biBitCount) * sizeof(RGBQUAD)));
  465. //
  466. // Buffer for bitmap bits, so we can copy them.
  467. //
  468. BYTE *psBits = (BYTE *)SHAlloc(uBufferOffset);
  469. if (NULL == psBits)
  470. {
  471. hr = THR(E_OUTOFMEMORY);
  472. }
  473. else
  474. {
  475. //
  476. // Put bmi into the memory block
  477. //
  478. CopyMemory(psBits, &bmi, sizeof(BITMAPINFOHEADER));
  479. //
  480. // Get the size of the buffer needed for bitmap bits
  481. //
  482. if (!GetDIBits(hdc, hbm, 0, 0, NULL, (BITMAPINFO *) psBits, DIB_RGB_COLORS))
  483. {
  484. hr = THR(ResultFromLastError());
  485. }
  486. else
  487. {
  488. //
  489. // Realloc our buffer to be big enough
  490. //
  491. psBits = (BYTE *)SHRealloc(psBits, uBufferOffset + ((BITMAPINFOHEADER *) psBits)->biSizeImage);
  492. if (NULL == psBits)
  493. {
  494. hr = THR(E_OUTOFMEMORY);
  495. }
  496. else
  497. {
  498. //
  499. // Fill the buffer
  500. //
  501. if (!GetDIBits(hdc,
  502. hbm,
  503. 0,
  504. bmi.biHeight,
  505. (void *)(psBits + uBufferOffset),
  506. (BITMAPINFO *)psBits,
  507. DIB_RGB_COLORS))
  508. {
  509. hr = THR(ResultFromLastError());
  510. }
  511. else
  512. {
  513. SHDRAGIMAGE shdi; // Drag images struct
  514. shdi.hbmpDragImage = CreateBitmapIndirect(&bm);
  515. if (NULL == shdi.hbmpDragImage)
  516. {
  517. hr = THR(ResultFromLastError());
  518. }
  519. else
  520. {
  521. //
  522. // Set the drag image bitmap
  523. //
  524. if (SetDIBits(hdc,
  525. shdi.hbmpDragImage,
  526. 0,
  527. lBitmapHeight,
  528. (void *)(psBits + uBufferOffset),
  529. (BITMAPINFO *)psBits,
  530. DIB_RGB_COLORS))
  531. {
  532. //
  533. // Populate the drag image structure
  534. //
  535. shdi.sizeDragImage.cx = lBitmapWidth;
  536. shdi.sizeDragImage.cy = lBitmapHeight;
  537. shdi.ptOffset.x = iClickPosX;
  538. shdi.ptOffset.y = iClickPosY;
  539. shdi.crColorKey = 0;
  540. //
  541. // Set the drag image
  542. //
  543. hr = pdsh->InitializeFromBitmap(&shdi, pdtobj);
  544. }
  545. else
  546. {
  547. hr = THR(ResultFromLastError());
  548. }
  549. if (FAILED(hr))
  550. {
  551. DeleteObject(shdi.hbmpDragImage);
  552. }
  553. }
  554. }
  555. }
  556. }
  557. if (NULL != psBits)
  558. {
  559. SHFree(psBits);
  560. }
  561. }
  562. }
  563. pdsh->Release();
  564. }
  565. DeleteObject(hbm);
  566. }
  567. DeleteDC(hdc);
  568. }
  569. DBG_EXIT_HRES(FTF_CPANEL, "CLinkElement::_SetDragImage", hr);
  570. return THR(hr);
  571. }
  572. HRESULT
  573. CLinkElement::_GetDragImageBitmap(
  574. HBITMAP *phbm,
  575. LONG *plWidth,
  576. LONG *plHeight
  577. )
  578. {
  579. DBG_ENTER(FTF_CPANEL, "CLinkElement::_GetDragImageBitmap");
  580. ASSERT(NULL != phbm);
  581. ASSERT(!IsBadWritePtr(phbm, sizeof(*phbm)));
  582. ASSERT(NULL != plWidth);
  583. ASSERT(!IsBadWritePtr(plWidth, sizeof(*plWidth)));
  584. ASSERT(NULL != plHeight);
  585. ASSERT(!IsBadWritePtr(plHeight, sizeof(*plHeight)));
  586. *phbm = NULL;
  587. *plWidth = 0;
  588. *plHeight = 0;
  589. HICON hIcon;
  590. HRESULT hr = _GetElementIcon(&hIcon);
  591. if (SUCCEEDED(hr))
  592. {
  593. ICONINFO iconinfo;
  594. if (GetIconInfo(hIcon, &iconinfo))
  595. {
  596. BITMAP bm;
  597. if (GetObject(iconinfo.hbmColor, sizeof(bm), &bm))
  598. {
  599. *plWidth = bm.bmWidth;
  600. *plHeight = bm.bmHeight;
  601. *phbm = iconinfo.hbmColor;
  602. }
  603. else
  604. {
  605. DeleteObject(iconinfo.hbmColor);
  606. hr = THR(ResultFromLastError());
  607. }
  608. DeleteObject(iconinfo.hbmMask);
  609. }
  610. else
  611. {
  612. hr = THR(ResultFromLastError());
  613. }
  614. DestroyIcon(hIcon);
  615. }
  616. DBG_EXIT_HRES(FTF_CPANEL, "CLinkElement::_GetDragImageBitmap", hr);
  617. return THR(hr);
  618. }
  619. HRESULT
  620. CLinkElement::_Initialize(
  621. void
  622. )
  623. {
  624. HRESULT hr = Button::Initialize(AE_Mouse | AE_Keyboard);
  625. if (SUCCEEDED(hr))
  626. {
  627. hr = _AddOrDeleteAtoms(true);
  628. }
  629. return THR(hr);
  630. }
  631. void
  632. CLinkElement::_Destroy(
  633. void
  634. )
  635. {
  636. if (NULL != m_hwndInfotip && IsWindow(m_hwndInfotip))
  637. {
  638. SHDestroyInfotipWindow(&m_hwndInfotip);
  639. }
  640. ATOMICRELEASE(m_pUiCommand);
  641. _AddOrDeleteAtoms(false);
  642. }
  643. HRESULT
  644. CLinkElement::_AddOrDeleteAtoms(
  645. bool bAdd
  646. )
  647. {
  648. struct CPL::ATOMINFO rgAtomInfo[] = {
  649. { L"title", &m_idTitle },
  650. { L"icon", &m_idIcon },
  651. };
  652. HRESULT hr = Dui_AddOrDeleteAtoms(rgAtomInfo, ARRAYSIZE(rgAtomInfo), bAdd);
  653. return THR(hr);
  654. }
  655. HRESULT
  656. CLinkElement::_CreateElementTitle(
  657. void
  658. )
  659. {
  660. LPWSTR pszTitle;
  661. HRESULT hr = _GetTitleText(&pszTitle);
  662. if (SUCCEEDED(hr))
  663. {
  664. hr = Dui_SetDescendentElementText(this, L"title", pszTitle);
  665. CoTaskMemFree(pszTitle);
  666. }
  667. return THR(hr);
  668. }
  669. HRESULT
  670. CLinkElement::_CreateElementIcon(
  671. void
  672. )
  673. {
  674. HICON hIcon;
  675. HRESULT hr = _GetElementIcon(&hIcon);
  676. if (SUCCEEDED(hr))
  677. {
  678. hr = Dui_SetDescendentElementIcon(this, L"icon", hIcon);
  679. if (FAILED(hr))
  680. {
  681. DestroyIcon(hIcon);
  682. }
  683. }
  684. return THR(hr);
  685. }
  686. HRESULT
  687. CLinkElement::_GetElementIcon(
  688. HICON *phIcon
  689. )
  690. {
  691. ASSERT(NULL != phIcon);
  692. ASSERT(!IsBadWritePtr(phIcon, sizeof(*phIcon)));
  693. ASSERT(NULL != m_pUiCommand);
  694. *phIcon = NULL;
  695. ICpUiElementInfo *pei;
  696. HRESULT hr = m_pUiCommand->QueryInterface(IID_PPV_ARG(ICpUiElementInfo, &pei));
  697. if (SUCCEEDED(hr))
  698. {
  699. hr = pei->LoadIcon(m_eIconSize, phIcon);
  700. pei->Release();
  701. }
  702. return THR(hr);
  703. }
  704. HRESULT
  705. CLinkElement::_OnContextMenu(
  706. DUI::ButtonContextEvent *peButton
  707. )
  708. {
  709. DBG_ENTER(FTF_CPANEL, "CLinkElement::_OnContextMenu");
  710. ICpUiCommand *pcmd;
  711. HRESULT hr = m_pUiCommand->QueryInterface(IID_PPV_ARG(ICpUiCommand, &pcmd));
  712. if (SUCCEEDED(hr))
  713. {
  714. HWND hwndRoot;
  715. hr = Dui_GetElementRootHWND(this, &hwndRoot);
  716. if (SUCCEEDED(hr))
  717. {
  718. if (-1 == peButton->pt.x)
  719. {
  720. //
  721. // Keyboard context menu.
  722. //
  723. SIZE size;
  724. hr = Dui_GetElementExtent(this, &size);
  725. if (SUCCEEDED(hr))
  726. {
  727. peButton->pt.x = size.cx / 2;
  728. peButton->pt.y = size.cy / 2;
  729. }
  730. }
  731. POINT pt;
  732. hr = Dui_MapElementPointToRootHWND(this, peButton->pt, &pt);
  733. if (SUCCEEDED(hr))
  734. {
  735. if (ClientToScreen(hwndRoot, &pt))
  736. {
  737. //
  738. // InvokeContextMenu returns S_FALSE if the command doesn't
  739. // provide a context menu.
  740. //
  741. hr = pcmd->InvokeContextMenu(hwndRoot, &pt);
  742. }
  743. else
  744. {
  745. hr = ResultFromLastError();
  746. }
  747. }
  748. }
  749. pcmd->Release();
  750. }
  751. else if (E_NOINTERFACE == hr)
  752. {
  753. hr = S_FALSE;
  754. }
  755. DBG_EXIT_HRES(FTF_CPANEL, "CLinkElement::_OnContextMenu", hr);
  756. return THR(hr);
  757. }
  758. HRESULT
  759. CLinkElement::_OnSelected(
  760. void
  761. )
  762. {
  763. ASSERT(NULL != m_pUiCommand);
  764. //
  765. // Delay navigation until double-click time times out occurs.
  766. //
  767. // KB: gpease 05-APR-2001 Fix for Whistler Bug #338552 (and others)
  768. //
  769. // Delaying this prevents the "second click" from being applied
  770. // to the newly navigated frame. Previously, if there happen to
  771. // be a new link in the new frame at the same mouse point at
  772. // which the previous navigation occured, the new link would have
  773. // received the 2nd click and we'd navigate that link as well. This
  774. // causes the current frame to get the 2nd click which we ignore
  775. // since we only care about the single click (see OnEvent above).
  776. //
  777. HWND hwndRoot;
  778. HRESULT hr = Dui_GetElementRootHWND(this, &hwndRoot);
  779. if (SUCCEEDED(hr))
  780. {
  781. SendMessage(hwndRoot, WM_USER_DELAY_NAVIGATION, (WPARAM) NULL, (LPARAM) m_pUiCommand);
  782. }
  783. return THR(hr);
  784. }
  785. void
  786. CLinkElement::_OnMouseOver(
  787. DUI::Value *pvNewMouseWithin
  788. )
  789. {
  790. _ShowInfotipWindow(pvNewMouseWithin->GetBool());
  791. }
  792. //
  793. // Retrieve the title text for the element.
  794. // Caller must free returned buffer using CoTaskMemFree.
  795. //
  796. HRESULT
  797. CLinkElement::_GetTitleText(
  798. LPWSTR *ppszTitle
  799. )
  800. {
  801. ASSERT(NULL != m_pUiCommand);
  802. ASSERT(NULL != ppszTitle);
  803. ASSERT(!IsBadWritePtr(ppszTitle, sizeof(*ppszTitle)));
  804. *ppszTitle = NULL;
  805. ICpUiElementInfo *pei;
  806. HRESULT hr = m_pUiCommand->QueryInterface(IID_PPV_ARG(ICpUiElementInfo, &pei));
  807. if (SUCCEEDED(hr))
  808. {
  809. hr = pei->LoadName(ppszTitle);
  810. pei->Release();
  811. }
  812. return THR(hr);
  813. }
  814. //
  815. // Retrieve the infotip text for the element.
  816. // Caller must free returned buffer using CoTaskMemFree.
  817. //
  818. HRESULT
  819. CLinkElement::_GetInfotipText(
  820. LPWSTR *ppszInfotip
  821. )
  822. {
  823. ASSERT(NULL != m_pUiCommand);
  824. ASSERT(NULL != ppszInfotip);
  825. ASSERT(!IsBadWritePtr(ppszInfotip, sizeof(*ppszInfotip)));
  826. *ppszInfotip = NULL;
  827. ICpUiElementInfo *pei;
  828. HRESULT hr = m_pUiCommand->QueryInterface(IID_PPV_ARG(ICpUiElementInfo, &pei));
  829. if (SUCCEEDED(hr))
  830. {
  831. hr = pei->LoadTooltip(ppszInfotip);
  832. pei->Release();
  833. }
  834. return THR(hr);
  835. }
  836. HRESULT
  837. CLinkElement::_ShowInfotipWindow(
  838. bool bShow
  839. )
  840. {
  841. HRESULT hr = S_OK;
  842. if (bShow)
  843. {
  844. if (NULL == m_hwndInfotip)
  845. {
  846. HWND hwndRoot;
  847. hr = THR(Dui_GetElementRootHWND(this, &hwndRoot));
  848. if (SUCCEEDED(hr))
  849. {
  850. LPWSTR pszInfotip;
  851. hr = THR(_GetInfotipText(&pszInfotip));
  852. if (SUCCEEDED(hr))
  853. {
  854. hr = THR(SHCreateInfotipWindow(hwndRoot, pszInfotip, &m_hwndInfotip));
  855. CoTaskMemFree(pszInfotip);
  856. }
  857. }
  858. }
  859. if (SUCCEEDED(hr))
  860. {
  861. hr = THR(SHShowInfotipWindow(m_hwndInfotip, TRUE));
  862. }
  863. }
  864. else
  865. {
  866. if (NULL != m_hwndInfotip)
  867. {
  868. hr = THR(SHDestroyInfotipWindow(&m_hwndInfotip));
  869. }
  870. }
  871. return THR(hr);
  872. }
  873. //
  874. // ClassInfo (must appear after property definitions).
  875. //
  876. DUI::IClassInfo *CLinkElement::Class = NULL;
  877. HRESULT CLinkElement::Register()
  878. {
  879. return DUI::ClassInfo<CLinkElement,DUI::Button>::Register(L"linkelement", NULL, 0);
  880. }