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