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

#include <WCompositeWidget>

namespace Wt {

class WIconPair;
class WTable;
class WText;
class WImage;
class WTableCell;

/*! \class WTreeNode WTreeNode WTreeNode
 *  \brief A single node in a tree list.
 *
 * A tree list is constructed by combining several tree node objects
 * in a tree hierarchy, by passing the parent tree node as the last
 * argument in the child node constructor, or by using addChildNode(),
 * to add a child to its parent.
 *
 * Each tree node has a label, and optionally a label icon pair. The
 * icon pair offers the capability to show a different icon depending
 * on the state of the node (expanded or collapsed). When the node has
 * any children, the child count is indicated next to the label.
 * When expanding a tree node it will collapse all its children.
 *
 * The treenode provides several policies to communicate the current
 * contents of the tree to the client (if possible):
 * <ul>
 *   <li>WTreeNode::PreLoading: the entire tree is transmitted to the client,
 *     and all tree navigation requires no further communication.</li>
 *   <li>WTreeNode::LazyLoading: only the minimum is transmitted to the
 *     client. When expanding a node for the first time, only then it is
 *     transmitted to the client, and this may thus have some latency.</li>
 *   <li>WTreeNode::NextLevelLoading: all leafs of visible children are
 *     transmitted, but not their children. This provides a good trade-off
 *     between bandwith use and interactivity, since expanding any
 *     tree node will happen instantly, and at the same time trigger some
 *     communication in the back-ground to load the next level of
 *     invisible nodes.</li>
 * </ul>
 *
 * The default policy is WTreeNode::LazyLoading. Another load policy
 * may be specified using setLoadPolicy() on the root node and before
 * adding any children.  The load policy is inherited by all children
 * in the tree.
 *
 * There are a few scenarios where it makes sense to specialize the
 * %WTreeNode class. One scenario is create a tree that is populated
 * dynamically while browsing. For this purpose you should reimplement the
 * populate() method, whose default implementation does nothing. This
 * method is called when 'loading' the node. The exact moment for loading
 * a treenode depends on the LoadPolicy.
 *
 * A second scenario that is if you want to customize the look of the
 * tree label (see labelArea()) or if you want to modify or augment
 * the event collapse/expand event handling (see doExpand() and
 * doCollapse()).
 *
 * Next to the icons, two style classes determine the look of a
 * %WTreeNode: the label has CSS style class "treenodelabel", and the
 * child count has CSS style class "treenodechildcount".
 *
 * For example, the following CSS stylesheet styles a tree for which the
 * root has style class "mytree":
 * <pre>
mytree * .treenodelabel {
  font-size: smaller;
}
mytree * .treenodechildcount {
  font-size: smaller;
  color: blue;
}
 * </pre>
 *
 * The tree node uses an image-pack, which is a collection of images to
 * render the expand/collapse icons and lines. Use setImagePack() to
 * specify the location of these icons. This needs only be done on the
 * root of the tree, as child nodes will query their ancestors for the
 * location of these images, when they are not set explicitly.
 *
 * <ul>
 *   <li><b>nav-plus-line-middle.gif</b>: expand icon for all but the last
 *     child in a node.</li>
 *   <li><b>nav-minus-line-middle.gif</b>: collapse icon for all but the last
 *     child in a node.</li>
 *   <li><b>line-middle.gif</b>: like nav-plus-line-middle.gif but for nodes
 *     that cannot be expanded as they have no children.</li>
 *   <li><b>nav-plus-line-last.gif</b>: same as nav-plus-line-middle.gif but
 *     for the last node (terminates the vertical line).</li>
 *   <li><b>nav-minus-line-last.gif</b>: same as nav-minus-line-middle.gif but
 *     for the last node (terminates the vertical line).</li>
 *   <li><b>line-last.gif</b>: same as line-middle.gif but for the last node
 *     (terminates the vertical line).</li>
 *   <li><b>line-trunk.gif</b>: extension gif for the vertical line.
 *     </li>
 * </ul>
 */
class WT_API WTreeNode : public WCompositeWidget
{
public:
  /*! \brief Load policy.
   */
  enum LoadPolicy { LazyLoading,     //!< Load-on-demand of child nodes
		    PreLoading,      //!< Pre-load all child nodes
		    NextLevelLoading //!< Pre-load one level of child nodes
  };

