The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

322 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #include "../jucedemo_headers.h"
  19. //==============================================================================
  20. /**
  21. This class shows how to implement a TableListBoxModel to show in a TableListBox.
  22. */
  23. class TableDemoComponent : public Component,
  24. public TableListBoxModel
  25. {
  26. public:
  27. //==============================================================================
  28. TableDemoComponent()
  29. : font (14.0f)
  30. {
  31. // Load some data from an embedded XML file..
  32. loadData();
  33. // Create our table component and add it to this component..
  34. addAndMakeVisible (&table);
  35. table.setModel (this);
  36. // give it a border
  37. table.setColour (ListBox::outlineColourId, Colours::grey);
  38. table.setOutlineThickness (1);
  39. // Add some columns to the table header, based on the column list in our database..
  40. forEachXmlChildElement (*columnList, columnXml)
  41. {
  42. table.getHeader().addColumn (columnXml->getStringAttribute ("name"),
  43. columnXml->getIntAttribute ("columnId"),
  44. columnXml->getIntAttribute ("width"),
  45. 50, 400,
  46. TableHeaderComponent::defaultFlags);
  47. }
  48. // we could now change some initial settings..
  49. table.getHeader().setSortColumnId (1, true); // sort forwards by the ID column
  50. table.getHeader().setColumnVisible (7, false); // hide the "length" column until the user shows it
  51. // un-comment this line to have a go of stretch-to-fit mode
  52. // table.getHeader().setStretchToFitActive (true);
  53. table.setMultipleSelectionEnabled (true);
  54. }
  55. ~TableDemoComponent()
  56. {
  57. }
  58. //==============================================================================
  59. // This is overloaded from TableListBoxModel, and must return the total number of rows in our table
  60. int getNumRows()
  61. {
  62. return numRows;
  63. }
  64. // This is overloaded from TableListBoxModel, and should fill in the background of the whole row
  65. void paintRowBackground (Graphics& g, int /*rowNumber*/, int /*width*/, int /*height*/, bool rowIsSelected)
  66. {
  67. if (rowIsSelected)
  68. g.fillAll (Colours::lightblue);
  69. }
  70. // This is overloaded from TableListBoxModel, and must paint any cells that aren't using custom
  71. // components.
  72. void paintCell (Graphics& g,
  73. int rowNumber,
  74. int columnId,
  75. int width, int height,
  76. bool /*rowIsSelected*/)
  77. {
  78. g.setColour (Colours::black);
  79. g.setFont (font);
  80. const XmlElement* rowElement = dataList->getChildElement (rowNumber);
  81. if (rowElement != 0)
  82. {
  83. const String text (rowElement->getStringAttribute (getAttributeNameForColumnId (columnId)));
  84. g.drawText (text, 2, 0, width - 4, height, Justification::centredLeft, true);
  85. }
  86. g.setColour (Colours::black.withAlpha (0.2f));
  87. g.fillRect (width - 1, 0, 1, height);
  88. }
  89. // This is overloaded from TableListBoxModel, and tells us that the user has clicked a table header
  90. // to change the sort order.
  91. void sortOrderChanged (int newSortColumnId, bool isForwards)
  92. {
  93. if (newSortColumnId != 0)
  94. {
  95. DemoDataSorter sorter (getAttributeNameForColumnId (newSortColumnId), isForwards);
  96. dataList->sortChildElements (sorter);
  97. table.updateContent();
  98. }
  99. }
  100. // This is overloaded from TableListBoxModel, and must update any custom components that we're using
  101. Component* refreshComponentForCell (int rowNumber, int columnId, bool /*isRowSelected*/,
  102. Component* existingComponentToUpdate)
  103. {
  104. if (columnId == 5) // If it's the ratings column, we'll return our custom component..
  105. {
  106. RatingColumnCustomComponent* ratingsBox = (RatingColumnCustomComponent*) existingComponentToUpdate;
  107. // If an existing component is being passed-in for updating, we'll re-use it, but
  108. // if not, we'll have to create one.
  109. if (ratingsBox == 0)
  110. ratingsBox = new RatingColumnCustomComponent (*this);
  111. ratingsBox->setRowAndColumn (rowNumber, columnId);
  112. return ratingsBox;
  113. }
  114. else
  115. {
  116. // for any other column, just return 0, as we'll be painting these columns directly.
  117. jassert (existingComponentToUpdate == 0);
  118. return 0;
  119. }
  120. }
  121. // This is overloaded from TableListBoxModel, and should choose the best width for the specified
  122. // column.
  123. int getColumnAutoSizeWidth (int columnId)
  124. {
  125. if (columnId == 5)
  126. return 100; // (this is the ratings column, containing a custom component)
  127. int widest = 32;
  128. // find the widest bit of text in this column..
  129. for (int i = getNumRows(); --i >= 0;)
  130. {
  131. const XmlElement* rowElement = dataList->getChildElement (i);
  132. if (rowElement != 0)
  133. {
  134. const String text (rowElement->getStringAttribute (getAttributeNameForColumnId (columnId)));
  135. widest = jmax (widest, font.getStringWidth (text));
  136. }
  137. }
  138. return widest + 8;
  139. }
  140. // A couple of quick methods to set and get the "rating" value when the user
  141. // changes the combo box
  142. int getRating (const int rowNumber) const
  143. {
  144. return dataList->getChildElement (rowNumber)->getIntAttribute ("Rating");
  145. }
  146. void setRating (const int rowNumber, const int newRating)
  147. {
  148. dataList->getChildElement (rowNumber)->setAttribute ("Rating", newRating);
  149. }
  150. //==============================================================================
  151. void resized()
  152. {
  153. // position our table with a gap around its edge
  154. table.setBoundsInset (BorderSize<int> (8));
  155. }
  156. private:
  157. TableListBox table; // the table component itself
  158. Font font;
  159. ScopedPointer<XmlElement> demoData; // This is the XML document loaded from the embedded file "demo table data.xml"
  160. XmlElement* columnList; // A pointer to the sub-node of demoData that contains the list of columns
  161. XmlElement* dataList; // A pointer to the sub-node of demoData that contains the list of data rows
  162. int numRows; // The number of rows of data we've got
  163. //==============================================================================
  164. // This is a custom component containing a combo box, which we're going to put inside
  165. // our table's "rating" column.
  166. class RatingColumnCustomComponent : public Component,
  167. public ComboBoxListener
  168. {
  169. public:
  170. RatingColumnCustomComponent (TableDemoComponent& owner_)
  171. : owner (owner_)
  172. {
  173. // just put a combo box inside this component
  174. addAndMakeVisible (&comboBox);
  175. comboBox.addItem ("fab", 1);
  176. comboBox.addItem ("groovy", 2);
  177. comboBox.addItem ("hep", 3);
  178. comboBox.addItem ("neat", 4);
  179. comboBox.addItem ("wild", 5);
  180. comboBox.addItem ("swingin", 6);
  181. comboBox.addItem ("mad for it", 7);
  182. // when the combo is changed, we'll get a callback.
  183. comboBox.addListener (this);
  184. comboBox.setWantsKeyboardFocus (false);
  185. }
  186. ~RatingColumnCustomComponent()
  187. {
  188. }
  189. void resized()
  190. {
  191. comboBox.setBoundsInset (BorderSize<int> (2));
  192. }
  193. // Our demo code will call this when we may need to update our contents
  194. void setRowAndColumn (const int newRow, const int newColumn)
  195. {
  196. row = newRow;
  197. columnId = newColumn;
  198. comboBox.setSelectedId (owner.getRating (row), true);
  199. }
  200. void comboBoxChanged (ComboBox* /*comboBoxThatHasChanged*/)
  201. {
  202. owner.setRating (row, comboBox.getSelectedId());
  203. }
  204. private:
  205. TableDemoComponent& owner;
  206. ComboBox comboBox;
  207. int row, columnId;
  208. };
  209. //==============================================================================
  210. // A comparator used to sort our data when the user clicks a column header
  211. class DemoDataSorter
  212. {
  213. public:
  214. DemoDataSorter (const String attributeToSort_, bool forwards)
  215. : attributeToSort (attributeToSort_),
  216. direction (forwards ? 1 : -1)
  217. {
  218. }
  219. int compareElements (XmlElement* first, XmlElement* second) const
  220. {
  221. int result = first->getStringAttribute (attributeToSort)
  222. .compareLexicographically (second->getStringAttribute (attributeToSort));
  223. if (result == 0)
  224. result = first->getStringAttribute ("ID")
  225. .compareLexicographically (second->getStringAttribute ("ID"));
  226. return direction * result;
  227. }
  228. private:
  229. String attributeToSort;
  230. int direction;
  231. };
  232. //==============================================================================
  233. // this loads the embedded database XML file into memory
  234. void loadData()
  235. {
  236. XmlDocument dataDoc (String ((const char*) BinaryData::demo_table_data_xml));
  237. demoData = dataDoc.getDocumentElement();
  238. dataList = demoData->getChildByName ("DATA");
  239. columnList = demoData->getChildByName ("COLUMNS");
  240. numRows = dataList->getNumChildElements();
  241. }
  242. // (a utility method to search our XML for the attribute that matches a column ID)
  243. const String getAttributeNameForColumnId (const int columnId) const
  244. {
  245. forEachXmlChildElement (*columnList, columnXml)
  246. {
  247. if (columnXml->getIntAttribute ("columnId") == columnId)
  248. return columnXml->getStringAttribute ("name");
  249. }
  250. return String::empty;
  251. }
  252. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TableDemoComponent);
  253. };
  254. //==============================================================================
  255. Component* createTableDemo()
  256. {
  257. return new TableDemoComponent();
  258. }