// 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 <Wt/WInteractWidget>

namespace Wt {

class WApplication;
class StdLayoutImpl;

/*! \class WContainerWidget Wt/WContainerWidget Wt/WContainerWidget
 *  \brief A widget that holds and manages child widgets.
 *
 * A %WContainerWidget acts as a container for child widgets. Child
 * widgets may be added directly to the container or using a layout
 * manager.
 *
 * Use addWidget() or pass the container as constructor argument to a
 * widget to directly add children to the container, without using a
 * layout manager. In that case, CSS-based layout is used, and the
 * resulting display is determined by properties of the children and
 * the container. By default, a %WContainerWidget is displayed as a
 * \link WWidget::setInline() block \endlink and manages its children
 * within a rectangle. Inline child widgets are layed out in lines,
 * wrapping around as needed, while block child widgets are stacked
 * vertically. The container may add padding at the container edges
 * using setPadding(), and provide alignment of contents using
 * setContentAlignment(). A container is rendered by default using a
 * HTML <tt>div</tt> tag, but this may be changed to an HTML
 * <tt>ul</tt> or <tt>ol</tt> tag to make use of other CSS layout
 * techniques, using setList(). In addition, specializations of this
 * class as implemented by WAnchor, WGroupBox, WStackedWidget and
 * WTableCell provide other alternative rendering of the container.
 *
 * 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. Block children are then not allowed (according to the
 * HTML specification).
 *
 * To use a layout manager instead of CSS-based layout, use
 * setLayout() or pass the container as constructor argument to a
 * layout manager. In that case you should not define any padding for
 * the container, and widgets and nested layout managers must be added
 * to the layout manager, instead of to the container directly.
 *
 * Usage example:
 * \if cpp
 * \code
 *
 * // Example 1:
 * // Instantiate a container widget and add some children whose layout 
 * // is governed based on HTML/CSS rules.
 * Wt::WContainerWidget *container1 = new Wt::WContainerWidget();
 * container1->addWidget(new Wt::WText("Some text"));
 * container1->addWidget(new Wt::WImage("images/img.png"));
 * Wt::WContainerWidget *child3 = new Wt::WContainerWidget(container1);
 *
 * // Example 2:
 * // Instantiate a container widget which uses a layout manager
 * Wt::WContainerWidget *container2 = new Wt::WContainerWidget();
 * container2->resize(WLength::Auto, 600); // give the container a fixed height.
 *
 * Wt::WVBoxLayout *layout = new Wt::WVBoxLayout();
 * layout->addWidget(new Wt::WText("Some text"));
 * layout->addWidget(new Wt::WImage("images/img.png"));
 *
 * container2->setLayout(layout);      // set the layout to the container.
 * \endcode
 * \elseif java
 * \code
 * // Example 1:
 * // Instantiate a container widget and add some children whose layout 
 * // is governed based on HTML/CSS rules.
 * WContainerWidget container1 = new WContainerWidget();
 * container1.addWidget(new WText("Some text"));
 * container1.addWidget(new WImage("images/img.png"));
 * WContainerWidget child3 = new WContainerWidget(container1);
		 
 * // Example 2:
 * // Instantiate a container widget which uses a layout manager
 * WContainerWidget container2 = new WContainerWidget();
 * // give the container a fixed height
 * container2.resize(WLength.Auto, new WLength(600)); 
 *
 * WVBoxLayout layout = new WVBoxLayout();
 * layout.addWidget(new WText("Some text"));
 * layout.addWidget(new WImage("images/img.png"));
		 
 * container2.setLayout(layout);      // set the layout to the container.
 * \endcode
 * \endif
 *
 * When using a layout manager, you need to carefully consider the
 * alignment of the layout manager with respect to the container: when
 * the container's height is unconstrained (not specified explicitly
 * using resize() or a style class, and the container is not included
 * in a layout manager), you should pass AlignTop to setLayout().
 *
 * <h3>CSS</h3>
 *
 * Depending on its configuration and usage, the widget corresponds to the
 * following HTML tags:
 *  - By default, the widget corresponds to a <tt>&lt;div&gt;</tt> tag.
 *  - When configured with setInline(true), the widget corresponds to a
 *    <tt>&lt;span&gt;</tt>.
 *  - When configured with setList(true), the widget corresponds to a
 *    <tt>&lt;ul&gt;</tt>.
 *  - When configured with setList(true, true), the widget corresponds to a
 *    <tt>&lt;ol&gt;</tt>.
 *  - When inserted into a container widget that isList(), the widget
 *    corresponds to a <tt>&lt;li&gt;</tt>.
 *
 * This widget does not provide styling, and can be styled using
 * inline or external CSS as appropriate.
 */
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 Creates a container with optional parent.
   */
  WContainerWidget(WContainerWidget *parent = 0);

