gwtquery.plugins.selectable.client.Selectable.java Source code

Java tutorial

Introduction

Here is the source code for gwtquery.plugins.selectable.client.Selectable.java

Source

/*
 * Copyright 2010 The gwtquery plugins team.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package gwtquery.plugins.selectable.client;

import gwtquery.plugins.selectable.client.SelectableOptions.Tolerance;
import gwtquery.plugins.selectable.client.event.SelectedEvent;
import gwtquery.plugins.selectable.client.event.SelectingEvent;
import gwtquery.plugins.selectable.client.event.SelectionStartEvent;
import gwtquery.plugins.selectable.client.event.SelectionStopEvent;
import gwtquery.plugins.selectable.client.event.UnselectedEvent;
import gwtquery.plugins.selectable.client.event.UnselectingEvent;

import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.shared.HasHandlers;
import com.google.gwt.query.client.GQuery;
import com.google.gwt.query.client.plugins.MousePlugin;
import com.google.gwt.query.client.plugins.Plugin;
import com.google.gwt.query.client.plugins.events.GqEvent;

/**
 * Class implementing the JQuery-ui Selectable plugin.
 * 
 * @author Julien Dramaix (julien.dramaix@gmail.com, @jdramaix)
 * 
 */
public class Selectable extends MousePlugin {

    public class SelectableItem {

        private Element element;
        private GQuery $element;
        private int left;
        private int top;
        private int right;
        private int bottom;
        private boolean startSelected = false;
        boolean selected = false;
        boolean selecting = false;
        boolean unselecting = false;

        public SelectableItem(Element e) {
            element = e;
            $element = $(e);
        }

        public Element getElement() {
            return element;
        }

        public GQuery $element() {
            return $element;
        }

        public int getLeft() {
            return left;
        }

        public void setLeft(int left) {
            this.left = left;
        }

        public int getTop() {
            return top;
        }

        public void setTop(int top) {
            this.top = top;
        }

        public int getRight() {
            return right;
        }

        public void setRight(int right) {
            this.right = right;
        }

        public int getBottom() {
            return bottom;
        }

        public void setBottom(int bottom) {
            this.bottom = bottom;
        }

        public boolean isStartSelected() {
            return startSelected;
        }

        public void setStartSelected(boolean startSelected) {
            this.startSelected = startSelected;
        }

        public boolean isSelected() {
            return selected;
        }

        public void setSelected(boolean selected) {
            this.selected = selected;
        }

        public boolean isSelecting() {
            return selecting;
        }

        public void setSelecting(boolean selecting) {
            this.selecting = selecting;
        }

        public boolean isUnselecting() {
            return unselecting;
        }

        public void setUnselecting(boolean unselecting) {
            this.unselecting = unselecting;
        }

        public void unselecting() {
            $element.removeClass(CssClassNames.UI_SELECTED);
            $element.addClass(CssClassNames.UI_UNSELECTING);
            setSelected(false);
            setSelecting(false);
            setUnselecting(true);
        }

        public void selecting() {
            $element.removeClass(CssClassNames.UI_UNSELECTING);
            $element.addClass(CssClassNames.UI_SELECTING);
            setSelected(false);
            setSelecting(true);
            setUnselecting(false);
        }

        public void unselect() {
            $element.removeClass(CssClassNames.UI_UNSELECTING);
            setSelected(false);
            setUnselecting(false);
            setSelecting(false);
        }

        public void select() {
            $element.removeClass(CssClassNames.UI_SELECTING);
            $element.addClass(CssClassNames.UI_SELECTED);
            setSelected(true);
            setSelecting(false);
            setUnselecting(false);

        }

    }

    /**
     * Class handling the lasso helper used for the selection.
     * 
     */
    private static class Lasso {

        private Element element;
        private int startingLeft = -1;
        private int startingTop = -1;

        public Lasso() {
            element = Document.get().createDivElement();
            element.addClassName("ui-selectable-lasso");
        }

