use ObjectURI more consistently
[gnash.git] / libcore / DisplayObject.cpp
blob59c12ba5802b4bff8d80f7f7a3e94acfe302d8ee
1 // DisplayObject.cpp: ActionScript DisplayObject class, for Gnash.
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
4 // Foundation, Inc
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 #ifdef HAVE_CONFIG_H
22 #include "gnashconfig.h" // USE_SWFTREE
23 #endif
25 #include "DisplayObject.h"
27 #include <boost/algorithm/string/case_conv.hpp>
28 #include <boost/assign/list_of.hpp>
29 #include <boost/bind.hpp>
30 #include <utility>
32 #include "smart_ptr.h" // GNASH_USE_GC
33 #include "movie_root.h"
34 #include "MovieClip.h"
35 #include "VM.h"
36 #include "fn_call.h"
37 #include "GnashException.h"
38 #include "ExecutableCode.h"
39 #include "namedStrings.h"
40 #include "GnashEnums.h"
41 #include "GnashNumeric.h"
42 #include "Global_as.h"
43 #include "Renderer.h"
44 #include "GnashAlgorithm.h"
45 #ifdef USE_SWFTREE
46 # include "tree.hh"
47 #endif
49 #undef set_invalidated
51 namespace gnash
54 // Forward declarations.
55 namespace {
56 /// Match blend modes.
57 typedef std::map<DisplayObject::BlendMode, std::string> BlendModeMap;
58 const BlendModeMap& getBlendModeMap();
59 bool blendModeMatches(const BlendModeMap::value_type& val,
60 const std::string& mode);
62 typedef as_value(*Getter)(DisplayObject&);
63 typedef void(*Setter)(DisplayObject&, const as_value&);
64 typedef std::pair<Getter, Setter> GetterSetter;
66 bool doSet(const ObjectURI& uri, DisplayObject& o, const as_value& val);
67 bool doGet(const ObjectURI& uri, DisplayObject& o, as_value& val);
68 const GetterSetter& getGetterSetterByIndex(size_t index);
70 // NOTE: comparison will be case-insensitive
71 const GetterSetter& getGetterSetterByURI(const ObjectURI& uri,
72 string_table& st);
74 // Convenience function to create a const URI-to-function map
75 template<typename Map> const Map getURIMap(
76 const typename Map::key_compare& cmp);
79 // Define static const members.
80 const int DisplayObject::lowerAccessibleBound;
81 const int DisplayObject::upperAccessibleBound;
82 const int DisplayObject::staticDepthOffset;
83 const int DisplayObject::removedDepthOffset;
84 const int DisplayObject::noClipDepthValue;
86 DisplayObject::DisplayObject(movie_root& mr, as_object* object,
87 DisplayObject* parent)
89 GcResource(mr.gc()),
90 _name(),
91 _parent(parent),
92 _object(object),
93 _stage(mr),
94 _xscale(100),
95 _yscale(100),
96 _rotation(0),
97 _depth(0),
98 _volume(100),
99 _ratio(0),
100 m_clip_depth(noClipDepthValue),
101 _mask(0),
102 _maskee(0),
103 _blendMode(BLENDMODE_NORMAL),
104 _visible(true),
105 _scriptTransformed(false),
106 _dynamicallyCreated(false),
107 _unloaded(false),
108 _destroyed(false),
109 _invalidated(true),
110 _child_invalidated(true)
112 assert(m_old_invalidated_ranges.isNull());
114 // This informs the core that the object is a DisplayObject.
115 if (_object) _object->setDisplayObject(this);
118 void
119 DisplayObject::getLoadedMovie(Movie* extern_movie)
121 LOG_ONCE(
122 log_unimpl("loadMovie against a %s DisplayObject", typeName(*this))
125 // TODO: look at the MovieClip implementation, but most importantly
126 // test all the event handlers copies etc..
128 UNUSED(extern_movie);
131 ObjectURI
132 DisplayObject::getNextUnnamedInstanceName()
134 assert(_object);
135 movie_root& mr = getRoot(*_object);
136 std::ostringstream ss;
137 ss << "instance" << mr.nextUnnamedInstance();
139 VM& vm = getVM(*_object);
140 return getURI(vm, ss.str(), true);
145 DisplayObject::getWorldVolume() const
147 int volume=_volume;
148 if (_parent != NULL)
150 volume = int(volume*_parent->getVolume()/100.0);
153 return volume;
157 as_object*
158 DisplayObject::pathElement(const ObjectURI& uri)
160 as_object* obj = getObject(this);
161 if (!obj) return 0;
163 string_table::key key = getName(uri);
165 string_table& st = stage().getVM().getStringTable();
167 // TODO: put ".." and "." in namedStrings
168 if (key == st.find("..")) return getObject(parent());
169 if (key == st.find(".")) return obj;
171 // The check is case-insensitive for SWF6 and below.
172 // TODO: cache ObjectURI(NSV::PROP_THIS) [as many others...]
173 if (ObjectURI::CaseEquals(st, caseless(*obj))
174 (uri, ObjectURI(NSV::PROP_THIS))) {
175 return obj;
177 return 0;
180 void
181 DisplayObject::set_invalidated()
183 set_invalidated("unknown", -1);
186 void
187 DisplayObject::set_invalidated(const char* debug_file, int debug_line)
189 // Set the invalidated-flag of the parent. Note this does not mean that
190 // the parent must re-draw itself, it just means that one of it's childs
191 // needs to be re-drawn.
192 if ( _parent ) _parent->set_child_invalidated();
194 // Ok, at this point the instance will change it's
195 // visual aspect after the
196 // call to set_invalidated(). We save the *current*
197 // position of the instance because this region must
198 // be updated even (or first of all) if the DisplayObject
199 // moves away from here.
201 if ( ! _invalidated )
203 _invalidated = true;
205 #ifdef DEBUG_SET_INVALIDATED
206 log_debug("%p set_invalidated() of %s in %s:%d",
207 (void*)this, get_name(), debug_file, debug_line);
208 #else
209 UNUSED(debug_file);
210 UNUSED(debug_line);
211 #endif
213 // NOTE: the SnappingRanges instance used here is not initialized by the
214 // GUI and therefore uses the default settings. This should not be a
215 // problem but special snapping ranges configuration done in gui.cpp
216 // is ignored here...
218 m_old_invalidated_ranges.setNull();
219 add_invalidated_bounds(m_old_invalidated_ranges, true);
224 void
225 DisplayObject::add_invalidated_bounds(InvalidatedRanges& ranges, bool force)
227 ranges.add(m_old_invalidated_ranges);
228 if (visible() && (_invalidated||force))
230 SWFRect bounds;
231 bounds.expand_to_transformed_rect(getWorldMatrix(*this), getBounds());
232 ranges.add(bounds.getRange());
236 void
237 DisplayObject::set_child_invalidated()
239 if ( ! _child_invalidated )
241 _child_invalidated=true;
242 if ( _parent ) _parent->set_child_invalidated();
246 void
247 DisplayObject::extend_invalidated_bounds(const InvalidatedRanges& ranges)
249 set_invalidated(__FILE__, __LINE__);
250 m_old_invalidated_ranges.add(ranges);
253 as_value
254 DisplayObject::blendMode(const fn_call& fn)
256 DisplayObject* ch = ensure<IsDisplayObject<> >(fn);
258 // This is AS-correct, but doesn't do anything.
259 // TODO: implement in the renderers!
260 LOG_ONCE(log_unimpl(_("blendMode")));
262 if (!fn.nargs)
264 // Getter
265 BlendMode bm = ch->getBlendMode();
267 /// If the blend mode is undefined, it doesn't return a string.
268 if (bm == BLENDMODE_UNDEFINED) return as_value();
270 std::ostringstream blendMode;
271 blendMode << bm;
272 return as_value(blendMode.str());
276 // Setter
279 const as_value& bm = fn.arg(0);
281 // Undefined argument sets blend mode to normal.
282 if (bm.is_undefined()) {
283 ch->setBlendMode(BLENDMODE_NORMAL);
284 return as_value();
287 // Numeric argument.
288 if (bm.is_number()) {
289 double mode = toNumber(bm, getVM(fn));
291 // Hardlight is the last known value. This also performs range checking
292 // for float-to-int conversion.
293 if (mode < 0 || mode > BLENDMODE_HARDLIGHT) {
295 // An invalid numeric argument becomes undefined.
296 ch->setBlendMode(BLENDMODE_UNDEFINED);
298 else {
299 /// The extra static cast is required to keep OpenBSD happy.
300 ch->setBlendMode(static_cast<BlendMode>(static_cast<int>(mode)));
302 return as_value();
305 // Other arguments use toString method.
306 const std::string& mode = bm.to_string();
308 const BlendModeMap& bmm = getBlendModeMap();
309 BlendModeMap::const_iterator it = std::find_if(bmm.begin(), bmm.end(),
310 boost::bind(blendModeMatches, _1, mode));
312 if (it != bmm.end()) {
313 ch->setBlendMode(it->first);
316 // An invalid string argument has no effect.
318 return as_value();
322 void
323 DisplayObject::set_visible(bool visible)
325 if (_visible != visible) set_invalidated(__FILE__, __LINE__);
327 // Remove focus from this DisplayObject if it changes from visible to
328 // invisible (see Selection.as).
329 if (_visible && !visible) {
330 assert(_object);
331 movie_root& mr = getRoot(*_object);
332 if (mr.getFocus() == this) {
333 mr.setFocus(0);
336 _visible = visible;
339 void
340 DisplayObject::setWidth(double newwidth)
342 const SWFRect& bounds = getBounds();
343 const double oldwidth = bounds.width();
344 assert(oldwidth >= 0);
346 const double xscale = oldwidth ? (newwidth / oldwidth) : 0;
347 const double rotation = _rotation * PI / 180.0;
349 SWFMatrix m = getMatrix(*this);
350 const double yscale = m.get_y_scale();
351 m.set_scale_rotation(xscale, yscale, rotation);
352 setMatrix(m, true);
355 as_value
356 getHeight(DisplayObject& o)
358 SWFRect bounds = o.getBounds();
359 const SWFMatrix m = getMatrix(o);
360 m.transform(bounds);
361 return twipsToPixels(bounds.height());
364 void
365 setHeight(DisplayObject& o, const as_value& val)
367 const double newheight = pixelsToTwips(toNumber(val, getVM(*getObject(&o))));
368 if (newheight <= 0) {
369 IF_VERBOSE_ASCODING_ERRORS(
370 log_aserror(_("Setting _height=%g of DisplayObject %s (%s)"),
371 newheight / 20, o.getTarget(), typeName(o));
374 o.setHeight(newheight);
377 void
378 DisplayObject::setHeight(double newheight)
380 const SWFRect& bounds = getBounds();
382 const double oldheight = bounds.height();
383 assert(oldheight >= 0);
385 const double yscale = oldheight ? (newheight / oldheight) : 0;
386 const double rotation = _rotation * PI / 180.0;
388 SWFMatrix m = getMatrix(*this);
389 const double xscale = m.get_x_scale();
390 m.set_scale_rotation(xscale, yscale, rotation);
391 setMatrix(m, true);
394 void
395 DisplayObject::setMatrix(const SWFMatrix& m, bool updateCache)
398 if (m == _transform.matrix) return;
400 //log_debug("setting SWFMatrix to: %s", m);
401 set_invalidated(__FILE__, __LINE__);
402 _transform.matrix = m;
404 // don't update caches if SWFMatrix wasn't updated too
405 if (updateCache) {
406 _xscale = _transform.matrix.get_x_scale() * 100.0;
407 _yscale = _transform.matrix.get_y_scale() * 100.0;
408 _rotation = _transform.matrix.get_rotation() * 180.0 / PI;
413 void
414 DisplayObject::set_event_handlers(const Events& copyfrom)
416 for (Events::const_iterator it=copyfrom.begin(), itE=copyfrom.end();
417 it != itE; ++it)
419 const event_id& ev = it->first;
420 const BufferList& bufs = it->second;
421 for (size_t i = 0, e = bufs.size(); i < e; ++i)
423 const action_buffer* buf = bufs[i];
424 assert(buf);
425 add_event_handler(ev, *buf);
430 void
431 DisplayObject::add_event_handler(const event_id& id, const action_buffer& code)
433 _event_handlers[id].push_back(&code);
435 // todo: drop the DisplayObject as a listener
436 // if it gets no valid handlers for
437 // mouse or Key events.
440 std::auto_ptr<ExecutableCode>
441 DisplayObject::get_event_handler(const event_id& id) const
443 std::auto_ptr<ExecutableCode> handler;
445 Events::const_iterator it = _event_handlers.find(id);
446 if ( it == _event_handlers.end() ) return handler;
448 #ifndef GNASH_USE_GC
449 assert(get_ref_count() > 0);
450 #endif // GNASH_USE_GC
451 DisplayObject* this_ptr = const_cast<DisplayObject*>(this);
453 handler.reset( new EventCode(this_ptr, it->second) );
454 return handler;
457 bool
458 DisplayObject::unload()
461 const bool childHandler = unloadChildren();
463 if (!_unloaded) {
464 queueEvent(event_id::UNLOAD, movie_root::PRIORITY_DOACTION);
467 // Unregister this DisplayObject as mask and/or maskee.
468 if (_maskee) _maskee->setMask(0);
469 if (_mask) _mask->setMaskee(0);
471 const bool hasEvent = hasEventHandler(event_id::UNLOAD) || childHandler;
473 if (!hasEvent) {
474 stage().removeQueuedConstructor(this);
477 _unloaded = true;
479 return hasEvent;
482 void
483 DisplayObject::queueEvent(const event_id& id, int lvl)
485 if (!_object) return;
486 std::auto_ptr<ExecutableCode> event(new QueuedEvent(this, id));
487 stage().pushAction(event, lvl);
490 bool
491 DisplayObject::hasEventHandler(const event_id& id) const
493 Events::const_iterator it = _event_handlers.find(id);
494 if (it != _event_handlers.end()) return true;
496 if (!_object) return false;
498 // Don't check resolve!
499 if (Property* prop = _object->findProperty(id.functionURI())) {
500 return prop->getValue(*_object).to_function();
502 return false;
506 /// Set the real and cached x scale.
508 /// Cached rotation and y scale are not updated.
509 void
510 DisplayObject::set_x_scale(double scale_percent)
512 double xscale = scale_percent / 100.0;
514 if (xscale != 0.0 && _xscale != 0.0)
516 if (scale_percent * _xscale < 0.0)
518 xscale = -std::abs(xscale);
520 else xscale = std::abs(xscale);
523 _xscale = scale_percent;
525 // As per misc-ming.all/SWFMatrix_test.{c,swf}
526 // we don't need to recompute the SWFMatrix from the
527 // caches.
529 SWFMatrix m = getMatrix(*this);
531 m.set_x_scale(xscale);
533 setMatrix(m); // we updated the cache ourselves
535 transformedByScript();
538 /// Set the real and cached rotation.
540 /// Cached scale values are not updated.
541 void
542 DisplayObject::set_rotation(double rot)
544 // Translate to the -180 .. 180 range
545 rot = std::fmod(rot, 360.0);
546 if (rot > 180.0) rot -= 360.0;
547 else if (rot < -180.0) rot += 360.0;
549 double rotation = rot * PI / 180.0;
551 if (_xscale < 0) rotation += PI;
553 SWFMatrix m = getMatrix(*this);
554 m.set_rotation(rotation);
556 // Update the matrix from the cached x scale to avoid accumulating
557 // errors.
558 // TODO: also update y scale? The x scale update is needed to keep
559 // TextField correct; no tests for y scale.
560 m.set_x_scale(std::abs(scaleX() / 100.0));
561 setMatrix(m); // we update the cache ourselves
563 _rotation = rot;
565 transformedByScript();
569 /// Set the real and cached y scale.
571 /// Cached rotation and x scale are not updated.
572 void
573 DisplayObject::set_y_scale(double scale_percent)
575 double yscale = scale_percent / 100.0;
577 if (yscale != 0.0 && _yscale != 0.0)
579 if (scale_percent * _yscale < 0.0) yscale = -std::abs(yscale);
580 else yscale = std::abs(yscale);
583 _yscale = scale_percent;
585 SWFMatrix m = getMatrix(*this);
586 m.set_y_scale(yscale);
587 setMatrix(m); // we updated the cache ourselves
589 transformedByScript();
593 std::string
594 DisplayObject::getTargetPath() const
596 // TODO: check what happens when this DisplayObject
597 // is a Movie loaded into another
598 // running movie.
600 typedef std::vector<std::string> Path;
601 Path path;
603 // Build parents stack
604 const DisplayObject* topLevel = 0;
605 const DisplayObject* ch = this;
607 string_table& st = getStringTable(*getObject(this));
608 for (;;)
610 const DisplayObject* parent = ch->parent();
612 // Don't push the _root name on the stack
613 if (!parent) {
614 topLevel = ch;
615 break;
618 path.push_back(ch->get_name().toString(st));
619 ch = parent;
622 assert(topLevel);
624 if (path.empty()) {
625 if (&stage().getRootMovie() == this) return "/";
626 std::stringstream ss;
627 ss << "_level" << _depth-DisplayObject::staticDepthOffset;
628 return ss.str();
631 // Build the target string from the parents stack
632 std::string target;
633 if (topLevel != &stage().getRootMovie()) {
634 std::stringstream ss;
635 ss << "_level" <<
636 topLevel->get_depth() - DisplayObject::staticDepthOffset;
637 target = ss.str();
639 for (Path::reverse_iterator it=path.rbegin(), itEnd=path.rend();
640 it != itEnd; ++it) {
641 target += "/" + *it;
643 return target;
647 std::string
648 DisplayObject::getTarget() const
651 // TODO: check what happens when this DisplayObject
652 // is a Movie loaded into another
653 // running movie.
655 typedef std::vector<std::string> Path;
656 Path path;
658 // Build parents stack
659 const DisplayObject* ch = this;
660 string_table& st = stage().getVM().getStringTable();
661 for (;;)
663 const DisplayObject* parent = ch->parent();
665 // Don't push the _root name on the stack
666 if (!parent) {
668 std::stringstream ss;
669 if (!dynamic_cast<const Movie*>(ch)) {
670 // must be an as-referenceable
671 // DisplayObject created using 'new'
672 // like, new MovieClip, new Video, new TextField...
673 //log_debug("DisplayObject %p (%s) doesn't have a parent and "
674 // "is not a Movie", ch, typeName(*ch));
675 ss << "<no parent, depth" << ch->get_depth() << ">";
676 path.push_back(ss.str());
678 else {
679 ss << "_level" <<
680 ch->get_depth() - DisplayObject::staticDepthOffset;
681 path.push_back(ss.str());
683 break;
686 path.push_back(ch->get_name().toString(st));
687 ch = parent;
690 assert (!path.empty());
692 // Build the target string from the parents stack
693 std::string target;
694 for (Path::const_reverse_iterator it=path.rbegin(), itEnd=path.rend();
695 it != itEnd; ++it) {
697 if (!target.empty()) target += ".";
698 target += *it;
701 return target;
705 void
706 DisplayObject::destroy()
708 // in case we are destroyed without being unloaded first
709 // see bug #21842
710 _unloaded = true;
712 /// we may destory a DisplayObject that's not unloaded.
713 ///(we don't have chance to unload it in current model,
714 /// see new_child_in_unload_test.c)
715 /// We don't destroy ourself twice, right ?
717 if (_object) _object->clearProperties();
719 assert(!_destroyed);
720 _destroyed = true;
723 void
724 DisplayObject::markReachableResources() const
726 markOwnResources();
727 if (_object) _object->setReachable();
728 if (_parent) _parent->setReachable();
729 if (_mask) _mask->setReachable();
730 if (_maskee) _maskee->setReachable();
733 /// Whether to use a hand cursor when the mouse is over this DisplayObject
735 /// This depends on the useHandCursor AS property, but:
736 /// 1. Only AS-referenceable objects may use a hand cursor (TODO: check
737 /// Video).
738 /// 2. Only objects with a release event may use a hand cursor.
739 /// CANNOT CONFIRM THE ABOVE, SEE ButtonEventsTest.swf in misc-ming.all
740 /// 3. The default value (if the property is not defined) is true.
741 bool
742 DisplayObject::allowHandCursor() const
744 as_object* obj = getObject(this);
745 if (!obj) return false;
747 // Checking for RELEASE breaks ButtonEventsTest.
748 // I guess such an event would influence wheter or not this
749 // character would become an active one, despite hand cursor
750 //if (!hasEventHandler(event_id::RELEASE)) return false;
752 as_value val;
753 if (!obj->get_member(NSV::PROP_USEHANDCURSOR, &val)) {
754 return true;
756 return toBool(val, getVM(*obj));
759 void
760 DisplayObject::setMask(DisplayObject* mask)
762 if ( _mask == mask ) return;
764 set_invalidated();
766 // Backup this before setMaskee has a chance to change it..
767 DisplayObject* prevMaskee = _maskee;
769 // If we had a previous mask unregister with it
770 if ( _mask && _mask != mask )
772 // the mask will call setMask(NULL)
773 // on any previously registered maskee
774 // so we make sure to set our _mask to
775 // NULL before getting called again
776 _mask->setMaskee(0);
779 // if we had a maskee, notify it to stop using
780 // us as a mask
781 if (prevMaskee) prevMaskee->setMask(0);
783 // TODO: should we reset any original clip depth
784 // specified by PlaceObject tag ?
785 set_clip_depth(noClipDepthValue);
786 _mask = mask;
787 _maskee = 0;
789 if (_mask) {
790 /// Register as as masked by the mask
791 _mask->setMaskee(this);
795 void
796 DisplayObject::setMaskee(DisplayObject* maskee)
798 if ( _maskee == maskee ) { return; }
800 if (_maskee) {
801 // We don't want the maskee to call setMaskee(null)
802 // on us again
803 _maskee->_mask = 0;
806 _maskee = maskee;
808 if (!maskee)
810 // TODO: should we reset any original clip depth
811 // specified by PlaceObject tag ?
812 set_clip_depth(noClipDepthValue);
817 bool
818 DisplayObject::boundsInClippingArea(Renderer& renderer) const
820 SWFRect mybounds = getBounds();
821 getWorldMatrix(*this).transform(mybounds);
823 return renderer.bounds_in_clipping_area(mybounds.getRange());
826 #ifdef USE_SWFTREE
827 DisplayObject::InfoTree::iterator
828 DisplayObject::getMovieInfo(InfoTree& tr, InfoTree::iterator it)
830 const std::string yes = _("yes");
831 const std::string no = _("no");
833 it = tr.append_child(it, std::make_pair(getTarget(), typeName(*this)));
835 std::ostringstream os;
836 os << get_depth();
837 tr.append_child(it, std::make_pair(_("Depth"), os.str()));
839 /// Don't add if the DisplayObject has no ratio value
840 if (get_ratio() >= 0)
842 os.str("");
843 os << get_ratio();
844 tr.append_child(it, std::make_pair(_("Ratio"), os.str()));
847 /// Don't add if it's not a real clipping depth
848 const int cd = get_clip_depth();
849 if (cd != noClipDepthValue) {
850 os.str("");
851 if (_maskee) os << "Dynamic mask";
852 else os << cd;
854 tr.append_child(it, std::make_pair(_("Clipping depth"), os.str()));
857 os.str("");
858 os << getBounds().width() << "x" << getBounds().height();
859 tr.append_child(it, std::make_pair(_("Dimensions"), os.str()));
861 tr.append_child(it, std::make_pair(_("Dynamic"), isDynamic() ? yes : no));
862 tr.append_child(it, std::make_pair(_("Mask"), isMaskLayer() ? yes : no));
863 tr.append_child(it, std::make_pair(_("Destroyed"),
864 isDestroyed() ? yes : no));
865 tr.append_child(it, std::make_pair(_("Unloaded"), unloaded() ? yes : no));
867 os.str("");
868 os << _blendMode;
869 tr.append_child(it, std::make_pair(_("Blend mode"), os.str()));
870 #ifndef NDEBUG
871 // This probably isn't interesting for non-developers
872 tr.append_child(it, std::make_pair(_("Invalidated"),
873 _invalidated ? yes : no));
874 tr.append_child(it, std::make_pair(_("Child invalidated"),
875 _child_invalidated ? yes : no));
876 #endif
877 return it;
879 #endif
881 MovieClip*
882 DisplayObject::getAsRoot()
884 return get_root();
887 void
888 setIndexedProperty(size_t index, DisplayObject& o, const as_value& val)
890 const Setter s = getGetterSetterByIndex(index).second;
891 if (!s) return; // read-only (warn?)
893 if (val.is_undefined() || val.is_null()) {
894 IF_VERBOSE_ASCODING_ERRORS(
895 log_aserror(_("Attempt to set property to %s, refused"),
896 o.getTarget(), val);
898 return;
901 (*s)(o, val);
904 void
905 getIndexedProperty(size_t index, DisplayObject& o, as_value& val)
907 const Getter s = getGetterSetterByIndex(index).first;
908 if (!s) {
909 val.set_undefined();
910 return;
912 val = (*s)(o);
916 /// DisplayObject property lookup
918 /// This function is only called on the first object in the inheritance chain
919 /// after the object's own properties have been checked.
920 /// In AS2, any DisplayObject marks the end of the inheritance chain for
921 /// lookups.
923 /// Lookup order:
925 /// 1. _level0.._level9
926 /// 2. Objects on the DisplayList of a MovieClip
927 /// 3. DisplayObject magic properties (_x, _y etc).
928 /// 4. MovieClips' TextField variables (this is probably not the best
929 /// way to do it, but as it is done like this, this must be called here.
930 /// It will cause an infinite recursion otherwise.
931 bool
932 getDisplayObjectProperty(DisplayObject& obj, const ObjectURI& uri,
933 as_value& val)
936 as_object* o = getObject(&obj);
937 assert(o);
939 string_table& st = getStringTable(*o);
940 const std::string& propname = uri.toString(st);
942 // Check _level0.._level9
943 movie_root& mr = getRoot(*getObject(&obj));
944 unsigned int levelno;
945 if (isLevelTarget(getSWFVersion(*o), propname, levelno)) {
946 MovieClip* mo = mr.getLevel(levelno);
947 if (mo) {
948 val = getObject(mo);
949 return true;
951 return false;
954 MovieClip* mc = dynamic_cast<MovieClip*>(&obj);
955 if (mc) {
956 DisplayObject* ch = mc->getDisplayListObject(uri);
957 if (ch) {
958 val = getObject(ch);
959 return true;
963 const string_table::key noCaseKey = uri.noCase(st);
965 // These properties have normal case-sensitivity.
966 // They are tested to exist for TextField, MovieClip, and Button
967 // but do not belong to the inheritance chain.
968 switch (caseless(*o) ? noCaseKey : getName(uri))
970 default:
971 break;
972 case NSV::PROP_uROOT:
973 if (getSWFVersion(*o) < 5) break;
974 val = getObject(obj.getAsRoot());
975 return true;
976 case NSV::PROP_uGLOBAL:
977 // TODO: clean up this mess.
978 assert(getObject(&obj));
979 if (getSWFVersion(*o) < 6) break;
980 val = &getGlobal(*o);
981 return true;
984 // These magic properties are case insensitive in all versions!
985 if (doGet(uri, obj, val)) return true;
987 // Check MovieClip such as TextField variables.
988 // TODO: check if there's a better way to find these properties.
989 if (mc && mc->getTextFieldVariables(uri, val)) return true;
991 return false;
995 bool
996 setDisplayObjectProperty(DisplayObject& obj, const ObjectURI& uri,
997 const as_value& val)
999 // These magic properties are case insensitive in all versions!
1000 return doSet(uri, obj, val);
1003 DisplayObject::MaskRenderer::MaskRenderer(Renderer& r, const DisplayObject& o)
1005 _renderer(r),
1006 _mask(o.visible() && o.getMask() && !o.getMask()->unloaded() ? o.getMask()
1007 : 0)
1009 if (!_mask) return;
1011 _renderer.begin_submit_mask();
1012 DisplayObject* p = _mask->parent();
1013 const Transform tr = p ?
1014 Transform(getWorldMatrix(*p), getWorldCxForm(*p)) : Transform();
1015 _mask->display(_renderer, tr);
1016 _renderer.end_submit_mask();
1019 DisplayObject::MaskRenderer::~MaskRenderer()
1021 if (_mask) _renderer.disable_mask();
1024 namespace {
1026 as_value
1027 getQuality(DisplayObject& o)
1029 movie_root& mr = getRoot(*getObject(&o));
1030 switch (mr.getQuality())
1032 case QUALITY_BEST:
1033 return as_value("BEST");
1034 case QUALITY_HIGH:
1035 return as_value("HIGH");
1036 case QUALITY_MEDIUM:
1037 return as_value("MEDIUM");
1038 case QUALITY_LOW:
1039 return as_value("LOW");
1042 return as_value();
1046 void
1047 setQuality(DisplayObject& o, const as_value& val)
1049 movie_root& mr = getRoot(*getObject(&o));
1051 if (!val.is_string()) return;
1053 const std::string& q = val.to_string();
1055 StringNoCaseEqual noCaseCompare;
1057 if (noCaseCompare(q, "BEST")) mr.setQuality(QUALITY_BEST);
1058 else if (noCaseCompare(q, "HIGH")) {
1059 mr.setQuality(QUALITY_HIGH);
1061 else if (noCaseCompare(q, "MEDIUM")) {
1062 mr.setQuality(QUALITY_MEDIUM);
1064 else if (noCaseCompare(q, "LOW")) {
1065 mr.setQuality(QUALITY_LOW);
1068 return;
1071 as_value
1072 getURL(DisplayObject& o)
1074 return as_value(o.get_root()->url());
1077 as_value
1078 getHighQuality(DisplayObject& o)
1080 movie_root& mr = getRoot(*getObject(&o));
1081 switch (mr.getQuality())
1083 case QUALITY_BEST:
1084 return as_value(2.0);
1085 case QUALITY_HIGH:
1086 return as_value(1.0);
1087 case QUALITY_MEDIUM:
1088 case QUALITY_LOW:
1089 return as_value(0.0);
1091 return as_value();
1094 void
1095 setHighQuality(DisplayObject& o, const as_value& val)
1097 movie_root& mr = getRoot(*getObject(&o));
1099 const double q = toNumber(val, getVM(*getObject(&o)));
1101 if (q < 0) mr.setQuality(QUALITY_HIGH);
1102 else if (q > 2) mr.setQuality(QUALITY_BEST);
1103 else {
1104 int i = static_cast<int>(q);
1105 switch(i)
1107 case 0:
1108 mr.setQuality(QUALITY_LOW);
1109 break;
1110 case 1:
1111 mr.setQuality(QUALITY_HIGH);
1112 break;
1113 case 2:
1114 mr.setQuality(QUALITY_BEST);
1115 break;
1121 void
1122 setY(DisplayObject& o, const as_value& val)
1125 const double newy = toNumber(val, getVM(*getObject(&o)));
1127 // NaN is skipped, Infinite isn't
1128 if (isNaN(newy))
1130 IF_VERBOSE_ASCODING_ERRORS(
1131 log_aserror(_("Attempt to set %s._y to %s "
1132 "(evaluating to number %g) refused"),
1133 o.getTarget(), val, newy);
1135 return;
1138 SWFMatrix m = getMatrix(o);
1139 // NOTE: infinite_to_zero is wrong here, see actionscript.all/setProperty.as
1140 m.set_y_translation(pixelsToTwips(infinite_to_zero(newy)));
1141 o.setMatrix(m);
1142 o.transformedByScript();
1145 as_value
1146 getY(DisplayObject& o)
1148 const SWFMatrix m = getMatrix(o);
1149 return twipsToPixels(m.get_y_translation());
1152 void
1153 setX(DisplayObject& o, const as_value& val)
1156 const double newx = toNumber(val, getVM(*getObject(&o)));
1158 // NaN is skipped, Infinite isn't
1159 if (isNaN(newx))
1161 IF_VERBOSE_ASCODING_ERRORS(
1162 log_aserror(_("Attempt to set %s._x to %s "
1163 "(evaluating to number %g) refused"),
1164 o.getTarget(), val, newx);
1166 return;
1169 SWFMatrix m = getMatrix(o);
1170 // NOTE: infinite_to_zero is wrong here, see actionscript.all/setProperty.as
1171 m.set_x_translation(pixelsToTwips(infinite_to_zero(newx)));
1172 o.setMatrix(m);
1173 o.transformedByScript();
1176 as_value
1177 getX(DisplayObject& o)
1179 const SWFMatrix m = getMatrix(o);
1180 return twipsToPixels(m.get_x_translation());
1183 void
1184 setScaleX(DisplayObject& o, const as_value& val)
1187 const double scale_percent = toNumber(val, getVM(*getObject(&o)));
1189 // NaN is skipped, Infinite is not, see actionscript.all/setProperty.as
1190 if (isNaN(scale_percent)) {
1191 IF_VERBOSE_ASCODING_ERRORS(
1192 log_aserror(_("Attempt to set %s._xscale to %s "
1193 "(evaluating to number %g) refused"),
1194 o.getTarget(), val, scale_percent);
1196 return;
1199 // input is in percent
1200 o.set_x_scale(scale_percent);
1204 as_value
1205 getScaleX(DisplayObject& o)
1207 return o.scaleX();
1210 void
1211 setScaleY(DisplayObject& o, const as_value& val)
1214 const double scale_percent = toNumber(val, getVM(*getObject(&o)));
1216 // NaN is skipped, Infinite is not, see actionscript.all/setProperty.as
1217 if (isNaN(scale_percent)) {
1218 IF_VERBOSE_ASCODING_ERRORS(
1219 log_aserror(_("Attempt to set %s._yscale to %s "
1220 "(evaluating to number %g) refused"),
1221 o.getTarget(), val, scale_percent);
1223 return;
1226 // input is in percent
1227 o.set_y_scale(scale_percent);
1231 as_value
1232 getScaleY(DisplayObject& o)
1234 return o.scaleY();
1237 as_value
1238 getVisible(DisplayObject& o)
1240 return o.visible();
1243 void
1244 setVisible(DisplayObject& o, const as_value& val)
1247 /// We cast to number and rely (mostly) on C++'s automatic
1248 /// cast to bool, as string "0" should be converted to
1249 /// its numeric equivalent, not interpreted as 'true', which
1250 /// SWF7+ does for strings.
1251 const double d = toNumber(val, getVM(*getObject(&o)));
1253 // Infinite or NaN is skipped
1254 if (isInf(d) || isNaN(d)) {
1255 IF_VERBOSE_ASCODING_ERRORS(
1256 log_aserror(_("Attempt to set %s._visible to %s "
1257 "(evaluating to number %g) refused"),
1258 o.getTarget(), val, d);
1260 return;
1263 o.set_visible(d);
1265 o.transformedByScript();
1268 as_value
1269 getAlpha(DisplayObject& o)
1271 return as_value(getCxForm(o).aa / 2.56);
1274 void
1275 setAlpha(DisplayObject& o, const as_value& val)
1278 // The new internal alpha value is input / 100.0 * 256.
1279 // We test for finiteness later, but the multiplication
1280 // won't make any difference.
1281 const double newAlpha = toNumber(val, getVM(*getObject(&o))) * 2.56;
1283 // NaN is skipped, Infinite is not, see actionscript.all/setProperty.as
1284 if (isNaN(newAlpha)) {
1285 IF_VERBOSE_ASCODING_ERRORS(
1286 log_aserror(_("Attempt to set %s._alpha to %s "
1287 "(evaluating to number %g) refused"),
1288 o.getTarget(), val, newAlpha);
1290 return;
1293 SWFCxForm cx = getCxForm(o);
1295 // Overflows are *not* truncated, but set to -32768.
1296 if (newAlpha > std::numeric_limits<boost::int16_t>::max() ||
1297 newAlpha < std::numeric_limits<boost::int16_t>::min()) {
1298 cx.aa = std::numeric_limits<boost::int16_t>::min();
1300 else {
1301 cx.aa = static_cast<boost::int16_t>(newAlpha);
1304 o.setCxForm(cx);
1305 o.transformedByScript();
1309 as_value
1310 getMouseX(DisplayObject& o)
1312 // Local coord of mouse IN PIXELS.
1313 boost::int32_t x, y;
1314 getRoot(*getObject(&o)).get_mouse_state(x, y);
1316 SWFMatrix m = getWorldMatrix(o);
1317 point a(pixelsToTwips(x), pixelsToTwips(y));
1319 m.invert().transform(a);
1320 return as_value(twipsToPixels(a.x));
1323 as_value
1324 getMouseY(DisplayObject& o)
1326 // Local coord of mouse IN PIXELS.
1327 boost::int32_t x, y;
1328 getRoot(*getObject(&o)).get_mouse_state(x, y);
1330 SWFMatrix m = getWorldMatrix(o);
1331 point a(pixelsToTwips(x), pixelsToTwips(y));
1332 m.invert().transform(a);
1333 return as_value(twipsToPixels(a.y));
1336 as_value
1337 getRotation(DisplayObject& o)
1339 return o.rotation();
1343 void
1344 setRotation(DisplayObject& o, const as_value& val)
1347 // input is in degrees
1348 const double rotation_val = toNumber(val, getVM(*getObject(&o)));
1350 // NaN is skipped, Infinity isn't
1351 if (isNaN(rotation_val)) {
1352 IF_VERBOSE_ASCODING_ERRORS(
1353 log_aserror(_("Attempt to set %s._rotation to %s "
1354 "(evaluating to number %g) refused"),
1355 o.getTarget(), val, rotation_val);
1357 return;
1359 o.set_rotation(rotation_val);
1363 as_value
1364 getParent(DisplayObject& o)
1366 as_object* p = getObject(o.parent());
1367 return p ? p : as_value();
1370 as_value
1371 getTarget(DisplayObject& o)
1373 return o.getTargetPath();
1376 as_value
1377 getNameProperty(DisplayObject& o)
1379 string_table& st = getStringTable(*getObject(&o));
1380 const std::string& name = o.get_name().toString(st);
1381 if (getSWFVersion(*getObject(&o)) < 6 && name.empty()) return as_value();
1382 return as_value(name);
1385 void
1386 setName(DisplayObject& o, const as_value& val)
1388 o.set_name(getURI(getVM(*getObject(&o)), val.to_string()));
1391 void
1392 setSoundBufTime(DisplayObject& /*o*/, const as_value& /*val*/)
1394 LOG_ONCE(log_unimpl("_soundbuftime setting"));
1397 as_value
1398 getSoundBufTime(DisplayObject& /*o*/)
1400 return as_value(0.0);
1403 as_value
1404 getWidth(DisplayObject& o)
1406 SWFRect bounds = o.getBounds();
1407 const SWFMatrix& m = getMatrix(o);
1408 m.transform(bounds);
1409 return twipsToPixels(bounds.width());
1412 void
1413 setWidth(DisplayObject& o, const as_value& val)
1415 const double newwidth = pixelsToTwips(toNumber(val, getVM(*getObject(&o))));
1416 if (newwidth <= 0) {
1417 IF_VERBOSE_ASCODING_ERRORS(
1418 log_aserror(_("Setting _width=%g of DisplayObject %s (%s)"),
1419 newwidth/20, o.getTarget(), typeName(o));
1422 o.setWidth(newwidth);
1425 as_value
1426 getFocusRect(DisplayObject& /*o*/)
1428 LOG_ONCE(log_unimpl("_focusrect"));
1429 return as_value(true);
1432 void
1433 setFocusRect(DisplayObject& /*o*/, const as_value& /*val*/)
1435 LOG_ONCE(log_unimpl("_focusrect setting"));
1438 as_value
1439 getDropTarget(DisplayObject& o)
1441 // This property only applies to MovieClips.
1442 MovieClip* mc = dynamic_cast<MovieClip*>(&o);
1443 if (!mc) return as_value();
1444 return as_value(mc->getDropTarget());
1447 as_value
1448 getCurrentFrame(DisplayObject& o)
1450 // This property only applies to MovieClips.
1451 MovieClip* mc = dynamic_cast<MovieClip*>(&o);
1452 if (!mc) return as_value();
1453 const int currframe =
1454 std::min(mc->get_loaded_frames(), mc->get_current_frame() + 1);
1455 return as_value(currframe);
1458 as_value
1459 getFramesLoaded(DisplayObject& o)
1461 // This property only applies to MovieClips.
1462 MovieClip* mc = dynamic_cast<MovieClip*>(&o);
1463 if (!mc) return as_value();
1464 return as_value(mc->get_loaded_frames());
1467 as_value
1468 getTotalFrames(DisplayObject& o)
1470 // This property only applies to MovieClips.
1471 MovieClip* mc = dynamic_cast<MovieClip*>(&o);
1472 if (!mc) return as_value();
1473 return as_value(mc->get_frame_count());
1477 /// @param uri The property to search for. Note that all special
1478 /// properties are lower-case.
1480 /// NOTE that all properties have getters so you can recognize a
1481 /// 'not-found' condition by checking .first = 0
1482 const GetterSetter&
1483 getGetterSetterByURI(const ObjectURI& uri, string_table& st)
1485 typedef std::map<ObjectURI, GetterSetter, ObjectURI::CaseLessThan>
1486 GetterSetters;
1488 static const GetterSetters gs =
1489 getURIMap<GetterSetters>(ObjectURI::CaseLessThan(st, true));
1491 const GetterSetters::const_iterator it = gs.find(uri);
1493 if (it == gs.end()) {
1494 static const GetterSetter none(0, 0);
1495 return none;
1498 return it->second;
1502 const GetterSetter&
1503 getGetterSetterByIndex(size_t index)
1505 const Setter n = 0;
1507 static const GetterSetter props[] = {
1508 GetterSetter(&getX, &setX),
1509 GetterSetter(&getY, &setY),
1510 GetterSetter(&getScaleX, &setScaleX),
1511 GetterSetter(&getScaleY, &setScaleY),
1513 GetterSetter(&getCurrentFrame, n),
1514 GetterSetter(&getTotalFrames, n),
1515 GetterSetter(&getAlpha, &setAlpha),
1516 GetterSetter(&getVisible, &setVisible),
1518 GetterSetter(&getWidth, &setWidth),
1519 GetterSetter(&getHeight, &setHeight),
1520 GetterSetter(&getRotation, &setRotation),
1521 GetterSetter(&getTarget, n),
1523 GetterSetter(&getFramesLoaded, n),
1524 GetterSetter(&getNameProperty, &setName),
1525 GetterSetter(&getDropTarget, n),
1526 GetterSetter(&getURL, n),
1528 GetterSetter(&getHighQuality, &setHighQuality),
1529 GetterSetter(&getFocusRect, &setFocusRect),
1530 GetterSetter(&getSoundBufTime, &setSoundBufTime),
1531 GetterSetter(&getQuality, &setQuality),
1533 GetterSetter(&getMouseX, n),
1534 GetterSetter(&getMouseY, n)
1538 if (index >= arraySize(props)) {
1539 const Getter ng = 0;
1540 static const GetterSetter none(ng, n);
1541 return none;
1544 return props[index];
1548 bool
1549 doGet(const ObjectURI& uri, DisplayObject& o, as_value& val)
1551 string_table& st = getStringTable(*getObject(&o));
1552 const Getter s = getGetterSetterByURI(uri, st).first;
1553 if (!s) return false;
1555 val = (*s)(o);
1556 return true;
1560 /// Do the actual setProperty
1562 /// Return true if the property is a DisplayObject property, regardless of
1563 /// whether it was successfully set or not.
1565 /// @param uri The property to search for. Note that all special
1566 /// properties are lower-case, so for a caseless check
1567 /// it is sufficient for prop to be caseless.
1568 bool
1569 doSet(const ObjectURI& uri, DisplayObject& o, const as_value& val)
1571 string_table& st = getStringTable(*getObject(&o));
1573 const GetterSetter gs = getGetterSetterByURI(uri, st);
1575 // not found (all props have getters)
1576 if (!gs.first) return false;
1578 const Setter s = gs.second;
1580 // read-only (TODO: aserror ?)
1581 if (!s) return true;
1583 if (val.is_undefined() || val.is_null()) {
1584 IF_VERBOSE_ASCODING_ERRORS(
1585 // TODO: add property name to this log...
1586 log_aserror(_("Attempt to set property to %s, refused"),
1587 o.getTarget(), val);
1589 return true;
1592 (*s)(o, val);
1593 return true;
1597 const BlendModeMap&
1598 getBlendModeMap()
1600 /// BLENDMODE_UNDEFINED has no matching string in AS. It is included
1601 /// here for logging purposes.
1602 static const BlendModeMap bm = boost::assign::map_list_of
1603 (DisplayObject::BLENDMODE_UNDEFINED, "undefined")
1604 (DisplayObject::BLENDMODE_NORMAL, "normal")
1605 (DisplayObject::BLENDMODE_LAYER, "layer")
1606 (DisplayObject::BLENDMODE_MULTIPLY, "multiply")
1607 (DisplayObject::BLENDMODE_SCREEN, "screen")
1608 (DisplayObject::BLENDMODE_LIGHTEN, "lighten")
1609 (DisplayObject::BLENDMODE_DARKEN, "darken")
1610 (DisplayObject::BLENDMODE_DIFFERENCE, "difference")
1611 (DisplayObject::BLENDMODE_ADD, "add")
1612 (DisplayObject::BLENDMODE_SUBTRACT, "subtract")
1613 (DisplayObject::BLENDMODE_INVERT, "invert")
1614 (DisplayObject::BLENDMODE_ALPHA, "alpha")
1615 (DisplayObject::BLENDMODE_ERASE, "erase")
1616 (DisplayObject::BLENDMODE_OVERLAY, "overlay")
1617 (DisplayObject::BLENDMODE_HARDLIGHT, "hardlight");
1619 return bm;
1623 // Match a blend mode to its string.
1624 bool
1625 blendModeMatches(const BlendModeMap::value_type& val, const std::string& mode)
1627 /// The match must be case-sensitive.
1628 if (mode.empty()) return false;
1629 return (val.second == mode);
1632 /// Return a const map of property URI to function.
1634 /// This function takes advantage of NRVO to allow the map to
1635 /// be constructed in the caller.
1636 template<typename Map>
1637 const Map
1638 getURIMap(const typename Map::key_compare& cmp)
1640 const Setter n = 0;
1642 Map ret(cmp);
1643 ret.insert(std::make_pair(NSV::PROP_uX, GetterSetter(&getX, &setX)));
1644 ret.insert(std::make_pair(NSV::PROP_uY, GetterSetter(&getY, &setY)));
1645 ret.insert(std::make_pair(NSV::PROP_uXSCALE,
1646 GetterSetter(&getScaleX, &setScaleX)));
1647 ret.insert(std::make_pair(NSV::PROP_uYSCALE,
1648 GetterSetter(&getScaleY, &setScaleY)));
1649 ret.insert(std::make_pair(NSV::PROP_uROTATION,
1650 GetterSetter(&getRotation, &setRotation)));
1651 ret.insert(std::make_pair(NSV::PROP_uHIGHQUALITY,
1652 GetterSetter(&getHighQuality, &setHighQuality)));
1653 ret.insert(std::make_pair(NSV::PROP_uQUALITY,
1654 GetterSetter(&getQuality, &setQuality)));
1655 ret.insert(std::make_pair(NSV::PROP_uALPHA,
1656 GetterSetter(&getAlpha, &setAlpha)));
1657 ret.insert(std::make_pair(NSV::PROP_uWIDTH,
1658 GetterSetter(&getWidth, &setWidth)));
1659 ret.insert(std::make_pair(NSV::PROP_uHEIGHT,
1660 GetterSetter(&getHeight, &setHeight)));
1661 ret.insert(std::make_pair(NSV::PROP_uNAME,
1662 GetterSetter(&getNameProperty, &setName)));
1663 ret.insert(std::make_pair(NSV::PROP_uVISIBLE,
1664 GetterSetter(&getVisible, &setVisible)));
1665 ret.insert(std::make_pair(NSV::PROP_uSOUNDBUFTIME,
1666 GetterSetter(&getSoundBufTime, &setSoundBufTime)));
1667 ret.insert(std::make_pair(NSV::PROP_uFOCUSRECT,
1668 GetterSetter(&getFocusRect, &setFocusRect)));
1669 ret.insert(std::make_pair(NSV::PROP_uDROPTARGET,
1670 GetterSetter(&getDropTarget, n)));
1671 ret.insert(std::make_pair(NSV::PROP_uCURRENTFRAME,
1672 GetterSetter(&getCurrentFrame, n)));
1673 ret.insert(std::make_pair(NSV::PROP_uFRAMESLOADED,
1674 GetterSetter(&getFramesLoaded, n)));
1675 ret.insert(std::make_pair(NSV::PROP_uTOTALFRAMES,
1676 GetterSetter(&getTotalFrames, n)));
1677 ret.insert(std::make_pair(NSV::PROP_uURL, GetterSetter(&getURL, n)));
1678 ret.insert(std::make_pair(NSV::PROP_uTARGET, GetterSetter(&getTarget, n)));
1679 ret.insert(std::make_pair(NSV::PROP_uXMOUSE, GetterSetter(&getMouseX, n)));
1680 ret.insert(std::make_pair(NSV::PROP_uYMOUSE, GetterSetter(&getMouseY, n)));
1681 ret.insert(std::make_pair(NSV::PROP_uPARENT, GetterSetter(&getParent, n)));
1682 return ret;
1685 } // anonymous namespace
1687 std::ostream&
1688 operator<<(std::ostream& o, DisplayObject::BlendMode bm)
1690 const BlendModeMap& bmm = getBlendModeMap();
1691 return (o << bmm.find(bm)->second);
1694 } // namespace gnash
1696 // local variables:
1697 // mode: c++
1698 // indent-tabs-mode: t
1699 // end: