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.

juce_TreeView.h 41KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939
  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. class TreeView;
  22. //==============================================================================
  23. /**
  24. An item in a treeview.
  25. A TreeViewItem can either be a leaf-node in the tree, or it can contain its
  26. own sub-items.
  27. To implement an item that contains sub-items, override the itemOpennessChanged()
  28. method so that when it is opened, it adds the new sub-items to itself using the
  29. addSubItem method. Depending on the nature of the item it might choose to only
  30. do this the first time it's opened, or it might want to refresh itself each time.
  31. It also has the option of deleting its sub-items when it is closed, or leaving them
  32. in place.
  33. */
  34. class JUCE_API TreeViewItem
  35. {
  36. public:
  37. //==============================================================================
  38. /** Constructor. */
  39. TreeViewItem();
  40. /** Destructor. */
  41. virtual ~TreeViewItem();
  42. //==============================================================================
  43. /** Returns the number of sub-items that have been added to this item.
  44. Note that this doesn't mean much if the node isn't open.
  45. @see getSubItem, mightContainSubItems, addSubItem
  46. */
  47. int getNumSubItems() const noexcept;
  48. /** Returns one of the item's sub-items.
  49. Remember that the object returned might get deleted at any time when its parent
  50. item is closed or refreshed, depending on the nature of the items you're using.
  51. @see getNumSubItems
  52. */
  53. TreeViewItem* getSubItem (int index) const noexcept;
  54. /** Removes any sub-items. */
  55. void clearSubItems();
  56. /** Adds a sub-item.
  57. @param newItem the object to add to the item's sub-item list. Once added, these can be
  58. found using getSubItem(). When the items are later removed with
  59. removeSubItem() (or when this item is deleted), they will be deleted.
  60. @param insertPosition the index which the new item should have when it's added. If this
  61. value is less than 0, the item will be added to the end of the list.
  62. */
  63. void addSubItem (TreeViewItem* newItem, int insertPosition = -1);
  64. /** Adds a sub-item with a sort-comparator, assuming that the existing items are already sorted.
  65. @param comparator the comparator object for sorting - see sortSubItems() for details about
  66. the methods this class must provide.
  67. @param newItem the object to add to the item's sub-item list. Once added, these can be
  68. found using getSubItem(). When the items are later removed with
  69. removeSubItem() (or when this item is deleted), they will be deleted.
  70. */
  71. template <class ElementComparator>
  72. void addSubItemSorted (ElementComparator& comparator, TreeViewItem* newItem)
  73. {
  74. addSubItem (newItem, findInsertIndexInSortedArray (comparator, subItems.begin(), newItem, 0, subItems.size()));
  75. }
  76. /** Removes one of the sub-items.
  77. @param index the item to remove
  78. @param deleteItem if true, the item that is removed will also be deleted.
  79. */
  80. void removeSubItem (int index, bool deleteItem = true);
  81. /** Sorts the list of sub-items using a standard array comparator.
  82. This will use a comparator object to sort the elements into order. The comparator
  83. object must have a method of the form:
  84. @code
  85. int compareElements (TreeViewItem* first, TreeViewItem* second);
  86. @endcode
  87. ..and this method must return:
  88. - a value of < 0 if the first comes before the second
  89. - a value of 0 if the two objects are equivalent
  90. - a value of > 0 if the second comes before the first
  91. To improve performance, the compareElements() method can be declared as static or const.
  92. */
  93. template <class ElementComparator>
  94. void sortSubItems (ElementComparator& comparator)
  95. {
  96. subItems.sort (comparator);
  97. }
  98. //==============================================================================
  99. /** Returns the TreeView to which this item belongs. */
  100. TreeView* getOwnerView() const noexcept { return ownerView; }
  101. /** Returns the item within which this item is contained. */
  102. TreeViewItem* getParentItem() const noexcept { return parentItem; }
  103. //==============================================================================
  104. /** True if this item is currently open in the treeview.
  105. @see getOpenness
  106. */
  107. bool isOpen() const noexcept;
  108. /** Opens or closes the item.
  109. When opened or closed, the item's itemOpennessChanged() method will be called,
  110. and a subclass should use this callback to create and add any sub-items that
  111. it needs to.
  112. Note that if this is called when the item is in its default openness state, and
  113. this call would not change whether it's open or closed, then no change will be
  114. stored. If you want to explicitly set the openness state to be non-default then
  115. you should use setOpenness instead.
  116. @see setOpenness, itemOpennessChanged, mightContainSubItems
  117. */
  118. void setOpen (bool shouldBeOpen);
  119. /** An enum of states to describe the explicit or implicit openness of an item. */
  120. enum Openness
  121. {
  122. opennessDefault = 0,
  123. opennessClosed = 1,
  124. opennessOpen = 2
  125. };
  126. /** Returns the openness state of this item.
  127. @see isOpen
  128. */
  129. Openness getOpenness() const noexcept;
  130. /** Opens or closes the item.
  131. If this causes the value of isOpen() to change, then the item's itemOpennessChanged()
  132. method will be called, and a subclass should use this callback to create and add any
  133. sub-items that it needs to.
  134. @see setOpen
  135. */
  136. void setOpenness (Openness newOpenness);
  137. /** True if this item is currently selected.
  138. Use this when painting the node, to decide whether to draw it as selected or not.
  139. */
  140. bool isSelected() const noexcept;
  141. /** Selects or deselects the item.
  142. If shouldNotify == sendNotification, then a callback will be made
  143. to itemSelectionChanged()
  144. */
  145. void setSelected (bool shouldBeSelected,
  146. bool deselectOtherItemsFirst,
  147. NotificationType shouldNotify = sendNotification);
  148. /** Returns the rectangle that this item occupies.
  149. If relativeToTreeViewTopLeft is true, the coordinates are relative to the
  150. top-left of the TreeView comp, so this will depend on the scroll-position of
  151. the tree. If false, it is relative to the top-left of the topmost item in the
  152. tree (so this would be unaffected by scrolling the view).
  153. */
  154. Rectangle<int> getItemPosition (bool relativeToTreeViewTopLeft) const noexcept;
  155. /** Sends a signal to the treeview to make it refresh itself.
  156. Call this if your items have changed and you want the tree to update to reflect this.
  157. */
  158. void treeHasChanged() const noexcept;
  159. /** Sends a repaint message to redraw just this item.
  160. Note that you should only call this if you want to repaint a superficial change. If
  161. you're altering the tree's nodes, you should instead call treeHasChanged().
  162. */
  163. void repaintItem() const;
  164. /** Returns the row number of this item in the tree.
  165. The row number of an item will change according to which items are open.
  166. @see TreeView::getNumRowsInTree(), TreeView::getItemOnRow()
  167. */
  168. int getRowNumberInTree() const noexcept;
  169. /** Returns true if all the item's parent nodes are open.
  170. This is useful to check whether the item might actually be visible or not.
  171. */
  172. bool areAllParentsOpen() const noexcept;
  173. /** Changes whether lines are drawn to connect any sub-items to this item.
  174. By default, line-drawing is turned on according to LookAndFeel::areLinesDrawnForTreeView().
  175. */
  176. void setLinesDrawnForSubItems (bool shouldDrawLines) noexcept;
  177. //==============================================================================
  178. /** Tells the tree whether this item can potentially be opened.
  179. If your item could contain sub-items, this should return true; if it returns
  180. false then the tree will not try to open the item. This determines whether or
  181. not the item will be drawn with a 'plus' button next to it.
  182. */
  183. virtual bool mightContainSubItems() = 0;
  184. /** Returns a string to uniquely identify this item.
  185. If you're planning on using the TreeView::getOpennessState() method, then
  186. these strings will be used to identify which nodes are open. The string
  187. should be unique amongst the item's sibling items, but it's ok for there
  188. to be duplicates at other levels of the tree.
  189. If you're not going to store the state, then it's ok not to bother implementing
  190. this method.
  191. */
  192. virtual String getUniqueName() const;
  193. /** Called when an item is opened or closed.
  194. When setOpen() is called and the item has specified that it might
  195. have sub-items with the mightContainSubItems() method, this method
  196. is called to let the item create or manage its sub-items.
  197. So when this is called with isNowOpen set to true (i.e. when the item is being
  198. opened), a subclass might choose to use clearSubItems() and addSubItem() to
  199. refresh its sub-item list.
  200. When this is called with isNowOpen set to false, the subclass might want
  201. to use clearSubItems() to save on space, or it might choose to leave them,
  202. depending on the nature of the tree.
  203. You could also use this callback as a trigger to start a background process
  204. which asynchronously creates sub-items and adds them, if that's more
  205. appropriate for the task in hand.
  206. @see mightContainSubItems
  207. */
  208. virtual void itemOpennessChanged (bool isNowOpen);
  209. /** Must return the width required by this item.
  210. If your item needs to have a particular width in pixels, return that value; if
  211. you'd rather have it just fill whatever space is available in the treeview,
  212. return -1.
  213. If all your items return -1, no horizontal scrollbar will be shown, but if any
  214. items have fixed widths and extend beyond the width of the treeview, a
  215. scrollbar will appear.
  216. Each item can be a different width, but if they change width, you should call
  217. treeHasChanged() to update the tree.
  218. */
  219. virtual int getItemWidth() const { return -1; }
  220. /** Must return the height required by this item.
  221. This is the height in pixels that the item will take up. Items in the tree
  222. can be different heights, but if they change height, you should call
  223. treeHasChanged() to update the tree.
  224. */
  225. virtual int getItemHeight() const { return 20; }
  226. /** You can override this method to return false if you don't want to allow the
  227. user to select this item.
  228. */
  229. virtual bool canBeSelected() const { return true; }
  230. /** Creates a component that will be used to represent this item.
  231. You don't have to implement this method - if it returns nullptr then no component
  232. will be used for the item, and you can just draw it using the paintItem()
  233. callback. But if you do return a component, it will be positioned in the
  234. treeview so that it can be used to represent this item.
  235. The component returned will be managed by the treeview, so always return
  236. a new component, and don't keep a reference to it, as the treeview will
  237. delete it later when it goes off the screen or is no longer needed. Also
  238. bear in mind that if the component keeps a reference to the item that
  239. created it, that item could be deleted before the component. Its position
  240. and size will be completely managed by the tree, so don't attempt to move it
  241. around.
  242. Something you may want to do with your component is to give it a pointer to
  243. the TreeView that created it. This is perfectly safe, and there's no danger
  244. of it becoming a dangling pointer because the TreeView will always delete
  245. the component before it is itself deleted.
  246. As long as you stick to these rules you can return whatever kind of
  247. component you like. It's most useful if you're doing things like drag-and-drop
  248. of items, or want to use a Label component to edit item names, etc.
  249. */
  250. virtual Component* createItemComponent() { return nullptr; }
  251. //==============================================================================
  252. /** Draws the item's contents.
  253. You can choose to either implement this method and draw each item, or you
  254. can use createItemComponent() to create a component that will represent the
  255. item.
  256. If all you need in your tree is to be able to draw the items and detect when
  257. the user selects or double-clicks one of them, it's probably enough to
  258. use paintItem(), itemClicked() and itemDoubleClicked(). If you need more
  259. complicated interactions, you may need to use createItemComponent() instead.
  260. @param g the graphics context to draw into
  261. @param width the width of the area available for drawing
  262. @param height the height of the area available for drawing
  263. */
  264. virtual void paintItem (Graphics& g, int width, int height);
  265. /** Draws the item's open/close button.
  266. If you don't implement this method, the default behaviour is to call
  267. LookAndFeel::drawTreeviewPlusMinusBox(), but you can override it for custom
  268. effects. You may want to override it and call the base-class implementation
  269. with a different backgroundColour parameter, if your implementation has a
  270. background colour other than the default (white).
  271. */
  272. virtual void paintOpenCloseButton (Graphics&, const Rectangle<float>& area,
  273. Colour backgroundColour, bool isMouseOver);
  274. /** Draws the line that connects this item to the vertical line extending below its parent. */
  275. virtual void paintHorizontalConnectingLine (Graphics&, const Line<float>& line);
  276. /** Draws the line that extends vertically up towards one of its parents, or down to one of its children. */
  277. virtual void paintVerticalConnectingLine (Graphics&, const Line<float>& line);
  278. /** Called when the user clicks on this item.
  279. If you're using createItemComponent() to create a custom component for the
  280. item, the mouse-clicks might not make it through to the treeview, but this
  281. is how you find out about clicks when just drawing each item individually.
  282. The associated mouse-event details are passed in, so you can find out about
  283. which button, where it was, etc.
  284. @see itemDoubleClicked
  285. */
  286. virtual void itemClicked (const MouseEvent&);
  287. /** Called when the user double-clicks on this item.
  288. If you're using createItemComponent() to create a custom component for the
  289. item, the mouse-clicks might not make it through to the treeview, but this
  290. is how you find out about clicks when just drawing each item individually.
  291. The associated mouse-event details are passed in, so you can find out about
  292. which button, where it was, etc.
  293. If not overridden, the base class method here will open or close the item as
  294. if the 'plus' button had been clicked.
  295. @see itemClicked
  296. */
  297. virtual void itemDoubleClicked (const MouseEvent&);
  298. /** Called when the item is selected or deselected.
  299. Use this if you want to do something special when the item's selectedness
  300. changes. By default it'll get repainted when this happens.
  301. */
  302. virtual void itemSelectionChanged (bool isNowSelected);
  303. /** Called when the owner view changes */
  304. virtual void ownerViewChanged (TreeView* newOwner);
  305. /** The item can return a tool tip string here if it wants to.
  306. @see TooltipClient
  307. */
  308. virtual String getTooltip();
  309. //==============================================================================
  310. /** To allow items from your treeview to be dragged-and-dropped, implement this method.
  311. If this returns a non-null variant then when the user drags an item, the treeview will
  312. try to find a DragAndDropContainer in its parent hierarchy, and will use it to trigger
  313. a drag-and-drop operation, using this string as the source description, with the treeview
  314. itself as the source component.
  315. If you need more complex drag-and-drop behaviour, you can use custom components for
  316. the items, and use those to trigger the drag.
  317. To accept drag-and-drop in your tree, see isInterestedInDragSource(),
  318. isInterestedInFileDrag(), etc.
  319. @see DragAndDropContainer::startDragging
  320. */
  321. virtual var getDragSourceDescription();
  322. /** If you want your item to be able to have files drag-and-dropped onto it, implement this
  323. method and return true.
  324. If you return true and allow some files to be dropped, you'll also need to implement the
  325. filesDropped() method to do something with them.
  326. Note that this will be called often, so make your implementation very quick! There's
  327. certainly no time to try opening the files and having a think about what's inside them!
  328. For responding to internal drag-and-drop of other types of object, see isInterestedInDragSource().
  329. @see FileDragAndDropTarget::isInterestedInFileDrag, isInterestedInDragSource
  330. */
  331. virtual bool isInterestedInFileDrag (const StringArray& files);
  332. /** When files are dropped into this item, this callback is invoked.
  333. For this to work, you'll need to have also implemented isInterestedInFileDrag().
  334. The insertIndex value indicates where in the list of sub-items the files were dropped.
  335. If files are dropped onto an area of the tree where there are no visible items, this
  336. method is called on the root item of the tree, with an insert index of 0.
  337. @see FileDragAndDropTarget::filesDropped, isInterestedInFileDrag
  338. */
  339. virtual void filesDropped (const StringArray& files, int insertIndex);
  340. /** If you want your item to act as a DragAndDropTarget, implement this method and return true.
  341. If you implement this method, you'll also need to implement itemDropped() in order to handle
  342. the items when they are dropped.
  343. To respond to drag-and-drop of files from external applications, see isInterestedInFileDrag().
  344. @see DragAndDropTarget::isInterestedInDragSource, itemDropped
  345. */
  346. virtual bool isInterestedInDragSource (const DragAndDropTarget::SourceDetails& dragSourceDetails);
  347. /** When a things are dropped into this item, this callback is invoked.
  348. For this to work, you need to have also implemented isInterestedInDragSource().
  349. The insertIndex value indicates where in the list of sub-items the new items should be placed.
  350. If files are dropped onto an area of the tree where there are no visible items, this
  351. method is called on the root item of the tree, with an insert index of 0.
  352. @see isInterestedInDragSource, DragAndDropTarget::itemDropped
  353. */
  354. virtual void itemDropped (const DragAndDropTarget::SourceDetails& dragSourceDetails, int insertIndex);
  355. //==============================================================================
  356. /** Sets a flag to indicate that the item wants to be allowed
  357. to draw all the way across to the left edge of the treeview.
  358. By default this is false, which means that when the paintItem()
  359. method is called, its graphics context is clipped to only allow
  360. drawing within the item's rectangle. If this flag is set to true,
  361. then the graphics context isn't clipped on its left side, so it
  362. can draw all the way across to the left margin. Note that the
  363. context will still have its origin in the same place though, so
  364. the coordinates of anything to its left will be negative. It's
  365. mostly useful if you want to draw a wider bar behind the
  366. highlighted item.
  367. */
  368. void setDrawsInLeftMargin (bool canDrawInLeftMargin) noexcept;
  369. /** Sets a flag to indicate that the item wants to be allowed
  370. to draw all the way across to the right edge of the treeview.
  371. Similar to setDrawsInLeftMargin: when this flag is set to true,
  372. then the graphics context isn't clipped on the right side. Unlike
  373. setDrawsInLeftMargin, you will very rarely need to use this function,
  374. as this method won't clip the right margin unless your TreeViewItem
  375. overrides getItemWidth to return a positive value.
  376. @see setDrawsInLeftMargin, getItemWidth
  377. */
  378. void setDrawsInRightMargin (bool canDrawInRightMargin) noexcept;
  379. //==============================================================================
  380. /** Saves the current state of open/closed nodes so it can be restored later.
  381. This takes a snapshot of which sub-nodes have been explicitly opened or closed,
  382. and records it as XML. To identify node objects it uses the
  383. TreeViewItem::getUniqueName() method to create named paths. This
  384. means that the same state of open/closed nodes can be restored to a
  385. completely different instance of the tree, as long as it contains nodes
  386. whose unique names are the same.
  387. You'd normally want to use TreeView::getOpennessState() rather than call it
  388. for a specific item, but this can be handy if you need to briefly save the state
  389. for a section of the tree.
  390. The caller is responsible for deleting the object that is returned.
  391. Note that if all nodes of the tree are in their default state, then this may
  392. return a nullptr.
  393. @see TreeView::getOpennessState, restoreOpennessState
  394. */
  395. XmlElement* getOpennessState() const;
  396. /** Restores the openness of this item and all its sub-items from a saved state.
  397. See TreeView::restoreOpennessState for more details.
  398. You'd normally want to use TreeView::restoreOpennessState() rather than call it
  399. for a specific item, but this can be handy if you need to briefly save the state
  400. for a section of the tree.
  401. @see TreeView::restoreOpennessState, getOpennessState
  402. */
  403. void restoreOpennessState (const XmlElement& xml);
  404. //==============================================================================
  405. /** Returns the index of this item in its parent's sub-items. */
  406. int getIndexInParent() const noexcept;
  407. /** Returns true if this item is the last of its parent's sub-itens. */
  408. bool isLastOfSiblings() const noexcept;
  409. /** Creates a string that can be used to uniquely retrieve this item in the tree.
  410. The string that is returned can be passed to TreeView::findItemFromIdentifierString().
  411. The string takes the form of a path, constructed from the getUniqueName() of this
  412. item and all its parents, so these must all be correctly implemented for it to work.
  413. @see TreeView::findItemFromIdentifierString, getUniqueName
  414. */
  415. String getItemIdentifierString() const;
  416. //==============================================================================
  417. /**
  418. This handy class takes a copy of a TreeViewItem's openness when you create it,
  419. and restores that openness state when its destructor is called.
  420. This can very handy when you're refreshing sub-items - e.g.
  421. @code
  422. void MyTreeViewItem::updateChildItems()
  423. {
  424. OpennessRestorer openness (*this); // saves the openness state here..
  425. clearSubItems();
  426. // add a bunch of sub-items here which may or may not be the same as the ones that
  427. // were previously there
  428. addSubItem (...
  429. // ..and at this point, the old openness is restored, so any items that haven't
  430. // changed will have their old openness retained.
  431. }
  432. @endcode
  433. */
  434. class OpennessRestorer
  435. {
  436. public:
  437. OpennessRestorer (TreeViewItem&);
  438. ~OpennessRestorer();
  439. private:
  440. TreeViewItem& treeViewItem;
  441. ScopedPointer<XmlElement> oldOpenness;
  442. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpennessRestorer)
  443. };
  444. private:
  445. //==============================================================================
  446. TreeView* ownerView = nullptr;
  447. TreeViewItem* parentItem = nullptr;
  448. OwnedArray<TreeViewItem> subItems;
  449. int y = 0, itemHeight = 0, totalHeight = 0, itemWidth = 0, totalWidth = 0;
  450. int uid = 0;
  451. bool selected : 1;
  452. bool redrawNeeded : 1;
  453. bool drawLinesInside : 1;
  454. bool drawLinesSet : 1;
  455. bool drawsInLeftMargin : 1;
  456. bool drawsInRightMargin : 1;
  457. unsigned int openness : 2;
  458. friend class TreeView;
  459. void updatePositions (int newY);
  460. int getIndentX() const noexcept;
  461. void setOwnerView (TreeView*) noexcept;
  462. void paintRecursively (Graphics&, int width);
  463. TreeViewItem* getTopLevelItem() noexcept;
  464. TreeViewItem* findItemRecursively (int y) noexcept;
  465. TreeViewItem* getDeepestOpenParentItem() noexcept;
  466. int getNumRows() const noexcept;
  467. TreeViewItem* getItemOnRow (int index) noexcept;
  468. void deselectAllRecursively (TreeViewItem* itemToIgnore);
  469. int countSelectedItemsRecursively (int depth) const noexcept;
  470. TreeViewItem* getSelectedItemWithIndex (int index) noexcept;
  471. TreeViewItem* getNextVisibleItem (bool recurse) const noexcept;
  472. TreeViewItem* findItemFromIdentifierString (const String&);
  473. void restoreToDefaultOpenness();
  474. bool isFullyOpen() const noexcept;
  475. XmlElement* getOpennessState (bool canReturnNull) const;
  476. bool removeSubItemFromList (int index, bool deleteItem);
  477. void removeAllSubItemsFromList();
  478. bool areLinesDrawn() const;
  479. #if JUCE_CATCH_DEPRECATED_CODE_MISUSE
  480. // The parameters for these methods have changed - please update your code!
  481. virtual void isInterestedInDragSource (const String&, Component*) {}
  482. virtual int itemDropped (const String&, Component*, int) { return 0; }
  483. #endif
  484. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TreeViewItem)
  485. };
  486. //==============================================================================
  487. /**
  488. A tree-view component.
  489. Use one of these to hold and display a structure of TreeViewItem objects.
  490. */
  491. class JUCE_API TreeView : public Component,
  492. public SettableTooltipClient,
  493. public FileDragAndDropTarget,
  494. public DragAndDropTarget
  495. {
  496. public:
  497. //==============================================================================
  498. /** Creates an empty treeview.
  499. Once you've got a treeview component, you'll need to give it something to
  500. display, using the setRootItem() method.
  501. */
  502. TreeView (const String& componentName = String());
  503. /** Destructor. */
  504. ~TreeView();
  505. //==============================================================================
  506. /** Sets the item that is displayed in the treeview.
  507. A tree has a single root item which contains as many sub-items as it needs. If
  508. you want the tree to contain a number of root items, you should still use a single
  509. root item above these, but hide it using setRootItemVisible().
  510. You can pass nullptr to this method to clear the tree and remove its current root item.
  511. The object passed in will not be deleted by the treeview, it's up to the caller
  512. to delete it when no longer needed. BUT make absolutely sure that you don't delete
  513. this item until you've removed it from the tree, either by calling setRootItem (nullptr),
  514. or by deleting the tree first. You can also use deleteRootItem() as a quick way
  515. to delete it.
  516. */
  517. void setRootItem (TreeViewItem* newRootItem);
  518. /** Returns the tree's root item.
  519. This will be the last object passed to setRootItem(), or nullptr if none has been set.
  520. */
  521. TreeViewItem* getRootItem() const noexcept { return rootItem; }
  522. /** This will remove and delete the current root item.
  523. It's a convenient way of deleting the item and calling setRootItem (nullptr).
  524. */
  525. void deleteRootItem();
  526. /** Changes whether the tree's root item is shown or not.
  527. If the root item is hidden, only its sub-items will be shown in the treeview - this
  528. lets you make the tree look as if it's got many root items. If it's hidden, this call
  529. will also make sure the root item is open (otherwise the treeview would look empty).
  530. */
  531. void setRootItemVisible (bool shouldBeVisible);
  532. /** Returns true if the root item is visible.
  533. @see setRootItemVisible
  534. */
  535. bool isRootItemVisible() const noexcept { return rootItemVisible; }
  536. /** Sets whether items are open or closed by default.
  537. Normally, items are closed until the user opens them, but you can use this
  538. to make them default to being open until explicitly closed.
  539. @see areItemsOpenByDefault
  540. */
  541. void setDefaultOpenness (bool isOpenByDefault);
  542. /** Returns true if the tree's items default to being open.
  543. @see setDefaultOpenness
  544. */
  545. bool areItemsOpenByDefault() const noexcept { return defaultOpenness; }
  546. /** This sets a flag to indicate that the tree can be used for multi-selection.
  547. You can always select multiple items internally by calling the
  548. TreeViewItem::setSelected() method, but this flag indicates whether the user
  549. is allowed to multi-select by clicking on the tree.
  550. By default it is disabled.
  551. @see isMultiSelectEnabled
  552. */
  553. void setMultiSelectEnabled (bool canMultiSelect);
  554. /** Returns whether multi-select has been enabled for the tree.
  555. @see setMultiSelectEnabled
  556. */
  557. bool isMultiSelectEnabled() const noexcept { return multiSelectEnabled; }
  558. /** Sets a flag to indicate whether to hide the open/close buttons.
  559. @see areOpenCloseButtonsVisible
  560. */
  561. void setOpenCloseButtonsVisible (bool shouldBeVisible);
  562. /** Returns whether open/close buttons are shown.
  563. @see setOpenCloseButtonsVisible
  564. */
  565. bool areOpenCloseButtonsVisible() const noexcept { return openCloseButtonsVisible; }
  566. //==============================================================================
  567. /** Deselects any items that are currently selected. */
  568. void clearSelectedItems();
  569. /** Returns the number of items that are currently selected.
  570. If maximumDepthToSearchTo is >= 0, it lets you specify a maximum depth to which the
  571. tree will be recursed.
  572. @see getSelectedItem, clearSelectedItems
  573. */
  574. int getNumSelectedItems (int maximumDepthToSearchTo = -1) const noexcept;
  575. /** Returns one of the selected items in the tree.
  576. @param index the index, 0 to (getNumSelectedItems() - 1)
  577. */
  578. TreeViewItem* getSelectedItem (int index) const noexcept;
  579. /** Moves the selected row up or down by the specified number of rows. */
  580. void moveSelectedRow (int deltaRows);
  581. //==============================================================================
  582. /** Returns the number of rows the tree is using.
  583. This will depend on which items are open.
  584. @see TreeViewItem::getRowNumberInTree()
  585. */
  586. int getNumRowsInTree() const;
  587. /** Returns the item on a particular row of the tree.
  588. If the index is out of range, this will return nullptr.
  589. @see getNumRowsInTree, TreeViewItem::getRowNumberInTree()
  590. */
  591. TreeViewItem* getItemOnRow (int index) const;
  592. /** Returns the item that contains a given y position.
  593. The y is relative to the top of the TreeView component.
  594. */
  595. TreeViewItem* getItemAt (int yPosition) const noexcept;
  596. /** Tries to scroll the tree so that this item is on-screen somewhere. */
  597. void scrollToKeepItemVisible (TreeViewItem* item);
  598. /** Returns the treeview's Viewport object. */
  599. Viewport* getViewport() const noexcept;
  600. /** Returns the number of pixels by which each nested level of the tree is indented.
  601. @see setIndentSize
  602. */
  603. int getIndentSize() noexcept;
  604. /** Changes the distance by which each nested level of the tree is indented.
  605. @see getIndentSize
  606. */
  607. void setIndentSize (int newIndentSize);
  608. /** Searches the tree for an item with the specified identifier.
  609. The identifier string must have been created by calling TreeViewItem::getItemIdentifierString().
  610. If no such item exists, this will return false. If the item is found, all of its items
  611. will be automatically opened.
  612. */
  613. TreeViewItem* findItemFromIdentifierString (const String& identifierString) const;
  614. //==============================================================================
  615. /** Saves the current state of open/closed nodes so it can be restored later.
  616. This takes a snapshot of which nodes have been explicitly opened or closed,
  617. and records it as XML. To identify node objects it uses the
  618. TreeViewItem::getUniqueName() method to create named paths. This
  619. means that the same state of open/closed nodes can be restored to a
  620. completely different instance of the tree, as long as it contains nodes
  621. whose unique names are the same.
  622. The caller is responsible for deleting the object that is returned.
  623. @param alsoIncludeScrollPosition if this is true, the state will also
  624. include information about where the
  625. tree has been scrolled to vertically,
  626. so this can also be restored
  627. @see restoreOpennessState
  628. */
  629. XmlElement* getOpennessState (bool alsoIncludeScrollPosition) const;
  630. /** Restores a previously saved arrangement of open/closed nodes.
  631. This will try to restore a snapshot of the tree's state that was created by
  632. the getOpennessState() method. If any of the nodes named in the original
  633. XML aren't present in this tree, they will be ignored.
  634. If restoreStoredSelection is true, it will also try to re-select any items that
  635. were selected in the stored state.
  636. @see getOpennessState
  637. */
  638. void restoreOpennessState (const XmlElement& newState,
  639. bool restoreStoredSelection);
  640. //==============================================================================
  641. /** A set of colour IDs to use to change the colour of various aspects of the treeview.
  642. These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
  643. methods.
  644. @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
  645. */
  646. enum ColourIds
  647. {
  648. backgroundColourId = 0x1000500, /**< A background colour to fill the component with. */
  649. linesColourId = 0x1000501, /**< The colour to draw the lines with.*/
  650. dragAndDropIndicatorColourId = 0x1000502, /**< The colour to use for the drag-and-drop target position indicator. */
  651. selectedItemBackgroundColourId = 0x1000503, /**< The colour to use to fill the background of any selected items. */
  652. oddItemsColourId = 0x1000504, /**< The colour to use to fill the backround of the odd numbered items. */
  653. evenItemsColourId = 0x1000505 /**< The colour to use to fill the backround of the even numbered items. */
  654. };
  655. //==============================================================================
  656. /** This abstract base class is implemented by LookAndFeel classes to provide
  657. treeview drawing functionality.
  658. */
  659. struct JUCE_API LookAndFeelMethods
  660. {
  661. virtual ~LookAndFeelMethods() {}
  662. virtual void drawTreeviewPlusMinusBox (Graphics&, const Rectangle<float>& area,
  663. Colour backgroundColour, bool isItemOpen, bool isMouseOver) = 0;
  664. virtual bool areLinesDrawnForTreeView (TreeView&) = 0;
  665. virtual int getTreeViewIndentSize (TreeView&) = 0;
  666. };
  667. //==============================================================================
  668. /** @internal */
  669. void paint (Graphics&) override;
  670. /** @internal */
  671. void resized() override;
  672. /** @internal */
  673. bool keyPressed (const KeyPress&) override;
  674. /** @internal */
  675. void colourChanged() override;
  676. /** @internal */
  677. void enablementChanged() override;
  678. /** @internal */
  679. bool isInterestedInFileDrag (const StringArray& files) override;
  680. /** @internal */
  681. void fileDragEnter (const StringArray& files, int x, int y) override;
  682. /** @internal */
  683. void fileDragMove (const StringArray& files, int x, int y) override;
  684. /** @internal */
  685. void fileDragExit (const StringArray& files) override;
  686. /** @internal */
  687. void filesDropped (const StringArray& files, int x, int y) override;
  688. /** @internal */
  689. bool isInterestedInDragSource (const SourceDetails&) override;
  690. /** @internal */
  691. void itemDragEnter (const SourceDetails&) override;
  692. /** @internal */
  693. void itemDragMove (const SourceDetails&) override;
  694. /** @internal */
  695. void itemDragExit (const SourceDetails&) override;
  696. /** @internal */
  697. void itemDropped (const SourceDetails&) override;
  698. private:
  699. class ContentComponent;
  700. class TreeViewport;
  701. class InsertPointHighlight;
  702. class TargetGroupHighlight;
  703. friend class TreeViewItem;
  704. friend class ContentComponent;
  705. friend struct ContainerDeletePolicy<TreeViewport>;
  706. friend struct ContainerDeletePolicy<InsertPointHighlight>;
  707. friend struct ContainerDeletePolicy<TargetGroupHighlight>;
  708. ScopedPointer<TreeViewport> viewport;
  709. CriticalSection nodeAlterationLock;
  710. TreeViewItem* rootItem = nullptr;
  711. ScopedPointer<InsertPointHighlight> dragInsertPointHighlight;
  712. ScopedPointer<TargetGroupHighlight> dragTargetGroupHighlight;
  713. int indentSize = -1;
  714. bool defaultOpenness = false, needsRecalculating = true, rootItemVisible = true;
  715. bool multiSelectEnabled = false, openCloseButtonsVisible = true;
  716. void itemsChanged() noexcept;
  717. void recalculateIfNeeded();
  718. void updateButtonUnderMouse (const MouseEvent&);
  719. struct InsertPoint;
  720. void showDragHighlight (const InsertPoint&) noexcept;
  721. void hideDragHighlight() noexcept;
  722. void handleDrag (const StringArray&, const SourceDetails&);
  723. void handleDrop (const StringArray&, const SourceDetails&);
  724. bool toggleOpenSelectedItem();
  725. void moveOutOfSelectedItem();
  726. void moveIntoSelectedItem();
  727. void moveByPages (int numPages);
  728. #if JUCE_CATCH_DEPRECATED_CODE_MISUSE
  729. // this method has been deprecated - see the new version..
  730. virtual int paintOpenCloseButton (Graphics&, int, int, bool) { return 0; }
  731. #endif
  732. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TreeView)
  733. };
  734. } // namespace juce