All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vaadin.v7.client.widget.grid.DefaultEditorEventHandler Maven / Gradle / Ivy

There is a newer version: 8.27.3
Show newest version
/*
 * Copyright (C) 2000-2024 Vaadin Ltd
 *
 * This program is available under Vaadin Commercial License and Service Terms.
 *
 * See  for the full
 * license.
 */
package com.vaadin.v7.client.widget.grid;

import com.google.gwt.core.client.Duration;
import com.google.gwt.dom.client.BrowserEvents;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.WidgetUtil;
import com.vaadin.client.ui.FocusUtil;
import com.vaadin.v7.client.widgets.Grid.Editor;
import com.vaadin.v7.client.widgets.Grid.EditorDomEvent;

/**
 * The default handler for Grid editor events. Offers several overridable
 * protected methods for easier customization.
 *
 * @since 7.6
 * @author Vaadin Ltd
 */
public class DefaultEditorEventHandler implements Editor.EventHandler {

    public static final int KEYCODE_OPEN = KeyCodes.KEY_ENTER;
    public static final int KEYCODE_MOVE_VERTICAL = KeyCodes.KEY_ENTER;
    public static final int KEYCODE_CLOSE = KeyCodes.KEY_ESCAPE;
    public static final int KEYCODE_MOVE_HORIZONTAL = KeyCodes.KEY_TAB;
    public static final int KEYCODE_BUFFERED_SAVE = KeyCodes.KEY_ENTER;

    private double lastTouchEventTime = 0;
    private int lastTouchEventX = -1;
    private int lastTouchEventY = -1;
    private int lastTouchEventRow = -1;

    /**
     * Returns whether the given event is a touch event that should open the
     * editor.
     *
     * @param event
     *            the received event
     * @return whether the event is a touch open event
     */
    protected boolean isTouchOpenEvent(EditorDomEvent event) {
        final Event e = event.getDomEvent();
        final int type = e.getTypeInt();

        final double now = Duration.currentTimeMillis();
        final int currentX = WidgetUtil.getTouchOrMouseClientX(e);
        final int currentY = WidgetUtil.getTouchOrMouseClientY(e);

        final boolean validTouchOpenEvent = type == Event.ONTOUCHEND
                && now - lastTouchEventTime < 500
                && lastTouchEventRow == event.getCell().getRowIndex()
                && Math.abs(lastTouchEventX - currentX) < 20
                && Math.abs(lastTouchEventY - currentY) < 20;

        if (type == Event.ONTOUCHSTART) {
            lastTouchEventX = currentX;
            lastTouchEventY = currentY;
        }

        if (type == Event.ONTOUCHEND) {
            lastTouchEventTime = now;
            lastTouchEventRow = event.getCell().getRowIndex();
        }

        return validTouchOpenEvent;
    }

    /**
     * Returns whether the given event should open the editor. The default
     * implementation returns true if and only if the event is a doubleclick or
     * if it is a keydown event and the keycode is {@link #KEYCODE_OPEN}.
     *
     * @param event
     *            the received event
     * @return true if the event is an open event, false otherwise
     */
    protected boolean isOpenEvent(EditorDomEvent event) {
        final Event e = event.getDomEvent();
        return e.getTypeInt() == Event.ONDBLCLICK
                || (e.getTypeInt() == Event.ONKEYDOWN
                        && e.getKeyCode() == KEYCODE_OPEN)
                || isTouchOpenEvent(event);
    }

    /**
     * Opens the editor on the appropriate row if the received event is an open
     * event. The default implementation uses
     * {@link #isOpenEvent(EditorDomEvent) isOpenEvent}.
     *
     * @param event
     *            the received event
     * @return true if this method handled the event and nothing else should be
     *         done, false otherwise
     */
    protected boolean handleOpenEvent(EditorDomEvent event) {
        if (isOpenEvent(event)) {
            final EventCellReference cell = event.getCell();

            editRow(event, cell.getRowIndex(), cell.getColumnIndexDOM());

            event.getDomEvent().preventDefault();

            return true;
        }
        return false;
    }

    /**
     * Moves the editor to another row or another column if the received event
     * is a move event. The default implementation moves the editor to the
     * clicked row if the event is a click; otherwise, if the event is a keydown
     * and the keycode is {@link #KEYCODE_MOVE_VERTICAL}, moves the editor one
     * row up or down if the shift key is pressed or not, respectively. Keydown
     * event with keycode {@link #KEYCODE_MOVE_HORIZONTAL} moves the editor left
     * or right if shift key is pressed or not, respectively.
     *
     * @param event
     *            the received event
     * @return true if this method handled the event and nothing else should be
     *         done, false otherwise
     */
    protected boolean handleMoveEvent(EditorDomEvent event) {
        Event e = event.getDomEvent();
        final EventCellReference cell = event.getCell();

        // TODO: Move on touch events
        if (e.getTypeInt() == Event.ONCLICK) {

            editRow(event, cell.getRowIndex(), cell.getColumnIndexDOM());

            return true;
        } else if (e.getTypeInt() == Event.ONKEYDOWN) {

            int rowDelta = 0;
            int colDelta = 0;

            if (e.getKeyCode() == KEYCODE_MOVE_VERTICAL) {
                rowDelta = (e.getShiftKey() ? -1 : +1);
            } else if (e.getKeyCode() == KEYCODE_MOVE_HORIZONTAL) {
                colDelta = (e.getShiftKey() ? -1 : +1);
                // Prevent tab out of Grid Editor
                event.getDomEvent().preventDefault();
            }

            final boolean changed = rowDelta != 0 || colDelta != 0;

            if (changed) {

                int columnCount = event.getGrid().getVisibleColumns().size();

                int colIndex = event.getFocusedColumnIndex() + colDelta;
                int rowIndex = event.getRowIndex();

                // Handle row change with horizontal move when column goes out
                // of range.
                if (rowDelta == 0) {
                    if (colIndex >= columnCount
                            && rowIndex < event.getGrid().getDataSource().size()
                                    - 1) {
                        rowDelta = 1;
                        colIndex = 0;
                    } else if (colIndex < 0 && rowIndex > 0) {
                        rowDelta = -1;
                        colIndex = columnCount - 1;
                    }
                }

                editRow(event, rowIndex + rowDelta, colIndex);
            }

            return changed;
        }

        return false;
    }

