/*
 * Copyright (c) 1991 Stanford University
 * Copyright (c) 1991 Silicon Graphics, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software and 
 * its documentation for any purpose is hereby granted without fee, provided
 * that (i) the above copyright notices and this permission notice appear in
 * all copies of the software and related documentation, and (ii) the names of
 * Stanford and Silicon Graphics may not be used in any advertising or
 * publicity relating to the software without the specific, prior written
 * permission of Stanford and Silicon Graphics.
 * 
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
 *
 * IN NO EVENT SHALL STANFORD OR SILICON GRAPHICS BE LIABLE FOR
 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
 * OF THIS SOFTWARE.
 */

/*
 * InputHandler - glyph that handles input
 */

/*
 * $Id: input.C,v 1.15 1996/09/02 08:33:10 bmarsch Exp $
 *
 * $Log: input.C,v $
 * Revision 1.15  1996/09/02 08:33:10  bmarsch
 * Removed memory leak
 *
 * Revision 1.14  1996/05/28 15:17:02  bmarsch
 * Grab input before calling press (instead of after)
 *
 * Revision 1.13  1996/05/15 14:13:34  bmarsch
 * Bugfix in destructor
 *
 * Revision 1.12  1996/04/25 14:28:26  bmarsch
 * Bugfixes in next_focus(), prev_focus(), and check_enabled()
 *
 * Revision 1.11  1996/04/24 15:33:17  bmarsch
 * InputHandlers may be disabled
 *
 * Revision 1.10  1996/03/21 16:43:39  bmarsch
 * Bugfix: hierarchical input handling in focus()
 *
 * Revision 1.9  1996/03/01 13:53:37  bmarsch
 * Added Instrumentation
 *
 * Revision 1.8  1996/02/15 17:25:01  bmarsch
 * Includes from OS/ were moved to hyperg/OS/
 *
 * Revision 1.7  1996/02/15 17:18:11  bmarsch
 * Includes from Dispatch/ were moved to hyperg/Dispatch/
 *
 * Revision 1.6  1996/01/29 08:54:21  bmarsch
 * Bugfix in InputHandlerImpl::up()
 *
 * Revision 1.5  1996/01/26 15:14:21  bmarsch
 * Check if pointer hasn't been moved to far before calling double_click()
 *
 * Revision 1.4  1996/01/22 14:33:58  bmarsch
 * New X attribute "helpdelay" to change delay between
 * ActiveHandler::enter and Window::map
 *
 * Revision 1.3  1996/01/22 12:47:36  bmarsch
 * New (protected) member to unmap_help_window()
 *
 * Revision 1.2  1996/01/18 09:28:45  bmarsch
 * Changed default delay time
 *
 */

#include <InterViews/input.h>
#include <InterViews/alloctbl.h>
#include <InterViews/canvas.h>
#include <InterViews/color.h>
#include <InterViews/display.h>
#include <InterViews/event.h>
#include <InterViews/handler.h>
#include <InterViews/hit.h>
#include <InterViews/style.h>
#include <InterViews/session.h>
#include <InterViews/transformer.h>
#include <InterViews/window.h>
#include <IV-look/kit.h>
#include <hyperg/utils/str.h>
#include <hyperg/Dispatch/dispatcher.h>
#include <hyperg/OS/list.h>
#include <hyperg/OS/math.h>
#ifdef INSTRUMENTED
#include <InterViews/logging.h>
#endif

// <begin> Peter Kogler (7.9.1995): use windows to describe the
//                                  function of the active handler
//                                  (for example buttons!) if mouse stays
//                                  a while on the active handler
#include <InterViews/helpwindowaction.h>
implementIOCallback(ActiveHandler)

// <end>


declarePtrList(InputHandlerList,InputHandler)
implementPtrList(InputHandlerList,InputHandler)

class InputHandlerImpl : public Handler {
private:
    friend class InputHandler;

