Java tutorial
/* * Copyright 2013 JBoss, by Red Hat, Inc * * 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 org.jboss.errai.otec.client.atomizer; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyDownEvent; import com.google.gwt.event.dom.client.KeyDownHandler; import com.google.gwt.event.dom.client.KeyEvent; import com.google.gwt.event.dom.client.KeyPressEvent; import com.google.gwt.event.dom.client.KeyPressHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.EventListener; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.ValueBoxBase; import org.jboss.errai.common.client.util.LogUtil; import org.jboss.errai.otec.client.EntityStreamRegistration; import org.jboss.errai.otec.client.ListenerRegistration; import org.jboss.errai.otec.client.OTEngine; import org.jboss.errai.otec.client.OTEntity; import org.jboss.errai.otec.client.StateChangeListener; import org.jboss.errai.otec.client.util.DiffUtil; import java.util.Collection; /** * @author Mike Brock * @author Christian Sadilek */ public abstract class Atomizer { private Atomizer() { } private static boolean NO_PROPAGATE_STATE_CHANGE = false; public static AtomizerSession syncWidgetWith(final OTEngine engine, final OTEntity entity, final ValueBoxBase widget) { LogUtil.log("NEW ATOMIZER SESSION (engine:" + engine.getId() + ", widget=" + widget + ")"); final Multimap<Object, HandlerRegistration> HANDLER_REGISTRATION_MAP = HashMultimap.create(); final EntityChangeStreamImpl entityChangeStream = new EntityChangeStreamImpl(engine, entity); final EntityStreamRegistration entityStreamRegistration = engine.getPeerState() .addEntityStream(entityChangeStream); widget.setValue(entity.getState().get()); HANDLER_REGISTRATION_MAP.put(widget, widget.addKeyDownHandler(new KeyDownHandler() { @Override public void onKeyDown(final KeyDownEvent event) { if (shouldIgnoreKeyPress(event)) { return; } if (widget.getSelectedText().length() > 0) { stopEvents(); entityChangeStream.notifyDelete(widget.getCursorPos(), widget.getSelectedText()); startEvents(); } else if (event.getNativeKeyCode() == KeyCodes.KEY_BACKSPACE) { stopEvents(); final int index = widget.getCursorPos() - 1; entityChangeStream.notifyDelete(index, " "); startEvents(); } else if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) { stopEvents(); entityChangeStream.notifyInsert(widget.getCursorPos(), "\n"); startEvents(); } } })); HANDLER_REGISTRATION_MAP.put(widget, widget.addKeyPressHandler(new KeyPressHandler() { @Override public void onKeyPress(final KeyPressEvent event) { if (event.getUnicodeCharCode() != 13 && event.getUnicodeCharCode() != 0) { stopEvents(); entityChangeStream.notifyInsert(widget.getCursorPos(), String.valueOf(event.getCharCode())); startEvents(); } } })); DOM.setEventListener(widget.getElement(), new EventListener() { @Override public void onBrowserEvent(Event event) { if (event.getTypeInt() == Event.ONPASTE) { final String before = (String) entity.getState().get(); new Timer() { @Override public void run() { final String after = (String) widget.getValue(); final DiffUtil.Delta diff = DiffUtil.diff(before, after); stopEvents(); entityChangeStream.notifyInsert(diff.getCursor(), diff.getDeltaText()); startEvents(); } }.schedule(1); } widget.onBrowserEvent(event); } }); attachCutHandler(widget.getElement(), new Runnable() { @Override public void run() { stopEvents(); entityChangeStream.notifyDelete(widget.getCursorPos(), widget.getSelectedText()); startEvents(); } }); attachTextDragHandler(widget.getElement(), new Runnable() { @Override public void run() { stopEvents(); entityChangeStream.notifyDelete(widget.getCursorPos(), widget.getSelectedText()); entityChangeStream.flush(); startEvents(); } }, new Runnable() { @Override public void run() { final String old = (String) entity.getState().get(); new Timer() { @Override public void run() { final DiffUtil.Delta diff = DiffUtil.diff(old, (String) widget.getValue()); if (diff.getDeltaText().length() > 0) { stopEvents(); entityChangeStream.notifyInsert(diff.getCursor(), diff.getDeltaText()); startEvents(); } } }.schedule(1); } }); final ListenerRegistration listenerRegistration = entity.getState() .addStateChangeListener(new StateChangeListener() { @Override public int getCursorPos() { return widget.getCursorPos(); } @Override public void onStateChange(final int newCursorPos, final Object newValue) { if (NO_PROPAGATE_STATE_CHANGE) { return; } widget.setValue(newValue, false); widget.setCursorPos(newCursorPos); } }); DOM.sinkEvents(widget.getElement(), DOM.getEventsSunk(widget.getElement()) | Event.ONPASTE); final Timer timer = new Timer() { @Override public void run() { entityChangeStream.flush(); } }; timer.scheduleRepeating(500); return new AtomizerSession() { @Override public void end() { entityChangeStream.close(); timer.cancel(); LogUtil.log("END ATOMIZER SESSION"); entityStreamRegistration.remove(); listenerRegistration.remove(); final Collection<HandlerRegistration> values = HANDLER_REGISTRATION_MAP.values(); for (final HandlerRegistration value : values) { value.removeHandler(); } } }; } private static boolean shouldIgnoreKeyPress(KeyEvent event) { if (event.isMetaKeyDown() || event.isControlKeyDown()) { return true; } int keyCode; if (event instanceof KeyDownEvent) { keyCode = ((KeyDownEvent) event).getNativeKeyCode(); } else { return true; } switch (keyCode) { case KeyCodes.KEY_DOWN: case KeyCodes.KEY_LEFT: case KeyCodes.KEY_UP: case KeyCodes.KEY_RIGHT: case KeyCodes.KEY_ESCAPE: case KeyCodes.KEY_PAGEDOWN: case KeyCodes.KEY_PAGEUP: case KeyCodes.KEY_HOME: case KeyCodes.KEY_END: return true; } return false; } public static void stopEvents() { NO_PROPAGATE_STATE_CHANGE = true; } public static void startEvents() { NO_PROPAGATE_STATE_CHANGE = false; } private static native void attachCutHandler(Element element, Runnable runnable) /*-{ element.oncut = function () { runnable.@java.lang.Runnable::run()(); return true; } }-*/; private static native void attachTextDragHandler(Element element, Runnable onStart, Runnable onFinish) /*-{ element.ondragstart = function () { onStart.@java.lang.Runnable::run()(); return true; } element.ondragend = function () { onFinish.@java.lang.Runnable::run()(); return true; } }-*/; }