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}