    InputHandlerImpl(InputHandler*, Style*);
    virtual ~InputHandlerImpl();

    InputHandler* input_;
    Style* style_;
    InputHandlerList children_;
    GlyphIndex focus_item_;
    InputHandler* focus_handler_;
    InputHandler* parent_;
    AllocationTable* allocations_;
    boolean pressed_ : 1;
    boolean recorded_time_ : 1;
    EventButton button_;
    unsigned long click_time_;
    Coord pointer_x_;
    Coord pointer_y_;

    virtual boolean event(Event&);

    AllocationInfo& info(Canvas*, const Allocation&);
    AllocationInfo* most_recent_info();
    void reset();
    void down(Event&);
    void motion(Event&);
    void up(Event&);
    boolean inside(const Event&, const AllocationInfo&);

    // bmarsch/jschipf: 24 Apr 96
    boolean enabled_;

    static unsigned long threshold_;
    static Coord pos_threshold_;
};

unsigned long InputHandlerImpl::threshold_ = 0;
Coord InputHandlerImpl::pos_threshold_ = 0;

InputHandler::InputHandler(Glyph* g, Style* s) : MonoGlyph(g) {
    impl_ = new InputHandlerImpl(this, s);
    Resource::ref(impl_);
    RString v;
    s->find_attribute("flat", v);
    Display* d = Session::instance()->default_display();
    const Color* c = Color::lookup(d, v);
    if (!c)
	c = new Color(0.7, 0.7, 0.7, 1.0);
    Resource::ref(c);
    disable_color_ = new Color(*c, 0.5);
    Resource::ref(disable_color_);
    Resource::unref(c);
}

InputHandler::~InputHandler() {
    AllocationInfo* info = impl_->most_recent_info();
    if (info && info->canvas() && info->canvas()->window() &&
        info->canvas()->window()->display()) {
        info->canvas()->window()->display()->ungrab(impl_, true);
    }
    Resource::unref(impl_);
    Resource::unref(disable_color_);
}

Handler* InputHandler::handler() const {
    return impl_;
}

InputHandler* InputHandler::parent() const {
    return impl_->parent_;
}

Style* InputHandler::style() const {
    return impl_->style_;
}

void InputHandler::append_input_handler(InputHandler* h) {
    if (h != nil) {
        impl_->children_.append(h);
        h->impl_->parent_ = this;
    }
}

void InputHandler::remove_input_handler(GlyphIndex index) {
    InputHandlerImpl& i = *impl_;
    InputHandlerList& list = i.children_;
    if (list.item(index) == i.focus_handler_) {
        next_focus();
        if (list.item(index) == i.focus_handler_) {
            i.focus_handler_ = nil;
        }
    }
    list.remove(index);
}

void InputHandler::remove_all_input_handlers() {
    InputHandlerImpl& i = *impl_;
    i.children_.remove_all();
    i.focus_handler_ = nil;
}

GlyphIndex InputHandler::input_handler_count() const {
    return impl_->children_.count();
}

InputHandler* InputHandler::input_handler(GlyphIndex index) const {
    return impl_->children_.item(index);
}

void InputHandler::focus (InputHandler* h)
{
  // bmarsch, jschipf 19960321:
  // make focus handling more hierarchical: allow h->focus(g) for any
  // g's and h's in the input handler hierarchy
  static boolean firsttime = true;
  if (h->impl_->parent_)  {
    if (firsttime) {   // first time: jump to correct branch
      firsttime = false;
      if (h->impl_->parent_ != this)   // prevent infinite loop
	h->impl_->parent_->focus(h);
    }
    else   // tell all parents recursively
      h->impl_->parent_->focus(this);
  }
  firsttime = true;

  InputHandlerImpl& i = *impl_;
  GlyphIndex n = i.children_.count ();
  for (GlyphIndex g = 0; g < n; g++)  // find index of new focus item
    if (i.children_.item (g) == h)
    {
      if (i.focus_handler_)
        i.focus_handler_->focus_out ();

      i.focus_item_ = g;
      i.focus_handler_ = h->focus_in ();
      break;
    } // if new focus item
}

