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