Counter Strike : Global Offensive Source Code
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.

2090 lines
58 KiB

  1. //========= Copyright (c) Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include <stdio.h>
  8. #include <vgui/IInput.h>
  9. #include <vgui/IPanel.h>
  10. #include <vgui/ILocalize.h>
  11. #include <vgui/IScheme.h>
  12. #include <vgui/ISurface.h>
  13. #include <keyvalues.h>
  14. #include <vgui/MouseCode.h>
  15. #include <vgui_controls/SectionedListPanel.h>
  16. #include <vgui_controls/Button.h>
  17. #include <vgui_controls/Controls.h>
  18. #include <vgui_controls/Label.h>
  19. #include <vgui_controls/ScrollBar.h>
  20. #include <vgui_controls/TextImage.h>
  21. #include <vgui_controls/ImageList.h>
  22. #include "utlvector.h"
  23. // memdbgon must be the last include file in a .cpp file!!!
  24. #include <tier0/memdbgon.h>
  25. using namespace vgui;
  26. enum
  27. {
  28. BUTTON_HEIGHT_DEFAULT = 20,
  29. BUTTON_HEIGHT_SPACER = 7,
  30. SECTION_GAP = 8,
  31. COLUMN_DATA_INDENT = 6,
  32. COLUMN_DATA_GAP = 2,
  33. };
  34. namespace vgui
  35. {
  36. //-----------------------------------------------------------------------------
  37. // Purpose: header label that separates and names each section
  38. //-----------------------------------------------------------------------------
  39. class CSectionHeader : public Label
  40. {
  41. DECLARE_CLASS_SIMPLE( CSectionHeader, Label );
  42. public:
  43. CSectionHeader(SectionedListPanel *parent, const char *name, int sectionID) : Label(parent, name, "")
  44. {
  45. m_pListPanel = parent;
  46. m_iSectionID = sectionID;
  47. SetTextImageIndex(-1);
  48. ClearImages();
  49. SetPaintBackgroundEnabled( false );
  50. }
  51. CSectionHeader(SectionedListPanel *parent, const wchar_t *name, int sectionID) : Label(parent, "SectionHeader", "")
  52. {
  53. SetText(name);
  54. SetVisible(false);
  55. m_pListPanel = parent;
  56. m_iSectionID = sectionID;
  57. SetTextImageIndex(-1);
  58. ClearImages();
  59. }
  60. void ApplySchemeSettings(IScheme *pScheme)
  61. {
  62. BaseClass::ApplySchemeSettings(pScheme);
  63. SetFgColor(GetSchemeColor("SectionedListPanel.HeaderTextColor", pScheme));
  64. m_SectionDividerColor = GetSchemeColor("SectionedListPanel.DividerColor", pScheme);
  65. SetBgColor(GetSchemeColor("SectionedListPanelHeader.BgColor", GetBgColor(), pScheme));
  66. HFont hFont = m_pListPanel->GetHeaderFont();
  67. if ( hFont != INVALID_FONT )
  68. {
  69. SetFont( hFont );
  70. }
  71. else
  72. {
  73. SetFont(pScheme->GetFont("DefaultVerySmall", IsProportional()));
  74. }
  75. ClearImages();
  76. }
  77. void Paint()
  78. {
  79. BaseClass::Paint();
  80. int x, y, wide, tall;
  81. GetBounds(x, y, wide, tall);
  82. y = (tall - 2); // draw the line under the panel
  83. surface()->DrawSetColor(m_SectionDividerColor);
  84. surface()->DrawFilledRect(1, y, GetWide() - 2, y + 1);
  85. }
  86. void SetColor(Color col)
  87. {
  88. m_SectionDividerColor = col;
  89. SetFgColor(col);
  90. }
  91. void SetDividerColor(Color col )
  92. {
  93. m_SectionDividerColor = col;
  94. }
  95. void PerformLayout()
  96. {
  97. BaseClass::PerformLayout();
  98. // set up the text in the header
  99. int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID);
  100. if (colCount != GetImageCount())
  101. {
  102. // rebuild the image list
  103. for (int i = 0; i < colCount; i++)
  104. {
  105. int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i);
  106. IImage *image = NULL;
  107. if (columnFlags & SectionedListPanel::HEADER_IMAGE)
  108. {
  109. //!! need some kind of image reference
  110. image = NULL;
  111. }
  112. else
  113. {
  114. TextImage *textImage = new TextImage("");
  115. textImage->SetFont(GetFont());
  116. HFont fallback = m_pListPanel->GetColumnFallbackFontBySection( m_iSectionID, i );
  117. if ( INVALID_FONT != fallback )
  118. {
  119. textImage->SetUseFallbackFont( true, fallback );
  120. }
  121. textImage->SetColor(GetFgColor());
  122. image = textImage;
  123. }
  124. SetImageAtIndex(i, image, 0);
  125. }
  126. }
  127. for (int repeat = 0; repeat <= 1; repeat++)
  128. {
  129. int xpos = 0;
  130. for (int i = 0; i < colCount; i++)
  131. {
  132. int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i);
  133. int columnWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i);
  134. int maxWidth = columnWidth;
  135. IImage *image = GetImageAtIndex(i);
  136. if (!image)
  137. {
  138. xpos += columnWidth;
  139. continue;
  140. }
  141. // set the image position within the label
  142. int contentWide, wide, tall;
  143. image->GetContentSize(wide, tall);
  144. contentWide = wide;
  145. // see if we can draw over the next few column headers (if we're left-aligned)
  146. if (!(columnFlags & SectionedListPanel::COLUMN_RIGHT))
  147. {
  148. for (int j = i + 1; j < colCount; j++)
  149. {
  150. // see if this column header has anything for a header
  151. int iwide = 0, itall = 0;
  152. if (GetImageAtIndex(j))
  153. {
  154. GetImageAtIndex(j)->GetContentSize(iwide, itall);
  155. }
  156. if (iwide == 0)
  157. {
  158. // it's a blank header, ok to draw over it
  159. maxWidth += m_pListPanel->GetColumnWidthBySection(m_iSectionID, j);
  160. }
  161. }
  162. }
  163. if (maxWidth >= 0)
  164. {
  165. wide = maxWidth;
  166. }
  167. if (columnFlags & SectionedListPanel::COLUMN_RIGHT)
  168. {
  169. SetImageBounds(i, xpos + wide - contentWide, contentWide - COLUMN_DATA_GAP);
  170. }
  171. else
  172. {
  173. SetImageBounds(i, xpos, wide - COLUMN_DATA_GAP);
  174. }
  175. xpos += columnWidth;
  176. if (!(columnFlags & SectionedListPanel::HEADER_IMAGE))
  177. {
  178. Assert(dynamic_cast<TextImage *>(image) != NULL);
  179. TextImage *textImage = (TextImage *)image;
  180. textImage->SetFont(GetFont());
  181. textImage->SetText(m_pListPanel->GetColumnTextBySection(m_iSectionID, i));
  182. textImage->ResizeImageToContentMaxWidth( maxWidth );
  183. }
  184. }
  185. }
  186. }
  187. private:
  188. int m_iSectionID;
  189. Color m_SectionDividerColor;
  190. SectionedListPanel *m_pListPanel;
  191. };
  192. //-----------------------------------------------------------------------------
  193. // Purpose: Individual items in the list
  194. //-----------------------------------------------------------------------------
  195. class CItemButton : public Label
  196. {
  197. DECLARE_CLASS_SIMPLE( CItemButton, Label );
  198. public:
  199. CItemButton(SectionedListPanel *parent, int itemID) : Label(parent, NULL, "< item >")
  200. {
  201. m_pListPanel = parent;
  202. m_iID = itemID;
  203. m_pData = NULL;
  204. Clear();
  205. }
  206. ~CItemButton()
  207. {
  208. // free all the keyvalues
  209. if (m_pData)
  210. {
  211. m_pData->deleteThis();
  212. }
  213. // clear any section data
  214. SetSectionID(-1);
  215. }
  216. void Clear()
  217. {
  218. m_bSelected = false;
  219. m_bOverrideColors = false;
  220. m_iSectionID = -1;
  221. SetPaintBackgroundEnabled( true );
  222. SetTextImageIndex(-1);
  223. ClearImages();
  224. }
  225. int GetID()
  226. {
  227. return m_iID;
  228. }
  229. void SetID(int itemID)
  230. {
  231. m_iID = itemID;
  232. }
  233. int GetSectionID()
  234. {
  235. return m_iSectionID;
  236. }
  237. void SetSectionID(int sectionID)
  238. {
  239. if (sectionID != m_iSectionID)
  240. {
  241. // free any existing textimage list
  242. ClearImages();
  243. // delete any images we've created
  244. for (int i = 0; i < m_TextImages.Count(); i++)
  245. {
  246. delete m_TextImages[i];
  247. }
  248. m_TextImages.RemoveAll();
  249. // mark the list as needing rebuilding
  250. InvalidateLayout();
  251. }
  252. m_iSectionID = sectionID;
  253. }
  254. void SetData(const KeyValues *data)
  255. {
  256. if (m_pData)
  257. {
  258. m_pData->deleteThis();
  259. }
  260. m_pData = data->MakeCopy();
  261. InvalidateLayout();
  262. }
  263. KeyValues *GetData()
  264. {
  265. return m_pData;
  266. }
  267. virtual void PerformLayout()
  268. {
  269. // get our button text
  270. int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID);
  271. if (!m_pData || colCount < 1)
  272. {
  273. SetText("< unset >");
  274. }
  275. else
  276. {
  277. if (colCount != GetImageCount())
  278. {
  279. // rebuild the image list
  280. for (int i = 0; i < colCount; i++)
  281. {
  282. int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i);
  283. if (!(columnFlags & SectionedListPanel::COLUMN_IMAGE))
  284. {
  285. TextImage *image = new TextImage("");
  286. m_TextImages.AddToTail(image);
  287. image->SetFont( GetFont() );
  288. HFont fallback = m_pListPanel->GetColumnFallbackFontBySection( m_iSectionID, i );
  289. if ( INVALID_FONT != fallback )
  290. {
  291. image->SetUseFallbackFont( true, fallback );
  292. }
  293. SetImageAtIndex(i, image, 0);
  294. }
  295. }
  296. {for ( int i = GetImageCount(); i < colCount; i++ ) // make sure we have enough image slots
  297. {
  298. AddImage( NULL, 0 );
  299. }}
  300. }
  301. // set the text for each column
  302. int xpos = 0;
  303. for (int i = 0; i < colCount; i++)
  304. {
  305. const char *keyname = m_pListPanel->GetColumnNameBySection(m_iSectionID, i);
  306. int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i);
  307. int maxWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i);
  308. IImage *image = NULL;
  309. if (columnFlags & SectionedListPanel::COLUMN_IMAGE)
  310. {
  311. // lookup which image is being referred to
  312. if (m_pListPanel->m_pImageList)
  313. {
  314. int imageIndex = m_pData->GetInt(keyname, 0);
  315. if (m_pListPanel->m_pImageList->IsValidIndex(imageIndex))
  316. {
  317. // 0 is always the blank image
  318. if (imageIndex > 0)
  319. {
  320. image = m_pListPanel->m_pImageList->GetImage(imageIndex);
  321. SetImageAtIndex(i, image, 0);
  322. }
  323. }
  324. else
  325. {
  326. // this is mildly valid (CGamesList hits it because of the way it uses the image indices)
  327. // Assert(!("Image index out of range for ImageList in SectionedListPanel"));
  328. }
  329. }
  330. else
  331. {
  332. Assert(!("Images columns used in SectionedListPanel with no ImageList set"));
  333. }
  334. }
  335. else
  336. {
  337. TextImage *textImage = dynamic_cast<TextImage *>(GetImageAtIndex(i));
  338. if (textImage)
  339. {
  340. textImage->SetText(m_pData->GetString(keyname, ""));
  341. textImage->ResizeImageToContentMaxWidth( maxWidth );
  342. // set the text color based on the selection state - if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected
  343. VPANEL focus = input()->GetFocus();
  344. if ( !m_bOverrideColors )
  345. {
  346. if (IsSelected() && !m_pListPanel->IsInEditMode())
  347. {
  348. if (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent())))
  349. {
  350. textImage->SetColor(m_ArmedFgColor2);
  351. }
  352. else
  353. {
  354. textImage->SetColor(m_OutOfFocusSelectedTextColor);
  355. }
  356. }
  357. else if (columnFlags & SectionedListPanel::COLUMN_BRIGHT)
  358. {
  359. textImage->SetColor(m_ArmedFgColor1);
  360. }
  361. else
  362. {
  363. textImage->SetColor(m_FgColor2);
  364. }
  365. }
  366. else
  367. {
  368. // custom colors
  369. if (IsSelected() && (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent()))))
  370. {
  371. textImage->SetColor(m_ArmedFgColor2);
  372. }
  373. else
  374. {
  375. textImage->SetColor(GetFgColor());
  376. }
  377. }
  378. }
  379. image = textImage;
  380. }
  381. // set the image position within the label
  382. int imageWide = 0, tall = 0;
  383. int wide;
  384. if (image)
  385. {
  386. image->GetContentSize(imageWide, tall);
  387. }
  388. if (maxWidth >= 0)
  389. {
  390. wide = maxWidth;
  391. }
  392. else
  393. {
  394. wide = imageWide;
  395. }
  396. if (i == 0 && !(columnFlags & SectionedListPanel::COLUMN_IMAGE))
  397. {
  398. // first column has an extra indent
  399. SetImageBounds(i, xpos + COLUMN_DATA_INDENT, wide - (COLUMN_DATA_INDENT + COLUMN_DATA_GAP));
  400. }
  401. else
  402. {
  403. if (columnFlags & SectionedListPanel::COLUMN_CENTER)
  404. {
  405. int offSet = (wide / 2) - (imageWide / 2);
  406. SetImageBounds(i, xpos + offSet, wide - offSet - COLUMN_DATA_GAP);
  407. }
  408. else if (columnFlags & SectionedListPanel::COLUMN_RIGHT)
  409. {
  410. SetImageBounds(i, xpos + wide - imageWide, wide - COLUMN_DATA_GAP);
  411. }
  412. else
  413. {
  414. SetImageBounds(i, xpos, wide - COLUMN_DATA_GAP);
  415. }
  416. }
  417. xpos += wide;
  418. }
  419. }
  420. BaseClass::PerformLayout();
  421. }
  422. virtual void ApplySchemeSettings(IScheme *pScheme)
  423. {
  424. BaseClass::ApplySchemeSettings(pScheme);
  425. m_ArmedFgColor1 = GetSchemeColor("SectionedListPanel.BrightTextColor", pScheme);
  426. m_ArmedFgColor2 = GetSchemeColor("SectionedListPanel.SelectedTextColor", pScheme);
  427. m_OutOfFocusSelectedTextColor = GetSchemeColor("SectionedListPanel.OutOfFocusSelectedTextColor", pScheme);
  428. m_ArmedBgColor = GetSchemeColor("SectionedListPanel.SelectedBgColor", pScheme);
  429. m_FgColor2 = GetSchemeColor("SectionedListPanel.TextColor", pScheme);
  430. m_BgColor = GetSchemeColor("SectionedListPanel.BgColor", GetBgColor(), pScheme);
  431. m_SelectionBG2Color = GetSchemeColor("SectionedListPanel.OutOfFocusSelectedBgColor", pScheme);
  432. m_MouseOverBgColor = GetSchemeColor("SectionedListPanel.MouseOverBgColor", pScheme);
  433. HFont hFont = m_pListPanel->GetRowFont();
  434. if ( hFont != INVALID_FONT )
  435. {
  436. SetFont( hFont );
  437. }
  438. else
  439. {
  440. const char *fontName = pScheme->GetResourceString( "SectionedListPanel.Font" );
  441. HFont font = pScheme->GetFont(fontName, IsProportional());
  442. if ( font != INVALID_FONT )
  443. {
  444. SetFont( font );
  445. }
  446. }
  447. ClearImages();
  448. }
  449. virtual void PaintBackground()
  450. {
  451. int wide, tall;
  452. GetSize(wide, tall);
  453. if (IsSelected() && !m_pListPanel->IsInEditMode())
  454. {
  455. VPANEL focus = input()->GetFocus();
  456. // if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected
  457. if (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent())))
  458. {
  459. surface()->DrawSetColor(m_ArmedBgColor);
  460. }
  461. else
  462. {
  463. surface()->DrawSetColor(m_SelectionBG2Color);
  464. }
  465. }
  466. else
  467. {
  468. if ( IsCursorOver() && !m_pListPanel->IsInEditMode() )
  469. {
  470. surface()->DrawSetColor( m_MouseOverBgColor );
  471. }
  472. else
  473. {
  474. surface()->DrawSetColor(GetBgColor());
  475. }
  476. }
  477. surface()->DrawFilledRect(0, 0, wide, tall);
  478. }
  479. virtual void Paint()
  480. {
  481. BaseClass::Paint();
  482. if ( !m_bShowColumns )
  483. return;
  484. // Debugging code to show column widths
  485. int wide, tall;
  486. GetSize(wide, tall);
  487. surface()->DrawSetColor( 255,255,255,255 );
  488. surface()->DrawOutlinedRect(0, 0, wide, tall);
  489. int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID);
  490. if (m_pData && colCount >= 0)
  491. {
  492. int xpos = 0;
  493. for (int i = 0; i < colCount; i++)
  494. {
  495. const char *keyname = m_pListPanel->GetColumnNameBySection(m_iSectionID, i);
  496. int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i);
  497. int maxWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i);
  498. IImage *image = NULL;
  499. if (columnFlags & SectionedListPanel::COLUMN_IMAGE)
  500. {
  501. // lookup which image is being referred to
  502. if (m_pListPanel->m_pImageList)
  503. {
  504. int imageIndex = m_pData->GetInt(keyname, 0);
  505. if (m_pListPanel->m_pImageList->IsValidIndex(imageIndex))
  506. {
  507. if (imageIndex > 0)
  508. {
  509. image = m_pListPanel->m_pImageList->GetImage(imageIndex);
  510. }
  511. }
  512. }
  513. }
  514. else
  515. {
  516. image = GetImageAtIndex(i);
  517. }
  518. int imageWide = 0, tall = 0;
  519. int wide;
  520. if (image)
  521. {
  522. image->GetContentSize(imageWide, tall);
  523. }
  524. if (maxWidth >= 0)
  525. {
  526. wide = maxWidth;
  527. }
  528. else
  529. {
  530. wide = imageWide;
  531. }
  532. xpos += wide;//max(maxWidth,wide);
  533. surface()->DrawOutlinedRect( xpos, 0, xpos, GetTall() );
  534. }
  535. }
  536. }
  537. virtual void OnMousePressed(MouseCode code)
  538. {
  539. //=============================================================================
  540. // HPE_BEGIN:
  541. // [tj] Only do this if clicking is enabled.
  542. //=============================================================================
  543. if (m_pListPanel && m_pListPanel->IsClickable())
  544. {
  545. if (code == MOUSE_LEFT)
  546. {
  547. m_pListPanel->PostActionSignal(new KeyValues("ItemLeftClick", "itemID", m_iID));
  548. }
  549. if (code == MOUSE_RIGHT)
  550. {
  551. KeyValues *msg = new KeyValues("ItemContextMenu", "itemID", m_iID);
  552. msg->SetPtr("SubPanel", this);
  553. m_pListPanel->PostActionSignal(msg);
  554. }
  555. m_pListPanel->SetSelectedItem(this);
  556. }
  557. //=============================================================================
  558. // HPE_END
  559. //=============================================================================
  560. }
  561. void SetSelected(bool state)
  562. {
  563. if (m_bSelected != state)
  564. {
  565. if (state)
  566. {
  567. RequestFocus();
  568. }
  569. m_bSelected = state;
  570. InvalidateLayout();
  571. Repaint();
  572. }
  573. }
  574. bool IsSelected()
  575. {
  576. return m_bSelected;
  577. }
  578. virtual void OnSetFocus()
  579. {
  580. InvalidateLayout(); // force the layout to be redone so we can change text color according to focus
  581. BaseClass::OnSetFocus();
  582. }
  583. virtual void OnKillFocus()
  584. {
  585. InvalidateLayout(); // force the layout to be redone so we can change text color according to focus
  586. BaseClass::OnSetFocus();
  587. }
  588. virtual void OnMouseDoublePressed(MouseCode code)
  589. {
  590. //=============================================================================
  591. // HPE_BEGIN:
  592. // [tj] Only do this if clicking is enabled.
  593. //=============================================================================
  594. if (m_pListPanel && m_pListPanel->IsClickable())
  595. {
  596. if (code == MOUSE_LEFT)
  597. {
  598. m_pListPanel->PostActionSignal(new KeyValues("ItemDoubleLeftClick", "itemID", m_iID));
  599. // post up an enter key being hit
  600. m_pListPanel->OnKeyCodeTyped(KEY_ENTER);
  601. }
  602. else
  603. {
  604. OnMousePressed(code);
  605. }
  606. m_pListPanel->SetSelectedItem(this);
  607. }
  608. //=============================================================================
  609. // HPE_END
  610. //=============================================================================
  611. }
  612. void GetCellBounds(int column, int &xpos, int &columnWide)
  613. {
  614. xpos = 0, columnWide = 0;
  615. int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID);
  616. for (int i = 0; i < colCount; i++)
  617. {
  618. int maxWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i);
  619. IImage *image = GetImageAtIndex(i);
  620. if (!image)
  621. continue;
  622. // set the image position within the label
  623. int wide, tall;
  624. image->GetContentSize(wide, tall);
  625. if (maxWidth >= 0)
  626. {
  627. wide = maxWidth;
  628. }
  629. if (i == column)
  630. {
  631. // found the cell size, bail
  632. columnWide = wide;
  633. return;
  634. }
  635. xpos += wide;
  636. }
  637. }
  638. //=============================================================================
  639. // HPE_BEGIN:
  640. // [menglish] gets the local coordinates of a cell using the max width for every column
  641. //=============================================================================
  642. void GetMaxCellBounds(int column, int &xpos, int &columnWide)
  643. {
  644. xpos = 0, columnWide = 0;
  645. int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID);
  646. for (int i = 0; i < colCount; i++)
  647. {
  648. int maxWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i);
  649. if (i == column)
  650. {
  651. // found the cell size, bail
  652. columnWide = maxWidth;
  653. return;
  654. }
  655. xpos += maxWidth;
  656. }
  657. }
  658. //=============================================================================
  659. // HPE_END
  660. //=============================================================================
  661. virtual void SetOverrideColors( bool state )
  662. {
  663. m_bOverrideColors = state;
  664. }
  665. void SetShowColumns( bool bShow )
  666. {
  667. m_bShowColumns = bShow;
  668. }
  669. private:
  670. SectionedListPanel *m_pListPanel;
  671. int m_iID;
  672. int m_iSectionID;
  673. KeyValues *m_pData;
  674. Color m_FgColor2;
  675. Color m_BgColor;
  676. Color m_ArmedFgColor1;
  677. Color m_ArmedFgColor2;
  678. Color m_OutOfFocusSelectedTextColor;
  679. Color m_ArmedBgColor;
  680. Color m_SelectionBG2Color;
  681. Color m_MouseOverBgColor;
  682. CUtlVector<vgui::TextImage *> m_TextImages;
  683. bool m_bSelected;
  684. bool m_bOverrideColors;
  685. bool m_bShowColumns;
  686. };
  687. }; // namespace vgui
  688. DECLARE_BUILD_FACTORY( SectionedListPanel );
  689. //-----------------------------------------------------------------------------
  690. // Purpose: Constructor
  691. //-----------------------------------------------------------------------------
  692. SectionedListPanel::SectionedListPanel(vgui::Panel *parent, const char *name) : BaseClass(parent, name)
  693. {
  694. m_pScrollBar = new ScrollBar(this, "SectionedScrollBar", true);
  695. m_pScrollBar->SetVisible(false);
  696. m_pScrollBar->AddActionSignalTarget(this);
  697. m_iEditModeItemID = 0;
  698. m_iEditModeColumn = 0;
  699. m_bSortNeeded = false;
  700. m_bVerticalScrollbarEnabled = true;
  701. m_iLineSpacing = 20;
  702. m_pImageList = NULL;
  703. m_bDeleteImageListWhenDone = false;
  704. m_hHeaderFont = INVALID_FONT;
  705. m_hRowFont = INVALID_FONT;
  706. //=============================================================================
  707. // HPE_BEGIN:
  708. //=============================================================================
  709. // [tj] Default clickability to true so existing controls aren't affected.
  710. m_clickable = true;
  711. // [tj] draw section headers by default so existing controls aren't affected.
  712. m_bDrawSectionHeaders = true;
  713. //=============================================================================
  714. // HPE_END
  715. //=============================================================================
  716. }
  717. //-----------------------------------------------------------------------------
  718. // Purpose: Destructor
  719. //-----------------------------------------------------------------------------
  720. SectionedListPanel::~SectionedListPanel()
  721. {
  722. }
  723. //-----------------------------------------------------------------------------
  724. // Purpose: Sorts the list
  725. //-----------------------------------------------------------------------------
  726. void SectionedListPanel::ReSortList()
  727. {
  728. m_SortedItems.RemoveAll();
  729. int sectionStart = 0;
  730. // layout the buttons
  731. for (int sectionIndex = 0; sectionIndex < m_Sections.Count(); sectionIndex++)
  732. {
  733. section_t &section = m_Sections[sectionIndex];
  734. sectionStart = m_SortedItems.Count();
  735. // find all the items in this section
  736. for( int i = m_Items.Head(); i != m_Items.InvalidIndex(); i = m_Items.Next( i ) )
  737. {
  738. if (m_Items[i]->GetSectionID() == m_Sections[sectionIndex].m_iID)
  739. {
  740. // insert the items sorted
  741. if (section.m_pSortFunc)
  742. {
  743. int insertionPoint = sectionStart;
  744. for (;insertionPoint < m_SortedItems.Count(); insertionPoint++)
  745. {
  746. if (section.m_pSortFunc(this, i, m_SortedItems[insertionPoint]->GetID()))
  747. break;
  748. }
  749. if (insertionPoint == m_SortedItems.Count())
  750. {
  751. m_SortedItems.AddToTail(m_Items[i]);
  752. }
  753. else
  754. {
  755. m_SortedItems.InsertBefore(insertionPoint, m_Items[i]);
  756. }
  757. }
  758. else
  759. {
  760. // just add to the end
  761. m_SortedItems.AddToTail(m_Items[i]);
  762. }
  763. }
  764. }
  765. }
  766. }
  767. //-----------------------------------------------------------------------------
  768. // Purpose: iterates through and sets up the position of all the sections and items
  769. //-----------------------------------------------------------------------------
  770. void SectionedListPanel::PerformLayout()
  771. {
  772. // lazy resort the list
  773. if (m_bSortNeeded)
  774. {
  775. ReSortList();
  776. m_bSortNeeded = false;
  777. }
  778. BaseClass::PerformLayout();
  779. LayoutPanels(m_iContentHeight);
  780. int cx, cy, cwide, ctall;
  781. GetBounds(cx, cy, cwide, ctall);
  782. if (m_iContentHeight > ctall && m_bVerticalScrollbarEnabled)
  783. {
  784. m_pScrollBar->SetVisible(true);
  785. m_pScrollBar->MoveToFront();
  786. m_pScrollBar->SetPos(cwide - m_pScrollBar->GetWide() - 2, 0);
  787. m_pScrollBar->SetSize(m_pScrollBar->GetWide(), ctall - 2);
  788. m_pScrollBar->SetRangeWindow(ctall);
  789. m_pScrollBar->SetRange(0, m_iContentHeight);
  790. m_pScrollBar->InvalidateLayout();
  791. m_pScrollBar->Repaint();
  792. // since we're just about to make the scrollbar visible, we need to re-layout
  793. // the buttons since they depend on the scrollbar size
  794. LayoutPanels(m_iContentHeight);
  795. }
  796. else
  797. {
  798. m_pScrollBar->SetValue(0);
  799. m_pScrollBar->SetVisible(false);
  800. }
  801. }
  802. //-----------------------------------------------------------------------------
  803. // Purpose: lays out the sections and rows in the panel
  804. //-----------------------------------------------------------------------------
  805. void SectionedListPanel::LayoutPanels(int &contentTall)
  806. {
  807. int tall = GetSectionTall();
  808. int x = 5, wide = GetWide() - 10;
  809. int y = 5;
  810. if (m_pScrollBar->IsVisible())
  811. {
  812. y -= m_pScrollBar->GetValue();
  813. wide -= m_pScrollBar->GetWide();
  814. }
  815. int iStart = -1;
  816. int iEnd = -1;
  817. // layout the buttons
  818. for (int sectionIndex = 0; sectionIndex < m_Sections.Count(); sectionIndex++)
  819. {
  820. section_t &section = m_Sections[sectionIndex];
  821. iStart = -1;
  822. iEnd = -1;
  823. for (int i = 0; i < m_SortedItems.Count(); i++)
  824. {
  825. if (m_SortedItems[i]->GetSectionID() == m_Sections[sectionIndex].m_iID)
  826. {
  827. if (iStart == -1)
  828. iStart = i;
  829. iEnd = i;
  830. }
  831. }
  832. // don't draw this section at all if their are no item in it
  833. if (iStart == -1 && !section.m_bAlwaysVisible)
  834. {
  835. section.m_pHeader->SetVisible(false);
  836. continue;
  837. }
  838. //=============================================================================
  839. // HPE_BEGIN:
  840. // [tj] Only draw the header if it is enabled
  841. //=============================================================================
  842. if (m_bDrawSectionHeaders)
  843. {
  844. // draw the header
  845. section.m_pHeader->SetBounds(x, y, wide, tall);
  846. section.m_pHeader->SetVisible(true);
  847. y += tall;
  848. }
  849. else
  850. {
  851. section.m_pHeader->SetVisible(false);
  852. }
  853. //=============================================================================
  854. // HPE_END
  855. //=============================================================================
  856. if (iStart == -1 && section.m_bAlwaysVisible)
  857. {
  858. }
  859. else
  860. {
  861. // arrange all the items in this section underneath
  862. for (int i = iStart; i <= iEnd; i++)
  863. {
  864. CItemButton *item = m_SortedItems[i]; //items[i];
  865. item->SetBounds(x, y, wide, m_iLineSpacing);
  866. // setup edit mode
  867. if (m_hEditModePanel.Get() && m_iEditModeItemID == item->GetID())
  868. {
  869. int cx, cwide;
  870. item->GetCellBounds(1, cx, cwide);
  871. m_hEditModePanel->SetBounds(cx, y, cwide, tall);
  872. }
  873. y += m_iLineSpacing;
  874. }
  875. }
  876. // add in a little boundry at the bottom
  877. y += SECTION_GAP;
  878. }
  879. // calculate height
  880. contentTall = y;
  881. if (m_pScrollBar->IsVisible())
  882. {
  883. contentTall += m_pScrollBar->GetValue();
  884. }
  885. }
  886. //-----------------------------------------------------------------------------
  887. // Purpose: Ensures that the specified item is visible in the display
  888. //-----------------------------------------------------------------------------
  889. void SectionedListPanel::ScrollToItem(int iItem)
  890. {
  891. int tall = GetSectionTall();
  892. int itemX, itemY ;
  893. int nCurrentValue = m_pScrollBar->GetValue();
  894. // find out where the item is
  895. m_Items[iItem]->GetPos(itemX, itemY);
  896. // add in the current scrollbar position
  897. itemY += nCurrentValue;
  898. // compare that in the list
  899. int cx, cy, cwide, ctall;
  900. GetBounds(cx, cy, cwide, ctall);
  901. if (m_iContentHeight > ctall)
  902. {
  903. if (itemY < nCurrentValue)
  904. {
  905. // scroll up
  906. m_pScrollBar->SetValue(itemY);
  907. }
  908. else if (itemY > nCurrentValue + ctall - tall)
  909. {
  910. // scroll down
  911. m_pScrollBar->SetValue(itemY - ctall + tall);
  912. }
  913. else
  914. {
  915. // keep the current value
  916. }
  917. }
  918. else
  919. {
  920. // area isn't big enough, just remove the scrollbar
  921. m_pScrollBar->SetValue(0);
  922. }
  923. // reset scrollbar
  924. Repaint();
  925. }
  926. //-----------------------------------------------------------------------------
  927. // Purpose: sets background color & border
  928. //-----------------------------------------------------------------------------
  929. void SectionedListPanel::ApplySchemeSettings(IScheme *pScheme)
  930. {
  931. BaseClass::ApplySchemeSettings(pScheme);
  932. SetBgColor(GetSchemeColor("SectionedListPanel.BgColor", GetBgColor(), pScheme));
  933. SetBorder(pScheme->GetBorder("ButtonDepressedBorder"));
  934. FOR_EACH_LL( m_Items, j )
  935. {
  936. m_Items[j]->SetShowColumns( m_bShowColumns );
  937. }
  938. }
  939. //-----------------------------------------------------------------------------
  940. // Purpose:
  941. //-----------------------------------------------------------------------------
  942. void SectionedListPanel::SetHeaderFont( HFont hFont )
  943. {
  944. m_hHeaderFont = hFont;
  945. }
  946. //-----------------------------------------------------------------------------
  947. // Purpose:
  948. //-----------------------------------------------------------------------------
  949. HFont SectionedListPanel::GetHeaderFont( void ) const
  950. {
  951. return m_hHeaderFont;
  952. }
  953. //-----------------------------------------------------------------------------
  954. // Purpose:
  955. //-----------------------------------------------------------------------------
  956. void SectionedListPanel::SetRowFont( HFont hFont )
  957. {
  958. m_hRowFont = hFont;
  959. }
  960. //-----------------------------------------------------------------------------
  961. // Purpose:
  962. //-----------------------------------------------------------------------------
  963. HFont SectionedListPanel::GetRowFont( void ) const
  964. {
  965. return m_hRowFont;
  966. }
  967. //-----------------------------------------------------------------------------
  968. // Purpose:
  969. //-----------------------------------------------------------------------------
  970. void SectionedListPanel::ApplySettings(KeyValues *inResourceData)
  971. {
  972. BaseClass::ApplySettings(inResourceData);
  973. m_iLineSpacing = inResourceData->GetInt("linespacing", 0);
  974. if (!m_iLineSpacing)
  975. {
  976. m_iLineSpacing = 20;
  977. }
  978. if (IsProportional())
  979. {
  980. m_iLineSpacing = scheme()->GetProportionalScaledValueEx(GetScheme(), m_iLineSpacing);
  981. }
  982. }
  983. //-----------------------------------------------------------------------------
  984. // Purpose: passes on proportional state to children
  985. //-----------------------------------------------------------------------------
  986. void SectionedListPanel::SetProportional(bool state)
  987. {
  988. BaseClass::SetProportional(state);
  989. // now setup the section headers and items
  990. int i;
  991. for (i = 0; i < m_Sections.Count(); i++)
  992. {
  993. m_Sections[i].m_pHeader->SetProportional(state);
  994. }
  995. FOR_EACH_LL( m_Items, j )
  996. {
  997. m_Items[j]->SetProportional(state);
  998. }
  999. }
  1000. //-----------------------------------------------------------------------------
  1001. // Purpose: sets whether or not the vertical scrollbar should ever be displayed
  1002. //-----------------------------------------------------------------------------
  1003. void SectionedListPanel::SetVerticalScrollbar(bool state)
  1004. {
  1005. m_bVerticalScrollbarEnabled = state;
  1006. }
  1007. //-----------------------------------------------------------------------------
  1008. // Purpose: adds a new section
  1009. //-----------------------------------------------------------------------------
  1010. void SectionedListPanel::AddSection(int sectionID, const char *name, SectionSortFunc_t sortFunc)
  1011. {
  1012. CSectionHeader *header = SETUP_PANEL(new CSectionHeader(this, name, sectionID));
  1013. AddSectionHelper(sectionID, header, sortFunc);
  1014. }
  1015. //-----------------------------------------------------------------------------
  1016. // Purpose: adds a new section
  1017. //-----------------------------------------------------------------------------
  1018. void SectionedListPanel::AddSection(int sectionID, const wchar_t *name, SectionSortFunc_t sortFunc)
  1019. {
  1020. CSectionHeader *header = SETUP_PANEL(new CSectionHeader(this, name, sectionID));
  1021. AddSectionHelper(sectionID, header, sortFunc);
  1022. }
  1023. //-----------------------------------------------------------------------------
  1024. // Purpose: helper function for AddSection
  1025. //-----------------------------------------------------------------------------
  1026. void SectionedListPanel::AddSectionHelper(int sectionID, CSectionHeader *header, SectionSortFunc_t sortFunc)
  1027. {
  1028. int index = m_Sections.AddToTail();
  1029. m_Sections[index].m_iID = sectionID;
  1030. m_Sections[index].m_pHeader = header;
  1031. m_Sections[index].m_pSortFunc = sortFunc;
  1032. m_Sections[index].m_bAlwaysVisible = false;
  1033. }
  1034. //-----------------------------------------------------------------------------
  1035. // Purpose: removes all the sections from the current panel
  1036. //-----------------------------------------------------------------------------
  1037. void SectionedListPanel::RemoveAllSections()
  1038. {
  1039. for (int i = 0; i < m_Sections.Count(); i++)
  1040. {
  1041. if (!m_Sections.IsValidIndex(i))
  1042. continue;
  1043. m_Sections[i].m_pHeader->SetVisible(false);
  1044. m_Sections[i].m_pHeader->MarkForDeletion();
  1045. }
  1046. m_Sections.RemoveAll();
  1047. m_Sections.Purge();
  1048. m_SortedItems.RemoveAll();
  1049. InvalidateLayout();
  1050. ReSortList();
  1051. }
  1052. //-----------------------------------------------------------------------------
  1053. // Purpose: adds a new column to a section
  1054. //-----------------------------------------------------------------------------
  1055. bool SectionedListPanel::AddColumnToSection(int sectionID, const char *columnName, const char *columnText, int columnFlags, int width, HFont fallbackFont /*= INVALID_FONT*/ )
  1056. {
  1057. wchar_t wtext[64];
  1058. wchar_t *pwtext = g_pVGuiLocalize->Find(columnText);
  1059. if (!pwtext)
  1060. {
  1061. g_pVGuiLocalize->ConvertANSIToUnicode(columnText, wtext, sizeof(wtext));
  1062. pwtext = wtext;
  1063. }
  1064. return AddColumnToSection(sectionID, columnName, pwtext, columnFlags, width, fallbackFont );
  1065. }
  1066. //-----------------------------------------------------------------------------
  1067. // Purpose: as above but with wchar_t's
  1068. //-----------------------------------------------------------------------------
  1069. bool SectionedListPanel::AddColumnToSection(int sectionID, const char *columnName, const wchar_t *columnText, int columnFlags, int width, HFont fallbackFont /*= INVALID_FONT*/ )
  1070. {
  1071. int index = FindSectionIndexByID(sectionID);
  1072. if (index < 0)
  1073. return false;
  1074. section_t &section = m_Sections[index];
  1075. // add the new column to the sections' list
  1076. index = section.m_Columns.AddToTail();
  1077. column_t &column = section.m_Columns[index];
  1078. Q_strncpy(column.m_szColumnName, columnName, sizeof(column.m_szColumnName));
  1079. wcsncpy(column.m_szColumnText, columnText, sizeof(column.m_szColumnText) / sizeof(wchar_t));
  1080. column.m_szColumnText[sizeof(column.m_szColumnText) / sizeof(wchar_t) - 1] = 0;
  1081. column.m_iColumnFlags = columnFlags;
  1082. column.m_iWidth = width;
  1083. column.m_hFallbackFont = fallbackFont;
  1084. return true;
  1085. }
  1086. //-----------------------------------------------------------------------------
  1087. // Purpose: modifies the text in an existing column
  1088. //-----------------------------------------------------------------------------
  1089. bool SectionedListPanel::ModifyColumn(int sectionID, const char *columnName, const wchar_t *columnText)
  1090. {
  1091. int index = FindSectionIndexByID(sectionID);
  1092. if (index < 0)
  1093. return false;
  1094. section_t &section = m_Sections[index];
  1095. // find the specified column
  1096. int columnIndex;
  1097. for (columnIndex = 0; columnIndex < section.m_Columns.Count(); columnIndex++)
  1098. {
  1099. if (!stricmp(section.m_Columns[columnIndex].m_szColumnName, columnName))
  1100. break;
  1101. }
  1102. if (!section.m_Columns.IsValidIndex(columnIndex))
  1103. return false;
  1104. column_t &column = section.m_Columns[columnIndex];
  1105. // modify the text
  1106. wcsncpy(column.m_szColumnText, columnText, sizeof(column.m_szColumnText) / sizeof(wchar_t));
  1107. column.m_szColumnText[sizeof(column.m_szColumnText) / sizeof(wchar_t) - 1] = 0;
  1108. section.m_pHeader->InvalidateLayout();
  1109. return true;
  1110. }
  1111. //-----------------------------------------------------------------------------
  1112. // Purpose: adds an item to the list; returns itemID
  1113. //-----------------------------------------------------------------------------
  1114. int SectionedListPanel::AddItem(int sectionID, const KeyValues *data)
  1115. {
  1116. int itemID = GetNewItemButton();
  1117. ModifyItem(itemID, sectionID, data);
  1118. // not sorted but in list
  1119. m_SortedItems.AddToTail(m_Items[itemID]);
  1120. m_bSortNeeded = true;
  1121. return itemID;
  1122. }
  1123. //-----------------------------------------------------------------------------
  1124. // Purpose: modifies an existing item; returns false if the item does not exist
  1125. //-----------------------------------------------------------------------------
  1126. bool SectionedListPanel::ModifyItem(int itemID, int sectionID, const KeyValues *data)
  1127. {
  1128. if ( !m_Items.IsValidIndex(itemID) )
  1129. return false;
  1130. InvalidateLayout();
  1131. m_Items[itemID]->SetSectionID(sectionID);
  1132. m_Items[itemID]->SetData(data);
  1133. m_Items[itemID]->InvalidateLayout();
  1134. m_bSortNeeded = true;
  1135. return true;
  1136. }
  1137. //-----------------------------------------------------------------------------
  1138. // Purpose:
  1139. //-----------------------------------------------------------------------------
  1140. void SectionedListPanel::SetItemFgColor( int itemID, Color color )
  1141. {
  1142. Assert( m_Items.IsValidIndex(itemID) );
  1143. if ( !m_Items.IsValidIndex(itemID) )
  1144. return;
  1145. m_Items[itemID]->SetFgColor( color );
  1146. m_Items[itemID]->SetOverrideColors( true );
  1147. m_Items[itemID]->InvalidateLayout();
  1148. }
  1149. //=============================================================================
  1150. // HPE_BEGIN:
  1151. // [menglish] Setter for the background color similar to the foreground color
  1152. //=============================================================================
  1153. void SectionedListPanel::SetItemBgColor( int itemID, Color color )
  1154. {
  1155. Assert( m_Items.IsValidIndex(itemID) );
  1156. if ( !m_Items.IsValidIndex(itemID) )
  1157. return;
  1158. m_Items[itemID]->SetBgColor( color );
  1159. m_Items[itemID]->SetPaintBackgroundEnabled( true );
  1160. m_Items[itemID]->SetOverrideColors( true );
  1161. m_Items[itemID]->InvalidateLayout();
  1162. }
  1163. void SectionedListPanel::SetItemFont( int itemID, HFont font )
  1164. {
  1165. Assert( m_Items.IsValidIndex(itemID) );
  1166. if ( !m_Items.IsValidIndex(itemID) )
  1167. return;
  1168. m_Items[itemID]->SetFont( font );
  1169. }
  1170. //=============================================================================
  1171. // HPE_END
  1172. //=============================================================================
  1173. //-----------------------------------------------------------------------------
  1174. // Purpose: sets the color of a section text & underline
  1175. //-----------------------------------------------------------------------------
  1176. void SectionedListPanel::SetSectionFgColor(int sectionID, Color color)
  1177. {
  1178. if (!m_Sections.IsValidIndex(sectionID))
  1179. return;
  1180. m_Sections[sectionID].m_pHeader->SetColor(color);
  1181. }
  1182. //-----------------------------------------------------------------------------
  1183. // Purpose: added so you can change the divider color AFTER the main color.
  1184. //-----------------------------------------------------------------------------
  1185. void SectionedListPanel::SetSectionDividerColor( int sectionID, Color color)
  1186. {
  1187. if (!m_Sections.IsValidIndex(sectionID))
  1188. return;
  1189. m_Sections[sectionID].m_pHeader->SetDividerColor(color);
  1190. }
  1191. //-----------------------------------------------------------------------------
  1192. // Purpose: forces a section to always be visible
  1193. //-----------------------------------------------------------------------------
  1194. void SectionedListPanel::SetSectionAlwaysVisible(int sectionID, bool visible)
  1195. {
  1196. if (!m_Sections.IsValidIndex(sectionID))
  1197. return;
  1198. m_Sections[sectionID].m_bAlwaysVisible = visible;
  1199. }
  1200. void SectionedListPanel::SetFontSection(int sectionID, HFont font)
  1201. {
  1202. if (!m_Sections.IsValidIndex(sectionID))
  1203. return;
  1204. m_Sections[sectionID].m_pHeader->SetFont(font);
  1205. }
  1206. //-----------------------------------------------------------------------------
  1207. // Purpose: removes an item from the list; returns false if the item does not exist or is already removed
  1208. //-----------------------------------------------------------------------------
  1209. bool SectionedListPanel::RemoveItem(int itemID)
  1210. {
  1211. if ( !m_Items.IsValidIndex(itemID) )
  1212. return false;
  1213. m_SortedItems.FindAndRemove(m_Items[itemID]);
  1214. m_bSortNeeded = true;
  1215. m_Items[itemID]->MarkForDeletion();
  1216. m_Items.Remove(itemID);
  1217. InvalidateLayout();
  1218. return true;
  1219. }
  1220. //-----------------------------------------------------------------------------
  1221. // Purpose: returns the number of columns in a section
  1222. //-----------------------------------------------------------------------------
  1223. int SectionedListPanel::GetColumnCountBySection(int sectionID)
  1224. {
  1225. int index = FindSectionIndexByID(sectionID);
  1226. if (index < 0)
  1227. return NULL;
  1228. return m_Sections[index].m_Columns.Count();
  1229. }
  1230. //-----------------------------------------------------------------------------
  1231. // Purpose: returns the name of a column by section and column index; returns NULL if there are no more columns
  1232. // valid range of columnIndex is [0, GetColumnCountBySection)
  1233. //-----------------------------------------------------------------------------
  1234. const char *SectionedListPanel::GetColumnNameBySection(int sectionID, int columnIndex)
  1235. {
  1236. int index = FindSectionIndexByID(sectionID);
  1237. if (index < 0 || columnIndex >= m_Sections[index].m_Columns.Count())
  1238. return NULL;
  1239. return m_Sections[index].m_Columns[columnIndex].m_szColumnName;
  1240. }
  1241. //-----------------------------------------------------------------------------
  1242. // Purpose: returns the text for a column by section and column index
  1243. //-----------------------------------------------------------------------------
  1244. const wchar_t *SectionedListPanel::GetColumnTextBySection(int sectionID, int columnIndex)
  1245. {
  1246. int index = FindSectionIndexByID(sectionID);
  1247. if (index < 0 || columnIndex >= m_Sections[index].m_Columns.Count())
  1248. return NULL;
  1249. return m_Sections[index].m_Columns[columnIndex].m_szColumnText;
  1250. }
  1251. //-----------------------------------------------------------------------------
  1252. // Purpose: returns the type of a column by section and column index
  1253. //-----------------------------------------------------------------------------
  1254. int SectionedListPanel::GetColumnFlagsBySection(int sectionID, int columnIndex)
  1255. {
  1256. int index = FindSectionIndexByID(sectionID);
  1257. if (index < 0)
  1258. return 0;
  1259. if (columnIndex >= m_Sections[index].m_Columns.Count())
  1260. return 0;
  1261. return m_Sections[index].m_Columns[columnIndex].m_iColumnFlags;
  1262. }
  1263. //-----------------------------------------------------------------------------
  1264. // Purpose:
  1265. //-----------------------------------------------------------------------------
  1266. int SectionedListPanel::GetColumnWidthBySection(int sectionID, int columnIndex)
  1267. {
  1268. int index = FindSectionIndexByID(sectionID);
  1269. if (index < 0)
  1270. return 0;
  1271. if (columnIndex >= m_Sections[index].m_Columns.Count())
  1272. return 0;
  1273. return m_Sections[index].m_Columns[columnIndex].m_iWidth;
  1274. }
  1275. //=============================================================================
  1276. // HPE_BEGIN:
  1277. // [menglish] Gets the column index by the string identifier
  1278. //=============================================================================
  1279. int SectionedListPanel::GetColumnIndexByName(int sectionID, char* name)
  1280. {
  1281. int index = FindSectionIndexByID(sectionID);
  1282. if (index < 0)
  1283. return 0;
  1284. for ( int columnIndex = 0; columnIndex < m_Sections[index].m_Columns.Count(); ++ columnIndex)
  1285. {
  1286. if( !V_strcmp( m_Sections[index].m_Columns[columnIndex].m_szColumnName, name ) )
  1287. return columnIndex;
  1288. }
  1289. return -1;
  1290. }
  1291. //=============================================================================
  1292. // HPE_END
  1293. //=============================================================================
  1294. //-----------------------------------------------------------------------------
  1295. // Purpose: returns -1 if section not found
  1296. //-----------------------------------------------------------------------------
  1297. int SectionedListPanel::FindSectionIndexByID(int sectionID)
  1298. {
  1299. for (int i = 0; i < m_Sections.Count(); i++)
  1300. {
  1301. if (m_Sections[i].m_iID == sectionID)
  1302. {
  1303. return i;
  1304. }
  1305. }
  1306. return -1;
  1307. }
  1308. //-----------------------------------------------------------------------------
  1309. // Purpose: Called when the scrollbar is moved
  1310. //-----------------------------------------------------------------------------
  1311. void SectionedListPanel::OnSliderMoved()
  1312. {
  1313. InvalidateLayout();
  1314. Repaint();
  1315. }
  1316. //-----------------------------------------------------------------------------
  1317. // Purpose: Scrolls the list according to the mouse wheel movement
  1318. //-----------------------------------------------------------------------------
  1319. void SectionedListPanel::OnMouseWheeled(int delta)
  1320. {
  1321. if (m_hEditModePanel.Get())
  1322. {
  1323. // ignore mouse wheel in edit mode, forward right up to parent
  1324. CallParentFunction(new KeyValues("MouseWheeled", "delta", delta));
  1325. return;
  1326. }
  1327. // scroll the window based on the delta
  1328. int val = m_pScrollBar->GetValue();
  1329. val -= (delta * BUTTON_HEIGHT_DEFAULT * 3);
  1330. m_pScrollBar->SetValue(val);
  1331. }
  1332. //-----------------------------------------------------------------------------
  1333. // Purpose: Resets the scrollbar position on size change
  1334. //-----------------------------------------------------------------------------
  1335. void SectionedListPanel::OnSizeChanged(int wide, int tall)
  1336. {
  1337. BaseClass::OnSizeChanged(wide, tall);
  1338. m_pScrollBar->SetValue(0);
  1339. InvalidateLayout();
  1340. Repaint();
  1341. }
  1342. //-----------------------------------------------------------------------------
  1343. // Purpose: deselects any items
  1344. //-----------------------------------------------------------------------------
  1345. void SectionedListPanel::OnMousePressed(MouseCode code)
  1346. {
  1347. //=============================================================================
  1348. // HPE_BEGIN:
  1349. // [tj] Only do this if clicking is enabled.
  1350. //=============================================================================
  1351. if (m_clickable){
  1352. ClearSelection();
  1353. }
  1354. //=============================================================================
  1355. // HPE_END
  1356. //=============================================================================}
  1357. }
  1358. //-----------------------------------------------------------------------------
  1359. // Purpose: deselects any items
  1360. //-----------------------------------------------------------------------------
  1361. void SectionedListPanel::ClearSelection( void )
  1362. {
  1363. SetSelectedItem((CItemButton *)NULL);
  1364. }
  1365. void SectionedListPanel::MoveSelectionDown( void )
  1366. {
  1367. int itemID = GetSelectedItem();
  1368. if (itemID == -1)
  1369. return;
  1370. if (!m_SortedItems.Count()) // if the list has been emptied
  1371. return;
  1372. int i;
  1373. for (i = 0; i < m_SortedItems.Count(); i++)
  1374. {
  1375. if (m_SortedItems[i]->GetID() == itemID)
  1376. break;
  1377. }
  1378. Assert(i != m_SortedItems.Count());
  1379. // we're already on the end
  1380. if (i >= m_SortedItems.Count() - 1)
  1381. return;
  1382. int newItemID = m_SortedItems[i + 1]->GetID();
  1383. SetSelectedItem(m_Items[newItemID]);
  1384. ScrollToItem(newItemID);
  1385. }
  1386. void SectionedListPanel::MoveSelectionUp( void )
  1387. {
  1388. int itemID = GetSelectedItem();
  1389. if (itemID == -1)
  1390. return;
  1391. if (!m_SortedItems.Count()) // if the list has been emptied
  1392. return;
  1393. int i;
  1394. for (i = 0; i < m_SortedItems.Count(); i++)
  1395. {
  1396. if (m_SortedItems[i]->GetID() == itemID)
  1397. break;
  1398. }
  1399. Assert(i != m_SortedItems.Count());
  1400. // we're already on the end
  1401. if (i == 0 || i >= m_SortedItems.Count() )
  1402. return;
  1403. int newItemID = m_SortedItems[i - 1]->GetID();
  1404. SetSelectedItem(m_Items[newItemID]);
  1405. ScrollToItem(newItemID);
  1406. }
  1407. //-----------------------------------------------------------------------------
  1408. // Purpose: arrow key movement handler
  1409. //-----------------------------------------------------------------------------
  1410. void SectionedListPanel::OnKeyCodeTyped(KeyCode code)
  1411. {
  1412. if (m_hEditModePanel.Get())
  1413. {
  1414. // ignore arrow keys in edit mode
  1415. // forward right up to parent so that tab focus change doesn't occur
  1416. CallParentFunction(new KeyValues("KeyCodeTyped", "code", code));
  1417. return;
  1418. }
  1419. int buttonTall = GetSectionTall();
  1420. if (code == KEY_DOWN)
  1421. {
  1422. MoveSelectionDown();
  1423. return;
  1424. }
  1425. else if (code == KEY_UP)
  1426. {
  1427. MoveSelectionUp();
  1428. return;
  1429. }
  1430. else if (code == KEY_PAGEDOWN)
  1431. {
  1432. // calculate info for # of rows
  1433. int cx, cy, cwide, ctall;
  1434. GetBounds(cx, cy, cwide, ctall);
  1435. int rowsperpage = ctall/buttonTall;
  1436. int itemID = GetSelectedItem();
  1437. int lastValidItem = itemID;
  1438. int secID = m_Items[itemID]->GetSectionID();
  1439. int i=0;
  1440. int row = m_SortedItems.Find(m_Items[itemID]);
  1441. while ( i < rowsperpage )
  1442. {
  1443. if ( m_SortedItems.IsValidIndex(++row) )
  1444. {
  1445. itemID = m_SortedItems[row]->GetID();
  1446. lastValidItem = itemID;
  1447. i++;
  1448. // if we switched sections, then count the section header as a row
  1449. if (m_Items[itemID]->GetSectionID() != secID)
  1450. {
  1451. secID = m_Items[itemID]->GetSectionID();
  1452. i++;
  1453. }
  1454. }
  1455. else
  1456. {
  1457. itemID = lastValidItem;
  1458. break;
  1459. }
  1460. }
  1461. SetSelectedItem(m_Items[itemID]);
  1462. ScrollToItem(itemID);
  1463. }
  1464. else if (code == KEY_PAGEUP)
  1465. {
  1466. // calculate info for # of rows
  1467. int cx, cy, cwide, ctall;
  1468. GetBounds(cx, cy, cwide, ctall);
  1469. int rowsperpage = ctall/buttonTall;
  1470. int itemID = GetSelectedItem();
  1471. int lastValidItem = itemID;
  1472. int secID = m_Items[itemID]->GetSectionID();
  1473. int i=0;
  1474. int row = m_SortedItems.Find(m_Items[itemID]);
  1475. while ( i < rowsperpage )
  1476. {
  1477. if ( m_SortedItems.IsValidIndex(--row) )
  1478. {
  1479. itemID = m_SortedItems[row]->GetID();
  1480. lastValidItem = itemID;
  1481. i++;
  1482. // if we switched sections, then count the section header as a row
  1483. if (m_Items[itemID]->GetSectionID() != secID)
  1484. {
  1485. secID = m_Items[itemID]->GetSectionID();
  1486. i++;
  1487. }
  1488. }
  1489. else
  1490. {
  1491. SetSelectedItem(m_Items[lastValidItem]);
  1492. m_pScrollBar->SetValue(0);
  1493. return;
  1494. }
  1495. }
  1496. SetSelectedItem(m_Items[itemID]);
  1497. ScrollToItem(itemID);
  1498. }
  1499. else if (code == KEY_LEFT || code == KEY_RIGHT)
  1500. {
  1501. }
  1502. else
  1503. {
  1504. BaseClass::OnKeyCodeTyped(code);
  1505. }
  1506. }
  1507. //-----------------------------------------------------------------------------
  1508. // Purpose: Clears the list
  1509. //-----------------------------------------------------------------------------
  1510. void SectionedListPanel::DeleteAllItems()
  1511. {
  1512. FOR_EACH_LL( m_Items, i )
  1513. {
  1514. m_Items[i]->SetVisible(false);
  1515. m_Items[i]->Clear();
  1516. // don't delete, move to free list
  1517. int freeIndex = m_FreeItems.AddToTail();
  1518. m_FreeItems[freeIndex] = m_Items[i];
  1519. }
  1520. m_Items.RemoveAll();
  1521. m_SortedItems.RemoveAll();
  1522. m_hSelectedItem = NULL;
  1523. InvalidateLayout();
  1524. m_bSortNeeded = true;
  1525. }
  1526. //-----------------------------------------------------------------------------
  1527. // Purpose: Changes the current list selection
  1528. //-----------------------------------------------------------------------------
  1529. void SectionedListPanel::SetSelectedItem(CItemButton *item)
  1530. {
  1531. if (m_hSelectedItem.Get() == item)
  1532. return;
  1533. // deselect the current item
  1534. if (m_hSelectedItem.Get())
  1535. {
  1536. m_hSelectedItem->SetSelected(false);
  1537. }
  1538. // set the new item
  1539. m_hSelectedItem = item;
  1540. if (m_hSelectedItem.Get())
  1541. {
  1542. m_hSelectedItem->SetSelected(true);
  1543. }
  1544. Repaint();
  1545. PostActionSignal(new KeyValues("ItemSelected", "itemID", m_hSelectedItem.Get() ? m_hSelectedItem->GetID() : -1));
  1546. }
  1547. //-----------------------------------------------------------------------------
  1548. // Purpose:
  1549. //-----------------------------------------------------------------------------
  1550. int SectionedListPanel::GetSelectedItem()
  1551. {
  1552. if (m_hSelectedItem.Get())
  1553. {
  1554. return m_hSelectedItem->GetID();
  1555. }
  1556. return -1;
  1557. }
  1558. //-----------------------------------------------------------------------------
  1559. // Purpose: sets which item is currently selected
  1560. //-----------------------------------------------------------------------------
  1561. void SectionedListPanel::SetSelectedItem(int itemID)
  1562. {
  1563. if ( m_Items.IsValidIndex(itemID) )
  1564. {
  1565. SetSelectedItem(m_Items[itemID]);
  1566. }
  1567. }
  1568. //-----------------------------------------------------------------------------
  1569. // Purpose: returns the data of a selected item
  1570. //-----------------------------------------------------------------------------
  1571. KeyValues *SectionedListPanel::GetItemData(int itemID)
  1572. {
  1573. Assert(m_Items.IsValidIndex(itemID));
  1574. if ( !m_Items.IsValidIndex(itemID) )
  1575. return NULL;
  1576. return m_Items[itemID]->GetData();
  1577. }
  1578. //-----------------------------------------------------------------------------
  1579. // Purpose: returns what section an item is in
  1580. //-----------------------------------------------------------------------------
  1581. int SectionedListPanel::GetItemSection(int itemID)
  1582. {
  1583. if ( !m_Items.IsValidIndex(itemID) )
  1584. return -1;
  1585. return m_Items[itemID]->GetSectionID();
  1586. }
  1587. //-----------------------------------------------------------------------------
  1588. // Purpose: returns true if the itemID is valid for use
  1589. //-----------------------------------------------------------------------------
  1590. bool SectionedListPanel::IsItemIDValid(int itemID)
  1591. {
  1592. return m_Items.IsValidIndex(itemID);
  1593. }
  1594. //-----------------------------------------------------------------------------
  1595. // Purpose: returns true if the itemID is valid for use
  1596. //-----------------------------------------------------------------------------
  1597. int SectionedListPanel::GetHighestItemID()
  1598. {
  1599. return m_Items.MaxElementIndex();
  1600. }
  1601. //-----------------------------------------------------------------------------
  1602. // Purpose: item iterators
  1603. //-----------------------------------------------------------------------------
  1604. int SectionedListPanel::GetItemCount()
  1605. {
  1606. return m_SortedItems.Count();
  1607. }
  1608. //-----------------------------------------------------------------------------
  1609. // Purpose: item iterators
  1610. //-----------------------------------------------------------------------------
  1611. int SectionedListPanel::GetItemIDFromRow(int row)
  1612. {
  1613. if ( !m_SortedItems.IsValidIndex(row) )
  1614. return -1;
  1615. return m_SortedItems[row]->GetID();
  1616. }
  1617. //-----------------------------------------------------------------------------
  1618. // Purpose: returns the row that this itemID occupies. -1 if the itemID is invalid
  1619. //-----------------------------------------------------------------------------
  1620. int SectionedListPanel::GetRowFromItemID(int itemID)
  1621. {
  1622. for (int i = 0; i < m_SortedItems.Count(); i++)
  1623. {
  1624. if ( m_SortedItems[i]->GetID() == itemID )
  1625. {
  1626. return i;
  1627. }
  1628. }
  1629. return -1;
  1630. }
  1631. //-----------------------------------------------------------------------------
  1632. // Purpose: gets the local coordinates of a cell
  1633. //-----------------------------------------------------------------------------
  1634. bool SectionedListPanel::GetCellBounds(int itemID, int column, int &x, int &y, int &wide, int &tall)
  1635. {
  1636. x = y = wide = tall = 0;
  1637. if ( !IsItemIDValid(itemID) )
  1638. return false;
  1639. // get the item
  1640. CItemButton *item = m_Items[itemID];
  1641. if ( !item->IsVisible() )
  1642. return false;
  1643. //!! ignores column for now
  1644. item->GetBounds(x, y, wide, tall);
  1645. item->GetCellBounds(column, x, wide);
  1646. return true;
  1647. }
  1648. //=============================================================================
  1649. // HPE_BEGIN:
  1650. // [menglish] Gets the local coordinates of a cell using the max width for every column
  1651. // Gets the local coordinates of a cell
  1652. //=============================================================================
  1653. bool SectionedListPanel::GetMaxCellBounds(int itemID, int column, int &x, int &y, int &wide, int &tall)
  1654. {
  1655. x = y = wide = tall = 0;
  1656. if ( !IsItemIDValid(itemID) )
  1657. return false;
  1658. // get the item
  1659. CItemButton *item = m_Items[itemID];
  1660. if ( !item->IsVisible() )
  1661. return false;
  1662. //!! ignores column for now
  1663. item->GetBounds(x, y, wide, tall);
  1664. item->GetMaxCellBounds(column, x, wide);
  1665. return true;
  1666. }
  1667. //-----------------------------------------------------------------------------
  1668. // Purpose: gets the local coordinates of a cell
  1669. //-----------------------------------------------------------------------------
  1670. bool SectionedListPanel::GetItemBounds(int itemID, int &x, int &y, int &wide, int &tall)
  1671. {
  1672. x = y = wide = tall = 0;
  1673. if ( !IsItemIDValid(itemID) )
  1674. return false;
  1675. // get the item
  1676. CItemButton *item = m_Items[itemID];
  1677. if ( !item->IsVisible() )
  1678. return false;
  1679. //!! ignores column for now
  1680. item->GetBounds(x, y, wide, tall);
  1681. return true;
  1682. }
  1683. //=============================================================================
  1684. // HPE_END
  1685. //=============================================================================
  1686. //-----------------------------------------------------------------------------
  1687. // Purpose: forces an item to redraw
  1688. //-----------------------------------------------------------------------------
  1689. void SectionedListPanel::InvalidateItem(int itemID)
  1690. {
  1691. if ( !IsItemIDValid(itemID) )
  1692. return;
  1693. m_Items[itemID]->InvalidateLayout();
  1694. m_Items[itemID]->Repaint();
  1695. }
  1696. //-----------------------------------------------------------------------------
  1697. // Purpose: set up a field for editing
  1698. //-----------------------------------------------------------------------------
  1699. void SectionedListPanel::EnterEditMode(int itemID, int column, vgui::Panel *editPanel)
  1700. {
  1701. m_hEditModePanel = editPanel;
  1702. m_iEditModeItemID = itemID;
  1703. m_iEditModeColumn = column;
  1704. editPanel->SetParent(this);
  1705. editPanel->SetVisible(true);
  1706. editPanel->RequestFocus();
  1707. editPanel->MoveToFront();
  1708. InvalidateLayout();
  1709. }
  1710. //-----------------------------------------------------------------------------
  1711. // Purpose: leaves editing mode
  1712. //-----------------------------------------------------------------------------
  1713. void SectionedListPanel::LeaveEditMode()
  1714. {
  1715. if (m_hEditModePanel.Get())
  1716. {
  1717. InvalidateItem(m_iEditModeItemID);
  1718. m_hEditModePanel->SetVisible(false);
  1719. m_hEditModePanel->SetParent((Panel *)NULL);
  1720. m_hEditModePanel = NULL;
  1721. }
  1722. }
  1723. //-----------------------------------------------------------------------------
  1724. // Purpose: returns true if we are currently in inline editing mode
  1725. //-----------------------------------------------------------------------------
  1726. bool SectionedListPanel::IsInEditMode()
  1727. {
  1728. return (m_hEditModePanel.Get() != NULL);
  1729. }
  1730. //-----------------------------------------------------------------------------
  1731. // Purpose: list used to match indexes in image columns to image pointers
  1732. //-----------------------------------------------------------------------------
  1733. void SectionedListPanel::SetImageList(ImageList *imageList, bool deleteImageListWhenDone)
  1734. {
  1735. m_bDeleteImageListWhenDone = deleteImageListWhenDone;
  1736. m_pImageList = imageList;
  1737. }
  1738. //-----------------------------------------------------------------------------
  1739. // Purpose:
  1740. //-----------------------------------------------------------------------------
  1741. void SectionedListPanel::OnSetFocus()
  1742. {
  1743. if (m_hSelectedItem.Get())
  1744. {
  1745. m_hSelectedItem->RequestFocus();
  1746. }
  1747. else
  1748. {
  1749. BaseClass::OnSetFocus();
  1750. }
  1751. }
  1752. //-----------------------------------------------------------------------------
  1753. // Purpose:
  1754. //-----------------------------------------------------------------------------
  1755. int SectionedListPanel::GetSectionTall()
  1756. {
  1757. if (m_Sections.Count())
  1758. {
  1759. HFont font = m_Sections[0].m_pHeader->GetFont();
  1760. if (font != INVALID_FONT)
  1761. {
  1762. return surface()->GetFontTall(font) + BUTTON_HEIGHT_SPACER;
  1763. }
  1764. }
  1765. return BUTTON_HEIGHT_DEFAULT;
  1766. }
  1767. //-----------------------------------------------------------------------------
  1768. // Purpose: returns the size required to fully draw the contents of the panel
  1769. //-----------------------------------------------------------------------------
  1770. void SectionedListPanel::GetContentSize(int &wide, int &tall)
  1771. {
  1772. // make sure our layout is done
  1773. if (IsLayoutInvalid())
  1774. {
  1775. if (m_bSortNeeded)
  1776. {
  1777. ReSortList();
  1778. m_bSortNeeded = false;
  1779. }
  1780. LayoutPanels(m_iContentHeight);
  1781. }
  1782. wide = GetWide();
  1783. tall = m_iContentHeight;
  1784. }
  1785. //-----------------------------------------------------------------------------
  1786. // Purpose: Returns the index of a new item button
  1787. //-----------------------------------------------------------------------------
  1788. int SectionedListPanel::GetNewItemButton()
  1789. {
  1790. int itemID = m_Items.AddToTail();
  1791. if (m_FreeItems.Count())
  1792. {
  1793. // reusing an existing CItemButton
  1794. m_Items[itemID] = m_FreeItems[m_FreeItems.Head()];
  1795. m_Items[itemID]->SetID(itemID);
  1796. m_Items[itemID]->SetVisible(true);
  1797. m_FreeItems.Remove(m_FreeItems.Head());
  1798. }
  1799. else
  1800. {
  1801. // create a new CItemButton
  1802. m_Items[itemID] = SETUP_PANEL(new CItemButton(this, itemID));
  1803. m_Items[itemID]->SetShowColumns( m_bShowColumns );
  1804. }
  1805. return itemID;
  1806. }
  1807. //-----------------------------------------------------------------------------
  1808. // Purpose: Returns fallback font to use for text image for this column
  1809. // Input : sectionID -
  1810. // columnIndex -
  1811. // Output : virtual HFont
  1812. //-----------------------------------------------------------------------------
  1813. HFont SectionedListPanel::GetColumnFallbackFontBySection( int sectionID, int columnIndex )
  1814. {
  1815. int index = FindSectionIndexByID(sectionID);
  1816. if (index < 0)
  1817. return INVALID_FONT;
  1818. if (columnIndex >= m_Sections[index].m_Columns.Count())
  1819. return INVALID_FONT;
  1820. return m_Sections[index].m_Columns[columnIndex].m_hFallbackFont;
  1821. }