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

#include <Wt/WAbstractItemDelegate>
#include <Wt/WCheckBox>
#include <Wt/WLineEdit>
#include <Wt/WString>

namespace Wt {
  class WAnchor;
  class WCheckBox;
  class WContainerWidget;
  class WImage;
  class WLineEdit;
  class WText;

#ifndef WT_CNOR
  template <class Widget> class IndexEdit;
  typedef IndexEdit<WCheckBox> IndexCheckBox;
#else
  class IndexCheckBox;
#endif // WT_CNOR

/*! \class WItemDelegate Wt/WItemDelegate Wt/WItemDelegate
 *  \brief Standard delegate class for rendering a view item.
 *
 * This class provides the standard implementation for rendering an
 * item (as in a WAbstractItemView), and renders data provided by the
 * standard data roles (see ItemDataRole). It also provides default
 * editing support using a line edit.
 *
 * You may provide special editing support for an item by specializing
 * the widget and reimplement createEditor(), setModelData(),
 * editState(), and setEditState().
 *
 * \ingroup modelview
 */
class WT_API WItemDelegate : public WAbstractItemDelegate
{
public:
  /*! \brief Create an item delegate.
   */
  WItemDelegate(WObject *parent = 0);

  /*! \brief Creates or updates a widget that renders an item.
   *
   * The following properties of an item are rendered:
   *
   * - text using the Wt::DisplayRole data, with the format specified
   *   by setTextFormat()
   * - a check box depending on the Wt::ItemIsUserCheckable flag and
   *   Wt::CheckStateRole data
   * - an anchor depending on Wt::InternalPathRole or Wt::UrlRole values
   * - an icon depending on the value of Wt::DecorationRole
   * - a tooltip depending on the value of Wt::ToolTipRole
   * - a custom style class depending on the value of Wt::StyleClassRole
   *
   * When the flags indicates Wt::RenderEditing, then createEditor() is
   * called to create a suitable editor for editing the item.
   */
  virtual WWidget *update(WWidget *widget, const WModelIndex& index,
			  WFlags<ViewItemRenderFlag> flags);

  virtual void updateModelIndex(WWidget *widget, const WModelIndex& index);

  /*! \brief Sets the text format string.
   *
   * \if cpp
   *
   * The DisplayRole data is converted to a string using asString() by passing
   * the given format.
   *
   * \elseif java
   *
   * The DisplayRole data is converted to a string using {javadoclink
   * StringUtils#asString(Object)}, passing the given format. If the format is
   * an empty string, this corresponds to {javadoclink Object#toString()}.
   *
   * \endif 
   *
   * The default value is "".
   */
  void setTextFormat(const WT_USTRING& format);

  /*! \brief Returns the text format string.
   *
   * \sa setTextFormat()
   */
  const WT_USTRING& textFormat() const { return textFormat_; }

  /*! \brief Saves the edited data to the model.
   *
   * The default implementation saves the current edit value to the model.
   * You will need to reimplement this method for a custom editor.
   *
   * As an example of how to deal with a specialized editor, consider the
   * default implementation:
   * \if cpp
   * \code
   * void WItemDelegate::setModelData(const boost::any& editState,
   *                                  Wt::WAbstractItemModel *model,
   *                                  const Wt::WModelIndex& index) const
   * {
   *   model->setData(index, editState, EditRole);
   * }
   * \endcode
   * \elseif java
   * \code
   * public void setModelData(Object editState, WAbstractItemModel model, WModelIndex index) {
   *   model.setData(index, editState, ItemDataRole.EditRole);
   * }
   * \endcode
   * \endif
   *
   * \sa createEditor(), editState()
   */
  virtual void setModelData(const boost::any& editState,
			    WAbstractItemModel *model,
			    const WModelIndex& index) const;

  /*! \brief Returns the current edit state.
   *
   * The default implementation returns the current text in the line edit.
   * You will need to reimplement this method for a custom editor.
   *
   * As an example of how to deal with a specialized editor, consider the
   * default implementation:
   * \if cpp
   * \code
   * boost::any WItemDelegate::editState(Wt::WWidget *editor) const
   * {
   *   Wt::WContainerWidget *w = dynamic_cast<Wt::WContainerWidget *>(editor);
   *   Wt::WLineEdit *lineEdit = dynamic_cast<Wt::WLineEdit *>(w->widget(0));
   *
   *   return boost::any(lineEdit->text());
   * }
   * \endcode
   * \elseif java
   * \code
   * public Object getEditState(WWidget editor) {
   *   WContainerWidget w = (WContainerWidget) editor;
   *   WLineEdit lineEdit = (WLineEdit) w.getWidget(0);
   *   return lineEdit.getText();
   * }
   * \endcode
   * \endif
   *
   * \sa createEditor(), setEditState(), setModelData()
   */
  virtual boost::any editState(WWidget *editor) const;

