1 // MovieClip.h: Stateful live Sprite instance, for Gnash.
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 // Free Software Foundation, Inc
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 // Stateful live Sprite instance
22 #ifndef GNASH_MOVIECLIP_H
23 #define GNASH_MOVIECLIP_H
26 #include "gnashconfig.h"
33 #include <boost/ptr_container/ptr_list.hpp>
34 #include <boost/intrusive_ptr.hpp>
36 #include "ControlTag.h"
37 #include "movie_definition.h" // for inlines
38 #include "DisplayList.h" // DisplayList
39 #include "DisplayObjectContainer.h"
40 #include "as_environment.h" // for composition
41 #include "DynamicShape.h" // for composition
42 #include "dsodefs.h" // for DSOEXPORT
44 // Forward declarations
49 class LoadVariablesThread
;
55 class PlaceObject2Tag
;
61 /// A MovieClip is a container for DisplayObjects.
63 /// TODO: This class should inherit from Sprite
65 /// In AS3 is it distinguished from a Sprite by having a timeline, i.e.
66 /// more than one frame. In AS2, there is no Sprite class.
68 /// There are basically two types of MovieClip: dynamic and non-dynamic.
69 /// Dynamic clips are created using createEmptyMovieClip() or
70 /// duplicateMovieClip(). Non-dynamic MovieClips are parsed from a SWF file.
71 /// The isDynamic() member function is the only way to tell the difference
72 /// (see following paragraph).
74 /// The presence of a definition (the _def member) reveals whether the
75 /// MovieClip was constructed with an immutable definition or not. MovieClips
76 /// created using createEmptyMovieClip() have no definition. MovieClips
77 /// constructed using duplicateMovieClip() have the same definition as the
78 /// duplicated clip. They are "dynamic", but may have a definition!
80 /// A MovieClip always has an _swf member. This is the top-level SWF
81 /// (Movie) containing either the definition or the code from
82 /// which the MovieClip was created. The _url member and SWF version are
83 /// dependent on the _swf. Exports are also sought in this Movie.
84 class MovieClip
: public DisplayObjectContainer
88 typedef std::vector
<TextField
*> TextFields
;
90 /// A container for textfields, indexed by their variable name
91 typedef std::map
<ObjectURI
, TextFields
, ObjectURI::LessThan
>
94 typedef std::map
<std::string
, std::string
> MovieVariables
;
96 typedef movie_definition::PlayList PlayList
;
104 /// Construct a MovieClip instance
107 /// Pointer to the movie_definition this object is an
108 /// instance of (may be a top-level movie or a sprite).
109 /// This may be 0 if there is no immutable definition.
112 /// The "relative" _swf of this sprite, which is the
113 /// instance of top-level sprite defined by the same
114 /// SWF that also contained *this* sprite definition.
115 /// Note that this can be *different* from the top-level
116 /// movie accessible through the VM, in case this sprite
117 /// was defined in an externally loaded movie.
120 /// Parent of the created instance in the display list.
121 /// May be 0 for top-level movies (_level#).
122 MovieClip(as_object
* object
, const movie_definition
* def
,
123 Movie
* root
, DisplayObject
* parent
);
125 virtual ~MovieClip();
127 // Return the originating SWF
128 virtual Movie
* get_root() const;
130 virtual bool trackAsMenu();
132 /// Queue event in the global action queue.
134 /// notifyEvent(id) will be called by execution of the queued
136 void queueEvent(const event_id
& id
, int lvl
);
140 /// Return the _root ActionScript property of this sprite.
142 /// Relative or absolute is determined by the _lockroot property,
143 /// see getLockRoot and setLockRoot. May return this.
144 virtual MovieClip
* getAsRoot();
146 /// Get the composite bounds of all component drawing elements
147 virtual SWFRect
getBounds() const;
149 // See dox in DisplayObject.h
150 virtual bool pointInShape(boost::int32_t x
, boost::int32_t y
) const;
152 // See dox in DisplayObject.h
153 virtual bool pointInVisibleShape(boost::int32_t x
, boost::int32_t y
) const;
155 /// return true if the given point is located in a(this) hitable sprite.
157 /// all sprites except mouse-insensitive dynamic masks are hitable.
158 /// _visible property is ignored for hitable DisplayObjects.
159 virtual bool pointInHitableShape(boost::int32_t x
, boost::int32_t y
) const;
161 /// Return 0-based index to current frame
162 size_t get_current_frame() const
164 return _currentFrame
;
167 size_t get_frame_count() const
169 return _def
? _def
->get_frame_count() : 1;
172 /// Return number of completely loaded frames of this sprite/movie
174 /// Note: the number is also the last frame accessible (frames
175 /// numberes are 1-based)
177 size_t get_loaded_frames() const
179 return _def
? _def
->get_loading_frame() : 1;
182 /// Return total number of bytes in the movie
184 size_t get_bytes_total() const
186 return isDynamic() ? 0 : _def
->get_bytes_total();
189 /// Return number of loaded bytes in the movie
191 size_t get_bytes_loaded() const
193 return isDynamic() ? 0 : _def
->get_bytes_loaded();
196 const SWFRect
& get_frame_size() const
198 static const SWFRect r
;
199 return _def
? _def
->get_frame_size() : r
;
202 /// Stop or play the sprite.
204 /// If stopped, any stream sound associated with this sprite
205 /// will also be stopped.
207 DSOEXPORT
void setPlayState(PlayState s
);
209 PlayState
getPlayState() const { return _playState
; }
211 // delegates to movie_root (possibly wrong)
212 void set_background_color(const rgba
& color
);
214 /// Return true if we have any mouse event handlers.
216 /// NOTE: this function currently does not consider
217 /// general mouse event handlers MOUSE_MOVE, MOUSE
218 virtual bool mouseEnabled() const;
221 /// Return the topmost entity that the given point
222 /// covers that can receive mouse events. NULL if
223 /// none. Coords are in parent's frame.
224 virtual InteractiveObject
* topmostMouseEntity(boost::int32_t x
,
227 // see dox in DisplayObject.h
228 const DisplayObject
* findDropTarget(boost::int32_t x
, boost::int32_t y
,
229 DisplayObject
* dragging
) const;
231 void setDropTarget(const std::string
& tgt
) {
235 const std::string
& getDropTarget() const {
239 /// Advance to the next frame of the MovieClip.
241 /// Actions will be executed or pushed to the queue as necessary.
242 virtual void advance();
244 /// Set the sprite state at the specified frame number.
246 /// 0-based frame numbers!!
247 ///(in contrast to ActionScript and Flash MX)
249 DSOEXPORT
void goto_frame(size_t target_frame_number
);
251 /// Parse frame spec and return a 0-based frame number.
253 /// If frame spec cannot be converted to !NAN and !Infinity number
254 /// it will be converted to a string and considered a
255 /// frame label (returns false if referring to an
258 /// @param frame_spec
259 /// The frame specification.
262 /// The evaluated frame number (0-based)
265 /// True if the frame_spec could be resolved to a frame number.
266 /// False if the frame_spec was invalid.
267 bool get_frame_number(const as_value
& frame_spec
, size_t& frameno
) const;
269 /// Look up the labeled frame, and jump to it.
270 bool goto_labeled_frame(const std::string
& label
);
272 /// Render this MovieClip.
273 virtual void display(Renderer
& renderer
, const Transform
& xform
);
275 /// Draw this MovieClip
277 /// This is effectively the same as display(), but uses only the passed
279 void draw(Renderer
& renderer
, const Transform
& xform
);
283 /// Swap depth of the given DisplayObjects in the DisplayList
285 /// See DisplayList::swapDepths for more info
286 void swapDepths(DisplayObject
* ch1
, int newdepth
)
288 _displayList
.swapDepths(ch1
, newdepth
);
291 /// Return the DisplayObject at given depth in our DisplayList.
293 /// @return NULL if the specified depth is available (no chars there)
294 DisplayObject
* getDisplayObjectAtDepth(int depth
);
296 /// Attach a DisplayObject at the specified depth.
297 DisplayObject
* addDisplayListObject(DisplayObject
* obj
, int depth
);
299 /// Place a DisplayObject or mask to the DisplayList.
301 /// This method instantiates the given DisplayObject definition
302 /// and places it on the stage at the given depth.
304 /// If the specified depth is already occupied, it results a no-ops.
305 /// Otherwise, a new DisplayObject will be created and onload handler
306 /// will be triggerred.
309 /// A swf defined placement tag (PlaceObject, or PlaceObject2,
310 /// or PlaceObject3).
311 /// No ownership transfer, the tag is still owned by the
312 /// movie_definition class.
315 /// The display list to add the DisplayObject to.
318 /// A pointer to the DisplayObject being added or NULL
319 DisplayObject
* add_display_object(const SWF::PlaceObject2Tag
* tag
,
322 /// Proxy of DisplayList::moveDisplayObject()
323 void move_display_object(const SWF::PlaceObject2Tag
* tag
,
326 /// Proxy of DisplayList::replaceDisplayObject()
327 void replace_display_object(const SWF::PlaceObject2Tag
* tag
,
330 /// Proxy of DisplayList::removeDisplayObject()
331 void remove_display_object(const SWF::PlaceObject2Tag
* tag
,
335 /// Remove the object at the specified depth.
338 /// (1)the id parameter is currently unused, but
339 /// required to avoid breaking of inheritance from movie.h.
340 /// (2)the id might be used for specifying a DisplayObject
341 /// in the depth(think about multiple DisplayObjects within the same
342 /// depth, not tested and a rare case)
343 void remove_display_object(int depth
, int /*id*/);
347 /// Attach the given DisplayObject instance to current display list
349 /// @param newch The DisplayObject instance to attach.
350 /// @param depth The depth to assign to the instance.
351 void attachCharacter(DisplayObject
& newch
, int depth
, as_object
* initObject
);
353 /// Handle placement event
355 /// This callback will (not known to be a problem):
357 /// (1) Register ourselves with the global instance list
358 /// (2) Take note of our original target path
359 /// (3) Register as listener of core broadcasters
360 /// (4) Execute tags of frame 0
362 /// The callback will also (known to be bogus):
364 /// (1) Construct this instance as an ActionScript object.
365 /// See constructAsScriptObject() method, including constructing
366 /// registered class and adding properties.
367 virtual void construct(as_object
* initObj
= 0);
369 /// Mark this sprite as destroyed
371 /// This is an override of DisplayObject::destroy()
373 /// A sprite should be destroyed when is removed from the display
374 /// list and is not more needed for names (target) resolutions.
375 /// Sprites are needed for names resolution whenever themselves
376 /// or a contained object has an onUnload event handler defined,
377 /// in which case we want the event handler to find the 'this'
378 /// variable w/out attempting to rebind it.
380 /// When a sprite is destroyed, all its children are also destroyed.
382 /// Note: this function will release most memory associated with
383 /// the sprite as no members or drawable should be needed anymore.
386 /// Add the given action buffer to the list of action
387 /// buffers to be processed at the end of the next
389 void add_action_buffer(const action_buffer
* a
)
391 if (!_callingFrameActions
) queueAction(*a
);
392 else execute_action(*a
);
397 /// Execute the given init action buffer, if not done yet
398 /// for the target DisplayObject id.
400 /// The action will normally be pushed on queue, but will
401 /// be executed immediately if we are executing actions
402 /// resulting from a callFame instead.
405 /// The action buffer to execute
408 /// The referenced DisplayObject id
409 void execute_init_action_buffer(const action_buffer
& a
, int cid
);
411 /// Execute a single action buffer (DOACTION block)
412 void execute_action(const action_buffer
& ab
);
414 MovieClip
* to_movie () { return this; }
416 /// The various methods for sending data in requests.
418 /// Used in loadMovie, getURL, loadVariables etc.
426 // See dox in DisplayObject.h
427 virtual void getLoadedMovie(Movie
* newMovie
);
430 /// Load url-encoded variables from the given url, optionally
431 /// sending variables from this timeline too.
433 /// A LoadVariablesThread will be started to load and parse variables
434 /// and added to the _loadVariableRequests. Then, at every ::advance_sprite
435 /// any completed threads will be processed
436 /// (see processCompletedLoadVariableRequests)
438 /// NOTE: the given url will be security-checked
440 /// @param urlstr: The url to load variables from.
442 /// @param sendVarsMethod: The VariablesMethod to use. If METHOD_NONE,
443 /// no data will be sent.
444 void loadVariables(const std::string
& urlstr
,
445 VariablesMethod sendVarsMethod
);
447 /// Get TextField variables
449 /// TODO: this is unlikely to be the best way of doing it, and it would
450 /// simplify things if this function could be dropped.
451 bool getTextFieldVariables(const ObjectURI
& uri
, as_value
& val
);
453 // Set TextField variables
455 /// TODO: this is also unlikely to be the best way to do it.
456 bool setTextFieldVariables(const ObjectURI
& uri
, const as_value
& val
);
458 /// Search for a named object on the DisplayList
460 /// These are properties, but not attached as genuine members to the
461 /// MovieClip object. They take priority over DisplayObject magic
462 /// properties and inherited properties, but not over own properties.
464 /// @param name Object identifier. This function handles
465 /// case-sensitivity.
466 /// @return The object if found, otherwise 0.
467 DisplayObject
* getDisplayListObject(const ObjectURI
& uri
);
469 /// Overridden to look in DisplayList for a match
470 as_object
* pathElement(const ObjectURI
& uri
);
472 /// Execute the actions for the specified frame.
474 /// The frame_spec could be an integer or a string.
475 virtual void call_frame_actions(const as_value
& frame_spec
);
477 /// Duplicate this sprite in its timeline
479 /// Add the new DisplayObject at a the given depth to this sprite
480 /// parent displaylist.
482 /// NOTE: the call will fail for the root movie (no parent).
483 /// NOTE2: any DisplayObject at the given target depth will be
484 /// replaced by the new DisplayObject
485 /// NOTE3: event handlers will also be copied
488 /// Name for the copy
491 /// Depth for the copy
493 /// @param init_object
494 /// If not null, will be used to copy properties over.
495 MovieClip
* duplicateMovieClip(const std::string
& newname
,
496 int newdepth
, as_object
* init_object
= 0);
498 /// Called when a mouse event affects this MovieClip
499 virtual void mouseEvent(const event_id
& id
) {
503 /// Dispatch event handler(s), if any.
505 /// This handles key, mouse, and specific MovieClip events.
506 /// TODO: split this sensibly.
507 void notifyEvent(const event_id
& id
);
509 // inherited from DisplayObject class, see dox in DisplayObject.h
510 virtual as_environment
& get_environment() {
515 /// Set a TextField variable to this timeline
517 /// A TextField variable is a variable that acts
518 /// as a setter/getter for a TextField 'text' member.
519 void set_textfield_variable(const ObjectURI
& name
, TextField
* ch
);
521 void add_invalidated_bounds(InvalidatedRanges
& ranges
, bool force
);
523 const DisplayList
& getDisplayList() const {
527 /// Return the next highest available depth
529 /// Placing an object at the depth returned by
530 /// this function should result in a DisplayObject
531 /// that is displayd above all others
532 int getNextHighestDepth() const {
533 return _displayList
.getNextHighestDepth();
536 /// Set the currently playing m_sound_stream_id
538 // TODO: rename to setStreamingSoundId
539 void setStreamSoundId(int id
);
541 /// Remove this sprite from the stage.
543 /// This function is intended to be called by
544 /// effect of a removeMovieClip() ActionScript call
545 /// and implements the checks required for this specific
549 /// - The ActionRemoveClip tag handler.
550 /// - The global removeMovieClip(target) function.
551 /// - The MovieClip.removeMovieClip() method.
553 /// The removal will not occur if the depth of this
554 /// DisplayObjects is not in the "dynamic" range [0..1048575]
555 /// as described at the following URL:
557 /// http://www.senocular.com/flash/tutorials/depths/?page=2
559 /// A testcases for this behaviour can be found in
561 /// testsuite/misc-ming.all/displaylist_depths_test.swf
562 void removeMovieClip();
564 /// Direct access to the Graphics object for drawing.
565 DynamicShape
& graphics() {
570 /// Set focus to this MovieClip
572 /// @return true if this MovieClip can receive focus.
573 virtual bool handleFocus();
577 /// Set all variables in the given map with their corresponding values
578 DSOEXPORT
void setVariables(const MovieVariables
& vars
);
580 /// Enumerate child DisplayObjects
582 /// See DisplayObject::enumerateNonProperties for more info.
583 virtual void visitNonProperties(KeyVisitor
& v
) const;
585 /// Delete DisplayObjects removed from the stage
586 /// from the display lists
587 void cleanupDisplayList();
589 /// Queue the given action buffer
591 /// The action will be pushed on the current
592 /// global list (see movie_root).
594 void queueAction(const action_buffer
& buf
);
596 /// Construct this instance as an ActionScript object
598 /// This method invokes the constructor associated with our
599 /// definition, either MovieClip or any user-speficied one
600 /// (see sprite_definition::registerClass).
601 /// It will also invoke the onClipConstruct and onConstruct handlers.
602 void constructAsScriptObject();
604 /// Return true if getAsRoot() should return the *relative* root,
606 bool getLockRoot() const { return _lockroot
; }
608 /// Set whether getAsRoot() should return the *relative* root,
609 /// false otherwise. True for relative root.
610 void setLockRoot(bool lr
) { _lockroot
=lr
; }
612 /// Return the version of the SWF this MovieClip was parsed from.
613 virtual int getDefinitionVersion() const;
617 /// Unload all contents in the displaylist and this instance
619 /// Return true if there was an unloadHandler.
620 virtual bool unloadChildren();
622 /// Mark sprite-specific reachable resources.
624 /// sprite-specific reachable resources are:
625 /// - DisplayList items (current, backup and frame0 ones)
626 /// - Canvas for dynamic drawing (_drawable)
627 /// - sprite environment
628 /// - definition the sprite has been instantiated from
629 /// - Textfields having an associated variable registered in this instance.
630 /// - Relative root of this instance (_swf)
632 virtual void markOwnResources() const;
634 // Used by BitmapMovie.
635 void placeDisplayObject(DisplayObject
* ch
, int depth
) {
636 _displayList
.placeDisplayObject(ch
, depth
);
641 /// Process any completed loadVariables request
642 void processCompletedLoadVariableRequests();
644 /// Process a completed loadVariables request
645 void processCompletedLoadVariableRequest(LoadVariablesThread
& request
);
648 /// Execute the tags associated with the specified frame.
651 /// Frame number. 0-based
654 /// The display list to have control tags act upon.
657 /// Which kind of control tags we want to execute.
658 void executeFrameTags(size_t frame
, DisplayList
& dlist
,
659 int typeflags
= SWF::ControlTag::TAG_DLIST
|
660 SWF::ControlTag::TAG_ACTION
);
662 void stopStreamSound();
664 /// Return value of the 'enabled' property cast to a boolean value.
666 /// This is true if not found (undefined to bool evaluates to false).
668 /// When a MovieClip is "disabled", its handlers of button-like events
669 /// are disabled, and automatic tab ordering won't include it.
670 bool isEnabled() const;
672 /// Check whether a point hits our drawable shape.
674 /// This is possible because the drawable does not have its own
675 /// transform, so we can use our own. The points are expressed in
677 bool hitTestDrawable(boost::int32_t x
, boost::int32_t y
) const;
679 /// Advance to a previous frame.
681 /// This function will basically restore the DisplayList as it supposedly
682 /// was *before* executing tags in target frame and then execute target
683 /// frame tags (both DLIST and ACTION ones).
685 /// In practice, it will:
687 /// - Remove from current DisplayList:
688 /// - Timeline instances constructed after target frame
689 /// - Timeline instances constructed before or at the target frame but no
690 /// more at the original depth
691 /// - Dynamic instances found in the static depth zone
692 /// - Execute all displaylist tags from first to one-before target frame,
693 /// appropriately setting _currentFrame as it goes, finally execute
694 /// both displaylist and action
695 /// tags for target frame.
697 /// Callers of this methods are:
698 /// - goto_frame (for jump-backs)
699 /// - advance_sprite (for loop-back)
702 // http://www.gnashdev.org/wiki/index.php/TimelineControl
703 /// #Timeline_instances
705 /// @param targetFrame
706 /// The target frame for which we're willing to restore the static
712 /// - _currentFrame == targetFrame
714 /// TODO: consider using this same function for jump-forward too,
715 /// with some modifications...
717 void restoreDisplayList(size_t targetFrame
);
719 /// Increment _currentFrame, and take care of looping.
720 void increment_frame_and_check_for_loop();
722 /// Unregister textfield variables bound to unloaded TextFields
723 void cleanup_textfield_variables();
725 /// This is either sprite_definition (for sprites defined by
726 /// DefineSprite tag) or movie_def_impl (for the top-level movie).
727 const boost::intrusive_ptr
<const movie_definition
> _def
;
729 /// List of loadVariables requests
730 typedef boost::ptr_list
<LoadVariablesThread
> LoadVariablesThreads
;
732 /// List of active loadVariable requests
734 /// At ::advance_sprite time, all completed requests will
735 /// be processed (variables imported in this timeline scope)
736 /// and removed from the list.
737 LoadVariablesThreads _loadVariableRequests
;
739 /// The SWF that this MovieClip belongs to.
742 /// The canvas for dynamic drawing
743 DynamicShape _drawable
;
745 PlayState _playState
;
747 /// This timeline's variable scope
748 as_environment _environment
;
750 /// We'll only allocate Textfield variables map if
751 /// we need them (ie: anyone calls set_textfield_variable)
753 std::auto_ptr
<TextFieldIndex
> _text_variables
;
755 std::string _droptarget
;
757 // 0-based index to current frame
758 size_t _currentFrame
;
760 /// soundid for current playing stream. If no stream set to -1
761 int m_sound_stream_id
;
763 // true if this sprite reached the last frame and restarted
766 // true if orphaned tags (tags found after last advertised showframe)
767 // have been executed at least once.
768 bool _flushedOrphanedTags
;
770 // true is we're calling frame actions
771 bool _callingFrameActions
;
778 } // end of namespace gnash
780 #endif // GNASH_SPRITE_INSTANCE_H