~ubuntu-branches/ubuntu/saucy/gnash/saucy-proposed

« back to all changes in this revision

Viewing changes to server/button_character_instance.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Alexander Sack
  • Date: 2008-10-13 14:29:49 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20081013142949-f6qdvnu4mn05ltdc
Tags: 0.8.4~~bzr9980-0ubuntu1
* new upstream release 0.8.4 (LP: #240325)
* ship new lib usr/lib/gnash/libmozsdk.so.* in mozilla-plugin-gnash
  - update debian/mozilla-plugin-gnash.install
* ship new lib usr/lib/gnash/libgnashnet.so.* in gnash-common
  - update debian/gnash-common.install
* add basic debian/build_head script to build latest CVS head packages.
  - add debian/build_head
* new sound architecture requires build depend on libsdl1.2-dev
  - update debian/control
* head build script now has been completely migrated to bzr (upstream +
  ubuntu)
  - update debian/build_head
* disable kde gui until klash/qt4 has been fixed; keep kde packages as empty
  packages for now.
  - update debian/rules
  - debian/klash.install
  - debian/klash.links
  - debian/klash.manpages
  - debian/konqueror-plugin-gnash.install
* drop libkonq5-dev build dependency accordingly
  - update debian/control
* don't install headers manually anymore. gnash doesnt provide a -dev
  package after all
  - update debian/rules
* update libs installed in gnash-common; libgnashserver-*.so is not available
  anymore (removed); in turn we add the new libgnashcore-*.so
  - update debian/gnash-common.install
* use -Os for optimization and properly pass CXXFLAGS=$(CFLAGS) to configure
  - update debian/rules
* touch firefox .autoreg in postinst of mozilla plugin
  - update debian/mozilla-plugin-gnash.postinst
* link gnash in ubufox plugins directory for the plugin alternative switcher
  - add debian/mozilla-plugin-gnash.links
* suggest ubufox accordingly
  - update debian/control
* add new required build-depends on libgif-dev
  - update debian/control
* add Xb-Npp-Description and Xb-Npp-File as new plugin database meta data
  - update debian/control

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// button_character_instance.cpp:  Mouse-sensitive buttons, for Gnash.
2
 
// 
3
 
//   Copyright (C) 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
4
 
// 
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.
9
 
// 
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.
14
 
//
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
18
 
//
19
 
 
20
 
#ifdef HAVE_CONFIG_H
21
 
#include "gnashconfig.h"
22
 
#endif
23
 
 
24
 
 
25
 
#include "button_character_instance.h"
26
 
#include "button_character_def.h"
27
 
#include "action.h" // for as_standard_member enum
28
 
#include "as_value.h"
29
 
 
30
 
#include "ActionExec.h"
31
 
#include "sprite_instance.h"
32
 
#include "movie_root.h"
33
 
#include "VM.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"
39
 
 
40
 
/** \page buttons Buttons and mouse behaviour
41
 
 
42
 
Observations about button & mouse behavior
43
 
 
44
 
Entities that receive mouse events: only buttons and sprites, AFAIK
45
 
 
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.
50
 
 
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
53
 
the display list.
54
 
 
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).
58
 
 
59
 
Mouse events:
60
 
 
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     |
68
 
|                  |               | onRollOver                          |
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          |
88
 
|                  |               | onDragOut                           |
89
 
+------------------+---------------+-------------------------------------+
90
 
 
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).
94
 
 
95
 
When the mouse button is up, the active entity is the topmost element
96
 
directly under the mouse pointer.
97
 
 
98
 
When the mouse button is down, the active entity remains whatever it
99
 
was when the button last went down.
100
 
 
101
 
The active entity is the only object that receives mouse events.
102
 
 
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). !!!
107
 
 
108
 
 
109
 
Pseudocode:
110
 
 
111
 
active_entity = NULL
112
 
mouse_button_state = UP
113
 
mouse_inside_entity_state = false
114
 
frame loop:
115
 
  if mouse_button_state == DOWN
116
 
 
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;
125
 
 
126
 
    // Handle onDragOut, onDragOver
127
 
    if (mouse_inside_entity_state == false)
128
 
      if (mouse is actually inside the active_entity)
129
 
        // onDragOver
130
 
        active_entity->onDragOver()
131
 
        mouse_inside_entity_state = true;
132
 
 
133
 
    else // mouse_inside_entity_state == true
134
 
      if (mouse is actually outside the active_entity)
135
 
        // onDragOut
136
 
        active_entity->onDragOut()
137
 
        mouse_inside_entity_state = false;
138
 
 
139
 
    // Handle onRelease, onReleaseOutside
140
 
    if (mouse button is up)
141
 
      if (mouse_inside_entity_state)
142
 
        // onRelease
143
 
        active_entity->onRelease()
144
 
      else
145
 
        // onReleaseOutside
146
 
        if (active_entity->trackAsMenu == false)
147
 
          active_entity->onReleaseOutside()
148
 
      mouse_button_state = UP
149
 
    
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()
157
 
    
158
 
    // Handle press
159
 
    if (mouse button is down)
160
 
      // onPress
161
 
      active_entity->onPress()
162
 
      mouse_inside_entity_state = true
163
 
      mouse_button_state = DOWN
164
 
 
165
 
*/
166
 
 
167
 
 
168
 
namespace gnash {
169
 
 
170
 
/// A couple of typedefs to make code neater
171
 
typedef button_character_instance Button;
172
 
typedef boost::intrusive_ptr<Button> ButtonPtr;
173
 
 
174
 
static bool charDepthLessThen(const character* ch1, const character* ch2) 
175
 
{
176
 
        return ch1->get_depth() < ch2->get_depth();
177
 
}
178
 
 
179
 
static void
180
 
attachButtonInterface(as_object& o)
181
 
{
182
 
        //int target_version = o.getVM().getSWFVersion();
183
 
 
184
 
        boost::intrusive_ptr<builtin_function> gettersetter;
185
 
 
186
 
        //
187
 
        // Properties (TODO: move to appropriate SWF version section)
188
 
        //
189
 
 
190
 
        gettersetter = new builtin_function(&character::x_getset, NULL);
191
 
        o.init_property("_x", *gettersetter, *gettersetter);
192
 
 
193
 
        gettersetter = new builtin_function(&character::y_getset, NULL);
194
 
        o.init_property("_y", *gettersetter, *gettersetter);
195
 
 
196
 
        gettersetter = new builtin_function(&character::xscale_getset, NULL);
197
 
        o.init_property("_xscale", *gettersetter, *gettersetter);
198
 
 
199
 
        gettersetter = new builtin_function(&character::yscale_getset, NULL);
200
 
        o.init_property("_yscale", *gettersetter, *gettersetter);
201
 
 
202
 
        gettersetter = new builtin_function(&character::xmouse_get, NULL);
203
 
        o.init_readonly_property("_xmouse", *gettersetter);
204
 
 
205
 
        gettersetter = new builtin_function(&character::ymouse_get, NULL);
206
 
        o.init_readonly_property("_ymouse", *gettersetter);
207
 
 
208
 
        gettersetter = new builtin_function(&character::alpha_getset, NULL);
209
 
        o.init_property("_alpha", *gettersetter, *gettersetter);
210
 
 
211
 
        gettersetter = new builtin_function(&character::visible_getset, NULL);
212
 
        o.init_property("_visible", *gettersetter, *gettersetter);
213
 
 
214
 
        gettersetter = new builtin_function(&character::width_getset, NULL);
215
 
        o.init_property("_width", *gettersetter, *gettersetter);
216
 
 
217
 
        gettersetter = new builtin_function(&character::height_getset, NULL);
218
 
        o.init_property("_height", *gettersetter, *gettersetter);
219
 
 
220
 
        gettersetter = new builtin_function(&character::rotation_getset, NULL);
221
 
        o.init_property("_rotation", *gettersetter, *gettersetter);
222
 
 
223
 
        gettersetter = new builtin_function(&character::parent_getset, NULL);
224
 
        o.init_property("_parent", *gettersetter, *gettersetter);
225
 
        
226
 
        gettersetter = new builtin_function(&character::target_getset, NULL);
227
 
        o.init_property("_target", *gettersetter, *gettersetter);
228
 
        
229
 
 
230
 
#if 0
231
 
        gettersetter = new builtin_function(&character::onrollover_getset, NULL);
232
 
        o.init_property("onRollOver", *gettersetter, *gettersetter);
233
 
 
234
 
        gettersetter = new builtin_function(&character::onrollout_getset, NULL);
235
 
        o.init_property("onRollOut", *gettersetter, *gettersetter);
236
 
 
237
 
        gettersetter = new builtin_function(&character::onpress_getset, NULL);
238
 
        o.init_property("onPress", *gettersetter, *gettersetter);
239
 
 
240
 
        gettersetter = new builtin_function(&character::onrelease_getset, NULL);
241
 
        o.init_property("onRelease", *gettersetter, *gettersetter);
242
 
 
243
 
        gettersetter = new builtin_function(&character::onreleaseoutside_getset, NULL);
244
 
        o.init_property("onReleaseOutside", *gettersetter, *gettersetter);
245
 
 
246
 
        gettersetter = new builtin_function(&character::onload_getset, NULL);
247
 
        o.init_property("onLoad", *gettersetter, *gettersetter);
248
 
#endif
249
 
 
250
 
        //--
251
 
 
252
 
        gettersetter = new builtin_function(&button_character_instance::enabled_getset, NULL);
253
 
        o.init_property("enabled", *gettersetter, *gettersetter);
254
 
 
255
 
}
256
 
 
257
 
button_character_instance::button_character_instance(
258
 
                button_character_definition* def,
259
 
                character* parent, int id)
260
 
        :
261
 
        character(parent, id),
262
 
        m_def(def),
263
 
        m_last_mouse_flags(IDLE),
264
 
        m_mouse_flags(IDLE),
265
 
        m_mouse_state(UP),
266
 
        m_enabled(true)
267
 
{
268
 
        assert(m_def);
269
 
 
270
 
        attachButtonInterface(*this);
271
 
 
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)
275
 
        {
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]
278
 
                {
279
 
                        _vm.getRoot().add_key_listener(this);
280
 
                        break;
281
 
                }
282
 
        }
283
 
 
284
 
}
285
 
 
286
 