  /*! \brief Sets the editor data from the editor state.
   *
   * The default implementation resets the text in the line edit.
   * You will need to reimplement this method if for a custom editor.
   *
   * As an example of how to deal with a specialized editor, consider the
   * default implementation:
   * \if cpp
   * \code
   * void WItemDelegate::setEditState(Wt::WWidget *editor, const boost::any& value) const
   * {
   *   Wt::WContainerWidget *w = dynamic_cast<Wt::WContainerWidget *>(editor);
   *   Wt::WLineEdit *lineEdit = dynamic_cast<Wt::WLineEdit *>(w->widget(0));
   *
   *   lineEdit->setText(boost::any_cast<Wt::WString>(value));
   * }
   * \endcode
   * \elseif java
   * \code
   * public void setEditState(WWidget editor, Object value) {
   *   WContainerWidget w = (WContainerWidget) editor;
   *   WLineEdit lineEdit = (WLineEdit) w.getWidget(0);
   *   lineEdit.setText((String) value);
   * }
   * \endcode
   * \endif
   *
   * \sa createEditor()
   */
  virtual void setEditState(WWidget *editor, const boost::any& value) const;

protected:
  /*! \brief Creates an editor for a data item.
   *
   * The default implementation returns a WLineEdit which edits the
   * item's Wt::EditRole value.
   *
   * You may reimplement this method to provide a suitable editor, or
   * to attach a custom validator. In that case, you will probably
   * also want to reimplement editState(), setEditState(), and
   * setModelData().
   *
   * The editor should not keep a reference to the model index (it
   * does not need to since setModelData() will provide the proper
   * model index to save the data to the model). Otherwise, because
   * model indexes may shift because of row or column insertions, you
   * should reimplement updateModelIndex().
   *
   * As an example of how to provide a specialized editor, consider the
   * default implementation, which returns a WLineEdit:
   * \if cpp
   * \code
   * Wt::WWidget *WItemDelegate::createEditor(const Wt::WModelIndex& index, WFlags<ViewItemRenderFlag> flags) const
   * {
   *   Wt::WContainerWidget *result = new Wt::WContainerWidget();
   *   result->setSelectable(true);
   *
   *   Wt::WLineEdit *lineEdit = new Wt::WLineEdit();
   *   lineEdit->setText(asString(index.data(EditRole), textFormat_));
   *   lineEdit->enterPressed().connect(boost::bind(&WItemDelegate::doCloseEditor, this, result, true));
   *   lineEdit->escapePressed().connect(boost::bind(&WItemDelegate::doCloseEditor, this, result, false));
   *
   *   if (flags & RenderFocused)
   *     lineEdit->setFocus();
   *
   *   // We use a layout so that the line edit fills the entire cell.
   *   result->setLayout(new WHBoxLayout());
   *   result->layout()->setContentsMargins(1, 1, 1, 1);
   *   result->layout()->addWidget(lineEdit);
   *
   *   return result;
   * }
   *
   * void WItemDelegate::doCloseEditor(Wt::WWidget *editor, bool save) const
   * {
   *   closeEditor().emit(editor, save);
   * }
   * \endcode
   * \elseif java
   * \code
   * protected WWidget createEditor(WModelIndex index, EnumSet&lt;ViewItemRenderFlag&gt; flags) {
   *  final WContainerWidget result = new WContainerWidget();
   *  result.setSelectable(true);
   *  WLineEdit lineEdit = new WLineEdit();
   *  lineEdit.setText(StringUtils.asString(index.getData(ItemDataRole.EditRole), this.textFormat_).toString());
   *  lineEdit.enterPressed().addListener(this, new Signal.Listener() {
   *    public void trigger() {
   *      WItemDelegate.this.closeEditor().trigger(result, true);
   *    }
   *  });
   *  lineEdit.escapePressed().addListener(this, new Signal.Listener() {
   *    public void trigger() {
   *      WItemDelegate.this.closeEditor().trigger(result, false);
   *    }
   *  });
   *
   *  if (flags.contains(ViewItemRenderFlag.RenderFocused))
   *    lineEdit.setFocus();
   *
   *  result.setLayout(new WHBoxLayout());
   *  result.getLayout().setContentsMargins(1, 1, 1, 1);
   *  result.getLayout().addWidget(lineEdit);
   *  return result;
   * }
   * \endcode
   * \endif
   */
  virtual WWidget *createEditor(const WModelIndex& index,
				WFlags<ViewItemRenderFlag> flags) const;

private:
  WT_USTRING textFormat_;

  struct WidgetRef {
    WWidget *w;
    WidgetRef(WWidget *widget) : w(widget) { }
  };

  IndexCheckBox *checkBox(WidgetRef& w, const WModelIndex& index,
			 bool autoCreate, bool triState = false);

  WText *textWidget(WidgetRef& w);
  WImage *iconWidget(WidgetRef& w, bool autoCreate = false);
  WAnchor *anchorWidget(WidgetRef& w);

  void onCheckedChange(IndexCheckBox *checkBox) const;
  void doCloseEditor(WWidget *editor, bool save) const;
};

}

#endif // WITEMDELEGATE_H_
