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