        public void show(int leftPosition, int topPosition, String appendToSelector) {
            this.startingLeft = leftPosition;
            this.startingTop = topPosition;
            // Todo use options ! not append directly to the body
            $(appendToSelector).append(element);
            element.getStyle().setZIndex(100);
            element.getStyle().setPosition(Position.ABSOLUTE);
            element.getStyle().setLeft(leftPosition, Unit.PX);
            element.getStyle().setTop(topPosition, Unit.PX);
            element.getStyle().setWidth(0, Unit.PX);
            element.getStyle().setHeight(0, Unit.PX);
        }

        public void draw(int x, int y) {

            int left = startingLeft;
            int top = startingTop;

            if (left > x) {
                int tmp = x;
                x = left;
                left = tmp;
            }

            if (top > y) {
                int tmp = y;
                y = top;
                top = tmp;
            }

            element.getStyle().setLeft(left, Unit.PX);
            element.getStyle().setTop(top, Unit.PX);
            element.getStyle().setWidth(x - left, Unit.PX);
            element.getStyle().setHeight(y - top, Unit.PX);

        }

        public void hide() {
            element.removeFromParent();
        }
    }

    /**
     * Interface containing all css classes used in this plug-in
     * 
     */
    private static interface CssClassNames {
        String UI_SELECTABLE = "ui-selectable";
        String UI_SELECTED = "ui-selected";
        String UI_SELECTING = "ui-selecting";
        String UI_UNSELECTING = "ui-unselecting";
        String UI_UNSELECTEE = "ui-unselectee";
    }

    public static Class<Selectable> Selectable = Selectable.class;
    private static String SELECTABLE_ITEM_KEY = "selectable-item";
    private static String SELECTEES_KEY = "selectees";
    private static final String SELECTABLE_KEY = "selectable";
    private static final String CURRENT_FILTER_KEY = "currentFilter";

    static {
        GQuery.registerPlugin(Selectable.class, new Plugin<Selectable>() {
            public Selectable init(GQuery gq) {
                Selectable s = new Selectable(gq);
                return s;
            }
        });
    }

    private Lasso lasso;

    // TODO maybe put that on selectable element
    private int[] opos;

    private SelectableOptions options;

    public Selectable(GQuery gq) {
        super(gq);

    }

    /**
     * Give the possibility of each children of each element to be selectable.
     * 
     * @return
     */
    public Selectable selectable() {
        return selectable(new SelectableOptions(), null);
    }

    /**
     * Give the possibility of each children of each element to be selectable.
     * 
     * @return
     */
    public Selectable selectable(SelectableOptions options) {
        return selectable(options, null);
    }

    /**
     * Give the possibility of each children of each element to be selectable.
     * 
     * @return
     */
    public Selectable selectable(HasHandlers eventBus) {
        return selectable(new SelectableOptions(), eventBus);
    }

    /**
     * Give the possibility of each children (depending of the filter option) of
     * each element to be selectable.
     * 
     * @return
     */
    public Selectable selectable(SelectableOptions options, HasHandlers eventBus) {
        this.options = options;
        this.eventBus = eventBus;
        lasso = new Lasso();

        for (Element e : elements()) {
            e.addClassName(CssClassNames.UI_SELECTABLE);
            $(e).data(CURRENT_FILTER_KEY, options.getFilter());
            refreshSelectees(e);
        }

        initMouseHandler(options);

        return this;
    }

    /**
     * Remove "selectable" feature of elements
     * 
     * @return
     */
    public Selectable unSelectable() {
        for (Element e : elements()) {
            GQuery $e = $(e);
            GQuery selectees = $e.data(SELECTEES_KEY, GQuery.class);
            if (selectees != null) {
                selectees.removeClass(CssClassNames.UI_UNSELECTEE).removeData(SELECTABLE_ITEM_KEY);
            }
            $e.removeClass(CssClassNames.UI_SELECTABLE, "ui-selectable-disabled");
            $e.removeData(SELECTEES_KEY).removeData(SELECTABLE_KEY);

        }
        destroyMouseHandler();
        return this;
    }

    @Override
    protected String getPluginName() {
        return "selectable";
    }

    @Override
    protected boolean mouseCapture(Element selectable, GqEvent event) {
        return !options.isDisabled();
    }