  /*! \brief Destructor.
   */
  ~WContainerWidget();

  /*! \brief Sets a layout manager for the container.
   *
   * Note that you can nest layout managers inside each other, to
   * create a complex layout hierarchy.
   *
   * If a previous layout manager was already set, it is first deleted.
   * In that case, you will need to make sure that you either readd all
   * widgets that were part of the previous layout to the new layout, or
   * delete them, to avoid memory leaks.
   *
   * \sa layout()
   */
  void setLayout(WLayout *layout);  

  /*! \brief Sets a layout manager for the container (<b>deprecated</b>).
   *
   * The \p alignment argument determines how the layout is
   * aligned inside the container. By default, the layout manager
   * arranges children over the entire width and height of the
   * container, corresponding to a value of AlignJustify.
   *
   * In general, \p alignment is the logical OR of a horizontal
   * and a vertical flag:
   * - The horizontal alignment option may be one of Wt::AlignLeft,
   *   Wt::AlignCenter, Wt::AlignRight, or Wt::AlignJustify.
   * - The vertical alignment option may be '0' (corresponding to vertical
   *   justification to the full height), or Wt::AlignTop.
   *
   * When using a horizontal alignment different from
   * Wt::AlignJustify, and a vertical alignment different from '0',
   * the widget is sized in that direction to fit the contents,
   * instead of the contents being adjusted to the widget size. This
   * is useful when the container does not have a specific size in
   * that direction and when the layout manager does not contain any
   * widgets that wish to consume all remaining space in that
   * direction.
   *
   * The widget will take ownership of \p layout.
   *
   * \deprecated using setLayout() instead, use spacers or a nested
   * layout to control the overall alignment of the layout contents
   * within the container, and use setMaximumSize() (if needed) to let
   * the layout contents determine the size of the container.
   *
   * \sa layout()
   */
  void setLayout(WLayout *layout, WFlags<AlignmentFlag> alignment);

  /*! \brief Returns the layout manager that was set for the container.
   *
   * If no layout manager was previously set using setLayout(WLayout
   * *), 0 is returned.
   *
   * \sa setLayout(WLayout *)
   */
  WLayout *layout() { return layout_; }

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

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

  /*! \brief Removes 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 Removes and deletes all child widgets.
   *
   * This deletes all children that have been added to this container.
   *
   * If a layout was set, also the layout manager is deleted.
   */
  virtual void clear();

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

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

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

  /*! \brief Specifies how child widgets must be aligned within the container
   *
   * For a WContainerWidget, only specifes 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.
   *
   * For a WTableCell, this may also specify the vertical alignment.
   * The default alignment is (Wt::AlignTop | Wt::AlignLeft).
   */
  void setContentAlignment(WFlags<AlignmentFlag> contentAlignment);

  /*! \brief Sets padding inside the widget
   *
   * Setting padding has the effect of adding distance between the
   * widget children and the border.
   */
  void setPadding(const WLength& padding, WFlags<Side> sides = All);

  /*! \brief Returns the padding set for the widget.
   *
   * \sa setPadding(const WLength&, WFlags<Side>)
   */
  WLength padding(Side side) const;

