/* * Copyright (c) 1987, 1988, 1989, 1990, 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. */ /* * Implementation of the base class of interaction. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Interactor::Interactor() { Init(); } Interactor::Interactor(const char* name) { Init(); SetInstance(name); } void Interactor::Init() { shape = new Shape; canvas = nil; window = nil; perspective = nil; xmax = 0; ymax = 0; input = nil; output = nil; classname = nil; instance = nil; style = new Style; Resource::ref(style); parent = nil; cursensor = nil; world = nil; insert_window = nil; managed_window = nil; cursor_ = nil; canvas_type_ = CanvasInputOutput; handler_ = new InteractorHandler(this); Resource::ref(handler_); ref(); } Interactor::~Interactor() { Resource::unref(input); Resource::unref(output); delete window; delete shape; Resource::unref(style); Resource::unref(handler_); } World* Interactor::GetWorld() const { return world; } void Interactor::Flush() { GetWorld()->Flush(); } void Interactor::Sync() { GetWorld()->Sync(); } /* * The implementation of Check must closely match the implementation * of Read because we want Check returning true to imply that a subsequent * Read will not block. */ boolean Interactor::Check() { Event e; e.display(world->display()); e.target = nil; do { if (!e.pending()) { return false; } e.read(); } while (e.handler() == nil || e.target == nil); e.unread(); return true; } class InteractorHelper : public Interactor { public: InteractorHelper(); virtual ~InteractorHelper(); static InteractorHelper* instance(Handler*); virtual void Handle(Event&); private: static InteractorHelper* instance_; static Handler* handler_; }; InteractorHelper* InteractorHelper::instance_; Handler* InteractorHelper::handler_; InteractorHelper::InteractorHelper() { } InteractorHelper::~InteractorHelper() { } InteractorHelper* InteractorHelper::instance(Handler* h) { if (instance_ == nil) { instance_ = new InteractorHelper; } handler_ = h; return instance_; } void InteractorHelper::Handle(Event& e) { Handler* h = handler_; Resource::ref(h); h->event(e); Resource::unref(h); } void Interactor::Read(Event& e) { e.display(world->display()); e.target = nil; Handler* h = nil; while (!world->done()) { e.read(); if (Event::event_tracker()) (*Event::event_tracker())(e); h = e.handler(); Resource::ref(h); if (e.target != nil) { Resource::unref(h); break; } if (h != nil && !e.is_grabbing(h)) { e.GetInfo(); e.target = InteractorHelper::instance(h); e.y = ymax - e.y; break; } Resource::unref(h); } } boolean Interactor::Read(long sec, long usec, Event& e) { e.display(world->display()); e.target = nil; while (!world->done() && e.read(sec, usec)) { if (Event::event_tracker()) (*Event::event_tracker())(e); Handler* h = e.handler(); if (e.target != nil) { return true; } if (h != nil && !e.is_grabbing(h)) { e.GetInfo(); e.target = InteractorHelper::instance(h); e.y = ymax - e.y; return true; } } return false; } void Interactor::UnRead(Event& e) { e.unread(); } void Interactor::Run() { Event e; do { Read(e); if (world->done()) { break; } e.target->Handle(e); } while (e.target != nil); } void Interactor::QuitRunning(Event& e) { e.target = nil; if (world != nil) { world->quit(); } } /* * Set (x,y) for a given alignment of a shape within a canvas. */ void Interactor::Align( Alignment a, int width, int height, IntCoord& l, IntCoord& b ) const { switch (a) { case Left: case TopLeft: case CenterLeft: case BottomLeft: l = 0; break; case TopCenter: case Center: case BottomCenter: case HorizCenter: l = (xmax + 1 - width) / 2; break; case Right: case TopRight: case CenterRight: case BottomRight: l = xmax + 1 - width; break; case Bottom: case Top: case VertCenter: /* leave unchanged */ break; } switch (a) { case Bottom: case BottomLeft: case BottomCenter: case BottomRight: b = 0; break; case CenterLeft: case Center: case CenterRight: case VertCenter: b = (ymax + 1 - height) / 2; break; case Top: case TopLeft: case TopCenter: case TopRight: b = ymax + 1 - height; break; case Left: case Right: case HorizCenter: /* leave unchanged */ break; } } void Interactor::Adjust(Perspective&) { /* default is to ignore */ } void Interactor::Update() { /* default is to ignore */ } /* * Start a configuration traversal of an interactor hierarchy, * assigning parents, output painters, and calling Reconfig * for each interactor. * * This routine only works correctly when "s" is a world. * In any other case, it correctly assigns parents but does not * set up the property path for attribute lookup. It should go up * from "s" to the world, then come down pushing property directories * in the same manner as DoConfig. Instead of passing rootReversed * to DoConfig, it should compute "reverseVideo" for "s" and * pass whether it is on or not. */ void Interactor::Config(Scene* s) { if (parent != s) { if (parent != nil) { parent->Remove(this); } parent = s; /* cast to workaround DEC C++ compiler bug */ world = ((Interactor*)s)->world; DoConfig(false); } } void Interactor::Config(World* w) { if (parent != nil) { parent->Remove(this); parent = nil; } world = w; Resource::unref(output); output = nil; DoConfig(false); } /* * Configure an interactor. This implies first configuring * all of its children, then calling the Reconfig virtual. * We automatically setup the interactor's painter and check * for any local properties before calling Reconfig. * * DoConfig is passed a flag indicating whether this interactor's * parent painter has had its foreground and background colors reversed. * We copy this into a local that DefaultConfig will modify * if a reverseVideo attribute is specified that swaps the colors. * A swap occurs if either reversed is false and reverseVideo:on is found * or reversed is true and reverseVideo:off is found. */ void Interactor::DoConfig(boolean parentReversed) { boolean reversed = parentReversed; if (parent != nil) { /* cast to workaround DEC C++ compiler bug */ output = ((Interactor*)parent)->output; } DefaultConfig(reversed); Resource::ref(output); Interactor* children[100]; Interactor** a; int n; GetComponents(children, sizeof(children) / sizeof(Interactor*), a, n); if (n > 0) { for (Interactor** ii = a; ii < &a[n]; ii++) { Interactor* i = *ii; i->parent = (Scene*)this; i->world = world; i->DoConfig(reversed); } if (a != children) { delete a; } } Reconfig(); } /* * Setup an interactor's painter. If the style hasn't changed from * its parent, then just use the parent's painter. */ void Interactor::DefaultConfig(boolean& reversed) { if (parent == nil) { world->display()->style()->append(style); } else { /* cast to workaround DEC C++ compiler bug */ ((Interactor*)parent)->style->append(style); } const Font* f = nil; const Color* fg = nil; const Color* bg = nil; Display* d = world->display(); String v; if (style->find_attribute("font", v) || style->find_attribute("Font", v)) { f = Font::lookup(v); } if (style->find_attribute("foreground", v) || style->find_attribute("Foreground", v) ) { fg = Color::lookup(d, v); } if (style->find_attribute("background", v) || style->find_attribute("Background", v) ) { bg = Color::lookup(d, v); } if (reversed) { const Color* c = fg; fg = bg; bg = c; } boolean swap_colors = false; String rv; if (style->find_attribute("reverseVideo", rv)) { if (rv.case_insensitive_equal("on")) { if (!reversed) { swap_colors = true; reversed = true; } } else if (rv.case_insensitive_equal("off")) { if (reversed) { swap_colors = true; reversed = false; } } else { /* error message? */ } } if (output == nil) { output = new Painter; } else if (!swap_colors && f == output->GetFont() && fg == output->GetFgColor() && bg == output->GetBgColor() ) { return; } else { output = new Painter(output); } if (f != nil) { output->SetFont(f); } if (swap_colors) { const Color* c = fg; fg = bg; bg = c; } output->SetColors(fg, bg); } /* * Retrieve an attribute value using the interactor's style. */ const char* Interactor::GetAttribute(const char* name) const { String v; if (style->find_attribute(name, v) || ( style->parent() == nil && World::current()->display()->style()->find_attribute(name, v) ) ) { return v.string(); } return nil; } /* * Short-hand for testing if an attribute is "on" or "true". */ boolean Interactor::AttributeIsSet(const char* name) const { String v; return ( style->value_is_on(name) || ( style->parent() == nil && !style->find_attribute(name, v) && World::current()->display()->style()->value_is_on(name) ) ); } /* * Given (x,y) relative to this, translate them to (x',y') that are * relative to the interactor "rel". If "rel" is nil, then make * (x',y') relative to the screen. */ void Interactor::GetRelative(IntCoord& x, IntCoord& y, Interactor* rel) const { /* make (x,y) screen-relative */ IntCoord left, bottom; GetPosition(left, bottom); x += left; y += bottom; if (rel != nil) { /* subtract position of other interactor */ IntCoord rel_left, rel_bottom; rel->GetPosition(rel_left, rel_bottom); x -= rel_left; y -= rel_bottom; } } /* * Return coordinates relative to the world. The given world * isn't relevant; it only makes sense for backward compatibility * (for the days when World was a subclass of Scene). */ void Interactor::GetRelative(IntCoord& x, IntCoord& y, World*) const { IntCoord left, bottom; GetPosition(left, bottom); x += left; y += bottom; } void Interactor::Orphan() { delete window; window = nil; canvas = nil; } void Interactor::GetComponents(Interactor**, int, Interactor**&, int& n) { n = 0; } void Interactor::Draw() { if (canvas != nil && canvas->status() != Canvas::unmapped) { Redraw(0, 0, xmax, ymax); } } void Interactor::Activate() { } void Interactor::Deactivate() { } void Interactor::Handle(Event&) { } void Interactor::Highlight(boolean) { } void Interactor::Reconfig() { } void Interactor::Redraw(IntCoord, IntCoord, IntCoord, IntCoord) { } void Interactor::Resize() { } /* * Default is to redraw each area separately. */ void Interactor::RedrawList( int n, IntCoord left[], IntCoord bottom[], IntCoord right[], IntCoord top[] ) { register int i; for (i = 0; i < n; i++) { Redraw(left[i], bottom[i], right[i], top[i]); } } /* * Default is to accept a new shape completely and * notify the parent (if set). */ void Interactor::Reshape(Shape& ns) { *shape = ns; if (parent != nil) { parent->Change(this); } } void Interactor::SetClassName(const char* s) { if (s != nil) { style->alias(String(s)); } classname = s; } const char* Interactor::GetClassName() const { return classname; } void Interactor::SetInstance(const char* s) { if (s != nil) { style->name(String(s)); } instance = s; } const char* Interactor::GetInstance() const { return instance; } void Interactor::SetCursor(Cursor* c) { cursor_ = c; if (canvas != nil) { window->cursor(c); } } Cursor* Interactor::GetCursor() const { return cursor_; } Canvas* Interactor::GetCanvas() const { return canvas; } /* * Set the type of canvas for save under, backing store, etc. * This operation has no effect until the next time the canvas * is explicitly mapped. */ void Interactor::SetCanvasType(CanvasType ct) { canvas_type_ = ct; } CanvasType Interactor::GetCanvasType() const { return canvas_type_; } ManagedWindow* Interactor::GetTopLevelWindow() const { return managed_window; }