    /**
     * Moves the editor to another column if the received event is a move event.
     * By default the editor is moved on a keydown event with keycode
     * {@link #KEYCODE_MOVE_HORIZONTAL}. This moves the editor left or right if
     * shift key is pressed or not, respectively.
     *
     * @param event
     *            the received event
     * @return true if this method handled the event and nothing else should be
     *         done, false otherwise
     */
    protected boolean handleBufferedMoveEvent(EditorDomEvent event) {
        Event e = event.getDomEvent();

        if (e.getType().equals(BrowserEvents.CLICK)
                && event.getRowIndex() == event.getCell().getRowIndex()) {

            editRow(event, event.getRowIndex(),
                    event.getCell().getColumnIndexDOM());

            return true;

        } else if (e.getType().equals(BrowserEvents.KEYDOWN)
                && e.getKeyCode() == KEYCODE_MOVE_HORIZONTAL) {

            // Prevent tab out of Grid Editor
            event.getDomEvent().preventDefault();

            editRow(event, event.getRowIndex(), event.getFocusedColumnIndex()
                    + (e.getShiftKey() ? -1 : +1));

            return true;
        } else if (e.getType().equals(BrowserEvents.KEYDOWN)
                && e.getKeyCode() == KEYCODE_BUFFERED_SAVE) {
            triggerValueChangeEvent(event);

            // Save and close.
            event.getGrid().getEditor().save();
            return true;
        }

        return false;
    }

    /**
     * Returns whether the given event should close the editor. The default
     * implementation returns true if and only if the event is a keydown event
     * and the keycode is {@link #KEYCODE_CLOSE}.
     *
     * @param event
     *            the received event
     * @return true if the event is a close event, false otherwise
     */
    protected boolean isCloseEvent(EditorDomEvent event) {
        final Event e = event.getDomEvent();
        return e.getTypeInt() == Event.ONKEYDOWN
                && e.getKeyCode() == KEYCODE_CLOSE;
    }

    /**
     * Closes the editor if the received event is a close event. The default
     * implementation uses {@link #isCloseEvent(EditorDomEvent) isCloseEvent}.
     *
     * @param event
     *            the received event
     * @return true if this method handled the event and nothing else should be
     *         done, false otherwise
     */
    protected boolean handleCloseEvent(EditorDomEvent event) {
        if (isCloseEvent(event)) {
            event.getEditor().cancel();
            FocusUtil.setFocus(event.getGrid(), true);
            return true;
        }
        return false;
    }

    protected void editRow(EditorDomEvent event, int rowIndex,
            int colIndex) {
        int rowCount = event.getGrid().getDataSource().size();
        // Limit rowIndex between 0 and rowCount - 1
        rowIndex = Math.max(0, Math.min(rowCount - 1, rowIndex));

        int colCount = event.getGrid().getVisibleColumns().size();
        // Limit colIndex between 0 and colCount - 1
        colIndex = Math.max(0, Math.min(colCount - 1, colIndex));

        if (rowIndex != event.getRowIndex()) {
            triggerValueChangeEvent(event);
        }

        event.getEditor().editRow(rowIndex, colIndex);
    }

    /**
     * Triggers a value change event from the editor field if it has focus. This
     * is based on the assumption that editor field will fire the value change
     * when a blur event occurs.
     *
     * @param event
     *            the editor DOM event
     */
    private void triggerValueChangeEvent(EditorDomEvent event) {
        // Force a blur to cause a value change event
        Widget editorWidget = event.getEditorWidget();
        if (editorWidget != null) {
            Element focusedElement = WidgetUtil.getFocusedElement();
            if (editorWidget.getElement().isOrHasChild(focusedElement)) {
                focusedElement.blur();
                focusedElement.focus();
            }
        }
    }

    @Override
    public boolean handleEvent(EditorDomEvent event) {
        final Editor editor = event.getEditor();
        final boolean isBody = event.getCell().isBody();

        final boolean handled;
        if (event.getGrid().isEditorActive()) {
            handled = handleCloseEvent(event)
                    || (!editor.isBuffered() && isBody
                            && handleMoveEvent(event))
                    || (editor.isBuffered() && isBody
                            && handleBufferedMoveEvent(event));
        } else {
            handled = event.getGrid().isEnabled() && isBody
                    && handleOpenEvent(event);
        }

        // Buffered mode should swallow all events, if not already handled.
        boolean swallowEvent = event.getGrid().isEditorActive()
                && editor.isBuffered();

        return handled || swallowEvent;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy