1 /****************************************************************************
  2  Copyright (c) 2011-2012 cocos2d-x.org
  3  Copyright (c) 2013-2014 Chukong Technologies Inc.
  4 
  5  http://www.cocos2d-x.org
  6 
  7  Permission is hereby granted, free of charge, to any person obtaining a copy
  8  of this software and associated documentation files (the "Software"), to deal
  9  in the Software without restriction, including without limitation the rights
 10  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 11  copies of the Software, and to permit persons to whom the Software is
 12  furnished to do so, subject to the following conditions:
 13 
 14  The above copyright notice and this permission notice shall be included in
 15  all copies or substantial portions of the Software.
 16 
 17  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 18  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 19  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 20  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 21  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 22  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 23  THE SOFTWARE.
 24  ****************************************************************************/
 25 
 26 /**
 27  * <p>
 28  *     The base class of event listener.                                                                        <br/>
 29  *     If you need custom listener which with different callback, you need to inherit this class.               <br/>
 30  *     For instance, you could refer to EventListenerAcceleration, EventListenerKeyboard,                       <br/>
 31  *      EventListenerTouchOneByOne, EventListenerCustom.
 32  * </p>
 33  * @class
 34  * @extends cc.Class
 35  */
 36 cc.EventListener = cc.Class.extend(/** @lends cc.EventListener# */{
 37     _onEvent: null,                          // Event callback function
 38     _type: 0,                                 // Event listener type
 39     _listenerID: null,                       // Event listener ID
 40     _registered: false,                     // Whether the listener has been added to dispatcher.
 41 
 42     _fixedPriority: 0,                      // The higher the number, the higher the priority, 0 is for scene graph base priority.
 43     _node: null,                           // scene graph based priority
 44     _paused: true,                        // Whether the listener is paused
 45     _isEnabled: true,                      // Whether the listener is enabled
 46 
 47     /**
 48      * Initializes event with type and callback function
 49      * @param {number} type
 50      * @param {string} listenerID
 51      * @param {function} callback
 52      */
 53     ctor: function (type, listenerID, callback) {
 54         this._onEvent = callback;
 55         this._type = type || 0;
 56         this._listenerID = listenerID || "";
 57     },
 58 
 59     /**
 60      * <p>
 61      *     Sets paused state for the listener
 62      *     The paused state is only used for scene graph priority listeners.
 63      *     `EventDispatcher::resumeAllEventListenersForTarget(node)` will set the paused state to `true`,
 64      *     while `EventDispatcher::pauseAllEventListenersForTarget(node)` will set it to `false`.
 65      *     @note 1) Fixed priority listeners will never get paused. If a fixed priority doesn't want to receive events,
 66      *              call `setEnabled(false)` instead.
 67      *            2) In `Node`'s onEnter and onExit, the `paused state` of the listeners which associated with that node will be automatically updated.
 68      * </p>
 69      * @param {boolean} paused
 70      * @private
 71      */
 72     _setPaused: function (paused) {
 73         this._paused = paused;
 74     },
 75 
 76     /**
 77      * Checks whether the listener is paused
 78      * @returns {boolean}
 79      * @private
 80      */
 81     _isPaused: function () {
 82         return this._paused;
 83     },
 84 
 85     /**
 86      * Marks the listener was registered by EventDispatcher
 87      * @param {boolean} registered
 88      * @private
 89      */
 90     _setRegistered: function (registered) {
 91         this._registered = registered;
 92     },
 93 
 94     /**
 95      * Checks whether the listener was registered by EventDispatcher
 96      * @returns {boolean}
 97      * @private
 98      */
 99     _isRegistered: function () {
100         return this._registered;
101     },
102 
103     /**
104      * Gets the type of this listener
105      * @note It's different from `EventType`, e.g. TouchEvent has two kinds of event listeners - EventListenerOneByOne, EventListenerAllAtOnce
106      * @returns {number}
107      * @private
108      */
109     _getType: function () {
110         return this._type;
111     },
112 
113     /**
114      *  Gets the listener ID of this listener
115      *  When event is being dispatched, listener ID is used as key for searching listeners according to event type.
116      * @returns {string}
117      * @private
118      */
119     _getListenerID: function () {
120         return this._listenerID;
121     },
122 
123     /**
124      * Sets the fixed priority for this listener
125      *  @note This method is only used for `fixed priority listeners`, it needs to access a non-zero value. 0 is reserved for scene graph priority listeners
126      * @param {number} fixedPriority
127      * @private
128      */
129     _setFixedPriority: function (fixedPriority) {
130         this._fixedPriority = fixedPriority;
131     },
132 
133     /**
134      * Gets the fixed priority of this listener
135      * @returns {number} 0 if it's a scene graph priority listener, non-zero for fixed priority listener
136      * @private
137      */
138     _getFixedPriority: function () {
139         return this._fixedPriority;
140     },
141 
142     /**
143      * Sets scene graph priority for this listener
144      * @param {cc.Node} node
145      * @private
146      */
147     _setSceneGraphPriority: function (node) {
148         this._node = node;
149     },
150 
151     /**
152      * Gets scene graph priority of this listener
153      * @returns {cc.Node} if it's a fixed priority listener, non-null for scene graph priority listener
154      * @private
155      */
156     _getSceneGraphPriority: function () {
157         return this._node;
158     },
159 
160     /**
161      * Checks whether the listener is available.
162      * @returns {boolean}
163      */
164     checkAvailable: function () {
165         return this._onEvent !== null;
166     },
167 
168     /**
169      * Clones the listener, its subclasses have to override this method.
170      * @returns {cc.EventListener}
171      */
172     clone: function () {
173         return null;
174     },
175 
176     /**
177      *  Enables or disables the listener
178      *  @note Only listeners with `enabled` state will be able to receive events.
179      *          When an listener was initialized, it's enabled by default.
180      *          An event listener can receive events when it is enabled and is not paused.
181      *          paused state is always false when it is a fixed priority listener.
182      * @param {boolean} enabled
183      */
184     setEnabled: function(enabled){
185         this._isEnabled = enabled;
186     },
187 
188     /**
189      * Checks whether the listener is enabled
190      * @returns {boolean}
191      */
192     isEnabled: function(){
193         return this._isEnabled;
194     },
195 
196     /**
197      * <p>Currently JavaScript Bindings (JSB), in some cases, needs to use retain and release. This is a bug in JSB,
198      * and the ugly workaround is to use retain/release. So, these 2 methods were added to be compatible with JSB.
199      * This is a hack, and should be removed once JSB fixes the retain/release bug<br/>
200      * You will need to retain an object if you created a listener and haven't added it any target node during the same frame.<br/>
201      * Otherwise, JSB's native autorelease pool will consider this object a useless one and release it directly,<br/>
202      * when you want to use it later, a "Invalid Native Object" error will be raised.<br/>
203      * The retain function can increase a reference count for the native object to avoid it being released,<br/>
204      * you need to manually invoke release function when you think this object is no longer needed, otherwise, there will be memory learks.<br/>
205      * retain and release function call should be paired in developer's game code.</p>
206      * @function
207      * @see cc.EventListener#release
208      */
209     retain:function () {
210     },
211     /**
212      * <p>Currently JavaScript Bindings (JSB), in some cases, needs to use retain and release. This is a bug in JSB,
213      * and the ugly workaround is to use retain/release. So, these 2 methods were added to be compatible with JSB.
214      * This is a hack, and should be removed once JSB fixes the retain/release bug<br/>
215      * You will need to retain an object if you created a listener and haven't added it any target node during the same frame.<br/>
216      * Otherwise, JSB's native autorelease pool will consider this object a useless one and release it directly,<br/>
217      * when you want to use it later, a "Invalid Native Object" error will be raised.<br/>
218      * The retain function can increase a reference count for the native object to avoid it being released,<br/>
219      * you need to manually invoke release function when you think this object is no longer needed, otherwise, there will be memory learks.<br/>
220      * retain and release function call should be paired in developer's game code.</p>
221      * @function
222      * @see cc.EventListener#retain
223      */
224     release:function () {
225     }
226 });
227 
228 // event listener type
229 /**
230  * The type code of unknown event listener.
231  * @constant
232  * @type {number}
233  */
234 cc.EventListener.UNKNOWN = 0;
235 /**
236  * The type code of one by one touch event listener.
237  * @constant
238  * @type {number}
239  */
240 cc.EventListener.TOUCH_ONE_BY_ONE = 1;
241 /**
242  * The type code of all at once touch event listener.
243  * @constant
244  * @type {number}
245  */
246 cc.EventListener.TOUCH_ALL_AT_ONCE = 2;
247 /**
248  * The type code of keyboard event listener.
249  * @constant
250  * @type {number}
251  */
252 cc.EventListener.KEYBOARD = 3;
253 /**
254  * The type code of mouse event listener.
255  * @constant
256  * @type {number}
257  */
258 cc.EventListener.MOUSE = 4;
259 /**
260  * The type code of acceleration event listener.
261  * @constant
262  * @type {number}
263  */
264 cc.EventListener.ACCELERATION = 5;
265 /**
266  * The type code of focus event listener.
267  * @constant
268  * @type {number}
269  */
270 cc.EventListener.ACCELERATION = 6;
271 /**
272  * The type code of custom event listener.
273  * @constant
274  * @type {number}
275  */
276 cc.EventListener.CUSTOM = 8;
277 
278 /**
279  * The type code of Focus change event listener.
280  * @constant
281  * @type {number}
282  */
283 cc.EventListener.FOCUS = 7;
284 
285 cc._EventListenerCustom = cc.EventListener.extend({
286     _onCustomEvent: null,
287     ctor: function (listenerId, callback) {
288         this._onCustomEvent = callback;
289         var selfPointer = this;
290         var listener = function (event) {
291             if (selfPointer._onCustomEvent !== null)
292                 selfPointer._onCustomEvent(event);
293         };
294 
295         cc.EventListener.prototype.ctor.call(this, cc.EventListener.CUSTOM, listenerId, listener);
296     },
297 
298     checkAvailable: function () {
299         return (cc.EventListener.prototype.checkAvailable.call(this) && this._onCustomEvent !== null);
300     },
301 
302     clone: function () {
303         return new cc._EventListenerCustom(this._listenerID, this._onCustomEvent);
304     }
305 });
306 
307 cc._EventListenerCustom.create = function (eventName, callback) {
308     return new cc._EventListenerCustom(eventName, callback);
309 };
310 
311 cc._EventListenerMouse = cc.EventListener.extend({
312     onMouseDown: null,
313     onMouseUp: null,
314     onMouseMove: null,
315     onMouseScroll: null,
316 
317     ctor: function () {
318         var selfPointer = this;
319         var listener = function (event) {
320             var eventType = cc.EventMouse;
321             switch (event._eventType) {
322                 case eventType.DOWN:
323                     if (selfPointer.onMouseDown)
324                         selfPointer.onMouseDown(event);
325                     break;
326                 case eventType.UP:
327                     if (selfPointer.onMouseUp)
328                         selfPointer.onMouseUp(event);
329                     break;
330                 case eventType.MOVE:
331                     if (selfPointer.onMouseMove)
332                         selfPointer.onMouseMove(event);
333                     break;
334                 case eventType.SCROLL:
335                     if (selfPointer.onMouseScroll)
336                         selfPointer.onMouseScroll(event);
337                     break;
338                 default:
339                     break;
340             }
341         };
342         cc.EventListener.prototype.ctor.call(this, cc.EventListener.MOUSE, cc._EventListenerMouse.LISTENER_ID, listener);
343     },
344 
345     clone: function () {
346         var eventListener = new cc._EventListenerMouse();
347         eventListener.onMouseDown = this.onMouseDown;
348         eventListener.onMouseUp = this.onMouseUp;
349         eventListener.onMouseMove = this.onMouseMove;
350         eventListener.onMouseScroll = this.onMouseScroll;
351         return eventListener;
352     },
353 
354     checkAvailable: function () {
355         return true;
356     }
357 });
358 
359 cc._EventListenerMouse.LISTENER_ID = "__cc_mouse";
360 
361 cc._EventListenerMouse.create = function () {
362     return new cc._EventListenerMouse();
363 };
364 
365 cc._EventListenerTouchOneByOne = cc.EventListener.extend({
366     _claimedTouches: null,
367     swallowTouches: false,
368     onTouchBegan: null,
369     onTouchMoved: null,
370     onTouchEnded: null,
371     onTouchCancelled: null,
372 
373     ctor: function () {
374         cc.EventListener.prototype.ctor.call(this, cc.EventListener.TOUCH_ONE_BY_ONE, cc._EventListenerTouchOneByOne.LISTENER_ID, null);
375         this._claimedTouches = [];
376     },
377 
378     setSwallowTouches: function (needSwallow) {
379         this.swallowTouches = needSwallow;
380     },
381 
382     isSwallowTouches: function(){
383         return this.swallowTouches;
384     },
385 
386     clone: function () {
387         var eventListener = new cc._EventListenerTouchOneByOne();
388         eventListener.onTouchBegan = this.onTouchBegan;
389         eventListener.onTouchMoved = this.onTouchMoved;
390         eventListener.onTouchEnded = this.onTouchEnded;
391         eventListener.onTouchCancelled = this.onTouchCancelled;
392         eventListener.swallowTouches = this.swallowTouches;
393         return eventListener;
394     },
395 
396     checkAvailable: function () {
397         if(!this.onTouchBegan){
398             cc.log(cc._LogInfos._EventListenerTouchOneByOne_checkAvailable);
399             return false;
400         }
401         return true;
402     }
403 });
404 
405 cc._EventListenerTouchOneByOne.LISTENER_ID = "__cc_touch_one_by_one";
406 
407 cc._EventListenerTouchOneByOne.create = function () {
408     return new cc._EventListenerTouchOneByOne();
409 };
410 
411 cc._EventListenerTouchAllAtOnce = cc.EventListener.extend({
412     onTouchesBegan: null,
413     onTouchesMoved: null,
414     onTouchesEnded: null,
415     onTouchesCancelled: null,
416 
417     ctor: function(){
418        cc.EventListener.prototype.ctor.call(this, cc.EventListener.TOUCH_ALL_AT_ONCE, cc._EventListenerTouchAllAtOnce.LISTENER_ID, null);
419     },
420 
421     clone: function(){
422         var eventListener = new cc._EventListenerTouchAllAtOnce();
423         eventListener.onTouchesBegan = this.onTouchesBegan;
424         eventListener.onTouchesMoved = this.onTouchesMoved;
425         eventListener.onTouchesEnded = this.onTouchesEnded;
426         eventListener.onTouchesCancelled = this.onTouchesCancelled;
427         return eventListener;
428     },
429 
430     checkAvailable: function(){
431         if (this.onTouchesBegan === null && this.onTouchesMoved === null
432             && this.onTouchesEnded === null && this.onTouchesCancelled === null) {
433             cc.log(cc._LogInfos._EventListenerTouchAllAtOnce_checkAvailable);
434             return false;
435         }
436         return true;
437     }
438 });
439 
440 cc._EventListenerTouchAllAtOnce.LISTENER_ID = "__cc_touch_all_at_once";
441 
442 cc._EventListenerTouchAllAtOnce.create = function(){
443      return new cc._EventListenerTouchAllAtOnce();
444 };
445 
446 /**
447  * Create a EventListener object by json object
448  * @function
449  * @static
450  * @param {object} argObj a json object
451  * @returns {cc.EventListener}
452  * todo: It should be the direct use new
453  * @example
454  * cc.EventListener.create({
455  *       event: cc.EventListener.TOUCH_ONE_BY_ONE,
456  *       swallowTouches: true,
457  *       onTouchBegan: function (touch, event) {
458  *           //do something
459  *           return true;
460  *       }
461  *    });
462  */
463 cc.EventListener.create = function(argObj){
464 
465     cc.assert(argObj&&argObj.event, cc._LogInfos.EventListener_create);
466 
467     var listenerType = argObj.event;
468     delete argObj.event;
469 
470     var listener = null;
471     if(listenerType === cc.EventListener.TOUCH_ONE_BY_ONE)
472         listener = new cc._EventListenerTouchOneByOne();
473     else if(listenerType === cc.EventListener.TOUCH_ALL_AT_ONCE)
474         listener = new cc._EventListenerTouchAllAtOnce();
475     else if(listenerType === cc.EventListener.MOUSE)
476         listener = new cc._EventListenerMouse();
477     else if(listenerType === cc.EventListener.CUSTOM){
478         listener = new cc._EventListenerCustom(argObj.eventName, argObj.callback);
479         delete argObj.eventName;
480         delete argObj.callback;
481     } else if(listenerType === cc.EventListener.KEYBOARD)
482         listener = new cc._EventListenerKeyboard();
483     else if(listenerType === cc.EventListener.ACCELERATION){
484         listener = new cc._EventListenerAcceleration(argObj.callback);
485         delete argObj.callback;
486     } else if(listenerType === cc.EventListener.FOCUS)
487         listener = new cc._EventListenerFocus();
488 
489     for(var key in argObj) {
490         listener[key] = argObj[key];
491     }
492 
493     return listener;
494 };
495 
496 cc._EventListenerFocus = cc.EventListener.extend({
497     clone: function(){
498         var listener = new cc._EventListenerFocus();
499         listener.onFocusChanged = this.onFocusChanged;
500         return listener;
501     },
502     checkAvailable: function(){
503         if(!this.onFocusChanged){
504             cc.log("Invalid EventListenerFocus!");
505             return false;
506         }
507         return true;
508     },
509     onFocusChanged: null,
510     ctor: function(){
511         var listener = function(event){
512             if(this.onFocusChanged)
513                 this.onFocusChanged(event._widgetLoseFocus, event._widgetGetFocus);
514         };
515         cc.EventListener.prototype.ctor.call(this, cc.EventListener.FOCUS, cc._EventListenerFocus.LISTENER_ID, listener);
516     }
517 });
518 
519 cc._EventListenerFocus.LISTENER_ID = "__cc_focus_event";