/*
* TriStateCheckboxCell.java
*
* Copyright (C) 2009-12 by RStudio, Inc.
*
* Unless you have received this program directly from RStudio pursuant
* to the terms of a commercial license agreement with RStudio, then
* this program is licensed to you under the terms of version 3 of the
* GNU Affero General Public License. This program is distributed WITHOUT
* ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
* AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
*
*/
package org.rstudio.core.client.cellview;
import com.google.gwt.cell.client.Cell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.view.client.SelectionModel;
import java.util.HashSet;
import java.util.Set;
import org.rstudio.core.client.resources.ImageResource2x;
/**
* WARNING: If you use this, take a look at ChangelistTable.NotEditingTextCell,
* which was necessary to get the table out of a state where cellIsEditing is
* stuck on true due to this cell getting mouseover but not mouseout.
*/
public class TriStateCheckboxCell<TKey> implements Cell<Boolean>
{
interface Resources extends ClientBundle
{
@Source("checkboxIndeterminate_2x.png")
ImageResource checkboxIndeterminate2x();
@Source("checkboxOn_2x.png")
ImageResource checkboxOn2x();
@Source("checkboxOff_2x.png")
ImageResource checkboxOff2x();
}
public TriStateCheckboxCell(SelectionModel<TKey> selectionModel)
{
selectionModel_ = selectionModel;
consumedEvents_ = new HashSet<String>();
consumedEvents_.add("click");
consumedEvents_.add("keydown");
consumedEvents_.add("mouseover");
consumedEvents_.add("mouseout");
}
@Override
public boolean dependsOnSelection()
{
return false;
}
@Override
public Set<String> getConsumedEvents()
{
return consumedEvents_;
}
@Override
public boolean handlesSelection()
{
return false;
}
@Override
@SuppressWarnings("unchecked")
public boolean isEditing(Context context, Element parent, Boolean value)
{
// We aren't actually editing here, of course. All we're trying to do
// is prevent selection from changing, if the user is clicking on the
// checkbox of a cell that's in a selected row.
return mouseInCheckbox_ &&
selectionModel_.isSelected((TKey) context.getKey());
}
@Override
public void onBrowserEvent(Context context,
Element parent,
Boolean value,
NativeEvent event,
ValueUpdater<Boolean> booleanValueUpdater)
{
if (Element.is(event.getEventTarget()) &&
Element.as(event.getEventTarget()).getTagName().equalsIgnoreCase("img"))
{
if ("click".equals(event.getType()))
{
booleanValueUpdater.update(value == null ? true : !value);
}
else if ("mouseover".equals(event.getType()))
{
mouseInCheckbox_ = true;
}
else if ("mouseout".equals(event.getType()))
{
// WARNING!!!! Sometimes we get mouseover without a corresponding
// mouseout!! See comment at top of this class!
mouseInCheckbox_ = false;
}
}
}
@Override
public void render(Context context, Boolean value, SafeHtmlBuilder sb)
{
ImageResource2x img;
if (value == null)
img = new ImageResource2x(RES.checkboxIndeterminate2x());
else if (value)
img = new ImageResource2x(RES.checkboxOn2x());
else
img = new ImageResource2x(RES.checkboxOff2x());
sb.append(img.getSafeHtml());
}
@Override
public boolean resetFocus(Context context, Element parent, Boolean value)
{
return false;
}
@Override
public void setValue(Context context, Element parent, Boolean value)
{
SafeHtmlBuilder builder = new SafeHtmlBuilder();
render(context, value, builder);
parent.setInnerHTML(builder.toSafeHtml().asString());
}
private final HashSet<String> consumedEvents_;
private boolean mouseInCheckbox_;
private final SelectionModel<TKey> selectionModel_;
private static final Resources RES = GWT.create(Resources.class);
}