1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12 
13    End User License Agreement: www.juce.com/juce-6-licence
14    Privacy Policy: www.juce.com/juce-privacy-policy
15 
16    Or: You may also use this code under the terms of the GPL v3 (see
17    www.gnu.org/licenses).
18 
19    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21    DISCLAIMED.
22 
23   ==============================================================================
24 */
25 
26 namespace juce
27 {
28 
29 //==============================================================================
30 /**
31     One of these is used by a TableListBox as the data model for the table's contents.
32 
33     The virtual methods that you override in this class take care of drawing the
34     table cells, and reacting to events.
35 
36     @see TableListBox
37 
38     @tags{GUI}
39 */
40 class JUCE_API  TableListBoxModel
41 {
42 public:
43     //==============================================================================
44     TableListBoxModel() = default;
45 
46     /** Destructor. */
47     virtual ~TableListBoxModel() = default;
48 
49     //==============================================================================
50     /** This must return the number of rows currently in the table.
51 
52         If the number of rows changes, you must call TableListBox::updateContent() to
53         cause it to refresh the list.
54     */
55     virtual int getNumRows() = 0;
56 
57     /** This must draw the background behind one of the rows in the table.
58 
59         The graphics context has its origin at the row's top-left, and your method
60         should fill the area specified by the width and height parameters.
61 
62         Note that the rowNumber value may be greater than the number of rows in your
63         list, so be careful that you don't assume it's less than getNumRows().
64     */
65     virtual void paintRowBackground (Graphics&,
66                                      int rowNumber,
67                                      int width, int height,
68                                      bool rowIsSelected) = 0;
69 
70     /** This must draw one of the cells.
71 
72         The graphics context's origin will already be set to the top-left of the cell,
73         whose size is specified by (width, height).
74 
75         Note that the rowNumber value may be greater than the number of rows in your
76         list, so be careful that you don't assume it's less than getNumRows().
77     */
78     virtual void paintCell (Graphics&,
79                             int rowNumber,
80                             int columnId,
81                             int width, int height,
82                             bool rowIsSelected) = 0;
83 
84     //==============================================================================
85     /** This is used to create or update a custom component to go in a cell.
86 
87         Any cell may contain a custom component, or can just be drawn with the paintCell() method
88         and handle mouse clicks with cellClicked().
89 
90         This method will be called whenever a custom component might need to be updated - e.g.
91         when the table is changed, or TableListBox::updateContent() is called.
92 
93         If you don't need a custom component for the specified cell, then return nullptr.
94         (Bear in mind that even if you're not creating a new component, you may still need to
95         delete existingComponentToUpdate if it's non-null).
96 
97         If you do want a custom component, and the existingComponentToUpdate is null, then
98         this method must create a new component suitable for the cell, and return it.
99 
100         If the existingComponentToUpdate is non-null, it will be a pointer to a component previously created
101         by this method. In this case, the method must either update it to make sure it's correctly representing
102         the given cell (which may be different from the one that the component was created for), or it can
103         delete this component and return a new one.
104     */
105     virtual Component* refreshComponentForCell (int rowNumber, int columnId, bool isRowSelected,
106                                                 Component* existingComponentToUpdate);
107 
108     //==============================================================================
109     /** This callback is made when the user clicks on one of the cells in the table.
110 
111         The mouse event's coordinates will be relative to the entire table row.
112         @see cellDoubleClicked, backgroundClicked
113     */
114     virtual void cellClicked (int rowNumber, int columnId, const MouseEvent&);
115 
116     /** This callback is made when the user clicks on one of the cells in the table.
117 
118         The mouse event's coordinates will be relative to the entire table row.
119         @see cellClicked, backgroundClicked
120     */
121     virtual void cellDoubleClicked (int rowNumber, int columnId, const MouseEvent&);
122 
123     /** This can be overridden to react to the user double-clicking on a part of the list where
124         there are no rows.
125 
126         @see cellClicked
127     */
128     virtual void backgroundClicked (const MouseEvent&);
129 
130     //==============================================================================
131     /** This callback is made when the table's sort order is changed.
132 
133         This could be because the user has clicked a column header, or because the
134         TableHeaderComponent::setSortColumnId() method was called.
135 
136         If you implement this, your method should re-sort the table using the given
137         column as the key.
138     */
139     virtual void sortOrderChanged (int newSortColumnId, bool isForwards);
140 
141     //==============================================================================
142     /** Returns the best width for one of the columns.
143 
144         If you implement this method, you should measure the width of all the items
145         in this column, and return the best size.
146 
147         Returning 0 means that the column shouldn't be changed.
148 
149         This is used by TableListBox::autoSizeColumn() and TableListBox::autoSizeAllColumns().
150     */
151     virtual int getColumnAutoSizeWidth (int columnId);
152 
153     /** Returns a tooltip for a particular cell in the table. */
154     virtual String getCellTooltip (int rowNumber, int columnId);
155 
156     //==============================================================================
157     /** Override this to be informed when rows are selected or deselected.
158         @see ListBox::selectedRowsChanged()
159     */
160     virtual void selectedRowsChanged (int lastRowSelected);
161 
162     /** Override this to be informed when the delete key is pressed.
163         @see ListBox::deleteKeyPressed()
164     */
165     virtual void deleteKeyPressed (int lastRowSelected);
166 
167     /** Override this to be informed when the return key is pressed.
168         @see ListBox::returnKeyPressed()
169     */
170     virtual void returnKeyPressed (int lastRowSelected);
171 
172     /** Override this to be informed when the list is scrolled.
173 
174         This might be caused by the user moving the scrollbar, or by programmatic changes
175         to the list position.
176     */
177     virtual void listWasScrolled();
178 
179     /** To allow rows from your table to be dragged-and-dropped, implement this method.
180 
181         If this returns a non-null variant then when the user drags a row, the table will try to
182         find a DragAndDropContainer in its parent hierarchy, and will use it to trigger a
183         drag-and-drop operation, using this string as the source description, and the listbox
184         itself as the source component.
185 
186         @see getDragSourceCustomData, DragAndDropContainer::startDragging
187     */
188     virtual var getDragSourceDescription (const SparseSet<int>& currentlySelectedRows);
189 
190 private:
191    #if JUCE_CATCH_DEPRECATED_CODE_MISUSE
192     // This method's signature has changed to take a MouseEvent parameter - please update your code!
193     JUCE_DEPRECATED_WITH_BODY (virtual int backgroundClicked(), { return 0; })
194    #endif
195 };
196 
197 
198 //==============================================================================
199 /**
200     A table of cells, using a TableHeaderComponent as its header.
201 
202     This component makes it easy to create a table by providing a TableListBoxModel as
203     the data source.
204 
205 
206     @see TableListBoxModel, TableHeaderComponent
207 
208     @tags{GUI}
209 */
210 class JUCE_API  TableListBox   : public ListBox,
211                                  private ListBoxModel,
212                                  private TableHeaderComponent::Listener
213 {
214 public:
215     //==============================================================================
216     /** Creates a TableListBox.
217 
218         The model pointer passed-in can be null, in which case you can set it later
219         with setModel(). The TableListBox does not take ownership of the model - it's
220         the caller's responsibility to manage its lifetime and make sure it
221         doesn't get deleted while still being used.
222     */
223     TableListBox (const String& componentName = String(),
224                   TableListBoxModel* model = nullptr);
225 
226     /** Destructor. */
227     ~TableListBox() override;
228 
229     //==============================================================================
230     /** Changes the TableListBoxModel that is being used for this table.
231         The TableListBox does not take ownership of the model - it's the caller's responsibility
232         to manage its lifetime and make sure it doesn't get deleted while still being used.
233     */
234     void setModel (TableListBoxModel* newModel);
235 
236     /** Returns the model currently in use. */
getModel()237     TableListBoxModel* getModel() const noexcept                    { return model; }
238 
239     //==============================================================================
240     /** Returns the header component being used in this table. */
getHeader()241     TableHeaderComponent& getHeader() const noexcept                { return *header; }
242 
243     /** Sets the header component to use for the table.
244         The table will take ownership of the component that you pass in, and will delete it
245         when it's no longer needed.
246         The pointer passed in may not be null.
247     */
248     void setHeader (std::unique_ptr<TableHeaderComponent> newHeader);
249 
250     /** Changes the height of the table header component.
251         @see getHeaderHeight
252     */
253     void setHeaderHeight (int newHeight);
254 
255     /** Returns the height of the table header.
256         @see setHeaderHeight
257     */
258     int getHeaderHeight() const noexcept;
259 
260     //==============================================================================
261     /** Resizes a column to fit its contents.
262 
263         This uses TableListBoxModel::getColumnAutoSizeWidth() to find the best width,
264         and applies that to the column.
265 
266         @see autoSizeAllColumns, TableHeaderComponent::setColumnWidth
267     */
268     void autoSizeColumn (int columnId);
269 
270     /** Calls autoSizeColumn() for all columns in the table. */
271     void autoSizeAllColumns();
272 
273     /** Enables or disables the auto size options on the popup menu.
274         By default, these are enabled.
275     */
276     void setAutoSizeMenuOptionShown (bool shouldBeShown) noexcept;
277 
278     /** True if the auto-size options should be shown on the menu.
279         @see setAutoSizeMenuOptionShown
280     */
isAutoSizeMenuOptionShown()281     bool isAutoSizeMenuOptionShown() const noexcept                 { return autoSizeOptionsShown; }
282 
283     /** Returns the position of one of the cells in the table.
284 
285         If relativeToComponentTopLeft is true, the coordinates are relative to
286         the table component's top-left. The row number isn't checked to see if it's
287         in-range, but the column ID must exist or this will return an empty rectangle.
288 
289         If relativeToComponentTopLeft is false, the coordinates are relative to the
290         top-left of the table's top-left cell.
291     */
292     Rectangle<int> getCellPosition (int columnId, int rowNumber,
293                                     bool relativeToComponentTopLeft) const;
294 
295     /** Returns the component that currently represents a given cell.
296         If the component for this cell is off-screen or if the position is out-of-range,
297         this may return nullptr.
298         @see getCellPosition
299     */
300     Component* getCellComponent (int columnId, int rowNumber) const;
301 
302     /** Scrolls horizontally if necessary to make sure that a particular column is visible.
303 
304         @see ListBox::scrollToEnsureRowIsOnscreen
305     */
306     void scrollToEnsureColumnIsOnscreen (int columnId);
307 
308     //==============================================================================
309     /** @internal */
310     int getNumRows() override;
311     /** @internal */
312     void paintListBoxItem (int, Graphics&, int, int, bool) override;
313     /** @internal */
314     Component* refreshComponentForRow (int rowNumber, bool isRowSelected, Component* existingComponentToUpdate) override;
315     /** @internal */
316     void selectedRowsChanged (int row) override;
317     /** @internal */
318     void deleteKeyPressed (int currentSelectedRow) override;
319     /** @internal */
320     void returnKeyPressed (int currentSelectedRow) override;
321     /** @internal */
322     void backgroundClicked (const MouseEvent&) override;
323     /** @internal */
324     void listWasScrolled() override;
325     /** @internal */
326     void tableColumnsChanged (TableHeaderComponent*) override;
327     /** @internal */
328     void tableColumnsResized (TableHeaderComponent*) override;
329     /** @internal */
330     void tableSortOrderChanged (TableHeaderComponent*) override;
331     /** @internal */
332     void tableColumnDraggingChanged (TableHeaderComponent*, int) override;
333     /** @internal */
334     void resized() override;
335 
336 
337 private:
338     //==============================================================================
339     class Header;
340     class RowComp;
341 
342     TableHeaderComponent* header = nullptr;
343     TableListBoxModel* model;
344     int columnIdNowBeingDragged = 0;
345     bool autoSizeOptionsShown = true;
346 
347     void updateColumnComponents() const;
348 
349     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TableListBox)
350 };
351 
352 } // namespace juce
353