Audio plugin host https://kx.studio/carla
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.

324 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #ifndef JUCE_SELECTEDITEMSET_H_INCLUDED
  18. #define JUCE_SELECTEDITEMSET_H_INCLUDED
  19. //==============================================================================
  20. /** Manages a list of selectable items.
  21. Use one of these to keep a track of things that the user has highlighted, like
  22. icons or things in a list.
  23. The class is templated so that you can use it to hold either a set of pointers
  24. to objects, or a set of ID numbers or handles, for cases where each item may
  25. not always have a corresponding object.
  26. To be informed when items are selected/deselected, register a ChangeListener with
  27. this object.
  28. */
  29. template <class SelectableItemType>
  30. class SelectedItemSet : public ChangeBroadcaster
  31. {
  32. public:
  33. //==============================================================================
  34. typedef SelectableItemType ItemType;
  35. typedef Array<SelectableItemType> ItemArray;
  36. typedef PARAMETER_TYPE (SelectableItemType) ParameterType;
  37. //==============================================================================
  38. /** Creates an empty set. */
  39. SelectedItemSet()
  40. {
  41. }
  42. /** Creates a set based on an array of items. */
  43. explicit SelectedItemSet (const ItemArray& items)
  44. : selectedItems (items)
  45. {
  46. }
  47. /** Creates a copy of another set. */
  48. SelectedItemSet (const SelectedItemSet& other)
  49. : selectedItems (other.selectedItems)
  50. {
  51. }
  52. /** Creates a copy of another set. */
  53. SelectedItemSet& operator= (const SelectedItemSet& other)
  54. {
  55. if (selectedItems != other.selectedItems)
  56. {
  57. changed();
  58. for (int i = selectedItems.size(); --i >= 0;)
  59. if (! other.isSelected (selectedItems.getReference (i)))
  60. itemDeselected (selectedItems.remove (i));
  61. for (SelectableItemType* i = other.selectedItems.begin(), *e = other.selectedItems.end(); i != e; ++i)
  62. {
  63. if (! isSelected (*i))
  64. {
  65. selectedItems.add (*i);
  66. itemSelected (*i);
  67. }
  68. }
  69. }
  70. return *this;
  71. }
  72. //==============================================================================
  73. /** Clears any other currently selected items, and selects this item.
  74. If this item is already the only thing selected, no change notification
  75. will be sent out.
  76. @see addToSelection, addToSelectionBasedOnModifiers
  77. */
  78. void selectOnly (ParameterType item)
  79. {
  80. if (isSelected (item))
  81. {
  82. for (int i = selectedItems.size(); --i >= 0;)
  83. {
  84. if (selectedItems.getUnchecked(i) != item)
  85. {
  86. deselect (selectedItems.getUnchecked(i));
  87. i = jmin (i, selectedItems.size());
  88. }
  89. }
  90. }
  91. else
  92. {
  93. changed();
  94. deselectAll();
  95. selectedItems.add (item);
  96. itemSelected (item);
  97. }
  98. }
  99. /** Selects an item.
  100. If the item is already selected, no change notification will be sent out.
  101. @see selectOnly, addToSelectionBasedOnModifiers
  102. */
  103. void addToSelection (ParameterType item)
  104. {
  105. if (! isSelected (item))
  106. {
  107. changed();
  108. selectedItems.add (item);
  109. itemSelected (item);
  110. }
  111. }
  112. /** Selects or deselects an item.
  113. This will use the modifier keys to decide whether to deselect other items
  114. first.
  115. So if the shift key is held down, the item will be added without deselecting
  116. anything (same as calling addToSelection() )
  117. If no modifiers are down, the current selection will be cleared first (same
  118. as calling selectOnly() )
  119. If the ctrl (or command on the Mac) key is held down, the item will be toggled -
  120. so it'll be added to the set unless it's already there, in which case it'll be
  121. deselected.
  122. If the items that you're selecting can also be dragged, you may need to use the
  123. addToSelectionOnMouseDown() and addToSelectionOnMouseUp() calls to handle the
  124. subtleties of this kind of usage.
  125. @see selectOnly, addToSelection, addToSelectionOnMouseDown, addToSelectionOnMouseUp
  126. */
  127. void addToSelectionBasedOnModifiers (ParameterType item,
  128. ModifierKeys modifiers)
  129. {
  130. if (modifiers.isShiftDown())
  131. {
  132. addToSelection (item);
  133. }
  134. else if (modifiers.isCommandDown())
  135. {
  136. if (isSelected (item))
  137. deselect (item);
  138. else
  139. addToSelection (item);
  140. }
  141. else
  142. {
  143. selectOnly (item);
  144. }
  145. }
  146. /** Selects or deselects items that can also be dragged, based on a mouse-down event.
  147. If you call addToSelectionOnMouseDown() at the start of your mouseDown event,
  148. and then call addToSelectionOnMouseUp() at the end of your mouseUp event, this
  149. makes it easy to handle multiple-selection of sets of objects that can also
  150. be dragged.
  151. For example, if you have several items already selected, and you click on
  152. one of them (without dragging), then you'd expect this to deselect the other, and
  153. just select the item you clicked on. But if you had clicked on this item and
  154. dragged it, you'd have expected them all to stay selected.
  155. When you call this method, you'll need to store the boolean result, because the
  156. addToSelectionOnMouseUp() method will need to be know this value.
  157. @see addToSelectionOnMouseUp, addToSelectionBasedOnModifiers
  158. */
  159. bool addToSelectionOnMouseDown (ParameterType item,
  160. ModifierKeys modifiers)
  161. {
  162. if (isSelected (item))
  163. return ! modifiers.isPopupMenu();
  164. addToSelectionBasedOnModifiers (item, modifiers);
  165. return false;
  166. }
  167. /** Selects or deselects items that can also be dragged, based on a mouse-up event.
  168. Call this during a mouseUp callback, when you have previously called the
  169. addToSelectionOnMouseDown() method during your mouseDown event.
  170. See addToSelectionOnMouseDown() for more info
  171. @param item the item to select (or deselect)
  172. @param modifiers the modifiers from the mouse-up event
  173. @param wasItemDragged true if your item was dragged during the mouse click
  174. @param resultOfMouseDownSelectMethod this is the boolean return value that came
  175. back from the addToSelectionOnMouseDown() call that you
  176. should have made during the matching mouseDown event
  177. */
  178. void addToSelectionOnMouseUp (ParameterType item,
  179. ModifierKeys modifiers,
  180. const bool wasItemDragged,
  181. const bool resultOfMouseDownSelectMethod)
  182. {
  183. if (resultOfMouseDownSelectMethod && ! wasItemDragged)
  184. addToSelectionBasedOnModifiers (item, modifiers);
  185. }
  186. /** Deselects an item. */
  187. void deselect (ParameterType item)
  188. {
  189. const int i = selectedItems.indexOf (item);
  190. if (i >= 0)
  191. {
  192. changed();
  193. itemDeselected (selectedItems.remove (i));
  194. }
  195. }
  196. /** Deselects all items. */
  197. void deselectAll()
  198. {
  199. if (selectedItems.size() > 0)
  200. {
  201. changed();
  202. for (int i = selectedItems.size(); --i >= 0;)
  203. {
  204. itemDeselected (selectedItems.remove (i));
  205. i = jmin (i, selectedItems.size());
  206. }
  207. }
  208. }
  209. //==============================================================================
  210. /** Returns the number of currently selected items.
  211. @see getSelectedItem
  212. */
  213. int getNumSelected() const noexcept { return selectedItems.size(); }
  214. /** Returns one of the currently selected items.
  215. If the index is out-of-range, this returns a default-constructed SelectableItemType.
  216. @see getNumSelected
  217. */
  218. SelectableItemType getSelectedItem (const int index) const { return selectedItems [index]; }
  219. /** True if this item is currently selected. */
  220. bool isSelected (ParameterType item) const noexcept { return selectedItems.contains (item); }
  221. /** Provides access to the array of items. */
  222. const ItemArray& getItemArray() const noexcept { return selectedItems; }
  223. /** Provides iterator access to the array of items. */
  224. SelectableItemType* begin() const noexcept { return selectedItems.begin(); }
  225. /** Provides iterator access to the array of items. */
  226. SelectableItemType* end() const noexcept { return selectedItems.end(); }
  227. //==============================================================================
  228. /** Can be overridden to do special handling when an item is selected.
  229. For example, if the item is an object, you might want to call it and tell
  230. it that it's being selected.
  231. */
  232. virtual void itemSelected (SelectableItemType) {}
  233. /** Can be overridden to do special handling when an item is deselected.
  234. For example, if the item is an object, you might want to call it and tell
  235. it that it's being deselected.
  236. */
  237. virtual void itemDeselected (SelectableItemType) {}
  238. /** Used internally, but can be called to force a change message to be sent
  239. to the ChangeListeners.
  240. */
  241. void changed()
  242. {
  243. sendChangeMessage();
  244. }
  245. /** Used internally, but can be called to force a change message to be sent
  246. to the ChangeListeners.
  247. */
  248. void changed (const bool synchronous)
  249. {
  250. if (synchronous)
  251. sendSynchronousChangeMessage();
  252. else
  253. sendChangeMessage();
  254. }
  255. private:
  256. //==============================================================================
  257. ItemArray selectedItems;
  258. JUCE_LEAK_DETECTOR (SelectedItemSet<SelectableItemType>)
  259. };
  260. #endif // JUCE_SELECTEDITEMSET_H_INCLUDED