001package com.github.gwtbootstrap.client.ui;
002
003import java.util.Set;
004
005import com.github.gwtbootstrap.client.ui.base.HasPlacement;
006import com.github.gwtbootstrap.client.ui.base.HasShowDelay;
007import com.github.gwtbootstrap.client.ui.base.HasTrigger;
008import com.github.gwtbootstrap.client.ui.base.IsAnimated;
009import com.github.gwtbootstrap.client.ui.constants.Placement;
010import com.github.gwtbootstrap.client.ui.constants.Trigger;
011import com.google.gwt.cell.client.Cell;
012import com.google.gwt.cell.client.ValueUpdater;
013import com.google.gwt.core.client.GWT;
014import com.google.gwt.core.client.Scheduler;
015import com.google.gwt.core.client.Scheduler.ScheduledCommand;
016import com.google.gwt.dom.client.Element;
017import com.google.gwt.dom.client.NativeEvent;
018import com.google.gwt.safehtml.client.SafeHtmlTemplates;
019import com.google.gwt.safehtml.client.SafeHtmlTemplates.Template;
020import com.google.gwt.safehtml.shared.SafeHtml;
021import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
022import com.google.gwt.user.client.DOM;
023import com.google.gwt.user.client.ui.HasText;
024
025/**
026 * Cell decorator as a Tooltip.
027 * @author ohashi keisuke
028 *
029 * @param <C> Decorated Cell Parameter
030 */
031public class TooltipCellDecorator<C> implements Cell<C> ,IsAnimated, HasTrigger, HasPlacement, HasText, HasShowDelay{
032
033    /**
034     * Whether the widget is animated or not.
035     */
036    protected boolean animated = true;
037
038    /**
039     * The placement of the widget relative to its trigger element.
040     */
041    protected Placement placement = Placement.TOP;
042
043    /**
044     * The action that triggers the widget.
045     */
046    protected Trigger trigger = Trigger.HOVER;
047
048    /**
049     * The delay until the widget is shown.
050     */
051    protected int showDelayInMilliseconds = 0;
052
053    /**
054     * The delay until the widget is hidden.
055     */
056    protected int hideDelayInMilliseconds = 0;
057
058    
059    interface Template extends SafeHtmlTemplates {
060        @Template("<span clss='gb-tooltip-cell' id='{0}'>{1}</span>")
061        SafeHtml span(String id , SafeHtml content);
062        
063    }
064    
065    private static Template template = GWT.create(Template.class);
066
067    private final Cell<C> cell;
068
069    private String tooltip;
070    
071    /**
072     * Create Decorator cell
073     * @param cell decorated cell
074     */
075    public TooltipCellDecorator(Cell<C> cell) {
076        this.cell = cell;
077    }
078
079    /**
080     * {@inheritDoc}
081     */
082    @Override
083    public boolean dependsOnSelection() {
084        return cell.dependsOnSelection();
085    }
086
087    /**
088     * {@inheritDoc}
089     */
090    @Override
091    public Set<String> getConsumedEvents() {
092        return cell.getConsumedEvents();
093    }
094
095    /**
096     * {@inheritDoc}
097     */
098    @Override
099    public boolean handlesSelection() {
100        return cell.handlesSelection();
101    }
102
103    /**
104     * {@inheritDoc}
105     */
106    @Override
107    public boolean isEditing(com.google.gwt.cell.client.Cell.Context context,
108            Element parent, C value) {
109        return cell.isEditing(context, getCellParent(parent), value);
110    }
111
112    /**
113     * {@inheritDoc}
114     */
115    @Override
116    public void onBrowserEvent(com.google.gwt.cell.client.Cell.Context context,
117            Element parent, C value, NativeEvent event,
118            ValueUpdater<C> valueUpdater) {
119        cell.onBrowserEvent(context, getCellParent(parent), value, event, valueUpdater);
120    }
121
122    /**
123     * {@inheritDoc}
124     */
125    @Override
126    public void render(final com.google.gwt.cell.client.Cell.Context context,
127            final C value, SafeHtmlBuilder sb) {
128        
129        SafeHtmlBuilder cellBuilder = new SafeHtmlBuilder();
130
131        cell.render(context, value, cellBuilder);
132
133        final String id = DOM.createUniqueId();
134        sb.append(template.span(id, cellBuilder.toSafeHtml()));
135        
136        Scheduler.get().scheduleDeferred(new ScheduledCommand() {
137            
138            @Override
139            public void execute() {
140                
141                Tooltip.configure("#" + id + "> :first-child",
142                    getTooltipText(context, value),
143                    getAnimation(context, value),
144                    getPlacement(context, value).get(),
145                    getTrigger(context, value).get(),
146                    getShowDelay(context, value),
147                    getHideDelay(context, value)
148                );
149                
150           };
151           
152        });
153    }
154    @Override
155    public boolean resetFocus(com.google.gwt.cell.client.Cell.Context context,
156            Element parent, C value) {
157        return cell.resetFocus(context, getCellParent(parent), value);
158    }
159
160    @Override
161    public void setValue(com.google.gwt.cell.client.Cell.Context context,
162            Element parent, C value) {
163        cell.setValue(context, getCellParent(parent), value);
164    }
165
166    /**
167     * {@inheritDoc}
168     */
169    public void setAnimation(boolean animated) {
170        this.animated = animated;
171    }
172
173    /**
174     * {@inheritDoc}
175     */
176    public boolean getAnimation() {
177        return animated;
178    }
179    
180    protected boolean getAnimation(Context context,C value) {
181        return getAnimation();
182    }
183
184    /**
185     * {@inheritDoc} Relative to its trigger element.
186     */
187    public void setPlacement(Placement placement) {
188        
189        assert placement != null : "should not be null";
190        
191        this.placement = placement;
192    }
193
194    /**
195     * {@inheritDoc}
196     */
197    public Placement getPlacement() {
198        return placement;
199    }
200    
201    protected Placement getPlacement(Context context, C value) {
202        return getPlacement();
203    }
204
205    /**
206     * {@inheritDoc}
207     */
208    public void setTrigger(Trigger trigger) {
209        assert trigger != null : "should not be null";
210        this.trigger = trigger;
211    }
212
213    /**
214     * {@inheritDoc}
215     */
216    public Trigger getTrigger() {
217        return trigger;
218    }
219    
220    protected Trigger getTrigger(Context context,C value) {
221        return getTrigger();
222    }
223
224    /**
225     * {@inheritDoc}
226     */
227    public void setShowDelay(int delayInMilliseconds) {
228        showDelayInMilliseconds = delayInMilliseconds;
229    }
230
231    /**
232     * {@inheritDoc}
233     */
234    public int getShowDelay() {
235        return showDelayInMilliseconds;
236    }
237    
238    protected int getShowDelay(Context context, C value) {
239        return getShowDelay();
240    }
241
242    /**
243     * {@inheritDoc}
244     */
245    public void setHideDelay(int delayInMilliseconds) {
246        hideDelayInMilliseconds = delayInMilliseconds;
247    }
248
249    /**
250     * {@inheritDoc}
251     */
252    public int getHideDelay() {
253        return hideDelayInMilliseconds;
254    }
255    
256    protected int getHideDelay(Context context, C value) {
257        return getHideDelay();
258    }
259
260    /**
261     * {@inheritDoc}
262     */
263    @Override
264    public String getText() {
265        return this.tooltip;
266    }
267
268    /**
269     * {@inheritDoc}
270     */
271    @Override
272    public void setText(String text) {
273        assert text != null : "should not be null";
274        this.tooltip = text;
275    }
276    
277    protected String getTooltipText(Context context, C value) {
278        return getText();
279    }
280
281    /**
282     * Get the parent element of the decorated cell.
283     * 
284     * @param parent the parent of this cell
285     * @return the decorated cell's parent
286     */
287    private Element getCellParent(Element parent) {
288      return parent.getFirstChildElement().cast();
289    }
290
291}