001package votorola.s.gwt.stage.talk; // Copyright 2012, Christian Weilbach.  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 java.util.Date;
004import java.util.Iterator;
005
006import votorola.a.web.gwt.App;
007import votorola.g.hold.Hold;
008import votorola.g.hold.Spool;
009import votorola.g.hold.Spool1;
010import votorola.g.lang.Warning;
011import votorola.g.web.gwt.GWTX;
012import votorola.g.web.gwt.event.Change;
013import votorola.g.web.gwt.event.ChangeHandler;
014import votorola.g.web.gwt.event.PropertyChange;
015import votorola.g.web.gwt.event.PropertyChangeHandler;
016import votorola.s.gwt.stage.Stage;
017import votorola.s.gwt.stage.StageV;
018
019import com.google.gwt.core.client.GWT;
020import com.google.gwt.core.client.JsArray;
021import com.google.gwt.uibinder.client.UiBinder;
022import com.google.gwt.uibinder.client.UiFactory;
023import com.google.gwt.uibinder.client.UiField;
024import com.google.gwt.user.client.ui.Composite;
025import com.google.gwt.user.client.ui.HTMLPanel;
026import com.google.gwt.user.client.ui.Image;
027import com.google.gwt.user.client.ui.Widget;
028import com.google.web.bindery.event.shared.HandlerRegistration;
029
030/**
031 * A view for difference messages. Views are on a chain from right to left,
032 * clipped on the right side of C. When the view is filled with data, newest
033 * data is on the right between 0 and F. UPDATE_BUTTON is the inline button to
034 * kick the harvesters through {@linkplain votorola.s.wap.KickWAP}.
035 * 
036 * <pre>
037 * VIEW_COUNT... clipped ... C ... hidden views ... F ... filled ... 1 0 UPDATE_BUTTON
038 * </pre>
039 * 
040 */
041public final class TalkTrackV extends Composite {
042    
043
044    
045    private static TalkTrackV instance;
046    {
047        if (instance != null)
048            throw new IllegalStateException(); // grep "instance" for reason
049
050        instance = TalkTrackV.this;
051    }
052    
053    
054
055    /**
056     * Returns the {@linkplain StageV staged instance} of PolltrackV, or null if
057     * none is staged.
058     */
059    public static TalkTrackV i(final StageV stageV) {
060        final Iterator<Widget> ww = stageV.iterator();
061        while (ww.hasNext()) {
062            final Widget w = ww.next();
063            if (w instanceof TalkTrackV)
064                return (TalkTrackV) w;
065        }
066
067        return null;
068    }
069
070
071
072    // - M o d e l ------------------------------------------------------------
073    
074    /**
075     * The number of views created by the view on startup and used to fill in
076     * data.
077     */
078    public final static int VIEW_COUNT = 200;
079    
080    
081    
082    private final TalkTrack track;
083
084    
085    
086    private final Spool spool = new Spool1();
087    
088    
089    
090    private final ChangeHandler trackHandler = new ChangeHandler() 
091    {
092        @Override
093        public void onChange(Change e) { stageUpdate(); }
094        
095    };
096
097    
098    
099    private final PropertyChangeHandler stageHandler = new PropertyChangeHandler() 
100    {
101
102        @Override
103        public void onPropertyChange(PropertyChange e) 
104        {
105            if (e.propertyName().equals("actorName")) { updateButton.reset(); }
106            
107        }
108
109    };
110
111    // - U i ------------------------------------------------------------
112
113    
114    private final TalkV[] talkVs = new TalkV[VIEW_COUNT];
115    
116    
117    
118    private final String historySeparatorUrl = App.getServletContextLocation() + "/stage/talk/historySeparator.png";
119    
120    
121    
122    private final class HistorySeparator extends Image 
123    {
124        HistorySeparator() 
125        {
126            super(historySeparatorUrl);
127            addStyleName("HistorySeparator");
128        }
129    };
130    
131    
132    
133    private final HistorySeparator[] historySeparators = new HistorySeparator[VIEW_COUNT+6];
134    
135    
136    
137    @Warning("non-API")
138    interface UiBinderI extends UiBinder<HTMLPanel, TalkTrackV> { }
139
140    {
141        final UiBinderI uiBinder = GWT.create(UiBinderI.class);
142        initWidget(uiBinder.createAndBindUi(TalkTrackV.this));
143    }
144
145    
146    
147    @UiField
148    @Warning("non-API")
149    HTMLPanel panel;
150
151    
152    
153    @UiField
154    @Warning("non-API")
155    UpdateButton updateButton;
156
157    
158    
159    @UiFactory 
160    @Warning("non-API") 
161    HeadsUpDisplay newHeadsUpDisplay() { return  new HeadsUpDisplay( TalkTrackV.this ); }
162    
163    
164    
165    @UiFactory
166    UpdateButton makeUpdateButton() {
167        return new UpdateButton(this);
168    }
169    
170    /**
171     * Constructs a TalkTrackV.
172     * 
173     */
174    public TalkTrackV(final TalkTrack _track, final StageV _stageV) {
175        track = _track;
176
177        spool.add(new Hold() {
178            final HandlerRegistration hR = GWTX.i().bus()
179                    .addHandlerToSource(Change.TYPE, /* source */
180                    _track, trackHandler);
181
182            public void release() {
183                hR.removeHandler();
184            }
185        });
186
187        spool.add(new Hold() {
188            final HandlerRegistration hR = GWTX.i().bus()
189                    .addHandlerToSource(PropertyChange.TYPE, /* source */
190                    Stage.i(), stageHandler);
191
192            public void release() {
193                hR.removeHandler();
194            }
195        });
196        
197        // allow two markers at the beginning if discussion is old
198        historySeparators[0] = new HistorySeparator();
199        historySeparators[0].setVisible(false);
200        panel.add(historySeparators[0]);
201        
202        historySeparators[1] = new HistorySeparator();
203        historySeparators[1].setVisible(false);
204        panel.add(historySeparators[1]);
205        
206        historySeparators[2] = new HistorySeparator();
207        historySeparators[2].setVisible(false);
208        panel.add(historySeparators[2]);
209
210        // direction is rtl, so 0 is rightest.
211        for (int i = 0; i < VIEW_COUNT; i++) 
212        {
213            historySeparators[i+3] = new HistorySeparator();
214            historySeparators[i+3].setVisible(false);
215            panel.add(historySeparators[i+3]);
216            
217            talkVs[i] = new TalkV(spool);
218            panel.add(talkVs[i]);
219        }
220        
221        // allow two markers at the end if discussion is young
222        historySeparators[VIEW_COUNT] = new HistorySeparator();
223        historySeparators[VIEW_COUNT].setVisible(false);
224        panel.add(historySeparators[VIEW_COUNT]);
225        
226        historySeparators[VIEW_COUNT+1] = new HistorySeparator();
227        historySeparators[VIEW_COUNT+1].setVisible(false);
228        panel.add(historySeparators[VIEW_COUNT+1]);
229        
230        historySeparators[VIEW_COUNT+2] = new HistorySeparator();
231        historySeparators[VIEW_COUNT+2].setVisible(false);
232        panel.add(historySeparators[VIEW_COUNT+2]);
233    }
234
235    
236    
237    protected @Override
238    void onUnload() {
239        spool.unwind();
240        super.onUnload();
241    }
242
243    
244    
245    private void stageUpdate() 
246    {
247        JsArray<DiffMessageJS> dMsgs = track.messages();
248        if (dMsgs == null) {
249            return;
250        }
251        final long day = 1000 * 60 * 60 * 24;
252        final long week = day * 7;
253        final long month = day * 30;
254        final long year = day * 365;
255        
256
257        // manage history separators
258        boolean changed = false;
259        final long now = new Date().getTime();
260        long prev = now;
261        int separatorsLeft = 3;
262        for (int i = 0; i < VIEW_COUNT; i++) 
263        {
264            if (i >= dMsgs.length()) 
265            {
266                talkVs[i].setMsg(null);
267                historySeparators[i].setVisible(false);
268                
269            } else 
270            {
271                final DiffMessageJS dMsg = dMsgs.get(i);
272                final long current = (long)dMsg.sentDate();
273                
274                if (i==0) {
275                    // show separators at end if discussion is young
276                    if(current < now-week) 
277                    {
278                        historySeparators[0].setVisible(true);
279                        separatorsLeft--;
280                    } else { historySeparators[0].setVisible(false); }
281
282                    if(current < now-month) 
283                    {
284                        historySeparators[1].setVisible(true);
285                        separatorsLeft--;
286                    } else { historySeparators[1].setVisible(false); }
287
288                    if(current < now-year) 
289                    {
290                        historySeparators[2].setVisible(true);
291                        separatorsLeft--;
292                    } else { historySeparators[2].setVisible(false); }
293                    
294                }
295                
296                
297                if (!DiffMessageJS.EQUATOR.equals(talkVs[i].getMsg(),
298                        dMsgs.get(i))) 
299                {
300                    talkVs[i].setMsg(dMsg);
301                    changed = true;
302                }
303                
304                
305                if( prev > now-week && current < now-week && i!=0 // otherwise it is set in the block above
306                    || prev > now-month && current < now-month && i!=0 
307                    || prev > now-year && current < now-year && i!=0 )
308                {
309                    historySeparators[i+3].setVisible(true);
310                    separatorsLeft--;
311                } else { historySeparators[i+3].setVisible(false); }
312                prev = current;
313                
314            }
315            
316        }
317        
318        // show separators at end if discussion is young
319        if(prev < now-week && separatorsLeft > 0) { historySeparators[VIEW_COUNT].setVisible(true); }
320        else { historySeparators[VIEW_COUNT].setVisible(false); }
321
322        
323        if(prev < now-month && separatorsLeft > 0) { historySeparators[VIEW_COUNT].setVisible(true); }
324        else { historySeparators[VIEW_COUNT].setVisible(false); }
325
326        
327        if(prev < now-year && separatorsLeft > 0) { historySeparators[VIEW_COUNT+1].setVisible(true); }
328        else { historySeparators[VIEW_COUNT+1].setVisible(false); }
329        
330        
331        updateButton.update(changed);
332        
333    }
334    
335}