1 // -*- C++ -*-
2 /* GG is a GUI for OpenGL.
3    Copyright (C) 2003-2008 T. Zachary Laine
4 
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public License
7    as published by the Free Software Foundation; either version 2.1
8    of the License, or (at your option) any later version.
9 
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA
19 
20    If you do not wish to comply with the terms of the LGPL please
21    contact the author as other terms are available for a fee.
22 
23    Zach Laine
24    whatwasthataddress@gmail.com */
25 
26 /** \file ListBox.h \brief Contains the ListBox class, a control that contains
27     rows of other controls, commonly TextControls. */
28 
29 #ifndef _GG_ListBox_h_
30 #define _GG_ListBox_h_
31 
32 
33 #include <GG/AlignmentFlags.h>
34 #include <GG/ClrConstants.h>
35 #include <GG/Control.h>
36 #include <GG/Timer.h>
37 
38 #include <boost/optional/optional.hpp>
39 
40 #include <memory>
41 #include <set>
42 #include <unordered_set>
43 
44 
45 namespace GG {
46 
47 class Font;
48 class Scroll;
49 class SubTexture;
50 class WndEvent;
51 
52 /** Styles for ListBox controls. */
53 GG_FLAG_TYPE(ListBoxStyle);
54 extern GG_API const ListBoxStyle LIST_NONE;           ///< Default style selected.
55 extern GG_API const ListBoxStyle LIST_VCENTER;        ///< Cells are aligned with the top of the list box control.
56 extern GG_API const ListBoxStyle LIST_TOP;            ///< Cells are aligned with the top of the list box control. This is the default.
57 extern GG_API const ListBoxStyle LIST_BOTTOM;         ///< Cells are aligned with the bottom of the list box control.
58 extern GG_API const ListBoxStyle LIST_CENTER;         ///< Cells are center-aligned.
59 extern GG_API const ListBoxStyle LIST_LEFT;           ///< Cells are left-aligned. This is the default.
60 extern GG_API const ListBoxStyle LIST_RIGHT;          ///< Cells are right-aligned.
61 extern GG_API const ListBoxStyle LIST_NOSORT;         ///< List items are not sorted. Items are sorted by default.  When used with drag-and-drop, this style allows arbitrary rearrangement of list elements by dragging.
62 extern GG_API const ListBoxStyle LIST_SORTDESCENDING; ///< Items are sorted based on item text in descending order. Ascending order is the default.
63 extern GG_API const ListBoxStyle LIST_NOSEL;          ///< No selection, dragging, or dropping allowed.  This makes the list box effectively read-only.
64 extern GG_API const ListBoxStyle LIST_SINGLESEL;      ///< Only one item at a time can be selected. By default, multiple items may be selected.
65 extern GG_API const ListBoxStyle LIST_QUICKSEL;       ///< Each click toggles an item without affecting any others; ignored when used with LIST_SINGLESEL.
66 extern GG_API const ListBoxStyle LIST_USERDELETE;     ///< Allows user to remove selected items by pressing the delete key.
67 extern GG_API const ListBoxStyle LIST_BROWSEUPDATES;  ///< Causes a signal to be emitted whenever the mouse moves over ("browses") a row.
68 
69 
70 /** \brief A flexible control that can contain rows and columns of other
71     controls, even other ListBoxes.
72 
73     A ListBox consists of rows of controls, usually text or graphics.  Each
74     row represents one item; rows can be added or removed, but not columns or
75     individual controls (though the individual controls can be removed from a
76     row by accessing it directly).  Each Row in a ListBox must have the same
77     number of cells and the same cell widths as all the others.  If you add a
78     row that has fewer cells than the ListBox you are adding it to, it will be
79     padded with empty cells; likewise, if it has too many cells to fit into
80     the Listbox, it will have cells removed.  ListBoxes are designed to be
81     easy to use in common cases, and useful in uncommon cases with only a
82     little work.  Adding a row to an empty ListBox will cause the ListBox to
83     take on the number of columns that the row has cells, and each column will
84     have an equal portion of the ListBox's width (any remainder is placed in
85     the last column).  This allows you to just add rows to a ListBox without
86     worrying about setting up the ListBox in any way ahead of time.  Use
87     LockColWidths() to prevent empty ListBoxes from taking on a new row's
88     number of columns.  To create a ListBox with user-defined widths, use the
89     ctor designed for that, or call SetNumCols(), set individual widths with
90     SetColWidth(), and lock the column widths with LockColWidths(). To create a
91     ListBox and manually control the column widths and alignment use
92     ManuallyManageColProps() and set the number of columns with SetNumCols().
93     Use DefineColWidths(), DefineColAlignments() and
94     DefineColStretches() to set widths and alignments from an exemplar row.
95 
96     <br>Note that Rows are stored by pointer.  If you want to move a Row from
97     one ListBox to another, use GetRow() and Insert().
98 
99     <br>Note that drag-and-drop support is a key part of ListBox's
100     functionality.  As such, special effort has been made to make its use as
101     natural and flexible as possible.  This includes allowing arbitrary
102     reordering of ListBox rows when the LIST_NOSORT is in effect.*/
103 class GG_API ListBox : public Control
104 {
105 public:
106     /** \brief A single item in a listbox.
107 
108         A Row is primarily a container for Controls.  Each cell in a Row
109         contains pointer to a Control-derived object.  As always, each such
110         Control can be connected to arbitrary functionality using signals and
111         slots.  During dragging and dropping, the data type associated with a
112         Row (DragDropDataType()) indicates to potential drop targets what type
113         of data the Row represents; the target may accept or decline the drop
114         based on the data type.  Rows are stored in ListBoxes by reference,
115         not value; this means that you can subclass from Row to create your
116         own custom Row types.  This is the recommended method for associating
117         a row with the non-GUI object that it represents.  Note that all
118         subclasses of Row must declare a SortKeyType, if it differs from
119         std::string, and must provide a SortKey() method if it should differ
120         from the default SortKey() that Row provides.  Note that SortKey is
121         not virtual; this part of its interface is used for compile-time
122         polymorphism -- whatever sorter is used with a Row subclass must know
123         the most-derived type of the Row subclass.  \note The margin, column
124         alignment, and width cell data are included so that each Row has all
125         the necessary information with which to render itself (this is
126         primarily needed to facilitate drag-and-drop); these data are
127         duplicates of the margin, alignment, and column widths data found in
128         the owning ListBox, and may be overwritten by the ListBox at any
129         time. */
130     struct GG_API Row : public Control
131     {
132         /** the type of key used to sort rows */
133         typedef std::string SortKeyType;
134 
135         /** \name Structors */ ///@{
136         Row();
137         Row(X w, Y h);
138         virtual ~Row();
139         //@}
140 
141         void CompleteConstruction() override;
142 
143         /** \name Accessors */ ///@{
144         /** Returns the string by which this row may be sorted. */
145         virtual SortKeyType SortKey(std::size_t column) const;
146         std::size_t         size() const;                       ///< returns the number of Controls in this Row
147         bool                empty() const;                      ///< returns true iff there are 0 Controls in this Row
148 
149         /** Returns the Control in the \a nth cell of this Row
150             \throw std::range_error throws when size() <= \a n */
151         virtual Control* at(std::size_t n) const;
152 
153         Alignment    RowAlignment() const;              ///< returns the vertical alignment of this Row
154         Alignment    ColAlignment(std::size_t n) const; ///< returns the horizontal alignment of the Control in the \a nth cell of this Row; not range checked
155         X            ColWidth(std::size_t n) const;     ///< returns the width of the \a nth cell of this Row; not range checked
156         unsigned int Margin() const;                    ///< returns the amount of space left between the contents of adjacent cells, in pixels
157         /** Return true if the row is normalized.  Used by ListBox to track normalization.*/
158         bool         IsNormalized() const;
159         //@}
160 
161         /** \name Mutators */ ///@{
162         void         Render() override;
163 
164         void         push_back(std::shared_ptr<Control> c); ///< adds a given Control to the end of the Row; this Control becomes property of the Row
165         void         clear(); ///< removes and deletes all cells in this Row
166         void         resize(std::size_t n); ///< resizes the Row to have \a n cells
167 
168         void         SetCell(std::size_t n, const std::shared_ptr<Control>& c); ///< sets the Control in the \a nth cell of this Row, deleting any preexisting Control; not range checked
169         Control*     RemoveCell(std::size_t n); ///< returns a pointer to the Control in the \a nth cell of this Row, and sets the contents of the cell to 0; not range checked
170         void         SetRowAlignment(Alignment align); ///< sets the vertical alignment of this Row
171         void         SetColAlignment(std::size_t n, Alignment align); ///< sets the horizontal alignment of the Control in the \a nth cell of this Row; not range checked
172         void         SetColWidth(std::size_t n, X width); ///< sets the width of the \a nth cell of this Row; not range checked
173         void         SetColAlignments(const std::vector<Alignment>& aligns); ///< sets the horizontal alignment of all the Controls in this Row; not range checked
174         void         ClearColAlignments(); ///< Clear the horizontal alignments of the cells in this Row
175         void         SetColWidths(const std::vector<X>& widths); ///< sets all the widths of the cells of this Row; not range checked
176         void         ClearColWidths(); ///< Clear the minimum widths of the cells of this Row.
177         void         SetColStretches(const std::vector<double>& stretches); ///< Set all column stretches.
178         void         SetMargin(unsigned int margin); ///< sets the amount of space left between the contents of adjacent cells, in pixels
179         /** Set normalized.  Used by ListBox to track normalization.*/
180         void         SetNormalized(bool normalized);
181         //@}
182 
183         boost::signals2::signal<void(const Pt&, GG::Flags<GG::ModKey>)> RightClickedSignal;
184 
185     protected:
186         /** Add elements to m_col_widths, m_col_stretches and m_col_alignments until they reach size nn. */
187         void GrowWidthsStretchesAlignmentsTo(std::size_t nn);
188         void RClick(const Pt& pt, GG::Flags<GG::ModKey> mod) override;
189 
190         std::vector<std::shared_ptr<Control>>   m_cells;                    ///< the Controls in this Row (each may be null)
191         Alignment                               m_row_alignment;            ///< row alignment; one of ALIGN_TOP, ALIGN_VCENTER, or ALIGN_BOTTOM
192         std::vector<Alignment>                  m_col_alignments;           ///< column alignments; each is one of ALIGN_TOP, ALIGN_VCENTER, or ALIGN_BOTTOM
193         std::vector<X>                          m_col_widths;               ///< column widths
194         std::vector<double>                     m_col_stretches;            ///< the stretch factor of each column
195         unsigned int                            m_margin = DEFAULT_MARGIN;  ///< the amount of space left between the contents of adjacent cells, in pixels
196         bool                                    m_ignore_adjust_layout = false;
197         bool                                    m_is_normalized = false;
198     };
199 
200     typedef std::list<std::shared_ptr<Row>>::iterator iterator;
201     typedef std::list<std::shared_ptr<Row>>::const_iterator const_iterator;
202 
203     /** \brief Sorts iterators to ListBox::Row*s from a container of
204         ListBox::Row*s.
205 
206         For instance for use in a std::map<> or std::set<> (eg,
207         ListBox::SelectionSet).  The iterators must refer to pointers to
208         ListBox::Rows that are laid out vertically (as in a ListBox).  This
209         layout is used to define a y-ordering that is used to sort the
210         iterators. */
211     struct GG_API RowPtrIteratorLess
212     {
213         bool operator()(const iterator& lhs, const iterator& rhs) const;
214     };
215 
216     struct IteratorHash
217     {
operatorIteratorHash218         std::size_t operator()(const iterator& it) const
219         { return boost::hash<const std::shared_ptr<Row>>()(*it); }
220     };
221 
222     typedef std::unordered_set<iterator, IteratorHash> SelectionSet;
223 
224     /** \name Signal Types */ ///@{
225     /** emitted when the list box is cleared */
226     typedef boost::signals2::signal<void ()>                                                ClearedRowsSignalType;
227     /** emitted when one or more rows are selected or deselected */
228     typedef boost::signals2::signal<void (const SelectionSet&)>                             SelRowsChangedSignalType;
229     /** the signature of row-change-notification signals */
230     typedef boost::signals2::signal<void (iterator)>                                        RowSignalType;
231     /** the signature of const row-change-notification signals */
232     typedef boost::signals2::signal<void (const_iterator)>                                  ConstRowSignalType;
233     /** the signature of row-click-notification signals */
234     typedef boost::signals2::signal<void(iterator, const Pt&,const GG::Flags<GG::ModKey>&)> RowClickSignalType;
235     /** the signature of row-move-notification signals */
236     typedef boost::signals2::signal<void (iterator, iterator)>                              MovedRowSignalType;
237 
238     typedef RowSignalType      BeforeInsertRowSignalType;   ///< emitted before a row is inserted into the list box
239     typedef RowSignalType      AfterInsertRowSignalType;    ///< emitted after a row is inserted into the list box
240     typedef RowSignalType      DroppedRowSignalType;        ///< emitted when a row is inserted into the list box via drag-and-drop
241     typedef ConstRowSignalType DropRowAcceptableSignalType; ///< emitted when a row may be inserted into the list box via drag-and-drop
242     typedef RowSignalType      BeforeEraseRowSignalType;    ///< emitted when a row in the listbox is erased; provides the deleted Row, and is emitted before the row is removed
243     typedef RowSignalType      AfterEraseRowSignalType;     ///< emitted when a row in the listbox is erased; provides the deleted Row, and is emitted after the row is removed
244     typedef RowSignalType      BrowsedRowSignalType;        ///< emitted when a row in the listbox is "browsed" (rolled over) by the cursor; provides the browsed row
245     //@}
246 
247     /** \name Constants */ ///@{
248     static const int DEFAULT_MARGIN;
249     static const X DEFAULT_ROW_WIDTH;
250     static const Y DEFAULT_ROW_HEIGHT;
251     static const unsigned int BORDER_THICK; ///< the thickness with which to render the border of the control
252     //@}
253 
254     /** \name Structors */ ///@{
255     ListBox(Clr color, Clr interior = CLR_ZERO);
256 
257     virtual ~ListBox();
258     //@}
259 
260     void CompleteConstruction() override;
261 
262     /** \name Accessors */ ///@{
263     Pt MinUsableSize() const override;
264     Pt ClientUpperLeft() const override;
265     Pt ClientLowerRight() const override;
266 
267     bool                Empty() const;          ///< returns true when the ListBox is empty
268     const_iterator      begin() const;          ///< returns an iterator to the first list row
269     const_iterator      end() const;            ///< returns an iterator to the imaginary row one past the last
270     const Row&          GetRow(std::size_t n) const; ///< returns a const reference to the row at index \a n; not range-checked.  \note This function is O(n).
271     iterator            Caret() const;          ///< returns the row that has the caret
272     const SelectionSet& Selections() const;     ///< returns a const reference to the set row indexes that is currently selected
273     bool                Selected(iterator it) const; ///< returns true if row \a it is selected
274     Clr                 InteriorColor() const;  ///< returns the color painted into the client area of the control
275     Clr                 HiliteColor() const;    ///< returns the color behind selected line items
276 
277     /** Returns the style flags of the listbox \see GG::ListBoxStyle */
278     Flags<ListBoxStyle> Style() const;
279 
280     const Row&      ColHeaders() const;     ///< returns the row containing the headings for the columns, if any.  If undefined, the returned heading Row will have size() 0.
281     iterator        FirstRowShown() const;  ///< returns the first row visible in the listbox
282     std::size_t     FirstColShown() const;  ///< returns the index of the first column visible in the listbox
283 
284     iterator        LastVisibleRow() const; ///< returns the last row that could be drawn, taking into account the contents and the size of client area
285     std::size_t     LastVisibleCol() const; ///< returns the index of the last column that could be drawn, taking into account the contents and the size of client area
286 
287     std::size_t     NumRows() const;        ///< returns the total number of rows in the ListBox
288     std::size_t     NumCols() const;        ///< returns the total number of columns in the ListBox
289 
290     /** Returns true iff column widths are fixed \see LockColWidths() */
291     bool            KeepColWidths() const;
292 
293     /** Return true if column width and alignment are not managed by ListBox. */
294     bool            ManuallyManagingColProps() const;
295 
296     /** Returns the index of the column used to sort rows, when sorting is
297         enabled.  \note The sort column is not range checked when it is set by
298         the user; it may be < 0 or >= NumCols(). */
299     std::size_t     SortCol() const;
300 
301     X               ColWidth(std::size_t n) const;     ///< returns the width of column \a n in pixels; not range-checked
302     Alignment       ColAlignment(std::size_t n) const; ///< returns the alignment of column \a n; must be ALIGN_LEFT, ALIGN_CENTER, or ALIGN_RIGHT; not range-checked
303     double          ColStretch(std::size_t n) const;   ///< Return the stretch factor of column \a n.
304     Alignment       RowAlignment(iterator it) const;   ///< returns the alignment of row \a it; must be ALIGN_TOP, ALIGN_VCENTER, or ALIGN_BOTTOM; not range-checked
305 
306     /** Returns true iff \p type is allowed to be dropped over this ListBox
307         when drag-and-drop is enabled. */
308     bool            AllowedDropType(const std::string& type) const;
309 
310     /** Whether the list should autoscroll when the user is attempting to drop
311         an item into a location that is not currently visible. */
312     bool            AutoScrollDuringDragDrops() const;
313 
314     /** The thickness of the area around the border of the client area that will
315         provoke an auto-scroll, if AutoScrollDuringDragDrops() returns true. */
316     unsigned int    AutoScrollMargin() const;
317 
318     /** The number of milliseconds that elapse between row/column scrolls when
319         auto-scrolling. */
320     unsigned int    AutoScrollInterval() const;
321 
322     /** Return true if drops are allowed.*/
323     bool            AllowingDrops();
324 
325 
326     mutable ClearedRowsSignalType        ClearedRowsSignal;        ///< the cleared signal object for this ListBox
327     mutable BeforeInsertRowSignalType    BeforeInsertRowSignal;    ///< the before insert signal object for this ListBox
328     mutable AfterInsertRowSignalType     AfterInsertRowSignal;     ///< the after insert signal object for this ListBox
329     mutable SelRowsChangedSignalType     SelRowsChangedSignal;     ///< the selection change signal object for this ListBox
330     mutable DroppedRowSignalType         DroppedRowSignal;         ///< the dropped signal object for this ListBox
331     mutable DropRowAcceptableSignalType  DropRowAcceptableSignal;  ///< the drop-acceptability signal object for this ListBox
332     mutable MovedRowSignalType           MovedRowSignal;           ///< the moved signal object for this ListBox
333     mutable RowClickSignalType           LeftClickedRowSignal;     ///< the left click signal object for this ListBox
334     mutable RowClickSignalType           RightClickedRowSignal;    ///< the right click signal object for this ListBox
335     mutable RowClickSignalType           DoubleClickedRowSignal;   ///< the double click signal object for this ListBox
336     mutable BeforeEraseRowSignalType     BeforeEraseRowSignal;     ///< the before erase signal object for this ListBox
337     mutable AfterEraseRowSignalType      AfterEraseRowSignal;      ///< the after erase signal object for this ListBox
338     mutable BrowsedRowSignalType         BrowsedRowSignal;         ///< the browsed signal object for this ListBox
339     //@}
340 
341     /** \name Mutators */ ///@{
342     void StartingChildDragDrop(const Wnd* wnd, const GG::Pt& offset) override;
343     void AcceptDrops(const Pt& pt, std::vector<std::shared_ptr<Wnd>> wnds, Flags<ModKey> mod_keys) override;
344     void ChildrenDraggedAway(const std::vector<Wnd*>& wnds, const Wnd* destination) override;
345     void PreRender() override;
346     void Render() override;
347 
348     /** Resizes the control, then resizes the scrollbars as needed. */
349     void SizeMove(const Pt& ul, const Pt& lr) override;
350 
351     /** Show the  list box.  If \p show_children is true then show the rows that are within the
352         boundaries of the list box.*/
353     void Show() override;
354 
355     void Disable(bool b = true) override;
356     void SetColor(Clr c) override;
357 
358     /** Insertion sorts \a row into the ListBox if sorted, or inserts into an
359         unsorted ListBox before \a it; returns insertion point.  This Row
360         becomes the property of this ListBox. */
361     iterator Insert(std::shared_ptr<Row> row, iterator it);
362 
363     /** Insertion sorts \a row into the ListBox if sorted, or inserts into an
364         unsorted ListBox at the end of the list; returns insertion point.
365         This Row becomes the property of this ListBox. */
366     iterator Insert(std::shared_ptr<Row> row);
367 
368     /** Insertion sorts \a rows into the ListBox if sorted, or inserts into an
369         unsorted ListBox before \a it. The Rows become the property of this
370         ListBox. */
371     void Insert(const std::vector<std::shared_ptr<Row>>& rows, iterator it);
372 
373     /** Insertion sorts \a rows into the ListBox if sorted, or inserts into an
374         unsorted ListBox at the end of the list. The Rows become the property
375         of this ListBox. */
376     void Insert(const std::vector<std::shared_ptr<Row>>& rows);
377 
378     std::shared_ptr<Row>Erase(iterator it, bool signal = false);        ///< removes and returns the row that \a it points to from the ListBox, or 0 if no such row exists
379 
380     void Clear();                                        ///< empties the ListBox
381     void SelectRow(iterator it, bool signal = false);    ///< selects row \a it
382     void DeselectRow(iterator it, bool signal = false);  ///< deselects row \a it
383     void SelectAll(bool signal = false);                 ///< selects all rows
384     void DeselectAll(bool signal = false);               ///< deselects all rows
385     void SetSelections(const SelectionSet& s, bool signal = false);  ///< sets the set of selected rows to \a s
386 
387     iterator    begin();                                ///< returns an iterator to the first list row
388     iterator    end();                                  ///< returns an iterator to the imaginary row one past the last one
389 
390     Row& GetRow(std::size_t n);                         ///< returns a reference to the Row at row index \a n; not range-checked.  \note This function is O(n).
391 
392     void SetCaret(iterator it);                         ///< sets the position of the caret to \a it
393     void BringRowIntoView(iterator it);                 ///< moves the scrollbars so that row \a it is visible
394     void SetFirstRowShown(iterator it);                 ///< moves the scrollbars so that row \a it is the first visible
395 
396     /** Sets how much to scroll when scrolled using the mousewheel. */
397     void SetVScrollWheelIncrement(unsigned int increment);
398     void SetHScrollWheelIncrement(unsigned int increment);
399 
400     void SetInteriorColor(Clr c);                       ///< sets the color painted into the client area of the control
401     void SetHiliteColor(Clr c);                         ///< sets the color behind selected line items
402 
403     /** sets the style flags for the ListBox to \a s. \see GG::ListBoxStyle */
404     void SetStyle(Flags<ListBoxStyle> s);
405 
406     void SetColHeaders(const std::shared_ptr<Row>& r);  ///< sets the row used as headings for the columns; this Row becomes property of the ListBox.
407     void RemoveColHeaders();                            ///< removes any columns headings set
408 
409     void SetColWidth(std::size_t n, X w);               ///< sets the width of column \n to \a w; not range-checked
410     void SetNumCols(std::size_t n);                     ///< sets the number of columns in the ListBox to \a n; if no column widths exist before this call, proportional widths are calulated and set, otherwise no column widths are set
411     void SetSortCol(std::size_t n);                     ///< sets the index of the column used to sort rows when sorting is enabled; not range-checked
412 
413     /** Sets the comparison function used to sort a given pair of Rows during
414         row sorting.  Note that \a sort_cmp is assumed to produce an ascending
415         order when used to sort; setting the LIST_SORTDESCENDING style can be
416         used to produce a reverse sort. */
417     void SetSortCmp(const std::function<bool (const Row&, const Row&, std::size_t)>& sort_cmp);
418 
419     /** Fixes the column widths; by default, an empty ListBox will take on the
420         number of columns of its first added row. \note The number of columns
421         and their widths may still be set via SetNumCols() and SetColWidth()
422         after this function has been called. */
423     void LockColWidths();
424 
425     /** Allows the number of columns to be determined by the first row added to
426         an empty ListBox */
427     void UnLockColWidths();
428 
429     /** Set ListBox to stop managing column widths and alignment.  The number of columns must be
430         set with SetColWidth(), but widths of individual rows columns or the header will not be
431         managed by ListBox. */
432     void ManuallyManageColProps();
433 
434     /** Sets the alignment of column \a n to \a align; not range-checked */
435     void SetColAlignment(std::size_t n, Alignment align);
436 
437     /** Sets the stretch of column \a n to \a stretch; not range-checked */
438     void SetColStretch(std::size_t n, double stretch);
439 
440     /** Sets the alignment of row \a it to \a align; not range-checked */
441     void SetRowAlignment(iterator it, Alignment align);
442 
443     /** Sets whether to normalize rows when inserted (true) or leave them as
444       * they are. */
445     void NormalizeRowsOnInsert(bool enable = true);
446 
447     /** Sets the column widths from an exemplar \p row.*/
448     void DefineColWidths(const Row& row);
449 
450     /** Sets the column alignments from an exemplar \p row.*/
451     void DefineColAlignments(const Row& row);
452 
453     /** Sets the column alignments from an exemplar \p row.*/
454     void DefineColStretches(const Row& row);
455 
456     /** Sets whether to add padding at the end of the scrolls when the ListBox is
457      *  bigger than the client area, so that any row can be scrolled all the way to
458      *  the top (true), or only use as much space as it needs. */
459     void AddPaddingAtEnd(bool enable = true);
460 
461     /** Allow drops if \p allow is true.*/
462     void AllowDrops(bool allow);
463 
464     /** Allow all drop types if \p allow is true.*/
465     void AllowAllDropTypes(bool allow);
466 
467     /** Allows Rows with data type \a str to be dropped over this ListBox when
468         drag-and-drop is enabled. \note Passing "" enables all drop types. */
469     void AllowDropType(const std::string& str);
470 
471     /** Set this to determine whether the list should autoscroll when the user
472         is attempting to drop an item into a location that is not currently
473         visible. */
474     void AutoScrollDuringDragDrops(bool auto_scroll);
475 
476     /** Sets the thickness of the area around the border of the client area that
477         will provoke an auto-scroll, if AutoScrollDuringDragDrops() returns
478         true. */
479     void SetAutoScrollMargin(unsigned int margin);
480 
481     /** Sets the number of milliseconds that elapse between row/column scrolls
482         when auto-scrolling. */
483     void SetAutoScrollInterval(unsigned int interval);
484     //@}
485 
486     /** \brief Sorts two Rows of a ListBox using operator<() on the
487         Row::SortKeyType provided by the rows' SortKey() methods.
488 
489         If you want to use operator<() with a Row subclass DerivedRow that has
490         a custom SortKeyType, use DefaultRowCmp<DerivedRow>. */
491     template <typename RowType>
492     struct DefaultRowCmp
493     {
494         /** Returns true iff lhs.SortKey( \a column ) < rhs.SortKey( \a column ). */
495         bool operator()(const Row& lhs, const Row& rhs, std::size_t column) const;
496     };
497 
498     /** \name Exceptions */ ///@{
499     //@}
500 
501 protected:
502     /** \name Accessors */ ///@{
503     X               RightMargin() const;     ///< space skipped at right of client area for vertical scroll bar
504     Y               BottomMargin() const;    ///< space skipped at bottom of client area for horizontal scroll bar
505     unsigned int    CellMargin() const;      ///< the number of pixels left between the contents of each cell and the cell boundary
506 
507     iterator        RowUnderPt(const Pt& pt) const; ///< returns row under pt, if any; value must be checked (i.e. it may be end())
508 
509     iterator        OldSelRow() const;   ///< returns the last row that was selected with a left-button mouse-down
510     iterator        OldRDownRow() const; ///< returns the last row that was selected with a right-button mouse-down
511     iterator        LClickRow() const;   ///< returns the last row that was left-clicked
512     iterator        RClickRow() const;   ///< returns the last row that was right-clicked
513 
514     bool            AutoScrollingUp() const;    ///< returns true iff the list is being autoscrolled up due to drag-and-drop
515     bool            AutoScrollingDown() const;  ///< returns true iff the list is being autoscrolled down due to drag-and-drop
516     bool            AutoScrollingLeft() const;  ///< returns true iff the list is being autoscrolled left due to drag-and-drop
517     bool            AutoScrollingRight() const; ///< returns true iff the list is being autoscrolled right due to drag-and-drop
518     //@}
519 
520     /** \name Mutators */ ///@{
521     void KeyPress(Key key, std::uint32_t key_code_point, Flags<ModKey> mod_keys) override;
522     void MouseWheel(const Pt& pt, int move, Flags<ModKey> mod_keys) override;
523     void DragDropEnter(const Pt& pt, std::map<const Wnd*, bool>& drop_wnds_acceptable, Flags<ModKey> mod_keys) override;
524     void DragDropHere(const Pt& pt, std::map<const Wnd*, bool>& drop_wnds_acceptable, Flags<ModKey> mod_keys) override;
525     void DragDropLeave() override;
526     void CancellingChildDragDrop(const std::vector<const Wnd*>& wnds) override;
527     void TimerFiring(unsigned int ticks, Timer* timer) override;
528 
529     bool EventFilter(Wnd* w, const WndEvent& event) override;
530 
531     /** Define the number of columns, the column widths and alignment from \p row.*/
532     /** Insertion sorts into list, or inserts into an unsorted list before
533         \a it; returns insertion point. */
534     iterator Insert(std::shared_ptr<Row> row, iterator it, bool dropped);
535 
536     /** Insertion sorts into list, or inserts into an unsorted list before
537         \a it; returns insertion point. */
538     void Insert(const std::vector<std::shared_ptr<Row>>& rows, iterator it, bool dropped);
539 
540     std::shared_ptr<Row> Erase(iterator it, bool removing_duplicate, bool signal); ///< erases the row at index \a idx, handling it as a duplicate removal (such as for drag-and-drops within a single ListBox) if indicated
541     void BringCaretIntoView();  ///< makes sure caret is visible when scrolling occurs due to keystrokes etc.
542     void ResetAutoScrollVars(); ///< resets all variables related to auto-scroll to their initial values
543     void Resort();              ///< performs a full resort of all rows, using the current sort functor.
544     Row& ColHeaders();          ///< returns the row containing the headings for the columns, if any.  If undefined, the returned heading Row will have size() 0. non-const for derivers
545     //@}
546 
547     /** creates, destroys, or resizes scrolls to reflect size of data in listbox. \p force_scroll
548         forces the scroll bar to be added.*/
549     void AdjustScrolls(bool adjust_for_resize,
550                        const std::pair<bool, bool>& force_scrolls = {false, false});
551 
552     void DropsAcceptable(DropsAcceptableIter first, DropsAcceptableIter last,
553                          const Pt& pt, Flags<ModKey> mod_keys) const override;
554     void HandleRowRightClicked(const Pt& pt, Flags<ModKey> mod);
555 
556 private:
557     /** Show only rows that are within the visible list box area and hide all others.  If
558         \p do_prerender is true then prerender the visible rows.  Return true if prerender
559         resulted in any visible row changing size. */
560     bool        ShowVisibleRows(bool do_prerender);
561     void        ConnectSignals();
562     void        ValidateStyle();                                        ///< reconciles inconsistencies in the style flags
563     void        VScrolled(int tab_low, int tab_high, int low, int high);///< signals from the vertical scroll bar are caught here
564     void        HScrolled(int tab_low, int tab_high, int low, int high);///< signals from the horizontal scroll bar are caught here
565     void        ClickAtRow(iterator it, Flags<ModKey> mod_keys);        ///< handles to a mouse-click or spacebar-click on \a it, modified by \a keys
566     void        NormalizeRow(Row* row);                                 ///< adjusts a Row so that it has the same number of cells as other rows, and that each cell has the correct width and alignment
567     iterator    FirstRowShownWhenBottomIs(iterator bottom_row);         ///< Returns the first row shown when the last row shown is \a bottom_row
568     std::size_t FirstColShownWhenRightIs(std::size_t right_col, X client_width); ///< Returns the index of the first column shown when the last column shown is \a right_col
569 
570     struct SelectionCache;
571     /** Cache the selected, clicked and last browsed rows.*/
572     std::shared_ptr<SelectionCache> CacheSelections();
573     /** Restore cached selected, clicked and last browsed rows.*/
574     void RestoreCachedSelections(const SelectionCache& cache);
575 
576     /** Return the client size excluding the scroll bar sizes, in order to determine if scroll bars
577         are needed. This is a private function that is a component of AdjustScrolls.*/
578     Pt ClientSizeExcludingScrolls() const;
579 
580     /** Return a pair of optional X and/or Y dimensions of the scollable area iff vscroll and/or
581         hscroll are required. If scrollbars are needed, the scrollable extent will be larger than the
582         client size.  If a scrollbar is not required in some dimension return boost::none
583         for that dimension. \p maybe_client_size might contain a precalculated client size.
584 
585         This is a private function that is a component of AdjustScrolls. */
586     std::pair<boost::optional<X>, boost::optional<Y>>
587         CheckIfScrollsRequired(const std::pair<bool, bool>& force_scrolls = {false, false},
588                                const boost::optional<Pt>& maybe_client_size = boost::none) const;
589 
590     /** Add vscroll and/or hscroll if \p required_total_extents the x andor y dimension exists. The
591         value of \p required_total_extents is the full x and y dimensions of the underlying ListBox
592         requiring the scrollbar as calculated in CheckIfScrollsRequired.  Return a pair of bools
593         indicating if vscroll and/or hscroll was added and/or removed.  \p maybe_client_size might
594         contain a precalculated client size as calculated in ClientSizeExcludingScrolls.
595 
596         This is a private function that is a component of AdjustScrolls. */
597     std::pair<bool, bool> AddOrRemoveScrolls(const std::pair<boost::optional<X>, boost::optional<Y>>& required_total_extents,
598                                              const boost::optional<Pt>& maybe_client_size = boost::none);
599 
600     /// m_rows is mutable to enable returning end() from const functions in constant time.
601     mutable std::list<std::shared_ptr<Row>> m_rows;             ///< line item data
602 
603     std::shared_ptr<Scroll> m_vscroll;          ///< vertical scroll bar on right
604     std::shared_ptr<Scroll> m_hscroll;          ///< horizontal scroll bar at bottom
605     unsigned int            m_vscroll_wheel_scroll_increment = 0;
606     unsigned int            m_hscroll_wheel_scroll_increment = 0;
607 
608     iterator        m_caret;            ///< the item currently selected, or the last item selected by the user
609     SelectionSet    m_selections;       ///< vector of indexes of selected items
610     iterator        m_old_sel_row;      ///< used to make sure clicks end on the same row where they started
611     bool            m_old_sel_row_selected = false; ///< set to true if m_old_sel_row was selected at the point at which it was designated
612     iterator        m_old_rdown_row;    ///< the row that most recently recieved a right button down message
613     iterator        m_lclick_row;       ///< the row most recently left-clicked
614     iterator        m_rclick_row;       ///< the row most recently right-clicked
615     iterator        m_last_row_browsed; ///< the last row sent out as having been browsed (used to prevent duplicate browse signals)
616 
617     GG::Pt          m_first_row_offset = {X(BORDER_THICK), Y(BORDER_THICK)};///< scrolled offset of the first row.
618     iterator        m_first_row_shown;      ///< index of row at top of visible area (always begin() for non-empty ListBox with LIST_NOSCROLL set)
619     std::size_t     m_first_col_shown = 0;  ///< like above, but index of column at left
620     std::size_t     m_num_cols = 1;         ///< the number of columns
621     std::vector<X>  m_col_widths;       ///< the width of each of the columns goes here
622 
623     std::vector<Alignment>  m_col_alignments;   ///< the horizontal alignment of each of the columns goes here
624     std::vector<double>     m_col_stretches;    ///< the stretch factor of each column
625     unsigned int            m_cell_margin;      ///< the amount of space left between each edge of the cell and its contents, in pixels
626 
627     Clr                     m_int_color;                ///< color painted into the client area of the control
628     Clr                     m_hilite_color = CLR_SHADOW;///< color behind selected line items
629     Flags<ListBoxStyle>     m_style = LIST_NONE;        ///< composed of ListBoxStyles enums (see GUIBase.h)
630 
631     std::shared_ptr<Row>    m_header_row;               ///< row of header text/graphics
632     bool                    m_keep_col_widths = false;  ///< should we keep the column widths, once set?
633     bool                    m_clip_cells = false;       ///< if true, the contents of each cell will be clipped to the visible area of that cell (TODO: currently unused)
634     std::size_t             m_sort_col = 0;             ///< the index of the column data used to sort the list
635 
636     std::function<bool (const Row&, const Row&, std::size_t)>
637                             m_sort_cmp;                 ///< the predicate used to sort the values in the m_sort_col column of two rows
638 
639     bool                    m_allow_drops = false;      ///< are we accepting drops
640 
641     /** Set to boost::none to allow all types.  Otherwise each string is an
642         allowed type.*/
643     boost::optional<std::unordered_set<std::string>>
644                             m_allowed_drop_types = boost::none;
645 
646     bool            m_auto_scroll_during_drag_drops = true;
647     unsigned int    m_auto_scroll_margin = 8;
648     bool            m_auto_scrolling_up = false;
649     bool            m_auto_scrolling_down = false;
650     bool            m_auto_scrolling_left = false;
651     bool            m_auto_scrolling_right = false;
652     Timer           m_auto_scroll_timer{250};
653 
654     bool            m_normalize_rows_on_insert = true;
655     bool            m_manage_column_props = true;
656     bool            m_add_padding_at_end = true;
657 
658     friend class DropDownList; ///< allow complete access to DropDownList, which relies on ListBox to do its rendering
659 };
660 
661 } // namespace GG
662 
663 
664 // template implementations
665 template <typename RowType>
operator()666 bool GG::ListBox::DefaultRowCmp<RowType>::operator()(const GG::ListBox::Row& lhs, const GG::ListBox::Row& rhs, std::size_t column) const
667 {
668     return static_cast<const RowType&>(lhs).SortKey(column) < static_cast<const RowType&>(rhs).SortKey(column);
669 }
670 
671 #endif
672