button_character_instance::~button_character_instance()
287
 
{
288
 
        _vm.getRoot().remove_key_listener(this);
289
 
}
290
 
 
291
 
 
292
 
bool 
293
 
button_character_instance::get_enabled()
294
 
{
295
 
        return m_enabled;
296
 
}
297
 
 
298
 
void 
299
 
button_character_instance::set_enabled(bool value)
300
 
{
301
 
        if (value == m_enabled) return;
302
 
        m_enabled = value; 
303
 
        
304
 
        // NOTE: no visual change
305
 
}
306
 
 
307
 
 
308
 
as_value
309
 
button_character_instance::enabled_getset(const fn_call& fn)
310
 
{
311
 
        ButtonPtr ptr = ensureType<Button>(fn.this_ptr);
312
 
 
313
 
        as_value rv;
314
 
 
315
 
        if ( fn.nargs == 0 ) // getter
316
 
        {
317
 
                rv = as_value(ptr->get_enabled());
318
 
        }
319
 
        else // setter
320
 
        {
321
 
                ptr->set_enabled(fn.arg(0).to_bool());
322
 
        }
323
 
        return rv;
324
 
}
325
 
 
326
 
 
327
 
 
328
 
// called from Key listener only
329
 
// (the above line is wrong, it's also called with onConstruct, for instance)
330
 