    private boolean isLassoSelectionEnable(GqEvent e) {
        return options.isMultiSelect() && (!options.isLassoOnMetaKey() || e.isMetaKeyPressed());
    }

    protected void onSelection(Element selectable, GqEvent event) {

        if (!isLassoSelectionEnable(event)) {
            return;
        }

        lasso.draw(event.pageX(), event.pageY());

        int x1 = lasso.element.getAbsoluteLeft(), y1 = lasso.element.getAbsoluteTop();
        int x2 = lasso.element.getAbsoluteRight(), y2 = lasso.element.getAbsoluteBottom();

        GQuery selectees = $(selectable).data(SELECTEES_KEY, GQuery.class);

        for (Element e : selectees.elements()) {
            SelectableItem si = $(e).data(SELECTABLE_ITEM_KEY, SelectableItem.class);
            if (si == null || si.getElement() == selectable) {
                continue;
            }

            boolean hit = false;
            if (Tolerance.TOUCH == options.getTolerance()) {
                hit = !(si.getLeft() > x2 || si.getRight() < x1 || si.getTop() > y2 || si.getBottom() < y1);
            } else if (Tolerance.FIT == options.getTolerance()) {
                hit = si.getLeft() > x1 && si.getRight() < x2 && si.getTop() > y1 && si.getBottom() < y2;
            }

            if (hit) {
                if (si.isSelected()) {
                    si.$element().removeClass(CssClassNames.UI_SELECTED);
                    si.setSelected(false);
                }
                if (si.isUnselecting()) {
                    si.$element().removeClass(CssClassNames.UI_UNSELECTING);
                    si.setUnselecting(false);
                }
                if (!si.isSelecting()) {
                    si.selecting();
                    trigger(new SelectingEvent(e), options.getOnSelecting(), e);
                }
            } else {
                if (si.isSelecting()) {
                    if (isMetaKeyEnabled(event) && si.isStartSelected()) {
                        // keep previously selected element
                        si.select();
                    } else {
                        si.$element().removeClass(CssClassNames.UI_SELECTING);
                        si.setSelecting(false);
                        if (si.isStartSelected()) { // previously selected ->
                            // flag it as unselecting
                            si.$element().addClass(CssClassNames.UI_UNSELECTING);
                            si.setUnselecting(true);
                            trigger(new UnselectingEvent(e), options.getOnUnselecting(), e);
                        }

                    }
                }

                if (si.isSelected() && !isMetaKeyEnabled(event) && !si.isStartSelected()) {
                    si.unselecting();
                    trigger(new UnselectingEvent(e), options.getOnUnselecting(), e);

                }
            }

        }

    }

    protected void onSelectionStart(Element selectable, GqEvent event) {

        trigger(new SelectionStartEvent(selectable), options.getOnStartSelection(), selectable);

        if (isLassoSelectionEnable(event)) {
            opos = new int[] { event.pageX(), event.pageY() };
            lasso.show(opos[0], opos[1], options.getAppendTo());
        }

        if (options.isAutoRefresh()) {
            refreshSelectees(selectable);
        }

        GQuery selectedSelectees = $(selectable).data(SELECTEES_KEY, GQuery.class)
                .filter("." + CssClassNames.UI_SELECTED);

        for (Element e : selectedSelectees.elements()) {
            SelectableItem si = $(e).data(SELECTABLE_ITEM_KEY, SelectableItem.class);
            si.setStartSelected(true);
            // if not meta-key or ctrl-keypressed, unselect elements
            if (!isMetaKeyEnabled(event)) {
                si.unselecting();
                trigger(new UnselectingEvent(e), options.getOnUnselecting(), e);
            }
        }

        // remove old selected (case where filter was change before this
        // selection) if no meta-key was pressed
        if (!isMetaKeyEnabled(event)) {
            GQuery oldSelected = $('.' + CssClassNames.UI_SELECTED, selectable);
            for (Element e : oldSelected.elements()) {
                SelectableItem si = $(e).data(SELECTABLE_ITEM_KEY, SelectableItem.class);
                if (si == null) { // old selectee
                    e.removeClassName(CssClassNames.UI_SELECTED);
                    trigger(new UnselectedEvent(e), options.getOnUnselected(), e);
                }
            }
        }

        // retrieve the element that was the target of the event and its parents
        // (maybe one of the parent is the selectee)
        GQuery eventTarget = $((Element) event.getEventTarget().cast());
        GQuery parentsAndSelf = eventTarget.parents().add(eventTarget);
        for (Element e : parentsAndSelf.elements()) {
            SelectableItem si = $(e).data(SELECTABLE_ITEM_KEY, SelectableItem.class);
            if (si != null) {
                // ok a selectable-item exists on the element...
                // it is a selectable element
                boolean doSelection = !isMetaKeyEnabled(event) || !si.isSelected();
                if (doSelection) {
                    si.selecting();
                    trigger(new SelectingEvent(e), options.getOnSelecting(), e);
                } else {
                    si.unselecting();
                    trigger(new UnselectingEvent(e), options.getOnUnselecting(), e);
                }

            }
        }
    }

