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

//<file>
//
// Name:    msgdlg.C
//
// Purpose: Implementation of class MessageDialog
//
// Created: 1 Feb 1996 Bernhard Marschall (with lots of stuff from mpichler)
//
// $Id: msgdlg.C,v 1.10 1996/07/11 15:04:22 bmarsch Exp $
//
// Description:
//   
//</file>
//
// $Log: msgdlg.C,v $
// Revision 1.10  1996/07/11 15:04:22  bmarsch
// Bugfix in build()
//
// Revision 1.9  1996/07/02 08:03:54  bmarsch
// Bugfix in destructor
//
// Revision 1.8  1996/06/27 14:19:38  bmarsch
// MessageDialog ref's/unref's itself when mapped/unmapped
//
// Revision 1.7  1996/03/20 16:50:53  bmarsch
// Remap positions window at previous position
//
// Revision 1.6  1996/02/06 16:25:32  bmarsch
// Use substyle MessageDialog (alias Dialog)
//
// Revision 1.5  1996/02/06 13:29:17  bmarsch
// Added member is_modified()
//
// Revision 1.4  1996/02/06 11:34:07  bmarsch
// Bugfix: ref TextBrowser after construction
//
// Revision 1.3  1996/02/06 08:50:05  bmarsch
// Added members is_mapped() and run()
//
// Revision 1.2  1996/02/01 14:30:14  bmarsch
// Added optional caption line
//
// Revision 1.1  1996/02/01 13:24:43  bmarsch
// Initial revision
//


static char rcsid[] = "$Id: msgdlg.C,v 1.10 1996/07/11 15:04:22 bmarsch Exp $";

#include "msgdlg.h"
#include "textb.h"
#include "wtranslate.h"
#include <InterViews/action.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/target.h>
#include <InterViews/window.h>
#include <IV-look/kit.h>
#include <IV-X11/xwindow.h>

#include <X11/keysym.h>

// ----------------------------------------------------------------------
// callback (for close button)

declareActionCallback(MessageDialog)
implementActionCallback(MessageDialog)

// ----------------------------------------------------------------------
// window handler

class MsgWinHandler : public Handler {
public:
  MsgWinHandler(MessageDialog* dlg) { dlg_ = dlg; }
  virtual boolean event(Event&) { dlg_->unmap(); return true; }

private:
  MessageDialog* dlg_;
};

// ----------------------------------------------------------------------
// TextBrowser: overloads keystroke to unmap the dialog on Return and
// Escape

class MsgTextBrowser : public TextBrowser {
public:
  MsgTextBrowser(WidgetKit&, MessageDialog* dlg);
  virtual void keystroke(const Event&);

private:
  MessageDialog* dlg_;
};

MsgTextBrowser::MsgTextBrowser(WidgetKit& k, MessageDialog* dlg)
: TextBrowser(k)
{
  dlg_ = dlg;
}

void MsgTextBrowser::keystroke(const Event& e)
{
  unsigned long sym = e.keysym();

  if (sym == XK_Return || sym == XK_Escape)
    // unmap dialog on Return or Escape
    dlg_->unmap();
  else
    // let TextBrowser handle all other keystrokes
    TextBrowser::keystroke(e);
}

// ----------------------------------------------------------------------

MessageDialog::MessageDialog(
  const char* title, HgLanguage::Language language, const char* caption
)
{
  init(nil, title, language, caption);
}

MessageDialog::MessageDialog(
  Style* style,  const char* title, HgLanguage::Language language, const char* caption
)
{
  init(style, title, language, caption);
}

void MessageDialog::init(
  Style* style, const char* title, HgLanguage::Language language, const char* caption
)
{
  // init variables
  text_ = nil;
  window_ = nil;
  mapped_ = false;
  modified_ = false;
  language_ = language;
  left_ = bottom_ = -1.0;

  // get WidgetKit
  WidgetKit& kit = *WidgetKit::instance();
  if (style) {
    style_ = style;
    kit.push_style();
    kit.style(style);
  }
  else {
    kit.begin_style("MessageDialog");
    kit.alias("Dialog");
    style_ = WidgetKit::instance()->style();
  }
  style_->ref();

  // set window title
  if (title && *title)
    style_->attribute("name", title);

  // create label for caption
  caption_ = caption ? kit.label(caption) : nil;
  Resource::ref(caption_);

  kit.pop_style();
}