void InputHandler::next_focus ()
{
  InputHandlerImpl* i = impl_;
  if (i->focus_handler_)
    i->focus_handler_->focus_out ();

  if (!i->enabled_) {
    // disabled
    if (i->parent_)
      i->parent_->next_focus();
    return;
  }

  GlyphIndex n = i->children_.count ();
  GlyphIndex old_focus_ = i->focus_item_;
  GlyphIndex f = i->focus_item_ + 1;  // new focus item
  while (f != old_focus_) {
    if (f >= n)                       // beyond last item
    {
      if (i->parent_)
      { i->parent_->next_focus ();
        return;
      }
      else if (n == 0)
        return;
      else  // circle to first item
        f = 0;
    }
    if (i->children_.item(f)->is_enabled() || n == 1)
      break;
    f++;
  }
  // no other enabled child
  if (f == old_focus_ && i->parent_) {
    i->parent_->next_focus();
    i->focus_handler_ = nil;
    i->focus_item_ = -1;
    return;
  }

  if (f == old_focus_ && !i->children_.item(f)->is_enabled()) {
    // everything is disabled and no parent
    i->focus_handler_ = nil;
    i->focus_item_ = -1;
    return;
  }

  i->focus_item_ = f;
  InputHandler* newfocus = i->children_.item (f);
  i->focus_handler_ = newfocus->focus_in ();

  // mpichler, 19941104: pass focus to deepest level
  // (in case of hierarchical input handlers)
  while (i = newfocus->impl_, i->children_.count ())
  {
    GlyphIndex j = 0;
    while (!i->children_.item(j)->is_enabled())
      j++;
    newfocus = i->children_.item (i->focus_item_ = j);
    i->focus_handler_ = newfocus->focus_in ();
  }
} // next_focus


void InputHandler::prev_focus ()
{
  InputHandlerImpl* i = impl_;
  if (i->focus_handler_)
    i->focus_handler_->focus_out ();

  if (!i->enabled_) {
    // disabled
    if (i->parent_)
      i->parent_->next_focus();
    return;
  }

  GlyphIndex n = i->children_.count();
  GlyphIndex old_focus_ = i->focus_item_;
  GlyphIndex f = i->focus_item_ - 1;
  while (f != old_focus_) {
    if (f < 0)
    {
      if (i->parent_)
      { i->parent_->prev_focus();
        return;
      }
      else if (n == 0)
        return;
      else  // circle to last item
        f = n - 1;
    }
    if (i->children_.item(f)->is_enabled() || n == 1)
      break;
    f--;
  }
  // no other enabled child
  if (f == old_focus_ && i->parent_) {
    i->parent_->prev_focus();
    i->focus_handler_ = nil;
    i->focus_item_ = -1;
    return;
  }

  if (f == old_focus_ && !i->children_.item(f)->is_enabled()) {
    // everything is disabled and no parent
    i->focus_handler_ = nil;
    i->focus_item_ = -1;
    return;
  }

  i->focus_item_ = f;
  InputHandler* newfocus = i->children_.item (f);
  i->focus_handler_ = newfocus->focus_in();

  // mpichler, 19941104: pass focus to deepest level
  // (in case of hierarchical input handlers)
  while (i = newfocus->impl_, n = i->children_.count ())
  {
    GlyphIndex j = n - 1;
    while (!i->children_.item(j)->is_enabled())
      j--;
    newfocus = i->children_.item (i->focus_item_ = j);
    i->focus_handler_ = newfocus->focus_in ();
  }
} // prev_focus

void InputHandler::allocate(Canvas* c, const Allocation& a, Extension& ext) {
    AllocationInfo& info = impl_->info(c, a);
    ext.merge(info.extension());
    allocation_changed(c, a);
}

