Update with current status
[gnash.git] / libcore / MovieClip.h
blob7aa00b1f01bbc007c4f2c384bbc232de41b06fd2
1 // MovieClip.h: Stateful live Sprite instance, for Gnash.
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 // Free Software Foundation, Inc
5 //
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.
10 //
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.
15 //
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
25 #ifdef HAVE_CONFIG_H
26 #include "gnashconfig.h"
27 #endif
29 #include <vector>
30 #include <map>
31 #include <string>
32 #include <boost/ptr_container/ptr_list.hpp>
33 #include <boost/intrusive_ptr.hpp>
35 #include "ControlTag.h"
36 #include "movie_definition.h" // for inlines
37 #include "DisplayObjectContainer.h"
38 #include "as_environment.h" // for composition
39 #include "DynamicShape.h" // for composition
40 #include "dsodefs.h" // for DSOEXPORT
42 // Forward declarations
43 namespace gnash {
44 class Movie;
45 class swf_event;
46 class drag_state;
47 class LoadVariablesThread;
48 class GradientRecord;
49 class TextField;
50 class BitmapData_as;
51 class CachedBitmap;
52 class DisplayList;
53 namespace SWF {
54 class PlaceObject2Tag;
58 namespace gnash {
60 /// A MovieClip is a container for DisplayObjects.
62 /// TODO: This class should inherit from Sprite
64 /// In AS3 is it distinguished from a Sprite by having a timeline, i.e.
65 /// more than one frame. In AS2, there is no Sprite class.
67 /// There are basically two types of MovieClip: dynamic and non-dynamic.
68 /// Dynamic clips are created using createEmptyMovieClip() or
69 /// duplicateMovieClip(). Non-dynamic MovieClips are parsed from a SWF file.
70 /// The isDynamic() member function is the only way to tell the difference
71 /// (see following paragraph).
73 /// The presence of a definition (the _def member) reveals whether the
74 /// MovieClip was constructed with an immutable definition or not. MovieClips
75 /// created using createEmptyMovieClip() have no definition. MovieClips
76 /// constructed using duplicateMovieClip() have the same definition as the
77 /// duplicated clip. They are "dynamic", but may have a definition!
79 /// A MovieClip always has an _swf member. This is the top-level SWF
80 /// (Movie) containing either the definition or the code from
81 /// which the MovieClip was created. The _url member and SWF version are
82 /// dependent on the _swf. Exports are also sought in this Movie.
83 class DSOTEXPORT MovieClip : public DisplayObjectContainer
85 public:
87 typedef std::vector<TextField*> TextFields;
89 /// A container for textfields, indexed by their variable name
90 typedef std::map<ObjectURI, TextFields, ObjectURI::LessThan>
91 TextFieldIndex;
93 typedef std::map<std::string, std::string> MovieVariables;
95 typedef movie_definition::PlayList PlayList;
97 enum PlayState
99 PLAYSTATE_PLAY,
100 PLAYSTATE_STOP
103 /// Construct a MovieClip instance
105 /// @param def
106 /// Pointer to the movie_definition this object is an
107 /// instance of (may be a top-level movie or a sprite).
108 /// This may be 0 if there is no immutable definition.
110 /// @param root
111 /// The "relative" _swf of this sprite, which is the
112 /// instance of top-level sprite defined by the same
113 /// SWF that also contained *this* sprite definition.
114 /// Note that this can be *different* from the top-level
115 /// movie accessible through the VM, in case this sprite
116 /// was defined in an externally loaded movie.
118 /// @param parent
119 /// Parent of the created instance in the display list.
120 /// May be 0 for top-level movies (_level#).
121 MovieClip(as_object* object, const movie_definition* def,
122 Movie* root, DisplayObject* parent);
124 virtual ~MovieClip();
126 // Return the originating SWF
127 virtual Movie* get_root() const;
129 virtual bool trackAsMenu();
131 /// Queue event in the global action queue.
133 /// notifyEvent(id) will be called by execution of the queued
134 /// action
135 void queueEvent(const event_id& id, int lvl);
137 void queueLoad();
139 /// Return the _root ActionScript property of this sprite.
141 /// Relative or absolute is determined by the _lockroot property,
142 /// see getLockRoot and setLockRoot. May return this.
143 virtual MovieClip* getAsRoot();
145 /// Get the composite bounds of all component drawing elements
146 virtual SWFRect getBounds() const;
148 // See dox in DisplayObject.h
149 virtual bool pointInShape(std::int32_t x, std::int32_t y) const;
151 // See dox in DisplayObject.h
152 virtual bool pointInVisibleShape(std::int32_t x, std::int32_t y) const;
154 /// return true if the given point is located in a(this) hitable sprite.
156 /// all sprites except mouse-insensitive dynamic masks are hitable.
157 /// _visible property is ignored for hitable DisplayObjects.
158 virtual bool pointInHitableShape(std::int32_t x, std::int32_t y) const;
160 /// Return 0-based index to current frame
161 size_t get_current_frame() const
163 return _currentFrame;
166 size_t get_frame_count() const
168 return _def ? _def->get_frame_count() : 1;
171 /// Return number of completely loaded frames of this sprite/movie
173 /// Note: the number is also the last frame accessible (frames
174 /// numberes are 1-based)
176 size_t get_loaded_frames() const
178 return _def ? _def->get_loading_frame() : 1;
181 /// Return total number of bytes in the movie
182 /// (not sprite!)
183 size_t get_bytes_total() const
185 return isDynamic() ? 0 : _def->get_bytes_total();
188 /// Return number of loaded bytes in the movie
189 /// (not sprite!)
190 size_t get_bytes_loaded() const
192 return isDynamic() ? 0 : _def->get_bytes_loaded();
195 const SWFRect& get_frame_size() const
197 static const SWFRect r;
198 return _def ? _def->get_frame_size() : r;
201 /// Stop or play the sprite.
203 /// If stopped, any stream sound associated with this sprite
204 /// will also be stopped.
206 DSOEXPORT void setPlayState(PlayState s);
208 PlayState getPlayState() const { return _playState; }
210 // delegates to movie_root (possibly wrong)
211 void set_background_color(const rgba& color);
213 /// Return true if we have any mouse event handlers.
215 /// NOTE: this function currently does not consider
216 /// general mouse event handlers MOUSE_MOVE, MOUSE
217 virtual bool mouseEnabled() const;
219 /// \brief
220 /// Return the topmost entity that the given point
221 /// covers that can receive mouse events. NULL if
222 /// none. Coords are in parent's frame.
223 virtual InteractiveObject* topmostMouseEntity(std::int32_t x,
224 std::int32_t y);
226 // see dox in DisplayObject.h
227 const DisplayObject* findDropTarget(std::int32_t x, std::int32_t y,
228 DisplayObject* dragging) const;
230 void setDropTarget(const std::string& tgt) {
231 _droptarget = tgt;
234 const std::string& getDropTarget() const {
235 return _droptarget;
238 /// Advance to the next frame of the MovieClip.
240 /// Actions will be executed or pushed to the queue as necessary.
241 virtual void advance();
243 /// Set the sprite state at the specified frame number.
245 /// 0-based frame numbers!!
246 ///(in contrast to ActionScript and Flash MX)
248 DSOEXPORT void goto_frame(size_t target_frame_number);
250 /// Parse frame spec and return a 0-based frame number.
252 /// If frame spec cannot be converted to !NAN and !Infinity number
253 /// it will be converted to a string and considered a
254 /// frame label (returns false if referring to an
255 /// unknwown label).
257 /// @param frame_spec
258 /// The frame specification.
260 /// @param frameno
261 /// The evaluated frame number (0-based)
263 /// @return
264 /// True if the frame_spec could be resolved to a frame number.
265 /// False if the frame_spec was invalid.
266 bool get_frame_number(const as_value& frame_spec, size_t& frameno) const;
268 /// Look up the labeled frame, and jump to it.
269 bool goto_labeled_frame(const std::string& label);
271 /// Render this MovieClip.
272 virtual void display(Renderer& renderer, const Transform& xform);
274 /// Draw this MovieClip
276 /// This is effectively the same as display(), but uses only the passed
277 /// transform.
278 void draw(Renderer& renderer, const Transform& xform);
280 void omit_display();
282 /// Swap depth of the given DisplayObjects in the DisplayList
284 /// See DisplayList::swapDepths for more info
285 void swapDepths(DisplayObject* ch1, int newdepth)
287 _displayList.swapDepths(ch1, newdepth);
290 /// Return the DisplayObject at given depth in our DisplayList.
292 /// @return NULL if the specified depth is available (no chars there)
293 DisplayObject* getDisplayObjectAtDepth(int depth);
295 /// Attach a DisplayObject at the specified depth.
296 DisplayObject* addDisplayListObject(DisplayObject* obj, int depth);
298 /// Place a DisplayObject or mask to the DisplayList.
300 /// This method instantiates the given DisplayObject definition
301 /// and places it on the stage at the given depth.
303 /// If the specified depth is already occupied, it results a no-ops.
304 /// Otherwise, a new DisplayObject will be created and onload handler
305 /// will be triggerred.
307 /// @param tag
308 /// A swf defined placement tag (PlaceObject, or PlaceObject2,
309 /// or PlaceObject3).
310 /// No ownership transfer, the tag is still owned by the
311 /// movie_definition class.
313 /// @param dlist
314 /// The display list to add the DisplayObject to.
316 /// @return
317 /// A pointer to the DisplayObject being added or NULL
318 DisplayObject* add_display_object(const SWF::PlaceObject2Tag* tag,
319 DisplayList& dlist);
321 /// Proxy of DisplayList::moveDisplayObject()
322 void move_display_object(const SWF::PlaceObject2Tag* tag,
323 DisplayList& dlist);
325 /// Proxy of DisplayList::replaceDisplayObject()
326 void replace_display_object(const SWF::PlaceObject2Tag* tag,
327 DisplayList& dlist);
329 /// Proxy of DisplayList::removeDisplayObject()
330 void remove_display_object(const SWF::PlaceObject2Tag* tag,
331 DisplayList& dlist);
333 /// \brief
334 /// Remove the object at the specified depth.
336 /// NOTE:
337 /// (1)the id parameter is currently unused, but
338 /// required to avoid breaking of inheritance from movie.h.
339 /// (2)the id might be used for specifying a DisplayObject
340 /// in the depth(think about multiple DisplayObjects within the same
341 /// depth, not tested and a rare case)
342 void remove_display_object(int depth, int /*id*/);
344 void unloadMovie();
346 /// Attach the given DisplayObject instance to current display list
348 /// @param newch The DisplayObject instance to attach.
349 /// @param depth The depth to assign to the instance.
350 void attachCharacter(DisplayObject& newch, int depth, as_object* initObject);
352 /// Handle placement event
354 /// This callback will (not known to be a problem):
356 /// (1) Register ourselves with the global instance list
357 /// (2) Take note of our original target path
358 /// (3) Register as listener of core broadcasters
359 /// (4) Execute tags of frame 0
361 /// The callback will also (known to be bogus):
363 /// (1) Construct this instance as an ActionScript object.
364 /// See constructAsScriptObject() method, including constructing
365 /// registered class and adding properties.
366 virtual void construct(as_object* initObj = nullptr);
368 /// Mark this sprite as destroyed
370 /// This is an override of DisplayObject::destroy()
372 /// A sprite should be destroyed when is removed from the display
373 /// list and is not more needed for names (target) resolutions.
374 /// Sprites are needed for names resolution whenever themselves
375 /// or a contained object has an onUnload event handler defined,
376 /// in which case we want the event handler to find the 'this'
377 /// variable w/out attempting to rebind it.
379 /// When a sprite is destroyed, all its children are also destroyed.
380 ///
381 /// Note: this function will release most memory associated with
382 /// the sprite as no members or drawable should be needed anymore.
383 void destroy();
385 /// Add the given action buffer to the list of action
386 /// buffers to be processed at the end of the next
387 /// frame advance.
388 void add_action_buffer(const action_buffer* a)
390 if (!_callingFrameActions) queueAction(*a);
391 else execute_action(*a);
395 /// \brief
396 /// Execute the given init action buffer, if not done yet
397 /// for the target DisplayObject id.
399 /// The action will normally be pushed on queue, but will
400 /// be executed immediately if we are executing actions
401 /// resulting from a callFame instead.
403 /// @param a
404 /// The action buffer to execute
406 /// @param cid
407 /// The referenced DisplayObject id
408 void execute_init_action_buffer(const action_buffer& a, int cid);
410 /// Execute a single action buffer (DOACTION block)
411 void execute_action(const action_buffer& ab);
413 MovieClip* to_movie () { return this; }
415 /// The various methods for sending data in requests.
417 /// Used in loadMovie, getURL, loadVariables etc.
418 enum VariablesMethod
420 METHOD_NONE = 0,
421 METHOD_GET,
422 METHOD_POST
425 // See dox in DisplayObject.h
426 virtual void getLoadedMovie(Movie* newMovie);
428 /// \brief
429 /// Load url-encoded variables from the given url, optionally
430 /// sending variables from this timeline too.
432 /// A LoadVariablesThread will be started to load and parse variables
433 /// and added to the _loadVariableRequests. Then, at every ::advance_sprite
434 /// any completed threads will be processed
435 /// (see processCompletedLoadVariableRequests)
437 /// NOTE: the given url will be security-checked
439 /// @param urlstr: The url to load variables from.
441 /// @param sendVarsMethod: The VariablesMethod to use. If METHOD_NONE,
442 /// no data will be sent.
443 void loadVariables(const std::string& urlstr,
444 VariablesMethod sendVarsMethod);
446 /// Get TextField variables
448 /// TODO: this is unlikely to be the best way of doing it, and it would
449 /// simplify things if this function could be dropped.
450 bool getTextFieldVariables(const ObjectURI& uri, as_value& val);
452 // Set TextField variables
454 /// TODO: this is also unlikely to be the best way to do it.
455 bool setTextFieldVariables(const ObjectURI& uri, const as_value& val);
457 /// Search for a named object on the DisplayList
459 /// These are properties, but not attached as genuine members to the
460 /// MovieClip object. They take priority over DisplayObject magic
461 /// properties and inherited properties, but not over own properties.
463 /// @param name Object identifier. This function handles
464 /// case-sensitivity.
465 /// @return The object if found, otherwise 0.
466 DisplayObject* getDisplayListObject(const ObjectURI& uri);
468 /// Overridden to look in DisplayList for a match
469 as_object* pathElement(const ObjectURI& uri);
471 /// Execute the actions for the specified frame.
473 /// The frame_spec could be an integer or a string.
474 virtual void call_frame_actions(const as_value& frame_spec);
476 /// Duplicate this sprite in its timeline
478 /// Add the new DisplayObject at a the given depth to this sprite
479 /// parent displaylist.
481 /// NOTE: the call will fail for the root movie (no parent).
482 /// NOTE2: any DisplayObject at the given target depth will be
483 /// replaced by the new DisplayObject
484 /// NOTE3: event handlers will also be copied
486 /// @param newname
487 /// Name for the copy
489 /// @param newdepth
490 /// Depth for the copy
492 /// @param init_object
493 /// If not null, will be used to copy properties over.
494 MovieClip* duplicateMovieClip(const std::string& newname,
495 int newdepth, as_object* init_object = nullptr);
497 /// Called when a mouse event affects this MovieClip
498 virtual void mouseEvent(const event_id& id) {
499 notifyEvent(id);
502 /// Dispatch event handler(s), if any.
504 /// This handles key, mouse, and specific MovieClip events.
505 /// TODO: split this sensibly.
506 void notifyEvent(const event_id& id);
508 // inherited from DisplayObject class, see dox in DisplayObject.h
509 virtual as_environment& get_environment() {
510 return _environment;
513 /// \brief
514 /// Set a TextField variable to this timeline
516 /// A TextField variable is a variable that acts
517 /// as a setter/getter for a TextField 'text' member.
518 void set_textfield_variable(const ObjectURI& name, TextField* ch);
520 void add_invalidated_bounds(InvalidatedRanges& ranges, bool force);
522 const DisplayList& getDisplayList() const {
523 return _displayList;
526 /// Return the next highest available depth
528 /// Placing an object at the depth returned by
529 /// this function should result in a DisplayObject
530 /// that is displayd above all others
531 int getNextHighestDepth() const {
532 return _displayList.getNextHighestDepth();
535 /// Set the currently playing m_sound_stream_id
537 // TODO: rename to setStreamingSoundId
538 void setStreamSoundId(int id);
540 /// Remove this sprite from the stage.
542 /// This function is intended to be called by
543 /// effect of a removeMovieClip() ActionScript call
544 /// and implements the checks required for this specific
545 /// case.
547 /// Callers are:
548 /// - The ActionRemoveClip tag handler.
549 /// - The global removeMovieClip(target) function.
550 /// - The MovieClip.removeMovieClip() method.
552 /// The removal will not occur if the depth of this
553 /// DisplayObjects is not in the "dynamic" range [0..1048575]
554 /// as described at the following URL:
555 ///
556 /// http://www.senocular.com/flash/tutorials/depths/?page=2
558 /// A testcases for this behaviour can be found in
560 /// testsuite/misc-ming.all/displaylist_depths_test.swf
561 void removeMovieClip();
563 /// Direct access to the Graphics object for drawing.
564 DynamicShape& graphics() {
565 set_invalidated();
566 return _drawable;
569 /// Set focus to this MovieClip
571 /// @return true if this MovieClip can receive focus.
572 virtual bool handleFocus();
574 /// @} Drawing API
576 /// Set all variables in the given map with their corresponding values
577 DSOEXPORT void setVariables(const MovieVariables& vars);
579 /// Enumerate child DisplayObjects
581 /// See DisplayObject::enumerateNonProperties for more info.
582 virtual void visitNonProperties(KeyVisitor& v) const;
584 /// Delete DisplayObjects removed from the stage
585 /// from the display lists
586 void cleanupDisplayList();
588 /// Queue the given action buffer
590 /// The action will be pushed on the current
591 /// global list (see movie_root).
593 void queueAction(const action_buffer& buf);
595 /// Construct this instance as an ActionScript object
597 /// This method invokes the constructor associated with our
598 /// definition, either MovieClip or any user-speficied one
599 /// (see sprite_definition::registerClass).
600 /// It will also invoke the onClipConstruct and onConstruct handlers.
601 void constructAsScriptObject();
603 /// Return true if getAsRoot() should return the *relative* root,
604 /// false otherwise.
605 bool getLockRoot() const { return _lockroot; }
607 /// Set whether getAsRoot() should return the *relative* root,
608 /// false otherwise. True for relative root.
609 void setLockRoot(bool lr) { _lockroot=lr; }
611 /// Return the version of the SWF this MovieClip was parsed from.
612 virtual int getDefinitionVersion() const;
614 protected:
616 /// Unload all contents in the displaylist and this instance
618 /// Return true if there was an unloadHandler.
619 virtual bool unloadChildren();
621 /// Mark sprite-specific reachable resources.
623 /// sprite-specific reachable resources are:
624 /// - DisplayList items (current, backup and frame0 ones)
625 /// - Canvas for dynamic drawing (_drawable)
626 /// - sprite environment
627 /// - definition the sprite has been instantiated from
628 /// - Textfields having an associated variable registered in this instance.
629 /// - Relative root of this instance (_swf)
631 virtual void markOwnResources() const;
633 // Used by BitmapMovie.
634 void placeDisplayObject(DisplayObject* ch, int depth) {
635 _displayList.placeDisplayObject(ch, depth);
638 private:
640 /// Process any completed loadVariables request
641 void processCompletedLoadVariableRequests();
643 /// Process a completed loadVariables request
644 void processCompletedLoadVariableRequest(LoadVariablesThread& request);
647 /// Execute the tags associated with the specified frame.
649 /// @param frame
650 /// Frame number. 0-based
652 /// @param dlist
653 /// The display list to have control tags act upon.
655 /// @param typeflags
656 /// Which kind of control tags we want to execute.
657 void executeFrameTags(size_t frame, DisplayList& dlist,
658 int typeflags = SWF::ControlTag::TAG_DLIST |
659 SWF::ControlTag::TAG_ACTION);
661 void stopStreamSound();
663 /// Return value of the 'enabled' property cast to a boolean value.
665 /// This is true if not found (undefined to bool evaluates to false).
667 /// When a MovieClip is "disabled", its handlers of button-like events
668 /// are disabled, and automatic tab ordering won't include it.
669 bool isEnabled() const;
671 /// Check whether a point hits our drawable shape.
673 /// This is possible because the drawable does not have its own
674 /// transform, so we can use our own. The points are expressed in
675 /// world space.
676 bool hitTestDrawable(std::int32_t x, std::int32_t y) const;
678 /// Advance to a previous frame.
680 /// This function will basically restore the DisplayList as it supposedly
681 /// was *before* executing tags in target frame and then execute target
682 /// frame tags (both DLIST and ACTION ones).
684 /// In practice, it will:
686 /// - Remove from current DisplayList:
687 /// - Timeline instances constructed after target frame
688 /// - Timeline instances constructed before or at the target frame but no
689 /// more at the original depth
690 /// - Dynamic instances found in the static depth zone
691 /// - Execute all displaylist tags from first to one-before target frame,
692 /// appropriately setting _currentFrame as it goes, finally execute
693 /// both displaylist and action
694 /// tags for target frame.
696 /// Callers of this methods are:
697 /// - goto_frame (for jump-backs)
698 /// - advance_sprite (for loop-back)
700 /// See:
701 // http://www.gnashdev.org/wiki/index.php/TimelineControl
702 /// #Timeline_instances
704 /// @param targetFrame
705 /// The target frame for which we're willing to restore the static
706 /// DisplayList.
707 /// 0-based.
709 /// POSTCONDITIONS:
711 /// - _currentFrame == targetFrame
713 /// TODO: consider using this same function for jump-forward too,
714 /// with some modifications...
716 void restoreDisplayList(size_t targetFrame);
718 /// Increment _currentFrame, and take care of looping.
719 void increment_frame_and_check_for_loop();
721 /// Unregister textfield variables bound to unloaded TextFields
722 void cleanup_textfield_variables();
724 /// This is either sprite_definition (for sprites defined by
725 /// DefineSprite tag) or movie_def_impl (for the top-level movie).
726 const boost::intrusive_ptr<const movie_definition> _def;
728 /// List of loadVariables requests
729 typedef boost::ptr_list<LoadVariablesThread> LoadVariablesThreads;
731 /// List of active loadVariable requests
733 /// At ::advance_sprite time, all completed requests will
734 /// be processed (variables imported in this timeline scope)
735 /// and removed from the list.
736 LoadVariablesThreads _loadVariableRequests;
738 /// The SWF that this MovieClip belongs to.
739 Movie* _swf;
741 /// The canvas for dynamic drawing
742 DynamicShape _drawable;
744 PlayState _playState;
746 /// This timeline's variable scope
747 as_environment _environment;
749 /// We'll only allocate Textfield variables map if
750 /// we need them (ie: anyone calls set_textfield_variable)
752 std::unique_ptr<TextFieldIndex> _text_variables;
754 std::string _droptarget;
756 // 0-based index to current frame
757 size_t _currentFrame;
759 /// soundid for current playing stream. If no stream set to -1
760 int m_sound_stream_id;
762 // true if this sprite reached the last frame and restarted
763 bool _hasLooped;
765 // true if orphaned tags (tags found after last advertised showframe)
766 // have been executed at least once.
767 bool _flushedOrphanedTags;
769 // true is we're calling frame actions
770 bool _callingFrameActions;
772 bool _lockroot;
774 bool _onLoadCalled;
777 } // end of namespace gnash
779 #endif // GNASH_SPRITE_INSTANCE_H