// This may look like C code, but it's really -*- C++ -*-
/*
 * Copyright (C) 2008 Emweb bvba, Kessel-Lo, Belgium.
 *
 * See the LICENSE file for terms of use.
 */
#ifndef WCONTAINER_WIDGET_H_
#define WCONTAINER_WIDGET_H_

#include <WInteractWidget>

namespace Wt {

class WApplication;

/*! \class WContainerWidget WContainerWidget WContainerWidget
 *  \brief A widget that holds and manages child widgets.
 *
 * A %WContainerWidget acts as a container for child widgets.
 *
 * By default, a %WContainerWidget is \link WWidget::setInline()
 * stacked \endlink and manages its children within a rectangle.  When
 * setting the %WContainerWidget \link WWidget::setInline() inline
 * \endlink the container only acts as a conceptual container,
 * offering a common style to its children. Inline children are still
 * layed out inline within the flow of the parent container of this
 * container, as if they were inserted directly into that parent
 * container.
 *
 * FIXME: We still need to provide a method to specify an inline-block,
 * this is a container that itself has an inline layout, but that manages
 * it children in a block.
 *
 */
class WT_API WContainerWidget : public WInteractWidget
{
public:
  /*! \brief How to handle overflow of inner content
   */
  enum Overflow {
    OverflowVisible=0x0,//!< Show content that overflows.
    OverflowAuto=0x1,   //!< Show scrollbars when needed.
    OverflowHidden=0x2, //!< Hide content that overflows.
    OverflowScroll=0x3  //!< Always show scroll bars.
  };

  /*! \brief Create a container with optional parent.
   */
  WContainerWidget(WContainerWidget *parent = 0);

  /*! \brief Destruct a %WContainerWidget
   */
  ~WContainerWidget();

  /*! \brief Add a child widget to this container.
   *
   * This is equivalent to passing this container as the parent when
   * constructing the child. The widget is appended to the list of
   * children, and thus also layed-out at the end.
   */
  virtual void addWidget(WWidget *widget);

  /*! \brief insert a child widget in this container, before another
   *         widget.
   *
   * The <i>widget</i> is inserted at the place of the <i>before</i>
   * widget, and subsequent widgets are shifted.
   *
   * \sa insertWidget(int index, WWidget *widget);
   */
  virtual void insertBefore(WWidget *widget, WWidget *before);

  /*! \brief insert a child widget in this container at given index.
   *
   * The <i>widget</i> is inserted at the given <i>index</i>, and
   * subsequent widgets are shifted.
   *
   * \sa insertBefore(WWidget *widget, WWidget *before);
   */
  virtual void insertWidget(int index, WWidget *widget);

  /*! \brief Remove a child widget from this container.
   *
   * This removes the widget from this container, but does not delete
   * the widget !
   */
  virtual void removeWidget(WWidget *widget);

  /*! \brief Remove and delete all child widgets.
   *
   * This deletes all children that have been added to this container.
   */
  virtual void clear();

  /*! \brief Return the index of a widget.
   */
  virtual int indexOf(WWidget *widget) const;

  /*! \brief Return the widget at <i>index</i>
   */
  virtual WWidget *widget(int index) const;

  /*! \brief Get the number of widgets in this container.
   */
  virtual int count() const;

  /*! \brief Specify how child widgets must be aligned within the container
   *
   * Specify the horizontal alignment of child widgets. Note that there
   * is no way to specify vertical alignment: children are always pushed
   * to the top of the container. Only in a WTableCell, there is a method
   * to align the children vertically.
   */
  void setContentAlignment(HorizontalAlignment contentAlignment);

  /*! \brief Set padding inside the widget
   *
   * Setting padding has the effect of adding distance between the
   * widget children and the border.
   */
  void setPadding(WLength padding, int sides = All);

  /*! \brief Get the padding set for the widget.
   *
   * \sa setPadding(WLength padding, Side sides);
   */
  WLength padding(Side side) const;

  /*! \brief Get the horizontal alignment of children
   *
   * \sa setContentAlignment(HorizontalAlignment)
   */
  HorizontalAlignment contentAlignment() const { return contentAlignment_; }

  /*! \brief Get the children.
   */
  virtual const std::vector<WWidget *>& children() const { return *children_; }

  /*! \brief Set how overflow of contained children must be handled.
   *
   * This is an alternative (CSS-ish) way to provide scroll bars on a
   * container widget, compared to wrapping inside a WScrollArea.
   *
   * Note that currently, you cannot separately specify vertical and
   * horizontal scroll behaviour, since this is not supported on
   * Opera. Therefore, settings will apply automatically to both orientations.
   *
   * \sa WScrollArea
   */
  void setOverflow(Overflow overflow,
		   int orientation = Horizontal | Vertical);

  /*! \brief Render the container as an HTML list.
   *
   * Setting <i>renderList</i> to true will cause the container to be
   * using an HTML &lt;UL&gt; or &lt;OL&gt; type, depending on the
   * value of <i>orderedList</i>. This must be set before the initial
   * render of the container. When set, any contained WContainerWidget
   * will be rendered as an HTML &lt;LI&gt;. Adding
   * non-WContainerWidget children results in unspecified behaviour.
   *
   * Note that CSS default layout rules for &lt;UL&gt; and &lt;OL&gt;
   * add margin and padding to the container, which may look odd if
   * you do not use bullets.
   *
   * By default, a container is rendered using a &lt;DIV&gt; element.
   *
   * \sa isList(), isOrderedList(), isUnorderedList()
   */
  void setList(bool list, bool ordered = false);

  /*! \brief Return if this container is rendered as a List
   *
   * \sa setList(), isOrderedList(), isUnorderedList()
   */  
  bool isList() const;

  /*! \brief Return if this container is rendered as an Unordered List
   *
   * \sa setList(), isList(), isOrderedList()
   */  
  bool isUnorderedList() const;

  /*! \brief Return if this container is rendered as an Ordered List
   *
   * \sa setList(), isList(), isUnrderedList()
   */  
  bool isOrderedList() const;

private:
  static const int BIT_CONTENT_ALIGNMENT_CHANGED = 0;
  static const int BIT_PADDINGS_CHANGED = 1;
  static const int BIT_OVERFLOW_CHANGED = 2;
  static const int BIT_ADJUST_CHILDREN_ALIGN = 3;
  static const int BIT_LIST = 4;
  static const int BIT_ORDERED_LIST = 5;

  /*
   * Frequently used attributes.
   */
  std::bitset<6>            flags_;
  HorizontalAlignment       contentAlignment_;
  Overflow                 *overflow_;
  WLength		   *padding_;
  std::vector<WWidget *>   *addedChildren_;

  virtual bool              wasEmpty() const;

  void rootAsJavaScript(WApplication *app, std::ostream& out, bool all);

  friend class WebRenderer;

protected:
  virtual void        removeChild(WWidget *child);

  void		      createDomChildren(DomElement& parent);

  void                updateDom(DomElement& element, bool all);
  virtual DomElement *createDomElement();
  virtual void        getDomChanges(std::vector<DomElement *>& result);
};

}

#endif // WCONTAINER_WIDGET_H_
