Team Fortress 2 Source Code as on 22/4/2020
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.

1082 lines
26 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include <assert.h>
  8. #include <ctype.h>
  9. #include <vgui/MouseCode.h>
  10. #include <vgui/KeyCode.h>
  11. #include <KeyValues.h>
  12. #include <vgui/ISurface.h>
  13. #include <vgui/IVGui.h>
  14. #include <vgui/IInput.h>
  15. #include <vgui/IPanel.h>
  16. #include <vgui/ILocalize.h>
  17. #include <vgui_controls/Controls.h>
  18. #include <vgui_controls/ScrollBar.h>
  19. #include <vgui_controls/ImageList.h>
  20. #include <vgui_controls/ImagePanel.h>
  21. #include <vgui_controls/Label.h>
  22. #include <vgui_controls/TextImage.h>
  23. #include <vgui_controls/ListViewPanel.h>
  24. // memdbgon must be the last include file in a .cpp file!!!
  25. #include <tier0/memdbgon.h>
  26. using namespace vgui;
  27. enum
  28. {
  29. WINDOW_BORDER_WIDTH=2 // the width of the window's border
  30. };
  31. namespace vgui
  32. {
  33. class ListViewItem : public Label
  34. {
  35. DECLARE_CLASS_SIMPLE( ListViewItem, Label );
  36. public:
  37. ListViewItem(Panel *parent) : Label(parent, NULL, "")
  38. {
  39. m_pListViewPanel = (ListViewPanel*) parent;
  40. m_pData = NULL;
  41. m_bSelected = false;
  42. SetPaintBackgroundEnabled(true);
  43. }
  44. ~ListViewItem()
  45. {
  46. if (m_pData)
  47. {
  48. m_pData->deleteThis();
  49. m_pData = NULL;
  50. }
  51. }
  52. void SetData(const KeyValues *data)
  53. {
  54. if (m_pData)
  55. {
  56. m_pData->deleteThis();
  57. }
  58. m_pData = data->MakeCopy();
  59. }
  60. virtual void OnMousePressed( MouseCode code)
  61. {
  62. m_pListViewPanel->OnItemMousePressed(this, code);
  63. }
  64. virtual void OnMouseDoublePressed( MouseCode code)
  65. {
  66. // double press should only select the item
  67. m_pListViewPanel->OnItemMouseDoublePressed(this, code);
  68. }
  69. KeyValues *GetData()
  70. {
  71. return m_pData;
  72. }
  73. void SetSelected(bool bSelected)
  74. {
  75. if (bSelected == m_bSelected)
  76. return;
  77. m_bSelected = bSelected;
  78. if (bSelected)
  79. {
  80. RequestFocus();
  81. }
  82. UpdateImage();
  83. InvalidateLayout();
  84. Repaint();
  85. }
  86. virtual void PerformLayout()
  87. {
  88. TextImage *textImage = GetTextImage();
  89. if (m_bSelected)
  90. {
  91. VPANEL focus = input()->GetFocus();
  92. // if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected
  93. if (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent())))
  94. {
  95. textImage->SetColor(m_ArmedFgColor2);
  96. }
  97. else
  98. {
  99. textImage->SetColor(m_FgColor2);
  100. }
  101. }
  102. else
  103. {
  104. textImage->SetColor(GetFgColor());
  105. }
  106. BaseClass::PerformLayout();
  107. Repaint();
  108. }
  109. virtual void PaintBackground()
  110. {
  111. int wide, tall;
  112. GetSize(wide, tall);
  113. if ( m_bSelected )
  114. {
  115. VPANEL focus = input()->GetFocus();
  116. // if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected
  117. if (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent())))
  118. {
  119. surface()->DrawSetColor(m_ArmedBgColor);
  120. }
  121. else
  122. {
  123. surface()->DrawSetColor(m_SelectionBG2Color);
  124. }
  125. }
  126. else
  127. {
  128. surface()->DrawSetColor(GetBgColor());
  129. }
  130. surface()->DrawFilledRect(0, 0, wide, tall);
  131. }
  132. virtual void ApplySchemeSettings(IScheme *pScheme)
  133. {
  134. BaseClass::ApplySchemeSettings(pScheme);
  135. m_ArmedFgColor2 = GetSchemeColor("ListPanel.SelectedTextColor", pScheme);
  136. m_ArmedBgColor = GetSchemeColor("ListPanel.SelectedBgColor", pScheme);
  137. m_FgColor1 = GetSchemeColor("ListPanel.TextColor", pScheme);
  138. m_FgColor2 = GetSchemeColor("ListPanel.SelectedTextColor", pScheme);
  139. m_BgColor = GetSchemeColor("ListPanel.BgColor", GetBgColor(), pScheme);
  140. m_BgColor = GetSchemeColor("ListPanel.TextBgColor", m_BgColor, pScheme);
  141. m_SelectionBG2Color = GetSchemeColor("ListPanel.SelectedOutOfFocusBgColor", pScheme);
  142. SetBgColor(m_BgColor);
  143. SetFgColor(m_FgColor1);
  144. UpdateImage();
  145. }
  146. void UpdateImage()
  147. {
  148. if ( m_pListViewPanel->m_pImageList )
  149. {
  150. int imageIndex = 0;
  151. if ( m_bSelected )
  152. {
  153. imageIndex = m_pData->GetInt("imageSelected", 0);
  154. }
  155. if ( imageIndex == 0 )
  156. {
  157. imageIndex = m_pData->GetInt("image", 0);
  158. }
  159. if ( m_pListViewPanel->m_pImageList->IsValidIndex(imageIndex) )
  160. {
  161. SetImageAtIndex(0, m_pListViewPanel->m_pImageList->GetImage(imageIndex), 0);
  162. }
  163. else
  164. {
  165. // use the default
  166. SetImageAtIndex(0, m_pListViewPanel->m_pImageList->GetImage(1), 0);
  167. }
  168. SizeToContents();
  169. InvalidateLayout();
  170. }
  171. }
  172. private:
  173. Color m_FgColor1;
  174. Color m_FgColor2;
  175. Color m_BgColor;
  176. Color m_ArmedFgColor2;
  177. Color m_ArmedBgColor;
  178. Color m_SelectionBG2Color;
  179. //IBorder *_keyFocusBorder; // maybe in the future when I'm the 'active' but not selected item, I'll have a border
  180. KeyValues *m_pData;
  181. ListViewPanel *m_pListViewPanel;
  182. bool m_bSelected;
  183. };
  184. }
  185. static bool DefaultSortFunc(KeyValues *kv1, KeyValues *kv2)
  186. {
  187. const char *string1 = kv1->GetString("text");
  188. const char *string2 = kv2->GetString("text");
  189. return Q_stricmp(string1, string2) < 0;
  190. }
  191. DECLARE_BUILD_FACTORY( ListViewPanel );
  192. //-----------------------------------------------------------------------------
  193. // Purpose: Constructor
  194. //-----------------------------------------------------------------------------
  195. ListViewPanel::ListViewPanel(Panel *parent, const char *panelName) : Panel(parent, panelName)
  196. {
  197. m_iRowHeight = 20;
  198. m_bNeedsSort = false;
  199. m_hFont = NULL;
  200. m_pImageList = NULL;
  201. m_bDeleteImageListWhenDone = false;
  202. m_pSortFunc = DefaultSortFunc;
  203. m_ShiftStartItemID = -1;
  204. m_hbar = new ScrollBar(this, "HorizScrollBar", false);
  205. m_hbar->AddActionSignalTarget(this);
  206. m_hbar->SetVisible(false);
  207. }
  208. //-----------------------------------------------------------------------------
  209. // Purpose:
  210. //-----------------------------------------------------------------------------
  211. ListViewPanel::~ListViewPanel()
  212. {
  213. DeleteAllItems();
  214. delete m_hbar;
  215. if ( m_bDeleteImageListWhenDone )
  216. {
  217. delete m_pImageList;
  218. m_pImageList = NULL;
  219. }
  220. }
  221. //-----------------------------------------------------------------------------
  222. // Purpose:
  223. //-----------------------------------------------------------------------------
  224. int ListViewPanel::AddItem(const KeyValues *data, bool bScrollToItem, bool bSortOnAdd)
  225. {
  226. ListViewItem *pNewItem = new ListViewItem(this);
  227. pNewItem->SetData(data);
  228. if (m_hFont)
  229. {
  230. pNewItem->SetFont(m_hFont);
  231. }
  232. int itemID = m_DataItems.AddToTail(pNewItem);
  233. ApplyItemChanges(itemID);
  234. m_SortedItems.AddToTail(itemID);
  235. if ( bSortOnAdd )
  236. {
  237. m_bNeedsSort = true;
  238. }
  239. InvalidateLayout();
  240. if ( bScrollToItem )
  241. {
  242. ScrollToItem(itemID);
  243. }
  244. return itemID;
  245. }
  246. //-----------------------------------------------------------------------------
  247. // Purpose:
  248. //-----------------------------------------------------------------------------
  249. void ListViewPanel::ScrollToItem(int itemID)
  250. {
  251. if (!m_hbar->IsVisible())
  252. {
  253. return;
  254. }
  255. int val = m_hbar->GetValue();
  256. int wide, tall;
  257. GetSize( wide, tall );
  258. int maxWidth = GetItemsMaxWidth();
  259. int maxColVisible = wide / maxWidth;
  260. int itemsPerCol = GetItemsPerColumn();
  261. int itemIndex = m_SortedItems.Find(itemID);
  262. int desiredCol = itemIndex / itemsPerCol;
  263. if (desiredCol < val || desiredCol >= (val + maxColVisible) )
  264. {
  265. m_hbar->SetValue(desiredCol);
  266. }
  267. InvalidateLayout();
  268. }
  269. //-----------------------------------------------------------------------------
  270. // Purpose:
  271. //-----------------------------------------------------------------------------
  272. int ListViewPanel::GetItemCount()
  273. {
  274. return m_DataItems.Count();
  275. }
  276. //-----------------------------------------------------------------------------
  277. // Purpose:
  278. //-----------------------------------------------------------------------------
  279. KeyValues *ListViewPanel::GetItem(int itemID)
  280. {
  281. if ( !m_DataItems.IsValidIndex(itemID) )
  282. return NULL;
  283. return m_DataItems[itemID]->GetData();
  284. }
  285. //-----------------------------------------------------------------------------
  286. // Purpose: Get ItemID from position in panel - valid from [0, GetItemCount)
  287. //-----------------------------------------------------------------------------
  288. int ListViewPanel::GetItemIDFromPos(int iPos)
  289. {
  290. if ( m_SortedItems.IsValidIndex(iPos) )
  291. {
  292. return m_SortedItems[iPos];
  293. }
  294. else
  295. {
  296. return m_DataItems.InvalidIndex();
  297. }
  298. }
  299. //-----------------------------------------------------------------------------
  300. // Purpose:
  301. //-----------------------------------------------------------------------------
  302. void ListViewPanel::ApplyItemChanges(int itemID)
  303. {
  304. if ( !m_DataItems.IsValidIndex(itemID) )
  305. return;
  306. KeyValues *kv = m_DataItems[itemID]->GetData();
  307. ListViewItem *pLabel = m_DataItems[itemID];
  308. pLabel->SetText(kv->GetString("text"));
  309. pLabel->SetTextImageIndex(1);
  310. pLabel->SetImagePreOffset(1, 5);
  311. TextImage *pTextImage = pLabel->GetTextImage();
  312. pTextImage->ResizeImageToContent();
  313. pLabel->UpdateImage();
  314. pLabel->SizeToContents();
  315. pLabel->InvalidateLayout();
  316. }
  317. //-----------------------------------------------------------------------------
  318. // Purpose:
  319. //-----------------------------------------------------------------------------
  320. void ListViewPanel::RemoveItem(int itemID)
  321. {
  322. if ( !m_DataItems.IsValidIndex(itemID) )
  323. return;
  324. m_DataItems[itemID]->MarkForDeletion();
  325. // mark the keyValues for deletion
  326. m_DataItems.Remove(itemID);
  327. m_SortedItems.FindAndRemove(itemID);
  328. m_SelectedItems.FindAndRemove(itemID);
  329. InvalidateLayout();
  330. }
  331. //-----------------------------------------------------------------------------
  332. // Purpose:
  333. //-----------------------------------------------------------------------------
  334. void ListViewPanel::DeleteAllItems()
  335. {
  336. FOR_EACH_LL( m_DataItems, index )
  337. {
  338. m_DataItems[index]->MarkForDeletion();
  339. }
  340. m_DataItems.RemoveAll();
  341. m_SortedItems.RemoveAll();
  342. m_SelectedItems.RemoveAll();
  343. }
  344. //-----------------------------------------------------------------------------
  345. // Purpose:
  346. //-----------------------------------------------------------------------------
  347. int ListViewPanel::InvalidItemID()
  348. {
  349. return m_DataItems.InvalidIndex();
  350. }
  351. //-----------------------------------------------------------------------------
  352. // Purpose:
  353. //-----------------------------------------------------------------------------
  354. bool ListViewPanel::IsValidItemID(int itemID)
  355. {
  356. return m_DataItems.IsValidIndex(itemID);
  357. }
  358. //-----------------------------------------------------------------------------
  359. // Purpose:
  360. //-----------------------------------------------------------------------------
  361. void ListViewPanel::SetSortFunc(ListViewSortFunc_t func)
  362. {
  363. if ( func )
  364. {
  365. m_pSortFunc = func;
  366. SortList();
  367. }
  368. }
  369. //-----------------------------------------------------------------------------
  370. // Purpose:
  371. //-----------------------------------------------------------------------------
  372. void ListViewPanel::SortList()
  373. {
  374. m_SortedItems.RemoveAll();
  375. // find all the items in this section
  376. for( int i = m_DataItems.Head(); i != m_DataItems.InvalidIndex(); i = m_DataItems.Next( i ) )
  377. {
  378. // insert the items sorted
  379. if (m_pSortFunc)
  380. {
  381. int insertionPoint;
  382. for (insertionPoint = 0; insertionPoint < m_SortedItems.Count(); insertionPoint++)
  383. {
  384. if ( m_pSortFunc(m_DataItems[i]->GetData(), m_DataItems[m_SortedItems[insertionPoint]]->GetData() ) )
  385. break;
  386. }
  387. if (insertionPoint == m_SortedItems.Count())
  388. {
  389. m_SortedItems.AddToTail(i);
  390. }
  391. else
  392. {
  393. m_SortedItems.InsertBefore(insertionPoint, i);
  394. }
  395. }
  396. else
  397. {
  398. // just add to the end
  399. m_SortedItems.AddToTail(i);
  400. }
  401. }
  402. }
  403. //-----------------------------------------------------------------------------
  404. // Purpose:
  405. //-----------------------------------------------------------------------------
  406. void ListViewPanel::SetImageList(ImageList *imageList, bool deleteImageListWhenDone)
  407. {
  408. // get rid of existing list image if there's one and we're supposed to get rid of it
  409. if ( m_pImageList && m_bDeleteImageListWhenDone )
  410. {
  411. delete m_pImageList;
  412. m_pImageList = NULL;
  413. }
  414. m_bDeleteImageListWhenDone = deleteImageListWhenDone;
  415. m_pImageList = imageList;
  416. FOR_EACH_LL( m_DataItems, i )
  417. {
  418. m_DataItems[i]->UpdateImage();
  419. }
  420. }
  421. //-----------------------------------------------------------------------------
  422. // Purpose:
  423. //-----------------------------------------------------------------------------
  424. void ListViewPanel::SetFont(HFont font)
  425. {
  426. Assert( font );
  427. if ( !font )
  428. return;
  429. m_hFont = font;
  430. m_iRowHeight = surface()->GetFontTall(font) + 1;
  431. FOR_EACH_LL( m_DataItems, i )
  432. {
  433. m_DataItems[i]->SetFont(m_hFont);
  434. TextImage *pTextImage = m_DataItems[i]->GetTextImage();
  435. pTextImage->ResizeImageToContent();
  436. m_DataItems[i]->SizeToContents();
  437. }
  438. }
  439. //-----------------------------------------------------------------------------
  440. // Purpose:
  441. //-----------------------------------------------------------------------------
  442. int ListViewPanel::GetSelectedItemsCount()
  443. {
  444. return m_SelectedItems.Count();
  445. }
  446. //-----------------------------------------------------------------------------
  447. // Purpose:
  448. //-----------------------------------------------------------------------------
  449. int ListViewPanel::GetSelectedItem(int selectionIndex)
  450. {
  451. if ( m_SelectedItems.IsValidIndex(selectionIndex) )
  452. return m_SelectedItems[selectionIndex];
  453. return -1;
  454. }
  455. //-----------------------------------------------------------------------------
  456. // Purpose:
  457. //-----------------------------------------------------------------------------
  458. void ListViewPanel::ClearSelectedItems()
  459. {
  460. int i;
  461. for (i = 0 ; i < m_SelectedItems.Count(); i++)
  462. {
  463. if ( m_DataItems.IsValidIndex(m_SelectedItems[i]) )
  464. {
  465. m_DataItems[m_SelectedItems[i]]->SetSelected(false);
  466. }
  467. }
  468. m_SelectedItems.RemoveAll();
  469. }
  470. //-----------------------------------------------------------------------------
  471. // Purpose:
  472. //-----------------------------------------------------------------------------
  473. void ListViewPanel::AddSelectedItem(int itemID)
  474. {
  475. if ( m_SelectedItems.Find(itemID) == -1 )
  476. {
  477. m_SelectedItems.AddToTail(itemID);
  478. m_DataItems[itemID]->SetSelected(true);
  479. m_LastSelectedItemID = itemID;
  480. m_ShiftStartItemID = itemID;
  481. PostActionSignal(new KeyValues("ListViewItemSelected"));
  482. }
  483. }
  484. //-----------------------------------------------------------------------------
  485. // Purpose:
  486. //-----------------------------------------------------------------------------
  487. void ListViewPanel::SetSingleSelectedItem(int itemID)
  488. {
  489. ClearSelectedItems();
  490. AddSelectedItem(itemID);
  491. }
  492. //-----------------------------------------------------------------------------
  493. // Purpose:
  494. //-----------------------------------------------------------------------------
  495. void ListViewPanel::OnMouseWheeled(int delta)
  496. {
  497. int val = m_hbar->GetValue();
  498. val -= delta;
  499. m_hbar->SetValue(val);
  500. }
  501. //-----------------------------------------------------------------------------
  502. // Purpose:
  503. //-----------------------------------------------------------------------------
  504. void ListViewPanel::OnSizeChanged(int wide, int tall)
  505. {
  506. BaseClass::OnSizeChanged(wide, tall);
  507. InvalidateLayout();
  508. Repaint();
  509. }
  510. //-----------------------------------------------------------------------------
  511. // Purpose:
  512. //-----------------------------------------------------------------------------
  513. int ListViewPanel::GetItemsMaxWidth()
  514. {
  515. int maxWidth = 0;
  516. FOR_EACH_LL( m_DataItems, i )
  517. {
  518. int labelWide, labelTall;
  519. m_DataItems[i]->GetSize(labelWide, labelTall);
  520. if (labelWide > maxWidth)
  521. {
  522. maxWidth = labelWide + 25;
  523. }
  524. }
  525. return maxWidth;
  526. }
  527. //-----------------------------------------------------------------------------
  528. // Purpose:
  529. //-----------------------------------------------------------------------------
  530. void ListViewPanel::PerformLayout()
  531. {
  532. if (m_bNeedsSort)
  533. {
  534. SortList();
  535. }
  536. if ( m_DataItems.Count() == 0 )
  537. return;
  538. int wide, tall;
  539. GetSize(wide, tall);
  540. int maxWidth = GetItemsMaxWidth();
  541. if (maxWidth < 24)
  542. {
  543. maxWidth = 24;
  544. }
  545. int maxColVisible = wide / maxWidth;
  546. m_hbar->SetVisible(false);
  547. int itemsPerCol = GetItemsPerColumn();
  548. if (itemsPerCol < 1)
  549. {
  550. itemsPerCol = 1;
  551. }
  552. int cols = ( GetItemCount() + (itemsPerCol - 1) ) / itemsPerCol;
  553. int startItem = 0;
  554. if ( cols > maxColVisible)
  555. {
  556. m_hbar->SetVisible(true);
  557. // recalulate # per column now that we've made the hbar visible
  558. itemsPerCol = GetItemsPerColumn();
  559. cols = ( GetItemCount() + (itemsPerCol - 1) ) / (itemsPerCol > 0 ? itemsPerCol : 1 );
  560. m_hbar->SetEnabled(false);
  561. m_hbar->SetRangeWindow( maxColVisible );
  562. m_hbar->SetRange( 0, cols);
  563. m_hbar->SetButtonPressedScrollValue( 1 );
  564. m_hbar->SetPos(0, tall - (m_hbar->GetTall()+WINDOW_BORDER_WIDTH));
  565. m_hbar->SetSize(wide - (WINDOW_BORDER_WIDTH*2), m_hbar->GetTall());
  566. m_hbar->InvalidateLayout();
  567. int val = m_hbar->GetValue();
  568. startItem += val*itemsPerCol;
  569. }
  570. else
  571. {
  572. m_hbar->SetVisible(false);
  573. }
  574. int lastItemVisible = startItem + (( maxColVisible + 1 )* itemsPerCol) - 1;
  575. int itemsThisCol = 0;
  576. int x = 0;
  577. int y = 0;
  578. int i;
  579. for ( i = 0 ; i < m_SortedItems.Count() ; i++ )
  580. {
  581. if ( i >= startItem && i <= lastItemVisible )
  582. {
  583. m_DataItems[ m_SortedItems[i] ]->SetVisible(true);
  584. m_DataItems[ m_SortedItems[i] ]->SetPos(x, y);
  585. itemsThisCol++;
  586. if ( itemsThisCol == itemsPerCol )
  587. {
  588. y = 0;
  589. x += maxWidth;
  590. itemsThisCol = 0;
  591. }
  592. else
  593. {
  594. y += m_iRowHeight;
  595. }
  596. }
  597. else
  598. {
  599. m_DataItems[ m_SortedItems[i] ]->SetVisible(false);
  600. }
  601. }
  602. }
  603. //-----------------------------------------------------------------------------
  604. // Purpose:
  605. //-----------------------------------------------------------------------------
  606. void ListViewPanel::Paint()
  607. {
  608. BaseClass::Paint();
  609. }
  610. //-----------------------------------------------------------------------------
  611. // Purpose:
  612. //-----------------------------------------------------------------------------
  613. void ListViewPanel::ApplySchemeSettings(IScheme *pScheme)
  614. {
  615. BaseClass::ApplySchemeSettings(pScheme);
  616. SetBgColor(GetSchemeColor("ListPanel.BgColor", pScheme));
  617. SetBorder(pScheme->GetBorder("ButtonDepressedBorder"));
  618. m_LabelFgColor = GetSchemeColor("ListPanel.TextColor", pScheme);
  619. m_SelectionFgColor = GetSchemeColor("ListPanel.SelectedTextColor", m_LabelFgColor, pScheme);
  620. m_hFont = pScheme->GetFont("Default", IsProportional());
  621. SetFont(m_hFont);
  622. }
  623. //-----------------------------------------------------------------------------
  624. // Purpose:
  625. //-----------------------------------------------------------------------------
  626. void ListViewPanel::OnMousePressed( MouseCode code)
  627. {
  628. if (code == MOUSE_LEFT || code == MOUSE_RIGHT)
  629. {
  630. ClearSelectedItems();
  631. RequestFocus();
  632. }
  633. // check for context menu open
  634. if (code == MOUSE_RIGHT)
  635. {
  636. // post it, but with the invalid row
  637. PostActionSignal(new KeyValues("OpenContextMenu", "itemID", -1));
  638. }
  639. }
  640. //-----------------------------------------------------------------------------
  641. // Purpose:
  642. //-----------------------------------------------------------------------------
  643. void ListViewPanel::OnShiftSelect(int itemID)
  644. {
  645. // if we dont' have a valid selected ItemID - then we just choose the first item
  646. if ( !m_DataItems.IsValidIndex(m_ShiftStartItemID) )
  647. {
  648. m_ShiftStartItemID = m_DataItems.Head();
  649. }
  650. // find out if the just pressed item is "earlier" or is the 'last selected item'
  651. int lowerPos = -1, upperPos = -1;
  652. int i;
  653. for ( i = 0 ; i < m_SortedItems.Count() ; i++ )
  654. {
  655. if ( m_SortedItems[i] == itemID )
  656. {
  657. lowerPos = i;
  658. upperPos = m_SortedItems.Find(m_ShiftStartItemID);
  659. break;
  660. }
  661. else if ( m_SortedItems[i] == m_ShiftStartItemID )
  662. {
  663. lowerPos = m_SortedItems.Find(m_ShiftStartItemID);
  664. upperPos = i;
  665. break;
  666. }
  667. }
  668. assert(lowerPos <= upperPos);
  669. if ( !input()->IsKeyDown(KEY_LCONTROL) && !input()->IsKeyDown(KEY_RCONTROL) )
  670. {
  671. ClearSelectedItems();
  672. }
  673. for ( i = lowerPos ; i <= upperPos ; i ++)
  674. {
  675. // do not use AddSelectedItem because we don't want to switch the shiftStartItemID
  676. m_DataItems[ m_SortedItems[i] ]->SetSelected(true);
  677. m_SelectedItems.AddToTail(m_SortedItems[i]);
  678. m_LastSelectedItemID = itemID;
  679. }
  680. }
  681. //-----------------------------------------------------------------------------
  682. // Purpose:
  683. //-----------------------------------------------------------------------------
  684. void ListViewPanel::OnItemMousePressed(ListViewItem* pItem, MouseCode code)
  685. {
  686. int itemID = m_DataItems.Find(pItem);
  687. if (!m_DataItems.IsValidIndex(itemID))
  688. return;
  689. // check for context menu open
  690. if (code == MOUSE_RIGHT)
  691. {
  692. // if this is a new item - unselect everything else
  693. if ( m_SelectedItems.Find(itemID) == -1)
  694. {
  695. ClearSelectedItems();
  696. AddSelectedItem(itemID);
  697. }
  698. PostActionSignal(new KeyValues("OpenContextMenu", "itemID", itemID));
  699. }
  700. else
  701. {
  702. if ( input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT) )
  703. {
  704. OnShiftSelect(itemID);
  705. }
  706. else if ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) )
  707. {
  708. if ( m_SelectedItems.Find(itemID) != -1)
  709. {
  710. m_SelectedItems.FindAndRemove(itemID);
  711. pItem->SetSelected(false);
  712. // manually select these since we 'last' clicked on these items
  713. m_ShiftStartItemID = itemID;
  714. m_LastSelectedItemID = itemID;
  715. m_DataItems[itemID]->RequestFocus();
  716. }
  717. else
  718. {
  719. AddSelectedItem(itemID);
  720. }
  721. }
  722. else
  723. {
  724. ClearSelectedItems();
  725. AddSelectedItem(itemID);
  726. }
  727. }
  728. }
  729. //-----------------------------------------------------------------------------
  730. // Purpose:
  731. //-----------------------------------------------------------------------------
  732. void ListViewPanel::OnMouseDoublePressed( MouseCode code)
  733. {
  734. }
  735. //-----------------------------------------------------------------------------
  736. // Purpose:
  737. //-----------------------------------------------------------------------------
  738. void ListViewPanel::OnItemMouseDoublePressed(ListViewItem* pItem, MouseCode code)
  739. {
  740. if (code == MOUSE_LEFT)
  741. {
  742. OnKeyCodeTyped(KEY_ENTER);
  743. }
  744. }
  745. //-----------------------------------------------------------------------------
  746. // Purpose:
  747. //-----------------------------------------------------------------------------
  748. void ListViewPanel::FinishKeyPress(int itemID)
  749. {
  750. if ( input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT) )
  751. {
  752. OnShiftSelect(itemID);
  753. }
  754. else if ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) )
  755. {
  756. m_DataItems[itemID]->RequestFocus();
  757. m_LastSelectedItemID = itemID;
  758. }
  759. else
  760. {
  761. SetSingleSelectedItem(itemID);
  762. }
  763. ScrollToItem(itemID);
  764. }
  765. //-----------------------------------------------------------------------------
  766. // Purpose:
  767. //-----------------------------------------------------------------------------
  768. void ListViewPanel::OnKeyCodeTyped( KeyCode code )
  769. {
  770. if ( m_DataItems.Count() == 0 )
  771. return;
  772. switch (code)
  773. {
  774. case KEY_HOME:
  775. {
  776. if (m_SortedItems.Count() > 0)
  777. {
  778. int itemID = m_SortedItems[0];
  779. FinishKeyPress(itemID);
  780. }
  781. break;
  782. }
  783. case KEY_END:
  784. {
  785. if (m_DataItems.Count() > 0)
  786. {
  787. int itemID = m_SortedItems[ m_SortedItems.Count() - 1 ];
  788. FinishKeyPress(itemID);
  789. }
  790. break;
  791. }
  792. case KEY_UP:
  793. {
  794. int itemPos = m_SortedItems.Find( m_LastSelectedItemID );
  795. itemPos--;
  796. if (itemPos < 0)
  797. itemPos = 0;
  798. FinishKeyPress(m_SortedItems[itemPos]);
  799. break;
  800. }
  801. case KEY_DOWN:
  802. {
  803. int itemPos = m_SortedItems.Find( m_LastSelectedItemID );
  804. itemPos++;
  805. if (itemPos >= m_DataItems.Count())
  806. itemPos = m_DataItems.Count() - 1;
  807. FinishKeyPress(m_SortedItems[itemPos]);
  808. break;
  809. }
  810. case KEY_LEFT:
  811. {
  812. int itemPos = m_SortedItems.Find( m_LastSelectedItemID );
  813. itemPos -= GetItemsPerColumn();
  814. if (itemPos < 0)
  815. {
  816. itemPos = 0;
  817. }
  818. FinishKeyPress(m_SortedItems[itemPos]);
  819. break;
  820. }
  821. case KEY_RIGHT:
  822. {
  823. int itemPos = m_SortedItems.Find( m_LastSelectedItemID );
  824. itemPos += GetItemsPerColumn();
  825. if (itemPos >= m_SortedItems.Count())
  826. {
  827. itemPos = m_SortedItems.Count() - 1;
  828. }
  829. FinishKeyPress(m_SortedItems[itemPos]);
  830. break;
  831. }
  832. case KEY_PAGEUP:
  833. {
  834. int wide, tall;
  835. GetSize(wide, tall);
  836. int maxWidth = GetItemsMaxWidth();
  837. if (maxWidth == 0)
  838. {
  839. maxWidth = wide;
  840. }
  841. int maxColVisible = wide / maxWidth;
  842. int delta = maxColVisible * GetItemsPerColumn();
  843. int itemPos = m_SortedItems.Find( m_LastSelectedItemID );
  844. itemPos -= delta;
  845. if (itemPos < 0)
  846. {
  847. itemPos = 0;
  848. }
  849. FinishKeyPress(m_SortedItems[itemPos]);
  850. break;
  851. }
  852. case KEY_PAGEDOWN:
  853. {
  854. int wide, tall;
  855. GetSize(wide, tall);
  856. int maxWidth = GetItemsMaxWidth();
  857. if (maxWidth == 0)
  858. {
  859. maxWidth = wide;
  860. }
  861. int maxColVisible = wide / maxWidth;
  862. int delta = maxColVisible * GetItemsPerColumn();
  863. int itemPos = m_SortedItems.Find( m_LastSelectedItemID );
  864. itemPos += delta;
  865. if (itemPos >= m_SortedItems.Count())
  866. {
  867. itemPos = m_SortedItems.Count() - 1;
  868. }
  869. FinishKeyPress(m_SortedItems[itemPos]);
  870. break;
  871. }
  872. default:
  873. {
  874. BaseClass::OnKeyCodeTyped(code);
  875. break;
  876. }
  877. }
  878. }
  879. //-----------------------------------------------------------------------------
  880. // Purpose:
  881. //-----------------------------------------------------------------------------
  882. void ListViewPanel::OnKeyTyped(wchar_t unichar)
  883. {
  884. if (!iswcntrl(unichar))
  885. {
  886. wchar_t uniString[2];
  887. uniString[0] = unichar;
  888. uniString[1] = 0;
  889. char buf[2];
  890. g_pVGuiLocalize->ConvertUnicodeToANSI(uniString, buf, sizeof(buf));
  891. int i;
  892. int itemPos = m_SortedItems.Find(m_LastSelectedItemID);
  893. if ( m_SortedItems.IsValidIndex(itemPos))
  894. {
  895. itemPos++;
  896. // start from the item AFTER our last selected Item and go to end
  897. for ( i = itemPos ; i != m_SortedItems.Count(); i++)
  898. {
  899. KeyValues *kv = m_DataItems[ m_SortedItems[i] ]->GetData();
  900. const char *pszText = kv->GetString("text");
  901. if (!strnicmp(pszText, buf, 1))
  902. {
  903. // select the next of this letter
  904. SetSingleSelectedItem(m_SortedItems[i]);
  905. ScrollToItem(m_SortedItems[i]);
  906. return;
  907. }
  908. }
  909. // if the after this item we couldn't fine an item with the same letter, fall through and just start from the beginning of list to the last selected item
  910. }
  911. for ( i = 0 ; i < m_SortedItems.Count() ; i++ )
  912. {
  913. // we've gone all the way around - break - if we had a valid index, this is one more that last selectedItem, if not it's an illegal index
  914. if ( i == itemPos)
  915. break;
  916. KeyValues *kv = m_DataItems[ m_SortedItems[i] ]->GetData();
  917. const char *pszText = kv->GetString("text");
  918. if (!strnicmp(pszText, buf, 1))
  919. {
  920. SetSingleSelectedItem(m_SortedItems[i]);
  921. ScrollToItem(m_SortedItems[i]);
  922. return;
  923. }
  924. }
  925. }
  926. else
  927. BaseClass::OnKeyTyped(unichar);
  928. }
  929. //-----------------------------------------------------------------------------
  930. // Purpose:
  931. //-----------------------------------------------------------------------------
  932. void ListViewPanel::OnSliderMoved()
  933. {
  934. InvalidateLayout();
  935. Repaint();
  936. }
  937. //-----------------------------------------------------------------------------
  938. // Purpose:
  939. //-----------------------------------------------------------------------------
  940. int ListViewPanel::GetItemsPerColumn()
  941. {
  942. int wide, tall;
  943. GetSize(wide, tall);
  944. if ( m_hbar->IsVisible() )
  945. {
  946. tall -= m_hbar->GetTall();
  947. }
  948. return tall / m_iRowHeight; // should round down
  949. }