void InputHandler::draw(Canvas* c, const Allocation& a) const {
    AllocationInfo& info = impl_->info(c, a);
    Glyph* g = body();
    if (g != nil && c->damaged(info.extension())) {
        g->draw(c, a);
        // bmarsch/jschipf: 24 Apr 96
        if (!impl_->enabled_)
          c->fill_rect(a.left(), a.bottom(), a.right(), a.top(), disable_color_);
    }
}

void InputHandler::pick(Canvas* c, const Allocation& a, int depth, Hit& h) {
  AllocationInfo& info = impl_->info(c, a);
  if (impl_->enabled_) {   // bmarsch/jschipf: 24 Apr 96
    const Event* e = h.event();
    EventType t = (e == nil) ? Event::undefined : e->type();
    switch (t) {
    case Event::key:
        if (impl_->inside(*e, info)) {
            InputHandler* ih = impl_->focus_handler_;
            InputHandlerImpl* handler = (ih == nil) ? impl_ : ih->impl_;
            h.target(depth, this, 0, handler); 
        }
        break;
    case Event::undefined:
    case Event::other_event:
        MonoGlyph::pick(c, a, depth, h);
        break;
    default:
        h.begin(depth, this, 0, impl_);
        MonoGlyph::pick(c, a, depth, h);
        h.end();
        break;
    }
  }
  else {
    Glyph::pick(c, a, depth, h);
  }
}

void InputHandler::undraw() {
    MonoGlyph::undraw();
    AllocationTable* table = impl_->allocations_;
    if (table != nil) {
        AllocationInfo* info = impl_->most_recent_info();
        if (info != nil) {
            Window* w = info->canvas()->window();
            if (w != nil) {
                w->display()->ungrab(impl_, true);
            }
        }
        table->flush();
    }
}

Canvas* InputHandler::canvas() const {
    AllocationInfo* info = impl_->most_recent_info();
    return info == nil ? nil : info->canvas();
}

const Transformer& InputHandler::transformer() const {
    return impl_->most_recent_info()->transformer();
}

const Allocation& InputHandler::allocation() const {
    return impl_->most_recent_info()->allocation();
}

void InputHandler::redraw() const {
    InputHandlerImpl& i = *impl_;
    AllocationInfo* info = i.most_recent_info();
    if (info != nil) {
        info->canvas()->damage(info->extension());
    }
}

void InputHandler::repick(int depth, Hit& h) {
    Canvas* c = canvas();
    if (c != nil) {
        const Transformer& t = transformer();
        c->push_transform();
        c->transformer(t);
        h.push_transform();
        h.transform(t);
        pick(c, allocation(), depth, h);
        h.pop_transform();
        c->pop_transform();
    }
}

void InputHandler::move(const Event&) { }
void InputHandler::press(const Event&) { }
void InputHandler::drag(const Event&) { }
void InputHandler::release(const Event&) { }

void InputHandler::keystroke(const Event& e) {
    InputHandlerImpl& i = *impl_;
    if (focus_handling()) {
        i.focus_handler_->keystroke(e);
    }
}

void InputHandler::double_click(const Event&) { }

InputHandler* InputHandler::focus_in ()
{
  return this;
}

void InputHandler::focus_out ()
{
  // mpichler, 19941104: when an input handler loses the focus,
  // all children must do also (if there are ones)
  InputHandler*& fhandler = impl_->focus_handler_;

  if (fhandler && fhandler != this)
  {
    fhandler->focus_out ();
    impl_->focus_item_ = -1;
    fhandler = 0;
  }
}

void InputHandler::allocation_changed(Canvas*, const Allocation&) { }

boolean InputHandler::inside(const Event& e) {
    InputHandlerImpl& i = *impl_;
    AllocationInfo* info = i.most_recent_info();
    return info != nil && i.inside(e, *info);
}