    protected void onSelectionStop(Element selectable, GqEvent event) {

        GQuery unselecting = $('.' + CssClassNames.UI_UNSELECTING, selectable);
        for (Element e : unselecting.elements()) {
            SelectableItem si = $(e).data(SELECTABLE_ITEM_KEY, SelectableItem.class);
            si.unselect();
            si.setStartSelected(false);
            trigger(new UnselectedEvent(e), options.getOnUnselected(), e);
        }

        GQuery selecting = $('.' + CssClassNames.UI_SELECTING, selectable);
        for (Element e : selecting.elements()) {
            SelectableItem si = $(e).data(SELECTABLE_ITEM_KEY, SelectableItem.class);
            si.select();
            si.setStartSelected(true);
            trigger(new SelectedEvent(e), options.getOnSelected(), e);
        }

        trigger(new SelectionStopEvent(selectable), options.getOnStopSelection(), selectable);

        if (isLassoSelectionEnable(event)) {
            lasso.hide();
        }

    }

    @Override
    protected boolean mouseDrag(Element element, GqEvent event) {
        onSelection(element, event);
        return false;
    }

    @Override
    protected boolean mouseStart(Element element, GqEvent event) {
        onSelectionStart(element, event);
        return true;
    }

    @Override
    protected boolean mouseStop(Element element, GqEvent event) {
        onSelectionStop(element, event);
        return false;
    }

    private boolean isMetaKeyEnabled(GqEvent event) {
        return options.isMultiSelect() && event.isMetaKeyPressed();
    }

    private GQuery refreshSelectees(Element e) {
        GQuery $e = $(e);

        String currentOptionsFilter = $e.data(CURRENT_FILTER_KEY, String.class);
        String newOptionsFilter = options.getFilter();

        if ((currentOptionsFilter == null && newOptionsFilter != null)
                || !currentOptionsFilter.equals(newOptionsFilter)) {
            // filter change since last selection... clean old selectee
            GQuery oldSelectees = $e.data(SELECTEES_KEY, GQuery.class);
            for (Element element : oldSelectees.elements()) {
                $(element).removeData(null).removeClass(CssClassNames.UI_UNSELECTEE);
            }
            $e.data(CURRENT_FILTER_KEY, newOptionsFilter);
        }

        GQuery selectees = $(newOptionsFilter, e);
        for (Element selectee : selectees.elements()) {
            SelectableItem si = new SelectableItem(selectee);
            Offset pos = si.$element().offset();
            si.setLeft(pos.left);
            si.setTop(pos.top);
            si.setRight(pos.left + selectee.getOffsetWidth());
            si.setBottom(pos.top + selectee.getOffsetHeight());
            si.setStartSelected(false);
            si.setSelected(si.$element().hasClass("ui-selected"));
            si.setSelecting(si.$element().hasClass("ui-selecting"));
            si.setUnselecting(si.$element().hasClass("ui-unselecting"));

            si.$element().data(SELECTABLE_ITEM_KEY, si);
            si.$element().addClass(CssClassNames.UI_UNSELECTEE);
        }
        $e.data(SELECTEES_KEY, selectees);
        return selectees;
    }

}