  /*! \brief Returns the alignment of children
   *
   * \sa setContentAlignment(WFlags<AlignmentFlag>)
   */
  WFlags<AlignmentFlag> contentAlignment() const { return contentAlignment_; }

  /*! \brief Sets how overflow of contained children must be handled.
   *
   * This is an alternative (CSS-ish) way to configure scroll bars on
   * a container widget, compared to wrapping inside a WScrollArea.
   *
   * Unlike WScrollArea, horizontal scrolling does not work reliably
   * when the container widget is inserted in a layout manager: the
   * layout manager will overflow rather than use scrollbars for this
   * container widget. A solution then is to use WScrollArea instead.
   *
   * \sa WScrollArea
   */
  void setOverflow(Overflow overflow,
		   WFlags<Orientation> orientation = (Horizontal | Vertical));

  /*! \brief Renders the container as an HTML list.
   *
   * Setting \p renderList to \c true will cause the container to be
   * using an HTML <tt>&lt;ul&gt;</tt> or <tt>&lt;ol&gt;</tt> type,
   * depending on the value of \p orderedList. This must be set
   * before the initial render of the container. When set, any
   * contained WContainerWidget will be rendered as an HTML
   * <tt>&lt;li&gt;</tt>. Adding non-WContainerWidget children results
   * in unspecified behaviour.
   *
   * Note that CSS default layout rules for <tt>&lt;ul&gt;</tt> and
   * <tt>&lt;ol&gt;</tt> 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 <tt>&lt;div&gt;</tt>
   * element.
   *
   * \sa isList(), isOrderedList(), isUnorderedList()
   */
  void setList(bool list, bool ordered = false);

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

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

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

  /*! \brief Event signal emitted when scrolling in the widget.
   *
   * This event is emitted when the user scrolls in the widget (for setting
   * the scroll bar policy, see setOverflow()). The event conveys details
   * such as the new scroll bar position, the total contents height and the
   * current widget height.
   *
   * \sa setOverflow()
   */
  EventSignal<WScrollEvent>& scrolled();

private:
  static const char *SCROLL_SIGNAL;

  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;
  static const int BIT_LAYOUT_NEEDS_RERENDER = 6;
  static const int BIT_LAYOUT_NEEDS_UPDATE = 7;

  /*
   * Frequently used attributes.
   */
  std::bitset<8>            flags_;
  WFlags<AlignmentFlag>     contentAlignment_;
  Overflow        *WT_ARRAY overflow_;
  WLength	  *WT_ARRAY padding_;
  WLayout                  *layout_;

  bool wasEmpty() const;

  void rootAsJavaScript(WApplication *app, WStringStream& out, bool all);

  friend class WebRenderer;

protected:
  using WWidget::removeChild;

  virtual void removeChild(WWidget *child);
  virtual int firstChildIndex() const;

  virtual void childResized(WWidget *child, WFlags<Orientation> directions);
  virtual void parentResized(WWidget *parent, WFlags<Orientation> directions);
  virtual void getDomChanges(std::vector<DomElement *>& result,
			     WApplication *app);
  DomElement *createDomElement(WApplication *app, bool addChildren);
  void createDomChildren(DomElement& parent, WApplication *app);
  void updateDomChildren(DomElement& parent, WApplication *app);

  virtual DomElementType domElementType() const;
  virtual void           updateDom(DomElement& element, bool all);
  virtual void propagateRenderOk(bool deep);
  virtual DomElement    *createDomElement(WApplication *app);

  virtual WLayoutItemImpl *createLayoutItemImpl(WLayoutItem *item);
  StdLayoutImpl *layoutImpl() const;

  friend class StdLayoutImpl;
  friend class StdGridLayoutImpl;
  friend class StdGridLayoutImpl2;

private:
  void propagateLayoutItemsOk(WLayoutItem *item);
  void layoutChanged(bool rerender, bool deleted);
  void removeFromLayout(WWidget *w);

  friend class StdWidgetItemImpl;
  friend class WImage;
  friend class WTableCell;
};

}

#endif // WCONTAINER_WIDGET_H_