// bmarsch/jschipf: 24 Apr 96
void InputHandler::enable()
{
  InputHandlerImpl& i = *impl_;
  i.enabled_ = true;
  // pass to children
  GlyphIndex n = i.children_.count ();
  for (GlyphIndex g = 0; g < n; g++)
    i.children_.item(g)->enable();
  // pass to parent
  if (i.parent_)
    i.parent_->check_enabled();
  else {
    // pass focus to first child (recursive)
    InputHandlerImpl* impl = impl_;
    while (impl->children_.count())
      impl = impl->children_.item(0)->impl_;
    focus(impl->input_);
  }
  redraw();
}

// bmarsch/jschipf: 24 Apr 96
void InputHandler::disable()
{
  InputHandlerImpl& i = *impl_;
  i.enabled_ = false;
  // pass to children
  GlyphIndex n = i.children_.count ();
  for (GlyphIndex g = 0; g < n; g++)
    i.children_.item(g)->disable();
  // pass to parent
  if (i.parent_)
    i.parent_->check_enabled();

  if (i.parent_ && i.parent_->impl_->focus_handler_ == this)
    next_focus();
  focus_out();
  redraw();
}

// bmarsch/jschipf: 24 Apr 96
boolean InputHandler::is_enabled() const
{
  return impl_->enabled_;
}

// bmarsch 951006: access functions to InputHandlerImpl
InputHandler* InputHandler::focus_handler() const {
    return impl_->focus_handler_;
}
boolean InputHandler::focus_handling() const {
    InputHandlerImpl& i = *impl_;
    return (i.focus_item_ != -1) && i.focus_handler_;
}

void InputHandler::check_enabled()
{
  InputHandlerImpl& i = *impl_;
  if (i.parent_ || !i.children_.count()) {
    if (i.children_.count())
      i.enabled_ = false;
    // check children
    GlyphIndex n = i.children_.count ();
    for (GlyphIndex g = 0; g < n; g++) {
      if (i.children_.item(g)->is_enabled()) {
        i.enabled_ = true;
        break;
      }
    }
  }
  // pass to parent
  if (i.parent_)
    i.parent_->check_enabled();
}

/* class InputHandlerImpl */

InputHandlerImpl::InputHandlerImpl(InputHandler* h, Style* s) {
    input_ = h;
    Resource::ref(s);
    style_ = s;
    parent_ = nil;
    allocations_ = nil;
    focus_item_ = -1;
    focus_handler_ = nil;
    reset();
    if (threshold_ == 0) {
        long t = 250;
        s->find_attribute("clickDelay", t);
        threshold_ = t;
        double d = 5.0;
        s->find_attribute("clickDistance", d);
        pos_threshold_ = d;
    }
    pointer_x_ = pointer_y_ = -2 * pos_threshold_;
    enabled_ = true;
}

InputHandlerImpl::~InputHandlerImpl() {
    Resource::unref(style_);
    delete allocations_;
}

AllocationInfo& InputHandlerImpl::info(Canvas* c, const Allocation& a) {
    if (allocations_ == nil) {
        allocations_ = new AllocationTable(0, 1);
    }
    AllocationInfo* info = allocations_->find(c, a);
    if (info == nil) {
        /*
         * The need for this code is unfortunate.
         * The problem is that an input handler needs to ensure
         * that it ungrabs if a canvas/window is replaced
         * from the allocation table.  Perhaps there should be
         * a general-purpose interface for this from allocation table,
         * but for now we know InputHandler only keeps a single allocation.
         * So, before allocating a new one we check to see if there is
         * an old one that has a valid window.  Then we do the ungrab.
         * If we didn't do anything about the ungrab, then handler
         * might stay grabbed even when we forgot about the window.
         */
        AllocationInfo* old_info = allocations_->most_recent();
        if (old_info != nil) {
            Canvas* old_c = old_info->canvas();
            if (old_c != nil) {
                Window* old_w = old_c->window();
                if (old_w != nil && old_w != c->window()) {
                    old_w->display()->ungrab(this, true);
                }
            }
        }
        info = allocations_->allocate(c, a);
        Extension ext;
        ext.clear();
        input_->MonoGlyph::allocate(c, a, ext);
        info->extension(ext);
    }
    return *info;
}

