001package votorola.s.gwt.stage.vote; // Copyright 2012-2013, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Votorola Software"), to deal in the Votorola Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Votorola Software, and to permit persons to whom the Votorola Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Votorola Software. THE VOTOROLA SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE VOTOROLA SOFTWARE OR THE USE OR OTHER DEALINGS IN THE VOTOROLA SOFTWARE. 002 003import com.google.gwt.dom.client.*; 004import com.google.gwt.event.dom.client.ClickEvent; 005import com.google.gwt.event.dom.client.ClickHandler; 006import com.google.gwt.event.dom.client.MouseOutEvent; 007import com.google.gwt.event.dom.client.MouseOutHandler; 008import com.google.gwt.event.dom.client.MouseOverEvent; 009import com.google.gwt.event.dom.client.MouseOverHandler; 010import com.google.web.bindery.event.shared.HandlerRegistration; 011import com.google.gwt.user.client.Timer; 012import org.vectomatic.dom.svg.*; 013import votorola.g.hold.*; 014import votorola.g.lang.*; 015import votorola.g.web.gwt.*; 016import votorola.g.web.gwt.event.*; 017import votorola.s.gwt.stage.*; 018import votorola.s.gwt.stage.light.*; 019 020import static org.vectomatic.dom.svg.OMSVGLength.SVG_LENGTHTYPE_EMS; 021import static org.vectomatic.dom.svg.OMSVGLength.SVG_LENGTHTYPE_PERCENTAGE; 022 023 024/** A staging control and view of an actor in an otherwise disabled vote track. It is 025 * rendered as a filled circle. Clicking sets or unsets the actor as the {@linkplain 026 * Stage#getActorName() staged actor}. The view appears whenever the track is invisible 027 * and an {@linkplain Stage#getActorName() actor} other than the {@linkplain 028 * Stage#getDefaultActorName() default} is staged. It remains visible till the track is 029 * shown, thus enabling the actor to be staged and unstaged, which otherwise might be 030 * difficult. 031 */ 032public final class Podium extends OMSVGCircleElement 033{ 034 035 036 /** Constructs a Podium. 037 */ 038 Podium( PeerBoardV _boardV ) 039 { 040 boardV = _boardV; 041 042 // View. 043 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 044 addClassNameBaseVal( "Podium" ); 045 setVisible( false ); 046 getR().getBaseVal().newValueSpecifiedUnits( SVG_LENGTHTYPE_EMS, RADIUS ); 047 getCx().getBaseVal().newValueSpecifiedUnits( SVG_LENGTHTYPE_PERCENTAGE, 75 ); // rightward 048 getCy().getBaseVal().newValueSpecifiedUnits( SVG_LENGTHTYPE_EMS, RADIUS ); // abut top 049 setAttribute( "transform", "translate(0 " + NodeVPainter.MARGIN_TOP + ")" ); 050 // per TRANS_ATT, respect margin 051 052 // Controllers. 053 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 054 new Clicker(); 055 painter = new Painter(); 056 sensor = new Sensor(); 057 new Timer() 058 { 059 public void run() 060 { 061 if( !boardV.spool().isUnwinding() ) new Modeller( painter ); 062 } 063 }.schedule( 3000/*ms*/ ); /* delay its appearance on init, which is normally only 064 till the vote track populates from the server */ 065 } 066 067 068 069//// P r i v a t e /////////////////////////////////////////////////////////////////////// 070 071 072 private final PeerBoardV boardV; 073 074 075 076 private String carriedActor; 077 078 079 080 /** Answers whether this podium is nominally visible. Returns true if the visibility 081 * style is "visible", false otherwise. 082 * 083 * @see #setVisible(boolean) 084 */ 085 private boolean isVisible() { return "visible".equals( getStyle().getVisibility() ); } 086 087 088 /** Sets the visibility of this podium, and also toggles the opacity. 089 * 090 * @see #isVisible() 091 */ 092 private void setVisible( final boolean toBe ) 093 { 094 if( toBe == isVisible() ) return; 095 096 final Style style = getStyle(); 097 if( toBe ) 098 { 099 style.setVisibility( Style.Visibility.VISIBLE ); 100 style.setOpacity( 1 ); /* for sake of delayed and smoothened transition in 101 track.css. Note that opacity transitions fail if visibility is 102 controlled by 'display' property */ 103 } 104 else 105 { 106 style.clearVisibility(); 107 style.clearOpacity(); 108 } 109 } 110 111 112 113 private final Painter painter; 114 115 116 117 private static final float RADIUS = 0.5f; // em 118 119 120 121 private final Sensor sensor; 122 123 124 125 // ==================================================================================== 126 127 128 private final class Clicker implements ClickHandler 129 { 130 131 Clicker() { addClickHandler( Clicker.this ); } // no need to unregister, registry does not outlive the handler 132 133 134 public void onClick( ClickEvent _e ) 135 { 136 if( carriedActor == null ) return; 137 138 String name = carriedActor; 139 if( name.equals( Stage.i().getActorName() )) name = null; // toggle off 140 Stage.setActorName( name ); 141 } 142 143 } 144 145 146 147 // ==================================================================================== 148 149 150 private final class Modeller implements PropertyChangeHandler 151 { 152 153 private Modeller( Painter _painter ) 154 { 155 assert _painter != null; // fail fast 156 boardV.spool().add( new Hold() 157 { 158 final HandlerRegistration hR = GWTX.i().bus().addHandlerToSource( 159 PropertyChange.TYPE, /*source*/boardV, Modeller.this ); 160 public void release() { hR.removeHandler(); } 161 }); 162 boardV.spool().add( new Hold() 163 { 164 final HandlerRegistration hR = GWTX.i().bus().addHandlerToSource( 165 PropertyChange.TYPE, /*source*/Stage.i(), Modeller.this ); 166 public void release() { hR.removeHandler(); } 167 }); 168 remodel(); // init 169 } 170 171 172 public void onPropertyChange( final PropertyChange e ) 173 { 174 if( boardV.spool().isUnwinding() ) return; 175 176 final Object source = e.getSource(); 177 final String name = e.propertyName(); 178 if( source == boardV ) 179 { 180 if( name.equals("visible") && !boardV.isVisible() ) // could become visible now 181 { 182 carriedActor = null; // revert to pristine state 183 remodel(); 184 if( carriedActor == null ) remodel_onChange(); // as remodel() did not 185 } 186 } 187 else 188 { 189 assert source.equals( Stage.i() ); 190 if( name.equals("actorName") || name.equals("defaultActorName") ) remodel(); 191 } 192 } 193 194 195 private void remodel() 196 { 197 final Stage stage = Stage.i(); 198 final String actorName = stage.getActorName(); 199 if( actorName == null || actorName.equals(stage.getDefaultActorName()) ) 200 { 201 removeClassNameBaseVal( "highlit" ); 202 return; 203 } 204 205 addClassNameBaseVal( "highlit" ); // if not already added 206 if( actorName.equals(carriedActor) ) return; 207 208 carriedActor = actorName; 209 remodel_onChange(); 210 } 211 212 213 private void remodel_onChange() 214 { 215 sensor.changed(); 216 painter.onLocalChange(); 217 } 218 219 } 220 221 222 223 // ==================================================================================== 224 225 226 private final class Painter implements PropertyChangeHandler 227 { 228 229 private Painter() 230 { 231 boardV.spool().add( new Hold() 232 { 233 final HandlerRegistration hR = GWTX.i().bus().addHandlerToSource( 234 PropertyChange.TYPE, /*source*/boardV, Painter.this ); 235 public void release() { hR.removeHandler(); } 236 }); 237 repaint(); // init 238 } 239 240 241 private void onLocalChange() { repaint(); } // called from remodel() 242 243 244 public void onPropertyChange( final PropertyChange e ) 245 { 246 if( boardV.spool().isUnwinding() ) return; 247 248 if( e.propertyName().equals( "visible" )) repaint(); 249 } 250 251 252 private void repaint() { setVisible( carriedActor != null && !boardV.isVisible() ); } 253 254 } 255 256 257 258 // ==================================================================================== 259 260 261 private final class Sensor extends Sensor1 262 implements MouseOutHandler, MouseOverHandler, NodalSensor 263 { 264 265 private Sensor() 266 { 267 addMouseOutHandler( Sensor.this ); // no need to unregister, registry does not outlive the handler 268 addMouseOverHandler( Sensor.this ); // " 269 Stage.i().lightBank().addSensor( Sensor.this ); 270 boardV.spool().add( new Hold() 271 { 272 public void release() { Stage.i().lightBank().removeSensor( Sensor.this ); } 273 }); 274 } 275 276 277 // - M o u s e - H a n d l e r -------------------------------------------------- 278 279 280 public void onMouseOut( MouseOutEvent _e ) { out(); } 281 282 283 public void onMouseOver( MouseOverEvent _e ) { over(); } 284 285 286 // - N o d a l - S e n s o r ------------------------------------------------------ 287 288 289 public NodeV.Box box() { return boardV; } 290 291 292 public String displayTitle() { return null; } // unknown 293 294 295 // - P o s i t i o n - S e n s o r ------------------------------------------------ 296 297 298 public String personName() { return carriedActor; } 299 300 301 public String pollName() { return null; } // unknown and not needed 302 303 } 304 305 306}