bool
331
 
button_character_instance::on_event(const event_id& id)
332
 
{
333
 
 
334
 
        if( (id.m_id==event_id::KEY_PRESS) && (id.keyCode == key::INVALID) )
335
 
        {
336
 
                // onKeypress only responds to valid key code
337
 
                return false;
338
 
        }
339
 
 
340
 
        bool called = false;
341
 
 
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)
345
 
        {
346
 
                button_action& ba = *(m_def->m_button_actions[i]);
347
 
 
348
 
                int keycode = (ba.m_conditions & 0xFE00) >> 9;
349
 
                
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)
353
 
                {
354
 
                        // Matching action.
355
 
                        VM::get().getRoot().pushAction(ba.m_actions, boost::intrusive_ptr<character>(this));
356
 
                        called = true;
357
 
                }
358
 
        }
359
 
 
360
 
        return called;
361
 
}
362
 
 
363
 
void
364
 
button_character_instance::restart()
365
 
{
366
 
  set_invalidated();
367
 
        m_last_mouse_flags = IDLE;
368
 
        m_mouse_flags = IDLE;
369
 
        m_mouse_state = UP;
370
 
        size_t r, r_num =  m_record_character.size();
371
 
        for (r = 0; r < r_num; r++)
372
 
        {
373
 
                m_record_character[r]->restart();
374
 
        }
375
 
}
376
 
 
377
 