AllocationInfo* InputHandlerImpl::most_recent_info() {
    AllocationTable* a = allocations_;
    if (a != nil) {
        AllocationInfo* info = a->most_recent();
        if (info != nil && info->canvas() != nil) {
            return info;
        }
    }
    return nil;
}

void InputHandlerImpl::reset() {
    pressed_ = false;
    recorded_time_ = false;
}

boolean InputHandlerImpl::event(Event& e) {
    boolean handled = true;
    switch (e.type()) {
    case Event::down:
        down(e);
        break;
    case Event::motion:
        motion(e);
        break;
    case Event::up:
        up(e);
        break;
    case Event::key:
        input_->keystroke(e);
        break;
    default:
        /* ignore */
        break;
    }
    return handled;
}


void InputHandlerImpl::down (Event& e)
{
  if (pressed_)  // multiclicks
    return;

  pressed_ = true;
  button_ = e.pointer_button();

  // bmarsch 28 May 96: grab before calling press
  e.grab (this);
  input_->press (e);

  if (parent_)  // tell my parent that I got the focus
  {
    // mpichler, 19941104: go up the hierarchy (as far as necessary)
    // and report the new focus item
    InputHandler* par = parent_;
    InputHandler* ihand = input_;
    while (par && par->impl_->focus_handler_ != ihand)
    {
      par->focus (ihand);
      ihand = par;
      par = par->impl_->parent_;
    }
  }
  else  // top level input handler
  {
    if (focus_handler_ != input_)
    {
      if (focus_handler_)
      {
        focus_handler_->focus_out ();
        focus_item_ = -1;
      }
      focus_handler_ = input_->focus_in ();
    }
  }
} // down


void InputHandlerImpl::motion(Event& e) {
    if (pressed_) {
        input_->drag(e);
    } else {
        input_->move(e);
    }
}

void InputHandlerImpl::up(Event& e) {
    if (pressed_ && e.pointer_button() == button_) {

        // get pointer coordinates (must be done before calling
        // InputHandler::release() because release might destroy the
        // window!)
        Coord px = e.pointer_root_x();
        Coord py = e.pointer_root_y();

        pressed_ = false;
        e.ungrab(this);
        input_->release(e);
        unsigned long t = e.time();

        //  kandrews 15 Mar 94
        //  sometimes events arrive in different time order, so take abs
        //  bmarsch: make position check before calling double_click
        if (recorded_time_ && Math::abs(long (t - click_time_)) < threshold_ &&
            Math::abs(double(px - pointer_x_)) < pos_threshold_ &&
            Math::abs(double(py - pointer_y_)) < pos_threshold_) {
#ifdef INSTRUMENTED
            Instrumentation::instance()->write_prefix(log_dclick);
#endif
	    input_->double_click(e);
	}

	click_time_ = t;
	recorded_time_ = true;

        // save pointer position for next up
        pointer_x_ = px;
        pointer_y_ = py;
    }
}

boolean InputHandlerImpl::inside(
    const Event& event, const AllocationInfo& info
) {
    Coord x = event.pointer_x();
    Coord y = event.pointer_y();
    Canvas* c = info.canvas();
    Window* w = c->window();
    if (w == nil || w != event.window()) {
	return false;
    }
    const Extension& e = info.extension();
    if (x < e.right() && x >= e.left() && y < e.top() && y >= e.bottom()) {
	const Transformer& t = info.transformer();
	Hit hit(&event);
	hit.transform(t);
	c->push_transform();
	c->transformer(t);
	input_->MonoGlyph::pick(c, info.allocation(), 0, hit);
	c->pop_transform();
	if (hit.any()) {
	    return true;
	}
    }
    return false;
}

