// -*- C++ -*-
/* GG is a GUI for SDL and OpenGL.
   Copyright (C) 2003-2008 T. Zachary Laine

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public License
   as published by the Free Software Foundation; either version 2.1
   of the License, or (at your option) any later version.
   
   This library 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
   Lesser General Public License for more details.
    
   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA

   If you do not wish to comply with the terms of the LGPL please
   contact the author as other terms are available for a fee.
    
   Zach Laine
   whatwasthataddress@gmail.com */

/** \file DropDownList.h \brief Contains the DropDownList class, a control
    that displays a current selection, and allows the user to select one of
    several options from a list that drops down when the control is
    clicked. */

#ifndef _GG_DropDownList_h_
#define _GG_DropDownList_h_

#include <GG/ListBox.h>


namespace GG {

/** \brief Displays a single choice, and allows the user to select items from
    a drop-down list.

    DropDownList is similar to GG::ListBox, but has significant restrictions
    over the functionality of GG::ListBox.  Specifically, all list items must
    have the same height, and there is no dragging or dropping.  Though any
    Control-derived object may be placed in an item cell, the items are only
    interactive in the drop-down list; the currently selected item is
    displayed only.  Most of the ListBox interface is duplicated in
    DropDownList.  Though you can still set the alignment, etc. of individual
    rows, as in ListBox, the currently-selected row will have the same
    alignment, etc. when displayed in the control in its unopened state.  Note
    that this may look quite ugly. */
class GG_API DropDownList : public Control
{
public:
    /** This is a single item in a dropdown list. \see See GG::ListBox for details.*/
    typedef ListBox::Row Row;

    typedef ListBox::iterator iterator;
    typedef ListBox::const_iterator const_iterator;
    typedef ListBox::reverse_iterator reverse_iterator;
    typedef ListBox::const_reverse_iterator const_reverse_iterator;

    /** \name Signal Types */ ///@{
    typedef boost::signal<void (iterator)>   SelChangedSignalType; ///< emitted when a new item is selected; will be end() when no item is selected
    //@}

    /** \name Structors */ ///@{
    /** basic ctor.  DropDownList retains ownership of \a lb, if it is non-null. */
    DropDownList(X x, Y y, X w, Y h, Y drop_ht, Clr color, Flags<WndFlag> flags = INTERACTIVE);

    ~DropDownList(); ///< dtor
    //@}

    /** \name Accessors */ ///@{
    iterator       CurrentItem() const;      ///< returns the currently selected list item (returns end() if none is selected)
    std::size_t    CurrentItemIndex() const; ///< returns the position of the currently selected list item within the list (returns -1 if none is selected)

    std::size_t    IteratorToIndex(iterator it) const; ///< returns the position of \a it within the list (returns -1 if \a it == end())
    iterator       IndexToIterator(std::size_t n) const; ///< returns an iterator to the row in position \a n (returns end() if \a n is an invalid index)

    bool           Empty() const;            ///< returns true when the list is empty
    const_iterator begin() const;            ///< returns an iterator to the first list row
    const_iterator end() const;              ///< returns an iterator to the imaginary row one past the last
    const_reverse_iterator
                   rbegin() const;           ///< returns an iterator to the last list row
    const_reverse_iterator
                   rend() const;             ///< returns an iterator to the imaginary row one past the first
    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).
    bool           Selected(iterator it) const; ///< returns true if row \a it is selected
    bool           Selected(std::size_t n) const; ///< returns true if row at position \a n is selected
    Clr            InteriorColor() const;    ///< returns the color painted into the client area of the control

    Y         DropHeight() const; ///< returns the height of the drop-down list

    /** Returns the style flags of the list \see GG::ListBoxStyle */
    Flags<ListBoxStyle> Style() const;

    std::size_t    NumRows() const;          ///< returns the total number of items in the list
    std::size_t    NumCols() const;          ///< returns the total number of columns in each list item

    /** Returns the index of the column used to sort items, when sorting is
        enabled.  \note The sort column is not range checked when it is set by
        the user; it may be >= NumCols(). */
    std::size_t    SortCol() const;

    X              ColWidth(std::size_t n) const;     ///< returns the width of column \a n in pixels; not range-checked
    Alignment      ColAlignment(std::size_t n) const; ///< returns the alignment of column \a n; must be LIST_LEFT, LIST_CENTER, or LIST_RIGHT; not range-checked
    Alignment      RowAlignment(iterator it) const;   ///< returns the alignment of row \a n; must be LIST_TOP, LIST_VCENTER, or LIST_BOTTOM; not range-checked