void
378
 
button_character_instance::display()
379
 
{
380
 
//      GNASH_REPORT_FUNCTION;
381
 
 
382
 
        std::vector<character*> actChars;
383
 
        get_active_characters(actChars);
384
 
        std::sort(actChars.begin(), actChars.end(), charDepthLessThen);
385
 
 
386
 
        std::for_each(actChars.begin(), actChars.end(), std::mem_fun(&character::display)); 
387
 
 
388
 
        clear_invalidated();
389
 
}
390
 
 
391
 
 
392
 
character*
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.
396
 
{
397
 
        if ( (!get_visible()) || (!get_enabled()))
398
 
        {
399
 
                return 0;
400
 
        }
401
 
 
402
 
        //-------------------------------------------------
403
 
        // Check our active and visible childrens first
404
 
        //-------------------------------------------------
405
 
 
406
 
        typedef std::vector<character*> Chars;
407
 
        Chars actChars;
408
 
        get_active_characters(actChars);
409
 
 
410
 
        if ( ! actChars.empty() )
411
 
        {
412
 
                std::sort(actChars.begin(), actChars.end(), charDepthLessThen);
413
 
 
414
 
                matrix  m = get_matrix();
415
 
                point p;
416
 
                m.transform_by_inverse(&p, point(x, y));
417
 
 
418
 
                for (Chars::reverse_iterator it=actChars.rbegin(), itE=actChars.rend(); it!=itE; ++it)
419
 
                {
420
 
                        character* ch = *it;
421
 
                        if ( ! ch->get_visible() ) continue;
422
 
                        character *hit = ch->get_topmost_mouse_entity(p.x, p.y);
423
 
                        if ( hit ) return hit;
424
 
                }
425
 
        }
426
 
 
427
 
        //-------------------------------------------------
428
 
        // If that failed, check our hit area
429
 
        //-------------------------------------------------
430
 
 
431
 
        // Find hit characters
432
 
        Chars hitChars;
433
 
        get_active_characters(hitChars, HIT);
434
 
        if ( hitChars.empty() ) return 0;
435
 
 
436
 
        // point is in parent's space,
437
 
        // we need to convert it in world space
438
 
        point wp(x,y);
439
 
        character* parent = get_parent();
440
 
        if ( parent )
441
 
        {
442
 
                parent->get_world_matrix().transform(wp);
443
 
        }
444
 
 
445
 
        for (size_t i=0, e=hitChars.size(); i<e; ++i)
446
 
        {
447
 
                character* ch = hitChars[i];
448
 
 
449
 
                if ( ch->pointInVisibleShape(wp.x, wp.y) )
450
 
                {
451
 
                        // The mouse is inside the shape.
452
 
                        return this;
453
 
                }
454
 
        }
455
 
 
456
 
        return NULL;
457
 
}
458
 
 
459
 
 
460
 
