/* * 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 */ #include #include #include #include #include #include #include #include #include #include #include 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_; 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&); static unsigned long threshold_; }; unsigned long InputHandlerImpl::threshold_ = 0; InputHandler::InputHandler(Glyph* g, Style* s) : MonoGlyph(g) { impl_ = new InputHandlerImpl(this, s); Resource::ref(impl_); } InputHandler::~InputHandler() { AllocationInfo* info = impl_->most_recent_info(); if (info != nil) { info->canvas()->window()->display()->ungrab(impl_, true); } Resource::unref(impl_); } 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) { InputHandlerImpl& i = *impl_; GlyphIndex n = i.children_.count(); for (GlyphIndex g = 0; g < n; g++) { if (i.children_.item(g) == h) { if (i.focus_handler_ != nil) { i.focus_handler_->focus_out(); } i.focus_item_ = g; i.focus_handler_ = h->focus_in(); break; } } } void InputHandler::next_focus() { InputHandlerImpl& i = *impl_; if (i.focus_handler_ != nil) { i.focus_handler_->focus_out(); } GlyphIndex n = i.children_.count(); GlyphIndex f = i.focus_item_ + 1; if (f >= n) { if (i.parent_ != nil) { i.parent_->next_focus(); return; } else if (n == 0) { return; } else { f = 0; } } i.focus_item_ = f; i.focus_handler_ = i.children_.item(f)->focus_in(); } void InputHandler::prev_focus() { InputHandlerImpl& i = *impl_; if (i.focus_handler_ != nil) { i.focus_handler_->focus_out(); } GlyphIndex n = i.children_.count(); GlyphIndex f = i.focus_item_ - 1; if (f < 0) { if (i.parent_ != nil) { i.parent_->prev_focus(); return; } else if (n == 0) { return; } else { f = n - 1; } } i.focus_item_ = f; i.focus_handler_ = i.children_.item(f)->focus_in(); } 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); } } void InputHandler::pick(Canvas* c, const Allocation& a, int depth, Hit& h) { AllocationInfo& info = impl_->info(c, a); 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; } } 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::selection_notify(const Event&) { } void InputHandler::keystroke(const Event& e) { InputHandlerImpl& i = *impl_; if (i.focus_item_ != -1) { i.focus_handler_->keystroke(e); } } void InputHandler::double_click(const Event&) { } InputHandler* InputHandler::focus_in() { return this; } void InputHandler::focus_out() { } 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); } /* 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; } } 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; case Event::selection_notify: input_->selection_notify(e); break; default: /* ignore */ break; } return handled; } void InputHandlerImpl::down(Event& e) { if (!pressed_) { pressed_ = true; button_ = e.pointer_button(); input_->press(e); e.grab(this); if (parent_ != nil) { parent_->focus(input_); } else { if (focus_handler_ != input_) { if (focus_handler_ != nil) { focus_handler_->focus_out(); focus_item_ = -1; } focus_handler_ = input_->focus_in(); } } } } 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_) { pressed_ = false; e.ungrab(this); input_->release(e); unsigned long t = e.time(); if (recorded_time_ && t - click_time_ < threshold_) { input_->double_click(e); } click_time_ = t; recorded_time_ = true; } } 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; } ActiveHandler::~ActiveHandler() { } void ActiveHandler::undraw() { if (inside_) { inside_ = false; leave(); } InputHandler::undraw(); } void ActiveHandler::move(const Event& e) { Handler* h = handler(); if (e.handler() == h) { if (!inside_) { inside_ = true; e.grab(h); enter(); } } else if (inside_) { inside_ = false; leave(); e.ungrab(h); } } void ActiveHandler::drag(const Event& e) { move(e); } void ActiveHandler::enter() { } void ActiveHandler::leave() { }