Merge remote-tracking branch 'gravitystorm/snapshotserver' into refactor
[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.STATUS_CHANGED, statusChanged, false, 0, true);
59                         entity.addEventListener(Connection.ADDED_TO_RELATION, relationAdded, false, 0, true);
60                         entity.addEventListener(Connection.REMOVED_FROM_RELATION, relationRemoved, false, 0, true);
61                         entity.addEventListener(Connection.SUSPEND_REDRAW, suspendRedraw, false, 0, true);
62                         entity.addEventListener(Connection.RESUME_REDRAW, resumeRedraw, false, 0, true);
63                         listenSprite.addEventListener(MouseEvent.CLICK, mouseEvent, false, 0, true);
64                         listenSprite.addEventListener(MouseEvent.DOUBLE_CLICK, mouseEvent, false, 0, true);
65                         listenSprite.addEventListener(MouseEvent.ROLL_OVER, mouseEvent, false, 0, true);
66                         listenSprite.addEventListener(MouseEvent.MOUSE_OUT, mouseEvent, false, 0, true);
67                         listenSprite.addEventListener(MouseEvent.MOUSE_DOWN, mouseEvent, false, 0, true);
68                         listenSprite.addEventListener(MouseEvent.MOUSE_UP, mouseEvent, false, 0, true);
69                         listenSprite.addEventListener(MouseEvent.MOUSE_MOVE, mouseEvent, false, 0, true);
70                 }
71
72                 /** Remove the default event listeners. */
73                 protected function removeGenericEventListeners():void {
74             entity.removeEventListener(Connection.TAG_CHANGED, tagChanged);
75             entity.removeEventListener(Connection.STATUS_CHANGED, statusChanged);
76                         entity.removeEventListener(Connection.ADDED_TO_RELATION, relationAdded);
77                         entity.removeEventListener(Connection.REMOVED_FROM_RELATION, relationRemoved);
78                         entity.removeEventListener(Connection.SUSPEND_REDRAW, suspendRedraw);
79                         entity.removeEventListener(Connection.RESUME_REDRAW, resumeRedraw);
80                         listenSprite.removeEventListener(MouseEvent.CLICK, mouseEvent);
81                         listenSprite.removeEventListener(MouseEvent.DOUBLE_CLICK, mouseEvent);
82                         listenSprite.removeEventListener(MouseEvent.ROLL_OVER, mouseEvent);
83                         listenSprite.removeEventListener(MouseEvent.MOUSE_OUT, mouseEvent);
84                         listenSprite.removeEventListener(MouseEvent.MOUSE_DOWN, mouseEvent);
85                         listenSprite.removeEventListener(MouseEvent.MOUSE_UP, mouseEvent);
86                         listenSprite.removeEventListener(MouseEvent.MOUSE_MOVE, mouseEvent);
87                 }
88
89                 // -----------------------------------------------------------------
90                 // Event listeners
91                 
92                 protected function attachRelationListeners():void {
93                     var relations:Array = entity.parentRelations;
94             for each(var relation:Relation in relations ) {
95                 relation.addEventListener(Connection.TAG_CHANGED, relationTagChanged, false, 0, true);
96             }
97                 }
98
99                 protected function removeRelationListeners():void {
100                         var relations:Array = entity.parentRelations;
101             for each(var relation:Relation in relations) {
102                 relation.removeEventListener(Connection.TAG_CHANGED, relationTagChanged);
103             }
104                 }
105
106                 protected function relationAdded(event:RelationMemberEvent):void {
107                     event.relation.addEventListener(Connection.TAG_CHANGED, relationTagChanged, false, 0, true);
108                         invalidateStyleList();
109                     redraw();
110                 }
111                 
112                 protected function relationRemoved(event:RelationMemberEvent):void {
113                     event.relation.removeEventListener(Connection.TAG_CHANGED, relationTagChanged);
114                         invalidateStyleList();
115                     redraw();
116                 }
117                 
118         protected function tagChanged(event:TagEvent):void {
119                         invalidateStyleList();
120             redraw();
121         }
122
123         protected function relationTagChanged(event:TagEvent):void {
124                         invalidateStyleList();
125             redraw();
126         }
127                 
128         protected function statusChanged(event:EntityEvent):void {
129                         invalidateStyleList();
130             redraw();
131         }
132
133         protected function mouseEvent(event:MouseEvent):void {
134                         paint.map.entityMouseEvent(event, entity);
135         }
136
137
138                 // -----------------------------------------------------------------
139
140                 /** Add object (stroke/fill/roadname) to layer sprite*/
141                 
142                 protected function addToLayer(s:DisplayObject,t:uint,sublayer:int=-1):void {
143                         var l:Sprite, o:Sprite;
144                         if (sublayer!=-1) {
145                                 o=paint.sublayer(layer,sublayer);
146                         } else {
147                                 l=paint.getPaintSpriteAt(layer);
148                                 o=l.getChildAt(t) as Sprite;
149                         }
150                         o.addChild(s);
151                         if (sprites.indexOf(s)==-1) { sprites.push(s); }
152             if ( s is Sprite ) {
153                 Sprite(s).mouseChildren = false;
154                 Sprite(s).mouseEnabled = false;
155             }
156                 }
157
158                 // What does this do, could someone please document?
159                 protected function setListenSprite():void {
160                         var l:Sprite=paint.getHitSpriteAt(layer);
161                         var s:Sprite;
162                         if (entity is Way) { s=l.getChildAt(0) as Sprite; }
163                                       else { s=l.getChildAt(1) as Sprite; }
164                         
165                         if (hitzone) {
166                                 if (!listenSprite.parent) { s.addChild(listenSprite); if (sprites.indexOf(listenSprite)==-1) { sprites.push(listenSprite); } }
167                                 if (!hitzone.parent)      { s.addChild(hitzone     ); if (sprites.indexOf(hitzone     )==-1) { sprites.push(hitzone     ); } }
168                                 listenSprite.hitArea = hitzone;
169                                 listenSprite.buttonMode = true;
170                                 listenSprite.mouseChildren = true;
171                                 listenSprite.mouseEnabled = true;
172                         } else if (listenSprite.parent) { 
173                                 listenSprite.parent.removeChild(listenSprite);
174                         }
175                 }
176
177                 /** Remove all sprites associated with this entity, and clear hitzone. */
178                 public function removeSprites():void {
179                         while (sprites.length>clearLimit) {
180                                 var d:DisplayObject=sprites.pop();
181                                 if (d.parent) { d.parent.removeChild(d); }
182                         }
183                         if (clearLimit==0) {
184                                 listenSprite.hitArea=null;
185                                 hitzone=null;
186                         }
187                 }
188                 
189                 public function removeListenSprite():void {
190                         if (listenSprite && listenSprite.parent) listenSprite.parent.removeChild(listenSprite);
191                         if (hitzone && hitzone.parent) hitzone.parent.removeChild(hitzone);
192                         listenSprite=null;
193                         hitzone=null;
194                 }
195                 
196                 public function protectSprites():void { clearLimit=sprites.length; }
197                 public function unprotectSprites():void { clearLimit=0; }
198
199                 protected function offsetSprites(x:Number, y:Number):void {
200                         for each (var d:DisplayObject in sprites) {
201                                 d.x=x; d.y=y;
202                         }
203                 }
204
205         public function setHighlight(settings:Object):void {
206                         var changed:Boolean=false;
207                         for (var stateType:String in settings) {
208                                 if (setStateClass(stateType, settings[stateType])) { changed=true; }
209                         }
210                         if (changed) redraw();
211         }
212
213         /**
214         * Sets a state class (eg :hover, :dupe) for this entityUI. If the state class has changed it will
215         * invalidate the style list to force the style to be recalculated during redraw.
216         */
217         public function setStateClass(stateType:String, isOn:*):Boolean {
218                         if ( isOn == true ) { isOn='yes'; }
219             if ( isOn && stateClasses[stateType] != isOn ) {
220                 stateClasses[stateType] = isOn;
221                                 invalidateStyleList();
222                                 return true;
223             } else if ( !isOn && stateClasses[stateType] != null ) {
224                 delete stateClasses[stateType];
225                                 invalidateStyleList();
226                                 return true;
227             }
228                         return false;
229         }
230
231         /**
232         * applies the state classes (eg :hover, :area) for this entityUI to the given list of 'real' tags.
233         * This then gives you a modified list of tags used for styling the entityUI.
234         */
235                 public function applyStateClasses(tags:Object):Object {
236             for (var stateKey:String in stateClasses) {
237                 tags[":"+stateKey] = 'yes';
238             }
239                         return tags;
240                 }
241                 
242                 public function toString():String {
243                         return "[EntityUI "+entity+"]";
244                 }
245
246                 /** Request redraw */
247                 
248                 public function redraw():Boolean {
249                         if (suspended) { redrawDue=true; return false; }
250                         return doRedraw();
251                 }
252                 
253                 /** Actually do the redraw. To be overwritten. */
254                 public function doRedraw():Boolean {
255                         return false;
256                 }
257                 
258                 /** Temporarily suspend redrawing of object. */
259                 public function suspendRedraw(event:EntityEvent):void {
260                         suspended=true;
261                         redrawDue=false;
262                 }
263                 
264                 /** Resume redrawing. */
265                 public function resumeRedraw(event:EntityEvent):void {
266                         suspended=false;
267                         if (redrawDue) { 
268                                 doRedraw();
269                                 redrawDue=false;
270                         }
271                 }
272                 
273                 public function invalidateStyleList():void {
274                         styleList=null;
275                 }
276                 
277         }
278
279 }