void
461
 
button_character_instance::on_button_event(const event_id& event)
462
 
{
463
 
  e_mouse_state new_state = m_mouse_state;
464
 
  
465
 
        // Set our mouse state (so we know how to render).
466
 
        switch (event.m_id)
467
 
        {
468
 
        case event_id::ROLL_OUT:
469
 
        case event_id::RELEASE_OUTSIDE:
470
 
                new_state = UP;
471
 
                break;
472
 
 
473
 
        case event_id::RELEASE:
474
 
        case event_id::ROLL_OVER:
475
 
        case event_id::DRAG_OUT:
476
 
        case event_id::MOUSE_UP:
477
 
                new_state = OVER;
478
 
                break;
479
 
 
480
 
        case event_id::PRESS:
481
 
        case event_id::DRAG_OVER:
482
 
        case event_id::MOUSE_DOWN:
483
 
                new_state = DOWN;
484
 
                break;
485
 
 
486
 
        default:
487
 
                //abort();      // missed a case?
488
 
                log_error(_("Unhandled button event %s"), event.get_function_name().c_str());
489
 
                break;
490
 
        };
491
 
        
492
 
        
493
 
        set_current_state(new_state);
494
 
    
495
 
        // Button transition sounds.
496
 
        if (m_def->m_sound != NULL)
497
 
        {
498
 
                int bi; // button sound array index [0..3]
499
 
                media::sound_handler* s = get_sound_handler();
500
 
 
501
 
                // Check if there is a sound handler
502
 
                if (s != NULL) {
503
 
                        switch (event.m_id)
504
 
                        {
505
 
                        case event_id::ROLL_OUT:
506
 
                                bi = 0;
507
 
                                break;
508
 
                        case event_id::ROLL_OVER:
509
 
                                bi = 1;
510
 
                                break;
511
 
                        case event_id::PRESS:
512
 
                                bi = 2;
513
 
                                break;
514
 
                        case event_id::RELEASE:
515
 
                                bi = 3;
516
 
                                break;
517
 
                        default:
518
 
                                bi = -1;
519
 
                                break;
520
 
                        }
521
 
                        if (bi >= 0)
522
 
                        {
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)
526
 
                                {
527
 
                                        if (m_def->m_sound->m_button_sounds[bi].m_sam != NULL)
528
 
                                        {
529
 
                                                if (bs.m_sound_style.m_stop_playback)
530
 
                                                {
531
 
                                                        s->stop_sound(bs.m_sam->m_sound_handler_id);
532
 
                                                }
533
 
                                                else
534
 
                                                {
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));
536
 
                                                }
537
 
                                        }
538
 
                                }
539
 
                        }
540
 
                }
541
 
        }
542
 
 
543
 
 
544
 
        // @@ eh, should just be a lookup table.
545
 
        int     c = 0;
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,
555
 
 
556
 
        // From: "ActionScript - The Definiteve Guide" by Colin Moock
557
 
        // (chapter 10: Events and Event Handlers)
558
 
 
559
 
        // "Event-based code [..] is said to be executed asynchronously
560
 
        //  because the triggering of events can occur at arbitrary times."
561
 
 
562
 
        // Immediately execute all events actions (don't append to
563
 
        // parent's action buffer for later execution!)
564
 
 
565
 
        for (size_t i = 0; i < m_def->m_button_actions.size(); i++)
566
 
        {
567
 
                button_action& ba = *(m_def->m_button_actions[i]);
568
 
 
569
 
                if (ba.m_conditions & c)
570
 
                {
571
 
                        // Matching action.
572
 
                        action_buffer& ab = ba.m_actions;
573
 
                        IF_VERBOSE_ACTION(
574
 
                                log_action(_("Executing actions for "
575
 
                                        "button condition %d"), c);
576
 
                        );
577
 
                        ActionExec exec(ab, get_environment());
578
 
                        exec();
579
 
                }
580
 
        }
