|
- /*
- ==============================================================================
-
- This file is part of the JUCE library - "Jules' Utility Class Extensions"
- Copyright 2004-11 by Raw Material Software Ltd.
-
- ------------------------------------------------------------------------------
-
- JUCE can be redistributed and/or modified under the terms of the GNU General
- Public License (Version 2), as published by the Free Software Foundation.
- A copy of the license is included in the JUCE distribution, or can be found
- online at www.gnu.org/licenses.
-
- JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- ------------------------------------------------------------------------------
-
- To release a closed-source product which uses JUCE, commercial licenses are
- available: visit www.rawmaterialsoftware.com/juce for more information.
-
- ==============================================================================
- */
-
- #ifndef __JUCE_LISTBOX_JUCEHEADER__
- #define __JUCE_LISTBOX_JUCEHEADER__
-
- #include "../layout/juce_Viewport.h"
-
-
- //==============================================================================
- /**
- A subclass of this is used to drive a ListBox.
-
- @see ListBox
- */
- class JUCE_API ListBoxModel
- {
- public:
- //==============================================================================
- /** Destructor. */
- virtual ~ListBoxModel() {}
-
- //==============================================================================
- /** This has to return the number of items in the list.
-
- @see ListBox::getNumRows()
- */
- virtual int getNumRows() = 0;
-
- /** This method must be implemented to draw a row of the list.
- */
- virtual void paintListBoxItem (int rowNumber,
- Graphics& g,
- int width, int height,
- bool rowIsSelected) = 0;
-
- /** This is used to create or update a custom component to go in a row of the list.
-
- Any row may contain a custom component, or can just be drawn with the paintListBoxItem() method
- and handle mouse clicks with listBoxItemClicked().
-
- This method will be called whenever a custom component might need to be updated - e.g.
- when the table is changed, or TableListBox::updateContent() is called.
-
- If you don't need a custom component for the specified row, then return nullptr.
- (Bear in mind that even if you're not creating a new component, you may still need to
- delete existingComponentToUpdate if it's non-null).
-
- If you do want a custom component, and the existingComponentToUpdate is null, then
- this method must create a suitable new component and return it.
-
- If the existingComponentToUpdate is non-null, it will be a pointer to a component previously created
- by this method. In this case, the method must either update it to make sure it's correctly representing
- the given row (which may be different from the one that the component was created for), or it can
- delete this component and return a new one.
-
- The component that your method returns will be deleted by the ListBox when it is no longer needed.
-
- Bear in mind that if you put a custom component inside the row but still want the
- listbox to automatically handle clicking, selection, etc, then you'll need to make sure
- your custom component doesn't intercept all the mouse events that land on it, e.g by
- using Component::setInterceptsMouseClicks().
- */
- virtual Component* refreshComponentForRow (int rowNumber, bool isRowSelected,
- Component* existingComponentToUpdate);
-
- /** This can be overridden to react to the user clicking on a row.
-
- @see listBoxItemDoubleClicked
- */
- virtual void listBoxItemClicked (int row, const MouseEvent& e);
-
- /** This can be overridden to react to the user double-clicking on a row.
-
- @see listBoxItemClicked
- */
- virtual void listBoxItemDoubleClicked (int row, const MouseEvent& e);
-
- /** This can be overridden to react to the user double-clicking on a part of the list where
- there are no rows.
-
- @see listBoxItemClicked
- */
- virtual void backgroundClicked();
-
- /** Override this to be informed when rows are selected or deselected.
-
- This will be called whenever a row is selected or deselected. If a range of
- rows is selected all at once, this will just be called once for that event.
-
- @param lastRowSelected the last row that the user selected. If no
- rows are currently selected, this may be -1.
- */
- virtual void selectedRowsChanged (int lastRowSelected);
-
- /** Override this to be informed when the delete key is pressed.
-
- If no rows are selected when they press the key, this won't be called.
-
- @param lastRowSelected the last row that had been selected when they pressed the
- key - if there are multiple selections, this might not be
- very useful
- */
- virtual void deleteKeyPressed (int lastRowSelected);
-
- /** Override this to be informed when the return key is pressed.
-
- If no rows are selected when they press the key, this won't be called.
-
- @param lastRowSelected the last row that had been selected when they pressed the
- key - if there are multiple selections, this might not be
- very useful
- */
- virtual void returnKeyPressed (int lastRowSelected);
-
- /** Override this to be informed when the list is scrolled.
-
- This might be caused by the user moving the scrollbar, or by programmatic changes
- to the list position.
- */
- virtual void listWasScrolled();
-
- /** To allow rows from your list to be dragged-and-dropped, implement this method.
-
- If this returns a non-null variant then when the user drags a row, the listbox will
- try to find a DragAndDropContainer in its parent hierarchy, and will use it to trigger
- a drag-and-drop operation, using this string as the source description, with the listbox
- itself as the source component.
-
- @see DragAndDropContainer::startDragging
- */
- virtual var getDragSourceDescription (const SparseSet<int>& currentlySelectedRows);
-
- /** You can override this to provide tool tips for specific rows.
- @see TooltipClient
- */
- virtual String getTooltipForRow (int row);
- };
-
-
- //==============================================================================
- /**
- A list of items that can be scrolled vertically.
-
- To create a list, you'll need to create a subclass of ListBoxModel. This can
- either paint each row of the list and respond to events via callbacks, or for
- more specialised tasks, it can supply a custom component to fill each row.
-
- @see ComboBox, TableListBox
- */
- class JUCE_API ListBox : public Component,
- public SettableTooltipClient
- {
- public:
- //==============================================================================
- /** Creates a ListBox.
-
- The model pointer passed-in can be null, in which case you can set it later
- with setModel().
- */
- ListBox (const String& componentName = String::empty,
- ListBoxModel* model = nullptr);
-
- /** Destructor. */
- ~ListBox();
-
-
- //==============================================================================
- /** Changes the current data model to display. */
- void setModel (ListBoxModel* newModel);
-
- /** Returns the current list model. */
- ListBoxModel* getModel() const noexcept { return model; }
-
-
- //==============================================================================
- /** Causes the list to refresh its content.
-
- Call this when the number of rows in the list changes, or if you want it
- to call refreshComponentForRow() on all the row components.
-
- This must only be called from the main message thread.
- */
- void updateContent();
-
- //==============================================================================
- /** Turns on multiple-selection of rows.
-
- By default this is disabled.
-
- When your row component gets clicked you'll need to call the
- selectRowsBasedOnModifierKeys() method to tell the list that it's been
- clicked and to get it to do the appropriate selection based on whether
- the ctrl/shift keys are held down.
- */
- void setMultipleSelectionEnabled (bool shouldBeEnabled);
-
- /** Makes the list react to mouse moves by selecting the row that the mouse if over.
-
- This function is here primarily for the ComboBox class to use, but might be
- useful for some other purpose too.
- */
- void setMouseMoveSelectsRows (bool shouldSelect);
-
- //==============================================================================
- /** Selects a row.
-
- If the row is already selected, this won't do anything.
-
- @param rowNumber the row to select
- @param dontScrollToShowThisRow if true, the list's position won't change; if false and
- the selected row is off-screen, it'll scroll to make
- sure that row is on-screen
- @param deselectOthersFirst if true and there are multiple selections, these will
- first be deselected before this item is selected
- @see isRowSelected, selectRowsBasedOnModifierKeys, flipRowSelection, deselectRow,
- deselectAllRows, selectRangeOfRows
- */
- void selectRow (int rowNumber,
- bool dontScrollToShowThisRow = false,
- bool deselectOthersFirst = true);
-
- /** Selects a set of rows.
-
- This will add these rows to the current selection, so you might need to
- clear the current selection first with deselectAllRows()
-
- @param firstRow the first row to select (inclusive)
- @param lastRow the last row to select (inclusive)
- */
- void selectRangeOfRows (int firstRow,
- int lastRow);
-
- /** Deselects a row.
-
- If it's not currently selected, this will do nothing.
-
- @see selectRow, deselectAllRows
- */
- void deselectRow (int rowNumber);
-
- /** Deselects any currently selected rows.
-
- @see deselectRow
- */
- void deselectAllRows();
-
- /** Selects or deselects a row.
-
- If the row's currently selected, this deselects it, and vice-versa.
- */
- void flipRowSelection (int rowNumber);
-
- /** Returns a sparse set indicating the rows that are currently selected.
- @see setSelectedRows
- */
- SparseSet<int> getSelectedRows() const;
-
- /** Sets the rows that should be selected, based on an explicit set of ranges.
-
- If sendNotificationEventToModel is true, the ListBoxModel::selectedRowsChanged()
- method will be called. If it's false, no notification will be sent to the model.
-
- @see getSelectedRows
- */
- void setSelectedRows (const SparseSet<int>& setOfRowsToBeSelected,
- bool sendNotificationEventToModel = true);
-
- /** Checks whether a row is selected.
- */
- bool isRowSelected (int rowNumber) const;
-
- /** Returns the number of rows that are currently selected.
-
- @see getSelectedRow, isRowSelected, getLastRowSelected
- */
- int getNumSelectedRows() const;
-
- /** Returns the row number of a selected row.
-
- This will return the row number of the Nth selected row. The row numbers returned will
- be sorted in order from low to high.
-
- @param index the index of the selected row to return, (from 0 to getNumSelectedRows() - 1)
- @returns the row number, or -1 if the index was out of range or if there aren't any rows
- selected
- @see getNumSelectedRows, isRowSelected, getLastRowSelected
- */
- int getSelectedRow (int index = 0) const;
-
- /** Returns the last row that the user selected.
-
- This isn't the same as the highest row number that is currently selected - if the user
- had multiply-selected rows 10, 5 and then 6 in that order, this would return 6.
-
- If nothing is selected, it will return -1.
- */
- int getLastRowSelected() const;
-
- /** Multiply-selects rows based on the modifier keys.
-
- If no modifier keys are down, this will select the given row and
- deselect any others.
-
- If the ctrl (or command on the Mac) key is down, it'll flip the
- state of the selected row.
-
- If the shift key is down, it'll select up to the given row from the
- last row selected.
-
- @see selectRow
- */
- void selectRowsBasedOnModifierKeys (int rowThatWasClickedOn,
- const ModifierKeys& modifiers,
- bool isMouseUpEvent);
-
- //==============================================================================
- /** Scrolls the list to a particular position.
-
- The proportion is between 0 and 1.0, so 0 scrolls to the top of the list,
- 1.0 scrolls to the bottom.
-
- If the total number of rows all fit onto the screen at once, then this
- method won't do anything.
-
- @see getVerticalPosition
- */
- void setVerticalPosition (double newProportion);
-
- /** Returns the current vertical position as a proportion of the total.
-
- This can be used in conjunction with setVerticalPosition() to save and restore
- the list's position. It returns a value in the range 0 to 1.
-
- @see setVerticalPosition
- */
- double getVerticalPosition() const;
-
- /** Scrolls if necessary to make sure that a particular row is visible.
- */
- void scrollToEnsureRowIsOnscreen (int row);
-
- /** Returns a pointer to the vertical scrollbar. */
- ScrollBar* getVerticalScrollBar() const noexcept;
-
- /** Returns a pointer to the horizontal scrollbar. */
- ScrollBar* getHorizontalScrollBar() const noexcept;
-
- /** Finds the row index that contains a given x,y position.
- The position is relative to the ListBox's top-left.
- If no row exists at this position, the method will return -1.
- @see getComponentForRowNumber
- */
- int getRowContainingPosition (int x, int y) const noexcept;
-
- /** Finds a row index that would be the most suitable place to insert a new
- item for a given position.
-
- This is useful when the user is e.g. dragging and dropping onto the listbox,
- because it lets you easily choose the best position to insert the item that
- they drop, based on where they drop it.
-
- If the position is out of range, this will return -1. If the position is
- beyond the end of the list, it will return getNumRows() to indicate the end
- of the list.
-
- @see getComponentForRowNumber
- */
- int getInsertionIndexForPosition (int x, int y) const noexcept;
-
- /** Returns the position of one of the rows, relative to the top-left of
- the listbox.
-
- This may be off-screen, and the range of the row number that is passed-in is
- not checked to see if it's a valid row.
- */
- Rectangle<int> getRowPosition (int rowNumber,
- bool relativeToComponentTopLeft) const noexcept;
-
- /** Finds the row component for a given row in the list.
-
- The component returned will have been created using createRowComponent().
-
- If the component for this row is off-screen or if the row is out-of-range,
- this will return 0.
-
- @see getRowContainingPosition
- */
- Component* getComponentForRowNumber (int rowNumber) const noexcept;
-
- /** Returns the row number that the given component represents.
- If the component isn't one of the list's rows, this will return -1.
- */
- int getRowNumberOfComponent (Component* rowComponent) const noexcept;
-
- /** Returns the width of a row (which may be less than the width of this component
- if there's a scrollbar).
- */
- int getVisibleRowWidth() const noexcept;
-
- //==============================================================================
- /** Sets the height of each row in the list.
- The default height is 22 pixels.
- @see getRowHeight
- */
- void setRowHeight (int newHeight);
-
- /** Returns the height of a row in the list.
- @see setRowHeight
- */
- int getRowHeight() const noexcept { return rowHeight; }
-
- /** Returns the number of rows actually visible.
-
- This is the number of whole rows which will fit on-screen, so the value might
- be more than the actual number of rows in the list.
- */
- int getNumRowsOnScreen() const noexcept;
-
- //==============================================================================
- /** A set of colour IDs to use to change the colour of various aspects of the label.
-
- These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
- methods.
-
- @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
- */
- enum ColourIds
- {
- backgroundColourId = 0x1002800, /**< The background colour to fill the list with.
- Make this transparent if you don't want the background to be filled. */
- outlineColourId = 0x1002810, /**< An optional colour to use to draw a border around the list.
- Make this transparent to not have an outline. */
- textColourId = 0x1002820 /**< The preferred colour to use for drawing text in the listbox. */
- };
-
- /** Sets the thickness of a border that will be drawn around the box.
-
- To set the colour of the outline, use @code setColour (ListBox::outlineColourId, colourXYZ); @endcode
- @see outlineColourId
- */
- void setOutlineThickness (int outlineThickness);
-
- /** Returns the thickness of outline that will be drawn around the listbox.
-
- @see setOutlineColour
- */
- int getOutlineThickness() const noexcept { return outlineThickness; }
-
- /** Sets a component that the list should use as a header.
-
- This will position the given component at the top of the list, maintaining the
- height of the component passed-in, but rescaling it horizontally to match the
- width of the items in the listbox.
-
- The component will be deleted when setHeaderComponent() is called with a
- different component, or when the listbox is deleted.
- */
- void setHeaderComponent (Component* newHeaderComponent);
-
- /** Changes the width of the rows in the list.
-
- This can be used to make the list's row components wider than the list itself - the
- width of the rows will be either the width of the list or this value, whichever is
- greater, and if the rows become wider than the list, a horizontal scrollbar will
- appear.
-
- The default value for this is 0, which means that the rows will always
- be the same width as the list.
- */
- void setMinimumContentWidth (int newMinimumWidth);
-
- /** Returns the space currently available for the row items, taking into account
- borders, scrollbars, etc.
- */
- int getVisibleContentWidth() const noexcept;
-
- /** Repaints one of the rows.
-
- This does not invoke updateContent(), it just invokes a straightforward repaint
- for the area covered by this row.
- */
- void repaintRow (int rowNumber) noexcept;
-
- /** This fairly obscure method creates an image that just shows the currently
- selected row components.
-
- It's a handy method for doing drag-and-drop, as it can be passed to the
- DragAndDropContainer for use as the drag image.
-
- Note that it will make the row components temporarily invisible, so if you're
- using custom components this could affect them if they're sensitive to that
- sort of thing.
-
- @see Component::createComponentSnapshot
- */
- virtual Image createSnapshotOfSelectedRows (int& x, int& y);
-
- /** Returns the viewport that this ListBox uses.
-
- You may need to use this to change parameters such as whether scrollbars
- are shown, etc.
- */
- Viewport* getViewport() const noexcept;
-
- //==============================================================================
- /** @internal */
- bool keyPressed (const KeyPress&);
- /** @internal */
- bool keyStateChanged (bool isKeyDown);
- /** @internal */
- void paint (Graphics&);
- /** @internal */
- void paintOverChildren (Graphics&);
- /** @internal */
- void resized();
- /** @internal */
- void visibilityChanged();
- /** @internal */
- void mouseWheelMove (const MouseEvent&, const MouseWheelDetails&);
- /** @internal */
- void mouseMove (const MouseEvent&);
- /** @internal */
- void mouseExit (const MouseEvent&);
- /** @internal */
- void mouseUp (const MouseEvent&);
- /** @internal */
- void colourChanged();
- /** @internal */
- void startDragAndDrop (const MouseEvent&, const var& dragDescription, bool allowDraggingToOtherWindows);
-
- private:
- //==============================================================================
- class ListViewport;
- class RowComponent;
- friend class ListViewport;
- friend class TableListBox;
- ListBoxModel* model;
- ScopedPointer<ListViewport> viewport;
- ScopedPointer<Component> headerComponent;
- int totalItems, rowHeight, minimumRowWidth;
- int outlineThickness;
- int lastRowSelected;
- bool mouseMoveSelects, multipleSelection, hasDoneInitialUpdate;
- SparseSet <int> selected;
-
- void selectRowInternal (int rowNumber, bool dontScrollToShowThisRow,
- bool deselectOthersFirst, bool isMouseClick);
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ListBox);
- };
-
-
- #endif // __JUCE_LISTBOX_JUCEHEADER__
|