package 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. import com.google.gwt.dom.client.*; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.MouseOutEvent; import com.google.gwt.event.dom.client.MouseOutHandler; import com.google.gwt.event.dom.client.MouseOverEvent; import com.google.gwt.event.dom.client.MouseOverHandler; import com.google.web.bindery.event.shared.HandlerRegistration; import com.google.gwt.user.client.Timer; import org.vectomatic.dom.svg.*; import votorola.g.hold.*; import votorola.g.lang.*; import votorola.g.web.gwt.*; import votorola.g.web.gwt.event.*; import votorola.s.gwt.stage.*; import votorola.s.gwt.stage.light.*; import static org.vectomatic.dom.svg.OMSVGLength.SVG_LENGTHTYPE_EMS; import static org.vectomatic.dom.svg.OMSVGLength.SVG_LENGTHTYPE_PERCENTAGE; /** A staging control and view of an actor in an otherwise disabled vote track. It is * rendered as a filled circle. Clicking sets or unsets the actor as the {@linkplain * Stage#getActorName() staged actor}. The view appears whenever the track is invisible * and an {@linkplain Stage#getActorName() actor} other than the {@linkplain * Stage#getDefaultActorName() default} is staged. It remains visible till the track is * shown, thus enabling the actor to be staged and unstaged, which otherwise might be * difficult. */ public final class Podium extends OMSVGCircleElement { /** Constructs a Podium. */ Podium( PeerBoardV _boardV ) { boardV = _boardV; // View. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - addClassNameBaseVal( "Podium" ); setVisible( false ); getR().getBaseVal().newValueSpecifiedUnits( SVG_LENGTHTYPE_EMS, RADIUS ); getCx().getBaseVal().newValueSpecifiedUnits( SVG_LENGTHTYPE_PERCENTAGE, 75 ); // rightward getCy().getBaseVal().newValueSpecifiedUnits( SVG_LENGTHTYPE_EMS, RADIUS ); // abut top setAttribute( "transform", "translate(0 " + NodeVPainter.MARGIN_TOP + ")" ); // per TRANS_ATT, respect margin // Controllers. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - new Clicker(); painter = new Painter(); sensor = new Sensor(); new Timer() { public void run() { if( !boardV.spool().isUnwinding() ) new Modeller( painter ); } }.schedule( 3000/*ms*/ ); /* delay its appearance on init, which is normally only till the vote track populates from the server */ } //// P r i v a t e /////////////////////////////////////////////////////////////////////// private final PeerBoardV boardV; private String carriedActor; /** Answers whether this podium is nominally visible. Returns true if the visibility * style is "visible", false otherwise. * * @see #setVisible(boolean) */ private boolean isVisible() { return "visible".equals( getStyle().getVisibility() ); } /** Sets the visibility of this podium, and also toggles the opacity. * * @see #isVisible() */ private void setVisible( final boolean toBe ) { if( toBe == isVisible() ) return; final Style style = getStyle(); if( toBe ) { style.setVisibility( Style.Visibility.VISIBLE ); style.setOpacity( 1 ); /* for sake of delayed and smoothened transition in track.css. Note that opacity transitions fail if visibility is controlled by 'display' property */ } else { style.clearVisibility(); style.clearOpacity(); } } private final Painter painter; private static final float RADIUS = 0.5f; // em private final Sensor sensor; // ==================================================================================== private final class Clicker implements ClickHandler { Clicker() { addClickHandler( Clicker.this ); } // no need to unregister, registry does not outlive the handler public void onClick( ClickEvent _e ) { if( carriedActor == null ) return; String name = carriedActor; if( name.equals( Stage.i().getActorName() )) name = null; // toggle off Stage.setActorName( name ); } } // ==================================================================================== private final class Modeller implements PropertyChangeHandler { private Modeller( Painter _painter ) { assert _painter != null; // fail fast boardV.spool().add( new Hold() { final HandlerRegistration hR = GWTX.i().bus().addHandlerToSource( PropertyChange.TYPE, /*source*/boardV, Modeller.this ); public void release() { hR.removeHandler(); } }); boardV.spool().add( new Hold() { final HandlerRegistration hR = GWTX.i().bus().addHandlerToSource( PropertyChange.TYPE, /*source*/Stage.i(), Modeller.this ); public void release() { hR.removeHandler(); } }); remodel(); // init } public void onPropertyChange( final PropertyChange e ) { if( boardV.spool().isUnwinding() ) return; final Object source = e.getSource(); final String name = e.propertyName(); if( source == boardV ) { if( name.equals("visible") && !boardV.isVisible() ) // could become visible now { carriedActor = null; // revert to pristine state remodel(); if( carriedActor == null ) remodel_onChange(); // as remodel() did not } } else { assert source.equals( Stage.i() ); if( name.equals("actorName") || name.equals("defaultActorName") ) remodel(); } } private void remodel() { final Stage stage = Stage.i(); final String actorName = stage.getActorName(); if( actorName == null || actorName.equals(stage.getDefaultActorName()) ) { removeClassNameBaseVal( "highlit" ); return; } addClassNameBaseVal( "highlit" ); // if not already added if( actorName.equals(carriedActor) ) return; carriedActor = actorName; remodel_onChange(); } private void remodel_onChange() { sensor.changed(); painter.onLocalChange(); } } // ==================================================================================== private final class Painter implements PropertyChangeHandler { private Painter() { boardV.spool().add( new Hold() { final HandlerRegistration hR = GWTX.i().bus().addHandlerToSource( PropertyChange.TYPE, /*source*/boardV, Painter.this ); public void release() { hR.removeHandler(); } }); repaint(); // init } private void onLocalChange() { repaint(); } // called from remodel() public void onPropertyChange( final PropertyChange e ) { if( boardV.spool().isUnwinding() ) return; if( e.propertyName().equals( "visible" )) repaint(); } private void repaint() { setVisible( carriedActor != null && !boardV.isVisible() ); } } // ==================================================================================== private final class Sensor extends Sensor1 implements MouseOutHandler, MouseOverHandler, NodalSensor { private Sensor() { addMouseOutHandler( Sensor.this ); // no need to unregister, registry does not outlive the handler addMouseOverHandler( Sensor.this ); // " Stage.i().lightBank().addSensor( Sensor.this ); boardV.spool().add( new Hold() { public void release() { Stage.i().lightBank().removeSensor( Sensor.this ); } }); } // - M o u s e - H a n d l e r -------------------------------------------------- public void onMouseOut( MouseOutEvent _e ) { out(); } public void onMouseOver( MouseOverEvent _e ) { over(); } // - N o d a l - S e n s o r ------------------------------------------------------ public NodeV.Box box() { return boardV; } public String displayTitle() { return null; } // unknown // - P o s i t i o n - S e n s o r ------------------------------------------------ public String personName() { return carriedActor; } public String pollName() { return null; } // unknown and not needed } }