1 // Button.cpp: Mouse-sensitive buttons, for Gnash.
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
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>
30 #include "smart_ptr.h"
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
{
210 ButtonActionPusher(movie_root
& mr
, DisplayObject
* this_ptr
)
216 void operator()(const action_buffer
& ab
)
218 _mr
.pushAction(ab
, _tp
);
229 void addInstanceProperty(Button
& b
, DisplayObject
* d
) {
231 const ObjectURI
& name
= d
->get_name();
232 if (name
.empty()) return;
233 getObject(&b
)->init_member(name
, getObject(d
), 0);
236 void removeInstanceProperty(Button
& b
, DisplayObject
* d
) {
238 const ObjectURI
& name
= d
->get_name();
239 if (name
.empty()) return;
240 getObject(&b
)->delProperty(name
);
244 /// Predicates for standard algorithms.
246 /// Depth comparator for DisplayObjects.
247 static bool charDepthLessThen(const DisplayObject
* ch1
, const DisplayObject
* ch2
)
249 return ch1
->get_depth() < ch2
->get_depth();
252 /// Predicate for finding active DisplayObjects.
254 /// Returns true if the DisplayObject should be skipped:
255 /// 1) if it is NULL, or
256 /// 2) if we don't want unloaded DisplayObjects and the DisplayObject is unloaded.
257 static bool isCharacterNull(DisplayObject
* ch
, bool includeUnloaded
)
259 return (!ch
|| (!includeUnloaded
&& ch
->unloaded()));
263 attachButtonInterface(as_object
& o
)
266 const int unprotected
= 0;
267 o
.init_member(NSV::PROP_ENABLED
, true, unprotected
);
268 o
.init_member("useHandCursor", true, unprotected
);
270 const int swf8Flags
= PropFlags::onlySWF8Up
;
273 o
.init_property("tabIndex", *vm
.getNative(105, 1), *vm
.getNative(105, 2),
276 o
.init_member("getDepth", vm
.getNative(105, 3), unprotected
);
279 gs
= vm
.getNative(105, 4);
280 o
.init_property("scale9Grid", *gs
, *gs
, swf8Flags
);
281 gs
= vm
.getNative(105, 5);
282 o
.init_property("filters", *gs
, *gs
, swf8Flags
);
283 gs
= vm
.getNative(105, 6);
284 o
.init_property("cacheAsBitmap", *gs
, *gs
, swf8Flags
);
285 gs
= vm
.getNative(105, 7);
286 o
.init_property("blendMode", *gs
, *gs
, swf8Flags
);
290 Button::Button(as_object
* object
, const SWF::DefineButtonTag
* def
,
291 DisplayObject
* parent
)
293 InteractiveObject(object
, parent
),
294 _mouseState(MOUSESTATE_UP
),
299 // check up presence Key events
300 if (_def
->hasKeyPressHandler()) {
301 stage().add_key_listener(this);
308 stage().remove_key_listener(this);
312 Button::trackAsMenu()
314 // TODO: check whether the AS or the tag value takes precedence.
315 as_object
* obj
= getObject(this);
318 VM
& vm
= getVM(*obj
);
322 const ObjectURI
& propTrackAsMenu
= getURI(vm
, "trackAsMenu");
323 if (obj
->get_member(propTrackAsMenu
, &track
)) {
324 return toBool(track
, vm
);
326 if (_def
) return _def
->trackAsMenu();
333 as_object
* obj
= getObject(this);
337 if (!obj
->get_member(NSV::PROP_ENABLED
, &enabled
)) return false;
339 return toBool(enabled
, getVM(*obj
));
344 Button::notifyEvent(const event_id
& id
)
347 // We dont' respond to events while unloaded
352 // We only respond keypress events
353 if ( id
.id() != event_id::KEY_PRESS
) return;
355 // We only respond to valid key code (should we assert here?)
356 if ( id
.keyCode() == key::INVALID
) return;
358 ButtonActionPusher
xec(stage(), this);
359 _def
->forEachTrigger(id
, xec
);
363 Button::handleFocus() {
364 /// Nothing to do, but can receive focus.
370 Button::display(Renderer
& renderer
, const Transform
& base
)
372 const DisplayObject::MaskRenderer
mr(renderer
, *this);
374 const Transform xform
= base
* transform();
376 DisplayObjects actChars
;
377 getActiveCharacters(actChars
);
379 // TODO: by keeping chars sorted by depth we'd avoid the sort on display
380 std::sort(actChars
.begin(), actChars
.end(), charDepthLessThen
);
382 for (DisplayObjects::iterator it
= actChars
.begin(), e
= actChars
.end();
384 (*it
)->display(renderer
, xform
);
391 // Return the topmost entity that the given point covers. NULL if none.
392 // I.e. check against ourself.
394 Button::topmostMouseEntity(boost::int32_t x
, boost::int32_t y
)
396 if (!visible() || !isEnabled())
401 //-------------------------------------------------
402 // Check our active and visible children first
403 //-------------------------------------------------
405 DisplayObjects actChars
;
406 getActiveCharacters(actChars
);
408 if ( ! actChars
.empty() )
410 std::sort(actChars
.begin(), actChars
.end(), charDepthLessThen
);
412 SWFMatrix m
= getMatrix(*this);
414 m
.invert().transform(p
);
416 for (DisplayObjects::reverse_iterator it
= actChars
.rbegin(),
417 itE
=actChars
.rend(); it
!=itE
; ++it
)
419 DisplayObject
* ch
= *it
;
420 if ( ! ch
->visible() ) continue;
421 InteractiveObject
*hit
= ch
->topmostMouseEntity(p
.x
, p
.y
);
422 if ( hit
) return hit
;
426 //-------------------------------------------------
427 // If that failed, check our hit area
428 //-------------------------------------------------
430 // Find hit DisplayObjects
431 if ( _hitCharacters
.empty() ) return 0;
433 // point is in p's space,
434 // we need to convert it in world space
436 DisplayObject
* p
= parent();
438 getWorldMatrix(*p
).transform(wp
);
441 for (DisplayObjects::const_iterator i
= _hitCharacters
.begin(),
442 e
= _hitCharacters
.end(); i
!=e
; ++i
)
444 if ((*i
)->pointInVisibleShape(wp
.x
, wp
.y
))
446 // The mouse is inside the shape.
456 Button::mouseEvent(const event_id
& event
)
460 // We don't respond to events while unloaded. See bug #22982.
461 log_debug("Button %s received %s button event while unloaded: ignored",
466 MouseState new_state
= _mouseState
;
468 // Set our mouse state (so we know how to render).
471 case event_id::ROLL_OUT
:
472 case event_id::RELEASE_OUTSIDE
:
473 new_state
= MOUSESTATE_UP
;
476 case event_id::RELEASE
:
477 case event_id::ROLL_OVER
:
478 case event_id::DRAG_OUT
:
479 case event_id::MOUSE_UP
:
480 new_state
= MOUSESTATE_OVER
;
483 case event_id::PRESS
:
484 case event_id::DRAG_OVER
:
485 case event_id::MOUSE_DOWN
:
486 new_state
= MOUSESTATE_DOWN
;
490 //abort(); // missed a case?
491 log_error(_("Unhandled button event %s"), event
);
495 set_current_state(new_state
);
497 // Button transition sounds.
500 if (!_def
->hasSound()) break;
502 // Check if there is a sound handler
503 sound::sound_handler
* s
= getRunResources(*getObject(this)).soundHandler();
506 int bi
; // button sound array index [0..3]
510 case event_id::ROLL_OUT
:
513 case event_id::ROLL_OVER
:
516 case event_id::PRESS
:
519 case event_id::RELEASE
:
527 // no sound for this transition
530 const SWF::DefineButtonSoundTag::ButtonSound
& bs
=
531 _def
->buttonSound(bi
);
533 // character zero is considered as null character
534 if (!bs
.soundID
) break;
537 if (!bs
.sample
) break;
539 if (bs
.soundInfo
.stopPlayback
) {
540 s
->stop_sound(bs
.sample
->m_sound_handler_id
);
543 const SWF::SoundInfoRecord
& sinfo
= bs
.soundInfo
;
545 const sound::SoundEnvelopes
* env
=
546 sinfo
.envelopes
.empty() ? 0 : &sinfo
.envelopes
;
548 s
->startSound(bs
.sample
->m_sound_handler_id
,
549 bs
.soundInfo
.loopCount
,
551 !sinfo
.noMultiple
, // allow multiple instances ?
559 // From: "ActionScript - The Definitive Guide" by Colin Moock
560 // (chapter 10: Events and Event Handlers)
562 // "Event-based code [..] is said to be executed asynchronously
563 // because the triggering of events can occur at arbitrary times."
565 // We'll push to the global list. The movie_root will process
566 // the action queue on mouse event.
569 movie_root
& mr
= stage();
571 ButtonActionPusher
xec(mr
, this);
572 _def
->forEachTrigger(event
, xec
);
574 // check for built-in event handler.
575 std::auto_ptr
<ExecutableCode
> code (get_event_handler(event
));
577 mr
.pushAction(code
, movie_root::PRIORITY_DOACTION
);
580 sendEvent(*getObject(this), get_environment(), event
.functionURI());
585 Button::getActiveCharacters(ConstDisplayObjects
& list
) const
589 // Copy all the DisplayObjects to the new list, skipping NULL and unloaded
591 std::remove_copy_if(_stateCharacters
.begin(), _stateCharacters
.end(),
592 std::back_inserter(list
),
593 boost::bind(&isCharacterNull
, _1
, false));
599 Button::getActiveCharacters(DisplayObjects
& list
, bool includeUnloaded
)
603 // Copy all the DisplayObjects to the new list, skipping NULL
604 // DisplayObjects, optionally including unloaded DisplayObjects.
605 std::remove_copy_if(_stateCharacters
.begin(), _stateCharacters
.end(),
606 std::back_inserter(list
),
607 boost::bind(&isCharacterNull
, _1
, includeUnloaded
));
612 Button::get_active_records(ActiveRecords
& list
, MouseState state
)
617 const DefineButtonTag::ButtonRecords
& br
= _def
->buttonRecords();
620 for (DefineButtonTag::ButtonRecords::const_iterator i
= br
.begin(),
621 e
= br
.end(); i
!= e
; ++i
, ++index
)
623 const ButtonRecord
& rec
=*i
;
624 if (rec
.hasState(state
)) list
.insert(index
);
628 #ifdef GNASH_DEBUG_BUTTON_DISPLAYLIST
629 static void dump(Button::DisplayObjects
& chars
, std::stringstream
& ss
)
631 for (size_t i
=0, e
=chars
.size(); i
<e
; ++i
)
633 ss
<< "Record" << i
<< ": ";
634 DisplayObject
* ch
= chars
[i
];
635 if ( ! ch
) ss
<< "NULL.";
638 ss
<< ch
->getTarget() << " (depth:" <<
639 ch
->get_depth()-DisplayObject::staticDepthOffset
-1
640 << " unloaded:" << ch
->unloaded() <<
641 " destroyed:" << ch
->isDestroyed() << ")";
649 Button::set_current_state(MouseState new_state
)
651 if (new_state
== _mouseState
)
654 #ifdef GNASH_DEBUG_BUTTON_DISPLAYLIST
655 std::stringstream ss
;
656 ss
<< "at set_current_state enter: " << std::endl
;
657 dump(_stateCharacters
, ss
);
658 log_debug("%s", ss
.str());
661 // Get new state records
662 ActiveRecords newChars
;
663 get_active_records(newChars
, new_state
);
665 // For each possible record, check if it should still be there
666 for (size_t i
=0, e
=_stateCharacters
.size(); i
<e
; ++i
)
668 DisplayObject
* oldch
= _stateCharacters
[i
];
669 bool shouldBeThere
= ( newChars
.find(i
) != newChars
.end() );
671 if ( ! shouldBeThere
)
674 // is there, but is unloaded: destroy, clear slot and go on
675 if ( oldch
&& oldch
->unloaded() ) {
676 removeInstanceProperty(*this, oldch
);
677 if ( ! oldch
->isDestroyed() ) oldch
->destroy();
678 _stateCharacters
[i
] = NULL
;
682 if ( oldch
) // the one we have should not be there... unload!
686 if ( ! oldch
->unload() )
688 // No onUnload handler: destroy and clear slot
689 removeInstanceProperty(*this, oldch
);
690 if (!oldch
->isDestroyed()) oldch
->destroy();
691 _stateCharacters
[i
] = NULL
;
695 // onUnload handler: shift depth and keep slot
696 int oldDepth
= oldch
->get_depth();
697 int newDepth
= DisplayObject::removedDepthOffset
- oldDepth
;
698 #ifdef GNASH_DEBUG_BUTTON_DISPLAYLIST
699 log_debug("Removed button record shifted from depth %d to depth %d", oldDepth
, newDepth
);
701 oldch
->set_depth(newDepth
);
705 else // should be there
707 // Is there already, but is unloaded: destroy and consider as gone
708 if ( oldch
&& oldch
->unloaded() )
710 removeInstanceProperty(*this, oldch
);
711 if ( ! oldch
->isDestroyed() ) oldch
->destroy();
712 _stateCharacters
[i
] = NULL
;
717 // Not there, instantiate
718 const SWF::ButtonRecord
& rec
= _def
->buttonRecords()[i
];
719 DisplayObject
* ch
= rec
.instantiate(this);
722 _stateCharacters
[i
] = ch
;
723 addInstanceProperty(*this, ch
);
729 #ifdef GNASH_DEBUG_BUTTON_DISPLAYLIST
731 ss
<< "at set_current_state end: " << std::endl
;
732 dump(_stateCharacters
, ss
);
733 log_debug("%s", ss
.str());
736 // Remember current state
737 _mouseState
=new_state
;
742 Button::add_invalidated_bounds(InvalidatedRanges
& ranges
, bool force
)
745 // Not visible anyway
746 if (!visible()) return;
748 ranges
.add(m_old_invalidated_ranges
);
750 DisplayObjects actChars
;
751 getActiveCharacters(actChars
);
752 std::for_each(actChars
.begin(), actChars
.end(),
753 boost::bind(&DisplayObject::add_invalidated_bounds
, _1
,
754 boost::ref(ranges
), force
|| invalidated())
759 Button::getBounds() const
763 typedef std::vector
<const DisplayObject
*> Chars
;
765 getActiveCharacters(actChars
);
766 for(Chars::const_iterator i
=actChars
.begin(),e
=actChars
.end(); i
!=e
; ++i
)
768 const DisplayObject
* ch
= *i
;
769 // Child bounds need be transformed in our coordinate space
770 SWFRect lclBounds
= ch
->getBounds();
771 SWFMatrix m
= getMatrix(*ch
);
772 allBounds
.expand_to_transformed_rect(m
, lclBounds
);
779 Button::pointInShape(boost::int32_t x
, boost::int32_t y
) const
781 typedef std::vector
<const DisplayObject
*> Chars
;
783 getActiveCharacters(actChars
);
784 for(Chars::const_iterator i
=actChars
.begin(),e
=actChars
.end(); i
!=e
; ++i
)
786 const DisplayObject
* ch
= *i
;
787 if (ch
->pointInShape(x
,y
)) return true;
793 Button::construct(as_object
* initObj
)
795 // This can happen if attachMovie is called with an exported Button and
796 // an init object. The attachment happens, but the init object is not used
797 // (see misc-ming.all/attachMovieTest.swf).
799 IF_VERBOSE_ASCODING_ERRORS(
800 log_aserror("Button placed with an init object. This will "
805 saveOriginalTarget(); // for soft refs
807 // Don't register this button instance as a live DisplayObject.
809 // Instantiate the hit DisplayObjects
810 ActiveRecords hitChars
;
811 get_active_records(hitChars
, MOUSESTATE_HIT
);
812 for (ActiveRecords::iterator i
=hitChars
.begin(),e
=hitChars
.end(); i
!=e
; ++i
)
814 const SWF::ButtonRecord
& rec
= _def
->buttonRecords()[*i
];
816 // These should not be named!
817 DisplayObject
* ch
= rec
.instantiate(this, false);
818 _hitCharacters
.push_back(ch
);
821 // Setup the state DisplayObjects container
822 // It will have a slot for each DisplayObject record.
823 // Some slots will probably be never used (consider HIT-only records)
824 // but for now this direct corrispondence between record number
825 // and active DisplayObject will be handy.
826 _stateCharacters
.resize(_def
->buttonRecords().size());
828 // Instantiate the default state DisplayObjects
829 ActiveRecords upChars
;
830 get_active_records(upChars
, MOUSESTATE_UP
);
832 for (ActiveRecords::iterator i
= upChars
.begin(), e
=upChars
.end();
836 const SWF::ButtonRecord
& rec
= _def
->buttonRecords()[rno
];
838 DisplayObject
* ch
= rec
.instantiate(this);
840 _stateCharacters
[rno
] = ch
;
841 addInstanceProperty(*this, ch
);
845 // There is no INITIALIZE/CONSTRUCT/LOAD/ENTERFRAME/UNLOAD event
850 Button::markOwnResources() const
853 // Mark state DisplayObjects as reachable
854 for (DisplayObjects::const_iterator i
= _stateCharacters
.begin(),
855 e
= _stateCharacters
.end(); i
!= e
; ++i
)
857 DisplayObject
* ch
= *i
;
858 if (ch
) ch
->setReachable();
861 // Mark hit DisplayObjects as reachable
862 std::for_each(_hitCharacters
.begin(), _hitCharacters
.end(),
863 std::mem_fun(&DisplayObject::setReachable
));
868 Button::unloadChildren()
871 bool childsHaveUnload
= false;
873 // We need to unload all children, or the global instance list
874 // will keep growing forever !
875 for (DisplayObjects::iterator i
= _stateCharacters
.begin(),
876 e
= _stateCharacters
.end(); i
!= e
; ++i
)
878 DisplayObject
* ch
= *i
;
879 if (!ch
|| ch
->unloaded()) continue;
880 if (ch
->unload()) childsHaveUnload
= true;
883 // NOTE: we don't need to ::unload or ::destroy here
884 // as the _hitCharacters are never placed on stage.
885 // As an optimization we might not even instantiate
886 // them, and only use the definition and the
887 // associated transform SWFMatrix... (would take
888 // hit instance off the GC).
889 _hitCharacters
.clear();
891 return childsHaveUnload
;
898 for (DisplayObjects::iterator i
= _stateCharacters
.begin(),
899 e
=_stateCharacters
.end(); i
!= e
; ++i
) {
900 DisplayObject
* ch
= *i
;
901 if (!ch
|| ch
->isDestroyed()) continue;
905 // NOTE: we don't need to ::unload or ::destroy here
906 // as the _hitCharacters are never placed on stage.
907 // As an optimization we might not even instantiate
908 // them, and only use the definition and the
909 // associated transform SWFMatrix... (would take
910 // hit instance off the GC).
911 _hitCharacters
.clear();
913 DisplayObject::destroy();
917 Button::getDefinitionVersion() const
919 return _def
->getSWFVersion();
923 button_ctor(const fn_call
& /*fn*/)
929 button_class_init(as_object
& global
, const ObjectURI
& uri
)
931 // This is going to be the global Button "class"/"function"
932 Global_as
& gl
= getGlobal(global
);
933 as_object
* proto
= createObject(gl
);
934 as_object
* cl
= gl
.createClass(&button_ctor
, proto
);
935 attachButtonInterface(*proto
);
937 // Register _global.MovieClip
938 global
.init_member(uri
, cl
, as_object::DefaultFlags
);
942 registerButtonNative(as_object
& global
)
944 VM
& vm
= getVM(global
);
945 vm
.registerNative(button_setTabIndex
, 105, 1);
946 vm
.registerNative(button_getTabIndex
, 105, 2);
947 vm
.registerNative(button_getDepth
, 105, 3);
948 vm
.registerNative(button_scale9Grid
, 105, 4);
949 vm
.registerNative(button_filters
, 105, 5);
950 vm
.registerNative(button_cacheAsBitmap
, 105, 6);
951 vm
.registerNative(button_blendMode
, 105, 7);
955 DisplayObject::InfoTree::iterator
956 Button::getMovieInfo(InfoTree
& tr
, InfoTree::iterator it
)
958 InfoTree::iterator selfIt
= DisplayObject::getMovieInfo(tr
, it
);
959 std::ostringstream os
;
961 DisplayObjects actChars
;
962 getActiveCharacters(actChars
, true);
963 std::sort(actChars
.begin(), actChars
.end(), charDepthLessThen
);
965 os
<< actChars
.size() << " active DisplayObjects for state " <<
966 mouseStateName(_mouseState
);
967 InfoTree::iterator localIter
= tr
.append_child(selfIt
,
968 std::make_pair(_("Button state"), os
.str()));
971 os
<< std::boolalpha
<< isEnabled();
972 localIter
= tr
.append_child(selfIt
, std::make_pair(_("Enabled"), os
.str()));
974 std::for_each(actChars
.begin(), actChars
.end(),
975 boost::bind(&DisplayObject::getMovieInfo
, _1
, tr
, localIter
));
983 Button::mouseStateName(MouseState s
)
987 case MOUSESTATE_UP
: return "UP";
988 case MOUSESTATE_DOWN
: return "DOWN";
989 case MOUSESTATE_OVER
: return "OVER";
990 case MOUSESTATE_HIT
: return "HIT";
991 default: std::abort();
998 button_blendMode(const fn_call
& fn
)
1000 Button
* obj
= ensure
<IsDisplayObject
<Button
> >(fn
);
1006 button_cacheAsBitmap(const fn_call
& fn
)
1008 Button
* obj
= ensure
<IsDisplayObject
<Button
> >(fn
);
1014 button_filters(const fn_call
& fn
)
1016 Button
* obj
= ensure
<IsDisplayObject
<Button
> >(fn
);
1022 button_scale9Grid(const fn_call
& fn
)
1024 Button
* obj
= ensure
<IsDisplayObject
<Button
> >(fn
);
1030 button_getTabIndex(const fn_call
& fn
)
1032 Button
* obj
= ensure
<IsDisplayObject
<Button
> >(fn
);
1038 button_setTabIndex(const fn_call
& fn
)
1040 Button
* obj
= ensure
<IsDisplayObject
<Button
> >(fn
);
1046 button_getDepth(const fn_call
& fn
)
1048 Button
* obj
= ensure
<IsDisplayObject
<Button
> >(fn
);
1053 } // anonymous namespace
1054 } // end of namespace gnash
1059 // c-basic-offset: 8
1061 // indent-tabs-mode: t