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.

547 lines
14 KiB

  1. #include "shellprv.h"
  2. #include "ids.h"
  3. #include "duiview.h"
  4. #include "duisec.h"
  5. #include "duitask.h"
  6. ////////////////////////////////////////////////////////
  7. // Expando class
  8. ////////////////////////////////////////////////////////
  9. // Cached IDs
  10. ATOM Expando::idTitle = NULL;
  11. ATOM Expando::idIcon = NULL;
  12. ATOM Expando::idTaskList = NULL;
  13. ATOM Expando::idWatermark = NULL;
  14. HRESULT Expando::Create(OUT Element** ppElement)
  15. {
  16. *ppElement = NULL;
  17. Expando* pex = HNewAndZero<Expando>();
  18. if (!pex)
  19. return E_OUTOFMEMORY;
  20. HRESULT hr = pex->Initialize();
  21. if (FAILED(hr))
  22. {
  23. pex->Destroy();
  24. return hr;
  25. }
  26. *ppElement = pex;
  27. return S_OK;
  28. }
  29. Expando::Expando()
  30. {
  31. // Catch unexpected STACK allocations which would break us.
  32. ASSERT(_puiHeader == NULL);
  33. ASSERT(_pDUIView == NULL);
  34. ASSERT(_pDefView == NULL);
  35. // Initialize member variables.
  36. _eDUISecID = DUISEC_UNKNOWN;
  37. _bInfotip = FALSE;
  38. }
  39. Expando::~Expando()
  40. {
  41. DeleteAtom(idTitle);
  42. DeleteAtom(idIcon);
  43. DeleteAtom(idTaskList);
  44. DeleteAtom(idWatermark);
  45. if (_bInfotip)
  46. _pDefView->DestroyInfotip(_hwndRoot, (UINT_PTR)this);
  47. if (_puiHeader)
  48. _puiHeader->Release();
  49. if (_pDUIView)
  50. _pDUIView->Release();
  51. if (_pDefView)
  52. _pDefView->Release();
  53. }
  54. HRESULT Expando::Initialize()
  55. {
  56. HRESULT hr;
  57. // Initialize base
  58. hr = Element::Initialize(0); // Normal display node creation
  59. if (FAILED(hr))
  60. return hr;
  61. // Initialize
  62. _fExpanding = false;
  63. SetSelected(true);
  64. // Cache atoms used for loading from resources
  65. idTitle = AddAtomW(L"title");
  66. idIcon = AddAtomW(L"icon");
  67. idTaskList = AddAtomW(L"tasklist");
  68. idWatermark = AddAtomW(L"watermark");
  69. return S_OK;
  70. }
  71. void Expando::Initialize(DUISEC eDUISecID, IUIElement *puiHeader, CDUIView *pDUIView, CDefView *pDefView)
  72. {
  73. ASSERT(eDUISecID != DUISEC_UNKNOWN);
  74. ASSERT(pDUIView);
  75. ASSERT(pDefView);
  76. _eDUISecID = eDUISecID;
  77. _puiHeader = puiHeader;
  78. if (_puiHeader)
  79. _puiHeader->AddRef();
  80. pDUIView->AddRef();
  81. _pDUIView = pDUIView;
  82. pDefView->AddRef();
  83. _pDefView = pDefView;
  84. _SetAccStateInfo(TRUE);
  85. }
  86. HRESULT Expando::ShowInfotipWindow(Element *peHeader, BOOL bShow)
  87. {
  88. HRESULT hr;
  89. if (_puiHeader)
  90. {
  91. RECT rect = { 0 };
  92. if (bShow)
  93. {
  94. _pDUIView->CalculateInfotipRect(peHeader, &rect);
  95. if (_bInfotip)
  96. {
  97. // Reposition infotip at position.
  98. hr = _pDefView->RepositionInfotip(_hwndRoot, (UINT_PTR)this, &rect);
  99. }
  100. else
  101. {
  102. // Create infotip at position (on the ui thread).
  103. LPWSTR pwszInfotip;
  104. hr = _puiHeader->get_Tooltip(NULL, &pwszInfotip);
  105. if (SUCCEEDED(hr))
  106. {
  107. hr = GetElementRootHWND(this, &_hwndRoot);
  108. if (SUCCEEDED(hr))
  109. {
  110. hr = _pDefView->CreateInfotip(_hwndRoot, (UINT_PTR)this, &rect, pwszInfotip, 0);
  111. if (SUCCEEDED(hr))
  112. {
  113. _bInfotip = TRUE;
  114. }
  115. }
  116. CoTaskMemFree(pwszInfotip);
  117. }
  118. }
  119. }
  120. else
  121. {
  122. if (_bInfotip)
  123. {
  124. // Reposition infotip at nowhere.
  125. hr = _pDefView->RepositionInfotip(_hwndRoot, (UINT_PTR)this, &rect);
  126. }
  127. else
  128. {
  129. // No infotip == no show!
  130. hr = S_OK;
  131. }
  132. }
  133. }
  134. else
  135. {
  136. hr = E_NOTIMPL;
  137. }
  138. return hr;
  139. }
  140. void Expando::OnEvent(Event* pev)
  141. {
  142. if (pev->uidType == Button::Click)
  143. {
  144. // Update exanded property based on clicks that originate
  145. // only from the first child's subtree
  146. Value* pv;
  147. ElementList* peList = GetChildren(&pv);
  148. if (peList && peList->GetSize() > 0)
  149. {
  150. if (peList->GetItem(0) == GetImmediateChild(pev->peTarget))
  151. {
  152. SetSelected(!GetSelected());
  153. pev->fHandled = true;
  154. }
  155. }
  156. pv->Release();
  157. }
  158. Element::OnEvent(pev);
  159. }
  160. ////////////////////////////////////////////////////////
  161. // System events
  162. void Expando::OnPropertyChanged(PropertyInfo* ppi, int iIndex, Value* pvOld, Value* pvNew)
  163. {
  164. // Do default processing
  165. Element::OnPropertyChanged(ppi, iIndex, pvOld, pvNew);
  166. if (IsProp(Selected))
  167. {
  168. // Update height of second child based on expanded state
  169. Value* pvChildren;
  170. ElementList* peList = GetChildren(&pvChildren);
  171. if (peList && peList->GetSize() > 1)
  172. {
  173. // The following will cause a relayout, mark object so that
  174. // when the expando's Extent changes, it'll go through
  175. // with the EnsureVisible. Otherwise, it's being resized
  176. // as a result of something else. In which case, do nothing.
  177. _fExpanding = true;
  178. Element* pe = peList->GetItem(1);
  179. // To achieve "pulldown" animation, we use a clipper control that will
  180. // size it's child based on it's unconstrained desired size in its Y direction.
  181. //
  182. if (pvNew->GetBool())
  183. {
  184. pe->RemoveLocalValue(HeightProp);
  185. _pDUIView->OnExpandSection(_eDUISecID, TRUE);
  186. }
  187. else
  188. {
  189. pe->SetHeight(0);
  190. _pDUIView->OnExpandSection(_eDUISecID, FALSE);
  191. }
  192. }
  193. pvChildren->Release();
  194. _SetAccStateInfo(pvNew->GetBool());
  195. }
  196. else if (IsProp(Extent))
  197. {
  198. if (_fExpanding && GetSelected())
  199. {
  200. _fExpanding = false;
  201. // On extent, we want to ensure that not just the client area but
  202. // also the bottom margin of the expando is visible. Why? Simply
  203. // because it looks better to scroll the expando plus its margin
  204. // into view versus just the expando.
  205. //
  206. Value* pvSize;
  207. Value* pvMargin;
  208. const SIZE* psize = GetExtent(&pvSize);
  209. const RECT* prect = GetMargin(&pvMargin);
  210. EnsureVisible(0, 0, psize->cx, psize->cy + prect->bottom);
  211. pvSize->Release();
  212. pvMargin->Release();
  213. }
  214. }
  215. else if (IsProp(MouseWithin))
  216. {
  217. // Extended processing for infotip...
  218. Value* pvChildren;
  219. ElementList* peList = GetChildren(&pvChildren);
  220. if (peList && peList->GetSize() > 0 && pvNew->GetBool() && SHShowInfotips())
  221. {
  222. // ... only displays tip if mouse is within title of Expando.
  223. Element *peHeader = peList->GetItem(0);
  224. ShowInfotipWindow(peHeader, peHeader->GetMouseWithin());
  225. }
  226. else
  227. {
  228. ShowInfotipWindow(NULL, FALSE);
  229. }
  230. pvChildren->Release();
  231. }
  232. }
  233. ////////////////////////////////////////////////////////
  234. // Property definitions
  235. ////////////////////////////////////////////////////////
  236. // ClassInfo (must appear after property definitions)
  237. // Define class info with type and base type, set static class pointer
  238. IClassInfo* Expando::Class = NULL;
  239. HRESULT Expando::Register()
  240. {
  241. return ClassInfo<Expando,Element>::Register(L"Expando", NULL, 0);
  242. }
  243. void Expando::UpdateTitleUI(IShellItemArray *psiItemArray)
  244. {
  245. if (_puiHeader)
  246. {
  247. LPWSTR pszTitle;
  248. if (SUCCEEDED(_puiHeader->get_Name(psiItemArray, &pszTitle)))
  249. {
  250. Value* pv = Value::CreateString(pszTitle);
  251. if (pv)
  252. {
  253. Element* pe = FindDescendent(StrToID(L"header"));
  254. if (pe)
  255. {
  256. pe->SetAccessible(true);
  257. pe->SetAccRole(ROLE_SYSTEM_OUTLINEBUTTON);
  258. pe->SetValue (Element::AccNameProp, PI_Local, pv);
  259. }
  260. else
  261. {
  262. TraceMsg (TF_ERROR, "Expando::UpdateTitleUI: Button child for Expando not found.");
  263. }
  264. pe = FindDescendent (Expando::idTitle);
  265. if (pe)
  266. {
  267. pe->SetValue (Element::ContentProp, PI_Local, pv);
  268. }
  269. else
  270. {
  271. TraceMsg (TF_ERROR, "Expando::UpdateTitleUI: FindDescendent for the title failed.");
  272. }
  273. pv->Release ();
  274. }
  275. else
  276. {
  277. TraceMsg (TF_ERROR, "Expando::UpdateTitleUI: CreateString for the title failed.");
  278. }
  279. CoTaskMemFree(pszTitle);
  280. }
  281. else
  282. {
  283. TraceMsg (TF_ERROR, "Expando::UpdateTitleUI: get_Name failed.");
  284. }
  285. }
  286. }
  287. void Expando::ShowExpando(BOOL fShow)
  288. {
  289. if (fShow && (_fShow != TRIBIT_TRUE))
  290. {
  291. SetHeight(-1);
  292. RemoveLocalValue(MarginProp);
  293. _fShow = TRIBIT_TRUE;
  294. }
  295. if (!fShow && (_fShow != TRIBIT_FALSE))
  296. {
  297. SetHeight(0);
  298. SetMargin(0,0,0,0);
  299. _fShow = TRIBIT_FALSE;
  300. }
  301. }
  302. void Expando::_SetAccStateInfo (BOOL bExpanded)
  303. {
  304. // Update the accessibility state information
  305. //
  306. // Note: In the Expando::Initialize() method, we explicitly set the
  307. // Selected state to true. This causes OnPropertyChanged to be called
  308. // for the Selected property, which will call this method. However,
  309. // the child elements will not exist yet (since we are in the creation process).
  310. // Hence, the call to FindDescendent will return NULL and this method will exit.
  311. // This method is explicitly called in the second version of Initialze to
  312. // set the correct accessibility information.
  313. Element * pe = FindDescendent(StrToID(L"header"));
  314. if (pe)
  315. {
  316. TCHAR szDefaultAction[50] = {0};
  317. if (bExpanded)
  318. {
  319. pe->SetAccState(STATE_SYSTEM_EXPANDED);
  320. LoadString(HINST_THISDLL, IDS_EXPANDO_DEFAULT_ACTION_COLLAPSE, szDefaultAction, ARRAYSIZE(szDefaultAction));
  321. }
  322. else
  323. {
  324. pe->SetAccState(STATE_SYSTEM_COLLAPSED);
  325. LoadString(HINST_THISDLL, IDS_EXPANDO_DEFAULT_ACTION_EXPAND, szDefaultAction, ARRAYSIZE(szDefaultAction));
  326. }
  327. pe->SetAccDefAction(szDefaultAction);
  328. }
  329. }
  330. ////////////////////////////////////////////////////////
  331. // Clipper class
  332. ////////////////////////////////////////////////////////
  333. HRESULT Clipper::Create(OUT Element** ppElement)
  334. {
  335. *ppElement = NULL;
  336. Clipper* pc = HNewAndZero<Clipper>();
  337. if (!pc)
  338. return E_OUTOFMEMORY;
  339. HRESULT hr = pc->Initialize();
  340. if (FAILED(hr))
  341. {
  342. pc->Destroy();
  343. return hr;
  344. }
  345. *ppElement = pc;
  346. return S_OK;
  347. }
  348. HRESULT Clipper::Initialize()
  349. {
  350. // Initialize base
  351. HRESULT hr = Element::Initialize(EC_SelfLayout); // Normal display node creation, self layout
  352. if (FAILED(hr))
  353. return hr;
  354. // Children can exist outside of Element bounds
  355. SetGadgetStyle(GetDisplayNode(), GS_CLIPINSIDE, GS_CLIPINSIDE);
  356. return S_OK;
  357. }
  358. ////////////////////////////////////////////////////////
  359. // Self-layout methods
  360. SIZE Clipper::_SelfLayoutUpdateDesiredSize(int cxConstraint, int cyConstraint, Surface* psrf)
  361. {
  362. UNREFERENCED_PARAMETER(cyConstraint);
  363. Value* pvChildren;
  364. SIZE size = { 0, 0 };
  365. ElementList* peList = GetChildren(&pvChildren);
  366. // Desired size of this is based solely on it's first child.
  367. // Width is child's width, height is unconstrained height of child.
  368. if (peList && peList->GetSize() > 0)
  369. {
  370. Element* pec = peList->GetItem(0);
  371. size = pec->_UpdateDesiredSize(cxConstraint, INT_MAX, psrf);
  372. if (size.cx > cxConstraint)
  373. size.cx = cxConstraint;
  374. if (size.cy > cyConstraint)
  375. size.cy = cyConstraint;
  376. }
  377. pvChildren->Release();
  378. return size;
  379. }
  380. void Clipper::_SelfLayoutDoLayout(int cx, int cy)
  381. {
  382. Value* pvChildren;
  383. ElementList* peList = GetChildren(&pvChildren);
  384. // Layout first child giving it's desired height and aligning
  385. // it with the clipper's bottom edge
  386. if (peList && peList->GetSize() > 0)
  387. {
  388. Element* pec = peList->GetItem(0);
  389. const SIZE* pds = pec->GetDesiredSize();
  390. pec->_UpdateLayoutPosition(0, cy - pds->cy);
  391. pec->_UpdateLayoutSize(cx, pds->cy);
  392. }
  393. pvChildren->Release();
  394. }
  395. ////////////////////////////////////////////////////////
  396. // Property definitions
  397. ////////////////////////////////////////////////////////
  398. // ClassInfo (must appear after property definitions)
  399. // Define class info with type and base type, set static class pointer
  400. IClassInfo* Clipper::Class = NULL;
  401. HRESULT Clipper::Register()
  402. {
  403. return ClassInfo<Clipper,Element>::Register(L"Clipper", NULL, 0);
  404. }
  405. ////////////////////////////////////////////////////////
  406. // TaskList class
  407. ////////////////////////////////////////////////////////
  408. HRESULT TaskList::Create(OUT Element** ppElement)
  409. {
  410. *ppElement = NULL;
  411. TaskList* pc = HNewAndZero<TaskList>();
  412. if (!pc)
  413. return E_OUTOFMEMORY;
  414. HRESULT hr = pc->Initialize();
  415. if (FAILED(hr))
  416. {
  417. pc->Destroy();
  418. return hr;
  419. }
  420. *ppElement = pc;
  421. return S_OK;
  422. }
  423. HRESULT TaskList::Initialize()
  424. {
  425. // Initialize base
  426. HRESULT hr = Element::Initialize(0); // Normal display node creation, self layout
  427. if (FAILED(hr))
  428. return hr;
  429. return S_OK;
  430. }
  431. ////////////////////////////////////////////////////////
  432. // Hierarchy
  433. Element* TaskList::GetAdjacent(Element* peFrom, int iNavDir, NavReference const* pnr, bool bKeyable)
  434. {
  435. if ((iNavDir & NAV_LOGICAL) && peFrom)
  436. return NULL;
  437. return Element::GetAdjacent(peFrom, iNavDir, pnr, bKeyable);
  438. }
  439. ////////////////////////////////////////////////////////
  440. // Property definitions
  441. ////////////////////////////////////////////////////////
  442. // ClassInfo (must appear after property definitions)
  443. // Define class info with type and base type, set static class pointer
  444. IClassInfo* TaskList::Class = NULL;
  445. HRESULT TaskList::Register()
  446. {
  447. return ClassInfo<TaskList,Element>::Register(L"TaskList", NULL, 0);
  448. }