  /*! \brief Construct a tree node with the given label.
   *
   * The labelIcon, if specified, will appear just before the label and
   * its state reflect the expand/collapse state of the node.
   *
   * The node is initialized to be collapsed.
   */
  WTreeNode(const WString& labelText,
	    WIconPair *labelIcon = 0,
	    WTreeNode *parentNode = 0);

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

  /*! \brief Get a reference to the label.
   */
  WText *label() const { return labelText_; }

  /*! \brief Get a reference to the label icon.
   */
  WIconPair *labelIcon() const { return labelIcon_; }

  /*! \brief Change the label icon.
   */
  void setLabelIcon(WIconPair *labelIcon);

  /*! \brief Add a child node.
   */
  virtual void addChildNode(WTreeNode *node);

  /*! \brief Remove a child node.
   */
  void removeChildNode(WTreeNode *node);

  /*! \brief Get the list of children.
   */
  const std::vector<WTreeNode *>& childNodes() const { return childNodes_; }

  /*! \brief Set the image pack for this (sub)tree.
   *
   * You must specify a valid url for the directory that contains
   * the icons.
   */
  void setImagePack(const std::string url);

  /*! \brief Change the load policy for this tree.
   *
   * This may only be set on the root of a tree, and before adding
   * any children.
   */
  void setLoadPolicy(LoadPolicy loadPolicy);  

  /*! \brief Is this node expanded ?
   */
  bool expanded() const;

protected:
  /*! \brief Construct a tree node with empty labelArea().
   *
   * This tree node has no label or labelicon, and is therefore ideally
   * suited to provide a custom look.
   */
  WTreeNode(WTreeNode *parentNode = 0);

  /*! \brief Access the container widget that holds the label area.
   *
   * Use this to customize how the label should look like.
   */
  WTableCell *labelArea();

  /*! \brief Populate the node dynamically on loading.
   *
   * Reimplement this method if you want to populate the widget dynamically,
   * as the tree is being browsed and therefore loaded. This is only
   * usefull with LazyLoading or NextLevelLoading strategies.
   */
  virtual void populate();

  /*! \brief The image pack that is used for this tree node.
   *
   * This is the imagepack that was set, or if not set, the image pack of
   * its parent.
   */
  std::string imagePack() const;

public slots:
  /*! \brief Expand this node.
   *
   * Besides the actual expansion of the node, this may also trigger
   * the loading and population of the node children, or of the children's
   * children.
   *
   * \sa collapse()
   * \sa doExpand()
   */
  void expand();

  /*! \brief Collapse this node.
   *
   * \sa expand()
   * \sa doCollapse()
   */
  void collapse();

protected slots:

  /*! \brief The actual expand.
   *
   * This method, which is implemented as a stateless slot, performs the
   * actual expansion of the node.
   *
   * You may want to reimplement this function (and undoDoExpand()) if you
   * wish to do additional things on node expansion.
   *
   * \sa doCollapse()
   * \sa expand()
   */
  virtual void doExpand();

  /*! \brief The actual collapse.
   *
   * This method, which is implemented as a stateless slot, performs the
   * actual collapse of the node.
   *
   * You may want to reimplement this function (and undoDoCollapse()) if you
   * wish to do additional things on node expansion.
   *
   * \sa doExpand()
   * \sa collapse()
   */
  virtual void doCollapse();

protected:
  /*! \brief Undo method for doCollapse() stateless implementation.
   *
   * \sa doCollapse()
   */
  virtual void undoDoExpand();

  /*! \brief Undo method for doCollapse() stateless implementation.
   *
   * \sa doExpand()
   */
  virtual void undoDoCollapse();

private slots:
  void loadChildren();
  void loadGrandChildren();

private:
  std::vector<WTreeNode *> childNodes_;
  bool                     collapsed_;
  WTreeNode		   *parentNode_;
  LoadPolicy               loadPolicy_;
  std::string              imagePackUrl_;

  WTable                   *layout_;
  WIconPair                *expandIcon_;
  WImage                   *noExpandIcon_;
  WIconPair		   *labelIcon_;
  WText			   *labelText_;
  WText			   *childCountLabel_;
  WContainerWidget         *expandedContent_;

  bool                     childrenLoaded_;
  bool                     populated_;

  void create();
  void update();
  bool isLastChildNode() const;
  void updateChildren(bool recursive = false);
  bool wasCollapsed_;

  enum ImageIndex { Middle = 0, Last = 1 };

  static std::string imageLine_[];
  static std::string imagePlus_[];
  static std::string imageMin_[];
};

}

#endif // WTREENODE_H_