581
 
 
582
 
        // check for built-in event handler.
583
 
        std::auto_ptr<ExecutableCode> code ( get_event_handler(event) );
584
 
        if ( code.get() )
585
 
        {
586
 
                code->execute();
587
 
        }
588
 
        else
589
 
        {
590
 
                //log_debug(_("No handler for event: %s"), event.get_function_name().c_str());
591
 
        }
592
 
 
593
 
 
594
 
        // Call conventional attached method.
595
 
        boost::intrusive_ptr<as_function> method = getUserDefinedEventHandler(event.get_function_key());
596
 
        if ( method )
597
 
        {
598
 
                call_method0(as_value(method.get()), &(get_environment()), this);
599
 
        }
600
 
}
601
 
 
602
 
void 
603
 
button_character_instance::get_active_characters(std::vector<character*>& list)
604
 
{
605
 
        get_active_characters(list, m_mouse_state);
606
 
}
607
 
 
608
 
void 
609
 
button_character_instance::get_active_characters(std::vector<character*>& list,
610
 
  e_mouse_state state)
611
 
{
612
 
        list.clear();
613
 
        
614
 
        for (size_t i = 0; i < m_def->m_button_records.size(); i++)
615
 
        {
616
 
                button_record&  rec = m_def->m_button_records[i];
617
 
                assert(m_record_character.size() > i);
618
 
                if (m_record_character[i] == NULL)
619
 
                {
620
 
                        continue;
621
 
                }
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))
626
 
                {
627
 
                        list.push_back(m_record_character[i].get());
628
 
                }
629
 
        } // for button record  
630
 
}
631
 
 
632
 
void
633
 
button_character_instance::set_current_state(e_mouse_state new_state)
634
 
{
635
 
        if (new_state == m_mouse_state)
636
 
                return;
637
 
                
638
 
        // save current "display list"
639
 
        std::vector<character*> old_list;
640
 
        get_active_characters(old_list, m_mouse_state);
641
 
        
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);
647
 
                
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 
651
 
  
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++) {
655
 
 
656
 
        bool found=false;
657
 
        for (size_t j=0; j<old_count; j++) { 
658
 
                if (new_list[i] == old_list[j]) {
659
 
                                found=true;
660
 
                                break; 
661
 
                        }
662
 
                }
663
 
                if (!found) {
664
 
                        // character (re-)appeared on stage -> restart!
665
 
                        new_list[i]->restart();
666
 
                        set_invalidated();
667
 
                } 
668
 
        }
669
 
 
670
 
        // effectively change state
671
 
        m_mouse_state=new_state;
672
 
         
673
 
}
674
 
 
675
 
//
676
 
// ActionScript overrides
677
 
//
678
 
 
679
 
 
680
 
 
681
 
void 
682
 
button_character_instance::add_invalidated_bounds(InvalidatedRanges& ranges, 
683
 
        bool force)
684
 
{
685
 
  if (!m_visible) return; // not visible anyway
686
 
 
687
 
        ranges.add(m_old_invalidated_ranges);  
688
 
 
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++)
692
 
        {
693
 
                button_record&  rec = m_def->m_button_records[i];
694
 
                assert(m_record_character.size() > i);
695
 
                if (m_record_character[i] == NULL)
696
 
                {
697
 
                        continue;
698
 
                }
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))
702
 
                {
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);        
707
 
                }
708
 
        }
709
 
 
710
 
}
711
 
 
712
 
geometry::Range2d<float>
713
 
button_character_instance::getBounds() const
714
 
{
715
 
        for (size_t i = 0; i < m_def->m_button_records.size(); i++)
716
 
        {
717
 
                button_record&  rec = m_def->m_button_records[i];
718
 
                assert(m_record_character.size() > i);
719
 
                if (m_record_character[i] == NULL)
720
 
                {
721
 
                        continue;
722
 
                }
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))
726
 
                {
727
 
                        // TODO: should we consider having multiple characters
728
 
                        //       for a single state ?
729
 
                        return m_record_character[i]->getBounds();
730
 
                }
