explicitly remove listensprite
[potlatch2.git] / net / systemeD / halcyon / EntityUI.as
1 package net.systemeD.halcyon {
2
3         import flash.display.*;
4         import flash.events.MouseEvent;
5         import flash.text.AntiAliasType;
6         import flash.text.GridFitType;
7         import net.systemeD.halcyon.styleparser.StyleList;
8     import net.systemeD.halcyon.connection.*;
9
10         /** Parent class of representations of map Entities, with properties about how they should be drawn. */ 
11         public class EntityUI {
12
13                 /** The entity represented by this class. */
14                 protected var entity:Entity;
15                 /** Current StyleList for this entity. */
16                 protected var styleList:StyleList;
17                 /** Instances in display list */
18                 protected var sprites:Array=new Array();
19                 /** The clickable sprite that will receive events. */
20                 protected var listenSprite:Sprite=new Sprite();
21                 /** Hitzone for the sprite - must be set by subclass-specific code. */
22                 protected var hitzone:Sprite;
23                 /** Special context-sensitive classes such as :hover. */
24                 protected var stateClasses:Object=new Object();
25                 /** Map layer */
26                 protected var layer:Number=0;
27                 /** Is drawing suspended? */
28                 protected var suspended:Boolean=false;  
29                 /** Redraw called while suspended? */
30                 protected var redrawDue:Boolean=false;
31                 /** Sprite to clear back to */
32                 protected var clearLimit:uint=0;
33                 /** Reference to parent MapPaint */
34                 public var paint:MapPaint;      
35                 /** Does object respond to clicks? */
36                 public var interactive:Boolean=true;
37                 /** Can it be deleted when offscreen? */
38                 public var purgable:Boolean=true;
39
40                 protected const FILLSPRITE:uint=0;
41                 protected const CASINGSPRITE:uint=1;
42                 protected const STROKESPRITE:uint=2;
43                 protected const NAMESPRITE:uint=3;
44                 protected const WAYCLICKSPRITE:uint=0;
45                 protected const NODECLICKSPRITE:uint=1;
46
47                 public static const DEFAULT_TEXTFIELD_PARAMS:Object = {
48                         embedFonts: true,
49                         antiAliasType: AntiAliasType.ADVANCED,
50                         gridFitType: GridFitType.NONE
51                 };
52
53                 /** Constructor function, adds a bunch of event listeners. */
54                 public function EntityUI(entity:Entity, paint:MapPaint) {
55                         this.entity=entity;
56                         this.paint=paint;
57             entity.addEventListener(Connection.TAG_CHANGED, tagChanged, false, 0, true);
58                         entity.addEventListener(Connection.ADDED_TO_RELATION, relationAdded, false, 0, true);
59                         entity.addEventListener(Connection.REMOVED_FROM_RELATION, relationRemoved, false, 0, true);
60                         entity.addEventListener(Connection.SUSPEND_REDRAW, suspendRedraw, false, 0, true);
61                         entity.addEventListener(Connection.RESUME_REDRAW, resumeRedraw, false, 0, true);
62                         listenSprite.addEventListener(MouseEvent.CLICK, mouseEvent, false, 0, true);
63                         listenSprite.addEventListener(MouseEvent.DOUBLE_CLICK, mouseEvent, false, 0, true);
64                         listenSprite.addEventListener(MouseEvent.ROLL_OVER, mouseEvent, false, 0, true);
65                         listenSprite.addEventListener(MouseEvent.MOUSE_OUT, mouseEvent, false, 0, true);
66                         listenSprite.addEventListener(MouseEvent.MOUSE_DOWN, mouseEvent, false, 0, true);
67                         listenSprite.addEventListener(MouseEvent.MOUSE_UP, mouseEvent, false, 0, true);
68                         listenSprite.addEventListener(MouseEvent.MOUSE_MOVE, mouseEvent, false, 0, true);
69                 }
70
71                 /** Remove the default event listeners. */
72                 protected function removeGenericEventListeners():void {
73             entity.removeEventListener(Connection.TAG_CHANGED, tagChanged);
74                         entity.removeEventListener(Connection.ADDED_TO_RELATION, relationAdded);
75                         entity.removeEventListener(Connection.REMOVED_FROM_RELATION, relationRemoved);
76                         entity.removeEventListener(Connection.SUSPEND_REDRAW, suspendRedraw);
77                         entity.removeEventListener(Connection.RESUME_REDRAW, resumeRedraw);
78                         listenSprite.removeEventListener(MouseEvent.CLICK, mouseEvent);
79                         listenSprite.removeEventListener(MouseEvent.DOUBLE_CLICK, mouseEvent);
80                         listenSprite.removeEventListener(MouseEvent.ROLL_OVER, mouseEvent);
81                         listenSprite.removeEventListener(MouseEvent.MOUSE_OUT, mouseEvent);
82                         listenSprite.removeEventListener(MouseEvent.MOUSE_DOWN, mouseEvent);
83                         listenSprite.removeEventListener(MouseEvent.MOUSE_UP, mouseEvent);
84                         listenSprite.removeEventListener(MouseEvent.MOUSE_MOVE, mouseEvent);
85                 }
86
87                 // -----------------------------------------------------------------
88                 // Event listeners
89                 
90                 protected function attachRelationListeners():void {
91                     var relations:Array = entity.parentRelations;
92             for each(var relation:Relation in relations ) {
93                 relation.addEventListener(Connection.TAG_CHANGED, relationTagChanged, false, 0, true);
94             }
95                 }
96
97                 protected function removeRelationListeners():void {
98                         var relations:Array = entity.parentRelations;
99             for each(var relation:Relation in relations) {
100                 relation.removeEventListener(Connection.TAG_CHANGED, relationTagChanged);
101             }
102                 }
103
104                 protected function relationAdded(event:RelationMemberEvent):void {
105                     event.relation.addEventListener(Connection.TAG_CHANGED, relationTagChanged, false, 0, true);
106                         invalidateStyleList();
107                     redraw();
108                 }
109                 
110                 protected function relationRemoved(event:RelationMemberEvent):void {
111                     event.relation.removeEventListener(Connection.TAG_CHANGED, relationTagChanged);
112                         invalidateStyleList();
113                     redraw();
114                 }
115                 
116         protected function tagChanged(event:TagEvent):void {
117                         invalidateStyleList();
118             redraw();
119         }
120
121         protected function relationTagChanged(event:TagEvent):void {
122                         invalidateStyleList();
123             redraw();
124         }
125                 
126         protected function mouseEvent(event:MouseEvent):void {
127                         paint.map.entityMouseEvent(event, entity);
128         }
129
130
131                 // -----------------------------------------------------------------
132
133                 /** Add object (stroke/fill/roadname) to layer sprite*/
134                 
135                 protected function addToLayer(s:DisplayObject,t:uint,sublayer:int=-1):void {
136                         var l:Sprite, o:Sprite;
137                         if (sublayer!=-1) {
138                                 o=paint.sublayer(layer,sublayer);
139                         } else {
140                                 l=paint.getPaintSpriteAt(layer);
141                                 o=l.getChildAt(t) as Sprite;
142                         }
143                         o.addChild(s);
144                         if (sprites.indexOf(s)==-1) { sprites.push(s); }
145             if ( s is Sprite ) {
146                 Sprite(s).mouseChildren = false;
147                 Sprite(s).mouseEnabled = false;
148             }
149                 }
150
151                 // What does this do, could someone please document?
152                 protected function setListenSprite():void {
153                         var l:Sprite=paint.getHitSpriteAt(layer);
154                         var s:Sprite;
155                         if (entity is Way) { s=l.getChildAt(0) as Sprite; }
156                                       else { s=l.getChildAt(1) as Sprite; }
157                         
158                         if (hitzone) {
159                                 if (!listenSprite.parent) { s.addChild(listenSprite); if (sprites.indexOf(listenSprite)==-1) { sprites.push(listenSprite); } }
160                                 if (!hitzone.parent)      { s.addChild(hitzone     ); if (sprites.indexOf(hitzone     )==-1) { sprites.push(hitzone     ); } }
161                                 listenSprite.hitArea = hitzone;
162                                 listenSprite.buttonMode = true;
163                                 listenSprite.mouseChildren = true;
164                                 listenSprite.mouseEnabled = true;
165                         } else if (listenSprite.parent) { 
166                                 listenSprite.parent.removeChild(listenSprite);
167                         }
168                 }
169
170                 /** Remove all sprites associated with this entity, and clear hitzone. */
171                 public function removeSprites():void {
172                         while (sprites.length>clearLimit) {
173                                 var d:DisplayObject=sprites.pop();
174                                 if (d.parent) { d.parent.removeChild(d); }
175                         }
176                         if (clearLimit==0) {
177                                 listenSprite.hitArea=null;
178                                 hitzone=null;
179                         }
180                 }
181                 
182                 public function removeListenSprite():void {
183                         if (listenSprite && listenSprite.parent) listenSprite.parent.removeChild(listenSprite);
184                         if (hitzone && hitzone.parent) hitzone.parent.removeChild(hitzone);
185                         listenSprite=null;
186                         hitzone=null;
187                 }
188                 
189                 public function protectSprites():void { clearLimit=sprites.length; }
190                 public function unprotectSprites():void { clearLimit=0; }
191
192                 protected function offsetSprites(x:Number, y:Number):void {
193                         for each (var d:DisplayObject in sprites) {
194                                 d.x=x; d.y=y;
195                         }
196                 }
197
198         public function setHighlight(settings:Object):void {
199                         var changed:Boolean=false;
200                         for (var stateType:String in settings) {
201                                 if (setStateClass(stateType, settings[stateType])) { changed=true; }
202                         }
203                         if (changed) redraw();
204         }
205
206         /**
207         * Sets a state class (eg :hover, :dupe) for this entityUI. If the state class has changed it will
208         * invalidate the style list to force the style to be recalculated during redraw.
209         */
210         public function setStateClass(stateType:String, isOn:*):Boolean {
211                         if ( isOn == true ) { isOn='yes'; }
212             if ( isOn && stateClasses[stateType] != isOn ) {
213                 stateClasses[stateType] = isOn;
214                                 invalidateStyleList();
215                                 return true;
216             } else if ( !isOn && stateClasses[stateType] != null ) {
217                 delete stateClasses[stateType];
218                                 invalidateStyleList();
219                                 return true;
220             }
221                         return false;
222         }
223
224         /**
225         * applies the state classes (eg :hover, :area) for this entityUI to the given list of 'real' tags.
226         * This then gives you a modified list of tags used for styling the entityUI.
227         */
228                 public function applyStateClasses(tags:Object):Object {
229             for (var stateKey:String in stateClasses) {
230                 tags[":"+stateKey] = 'yes';
231             }
232                         return tags;
233                 }
234                 
235                 public function toString():String {
236                         return "[EntityUI "+entity+"]";
237                 }
238
239                 /** Request redraw */
240                 
241                 public function redraw():Boolean {
242                         if (suspended) { redrawDue=true; return false; }
243                         return doRedraw();
244                 }
245                 
246                 /** Actually do the redraw. To be overwritten. */
247                 public function doRedraw():Boolean {
248                         return false;
249                 }
250                 
251                 /** Temporarily suspend redrawing of object. */
252                 public function suspendRedraw(event:EntityEvent):void {
253                         suspended=true;
254                         redrawDue=false;
255                 }
256                 
257                 /** Resume redrawing. */
258                 public function resumeRedraw(event:EntityEvent):void {
259                         suspended=false;
260                         if (redrawDue) { 
261                                 doRedraw();
262                                 redrawDue=false;
263                         }
264                 }
265                 
266                 public function invalidateStyleList():void {
267                         styleList=null;
268                 }
269                 
270         }
271
272 }