1
// button_character_instance.cpp: Mouse-sensitive buttons, for Gnash.
3
// Copyright (C) 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
5
// This program is free software; you can redistribute it and/or modify
6
// it under the terms of the GNU General Public License as published by
7
// the Free Software Foundation; either version 3 of the License, or
8
// (at your option) any later version.
10
// This program is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
// GNU General Public License for more details.
15
// You should have received a copy of the GNU General Public License
16
// along with this program; if not, write to the Free Software
17
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
#include "gnashconfig.h"
25
#include "button_character_instance.h"
26
#include "button_character_def.h"
27
#include "action.h" // for as_standard_member enum
30
#include "ActionExec.h"
31
#include "sprite_instance.h"
32
#include "movie_root.h"
34
#include "builtin_function.h"
35
#include "fn_call.h" // for shared ActionScript getter-setters
36
#include "GnashException.h" // for shared ActionScript getter-setters
37
#include "ExecutableCode.h"
38
#include "namedStrings.h"
40
/** \page buttons Buttons and mouse behaviour
42
Observations about button & mouse behavior
44
Entities that receive mouse events: only buttons and sprites, AFAIK
46
When the mouse button goes down, it becomes "captured" by whatever
47
element is topmost, directly below the mouse at that moment. While
48
the mouse is captured, no other entity receives mouse events,
49
regardless of how the mouse or other elements move.
51
The mouse remains captured until the mouse button goes up. The mouse
52
remains captured even if the element that captured it is removed from
55
If the mouse isn't above a button or sprite when the mouse button goes
56
down, then the mouse is captured by the background (i.e. mouse events
57
just don't get sent, until the mouse button goes up again).
61
+------------------+---------------+-------------------------------------+
62
| Event | Mouse Button | description |
63
=========================================================================
64
| onRollOver | up | sent to topmost entity when mouse |
65
| | | cursor initially goes over it |
66
+------------------+---------------+-------------------------------------+
67
| onRollOut | up | when mouse leaves entity, after |
69
+------------------+---------------+-------------------------------------+
70
| onPress | up -> down | sent to topmost entity when mouse |
71
| | | button goes down. onRollOver |
72
| | | always precedes onPress. Initiates |
73
| | | mouse capture. |
74
+------------------+---------------+-------------------------------------+
75
| onRelease | down -> up | sent to active entity if mouse goes |
76
| | | up while over the element |
77
+------------------+---------------+-------------------------------------+
78
| onDragOut | down | sent to active entity if mouse |
79
| | | is no longer over the entity |
80
+------------------+---------------+-------------------------------------+
81
| onReleaseOutside | down -> up | sent to active entity if mouse goes |
82
| | | up while not over the entity. |
83
| | | onDragOut always precedes |
84
| | | onReleaseOutside |
85
+------------------+---------------+-------------------------------------+
86
| onDragOver | down | sent to active entity if mouse is |
87
| | | dragged back over it after |
89
+------------------+---------------+-------------------------------------+
91
There is always one active entity at any given time (considering NULL to
92
be an active entity, representing the background, and other objects that
93
don't receive mouse events).
95
When the mouse button is up, the active entity is the topmost element
96
directly under the mouse pointer.
98
When the mouse button is down, the active entity remains whatever it
99
was when the button last went down.
101
The active entity is the only object that receives mouse events.
103
!!! The "trackAsMenu" property alters this behavior! If trackAsMenu
104
is set on the active entity, then onReleaseOutside is filtered out,
105
and onDragOver from another entity is allowed (from the background, or
106
another trackAsMenu entity). !!!
112
mouse_button_state = UP
113
mouse_inside_entity_state = false
115
if mouse_button_state == DOWN
117
// Handle trackAsMenu
118
if (active_entity->trackAsMenu)
119
possible_entity = topmost entity below mouse
120
if (possible_entity != active_entity && possible_entity->trackAsMenu)
121
// Transfer to possible entity
122
active_entity = possible_entity
123
active_entity->onDragOver()
124
mouse_inside_entity_state = true;
126
// Handle onDragOut, onDragOver
127
if (mouse_inside_entity_state == false)
128
if (mouse is actually inside the active_entity)
130
active_entity->onDragOver()
131
mouse_inside_entity_state = true;
133
else // mouse_inside_entity_state == true
134
if (mouse is actually outside the active_entity)
136
active_entity->onDragOut()
137
mouse_inside_entity_state = false;
139
// Handle onRelease, onReleaseOutside
140
if (mouse button is up)
141
if (mouse_inside_entity_state)
143
active_entity->onRelease()
146
if (active_entity->trackAsMenu == false)
147
active_entity->onReleaseOutside()
148
mouse_button_state = UP
150
if mouse_button_state == UP
151
new_active_entity = topmost entity below the mouse
152
if (new_active_entity != active_entity)
153
// onRollOut, onRollOver
154
active_entity->onRollOut()
155
active_entity = new_active_entity
156
active_entity->onRollOver()
159
if (mouse button is down)
161
active_entity->onPress()
162
mouse_inside_entity_state = true
163
mouse_button_state = DOWN
170
/// A couple of typedefs to make code neater
171
typedef button_character_instance Button;
172
typedef boost::intrusive_ptr<Button> ButtonPtr;
174
static bool charDepthLessThen(const character* ch1, const character* ch2)
176
return ch1->get_depth() < ch2->get_depth();
180
attachButtonInterface(as_object& o)
182
//int target_version = o.getVM().getSWFVersion();
184
boost::intrusive_ptr<builtin_function> gettersetter;
187
// Properties (TODO: move to appropriate SWF version section)
190
gettersetter = new builtin_function(&character::x_getset, NULL);
191
o.init_property("_x", *gettersetter, *gettersetter);
193
gettersetter = new builtin_function(&character::y_getset, NULL);
194
o.init_property("_y", *gettersetter, *gettersetter);
196
gettersetter = new builtin_function(&character::xscale_getset, NULL);
197
o.init_property("_xscale", *gettersetter, *gettersetter);
199
gettersetter = new builtin_function(&character::yscale_getset, NULL);
200
o.init_property("_yscale", *gettersetter, *gettersetter);
202
gettersetter = new builtin_function(&character::xmouse_get, NULL);
203
o.init_readonly_property("_xmouse", *gettersetter);
205
gettersetter = new builtin_function(&character::ymouse_get, NULL);
206
o.init_readonly_property("_ymouse", *gettersetter);
208
gettersetter = new builtin_function(&character::alpha_getset, NULL);
209
o.init_property("_alpha", *gettersetter, *gettersetter);
211
gettersetter = new builtin_function(&character::visible_getset, NULL);
212
o.init_property("_visible", *gettersetter, *gettersetter);
214
gettersetter = new builtin_function(&character::width_getset, NULL);
215
o.init_property("_width", *gettersetter, *gettersetter);
217
gettersetter = new builtin_function(&character::height_getset, NULL);
218
o.init_property("_height", *gettersetter, *gettersetter);
220
gettersetter = new builtin_function(&character::rotation_getset, NULL);
221
o.init_property("_rotation", *gettersetter, *gettersetter);
223
gettersetter = new builtin_function(&character::parent_getset, NULL);
224
o.init_property("_parent", *gettersetter, *gettersetter);
226
gettersetter = new builtin_function(&character::target_getset, NULL);
227
o.init_property("_target", *gettersetter, *gettersetter);
231
gettersetter = new builtin_function(&character::onrollover_getset, NULL);
232
o.init_property("onRollOver", *gettersetter, *gettersetter);
234
gettersetter = new builtin_function(&character::onrollout_getset, NULL);
235
o.init_property("onRollOut", *gettersetter, *gettersetter);
237
gettersetter = new builtin_function(&character::onpress_getset, NULL);
238
o.init_property("onPress", *gettersetter, *gettersetter);
240
gettersetter = new builtin_function(&character::onrelease_getset, NULL);
241
o.init_property("onRelease", *gettersetter, *gettersetter);
243
gettersetter = new builtin_function(&character::onreleaseoutside_getset, NULL);
244
o.init_property("onReleaseOutside", *gettersetter, *gettersetter);
246
gettersetter = new builtin_function(&character::onload_getset, NULL);
247
o.init_property("onLoad", *gettersetter, *gettersetter);
252
gettersetter = new builtin_function(&button_character_instance::enabled_getset, NULL);
253
o.init_property("enabled", *gettersetter, *gettersetter);
257
button_character_instance::button_character_instance(
258
button_character_definition* def,
259
character* parent, int id)
261
character(parent, id),
263
m_last_mouse_flags(IDLE),
270
attachButtonInterface(*this);
272
// check up presence Key events
273
// TODO: use a service of button_character_def, not this hard-coded thing here
274
for (size_t i = 0, e = m_def->m_button_actions.size(); i < e; ++i)
276
// TODO: use labels, not magic numbers here !!
277
if (m_def->m_button_actions[i]->m_conditions & 0xFE00) // check up on CondKeyPress: UB[7]
279
_vm.getRoot().add_key_listener(this);
286
button_character_instance::~button_character_instance()
288
_vm.getRoot().remove_key_listener(this);
293
button_character_instance::get_enabled()
299
button_character_instance::set_enabled(bool value)
301
if (value == m_enabled) return;
304
// NOTE: no visual change
309
button_character_instance::enabled_getset(const fn_call& fn)
311
ButtonPtr ptr = ensureType<Button>(fn.this_ptr);
315
if ( fn.nargs == 0 ) // getter
317
rv = as_value(ptr->get_enabled());
321
ptr->set_enabled(fn.arg(0).to_bool());
328
// called from Key listener only
329
// (the above line is wrong, it's also called with onConstruct, for instance)
331
button_character_instance::on_event(const event_id& id)
334
if( (id.m_id==event_id::KEY_PRESS) && (id.keyCode == key::INVALID) )
336
// onKeypress only responds to valid key code
342
// Add appropriate actions to the global action list ...
343
// TODO: should we execute immediately instead ?
344
for (size_t i = 0, ie=m_def->m_button_actions.size(); i<ie; ++i)
346
button_action& ba = *(m_def->m_button_actions[i]);
348
int keycode = (ba.m_conditions & 0xFE00) >> 9;
350
// Test match between button action conditions and the SWF code
351
// that maps to id.keyCode (the gnash unique key code).
352
if (id.m_id == event_id::KEY_PRESS && gnash::key::codeMap[id.keyCode][key::SWF] == keycode)
355
VM::get().getRoot().pushAction(ba.m_actions, boost::intrusive_ptr<character>(this));
364
button_character_instance::restart()
367
m_last_mouse_flags = IDLE;
368
m_mouse_flags = IDLE;
370
size_t r, r_num = m_record_character.size();
371
for (r = 0; r < r_num; r++)
373
m_record_character[r]->restart();
378
button_character_instance::display()
380
// GNASH_REPORT_FUNCTION;
382
std::vector<character*> actChars;
383
get_active_characters(actChars);
384
std::sort(actChars.begin(), actChars.end(), charDepthLessThen);
386
std::for_each(actChars.begin(), actChars.end(), std::mem_fun(&character::display));
393
button_character_instance::get_topmost_mouse_entity(float x, float y)
394
// Return the topmost entity that the given point covers. NULL if none.
395
// I.e. check against ourself.
397
if ( (!get_visible()) || (!get_enabled()))
402
//-------------------------------------------------
403
// Check our active and visible childrens first
404
//-------------------------------------------------
406
typedef std::vector<character*> Chars;
408
get_active_characters(actChars);
410
if ( ! actChars.empty() )
412
std::sort(actChars.begin(), actChars.end(), charDepthLessThen);
414
matrix m = get_matrix();
416
m.transform_by_inverse(&p, point(x, y));
418
for (Chars::reverse_iterator it=actChars.rbegin(), itE=actChars.rend(); it!=itE; ++it)
421
if ( ! ch->get_visible() ) continue;
422
character *hit = ch->get_topmost_mouse_entity(p.x, p.y);
423
if ( hit ) return hit;
427
//-------------------------------------------------
428
// If that failed, check our hit area
429
//-------------------------------------------------
431
// Find hit characters
433
get_active_characters(hitChars, HIT);
434
if ( hitChars.empty() ) return 0;
436
// point is in parent's space,
437
// we need to convert it in world space
439
character* parent = get_parent();
442
parent->get_world_matrix().transform(wp);
445
for (size_t i=0, e=hitChars.size(); i<e; ++i)
447
character* ch = hitChars[i];
449
if ( ch->pointInVisibleShape(wp.x, wp.y) )
451
// The mouse is inside the shape.
461
button_character_instance::on_button_event(const event_id& event)
463
e_mouse_state new_state = m_mouse_state;
465
// Set our mouse state (so we know how to render).
468
case event_id::ROLL_OUT:
469
case event_id::RELEASE_OUTSIDE:
473
case event_id::RELEASE:
474
case event_id::ROLL_OVER:
475
case event_id::DRAG_OUT:
476
case event_id::MOUSE_UP:
480
case event_id::PRESS:
481
case event_id::DRAG_OVER:
482
case event_id::MOUSE_DOWN:
487
//abort(); // missed a case?
488
log_error(_("Unhandled button event %s"), event.get_function_name().c_str());
493
set_current_state(new_state);
495
// Button transition sounds.
496
if (m_def->m_sound != NULL)
498
int bi; // button sound array index [0..3]
499
media::sound_handler* s = get_sound_handler();
501
// Check if there is a sound handler
505
case event_id::ROLL_OUT:
508
case event_id::ROLL_OVER:
511
case event_id::PRESS:
514
case event_id::RELEASE:
523
button_character_definition::button_sound_info& bs = m_def->m_sound->m_button_sounds[bi];
524
// character zero is considered as null character
525
if (bs.m_sound_id > 0)
527
if (m_def->m_sound->m_button_sounds[bi].m_sam != NULL)
529
if (bs.m_sound_style.m_stop_playback)
531
s->stop_sound(bs.m_sam->m_sound_handler_id);
535
s->play_sound(bs.m_sam->m_sound_handler_id, bs.m_sound_style.m_loop_count, 0, 0, (bs.m_sound_style.m_envelopes.size() == 0 ? NULL : &bs.m_sound_style.m_envelopes));
544
// @@ eh, should just be a lookup table.
546
if (event.m_id == event_id::ROLL_OVER) c |= (button_action::IDLE_TO_OVER_UP);
547
else if (event.m_id == event_id::ROLL_OUT) c |= (button_action::OVER_UP_TO_IDLE);
548
else if (event.m_id == event_id::PRESS) c |= (button_action::OVER_UP_TO_OVER_DOWN);
549
else if (event.m_id == event_id::RELEASE) c |= (button_action::OVER_DOWN_TO_OVER_UP);
550
else if (event.m_id == event_id::DRAG_OUT) c |= (button_action::OVER_DOWN_TO_OUT_DOWN);
551
else if (event.m_id == event_id::DRAG_OVER) c |= (button_action::OUT_DOWN_TO_OVER_DOWN);
552
else if (event.m_id == event_id::RELEASE_OUTSIDE) c |= (button_action::OUT_DOWN_TO_IDLE);
553
//IDLE_TO_OVER_DOWN = 1 << 7,
554
//OVER_DOWN_TO_IDLE = 1 << 8,
556
// From: "ActionScript - The Definiteve Guide" by Colin Moock
557
// (chapter 10: Events and Event Handlers)
559
// "Event-based code [..] is said to be executed asynchronously
560
// because the triggering of events can occur at arbitrary times."
562
// Immediately execute all events actions (don't append to
563
// parent's action buffer for later execution!)
565
for (size_t i = 0; i < m_def->m_button_actions.size(); i++)
567
button_action& ba = *(m_def->m_button_actions[i]);
569
if (ba.m_conditions & c)
572
action_buffer& ab = ba.m_actions;
574
log_action(_("Executing actions for "
575
"button condition %d"), c);
577
ActionExec exec(ab, get_environment());
582
// check for built-in event handler.
583
std::auto_ptr<ExecutableCode> code ( get_event_handler(event) );
590
//log_debug(_("No handler for event: %s"), event.get_function_name().c_str());
594
// Call conventional attached method.
595
boost::intrusive_ptr<as_function> method = getUserDefinedEventHandler(event.get_function_key());
598
call_method0(as_value(method.get()), &(get_environment()), this);
603
button_character_instance::get_active_characters(std::vector<character*>& list)
605
get_active_characters(list, m_mouse_state);
609
button_character_instance::get_active_characters(std::vector<character*>& list,
614
for (size_t i = 0; i < m_def->m_button_records.size(); i++)
616
button_record& rec = m_def->m_button_records[i];
617
assert(m_record_character.size() > i);
618
if (m_record_character[i] == NULL)
622
if ((state == UP && rec.m_up)
623
|| (state == DOWN && rec.m_down)
624
|| (state == OVER && rec.m_over)
625
|| (state == HIT && rec.m_hit_test))
627
list.push_back(m_record_character[i].get());
629
} // for button record
633
button_character_instance::set_current_state(e_mouse_state new_state)
635
if (new_state == m_mouse_state)
638
// save current "display list"
639
std::vector<character*> old_list;
640
get_active_characters(old_list, m_mouse_state);
642
// load new "display list"
643
// NOTE: We don't change state yet, so that set_invalidated() can
644
// load the current bounds first.
645
std::vector<character*> new_list;
646
get_active_characters(new_list, new_state);
648
// see if the two lists differ and restart characters if needed
649
if (new_list.size() != old_list.size())
650
set_invalidated(); // something changed
652
size_t old_count = old_list.size();
653
size_t new_count = new_list.size();
654
for (size_t i=0; i<new_count; i++) {
657
for (size_t j=0; j<old_count; j++) {
658
if (new_list[i] == old_list[j]) {
664
// character (re-)appeared on stage -> restart!
665
new_list[i]->restart();
670
// effectively change state
671
m_mouse_state=new_state;
676
// ActionScript overrides
682
button_character_instance::add_invalidated_bounds(InvalidatedRanges& ranges,
685
if (!m_visible) return; // not visible anyway
687
ranges.add(m_old_invalidated_ranges);
689
// TODO: Instead of using these for loops again and again, wouldn't it be a
690
// good idea to have a generic "get_record_character()" method?
691
for (size_t i = 0; i < m_def->m_button_records.size(); i++)
693
button_record& rec = m_def->m_button_records[i];
694
assert(m_record_character.size() > i);
695
if (m_record_character[i] == NULL)
699
if ((m_mouse_state == UP && rec.m_up)
700
|| (m_mouse_state == DOWN && rec.m_down)
701
|| (m_mouse_state == OVER && rec.m_over))
703
/*bounds->expand_to_transformed_rect(get_world_matrix(),
704
m_record_character[i]->get_bound());*/
705
m_record_character[i]->add_invalidated_bounds(ranges,
706
force||m_invalidated);
712
geometry::Range2d<float>
713
button_character_instance::getBounds() const
715
for (size_t i = 0; i < m_def->m_button_records.size(); i++)
717
button_record& rec = m_def->m_button_records[i];
718
assert(m_record_character.size() > i);
719
if (m_record_character[i] == NULL)
723
if ((m_mouse_state == UP && rec.m_up)
724
|| (m_mouse_state == DOWN && rec.m_down)
725
|| (m_mouse_state == OVER && rec.m_over))
727
// TODO: should we consider having multiple characters
728
// for a single state ?
729
return m_record_character[i]->getBounds();
732
return geometry::Range2d<float>(geometry::nullRange);
736
button_character_instance::pointInShape(float x, float y) const
738
for (size_t i = 0; i < m_def->m_button_records.size(); i++)
740
button_record& rec = m_def->m_button_records[i];
741
assert(m_record_character.size() > i);
742
if (m_record_character[i] == NULL)
746
if ((m_mouse_state == UP && rec.m_up)
747
|| (m_mouse_state == DOWN && rec.m_down)
748
|| (m_mouse_state == OVER && rec.m_over))
750
// TODO: should we consider having multiple characters
751
// for a single state ?
752
return m_record_character[i]->pointInShape(x, y);
755
return false; // no shape
759
button_character_instance::get_path_element(string_table::key key)
761
as_object* ch = get_path_element_character(key);
764
std::string name = _vm.getStringTable().value(key);
765
return getChildByName(name); // possibly NULL
769
button_character_instance::getChildByName(const std::string& name) const
771
// See if we have a match on the button records list
772
for (size_t i=0, n=m_record_character.size(); i<n; ++i)
774
character* child = m_record_character[i].get();
775
const char* pat_c = child->get_name().c_str();
776
const char* nam_c = name.c_str();
778
if ( _vm.getSWFVersion() >= 7 )
780
if (! strcmp(pat_c, nam_c) ) return child;
784
if ( ! strcasecmp(pat_c, nam_c) ) return child;
792
button_character_instance::stagePlacementCallback()
794
saveOriginalTarget(); // for soft refs
796
// Register this button instance as a live character
797
// do we need this???
798
//_vm.getRoot().addLiveChar(this);
800
size_t r, r_num = m_def->m_button_records.size();
801
m_record_character.resize(r_num);
803
for (r = 0; r < r_num; r++)
805
button_record& bdef = m_def->m_button_records[r];
807
const matrix& mat = bdef.m_button_matrix;
808
const cxform& cx = bdef.m_button_cxform;
809
int ch_depth = bdef.m_button_layer;
810
int ch_id = bdef.m_character_id;
812
boost::intrusive_ptr<character> ch = bdef.m_character_def->create_character_instance(this, ch_id);
815
ch->set_depth(ch_depth);
816
assert(ch->get_parent() == this);
818
if (ch->get_name().empty() && ch->wantsInstanceName())
820
std::string instance_name = getNextUnnamedInstanceName();
821
ch->set_name(instance_name.c_str());
824
m_record_character[r] = ch;
826
ch->stagePlacementCallback(); // give this character life (TODO: they aren't on stage, are them ?)
829
// there's no INITIALIZE/CONSTRUCT/LOAD/ENTERFRAME/UNLOAD events for buttons
834
button_character_instance::markReachableResources() const
836
assert(isReachable());
838
m_def->setReachable();
840
// Markstate characters as reachable
841
for (CharsVect::const_iterator i=m_record_character.begin(), e=m_record_character.end();
844
(*i)->setReachable();
847
// character class members
848
markCharacterReachable();
850
#endif // GNASH_USE_GC
853
button_character_instance::unload()
855
bool childsHaveUnload = false;
857
// We need to unload all childs, or the global instance list will keep growing forever !
858
//std::for_each(m_record_character.begin(), m_record_character.end(), boost::bind(&character::unload, _1));
859
for (CharsVect::iterator i=m_record_character.begin(), e=m_record_character.end(); i!=e; ++i)
861
boost::intrusive_ptr<character> ch = *i;
862
if ( ch->unload() ) childsHaveUnload = true;
863
//log_debug("Button child %s (%s) unloaded", ch->getTarget().c_str(), typeName(*ch).c_str());
866
bool hasUnloadEvent = character::unload();
868
return hasUnloadEvent || childsHaveUnload;
872
button_character_instance::get_member(string_table::key name_key, as_value* val,
873
string_table::key nsname)
875
// FIXME: use addProperty interface for these !!
876
// TODO: or at least have a character:: protected method take
878
// Duplicates code in character::get_path_element_character too..
880
if (name_key == NSV::PROP_uROOT)
883
// Let ::get_root() take care of _lockroot
884
movie_instance* relRoot = get_root();
885
val->set_as_object( relRoot );
889
// NOTE: availability of _global doesn't depend on VM version
890
// but on actual movie version. Example: if an SWF4 loads
891
// an SWF6 (to, say, _level2), _global will be unavailable
892
// to the SWF4 code but available to the SWF6 one.
894
if ( getSWFVersion() > 5 && name_key == NSV::PROP_uGLOBAL ) // see MovieClip.as
896
// The "_global" ref was added in SWF6
897
val->set_as_object( _vm.getGlobal() );
901
const std::string& name = _vm.getStringTable().value(name_key);
903
movie_root& mr = _vm.getRoot();
904
unsigned int levelno;
905
if ( mr.isLevelTarget(name, levelno) )
907
movie_instance* mo = mr.getLevel(levelno).get();
910
val->set_as_object(mo);
919
// TOCHECK : Try object members, BEFORE display list items
921
if (get_member_default(name_key, val, nsname))
924
// ... trying to be useful to Flash coders ...
925
// The check should actually be performed before any return
926
// prior to the one due to a match in the DisplayList.
927
// It's off by default anyway, so not a big deal.
929
#define CHECK_FOR_NAME_CLASHES 1
930
#ifdef CHECK_FOR_NAME_CLASHES
931
IF_VERBOSE_ASCODING_ERRORS(
932
if ( getChildByName(name) )
934
log_aserror(_("A button member (%s) clashes with "
935
"the name of an existing character "
936
"in its display list. "
937
"The member will hide the "
938
"character"), name.c_str());
947
// Try items on our display list.
948
character* ch = getChildByName(name);
954
// If the object is an ActionScript referenciable one we
955
// return it, otherwise we return ourselves
956
if ( ch->isActionScriptReferenceable() )
958
val->set_as_object(ch);
962
val->set_as_object(this);
973
button_character_instance::getSWFVersion() const
975
return m_def->getSWFVersion();
978
} // end of namespace gnash
985
// indent-tabs-mode: t