731
 
        }
732
 
        return geometry::Range2d<float>(geometry::nullRange);
733
 
}
734
 
 
735
 
bool
736
 
button_character_instance::pointInShape(float x, float y) const
737
 
{
738
 
        for (size_t i = 0; i < m_def->m_button_records.size(); i++)
739
 
        {
740
 
                button_record&  rec = m_def->m_button_records[i];
741
 
                assert(m_record_character.size() > i);
742
 
                if (m_record_character[i] == NULL)
743
 
                {
744
 
                        continue;
745
 
                }
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))
749
 
                {
750
 
                        // TODO: should we consider having multiple characters
751
 
                        //       for a single state ?
752
 
                        return m_record_character[i]->pointInShape(x, y);
753
 
                }
754
 
        }
755
 
        return false; // no shape
756
 
}
757
 
 
758
 
as_object*
759
 
button_character_instance::get_path_element(string_table::key key)
760
 
{
761
 
        as_object* ch = get_path_element_character(key);
762
 
        if ( ch ) return ch;
763
 
 
764
 
        std::string name = _vm.getStringTable().value(key);
765
 
        return getChildByName(name); // possibly NULL
766
 
}
767
 
 
768
 
character *
769
 
button_character_instance::getChildByName(const std::string& name) const
770
 
{
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)
773
 
        {
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();
777
 
 
778
 
                if ( _vm.getSWFVersion() >= 7 )
779
 
                {
780
 
                        if (! strcmp(pat_c, nam_c) ) return child;
781
 
                }
782
 
                else
783
 
                {
784
 
                        if ( ! strcasecmp(pat_c, nam_c) ) return child;
785
 
                }
786
 
        }
787
 
 
788
 
        return NULL;
789
 
}
790
 
 
791
 
void
792
 
button_character_instance::stagePlacementCallback()
793
 
{
794
 
        saveOriginalTarget(); // for soft refs
795
 
 
796
 
        // Register this button instance as a live character
797
 
        // do we need this???
798
 
        //_vm.getRoot().addLiveChar(this);
799
 
 
800
 
        size_t r, r_num =  m_def->m_button_records.size();
801
 
        m_record_character.resize(r_num);
802
 
 
803
 
        for (r = 0; r < r_num; r++)
804
 
        {
805
 
                button_record& bdef = m_def->m_button_records[r];
806
 
 
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;
811
 
 
812
 
                boost::intrusive_ptr<character> ch = bdef.m_character_def->create_character_instance(this, ch_id);
813
 
                ch->set_matrix(mat);
814
 
                ch->set_cxform(cx);
815
 
                ch->set_depth(ch_depth);
816
 
                assert(ch->get_parent() == this);
817
 
 
818
 
                if (ch->get_name().empty() && ch->wantsInstanceName()) 
819
 
                {
820
 
                        std::string instance_name = getNextUnnamedInstanceName();
821
 
                        ch->set_name(instance_name.c_str());
822
 
                }
823
 
 
824
 
                m_record_character[r] = ch;
825
 
 
826
 
                ch->stagePlacementCallback(); // give this character life (TODO: they aren't on stage, are them ?)
827
 
        }
828
 
 
829
 
        // there's no INITIALIZE/CONSTRUCT/LOAD/ENTERFRAME/UNLOAD events for buttons
830
 
}
831
 
 
832
 
#ifdef GNASH_USE_GC
833
 
void
834
 
button_character_instance::markReachableResources() const
835
 
{
836
 
        assert(isReachable());
837
 
 
838
 
        m_def->setReachable();
839
 
 
840
 
        // Markstate characters as reachable
841
 
        for (CharsVect::const_iterator i=m_record_character.begin(), e=m_record_character.end();
842
 
                        i!=e; ++i)
843
 
        {
844
 
                (*i)->setReachable();
845
 
        }
846
 
 
847
 
        // character class members
848
 
        markCharacterReachable();
849
 
}
850
 
