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.event.logical.shared.ResizeEvent;
004import com.google.gwt.event.logical.shared.ResizeHandler;
005import com.google.gwt.user.client.Window;
006import com.google.gwt.user.client.ui.*;
007import com.google.web.bindery.event.shared.HandlerRegistration;
008import votorola.g.hold.*;
009import votorola.g.web.gwt.*;
010import votorola.g.web.gwt.event.*;
011
012
013/** A device to calculate the dimensions and drawing parameters of node views.  This is
014  * complicated because the rendered size of the SVG element is unknown, its offset
015  * dimensions always being reported as zero (at least with Firefox 9).  As a workaround,
016  * the calculations are based instead on the rendered size of the HTML container.  For
017  * this to work correctly, it is crucial that the sizes of the two match.  SVG overflow
018  * <a href='https://bugzilla.mozilla.org/show_bug.cgi?id=378923' target='_top'>cannot be
019  * made visible</a> in Firefox; so if the SVG element happens to be smaller than the
020  * container, then its overflow will end up being truncated ("hidden").  Ideally the
021  * element should be set to 100% of the container size in <a
022  * href='../../../../../../../a/web/context/stage/vote/track.css' >vote/track.css</a>.
023  */
024abstract class NodeVPainter implements PropertyChangeHandler, ResizeHandler
025{
026
027
028    /** Partially creates a NodeVPainter for {@linkplain #init(Spool) init} to
029      * finish.
030      *
031      *     @see #container()
032      */
033    NodeVPainter( MajorV _container ) { container = _container; }
034
035
036
037    /** Completes the creation of this NodeVPainter and activates it.  Call once
038      * only, preferably after the {@linkplain #container() HTML container} is rendered.
039      *
040      *     @param _spool the spool for the release of associated holds.  When unwound it
041      *       releases the holds of the painter and thereby disables it.
042      */
043    final void init( Spool _spool )
044    {
045        spool = _spool;
046        GWTX.i().bus().addHandlerToSource( PropertyChange.TYPE, /*source*/container,
047          NodeVPainter.this ); // no need to unregister, registry does not outlive the handler
048     // svg.addResizeHandler( NodeVPainter.this );
049     /// no effect, as size of OMSVGSVGElement is stable, apparently related to logical size of drawing
050        spool.add( new Hold()
051        {
052            final HandlerRegistration hR = Window.addResizeHandler( NodeVPainter.this );
053            public void release() { hR.removeHandler(); }
054        });
055    }
056
057
058
059   // ------------------------------------------------------------------------------------
060
061
062    /** The HTML container whose rendered size determines the dimensions of the node
063      * views.
064      */
065    final UIObject container() { return container; }
066
067
068        private final UIObject container;
069
070
071
072    /** The margin to respect below the node view in pixels.
073      */
074    static final int MARGIN_BOTTOM = 3/*apparent*/ - NodeV.STROKE_WIDTH / 2;
075
076
077
078    /** The margin to respect above the node view in pixels.
079      */
080    static final int MARGIN_TOP = 2/*apparent*/ - NodeV.STROKE_WIDTH / 2;
081
082
083
084    /** Forces a recalibration.
085      */
086    final void repaint() { repaint( container.getOffsetWidth() ); }
087
088
089
090    /** Effects a recalibration and redraws the node views accordingly.
091      *
092      *     @param width the rendered width of the {@linkplain #container() HTML
093      *     container}.
094      *     @see NodeV#repaint(float,float,float,int,float)
095      */
096    abstract void repaint( int width, float protrusion, int y, float halfThickness );
097
098
099
100   // - P r o p e r t y - C h a n g e - H a n d l e r ------------------------------------
101
102
103    public void onPropertyChange( final PropertyChange e )
104    {
105        if( spool.isUnwinding() ) return;
106
107        if( e.getSource() == container && e.propertyName().equals("visible")
108          && container.isVisible() ) // becomes visible
109        {
110            if( widthCalibrated == -1 ) repaint(); // width changed meantime, so repaint
111        }
112    }
113
114
115
116   // - R e s i z e - H a n d l e r --------------------------------------------------
117
118
119    public final void onResize( ResizeEvent _e )
120    {
121        if( spool.isUnwinding() ) return;
122
123        final int width = container.getOffsetWidth();
124        if( width == widthCalibrated ) return;
125
126        repaint( width );
127    }
128
129
130
131//// P r i v a t e ///////////////////////////////////////////////////////////////////////
132
133
134    private void repaint( final int width )
135    {
136        if( !container.isVisible() )
137        {
138            widthCalibrated = -1; // skipping calibration, therefore no width is calibrated
139            return;
140        }
141
142        widthCalibrated = width;
143        final int height = container.getOffsetHeight();
144        final float halfThickness = (height - MARGIN_TOP - MARGIN_BOTTOM) / 2f;
145        final float protrusion = halfThickness;
146        repaint( width, protrusion, /*y*/MARGIN_TOP, halfThickness );
147    }
148
149
150
151    private Spool spool; // final after init
152
153
154
155    private int widthCalibrated = -1; /* guard against redundant resize events, assumes
156      that window resize cannot affect container height, only width */
157
158
159}