// 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 EXT_COMBOBOX_H_
#define EXT_COMBOBOX_H_

#include <Wt/WJavaScript>
#include <Wt/Ext/LineEdit>

namespace Wt {

  class WAbstractItemModel;
  class WModelIndex;

  namespace Ext {

    class DataStore;

/*! \class ComboBox Wt/Ext/ComboBox Wt/Ext/ComboBox
 *  \brief A widget that provides a drop-down combo-box control.
 *
 * A combo-box provides the user with a choice of options to chose from.
 * Use addItem(const WString&) or insertItem(int, const WString&) to
 * populate the combo-box. All the content can be cleared through clear().
 *
 * To act on a new selection, either connect a slot to the activated()
 * signal.
 *
 * At all times, the current selection index is available through
 * currentIndex() or the text using currentText().
 *
 * By default the %ComboBox uses a WStringListModel to store the
 * items. This may be changed by specifying a custom model with
 * setModel().
 *
 * When the combobox is configured to be \link setEditable(bool)
 * editable\endlink, the user may enter a value which is not available
 * in the list of values. In that case, currentText() will hold this value,
 * while currentIndex() == -1 indicates that the value was not in the list.
 *
 * By default, all the data is downloaded to the client. This may be
 * changed using setDataLocation(DataLocation) to keep the data at hte
 * server, and download data on-demand. This is useful if you have a
 * very large list of options.
 *
 * The API is a superset of the WComboBox API.
 *
 * \image html ExtComboBox-1.png "ComboBox with expanded drop-down list."
 *
 * \ingroup ext
 * \ingroup modelview
 */
class WT_EXT_API ComboBox : public LineEdit
{
public:
  /*! \brief Create an empty combo-box with optional <i>parent</i>.
   */
  ComboBox(WContainerWidget *parent = 0);

  /*! \brief Add an option item.
   *
   * Equivalent to
   * \link insertItem(int, const WString&) insertItem \endlink (count(),
   * <i>text</i>).
   */
  void addItem(const WString& text);

  /*! \brief Get the number of items
   */
  int count() const;

  /*! \brief Get the currently selected item.
   *
   * If no item is currently selected, the method returns -1. <br> If
   * the combo-box is \link setEditable() editable\endlink and the
   * entered text does not match an existing item, then this method
   * also returns -1. Otherwise the index is returned of the first item
   * that matches the value of currentText().
   */
  int currentIndex() const;

  /*! \brief Insert an option item at the specified position.
   *
   * The option item is inserted at position <i>index</i>.
   */
  void insertItem(int index, const WString& text);

  /*! \brief Remove the option item at the specified position.
   *
   * The option item at position <i>index</i> will be removed.
   */
  void removeItem(int index);

  /*! \brief Change the current selection.
   *
   * Specify a value of -1 for <i>index</i> to clear the selection.
   */
  void setCurrentIndex(int index);

  /*! \brief Change the text for a specified option.
   *
   * Use this method to change the text for the item at position <i>index</i>.
   */
  void setItemText(int index, const WString& text);

  /*! \brief Get the text of the currently selected item.
   *
   * This retrieves the current text entered by the user. When the
   * combo-box is \link setEditable() editable\endlink, this may be
   * text that does not correspond to any of the items.
   */
  const WString currentText() const;

  /*! \brief Get the text of a particular item.
   *
   * Use this method to get the text for the item at position <i>index</i>.
   */
  const WString itemText(int index) const;

  /*! \brief Set the model to be used for the items.
   *
   * The <i>model</i> may not be 0, and ownership of the model is
   * not transferred.
   *
   * \sa setModelColumn(int)
   */
  void setModel(WAbstractItemModel *model);

  /*! \brief Set the column in the model to be used for the items.
   *
   * The column <i>index</i> in the model will be used to retrieve data.
   *
   * \sa setModel()
   */
  void setModelColumn(int index);

  /*! \brief Set if the combo-box is editable.
   *
   * An editable combo-box allows the user to specify a value that is not
   * part of the list of values.
   *
   * The default is false.
   *
   * \sa currentText()
   */
  void setEditable(bool how);

  /*! \brief Configure the delay to filter the list matching the current input.
   *
   * After the a delay of <i>milliSeconds</i> milli seconds, the list
   * items are filtered to match the currently entered text and the
   * drop-down list is updated. It is recommended to specify a
   * non-zero delay when using a Remote \link setDataLocation() data
   * location\endlink.
   *
   * The default is 0.
   *
   * \sa setMinQueryLength(int)
   */
  void setQueryDelay(int milliSeconds);

  /*! \brief Configure the minimum number of characters to be entered
   *         before filtering the drop-down list.
   *
   * Values in the drop-down list will only be filtered when the entered
   * text contains more that <i>numChars</i> characters.
   *
   * The default value is 4.
   *
   * \sa setQueryDelay(int)
   */ 
  void setMinQueryLength(int numChars);

  /*! \brief Configure a page size to show matching values one page at a time
   *
   * At most <i>pageSize</i> matches will be shown at the same time in the
   * combo-box.
   *
   * The default value is 0 (no paging).
   */
  void setPageSize(int pageSize);

  /*! \brief Configure the location of the data for the combo-box.
   *
   * When a ClientSide data location is specified, all data for the combo-box
   * is downloaded when the combo-box is created. Using a ServerSide data
   * location, data is downloaded to the client only when needed.
   *
   * The defualt value is ClientSide.
   */
  void setDataLocation(DataLocation dataLocation);

  void setLoadingText(const WString& text);

  virtual void refresh();

  /*! \brief Clear all items.
   */
  void clear();

public:
  /*! \brief %Signal emitted when the selection changed.
   */
  JSignal<int>& activated() { return activated_; }

protected:
  virtual void updateExt();
  virtual void createConfig(std::ostream& config);

private:
  JSignal<int>        activated_;
  DataLocation        dataLocation_;
  bool                editable_;
  int                 queryDelay_;
  int                 minQueryLength_;
  int                 pageSize_;
  WAbstractItemModel *model_;
  int                 modelColumn_;

  DataStore          *dataStore_;

  std::vector<boost::signals::connection> modelConnections_;

  virtual std::string createJS(DomElement *inContainer);

  void modelColumnsInserted(const WModelIndex& parent, int start, int end);
  void modelColumnsRemoved(const WModelIndex& parent, int start, int end);
  void modelRowsInserted(const WModelIndex& parent, int start, int end);
  void modelRowsRemoved(const WModelIndex& parent, int start, int end);
  void modelDataChanged(const WModelIndex& topLeft,
			const WModelIndex& bottomRight);
  void modelLayoutChanged();
};

  }
}

#endif // EXT_COMBOBOX_H_
