1 // Button.cpp: Mouse-sensitive buttons, for Gnash.
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 // Free Software Foundation, Inc
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.
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
22 #include "gnashconfig.h" // USE_SWF_TREE
27 #include <boost/bind.hpp>
31 #include "DefineButtonTag.h"
34 #include "ActionExec.h"
35 #include "MovieClip.h"
36 #include "movie_root.h"
38 #include "NativeFunction.h"
40 #include "ExecutableCode.h"
41 #include "namedStrings.h"
42 #include "StringPredicates.h"
44 #include "SoundInfoRecord.h"
45 #include "Global_as.h"
46 #include "RunResources.h"
47 #include "sound_definition.h"
48 #include "Transform.h"
50 /** \page buttons Buttons and mouse behaviour
52 Observations about button & mouse behavior
54 Entities that receive mouse events: only buttons and sprites, AFAIK
56 When the mouse button goes down, it becomes "captured" by whatever
57 element is topmost, directly below the mouse at that moment. While
58 the mouse is captured, no other entity receives mouse events,
59 regardless of how the mouse or other elements move.
61 The mouse remains captured until the mouse button goes up. The mouse
62 remains captured even if the element that captured it is removed from
65 If the mouse isn't above a button or sprite when the mouse button goes
66 down, then the mouse is captured by the background (i.e. mouse events
67 just don't get sent, until the mouse button goes up again).
71 +------------------+---------------+-------------------------------------+
72 | Event | Mouse Button | description |
73 =========================================================================
74 | onRollOver | up | sent to topmost entity when mouse |
75 | | | cursor initially goes over it |
76 +------------------+---------------+-------------------------------------+
77 | onRollOut | up | when mouse leaves entity, after |
79 +------------------+---------------+-------------------------------------+
80 | onPress | up -> down | sent to topmost entity when mouse |
81 | | | button goes down. onRollOver |
82 | | | always precedes onPress. Initiates |
83 | | | mouse capture. |
84 +------------------+---------------+-------------------------------------+
85 | onRelease | down -> up | sent to active entity if mouse goes |
86 | | | up while over the element |
87 +------------------+---------------+-------------------------------------+
88 | onDragOut | down | sent to active entity if mouse |
89 | | | is no longer over the entity |
90 +------------------+---------------+-------------------------------------+
91 | onReleaseOutside | down -> up | sent to active entity if mouse goes |
92 | | | up while not over the entity. |
93 | | | onDragOut always precedes |
94 | | | onReleaseOutside |
95 +------------------+---------------+-------------------------------------+
96 | onDragOver | down | sent to active entity if mouse is |
97 | | | dragged back over it after |
99 +------------------+---------------+-------------------------------------+
101 There is always one active entity at any given time (considering NULL to
102 be an active entity, representing the background, and other objects that
103 don't receive mouse events).
105 When the mouse button is up, the active entity is the topmost element
106 directly under the mouse pointer.
108 When the mouse button is down, the active entity remains whatever it
109 was when the button last went down.
111 The active entity is the only object that receives mouse events.
113 !!! The "trackAsMenu" property alters this behavior! If trackAsMenu
114 is set on the active entity, then onReleaseOutside is filtered out,
115 and onDragOver from another entity is allowed (from the background, or
116 another trackAsMenu entity). !!!
122 mouse_button_state = UP
123 mouse_inside_entity_state = false
125 if mouse_button_state == DOWN
127 // Handle trackAsMenu
128 if (active_entity->trackAsMenu)
129 possible_entity = topmost entity below mouse
130 if (possible_entity != active_entity && possible_entity->trackAsMenu)
131 // Transfer to possible entity
132 active_entity = possible_entity
133 active_entity->onDragOver()
134 mouse_inside_entity_state = true;
136 // Handle onDragOut, onDragOver
137 if (mouse_inside_entity_state == false)
138 if (mouse is actually inside the active_entity)
140 active_entity->onDragOver()
141 mouse_inside_entity_state = true;
143 else // mouse_inside_entity_state == true
144 if (mouse is actually outside the active_entity)
146 active_entity->onDragOut()
147 mouse_inside_entity_state = false;
149 // Handle onRelease, onReleaseOutside
150 if (mouse button is up)
151 if (mouse_inside_entity_state)
153 active_entity->onRelease()
156 if (active_entity->trackAsMenu == false)
157 active_entity->onReleaseOutside()
158 mouse_button_state = UP
160 if mouse_button_state == UP
161 new_active_entity = topmost entity below the mouse
162 if (new_active_entity != active_entity)
163 // onRollOut, onRollOver
164 active_entity->onRollOut()
165 active_entity = new_active_entity
166 active_entity->onRollOver()
169 if (mouse button is down)
171 active_entity->onPress()
172 mouse_inside_entity_state = true
173 mouse_button_state = DOWN
181 as_value
button_blendMode(const fn_call
& fn
);
182 as_value
button_cacheAsBitmap(const fn_call
& fn
);
183 as_value
button_filters(const fn_call
& fn
);
184 as_value
button_scale9Grid(const fn_call
& fn
);
185 as_value
button_setTabIndex(const fn_call
& fn
);
186 as_value
button_getTabIndex(const fn_call
& fn
);
187 as_value
button_getDepth(const fn_call
& fn
);
192 class ButtonActionExecutor
{
194 ButtonActionExecutor(as_environment
& env
)
199 void operator() (const action_buffer
& ab
)
201 ActionExec
exec(ab
, _env
);
205 as_environment
& _env
;
208 class ButtonActionPusher
211 ButtonActionPusher(movie_root
& mr
, DisplayObject
* this_ptr
)
217 void operator()(const action_buffer
& ab
)
219 _mr
.pushAction(ab
, _tp
);
230 void addInstanceProperty(Button
& b
, DisplayObject
* d
) {
232 const ObjectURI
& name
= d
->get_name();
233 if (name
.empty()) return;
234 getObject(&b
)->init_member(name
, getObject(d
), 0);
237 void removeInstanceProperty(Button
& b
, DisplayObject
* d
) {
239 const ObjectURI
& name
= d
->get_name();
240 if (name
.empty()) return;
241 getObject(&b
)->delProperty(name
);
245 /// Predicates for standard algorithms.
247 /// Depth comparator for DisplayObjects.
248 static bool charDepthLessThen(const DisplayObject
* ch1
, const DisplayObject
* ch2
)
250 return ch1
->get_depth() < ch2
->get_depth();
253 /// Predicate for finding active DisplayObjects.
255 /// Returns true if the DisplayObject should be skipped:
256 /// 1) if it is NULL, or
257 /// 2) if we don't want unloaded DisplayObjects and the DisplayObject is unloaded.
258 static bool isCharacterNull(DisplayObject
* ch
, bool includeUnloaded
)
260 return (!ch
|| (!includeUnloaded
&& ch
->unloaded()));
264 attachButtonInterface(as_object
& o
)
267 const int unprotected
= 0;
268 o
.init_member(NSV::PROP_ENABLED
, true, unprotected
);
269 o
.init_member("useHandCursor", true, unprotected
);
271 const int swf8Flags
= PropFlags::onlySWF8Up
;
274 o
.init_property("tabIndex", *vm
.getNative(105, 1), *vm
.getNative(105, 2),
277 o
.init_member("getDepth", vm
.getNative(105, 3), unprotected
);
280 gs
= vm
.getNative(105, 4);
281 o
.init_property("scale9Grid", *gs
, *gs
, swf8Flags
);
282 gs
= vm
.getNative(105, 5);
283 o
.init_property("filters", *gs
, *gs
, swf8Flags
);
284 gs
= vm
.getNative(105, 6);
285 o
.init_property("cacheAsBitmap", *gs
, *gs
, swf8Flags
);
286 gs
= vm
.getNative(105, 7);
287 o
.init_property("blendMode", *gs
, *gs
, swf8Flags
);
291 Button::Button(as_object
* object
, const SWF::DefineButtonTag
* def
,
292 DisplayObject
* parent
)
294 InteractiveObject(object
, parent
),
295 _mouseState(MOUSESTATE_UP
),
306 Button::trackAsMenu()
308 // TODO: check whether the AS or the tag value takes precedence.
309 as_object
* obj
= getObject(this);
312 VM
& vm
= getVM(*obj
);
316 const ObjectURI
& propTrackAsMenu
= getURI(vm
, "trackAsMenu");
317 if (obj
->get_member(propTrackAsMenu
, &track
)) {
318 return toBool(track
, vm
);
320 if (_def
) return _def
->trackAsMenu();
327 as_object
* obj
= getObject(this);
331 if (!obj
->get_member(NSV::PROP_ENABLED
, &enabled
)) return false;
333 return toBool(enabled
, getVM(*obj
));
338 Button::keyPress(key::code c
)
341 // We don't respond to events while unloaded
346 ButtonActionPusher
xec(stage(), this);
347 _def
->forEachTrigger(event_id(event_id::KEY_PRESS
, c
), xec
);
351 Button::handleFocus()
353 /// Nothing to do, but can receive focus.
359 Button::display(Renderer
& renderer
, const Transform
& base
)
361 const DisplayObject::MaskRenderer
mr(renderer
, *this);
363 const Transform xform
= base
* transform();
365 DisplayObjects actChars
;
366 getActiveCharacters(actChars
);
368 // TODO: by keeping chars sorted by depth we'd avoid the sort on display
369 std::sort(actChars
.begin(), actChars
.end(), charDepthLessThen
);
371 for (DisplayObjects::iterator it
= actChars
.begin(), e
= actChars
.end();
373 (*it
)->display(renderer
, xform
);
380 // Return the topmost entity that the given point covers. NULL if none.
381 // I.e. check against ourself.
383 Button::topmostMouseEntity(boost::int32_t x
, boost::int32_t y
)
385 if (!visible() || !isEnabled())
390 //-------------------------------------------------
391 // Check our active and visible children first
392 //-------------------------------------------------
394 DisplayObjects actChars
;
395 getActiveCharacters(actChars
);
397 if ( ! actChars
.empty() )
399 std::sort(actChars
.begin(), actChars
.end(), charDepthLessThen
);
401 SWFMatrix m
= getMatrix(*this);
403 m
.invert().transform(p
);
405 for (DisplayObjects::reverse_iterator it
= actChars
.rbegin(),
406 itE
=actChars
.rend(); it
!=itE
; ++it
)
408 DisplayObject
* ch
= *it
;
409 if ( ! ch
->visible() ) continue;
410 InteractiveObject
*hit
= ch
->topmostMouseEntity(p
.x
, p
.y
);
411 if ( hit
) return hit
;
415 //-------------------------------------------------
416 // If that failed, check our hit area
417 //-------------------------------------------------
419 // Find hit DisplayObjects
420 if ( _hitCharacters
.empty() ) return 0;
422 // point is in p's space,
423 // we need to convert it in world space
425 DisplayObject
* p
= parent();
427 getWorldMatrix(*p
).transform(wp
);
430 for (DisplayObjects::const_iterator i
= _hitCharacters
.begin(),
431 e
= _hitCharacters
.end(); i
!=e
; ++i
)
433 if ((*i
)->pointInVisibleShape(wp
.x
, wp
.y
))
435 // The mouse is inside the shape.
445 Button::mouseEvent(const event_id
& event
)
448 // We don't respond to events while unloaded. See bug #22982.
452 MouseState new_state
= _mouseState
;
454 // Set our mouse state (so we know how to render).
457 case event_id::ROLL_OUT
:
458 case event_id::RELEASE_OUTSIDE
:
459 new_state
= MOUSESTATE_UP
;
462 case event_id::RELEASE
:
463 case event_id::ROLL_OVER
:
464 case event_id::DRAG_OUT
:
465 case event_id::MOUSE_UP
:
466 new_state
= MOUSESTATE_OVER
;
469 case event_id::PRESS
:
470 case event_id::DRAG_OVER
:
471 case event_id::MOUSE_DOWN
:
472 new_state
= MOUSESTATE_DOWN
;
476 //abort(); // missed a case?
477 log_error(_("Unhandled button event %s"), event
);
481 set_current_state(new_state
);
483 // Button transition sounds.
486 if (!_def
->hasSound()) break;
488 // Check if there is a sound handler
489 sound::sound_handler
* s
= getRunResources(*getObject(this)).soundHandler();
492 int bi
; // button sound array index [0..3]
496 case event_id::ROLL_OUT
:
499 case event_id::ROLL_OVER
:
502 case event_id::PRESS
:
505 case event_id::RELEASE
:
513 // no sound for this transition
516 const SWF::DefineButtonSoundTag::ButtonSound
& bs
=
517 _def
->buttonSound(bi
);
519 // character zero is considered as null character
520 if (!bs
.soundID
) break;
523 if (!bs
.sample
) break;
525 if (bs
.soundInfo
.stopPlayback
) {
526 s
->stopEventSound(bs
.sample
->m_sound_handler_id
);
529 const SWF::SoundInfoRecord
& sinfo
= bs
.soundInfo
;
531 const sound::SoundEnvelopes
* env
=
532 sinfo
.envelopes
.empty() ? 0 : &sinfo
.envelopes
;
534 s
->startSound(bs
.sample
->m_sound_handler_id
,
535 bs
.soundInfo
.loopCount
,
537 !sinfo
.noMultiple
, // allow multiple instances ?
545 // From: "ActionScript - The Definitive Guide" by Colin Moock
546 // (chapter 10: Events and Event Handlers)
548 // "Event-based code [..] is said to be executed asynchronously
549 // because the triggering of events can occur at arbitrary times."
551 // We'll push to the global list. The movie_root will process
552 // the action queue on mouse event.
555 movie_root
& mr
= stage();
557 ButtonActionPusher
xec(mr
, this);
558 _def
->forEachTrigger(event
, xec
);
560 // check for built-in event handler.
561 std::auto_ptr
<ExecutableCode
> code (get_event_handler(event
));
563 mr
.pushAction(code
, movie_root::PRIORITY_DOACTION
);
566 sendEvent(*getObject(this), get_environment(), event
.functionURI());
571 Button::getActiveCharacters(ConstDisplayObjects
& list
) const
575 // Copy all the DisplayObjects to the new list, skipping NULL and unloaded
577 std::remove_copy_if(_stateCharacters
.begin(), _stateCharacters
.end(),
578 std::back_inserter(list
),
579 boost::bind(&isCharacterNull
, _1
, false));
585 Button::getActiveCharacters(DisplayObjects
& list
, bool includeUnloaded
)
589 // Copy all the DisplayObjects to the new list, skipping NULL
590 // DisplayObjects, optionally including unloaded DisplayObjects.
591 std::remove_copy_if(_stateCharacters
.begin(), _stateCharacters
.end(),
592 std::back_inserter(list
),
593 boost::bind(&isCharacterNull
, _1
, includeUnloaded
));
598 Button::get_active_records(ActiveRecords
& list
, MouseState state
)
603 const DefineButtonTag::ButtonRecords
& br
= _def
->buttonRecords();
606 for (DefineButtonTag::ButtonRecords::const_iterator i
= br
.begin(),
607 e
= br
.end(); i
!= e
; ++i
, ++index
)
609 const ButtonRecord
& rec
=*i
;
610 if (rec
.hasState(state
)) list
.insert(index
);
614 #ifdef GNASH_DEBUG_BUTTON_DISPLAYLIST
615 static void dump(Button::DisplayObjects
& chars
, std::stringstream
& ss
)
617 for (size_t i
=0, e
=chars
.size(); i
<e
; ++i
)
619 ss
<< "Record" << i
<< ": ";
620 DisplayObject
* ch
= chars
[i
];
621 if ( ! ch
) ss
<< "NULL.";
624 ss
<< ch
->getTarget() << " (depth:" <<
625 ch
->get_depth()-DisplayObject::staticDepthOffset
-1
626 << " unloaded:" << ch
->unloaded() <<
627 " destroyed:" << ch
->isDestroyed() << ")";
635 Button::set_current_state(MouseState new_state
)
637 if (new_state
== _mouseState
)
640 #ifdef GNASH_DEBUG_BUTTON_DISPLAYLIST
641 std::stringstream ss
;
642 ss
<< "at set_current_state enter: " << std::endl
;
643 dump(_stateCharacters
, ss
);
644 log_debug("%s", ss
.str());
647 // Get new state records
648 ActiveRecords newChars
;
649 get_active_records(newChars
, new_state
);
651 // For each possible record, check if it should still be there
652 for (size_t i
=0, e
=_stateCharacters
.size(); i
<e
; ++i
)
654 DisplayObject
* oldch
= _stateCharacters
[i
];
655 bool shouldBeThere
= ( newChars
.find(i
) != newChars
.end() );
657 if ( ! shouldBeThere
)
660 // is there, but is unloaded: destroy, clear slot and go on
661 if ( oldch
&& oldch
->unloaded() ) {
662 removeInstanceProperty(*this, oldch
);
663 if ( ! oldch
->isDestroyed() ) oldch
->destroy();
664 _stateCharacters
[i
] = NULL
;
668 if ( oldch
) // the one we have should not be there... unload!
672 if ( ! oldch
->unload() )
674 // No onUnload handler: destroy and clear slot
675 removeInstanceProperty(*this, oldch
);
676 if (!oldch
->isDestroyed()) oldch
->destroy();
677 _stateCharacters
[i
] = NULL
;
681 // onUnload handler: shift depth and keep slot
682 int oldDepth
= oldch
->get_depth();
683 int newDepth
= DisplayObject::removedDepthOffset
- oldDepth
;
684 #ifdef GNASH_DEBUG_BUTTON_DISPLAYLIST
685 log_debug("Removed button record shifted from depth %d to depth %d",
688 oldch
->set_depth(newDepth
);
692 else // should be there
694 // Is there already, but is unloaded: destroy and consider as gone
695 if ( oldch
&& oldch
->unloaded() )
697 removeInstanceProperty(*this, oldch
);
698 if ( ! oldch
->isDestroyed() ) oldch
->destroy();
699 _stateCharacters
[i
] = NULL
;
704 // Not there, instantiate
705 const SWF::ButtonRecord
& rec
= _def
->buttonRecords()[i
];
706 DisplayObject
* ch
= rec
.instantiate(this);
709 _stateCharacters
[i
] = ch
;
710 addInstanceProperty(*this, ch
);
716 #ifdef GNASH_DEBUG_BUTTON_DISPLAYLIST
718 ss
<< "at set_current_state end: " << std::endl
;
719 dump(_stateCharacters
, ss
);
720 log_debug("%s", ss
.str());
723 // Remember current state
724 _mouseState
= new_state
;
729 Button::add_invalidated_bounds(InvalidatedRanges
& ranges
, bool force
)
732 // Not visible anyway
733 if (!visible()) return;
735 ranges
.add(m_old_invalidated_ranges
);
737 DisplayObjects actChars
;
738 getActiveCharacters(actChars
);
739 std::for_each(actChars
.begin(), actChars
.end(),
740 boost::bind(&DisplayObject::add_invalidated_bounds
, _1
,
741 boost::ref(ranges
), force
|| invalidated())
746 Button::getBounds() const
750 typedef std::vector
<const DisplayObject
*> Chars
;
752 getActiveCharacters(actChars
);
753 for (Chars::const_iterator i
= actChars
.begin(), e
= actChars
.end();
756 const DisplayObject
* ch
= *i
;
757 // Child bounds need be transformed in our coordinate space
758 SWFRect lclBounds
= ch
->getBounds();
759 SWFMatrix m
= getMatrix(*ch
);
760 allBounds
.expand_to_transformed_rect(m
, lclBounds
);
767 Button::pointInShape(boost::int32_t x
, boost::int32_t y
) const
769 typedef std::vector
<const DisplayObject
*> Chars
;
771 getActiveCharacters(actChars
);
772 for(Chars::const_iterator i
=actChars
.begin(),e
=actChars
.end(); i
!=e
; ++i
)
774 const DisplayObject
* ch
= *i
;
775 if (ch
->pointInShape(x
,y
)) return true;
781 Button::construct(as_object
* initObj
)
783 // This can happen if attachMovie is called with an exported Button and
784 // an init object. The attachment happens, but the init object is not used
785 // (see misc-ming.all/attachMovieTest.swf).
787 IF_VERBOSE_ASCODING_ERRORS(
788 log_aserror(_("Button placed with an init object. This will "
793 saveOriginalTarget(); // for soft refs
795 // Don't register this button instance as a live DisplayObject.
797 // Instantiate the hit DisplayObjects
798 ActiveRecords hitChars
;
799 get_active_records(hitChars
, MOUSESTATE_HIT
);
800 for (ActiveRecords::iterator i
=hitChars
.begin(),e
=hitChars
.end(); i
!=e
; ++i
)
802 const SWF::ButtonRecord
& rec
= _def
->buttonRecords()[*i
];
804 // These should not be named!
805 DisplayObject
* ch
= rec
.instantiate(this, false);
806 _hitCharacters
.push_back(ch
);
809 // Setup the state DisplayObjects container
810 // It will have a slot for each DisplayObject record.
811 // Some slots will probably be never used (consider HIT-only records)
812 // but for now this direct corrispondence between record number
813 // and active DisplayObject will be handy.
814 _stateCharacters
.resize(_def
->buttonRecords().size());
816 // Instantiate the default state DisplayObjects
817 ActiveRecords upChars
;
818 get_active_records(upChars
, MOUSESTATE_UP
);
820 for (ActiveRecords::iterator i
= upChars
.begin(), e
=upChars
.end();
824 const SWF::ButtonRecord
& rec
= _def
->buttonRecords()[rno
];
826 DisplayObject
* ch
= rec
.instantiate(this);
828 _stateCharacters
[rno
] = ch
;
829 addInstanceProperty(*this, ch
);
833 // There is no INITIALIZE/CONSTRUCT/LOAD/ENTERFRAME/UNLOAD event
836 // Register key events.
837 if (_def
->hasKeyPressHandler()) {
838 stage().registerButton(this);
844 Button::markOwnResources() const
847 // Mark state DisplayObjects as reachable
848 for (DisplayObjects::const_iterator i
= _stateCharacters
.begin(),
849 e
= _stateCharacters
.end(); i
!= e
; ++i
)
851 DisplayObject
* ch
= *i
;
852 if (ch
) ch
->setReachable();
855 // Mark hit DisplayObjects as reachable
856 std::for_each(_hitCharacters
.begin(), _hitCharacters
.end(),
857 std::mem_fun(&DisplayObject::setReachable
));
862 Button::unloadChildren()
864 bool childsHaveUnload
= false;
866 // We need to unload all children, or the global instance list
867 // will keep growing forever !
868 for (DisplayObjects::iterator i
= _stateCharacters
.begin(),
869 e
= _stateCharacters
.end(); i
!= e
; ++i
)
871 DisplayObject
* ch
= *i
;
872 if (!ch
|| ch
->unloaded()) continue;
873 if (ch
->unload()) childsHaveUnload
= true;
876 // NOTE: we don't need to ::unload or ::destroy here
877 // as the _hitCharacters are never placed on stage.
878 // As an optimization we might not even instantiate
879 // them, and only use the definition and the
880 // associated transform SWFMatrix... (would take
881 // hit instance off the GC).
882 _hitCharacters
.clear();
884 return childsHaveUnload
;
890 stage().removeButton(this);
892 for (DisplayObjects::iterator i
= _stateCharacters
.begin(),
893 e
=_stateCharacters
.end(); i
!= e
; ++i
) {
894 DisplayObject
* ch
= *i
;
895 if (!ch
|| ch
->isDestroyed()) continue;
899 // NOTE: we don't need to ::unload or ::destroy here
900 // as the _hitCharacters are never placed on stage.
901 // As an optimization we might not even instantiate
902 // them, and only use the definition and the
903 // associated transform SWFMatrix... (would take
904 // hit instance off the GC).
905 _hitCharacters
.clear();
907 DisplayObject::destroy();
911 Button::getDefinitionVersion() const
913 return _def
->getSWFVersion();
917 button_class_init(as_object
& global
, const ObjectURI
& uri
)
919 // This is going to be the global Button "class"/"function"
920 Global_as
& gl
= getGlobal(global
);
921 as_object
* proto
= createObject(gl
);
922 as_object
* cl
= gl
.createClass(emptyFunction
, proto
);
923 attachButtonInterface(*proto
);
925 // Register _global.MovieClip
926 global
.init_member(uri
, cl
, as_object::DefaultFlags
);
930 registerButtonNative(as_object
& global
)
932 VM
& vm
= getVM(global
);
933 vm
.registerNative(button_setTabIndex
, 105, 1);
934 vm
.registerNative(button_getTabIndex
, 105, 2);
935 vm
.registerNative(button_getDepth
, 105, 3);
936 vm
.registerNative(button_scale9Grid
, 105, 4);
937 vm
.registerNative(button_filters
, 105, 5);
938 vm
.registerNative(button_cacheAsBitmap
, 105, 6);
939 vm
.registerNative(button_blendMode
, 105, 7);
943 DisplayObject::InfoTree::iterator
944 Button::getMovieInfo(InfoTree
& tr
, InfoTree::iterator it
)
946 InfoTree::iterator selfIt
= DisplayObject::getMovieInfo(tr
, it
);
947 std::ostringstream os
;
949 DisplayObjects actChars
;
950 getActiveCharacters(actChars
, true);
951 std::sort(actChars
.begin(), actChars
.end(), charDepthLessThen
);
954 os
<< std::boolalpha
<< isEnabled();
955 InfoTree::iterator localIter
= tr
.append_child(selfIt
,
956 std::make_pair(_("Enabled"), os
.str()));
960 localIter
= tr
.append_child(selfIt
,
961 std::make_pair(_("Button state"), os
.str()));
964 os
<< actChars
.size();
965 localIter
= tr
.append_child(selfIt
, std::make_pair(_("Action characters"),
968 std::for_each(actChars
.begin(), actChars
.end(),
969 boost::bind(&DisplayObject::getMovieInfo
, _1
, tr
, localIter
));
977 operator<<(std::ostream
& o
, const Button::MouseState
& st
)
980 case Button::MOUSESTATE_UP
: return o
<< "UP";
981 case Button::MOUSESTATE_DOWN
: return o
<< "DOWN";
982 case Button::MOUSESTATE_OVER
: return o
<< "OVER";
983 case Button::MOUSESTATE_HIT
: return o
<< "HIT";
984 default: return o
<< "Unknown state";
991 button_blendMode(const fn_call
& fn
)
993 Button
* obj
= ensure
<IsDisplayObject
<Button
> >(fn
);
994 LOG_ONCE(log_unimpl(_("Button.blendMode")));
1000 button_cacheAsBitmap(const fn_call
& fn
)
1002 Button
* obj
= ensure
<IsDisplayObject
<Button
> >(fn
);
1003 LOG_ONCE(log_unimpl(_("Button.cacheAsBitmap")));
1009 button_filters(const fn_call
& fn
)
1011 Button
* obj
= ensure
<IsDisplayObject
<Button
> >(fn
);
1012 LOG_ONCE(log_unimpl(_("Button.filters")));
1018 button_scale9Grid(const fn_call
& fn
)
1020 Button
* obj
= ensure
<IsDisplayObject
<Button
> >(fn
);
1021 LOG_ONCE(log_unimpl(_("Button.scale9Grid")));
1027 button_getTabIndex(const fn_call
& fn
)
1029 Button
* obj
= ensure
<IsDisplayObject
<Button
> >(fn
);
1030 LOG_ONCE(log_unimpl(_("Button.getTabIndex")));
1036 button_setTabIndex(const fn_call
& fn
)
1038 Button
* obj
= ensure
<IsDisplayObject
<Button
> >(fn
);
1039 LOG_ONCE(log_unimpl(_("Button.setTabIndex")));
1045 button_getDepth(const fn_call
& fn
)
1047 // This does exactly the same as MovieClip.getDepth, but appears to be
1048 // a separate function.
1049 DisplayObject
* obj
= ensure
<IsDisplayObject
<Button
> >(fn
);
1050 return as_value(obj
->get_depth());
1053 } // anonymous namespace
1054 } // end of namespace gnash
1059 // c-basic-offset: 8
1061 // indent-tabs-mode: nil