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.

326 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. demoData (0)
  31. {
  32. // Load some data from an embedded XML file..
  33. loadData();
  34. // Create our table component and add it to this component..
  35. addAndMakeVisible (table = new TableListBox ("demo table", 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. deleteAllChildren();
  58. delete demoData;
  59. }
  60. //==============================================================================
  61. // This is overloaded from TableListBoxModel, and must return the total number of rows in our table
  62. int getNumRows()
  63. {
  64. return numRows;
  65. }
  66. // This is overloaded from TableListBoxModel, and should fill in the background of the whole row
  67. void paintRowBackground (Graphics& g, int rowNumber, int width, int height, bool rowIsSelected)
  68. {
  69. if (rowIsSelected)
  70. g.fillAll (Colours::lightblue);
  71. }
  72. // This is overloaded from TableListBoxModel, and must paint any cells that aren't using custom
  73. // components.
  74. void paintCell (Graphics& g,
  75. int rowNumber,
  76. int columnId,
  77. int width, int height,
  78. bool rowIsSelected)
  79. {
  80. g.setColour (Colours::black);
  81. g.setFont (font);
  82. const XmlElement* rowElement = dataList->getChildElement (rowNumber);
  83. if (rowElement != 0)
  84. {
  85. const String text (rowElement->getStringAttribute (getAttributeNameForColumnId (columnId)));
  86. g.drawText (text, 2, 0, width - 4, height, Justification::centredLeft, true);
  87. }
  88. g.setColour (Colours::black.withAlpha (0.2f));
  89. g.fillRect (width - 1, 0, 1, height);
  90. }
  91. // This is overloaded from TableListBoxModel, and tells us that the user has clicked a table header
  92. // to change the sort order.
  93. void sortOrderChanged (int newSortColumnId, bool isForwards)
  94. {
  95. if (newSortColumnId != 0)
  96. {
  97. DemoDataSorter sorter (getAttributeNameForColumnId (newSortColumnId), isForwards);
  98. dataList->sortChildElements (sorter);
  99. table->updateContent();
  100. }
  101. }
  102. // This is overloaded from TableListBoxModel, and must update any custom components that we're using
  103. Component* refreshComponentForCell (int rowNumber, int columnId, bool isRowSelected,
  104. Component* existingComponentToUpdate)
  105. {
  106. if (columnId == 5) // If it's the ratings column, we'll return our custom component..
  107. {
  108. RatingColumnCustomComponent* ratingsBox = (RatingColumnCustomComponent*) existingComponentToUpdate;
  109. // If an existing component is being passed-in for updating, we'll re-use it, but
  110. // if not, we'll have to create one.
  111. if (ratingsBox == 0)
  112. ratingsBox = new RatingColumnCustomComponent (*this);
  113. ratingsBox->setRowAndColumn (rowNumber, columnId);
  114. return ratingsBox;
  115. }
  116. else
  117. {
  118. // for any other column, just return 0, as we'll be painting these columns directly.
  119. jassert (existingComponentToUpdate == 0);
  120. return 0;
  121. }
  122. }
  123. // This is overloaded from TableListBoxModel, and should choose the best width for the specified
  124. // column.
  125. int getColumnAutoSizeWidth (int columnId)
  126. {
  127. if (columnId == 5)
  128. return 100; // (this is the ratings column, containing a custom component)
  129. int widest = 32;
  130. // find the widest bit of text in this column..
  131. for (int i = getNumRows(); --i >= 0;)
  132. {
  133. const XmlElement* rowElement = dataList->getChildElement (i);
  134. if (rowElement != 0)
  135. {
  136. const String text (rowElement->getStringAttribute (getAttributeNameForColumnId (columnId)));
  137. widest = jmax (widest, font.getStringWidth (text));
  138. }
  139. }
  140. return widest + 8;
  141. }
  142. // A couple of quick methods to set and get the "rating" value when the user
  143. // changes the combo box
  144. int getRating (const int rowNumber, const int columnId) const
  145. {
  146. return dataList->getChildElement (rowNumber)->getIntAttribute ("Rating");
  147. }
  148. void setRating (const int rowNumber, const int columnId, const int newRating)
  149. {
  150. dataList->getChildElement (rowNumber)->setAttribute ("Rating", newRating);
  151. }
  152. //==============================================================================
  153. void resized()
  154. {
  155. // position our table with a gap around its edge
  156. table->setBoundsInset (BorderSize (8));
  157. }
  158. //==============================================================================
  159. juce_UseDebuggingNewOperator
  160. private:
  161. TableListBox* table; // the table component itself
  162. Font font;
  163. XmlElement* demoData; // This is the XML document loaded from the embedded file "demo table data.xml"
  164. XmlElement* columnList; // A pointer to the sub-node of demoData that contains the list of columns
  165. XmlElement* dataList; // A pointer to the sub-node of demoData that contains the list of data rows
  166. int numRows; // The number of rows of data we've got
  167. //==============================================================================
  168. // This is a custom component containing a combo box, which we're going to put inside
  169. // our table's "rating" column.
  170. class RatingColumnCustomComponent : public Component,
  171. public ComboBoxListener
  172. {
  173. public:
  174. RatingColumnCustomComponent (TableDemoComponent& owner_)
  175. : owner (owner_)
  176. {
  177. // just put a combo box inside this component
  178. addAndMakeVisible (comboBox = new ComboBox (String::empty));
  179. comboBox->addItem ("fab", 1);
  180. comboBox->addItem ("groovy", 2);
  181. comboBox->addItem ("hep", 3);
  182. comboBox->addItem ("neat", 4);
  183. comboBox->addItem ("wild", 5);
  184. comboBox->addItem ("swingin", 6);
  185. comboBox->addItem ("mad for it", 7);
  186. // when the combo is changed, we'll get a callback.
  187. comboBox->addListener (this);
  188. comboBox->setWantsKeyboardFocus (false);
  189. }
  190. ~RatingColumnCustomComponent()
  191. {
  192. deleteAllChildren();
  193. }
  194. void resized()
  195. {
  196. comboBox->setBoundsInset (BorderSize (2));
  197. }
  198. // Our demo code will call this when we may need to update our contents
  199. void setRowAndColumn (const int newRow, const int newColumn)
  200. {
  201. row = newRow;
  202. columnId = newColumn;
  203. comboBox->setSelectedId (owner.getRating (row, columnId), true);
  204. }
  205. void comboBoxChanged (ComboBox* comboBoxThatHasChanged)
  206. {
  207. owner.setRating (row, columnId, comboBox->getSelectedId());
  208. }
  209. private:
  210. TableDemoComponent& owner;
  211. ComboBox* comboBox;
  212. int row, columnId;
  213. };
  214. //==============================================================================
  215. // A comparator used to sort our data when the user clicks a column header
  216. class DemoDataSorter
  217. {
  218. public:
  219. DemoDataSorter (const String attributeToSort_, bool forwards)
  220. : attributeToSort (attributeToSort_),
  221. direction (forwards ? 1 : -1)
  222. {
  223. }
  224. int compareElements (XmlElement* first, XmlElement* second) const
  225. {
  226. int result = first->getStringAttribute (attributeToSort)
  227. .compareLexicographically (second->getStringAttribute (attributeToSort));
  228. if (result == 0)
  229. result = first->getStringAttribute ("ID")
  230. .compareLexicographically (second->getStringAttribute ("ID"));
  231. return direction * result;
  232. }
  233. private:
  234. const String attributeToSort;
  235. const int direction;
  236. };
  237. //==============================================================================
  238. // this loads the embedded database XML file into memory
  239. void loadData()
  240. {
  241. XmlDocument dataDoc (String ((const char*) BinaryData::demo_table_data_xml));
  242. demoData = dataDoc.getDocumentElement();
  243. dataList = demoData->getChildByName ("DATA");
  244. columnList = demoData->getChildByName ("COLUMNS");
  245. numRows = dataList->getNumChildElements();
  246. }
  247. // (a utility method to search our XML for the attribute that matches a column ID)
  248. const String getAttributeNameForColumnId (const int columnId) const
  249. {
  250. forEachXmlChildElement (*columnList, columnXml)
  251. {
  252. if (columnXml->getIntAttribute ("columnId") == columnId)
  253. return columnXml->getStringAttribute ("name");
  254. }
  255. return String::empty;
  256. }
  257. };
  258. //==============================================================================
  259. Component* createTableDemo()
  260. {
  261. return new TableDemoComponent();
  262. }