// 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_DIALOG_H_
#define EXT_DIALOG_H_

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

namespace Wt {
  namespace Ext {

class Button;

/*! \class Dialog Wt/Ext/Dialog Wt/Ext/Dialog
 *  \brief A dialog emulates a modal window that blocks the user-interface.
 *
 * A modal window blocks the user interface, and does not allow the
 * user to interact with any other part of the user interface until
 * the dialog is closed.
 *
 * There are two ways for using a %Dialog window.
 *
 * The easiest way is using the exec() method: after creating a
 * %Dialog window, call the exec() method which blocks until
 * the dialog window is closed, and returns the dialog
 * result. Typically, an OK button will be connected to the accept()
 * slot, and a Cancel button to the reject() slot. This solution has
 * the drawback that it is not scalable to many concurrent sessions,
 * since every recursive event loop (which is running during the
 * exec() method) locks a thread. Therefore it is only suitable for
 * software that doesn't need to scale (to thousands of users).
 *
 * A second way is by treating the %Dialog as another widget. The
 * dialog may be closed by calling accept(), reject() or done() (or
 * connecting a signal to one of these methods). This will hide the
 * dialog and emit the finished() signal, which you then can listen
 * for to process the dialog result and delete the dialog. Unlike
 * other widgets, a dialog is hidden by default. You must use the
 * method show() or setHidden(true) to show the dialog.
 *
 * Since %Dialog is a Panel, the dialog contents may be layed out
 * inside the dialog using layout managers. To be compatible with
 * WDialog howevere, a contents() method is provided which creates a
 * WFitLayout that fits a single WContainerWidget widget inside the
 * dialog.
 *
 * Only one %Dialog window may exist at any time in a single
 * application. An attempt to instantiate a second dialog will result
 * in undefined behaviour.
 *
 * The API is a superset of the WDialog API:
 * <ul>
 *   <li>has additional methods to manage standard buttons
 *     (addButton(), removeButton(), buttons());</li> 
 *   <li>may be resized by the user (unless disabled using
 *     setSizeGripEnabled()).</li> 
 * </ul>
 *
 * \image html ExtDialog-1.png "An example Dialog using BorderLayouts"
 *
 * \ingroup ext
 */
class WT_EXT_API Dialog : public Panel
{
public:
  /*! \brief The result of a modal dialog execution.
   */
  enum DialogCode { Rejected, //!< Dialog closed with reject()
		    Accepted  //!< Dialog closed with accept()
  };

  /*! \brief Construct a %Dialog with a given window title.
   *
   * Only a single %Dialog may be constructed at any time. Unlike
   * other widgets, a dialog should not need be added to a container
   * widget to be displayed.
   */
  Dialog(const WString& windowTitle = WString());

  /*! \brief Destruct a %Dialog.
   */
  ~Dialog();

  /*! \brief Set the dialog window title.
   *
   * Is the same as Panel::setTitle(const WString&)
   */
  void setWindowTitle(const WString& windowTitle);

  /*! \brief Return the dialog window title.
   *
   * Is the same as Panel::title()
   */
  const WString& windowTitle() const { return Panel::title(); }

  /*! \brief Return the dialog contents container.
   *
   * The first invocation to this method creates a single
   * WContainerWidget that is fitted in the panel content area, like
   * this:
   * \code
   * WContainerWidget *contents = new WContainerWidget();
   * dialog->setLayout(new WFitLayout());
   * dialog->layout()->addWidget(contents);
   * \endcode
   */
  WContainerWidget *contents() const;

  /*! \brief Execute the dialog in a recursive event loop.
   *
   * Executes the dialog. This blocks the current thread of execution
   * until one of done(DialogCode), accept() or reject() is called.
   *
   * <i>Warning: using exec() does not scale to many concurrent
   * sessions, since the thread is locked.</i>
   *
   * \sa done(DialogCode r), accept(), reject()
   */
  DialogCode exec();

  /*! \brief Stop a recursive event loop.
   *
   * Sets the dialog result, and ends a recursive event loop that was
   * started using the exec() method.
   */
  virtual void done(DialogCode r);

  /*! \brief Stop a recursive event loop with result Accepted.
   *
   * \sa done(DialogCode), reject()
   */
  virtual void accept();

  /*! \brief Stop a recursive event loop with result Rejected.
   *
   * \sa done(DialogCode), accept()
   */
  virtual void reject();

  /*! \brief %Signal emitted when the recursive event loop is ended.
   *
   * \sa done(DialogCode), accept(), reject()
   */
  Signal<DialogCode>& finished() { return finished_; }

  /*! \brief Return the result that was set for this dialog.
   *
   * \sa done(DialogCode)
   */
  DialogCode result() const { return result_; }

  virtual void setHidden(bool hidden,
			 const WAnimation& animation = WAnimation());

  /*! \brief Add a button at the bottom of this dialog.
   *
   * Is the same as Panel::addFooterButton()
   */
  void addButton(Button *button);

  /*! \brief Remove a button from the bottom of this dialog.
   *
   * The <i>button</i> must have been previously added using addButton().
   * Is the same as Panel::removeFooterButton()
   */
  void removeButton(Button *button);

  /*! \brief Return the list of buttons at the bottom of this dialog.
   *
   * Is the same as Panel::footerButtons()
   */
  const std::vector<Button *>& buttons()
    const { return Panel::footerButtons(); }

  /*! \brief Configure a default button for this dialog.
   *
   * The <i>button</i> must have been previously added using addButton().
   * A default button is activated when the user presses Return in the
   * dialog.
   *
   * \sa Button::setDefault()
   */
  void setDefaultButton(Button *button);

  /*! \brief Return the default button for this dialog.
   *
   * \sa setDefaultButton(), Button::isDefault()
   */
  Button *defaultButton() const;

  /*! \brief Configure a size grip to allow the user to resize this dialog.
   *
   * When a size grip is enabled, then the user may resize the dialog window.
   *
   * The default is <i>true</i>.
   */
  void setSizeGripEnabled(bool enabled);

  /*! \brief Return if the size grip is enabled.
   *
   * \sa setSizeGripEnabled()
   */
  bool isSizeGripEnabled() const { return sizeGripEnabled_; }

private:
  Signal<DialogCode>    finished_;
  WContainerWidget     *contents_;
  bool                  sizeGripEnabled_;

  DialogCode result_;
  bool       recursiveEventLoop_;

  JSignal<void> hiddenS_;

  std::string createJS(DomElement *inContainer);
  void        wasHidden();

protected:
  virtual void createConfig(std::ostream& config);
  virtual std::string extClassName() const;

  class Bla { };

  Dialog(Bla);

  void setExposeMask(WApplication *app);
  void restoreExposeMask(WApplication *app);

  bool hidden_;
};

  }
}

#endif // EXT_DIALOG_H_