/* class ActiveHandler */

ActiveHandler::ActiveHandler(Glyph* g, Style* s) : InputHandler(g, s) {
    inside_ = false;
// <begin> Peter Kogler (7.9.1995): use windows to describe the
//                                  function of the active handler
//                                  (for example buttons!) if mouse stays
//                                  a while on the active handler
    window_=nil;
    help_window_action_=nil;
    help_delay_handler_=nil;
    long l = 3;
    s->find_attribute("helpdelay", l);
    help_delay_=int(l);
// <end>
}

ActiveHandler::~ActiveHandler() {
// bmarsch/jschipf, 19950728
  Handler* h = handler ();
  if (h)
    Session::instance ()->default_display ()->ungrab (h);

// <begin> Peter Kogler (7.9.1995): use windows to describe the
//                                  function of the active handler
//                                  (for example buttons!) if mouse stays
//                                  a while on the active handler
  if (window_)
  {
    delete window_;
    window_=nil;
  }
  if (help_delay_handler_)
  {
    Dispatcher::instance().stopTimer(help_delay_handler_);
    delete help_delay_handler_;
  }
  Resource::unref(help_window_action_);

// <end>
}

void ActiveHandler::undraw() {
    if (inside_) {
	inside_ = false;
// <begin> Peter Kogler (7.9.1995): use windows to describe the
//                                  function of the active handler
//                                  (for example buttons!) if mouse stays
//                                  a while on the active handler
        if (window_)
	{
          window_->unmap();
	  delete window_;
	  window_=nil;
	}
	if (help_delay_handler_)
	  Dispatcher::instance().stopTimer(help_delay_handler_);
// <end>
	leave();
    }
    InputHandler::undraw();
}

void ActiveHandler::move(const Event& e) {
    Handler* h = handler();
    if (e.handler() == h) {
	if (!inside_) {
	    inside_ = true;
	    e.grab(h);
// <begin> Peter Kogler (7.9.1995): use BubbleWindows to describe the
//                                  function of the active handler
//                                  (for example buttons!) if mouse stays
//                                  a while on the active handler
            // start timer
            // do next in timer
            if (help_window_action_ && !style()->value_is_on("nobubbles"))
	    {
	      if (!help_delay_handler_)
		help_delay_handler_ =
		  new IOCallback(ActiveHandler)(this,&ActiveHandler::show_window);
	      Dispatcher::instance().startTimer((long)help_delay_,0,help_delay_handler_);
	    }
// <end>
	    enter();
	}
    } else if (inside_) {
	inside_ = false;
// <begin> Peter Kogler (7.9.1995): use windows to describe the
//                                  function of the active handler
//                                  (for example buttons!) if mouse stays
//                                  a while on the active handler
        unmap_help_window();
// <end>
	leave();
	e.ungrab(h);
    }
}

void ActiveHandler::drag(const Event& e) {
    move(e);
}

// <begin> Peter Kogler (7.9.1995): use windows to describe the
//                                  function of the active handler
//                                  (for example buttons!) if mouse stays
//                                  a while on the active handler
void ActiveHandler::show_window(long, long)
{
  if (help_window_action_)
  {
    window_=help_window_action_->execute();
    {
      window_->map();
    }
  }
}

void ActiveHandler::set_help_window_action(HelpWindowAction* helpwindowaction)
{
  Resource::ref(helpwindowaction);
  Resource::unref(help_window_action_);
  help_window_action_=helpwindowaction;
}
// <end>

void ActiveHandler::enter() { }
void ActiveHandler::leave() { }

void ActiveHandler::unmap_help_window()
{
  if (window_) {
    window_->unmap();
    delete window_;
    window_ = nil;
  }
  if (help_delay_handler_)
    Dispatcher::instance().stopTimer(help_delay_handler_);
}
