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

#include <Wt/WObject>
#include <Wt/WSignal>

namespace Wt {

class WRadioButton;

  namespace Ext {
    class RadioButton;
  }

/*! \class WButtonGroup Wt/WButtonGroup Wt/WButtonGroup
 *  \brief A class for grouping radio buttons logically together.
 *
 * A button group manages a set of \link WRadioButton radio
 * buttons\endlink, making them exclusive of each other.
 *
 * It is not a widget, but instead provides only a logical
 * grouping. Radio buttons are aware of the group in which they have
 * been added, see WRadioButton::group(). When a button is deleted, it
 * is automatically removed its button group.
 *
 * It allows you to associate id's with each button, which you may use
 * to identify a particular button. The special value of -1 is
 * reserved to indicate <i>no button</i>.
 *
 * \if cpp
 * Usage example:
 * \code
 * enum Vote { Republican = 1, Democrate = 2, NoVote = 10 };
 *
 * // use a group box as widget container for 3 radio buttons, with a title
 * Wt::WGroupBox *container = new Wt::WGroupBox("USA elections vote");
 *
 * // use a button group to logically group the 3 options
 * Wt::WButtonGroup *group = new Wt::WButtonGroup(this);
 *
 * Wt::WRadioButton *button;
 * button = new Wt::WRadioButton("I voted Republican", container);
 * new Wt::WBreak(container);
 * group->addButton(button, Republican);

 * button = new Wt::WRadioButton("I voted Democrat", container);
 * new Wt::WBreak(container);
 * group->addButton(button, Democrate);

 * button = new Wt::WRadioButton("I didn't vote", container);
 * new Wt::WBreak(container);
 * group->addButton(button, NoVote);
 *
 * group->setCheckedButton(group->button(NoVote));
 * \endcode
 * \endif
 *
 * \if cpp
 * \sa WRadioButton, Ext::RadioButton
 * \elseif java
 * \sa WRadioButton
 * \endif
 */
class WT_API WButtonGroup : public WObject
{
public:
  /*! \brief Creates a new empty button group.
   */
  WButtonGroup(WObject* parent = 0);

  /*! \brief Destructor.
   *
   * This does not delete the radio buttons, but simply removes them
   * from the group.
   */
  ~WButtonGroup();

  /*! \brief Adds a radio button to the group.
   *
   * You can assign an id to the button. If \p id is -1, then a
   * unique id will be generated.
   *
   * \sa removeButton(WRadioButton *)
   */
  void addButton(WRadioButton *button, int id = -1);

#ifndef WT_TARGET_JAVA
  /*! \brief Adds a radio button to the group.
   *
   * You can assign an id to the button. If \p id is -1, then a
   * unique id will be generated.
   *
   * \sa removeButton(Ext::RadioButton *)
   */
  void addButton(Ext::RadioButton *button, int id = -1);
#endif // WT_TARGET_JAVA

  /*! \brief Removes a radio button from the group.
   *
   * \sa addButton(WRadioButton *, int)
   */
  void removeButton(WRadioButton *button);

  /*! \brief Removes a radio button from the group.
   *
   * \if cpp
   * \sa addButton(Ext::RadioButton *, int)
   * \endif
   */
  void removeButton(Ext::RadioButton *button);

  /*! \brief Returns the button for the given id.
   *
   * \sa id(), addButton()
   */
  WRadioButton *button(int id) const;

  /*! \brief Returns the id for the given button.
   *
   * \sa button(), addButton()
   */
  int id(WRadioButton *button) const;

  virtual const std::string id() const { return WObject::id(); }

  /*! \brief Returns the buttons in this group.
   */
  std::vector<WRadioButton *> buttons() const;

  /*! \brief Returns the number of radiobuttons in this group.
   */
  int count() const;

  /*! \brief Returns the id of the checked button.
   *
   * Returns the id of the currently checked button, or -1 if no button
   * is currently checked.
   */
  int checkedId() const;

  /*! \brief Sets the currently checked radiobutton.
   *
   * The button \p button of this group is checked. A value of \c 0
   * will uncheck all radiobuttons.
   *
   * Initially, no button is checked.
   *
   * \sa checkedId()
   */
  void setCheckedButton(WRadioButton *button);

  /*! \brief Returns the checked radiobutton.
   *
   * If there is no radiobutton currently checked this function
   * returns \c 0.
   *
   * \sa setCheckedButton(), selectedButtonIndex()
   */
  WRadioButton* checkedButton() const;

#ifndef WT_TARGET_JAVA
  WRadioButton* selectedButton() const; // deprecated
#endif // WT_TARGET_JAVA

  /*! \brief Sets the currently checked radiobutton.
   *
   * Sets the \p idx'th radiobutton checked. A value of -1 will
   * uncheck all radiobuttons.
   *
   * Initially, no button is checked.
   */
  void setSelectedButtonIndex(int idx);

  /*! \brief Returns the index of the checked radiobutton.
   *
   * The index reflects the order in which the buttons have been added
   * to the button group. Use checkedId() if you want to know the id
   * of the button that is currently checked. If there is no
   * radiobutton selected this function returns -1.
   *
   * \sa checkedId()
   */
  int selectedButtonIndex() const;

  /*! \brief %Signal emitted when a button was checked.
   *
   * The argument passed is the new checkedButton().
   */
  Signal<WRadioButton *>& checkedChanged();

private:
  struct Button {
    WRadioButton *button;
    int           id;
  };

  std::vector<Button> buttons_;
  Signal<WRadioButton *> checkedChanged_;
  bool                   checkedChangedConnected_;

  void uncheckOthers(WRadioButton *button);
  int  generateId() const;
  void onButtonChange();
  virtual void setFormData(const FormData& formData);

  friend class WRadioButton;
};

}

#endif // WBUTTONGROUP_H_
