1 /***********************************************************************
2  *
3  *   Copyright (C) 2005, 2006, 2009, 2010, 2011 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  ***********************************************************************
21  *
22  * Test case for mouse events.
23  *
24  * In a movie of 120x120 pixels, it places a movieclip containing a squared
25  * button in the middle of the stage, and a text area on top.
26  *
27  * The movie has 4 frames, with the second adding a shape at a lower depth,
28  * the third one at an higher depth, and fourth disabling the button.
29  *
30  * The following events print the event name in the text area
31  * (called _root.textfield) and change the color of the button:
32  *
33  * MouseOut  : red button (initial state)
34  * MouseOver : yellow button
35  * MouseDown : green button
36  * MouseUp   : yellow button (same as MouseOver, but the label on top changes)
37  *
38  * Tests are triggered by events, in particular:
39  * - Test for _target and _name referring to button's parent.
40  * - Test for bounds of buttons being the union of all active state
41  *   DisplayObjects' bounds.
42  *
43  * Note that you need to play with your mouse on the button for the tests
44  * to be run, and that there's currently no END OF TEST condition.
45  * For gnash test automation, we use the ButtonEventsTest-Runner script
46  * that supposedly triggers all tests (still worth making the test
47  * more explicitly guided, also to provide an end-of-test flags for
48  * consistency checking).
49  *
50  * TODO:
51  *  - Turn the test into a guided interaction, like the DragDropTest.swf one..
52  *  - Add tests for invalidated bounds
53  *  - Add matrix transformation to some child to also test that.
54  *
55  ***********************************************************************/
56 
57 #include "ming_utils.h"
58 
59 #include <ming.h>
60 #include <stdio.h>
61 #include <stdlib.h>
62 
63 #define OUTPUT_VERSION 6
64 #define OUTPUT_FILENAME "ButtonEventsTest.swf"
65 
66 SWFFont font;
67 
68 void add_event(SWFMovie mo, const char* name, const char* event, const char* action);
69 void add_code(SWFMovie mo, const char* code);
70 void add_text_field(SWFMovie mo, const char* name, const char* varname, const char* initial_label, int depth, int x, int y);
71 void set_text(SWFMovie mo, const char* text);
72 SWFDisplayItem add_square(SWFMovie mo, byte r, byte g, byte b, int depth);
73 
74 void
add_event(SWFMovie mo,const char * name,const char * event,const char * action)75 add_event(SWFMovie mo, const char* name, const char* event, const char* action)
76 {
77 	SWFAction ac;
78 	char buf[1024];
79 
80 	sprintf(buf,
81 	"event=undefined;"
82 	"%s.on%s=function() { %s; };"
83 	, name, event, action
84 	);
85 	ac = compileSWFActionCode(buf);
86 
87 	SWFMovie_add(mo, (SWFBlock)ac);
88 }
89 
90 void
add_code(SWFMovie mo,const char * code)91 add_code(SWFMovie mo, const char* code)
92 {
93 	SWFAction ac;
94 
95 	ac = compileSWFActionCode(code);
96 
97 	SWFMovie_add(mo, (SWFBlock)ac);
98 }
99 
100 SWFDisplayItem add_button(SWFMovie mo);
101 SWFDisplayItem
add_button(SWFMovie mo)102 add_button(SWFMovie mo)
103 {
104 	SWFDisplayItem it;
105 	SWFMovieClip mc;
106 	SWFButtonRecord br;
107 	SWFShape sh1, sh2, sh3, sh4, sh1a, sh2a, sh3a, sh4a;
108 	SWFButton bu = newSWFButton();
109 	static SWFMovieClip ermc; /* Events-reporting mc */
110 	mc = newSWFMovieClip();
111 
112 	if ( ! ermc )
113 	{
114 		ermc = newSWFMovieClip();
115 		SWFMovieClip_add(ermc, (SWFBlock)newSWFAction(
116 			"_global.dumpObj = function(o,indent) {"
117 			"	var s = '';"
118 			"	if ( typeof(o) == 'object' ) {"
119 			"		s += '{';"
120 			"		var first=1;"
121 			"		for (var i in o) {"
122 			"			if (!first) s+=',';"
123 			"			s+= i+':'+dumpObj(o[i]);"
124 			"			first=0;"
125 			"		}"
126 			"		s += '}';"
127 			"	} else {"
128 			"		s += o;"
129 			"	}"
130 			"	return s;"
131 			"};"
132 			"if ( _root.buttonChild == undefined ) _root.buttonChild = [];"
133 			"var myDepth = getDepth()+16383;"
134 			"var myName = ''+this;"
135 			"if ( _root.buttonChild[myDepth] == undefined ) _root.buttonChild[myDepth] = {nam:myName,exe:1,uld:0};"
136 			"else _root.buttonChild[myDepth]['exe']++;"
137 			 //"_root.note('Actions in frame0 of '+this+' at depth '+myDepth+' executed.');"
138 			"this.onUnload = function() {"
139 			"	var myDepth = -(getDepth()+32769-16383);"
140 			//"	_root.note(''+this+' at depth '+myDepth+' unloaded.');"
141 			"	_root.buttonChild[myDepth]['uld']++;"
142 			"};"
143             "for (i in _level0.square1.button) { trace (i); };"
144 			//"_root.note('buttonChilds:'+dumpObj(_root.buttonChild));"
145 		));
146 		SWFMovieClip_nextFrame(ermc);
147 	}
148 
149 	sh1 = make_fill_square(0, 0, 40, 40, 0, 0, 0, 0, 0, 0);
150 	sh1a = make_fill_square(30, 30, 5, 5, 128, 128, 128, 128, 128, 128);
151 	sh2 = make_fill_square(0, 0, 40, 40, 255, 0, 0, 255, 0, 0);
152 	sh2a = make_fill_square(30, 30, 5, 5, 128, 0, 0, 128, 0, 0);
153 	sh3 = make_fill_square(0, 0, 40, 40, 0, 255, 0, 0, 255, 0);
154 	sh3a = make_fill_square(30, 30, 5, 5, 0, 128, 0, 0, 128, 0);
155 	sh4 = make_fill_square(0, 0, 40, 40, 255, 255, 0, 255, 255, 0);
156 	sh4a = make_fill_square(30, 30, 5, 5, 128, 128, 0, 128, 128, 0);
157 
158 	/* Higher depth DisplayObject is intentionally added before lower depth one */
159 	br = SWFButton_addCharacter(bu, (SWFCharacter)sh1a, SWFBUTTON_HIT);
160 	SWFButtonRecord_setDepth(br, 2);
161 	br = SWFButton_addCharacter(bu, (SWFCharacter)sh1, SWFBUTTON_HIT);
162 	SWFButtonRecord_setDepth(br, 1);
163 
164 	/* Higher depth DisplayObject is intentionally added before lower depth one */
165 	br = SWFButton_addCharacter(bu, (SWFCharacter)sh2a, SWFBUTTON_UP );
166 	SWFButtonRecord_setDepth(br, 2);
167 	br = SWFButton_addCharacter(bu, (SWFCharacter)sh2, SWFBUTTON_UP );
168 	SWFButtonRecord_setDepth(br, 1);
169 
170 	/* Higher depth DisplayObject is intentionally added before lower depth one */
171 	br = SWFButton_addCharacter(bu, (SWFCharacter)sh3a, SWFBUTTON_DOWN );
172 	SWFButtonRecord_setDepth(br, 2);
173 	br = SWFButton_addCharacter(bu, (SWFCharacter)sh3, SWFBUTTON_DOWN );
174 	SWFButtonRecord_setDepth(br, 1);
175 
176 	/* Higher depth DisplayObject is intentionally added before lower depth one */
177 	br = SWFButton_addCharacter(bu, (SWFCharacter)sh4a, SWFBUTTON_OVER );
178 	SWFButtonRecord_setDepth(br, 2);
179 	br = SWFButton_addCharacter(bu, (SWFCharacter)sh4, SWFBUTTON_OVER );
180 	SWFButtonRecord_setDepth(br, 1);
181 
182 	/* Add events reported DisplayObject in all states at depth 10 */
183 	br = SWFButton_addCharacter(bu, (SWFCharacter)ermc, SWFBUTTON_HIT|SWFBUTTON_DOWN|SWFBUTTON_OVER|SWFBUTTON_UP);
184 	SWFButtonRecord_setDepth(br, 10);
185 
186 	/* Add events reported DisplayObject just HIT state at depth 11 */
187 	br = SWFButton_addCharacter(bu, (SWFCharacter)ermc, SWFBUTTON_HIT);
188 	SWFButtonRecord_setDepth(br, 11);
189 
190 	/* Add events reported DisplayObject just UP state at depth 12 */
191 	br = SWFButton_addCharacter(bu, (SWFCharacter)ermc, SWFBUTTON_UP);
192 	SWFButtonRecord_setDepth(br, 12);
193 
194 	/* Add events reported DisplayObject just OVER state at depth 13 */
195 	br = SWFButton_addCharacter(bu, (SWFCharacter)ermc, SWFBUTTON_OVER);
196 	SWFButtonRecord_setDepth(br, 13);
197 
198 	/* Add events reported DisplayObject just DOWN state at depth 14 */
199 	br = SWFButton_addCharacter(bu, (SWFCharacter)ermc, SWFBUTTON_DOWN);
200 	SWFButtonRecord_setDepth(br, 14);
201 
202 
203 	SWFButton_addAction(bu, compileSWFActionCode(
204 		"_root.msg='MouseOut';"
205 		"if ( _root.testno == 4 || _root.testno == 9 || _root.testno == 14 ) {"
206 		"	_root.check_equals(_root.printBounds(getBounds()), '-0.05,-0.05 40.05,40.05');"
207 		/* Target of button action is the button's parent sprite */
208 		"	_root.check_equals(_target, '/square1');"
209 		"	setTarget('/');"
210 		"	_root.check_equals(_target, '/');"
211 		"	_root.testno++;"
212 		"	_root.note(_root.testno+'. Press mouse button inside the square, and release it outside.');"
213 		"} else {"
214 		//"	_root.note('SWFBUTTON_MOUSEOUT');"
215 		"	_root.xfail('Unexpectedly got SWFBUTTON_MOUSEOUT event (testno:'+_root.testno+')');"
216 		"}"
217 		), SWFBUTTON_MOUSEOUT);
218 
219 	SWFButton_addAction(bu, compileSWFActionCode(
220 		"_root.msg='MouseOver';"
221 
222 		"if ( _root.testno == 1 ) {" /* ONLY CHECK buttonChild on first frame */
223 
224 		/* "_root.note('buttonChild is '+dumpObj(_root.buttonChild));" */
225 
226 		/* added OVER state char */
227 		"	_root.check_equals(_root.buttonChild.realLength(), 3);"
228 
229 		/* OVER state char loaded */
230 		"	_root.check_equals(typeof(_root.buttonChild[13]), 'object');"
231 		"	_root.check_equals(_root.buttonChild[13].nam, '_level0.square1.button.instance7');"
232 		"	_root.check_equals(_root.buttonChild[13].exe, 1);" /* OVER state char */
233 		"	_root.check_equals(_root.buttonChild[13].uld, 0);" /* OVER state char */
234 
235 		/* UP state char unloaded */
236 		"	_root.check_equals(_root.buttonChild[12].exe, 1);"
237 		"	_root.check_equals(_root.buttonChild[12].uld, 1);"
238 		"	_root.check_equals(typeof(_level0.square1.button.instance6), 'movieclip');"
239 		"	_root.check_equals(_level0.square1.button.instance6._name, 'instance6');"
240 		"	_root.check_equals(_level0.square1.button.instance6.getDepth(), -16398);"
241 
242 		/* ALL state char still there, not reloaded, not unloaded */
243 		"	_root.check_equals(_root.buttonChild[10].exe, 1);"
244 		"	_root.check_equals(_root.buttonChild[10].uld, 0);"
245 
246 		"}"
247 
248 		"if ( _root.testno == 1 || _root.testno == 6 || _root.testno == 11 ) {"
249 
250 		//"	_root.note('buttonChild is '+dumpObj(_root.buttonChild));"
251 		"	_root.check_equals(_root.printBounds(getBounds()), '-0.05,-0.05 40.05,40.05');"
252 		/* Target of button action is the button's parent sprite */
253 		"	_root.check_equals(_target, '/square1');"
254 		"	setTarget('/');"
255 		"	_root.check_equals(_target, '/');"
256 		"	_root.testno++;"
257 		"	_root.note(_root.testno+'. Press (and keep pressed) the mouse button inside the square.');"
258 		"} else {"
259 		//"	_root.note('SWFBUTTON_MOUSEOVER');"
260 		// need MOUSEOVER for MOUSEUPOUTSIDE
261 		"	if ( _root.testno != 5 && _root.testno != 10 && _root.testno != 15 ) {"
262 		"		_root.fail('Unexpectedly got SWFBUTTON_MOUSEOVER event (testno:'+_root.testno+')');"
263 		"	}"
264 		"}"
265 		), SWFBUTTON_MOUSEOVER);
266 
267 	SWFButton_addAction(bu, compileSWFActionCode(
268 		"_root.msg='MouseDown';"
269 
270 		"if ( _root.testno == 2 ) {" /* ONLY CHECK buttonChild on first frame */
271 
272 		/* Added DOWN state char */
273 		"	_root.check_equals(_root.buttonChild.realLength(), 4);"
274 
275 		/* DOWN state char loaded */
276 		"	_root.check_equals(typeof(_root.buttonChild[14]), 'object');"
277 		"	_root.check_equals(_root.buttonChild[14].nam, '_level0.square1.button.instance8');"
278 		"	_root.check_equals(_root.buttonChild[14].exe, 1);"
279 		"	_root.check_equals(_root.buttonChild[14].uld, 0);"
280 
281 		/* OVER state char unloaded */
282 		"	_root.check_equals(_root.buttonChild[13].exe, 1);"
283 		"	_root.check_equals(_root.buttonChild[13].uld, 1);"
284 
285 		/* ALL state char still there, not reloaded, not unloaded */
286 		"	_root.check_equals(_root.buttonChild[10].exe, 1);"
287 		"	_root.check_equals(_root.buttonChild[10].uld, 0);"
288 
289 		"}"
290 
291 		"if ( _root.testno == 2 || _root.testno == 7 || _root.testno == 12 ) {"
292 		"	_root.check_equals(_root.printBounds(getBounds()), '-0.05,-0.05 40.05,40.05');"
293 		/* Target (and name) of button action is the button's parent sprite */
294 		"	_root.check_equals(_target, '/square1');"
295 		"	_root.check_equals(_name, 'square1');"
296 		"	setTarget('/');"
297 		"	_root.check_equals(_target, '/');"
298 		"	_root.check_equals(typeof(_name), 'string');"
299 		"	_root.check_equals(_name, '');"
300 		"	_root.testno++;"
301 		"	_root.note(_root.testno+'. Depress the mouse button inside the square.');"
302 		"} else {"
303 		//"	_root.note('SWFBUTTON_MOUSEDOWN');"
304 		// need MOUSEDOWN for MOUSEUPOUTSIDE
305 		"	if ( _root.testno != 5 && _root.testno != 10 && _root.testno != 15 ) {"
306 		"		_root.fail('Unexpectedly got SWFBUTTON_MOUSEDOWN event (testno:'+_root.testno+')');"
307 		"	}"
308 		"}"
309 		), SWFBUTTON_MOUSEDOWN);
310 
311 	SWFButton_addAction(bu, compileSWFActionCode(
312 		"_root.msg='MouseUp';"
313 		"if ( _root.testno == 3 || _root.testno == 8 || _root.testno == 13 ) {"
314 		"	_root.check_equals(_root.printBounds(getBounds()), '-0.05,-0.05 40.05,40.05');"
315 		/* Target of button action is the button's parent sprite */
316 		"	_root.check_equals(_target, '/square1');"
317 		"	setTarget('/');"
318 		"	_root.check_equals(_target, '/');"
319 		"	_root.testno++;"
320 		"	_root.note(_root.testno+'. Move the mouse pointer off the square.');"
321 		"} else {"
322 		//"	_root.note('SWFBUTTON_MOUSEUP');"
323 		"	_root.fail('Unexpectedly got SWFBUTTON_MOUSEUP event (testno:'+_root.testno+')');"
324 		"}"
325 		), SWFBUTTON_MOUSEUP);
326 
327 	/* SWFBUTTON_MOUSEUPOUTSIDE *should* be invoked !! */
328 	SWFButton_addAction(bu, compileSWFActionCode(
329 		"_root.msg='MouseUpOutside';"
330 		"if ( _root.testno == 5 || _root.testno == 10 || _root.testno == 15 ) {"
331 		"	_root.check_equals(_root.printBounds(getBounds()), '-0.05,-0.05 40.05,40.05');"
332 		/* Target of button action is the button's parent sprite */
333 		"	_root.check_equals(_target, '/square1');"
334 		"	_root.check_equals(_name, 'square1');"
335 		"	setTarget('/');"
336 		"	_root.check_equals(_target, '/');"
337 		"	_root.nextFrame();"
338 		"} else {"
339 		//"	_root.note('SWFBUTTON_MOUSEUPOUTSIDE');"
340 		"	_root.fail('Unexpectedly got SWFBUTTON_MOUSEUPOUTSIDE event (testno:'+_root.testno+')');"
341 		"}"
342 		), SWFBUTTON_MOUSEUPOUTSIDE);
343 
344 	/* Keypress */
345 	SWFButton_addAction(bu, compileSWFActionCode(
346 		"_root.note('KeyPress: a');"
347 		//"_root.check(Key.isDown('a'));"
348 	), SWFBUTTON_KEYPRESS('a'));
349 	SWFButton_addAction(bu, compileSWFActionCode(
350 		"_root.note('KeyPress: b');"
351 		//"_root.check(Key.isDown('b'));"
352 	), SWFBUTTON_KEYPRESS('b'));
353 
354 	it = SWFMovieClip_add(mc, (SWFBlock)bu);
355 	SWFDisplayItem_setName(it, "button");
356 	SWFMovieClip_nextFrame(mc); /* showFrame */
357 
358 	it = SWFMovie_add(mo, (SWFBlock)mc);
359 	return it;
360 }
361 
362 void
add_text_field(SWFMovie mo,const char * name,const char * varname,const char * initial_label,int depth,int x,int y)363 add_text_field(SWFMovie mo, const char* name, const char* varname, const char* initial_label, int depth, int x, int y)
364 {
365 	SWFDisplayItem it;
366 	SWFTextField tf = newSWFTextField();
367 	SWFTextField_setFont(tf, (void*)font);
368 	SWFTextField_addChars(tf, " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345689:.,/\\#@?!");
369 	SWFTextField_setVariableName(tf, varname);
370 	SWFTextField_addString(tf, "Idle");
371 	SWFTextField_setBounds(tf, 120, 12);
372 	SWFTextField_setFlags(tf, SWFTEXTFIELD_DRAWBOX|SWFTEXTFIELD_NOEDIT);
373 
374 	it = SWFMovie_add(mo, (SWFBlock)tf);
375 	SWFDisplayItem_moveTo(it, x, y+2);
376 	SWFDisplayItem_setDepth(it, depth);
377 	SWFDisplayItem_setName(it, name); // "textfield");
378 
379 	// Label
380 	tf = newSWFTextField();
381 	SWFTextField_setFont(tf, (void*)font);
382 	SWFTextField_addString(tf, initial_label);
383 	SWFTextField_setFlags(tf, SWFTEXTFIELD_DRAWBOX|SWFTEXTFIELD_NOEDIT);
384 	it = SWFMovie_add(mo, (SWFBlock)tf);
385 	SWFDisplayItem_scale(it, 0.3, 0.3);
386 	SWFDisplayItem_setDepth(it, depth*10);
387 	SWFDisplayItem_moveTo(it, x, y);
388 }
389 
390 void
set_text(SWFMovie mo,const char * text)391 set_text(SWFMovie mo, const char* text)
392 {
393 	char buf[1024];
394 	sprintf(buf, "_root.msg=\"%s\";", text);
395 	add_code(mo, buf);
396 }
397 
398 int
main(int argc,char ** argv)399 main(int argc, char **argv)
400 {
401 	SWFMovie mo;
402 	SWFDisplayItem it;
403 	const char *srcdir=".";
404 	SWFMovieClip dejagnuclip;
405 
406 	/*********************************************
407 	 *
408 	 * Initialization
409 	 *
410 	 *********************************************/
411 
412 	puts("Setting things up");
413 
414 	Ming_init();
415         Ming_useSWFVersion (OUTPUT_VERSION);
416 	Ming_setScale(20.0);
417 
418 	mo = newSWFMovie();
419 	SWFMovie_setDimension(mo, 800, 600);
420 	SWFMovie_setRate(mo, 12);
421 
422 	if ( argc>1 ) srcdir=argv[1];
423 	else
424 	{
425 		fprintf(stderr, "Usage: %s <mediadir>\n", argv[0]);
426 		return 1;
427 	}
428 
429 	font = get_default_font(srcdir);
430 
431 	/* Dejagnu equipment */
432 	dejagnuclip = get_dejagnu_clip((SWFBlock)font, 10, 0, 0, 800, 600);
433 	it = SWFMovie_add(mo, (SWFBlock)dejagnuclip);
434   	SWFDisplayItem_setDepth(it, 200);
435   	SWFDisplayItem_move(it, 200, 0);
436 
437 	add_text_field(mo, "textfield", "_root.msg", "Button events", 10, 0, 5);
438 	add_text_field(mo, "textfield2", "_root.msg2", "Mouse events", 11, 0, 100);
439 	add_text_field(mo, "textfield3", "_root.msg3", "Key events", 12, 0, 80);
440 
441 	SWFMovie_nextFrame(mo); /* showFrame */
442 
443 	/*****************************************************
444 	 *
445 	 * Add button
446 	 *
447 	 *****************************************************/
448 
449 	it = add_button(mo);
450 	SWFDisplayItem_moveTo(it, 40, 30);
451 	SWFDisplayItem_setName(it, "square1");
452 	SWFDisplayItem_setDepth(it, 2);
453 
454 	add_actions(mo,
455 		"function printBounds(b) {"
456 		"   return ''+Math.round(b.xMin*100)/100+','+Math.round(b.yMin*100)/100+' '+Math.round(b.xMax*100)/100+','+Math.round(b.yMax*100)/100;"
457 		"}"
458 	);
459 
460 	//
461 	// Mouse pointer events
462 	//
463 
464 	add_actions(mo,
465 		"square1.button.onRollOver = function() { "
466 		"	_root.msg2 = 'RollOver'; "
467 		// Target is the one this function was defined in
468 		"	check_equals(_target, '/');"
469 		"};"
470 		);
471 
472 	add_actions(mo,
473 		"square1.button.onRollOut = function() {"
474 		"	_root.msg2 = 'RollOut'; "
475 		// Target is the one this function was defined in
476 		"	check_equals(_target, '/');"
477 		"};"
478 		);
479 
480 	check_equals(mo, "typeof(square1.button)", "'object'");
481 	check(mo, "square1.button instanceOf Button");
482 	check_equals(mo, "typeof(square1.button.useHandCursor)", "'boolean'");
483 	check_equals(mo, "square1.button.useHandCursor", "true");
484 
485 	//
486 	// Mouse buttons events
487 	//
488 
489 	add_actions(mo,
490 		"square1.button.onPress = function() {"
491 		"	_root.msg2 = 'Press'; "
492 		// Target is the one this function was defined in
493 		"	check_equals(_target, '/');"
494 		"};"
495 		);
496 
497 	add_actions(mo,
498 		"square1.button.onRelease = function() {"
499 		"	_root.msg2 = 'Release'; "
500 		// Target is the one this function was defined in
501 		"	check_equals(_target, '/');"
502 		"};"
503 		);
504 
505 	add_actions(mo,
506 		"square1.button.onReleaseOutside = function() {"
507 		"	_root.msg2 = 'ReleaseOutside'; "
508 		// Target is the one this function was defined in
509 		"	check_equals(_target, '/');"
510 		"};"
511 		);
512 
513 	//
514 	// Focus events
515 	//
516 
517 	add_actions(mo,
518 		"square1.button.onSetFocus = function() {"
519 		"	_root.msg3 = 'SetFocus';"
520 		// Target is the one this function was defined in
521 		"	check_equals(_target, '/');"
522 		"};"
523 		);
524 
525 	//
526 	// Key events - button needs focus for these to work
527 	//
528 
529 	add_actions(mo,
530 		"square1.button.onKeyDown = function() {"
531 		"	_root.msg3 = 'KeyDown';"
532 		// Target is the one this function was defined in
533 		"	check_equals(_target, '/');"
534 		"};"
535 		);
536 
537 	add_actions(mo,
538 		"square1.button.onKeyUp = function() {"
539 		"	_root.msg3 = 'KeyUp';"
540 		// Target is the one this function was defined in
541 		"	check_equals(_target, '/');"
542 		"};"
543 		);
544 
545 	SWFMovie_nextFrame(mo); /* showFrame */
546 
547 	/*****************************************************
548 	 *
549 	 * On second frame, check construction of the button
550 	 * DisplayObject states and give instructions to proceed
551 	 *
552 	 *
553 	 *****************************************************/
554 
555 	add_actions(mo, "Array.prototype.realLength = function() {"
556 		" var l=0; for (var i in this) { "
557 		"	if (Number(i) == i) l++;" /* count only numbers */
558 		" };"
559 		" return l;"
560 		"};");
561 
562 	/* buttonChild was initialized with 2 elements */
563 	check_equals(mo, "typeof(_root.buttonChild)", "'object'");
564 	check(mo, "_root.buttonChild instanceof Array");
565 	check_equals(mo, "_root.buttonChild.realLength()", "2"); /* UP and ALL states */
566 
567 	/* sprite for ALL states */
568 	check_equals(mo, "typeof(_root.buttonChild[10])", "'object'");
569 	check_equals(mo, "(_root.buttonChild[10].nam)", "'_level0.square1.button.instance5'");
570 	check_equals(mo, "(_root.buttonChild[10].exe)", "1");
571 	check_equals(mo, "(_root.buttonChild[10].uld)", "0");
572 
573 	/* sprite for UP state */
574 	check_equals(mo, "typeof(_root.buttonChild[12])", "'object'");
575 	check_equals(mo, "(_root.buttonChild[12].nam)", "'_level0.square1.button.instance6'");
576 	check_equals(mo, "(_root.buttonChild[12].exe)", "1");
577 	check_equals(mo, "(_root.buttonChild[12].uld)", "0");
578 	check_equals(mo, "_level0.square1.button.instance6._name", "'instance6'");
579 	check_equals(mo, "_level0.square1.button.instance6.getDepth()", "-16371");
580 
581 	/* sprite for HIT state not constructed */
582 	check_equals(mo, "typeof(_root.buttonChild[11])", "'undefined'");
583 
584 	/* sprite for DOWN state not constructed */
585 	check_equals(mo, "typeof(_root.buttonChild[13])", "'undefined'");
586 
587 	add_actions(mo,
588 		"stop();"
589 		/*"_root.note('buttonChild is '+dumpObj(_root.buttonChild));"*/
590 		"_root.testno=0;"
591 		"_root.square1.onRollOut = function() { _root.testno++; delete _root.square1.onRollOut; nextFrame(); };"
592 		"_root.note('"
593 		"0. Roll over and out the red square, not touching the small dark-red square in it.\n   The cursor should turn to an hand while on the square."
594 		"');");
595 
596 	/* hitTest should work on every child, not just first added */
597 	check(mo, "_level0.square1.hitTest(60,60,true)");
598 
599 	SWFMovie_nextFrame(mo); /* showFrame */
600 
601 	/*****************************************************
602 	 *
603 	 * On third frame, start the button event test
604 	 *
605 	 *****************************************************/
606 
607 	add_actions(mo,
608 		"stop();"
609 		/*"_root.note('buttonChild is '+dumpObj(_root.buttonChild));"*/
610 		"_root.testno=1;"
611 		"_root.note('"
612 		"1. Roll over the red square."
613 		"');");
614 
615 	SWFMovie_nextFrame(mo); /* showFrame */
616 
617 	/*****************************************************
618 	 *
619 	 * On fourth frame, add a shape at lower depth,
620 	 * and check bounds of square1
621 	 *
622 	 *
623 	 *****************************************************/
624 
625 	{
626 		SWFShape sh = make_fill_square(0, 0, 120, 120, 0, 0, 0, 0, 255, 0);
627 		SWFDisplayItem itsh = SWFMovie_add(mo, (SWFBlock)sh);
628 		SWFDisplayItem_setDepth(itsh, 1);
629 
630 		check_equals(mo, "printBounds(square1.getBounds())", "'-0.05,-0.05 40.05,40.05'");
631 
632 		/* buttonChild should now have a total of 4 elements (UP,DOWN, OVER and ALL states) */
633 		check_equals(mo, "typeof(_root.buttonChild)", "'object'");
634 		check(mo, "_root.buttonChild instanceof Array");
635 		check_equals(mo, "_root.buttonChild.realLength()", "4");
636 
637 		/* sprite for ALL states */
638 		check_equals(mo, "typeof(_root.buttonChild[10])", "'object'");
639 		check_equals(mo, "(_root.buttonChild[10].nam)", "'_level0.square1.button.instance5'");
640 		check_equals(mo, "(_root.buttonChild[10].exe)", "1");
641 		check_equals(mo, "(_root.buttonChild[10].uld)", "0");
642 
643 		/* sprite for UP state */
644 		check_equals(mo, "typeof(_root.buttonChild[12])", "'object'");
645 		check_equals(mo, "(_root.buttonChild[12].nam)", "'_level0.square1.button.instance6'");
646 		check_equals(mo, "(_root.buttonChild[12].exe)", "3");
647 		check_equals(mo, "(_root.buttonChild[12].uld)", "2");
648 
649 		/* sprite for OVER state */
650 		check_equals(mo, "typeof(_root.buttonChild[13])", "'object'");
651 		check_equals(mo, "(_root.buttonChild[13].nam)", "'_level0.square1.button.instance7'");
652 		check_equals(mo, "(_root.buttonChild[13].exe)", "4");
653 		check_equals(mo, "(_root.buttonChild[13].uld)", "4");
654 
655 		/* sprite for DOWN state */
656 		check_equals(mo, "typeof(_root.buttonChild[14])", "'object'");
657 		check_equals(mo, "(_root.buttonChild[14].nam)", "'_level0.square1.button.instance8'");
658 		check_equals(mo, "(_root.buttonChild[14].exe)", "2");
659 		check_equals(mo, "(_root.buttonChild[14].uld)", "2");
660 
661 		/* sprite for HIT state never constructed */
662 		check_equals(mo, "typeof(_root.buttonChild[11])", "'undefined'");
663 
664 
665 
666 		add_actions(mo,
667 			"stop();"
668 			"_root.note('-- Added shape at lower depth --');"
669 			"_root.testno++;"
670 			"_root.note(_root.testno+'. Roll over the square.');"
671 		);
672 
673 		SWFMovie_nextFrame(mo); /* showFrame */
674 	}
675 
676 	/*****************************************************
677 	 *
678 	 * On fifth frame, add a shape at higher depth
679 	 *
680 	 *****************************************************/
681 
682 	{
683 		SWFShape sh = make_fill_square(0, 0, 120, 120, 0, 0, 0, 0, 255, 0);
684 		SWFDisplayItem itsh = SWFMovie_add(mo, (SWFBlock)sh);
685 		SWFDisplayItem_setDepth(itsh, 3);
686 		SWFDisplayItem_setColorAdd(itsh, 0, 0, 0, -128);
687 
688 		/* buttonChild should now have a total of 4 elements (UP,DOWN, OVER and ALL states) */
689 		check_equals(mo, "typeof(_root.buttonChild)", "'object'");
690 		check(mo, "_root.buttonChild instanceof Array");
691 		check_equals(mo, "_root.buttonChild.realLength()", "4");
692 
693 		/* sprite for ALL states */
694 		check_equals(mo, "typeof(_root.buttonChild[10])", "'object'");
695 		check_equals(mo, "(_root.buttonChild[10].nam)", "'_level0.square1.button.instance5'");
696 		check_equals(mo, "(_root.buttonChild[10].exe)", "1");
697 		check_equals(mo, "(_root.buttonChild[10].uld)", "0");
698 
699 		/* sprite for UP state */
700 		check_equals(mo, "typeof(_root.buttonChild[12])", "'object'");
701 		check_equals(mo, "(_root.buttonChild[12].nam)", "'_level0.square1.button.instance6'");
702 		check_equals(mo, "(_root.buttonChild[12].exe)", "5");
703 		check_equals(mo, "(_root.buttonChild[12].uld)", "4");
704 
705 		/* sprite for OVER state */
706 		check_equals(mo, "typeof(_root.buttonChild[13])", "'object'");
707 		check_equals(mo, "(_root.buttonChild[13].nam)", "'_level0.square1.button.instance7'");
708 		check_equals(mo, "(_root.buttonChild[13].exe)", "8");
709 		check_equals(mo, "(_root.buttonChild[13].uld)", "8");
710 
711 		/* sprite for DOWN state */
712 		check_equals(mo, "typeof(_root.buttonChild[14])", "'object'");
713 		check_equals(mo, "(_root.buttonChild[14].nam)", "'_level0.square1.button.instance8'");
714 		check_equals(mo, "(_root.buttonChild[14].exe)", "4");
715 		check_equals(mo, "(_root.buttonChild[14].uld)", "4");
716 
717 		/* sprite for HIT state never constructed */
718 		check_equals(mo, "typeof(_root.buttonChild[11])", "'undefined'");
719 
720 		add_actions(mo,
721 			"stop();"
722 			"_root.note('-- Added shape at higher depth --');"
723 			"_root.testno++;"
724 			"_root.note(_root.testno+'. Roll over the square.');"
725 		);
726 
727 		SWFMovie_nextFrame(mo); /* showFrame */
728 	}
729 
730 	/*****************************************************
731 	 *
732 	 * On sixth frame, disable the button
733 	 * and check total tests so far
734 	 *
735 	 *****************************************************/
736 
737 	{
738 
739 		add_actions(mo,
740             "check_equals(square1.button.enabled, true);"
741 			"square1.button.enabled = 6;"
742             "check_equals(square1.button.enabled, 6);"
743 			"square1.button.enabled = 'string';"
744             "check_equals(square1.button.enabled, 'string');"
745             "square1.button._visible = false;"
746             "check_equals(square1.button.enabled, 'string');"
747             "square1.button._visible = true;"
748 			"square1.button.enabled = false;"
749 			"stop();"
750 			"_root.totals(164);"
751 			"_root.note('-- Button disabled, try playing with it, nothing should happen --');"
752 		);
753 		SWFMovie_nextFrame(mo); /* showFrame */
754 	}
755 
756 	/*****************************************************
757 	 *
758 	 * Save it...
759 	 *
760 	 *****************************************************/
761 
762 	puts("Saving " OUTPUT_FILENAME );
763 
764 	SWFMovie_save(mo, OUTPUT_FILENAME);
765 
766 	return 0;
767 }
768