    virtual Pt     ClientUpperLeft() const;
    virtual Pt     ClientLowerRight() const;

    mutable SelChangedSignalType SelChangedSignal; ///< the selection change signal object for this DropDownList
    //@}

    /** \name Mutators */ ///@{
    virtual void   Render();

    virtual void   SizeMove(const Pt& ul, const Pt& lr); ///< resizes the control, ensuring the proper height is maintained based on the list's row height

    virtual void   SetColor(Clr c);

    /** Insertion sorts \a row into a sorted list, or inserts into an unsorted
        list before \a it; returns index of insertion point.  This Row becomes
        the property of the DropDownList and should not be deleted or inserted
        into any other DropDownLists */
    iterator       Insert(Row* row, iterator it);

    /** Insertion sorts \a row into a sorted list, or inserts into an unsorted
        list at the end of the list; returns index of insertion point.  This
        Row becomes the property of the DropDownList and should not be deleted
        or inserted into any other DropDownLists */
    iterator       Insert(Row* row);

    Row*           Erase(iterator it);            ///< removes and returns \a it from the list, or 0 if no such row exists
    void           Clear();                       ///< empties the list
    iterator       begin();                       ///< returns an iterator to the first list row
    iterator       end();                         ///< returns an iterator to the imaginary row one past the last one
    reverse_iterator
                   rbegin();                      ///< returns an iterator to the last list row
    reverse_iterator
                   rend();                        ///< returns an iterator to the imaginary row one past the first
    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).

    void           Select(iterator it);           ///< selects row-item \a it in the list
    void           Select(std::size_t n);         ///< selects row-item \a it in the list

    void           SetInteriorColor(Clr c);       ///< sets the color painted into the client area of the control
    void           SetDropHeight(Y h);       ///< sets the height of the drop-down list

    /** sets the style flags for the list to \a s (invalidates currently selected item). \see GG::ListBoxStyle */
    void           SetStyle(Flags<ListBoxStyle> s);

    void           SetNumCols(std::size_t n);            ///< sets the number of columns in each list item to \a n; if no column widths exist before this call, proportional widths are calulated and set, otherwise no column widths are set
    void           SetSortCol(std::size_t n);            ///< sets the index of the column used to sort rows when sorting is enabled (invalidates currently selected item); not range-checked
    void           SetColWidth(std::size_t n, X w); ///< sets the width of column \n to \a w; not range-checked

    /** Fixes the column widths; by default, an empty list will take on the
        number of columns of its first added row. \note The number of columns
        and their widths may still be set via SetNumCols() and SetColWidth()
        after this function has been called. */
    void           LockColWidths();

    /** Allows the number of columns to be determined by the first row added
        to an empty ListBox */
    void           UnLockColWidths();

    void           SetColAlignment(std::size_t n, Alignment align); ///< sets the alignment of column \a n to \a align; not range-checked
    void           SetRowAlignment(iterator it, Alignment align);   ///< sets the alignment of the Row at row index \a n to \a align; not range-checked

    virtual void   DefineAttributes(WndEditor* editor);
    //@}

protected:
    /** \name Structors */ ///@{
    DropDownList(); ///< default ctor
    //@}

    /** \name Mutators */ ///@{
    virtual void   LClick(const Pt& pt, Flags<ModKey> mod_keys);
    virtual void   KeyPress(Key key, boost::uint32_t key_code_point, Flags<ModKey> mod_keys);

    ListBox*       LB();                ///< returns the ListBox used to render the selected row and the popup list
    //@}

private:
    void           SelectImpl(iterator it, bool signal);

    iterator       m_current_item;  ///< the currently-selected list item (end() if none is selected)
    ListBox*       m_LB;            ///< the ListBox used to render the selected row and the popup list

    friend class boost::serialization::access;
    template <class Archive>
    void serialize(Archive& ar, const unsigned int version);
};

} // namespace GG

// template implementations
template <class Archive>
void GG::DropDownList::serialize(Archive& ar, const unsigned int version)
{
    std::size_t current_item;
    if (Archive::is_saving::value)
        current_item = std::distance(m_LB->begin(), m_current_item);
    ar  & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Control)
        & BOOST_SERIALIZATION_NVP(current_item)
        & BOOST_SERIALIZATION_NVP(m_LB);
    if (Archive::is_loading::value)
        m_current_item = boost::next(m_LB->begin(), current_item);
}

#endif // _GG_DropDownList_h_