MessageDialog::~MessageDialog()
{
  // cleanup
  Resource::unref(text_);
  Resource::unref(caption_);
  delete window_;
  window_ = nil;
  Resource::unref(style_);
}

void MessageDialog::map()
{
  if (!window_)  // first time: build window
    build();
  else if (left_ >= 0 && bottom_ >= 0)  // map window at previous position
    window_->place(left_, bottom_);

  // map window
  window_->map();
  mapped_ = true;
  // ref myself
  ref();
}

void MessageDialog::unmap()
{
  // unmap window if there's one already
  if (window_) {
    // save position
    left_ = window_->left();
    bottom_ = window_->bottom();
    // unmap
    window_->unmap();
    mapped_ = false;
    modified_ = false;
    // unref myself (deletes class if it isn't ref'ed outside!)
    unref_deferred();
  }
}

void MessageDialog::run()
{
  // map window
  map();

  // runloop until close button is pressed
  Session* session = Session::instance();
  Event event;
  while (!session->done() && mapped_) {
    session->read(event);
    // events are only handled if there's either a grabber or the
    // event occured in the dialog window
    if (event.grabber() || focus_->inside(event))
      event.handle();
    // all keyboard events are sent to the dialog
    else if (event.type() == Event::key)
      focus_->keystroke(event);
  }
}

void MessageDialog::appendMessage(const char* msg, boolean scrollToBottom)
{
  // build window (and browser) if there's none yet
  if (!text_)
    build();

  // append the lines
  text_->appendText(msg);
  modified_ = true;

  // scroll
  if (scrollToBottom) {
    text_->cursorPosition(text_->numLines()-1, 0, 0);
    text_->scrollToCursor();
  }
}

void MessageDialog::clear()
{
  // clear text browser
  if (text_) {
    text_->clearText();
    modified_ = true;
  }
}

TextBrowser* MessageDialog::browser() const
{
  // return text browser
  return text_;
}

void MessageDialog::build()
{
  WidgetKit& kit = *WidgetKit::instance();
  const LayoutKit& layout = *LayoutKit::instance();

  // set style
  kit.push_style();
  kit.style(style_);

  // make text browser
  text_ = new MsgTextBrowser(kit, this);
  text_->ref();

  // get default size of browser
  Coord hsize = 400;
  style_->find_attribute("xsize", hsize);
  Coord vsize = 500;
  style_->find_attribute("ysize", vsize);

  // build "main" glyph
  Glyph* glyph = new Background(
    layout.vbox(
      kit.outset_frame(
        layout.margin(
          layout.vbox(
            layout.margin(
              caption_ ? caption_ : layout.hglue(0),
              0, 0, 0, 0, fil, 0, 5, 0, 0, 0, 0, 0
            ),
            layout.natural(text_, hsize, vsize)
          ),
          15.0
        )
      ),
      kit.outset_frame(
        layout.margin(
          kit.default_button(
            WTranslate::str(WTranslate::CLOSE, language_),
            new ActionCallback(MessageDialog)(this, &MessageDialog::unmap)
          ),
          10, fil, 0, 10, fil, 0, 5, 0, 0, 5, 0, 0
        )
      )
    ),
    kit.background()
  );

  // keyboard handling
  focus_ = new InputHandler(
    new Target(glyph, TargetTransparentHit),
    style_
  );
  focus_->append_input_handler(text_);
  focus_->focus(text_);

  // create window
  window_ = new ApplicationWindow(focus_);
  window_->style(style_);
  window_->wm_delete(new MsgWinHandler(this));

  // reset style
  kit.pop_style();
}
