/** @file sample for a tree view where rows are filled on expand
 *
 * Last CVS checkin:
 * $Date: $
 * $Author: scheff $
 */

/**************************************************************************/

// standard C/C++ header:
#include <assert.h>

// Gnome header:
#include <glibmm/ustring.h>
#include <glibmm/nodetree.h>
#include <gtkmm/box.h>
#include <gtkmm/main.h>
#include <gtkmm/treestore.h>
#include <gtkmm/treeview.h>
#include <gtkmm/window.h>

/**************************************************************************/

/// @name Convinience Typedefs
//@{
typedef unsigned int uint;
typedef Glib::NodeTree<Glib::ustring> Node;
//@}

/// depth of sample tree
const uint DepthTree = 4;
const uint NChildren = 3;//8;

/** create a (sub-)tree of a certain depth.
 *
 * RECURSIVE FUNCTION
 *
 * @param depth depth of tree
 * @param name stub of tree node names
 * @return pointer to root node of created tree
 */
static Node* buildTree(
  int depth = DepthTree, const Glib::ustring &name = "1")
{
  Node *pNode = new Node(name);
  if (--depth) {
    for (uint i = 0; i < NChildren; ++i) {
      Glib::ustring nameChild = name;
      nameChild += '-'; nameChild += '0' + i;
      pNode->append(*buildTree(depth, nameChild));
    }
  }
  return pNode;
}

/**************************************************************************/

/// provides a class for a customized tree view widget.
class TreeView: public Gtk::TreeView {

  // types:
  private:

    /// provides the column model for the tree view.
    struct ModelColumns: public Gtk::TreeModelColumnRecord {
      /// @name Columns
      //@{
      Gtk::TreeModelColumn<Node*> pNode;
      Gtk::TreeModelColumn<Glib::ustring> name;
      //@}

      /// constructs column model.
      ModelColumns(void) { add(pNode); add(name); }
    };

  // constants:
  private:
    /// the column model for the tree view
    const ModelColumns _modelColumns;

  // variables:
  private:
    /// GTK+ TreeStore
    Glib::RefPtr<Gtk::TreeStore> _refTreeStore;

  // methods:
  public:
    /// @name Construction & Destruction
    //@{

    /** constructs a tree view for a certain tree.
     *
     * @param pNode pointer to root tree node
     */
    TreeView(Node *pNode);

    /// destroys tree view for a certain tree.
    virtual ~TreeView(void);

    //@}
  protected:
    /// @name Overloaded Event Handlers
    //@{

    /** called when user requested to expand a row.
     *
     * @param it tree model iterator for related row
     * @param path tree model path to related row
     */
    virtual void on_row_expanded(
      const Gtk::TreeModel::iterator &it, const Gtk::TreeModel::Path &path);

    //@}
  private:
    /// @name Internal Stuff
    //@{

    /** initializes a row for a certain node.
     *
     * @param row row
     * @param pNode pointer to corresponding node
     */
    void initRow(const Gtk::TreeModel::Row &row, Node *pNode);

    //@}
};

/**************************************************************************/

// Construction & Destruction

TreeView::TreeView(Node *pNode): Gtk::TreeView(),
  _refTreeStore(Gtk::TreeStore::create(_modelColumns))
{
  // init
  set_enable_tree_lines(true);
  //set_headers_visible(false);
  set_model(_refTreeStore);
  append_column("Name", _modelColumns.name);
  // make root row
  initRow(*_refTreeStore->append(), pNode);
}

TreeView::~TreeView(void) { }

// Overloaded Event Handlers

void TreeView::on_row_expanded(
  const Gtk::TreeModel::iterator &it, const Gtk::TreeModel::Path &path)
{
  const Gtk::TreeModel::Row &row = *it;
  assert(!row.children().empty());
  /* check whether row contains a dummy child
   * (recognized by NULL pointer for node)
   */
  const Gtk::TreeRow &row1stChild = *row.children().begin();
  if (!row1stChild[_modelColumns.pNode]) {
    // remove dummy row
    _refTreeStore->erase(row1stChild);
    // create child rows for nodes
    Node *pNode = row[_modelColumns.pNode];
    for (pNode = pNode->first_child(); pNode; pNode = pNode->next_sibling())
      initRow(*_refTreeStore->append(row.children()), pNode);
    // expand row
    expand_row(path, false);
  } else Gtk::TreeView::on_row_expanded(it, path);
}

// Internal Stuff

void TreeView::initRow(const Gtk::TreeModel::Row &row, Node *pNode)
{
  row[_modelColumns.pNode] = pNode;
  row[_modelColumns.name] = pNode->data();
  // if node has children
  if (!pNode->is_leaf()) {
    // create a dummy child row to force an expander icon
    Gtk::TreeModel::Row rowDummy = *_refTreeStore->append(row.children());
    rowDummy[_modelColumns.pNode] = 0; // to identify it as dummy
  }
}

/**************************************************************************/

/** provides the main function of application.
 *
 * @param argc number of command line arguments
 * @param argv pointer to array of pointers to strings with command line
 *        arguments
 * @return 0 ... application exited regularly\n
 *         else ... execution of application aborted
 */
int main(int argc, char *argv[])
{
  // init
  Gtk::Main gtkMain(argc, argv);
  // init a tree
  Node *pNode = buildTree();
  // init GUI
  Gtk::Window gtkWin;
   Gtk::VBox gtkVBox;
    TreeView treeView(pNode);
   gtkVBox.pack_start(treeView);
  gtkWin.add(gtkVBox);
  gtkWin.show_all();
  // run
  gtkMain.run(gtkWin);
  // done
  return 0;
}

