//<copyright>
// 
// Copyright (c) 1993,94
// Institute for Information Processing and Computer Supported New Media (IICM),
// Graz University of Technology, Austria.
// 
//</copyright>

//<file>
//
// Name:        msgbox.C
//
// Purpose:     implementation of class MessageBox
//
// Created:     19 Jan 94   Michael Pichler
//
// Changed:     23 Jan 94   Michael Pichler
//
//
//</file>


#include "msgbox.h"
#include "wtranslate.h"
#include "glyphutil.h"

#include <IV-look/kit.h>

#include <InterViews/background.h>
#include <InterViews/event.h>
#include <InterViews/handler.h>
#include <InterViews/input.h>
#include <InterViews/layout.h>
#include <InterViews/session.h>
#include <InterViews/style.h>
#include <InterViews/window.h>
#include <InterViews/hit.h>

#include <IV-X11/xevent.h>
#include <IV-X11/xwindow.h>

#include <X11/keysym.h>

#include <string.h>
//#include <iostream.h>



// static members

WTranslate::StrSym MessageBox::label_ [MessageBox::NumDefined] =
{
  WTranslate::OK, WTranslate::YES, WTranslate::NO, WTranslate::CANCEL, WTranslate::HELP
};



/* class MsgBoxDelete: delete handler for messagebox */

class MsgBoxDelete: public Handler
{
  public:
    boolean event (Event& e);  // called when attemting to delete window
};


boolean MsgBoxDelete::event (Event&)
{
  return 0;  // ignore window delete
}



/* class MsgBoxAction: Action for message box buttons */

class MsgBoxAction: public Action
{
  public:
    MsgBoxAction (int& button,  // where to store which button was pressed
                  int value     // the number of the button
                 );

    void execute ();

  private:
    int& button_;
    int value_;
};


declareActionCallback(MsgBoxAction)
implementActionCallback(MsgBoxAction)


MsgBoxAction::MsgBoxAction (int& button, int value)
: button_ (button)
{
  value_ = value;
}


void MsgBoxAction::execute ()
{
  if (value_ == MessageBox::Help)  // call a help without closing the dialog box
    MessageBox::sorryNotImplemented (HgLanguage::Default, 0, "Help");
  else                             // report the pressed button
    button_ = value_;
}



/* class MsgBoxInput: InputHandler for message boxes */

class MsgBoxInput: public InputHandler
{
  public:
    MsgBoxInput (Glyph*, Style*,
       int buttons,                 // which buttons are valid
       int& button,                 // where to store which key was pressed
       int defbutton);              // the default button (return)

    void keystroke (const Event&);

  private:
    int buttons_;
    int& button_;
    int defbutton_;
};


MsgBoxInput::MsgBoxInput (Glyph* g, Style* s, int buttons, int& button, int defbutton)
: InputHandler (g, s),
  button_ (button)
{
  buttons_ = buttons;
  defbutton_ = defbutton;
}


void MsgBoxInput::keystroke (const Event& e)
{
  unsigned long keysym = e.keysym ();
  unsigned char key = (unsigned char) (keysym & 0xff);

//   cerr << "keystroke: symbol " << hex << keysym << endl;
//   cerr << "defbutton_: " << defbutton_ << endl;

  if (defbutton_)  // RETURN, ENTER: default button
  { if (key == '\r' || keysym == XK_KP_Enter)
      button_ = defbutton_;
  }
  if (buttons_ & MessageBox::Cancel)  // ESC, CANCEL: cancel
  { 
    if (key == '\x1b' || keysym == XK_Cancel)
      button_ = MessageBox::Cancel;
  }
  if (buttons_ & MessageBox::Help)  // F1, HELP: help
  { if (keysym == XK_F1 || keysym == XK_Help)
      MsgBoxAction (button_, MessageBox::Help).execute ();
  }

} // keystroke



/* implementation of class MessageBox */


void MessageBox::sorryNotImplemented (HgLanguage::Language language,
                                      Window* parentwin, const char* title)
{
  message (language, parentwin, "sorry, not implemented", title, Ok);
}



int MessageBox::message (HgLanguage::Language language, Window* parentwin,
                         const char* msg, const char* title,
                         int buttons, int defbutton)
{
  WidgetKit& kit = *WidgetKit::instance ();
  const LayoutKit& layout = *LayoutKit::instance ();

  kit.begin_style ("MessageBox", "Dialog");  // push new style "MessageBox"
  Style* style = kit.style ();

  if (title && *title)  // set window title
  { style->attribute ("name", title);
    style->attribute ("iconName", title);
  } // otherwise same title as parent window

  int vspace = 10, hspace = 20;  // might use X-attribute

  PolyGlyph* buttonbox = layout.hbox ();

  int retval = 0;  // return value: ID of pressed button

  if (!buttons)  // display at least one button
    buttons = Ok;

  if (!(defbutton & buttons))  // invalid default button
    defbutton = 0;

  buttonbox->append (layout.hglue (hspace));

  for (int i = 0;  i < NumDefined;  i++)
  {
    int button_i = 1 << i;
    if (buttons & button_i)
    {
      const char* str = WTranslate::str (label_ [i], language);
      Action* action = new MsgBoxAction (retval, button_i);
  
      if (button_i & defbutton)  // default button
      { buttonbox->append (kit.default_button (str, action));
        defbutton = button_i;  // prevent more than one default button
      }
      else
        buttonbox->append (kit.push_button (str, action));

      buttonbox->append (layout.hglue (hspace));
    }
  } // for i

  Glyph* label = multiLineLabel (msg);
  Glyph* vspc = layout.vglue (vspace, 0.5, 0);
  // must allow stretching a little bit to compensate for rounding errors
  // of nasty float coordinates (see also Tile::allocate)

  Glyph* body = layout.vbox (
    kit.outset_frame (layout.vbox (
      vspc,
      layout.hbox (
        layout.hglue (hspace),
        label,        // kit.label (msg),
        layout.hglue (hspace)
      ),
      vspc
    )),
    kit.outset_frame (layout.vbox (
      vspc,
      buttonbox,
      vspc
    ))
  );

  MsgBoxInput* ihandler = new MsgBoxInput (body, style, buttons, retval, defbutton);


  TransientWindow* win = new TransientWindow (ihandler);  // ApplicationWindow (ihandler);
  win->style (style);

  // set delete handler
  MsgBoxDelete* delhandler = new MsgBoxDelete;
  win->wm_delete (delhandler);

  if (parentwin)  // center atop primary window
  {
    win->transient_for (parentwin);
    win->place (parentwin->left () + 0.5 * parentwin->width (),
                parentwin->bottom () + 0.5 * parentwin->height ());
    win->align (0.5, 0.5);
  }

  win->map ();

  // event loop

  Session* s = Session::instance ();
  Event e;
  for (;;)
  {
    s->read (e);
    // mpichler, 19950311: pushbuttons are grabbing, so when still handling events of
    // grabbing handlers, another instance of the dialog could be created by accident
    if (/*e.grabber () ||*/ ihandler->inside (e))
    { // cerr << "grabber: " << (int) e.grabber () << ", inside: " << (int) ihandler->inside (e) << endl;
      e.handle ();
    }
    else if (e.type () == Event::key)
      ihandler->keystroke (e);
    if (retval)   // a button or key was pressed
      break;
    if (s->done ())  // this happens when the application window is deleted
    {
      retval = Cancel;
      break;
    }
  } // read events

  // close dialog window

  win->unmap ();
  win->unbind ();

  delete win;

  kit.end_style ();  // pop style "MessgeBox"

  return retval;

} // message