#endif // GNASH_USE_GC
851
 
 
852
 
bool
853
 
button_character_instance::unload()
854
 
{
855
 
        bool childsHaveUnload = false;
856
 
 
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)
860
 
        {
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());
864
 
        }
865
 
 
866
 
        bool hasUnloadEvent = character::unload();
867
 
 
868
 
        return hasUnloadEvent || childsHaveUnload;
869
 
}
870
 
 
871
 
bool
872
 
button_character_instance::get_member(string_table::key name_key, as_value* val,
873
 
  string_table::key nsname)
874
 
{
875
 
  // FIXME: use addProperty interface for these !!
876
 
  // TODO: or at least have a character:: protected method take
877
 
  //       care of these ?
878
 
  //       Duplicates code in character::get_path_element_character too..
879
 
  //
880
 
  if (name_key == NSV::PROP_uROOT)
881
 
  {
882
 
 
883
 
    // Let ::get_root() take care of _lockroot
884
 
    movie_instance* relRoot = get_root();
885
 
    val->set_as_object( relRoot );
886
 
    return true;
887
 
  }
888
 
 
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.
893
 
  //
894
 
  if ( getSWFVersion() > 5 && name_key == NSV::PROP_uGLOBAL ) // see MovieClip.as
895
 
  {
896
 
    // The "_global" ref was added in SWF6
897
 
    val->set_as_object( _vm.getGlobal() );
898
 
    return true;
899
 
  }
900
 
 
901
 
  const std::string& name = _vm.getStringTable().value(name_key);
902
 
 
903
 
  movie_root& mr = _vm.getRoot();
904
 
  unsigned int levelno;
905
 
  if ( mr.isLevelTarget(name, levelno) )
906
 
  {
907
 
    movie_instance* mo = mr.getLevel(levelno).get();
908
 
    if ( mo )
909
 
    {
910
 
      val->set_as_object(mo);
911
 
      return true;
912
 
    }
913
 
    else
914
 
    {
915
 
      return false;
916
 
    }
917
 
        }
918
 
 
919
 
  // TOCHECK : Try object members, BEFORE display list items
920
 
  //
921
 
  if (get_member_default(name_key, val, nsname))
922
 
  {
923
 
 
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.
928
 
// See bug #18457
929
 
#define CHECK_FOR_NAME_CLASHES 1
930
 
#ifdef CHECK_FOR_NAME_CLASHES
931
 
    IF_VERBOSE_ASCODING_ERRORS(
932
 
    if ( getChildByName(name) )
933
 
    {
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());
939
 
    }
940
 
    );
941
 
#endif
942
 
 
943
 
    return true;
944
 
  }
945
 
 
946
 
 
947
 
  // Try items on our display list.
948
 
  character* ch = getChildByName(name);
949
 
 
950
 
  if (ch)
951
 
  {
952
 
      // Found object.
953
 
 
954
 
      // If the object is an ActionScript referenciable one we
955
 
      // return it, otherwise we return ourselves
956
 
      if ( ch->isActionScriptReferenceable() )
957
 
      {
958
 
        val->set_as_object(ch);
959
 
      }
960
 
      else
961
 
      {
962
 
        val->set_as_object(this);
963
 
      }
964
 
 
965
 
      return true;
966
 
  }
967
 
 
968
 
  return false;
969
 
 
970
 
}
971
 
 
972
 
int
973
 
button_character_instance::getSWFVersion() const
974
 
{
975
 
        return m_def->getSWFVersion();
976
 
}
977
 
 
978
 
} // end of namespace gnash
979
 
 
980
 
 
981
 
// Local Variables:
982
 
// mode: C++
983
 
// c-basic-offset: 8 
984
 
// tab-width: 8
985
 
// indent-tabs-mode: t
986
 
// End: