├── .settings └── .gitignore ├── GridFastNavigation-demo ├── .gitignore ├── src │ └── main │ │ ├── webapp │ │ ├── META-INF │ │ │ ├── MANIFEST.MF │ │ │ └── context.xml │ │ └── VAADIN │ │ │ ├── themes │ │ │ └── demo │ │ │ │ ├── favicon.ico │ │ │ │ ├── images │ │ │ │ └── radial-gradient.png │ │ │ │ └── styles.scss │ │ │ └── widgetsets │ │ │ └── org.vaadin.patrik.demo.DemoWidgetSet │ │ │ └── GridFastNavigation │ │ │ └── styles.css │ │ ├── java │ │ └── org │ │ │ └── vaadin │ │ │ └── patrik │ │ │ └── demo │ │ │ ├── MessageLog.java │ │ │ ├── ServerMessage.java │ │ │ ├── MessageGrid.java │ │ │ ├── DemoColumns.java │ │ │ ├── DemoUI.java │ │ │ └── DemoFastGrid.java │ │ └── resources │ │ └── org │ │ └── vaadin │ │ └── patrik │ │ └── demo │ │ └── DemoWidgetSet.gwt.xml ├── .settings │ └── .gitignore └── pom.xml ├── .gitignore ├── GridFastNavigation-addon ├── src │ └── main │ │ ├── resources │ │ └── org │ │ │ └── vaadin │ │ │ └── patrik │ │ │ ├── public │ │ │ └── GridFastNavigation │ │ │ │ └── styles.css │ │ │ └── GridFastNavigationWidgetSet.gwt.xml │ │ └── java │ │ └── org │ │ └── vaadin │ │ └── patrik │ │ ├── shared │ │ ├── DeleteButtonRendererServerRpc.java │ │ ├── DeleteButtonRendererState.java │ │ ├── FastNavigationClientRPC.java │ │ ├── FastNavigationServerRPC.java │ │ └── FastNavigationState.java │ │ ├── events │ │ ├── Listener.java │ │ ├── ClickOutEvent.java │ │ ├── EventListenerList.java │ │ ├── RowEditEvent.java │ │ ├── RowFocusEvent.java │ │ ├── EditorCloseEvent.java │ │ ├── CellEditEvent.java │ │ ├── CellFocusEvent.java │ │ └── EditorOpenEvent.java │ │ ├── client │ │ ├── RPCLock.java │ │ ├── SpinLock.java │ │ ├── FocusTracker.java │ │ ├── GridViolators.java │ │ ├── Keys.java │ │ ├── DeleteButtonRendererConnector.java │ │ ├── GridFastNavigationConnector.java │ │ └── EditorWidgets.java │ │ ├── helper │ │ └── OffsetHelper.java │ │ ├── DeleteButtonRenderer.java │ │ └── FastNavigation.java ├── .settings │ └── .gitignore ├── assembly │ ├── MANIFEST.MF │ └── assembly.xml └── pom.xml ├── pom.xml ├── LICENSE.txt └── README.md /.settings/.gitignore: -------------------------------------------------------------------------------- 1 | /org.eclipse.m2e.core.prefs 2 | -------------------------------------------------------------------------------- /GridFastNavigation-demo/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.classpath 3 | /.project 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.project 2 | GridFastNavigation-demo/src/main/webapp/VAADIN/widgetsets/ 3 | -------------------------------------------------------------------------------- /GridFastNavigation-demo/src/main/webapp/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Class-Path: 3 | 4 | -------------------------------------------------------------------------------- /GridFastNavigation-demo/src/main/webapp/META-INF/context.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /GridFastNavigation-demo/src/main/webapp/VAADIN/themes/demo/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TatuLund/GridFastNavigation/HEAD/GridFastNavigation-demo/src/main/webapp/VAADIN/themes/demo/favicon.ico -------------------------------------------------------------------------------- /GridFastNavigation-demo/src/main/java/org/vaadin/patrik/demo/MessageLog.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.demo; 2 | 3 | public interface MessageLog 4 | { 5 | 6 | void writeOutput(String string); 7 | 8 | } 9 | -------------------------------------------------------------------------------- /GridFastNavigation-demo/src/main/webapp/VAADIN/themes/demo/images/radial-gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TatuLund/GridFastNavigation/HEAD/GridFastNavigation-demo/src/main/webapp/VAADIN/themes/demo/images/radial-gradient.png -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/resources/org/vaadin/patrik/public/GridFastNavigation/styles.css: -------------------------------------------------------------------------------- 1 | div.GridFastNavigation { 2 | cursor: pointer; 3 | user-select: none; 4 | -webkit-user-select: none; 5 | -moz-user-select: none; 6 | -ms-user-select: none; 7 | } -------------------------------------------------------------------------------- /GridFastNavigation-demo/src/main/webapp/VAADIN/widgetsets/org.vaadin.patrik.demo.DemoWidgetSet/GridFastNavigation/styles.css: -------------------------------------------------------------------------------- 1 | div.GridFastNavigation { 2 | cursor: pointer; 3 | user-select: none; 4 | -webkit-user-select: none; 5 | -moz-user-select: none; 6 | -ms-user-select: none; 7 | } -------------------------------------------------------------------------------- /GridFastNavigation-addon/.settings/.gitignore: -------------------------------------------------------------------------------- 1 | /org.eclipse.core.resources.prefs 2 | /org.eclipse.jdt.core.prefs 3 | /org.eclipse.m2e.core.prefs 4 | /org.eclipse.wst.common.component 5 | /org.eclipse.wst.common.project.facet.core.xml 6 | /org.eclipse.wst.validation.prefs 7 | /com.vaadin.integration.eclipse.prefs 8 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/assembly/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Vaadin-Package-Version: 1 3 | Vaadin-Addon: ${Vaadin-Addon} 4 | Vaadin-License-Title: ${Vaadin-License-Title} 5 | Implementation-Vendor: ${Implementation-Vendor} 6 | Implementation-Title: ${Implementation-Title} 7 | Implementation-Version: ${Implementation-Version} 8 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/java/org/vaadin/patrik/shared/DeleteButtonRendererServerRpc.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.shared; 2 | 3 | import com.vaadin.shared.MouseEventDetails; 4 | import com.vaadin.shared.communication.ServerRpc; 5 | 6 | public interface DeleteButtonRendererServerRpc extends ServerRpc { 7 | 8 | public void onClick(String rowKey, MouseEventDetails mouseEventDetails); 9 | } 10 | -------------------------------------------------------------------------------- /GridFastNavigation-demo/.settings/.gitignore: -------------------------------------------------------------------------------- 1 | /.jsdtscope 2 | /org.eclipse.core.resources.prefs 3 | /org.eclipse.jdt.core.prefs 4 | /org.eclipse.m2e.core.prefs 5 | /org.eclipse.wst.common.component 6 | /org.eclipse.wst.common.project.facet.core.xml 7 | /org.eclipse.wst.jsdt.ui.superType.container 8 | /org.eclipse.wst.jsdt.ui.superType.name 9 | /org.eclipse.wst.validation.prefs 10 | /com.vaadin.integration.eclipse.prefs 11 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/java/org/vaadin/patrik/shared/DeleteButtonRendererState.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.shared; 2 | 3 | import com.vaadin.shared.ui.grid.renderers.ClickableRendererState; 4 | 5 | public class DeleteButtonRendererState extends ClickableRendererState { 6 | public boolean htmlContentAllowed = false; 7 | public String delete = "Delete"; 8 | public String confirm = "Confirm"; 9 | } 10 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/java/org/vaadin/patrik/events/Listener.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.events; 2 | 3 | import com.vaadin.event.SerializableEventListener; 4 | 5 | /** 6 | * Supertype of the Listeners used in {@link org.vaadin.patrik.FastNavigation} 7 | * 8 | * @param Type of the Event 9 | */ 10 | public interface Listener extends SerializableEventListener { 11 | public void onEvent(EventType event); 12 | } -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/resources/org/vaadin/patrik/GridFastNavigationWidgetSet.gwt.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/java/org/vaadin/patrik/events/ClickOutEvent.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.events; 2 | 3 | import com.vaadin.ui.Component; 4 | 5 | /** 6 | * ClickOutEvent is emitted when user clicks outside the Grid. 7 | * 8 | * @since 2.1.8 9 | * 10 | * @see org.vaadin.patrik.FastNavigation#FastNavigation(com.vaadin.ui.Grid, boolean, boolean) 11 | * @see org.vaadin.patrik.FastNavigation#addClickOutListener(org.vaadin.patrik.FastNavigation.ClickOutListener) 12 | * 13 | */ 14 | public class ClickOutEvent extends Component.Event { 15 | 16 | public ClickOutEvent(Component source) { 17 | super(source); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/java/org/vaadin/patrik/shared/FastNavigationClientRPC.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.shared; 2 | 3 | import java.util.List; 4 | 5 | import com.vaadin.shared.communication.ClientRpc; 6 | 7 | /** 8 | * Server-to-client RPC methods 9 | */ 10 | public interface FastNavigationClientRPC extends ClientRpc { 11 | 12 | void setDisabledColumns(List indices); 13 | 14 | void unlockEditor(int lockId); 15 | 16 | void validationHasErrors(); 17 | 18 | void setFocusedCell(int row, int col, boolean wait); 19 | 20 | void closeEditor(); 21 | 22 | void editRow(int rowIndex, int columnIndexDOM); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/java/org/vaadin/patrik/client/RPCLock.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.client; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | public class RPCLock { 7 | 8 | private Set locks; 9 | private int seq; 10 | 11 | public RPCLock() { 12 | locks = new HashSet(); 13 | seq = 1; 14 | } 15 | 16 | int requestLock() { 17 | locks.add(seq); 18 | return seq++; 19 | } 20 | 21 | void releaseLock(int id) { 22 | locks.remove(id); 23 | } 24 | 25 | void clearLocks() { 26 | locks.clear(); 27 | } 28 | 29 | boolean isLocked() { 30 | return !locks.isEmpty(); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/java/org/vaadin/patrik/shared/FastNavigationServerRPC.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.shared; 2 | 3 | import com.vaadin.shared.communication.ServerRpc; 4 | 5 | /** 6 | * Client-to-server RPC methods 7 | */ 8 | public interface FastNavigationServerRPC extends ServerRpc { 9 | 10 | void focusUpdated(int rowIndex, int colIndex, boolean isUserOriginated); 11 | 12 | void rowUpdated(int rowIndex, boolean moved); 13 | 14 | void cellUpdated(int rowIndex, int colIndex, String newData, String oldData, boolean moved); 15 | 16 | void editorOpened(int rowIndex, int colIndex, int lockId, int keyCode, boolean isUserOriginated); 17 | 18 | void editorClosed(int rowIndex, int colIndex, boolean wasCancelled); 19 | 20 | void clickOut(); 21 | 22 | void ping(); 23 | 24 | void forceValidate(boolean move); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /GridFastNavigation-demo/src/main/resources/org/vaadin/patrik/demo/DemoWidgetSet.gwt.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/java/org/vaadin/patrik/events/EventListenerList.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.events; 2 | 3 | import java.io.Serializable; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | /** 8 | * Used in {@link org.vaadin.patrik.FastNavigation} for listener maintenance 9 | * 10 | * @param Listener type 11 | * @param Event type 12 | */ 13 | public class EventListenerList, EVENT> implements Serializable { 14 | private final List listeners; 15 | 16 | public EventListenerList() { 17 | listeners = new ArrayList(); 18 | } 19 | 20 | public void addListener(LISTENER l) { 21 | listeners.add(l); 22 | } 23 | 24 | public void removeListener(LISTENER l) { 25 | listeners.remove(l); 26 | } 27 | 28 | public void clearListeners() { 29 | listeners.clear(); 30 | } 31 | 32 | public void dispatch(EVENT event) { 33 | for(LISTENER l : listeners) { 34 | l.onEvent(event); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /GridFastNavigation-demo/src/main/java/org/vaadin/patrik/demo/ServerMessage.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.demo; 2 | 3 | public class ServerMessage 4 | { 5 | private String message; 6 | 7 | public ServerMessage(String msg) 8 | { 9 | this.message = msg; 10 | } 11 | 12 | public String getMessage() 13 | { 14 | return message; 15 | } 16 | 17 | public void setMessage(String message) 18 | { 19 | this.message = message; 20 | } 21 | 22 | @Override 23 | public String toString() 24 | { 25 | return "ServerMessage [message=" + message + "]"; 26 | } 27 | 28 | @Override 29 | public int hashCode() 30 | { 31 | final int prime = 31; 32 | int result = 1; 33 | result = prime * result + ((message == null) ? 0 : message.hashCode()); 34 | return result; 35 | } 36 | 37 | @Override 38 | public boolean equals(Object obj) 39 | { 40 | if (this == obj) 41 | return true; 42 | if (obj == null) 43 | return false; 44 | if (getClass() != obj.getClass()) 45 | return false; 46 | ServerMessage other = (ServerMessage) obj; 47 | if (message == null) 48 | { 49 | if (other.message != null) 50 | return false; 51 | } 52 | else if (!message.equals(other.message)) 53 | return false; 54 | return true; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/java/org/vaadin/patrik/events/RowEditEvent.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.events; 2 | 3 | import com.vaadin.ui.Component; 4 | 5 | /** 6 | * RowEditEvent is emitted when value in the Editor has been changed 7 | * 8 | * @see org.vaadin.patrik.FastNavigation#addRowEditListener(org.vaadin.patrik.FastNavigation.RowEditListener) 9 | * 10 | * @param Bean type of the Grid where {@link org.vaadin.patrik.FastNavigation} is being used 11 | */ 12 | @SuppressWarnings("serial") 13 | public class RowEditEvent extends Component.Event { 14 | 15 | private int rowIndex; 16 | private T item; 17 | 18 | public RowEditEvent(Component source, Integer rowIndex, T item) { 19 | super(source); 20 | this.rowIndex = rowIndex; 21 | this.item = item; 22 | } 23 | 24 | /** 25 | * Get index of the row which was edited 26 | * 27 | * @return Index of the row which is edited 28 | */ 29 | public int getRowIndex() { 30 | return rowIndex; 31 | } 32 | 33 | /** 34 | * Get item which was edited from underlying datasource 35 | * 36 | * @since 2.1.4 37 | * 38 | * @return Item which is edited 39 | */ 40 | public T getItem() { 41 | return item; 42 | } 43 | 44 | } 45 | 46 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | org.vaadin.patrik 6 | GridFastNavigation-root 7 | pom 8 | 2.6.3 9 | GridFastNavigation Add-on Root Project 10 | 11 | 12 | GridFastNavigation-addon 13 | GridFastNavigation-demo 14 | 15 | 16 | 17 | 18 | 19 | vaadin-prerelease 20 | 21 | false 22 | 23 | 24 | 25 | 26 | vaadin-prereleases 27 | https://maven.vaadin.com/vaadin-prereleases 28 | 29 | 30 | 31 | 32 | vaadin-prereleases 33 | https://maven.vaadin.com/vaadin-prereleases 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/java/org/vaadin/patrik/events/RowFocusEvent.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.events; 2 | 3 | import com.vaadin.ui.Component; 4 | 5 | /** 6 | * RowFocusEvent is emitted when focused row is changed in the Grid 7 | * 8 | * @see org.vaadin.patrik.FastNavigation#addRowFocusListener(org.vaadin.patrik.FastNavigation.RowFocusListener) 9 | * 10 | * @param Bean type of the Grid where {@link org.vaadin.patrik.FastNavigation} is being used 11 | */ 12 | @SuppressWarnings("serial") 13 | public class RowFocusEvent extends Component.Event { 14 | 15 | private int row; 16 | private T item; 17 | 18 | public RowFocusEvent(Component source, int rowIndex, T item) { 19 | super(source); 20 | this.row = rowIndex; 21 | this.item = item; 22 | } 23 | 24 | /** 25 | * Get index of the row which was edited 26 | * 27 | * @return Index of the row which is edited, -1 if focus in Header/Footer 28 | */ 29 | public int getRow() { 30 | return row; 31 | } 32 | 33 | /** 34 | * Get item which wherew focus is from underlying datasource 35 | * 36 | * @since 2.1.5 37 | * 38 | * @return item where focus is, null if focus in Header/Footer 39 | */ 40 | public T getItem() { 41 | return item; 42 | } 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/java/org/vaadin/patrik/client/SpinLock.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.client; 2 | 3 | import com.google.gwt.animation.client.AnimationScheduler; 4 | import com.google.gwt.animation.client.AnimationScheduler.AnimationCallback; 5 | 6 | /** 7 | * Perform asynchronous spinlocking through RequestAnimationFrame 8 | */ 9 | public class SpinLock { 10 | 11 | /** 12 | * LockFunction interface: single function, should return 'true' as long 13 | * as lock is to be held, and 'false' when it should be released 14 | */ 15 | public interface LockFunction { 16 | boolean execute(); 17 | } 18 | 19 | /** 20 | * Callback function gets called when the lock is released. 21 | */ 22 | public interface Callback { 23 | void complete(); 24 | } 25 | 26 | public static void lock(final LockFunction fn, final Callback cb) { 27 | final AnimationCallback a = new AnimationCallback() { 28 | @Override 29 | public void execute(double timestamp) { 30 | if(fn.execute()) { 31 | AnimationScheduler.get().requestAnimationFrame(this); 32 | } else { 33 | cb.complete(); 34 | } 35 | } 36 | }; 37 | AnimationScheduler.get().requestAnimationFrame(a); 38 | 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/assembly/assembly.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | addon 7 | 8 | 10 | 11 | 12 | zip 13 | 14 | 15 | 16 | false 17 | 18 | 19 | 20 | .. 21 | 22 | LICENSE.txt 23 | README.md 24 | 25 | 26 | 27 | target 28 | 29 | 30 | *.jar 31 | *.pdf 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | assembly/MANIFEST.MF 40 | META-INF 41 | true 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/java/org/vaadin/patrik/helper/OffsetHelper.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.helper; 2 | 3 | import java.io.Serializable; 4 | 5 | import com.vaadin.ui.Grid; 6 | import com.vaadin.ui.components.grid.GridSelectionModel; 7 | import com.vaadin.ui.components.grid.MultiSelectionModel; 8 | 9 | /** 10 | * Helps in calculation the offset for the columns in an EditorOpenEvent. 11 | * 12 | * @since 2.3.10 13 | */ 14 | public class OffsetHelper implements Serializable { 15 | 16 | /** 17 | * Calculates offset needed e.g. in multiselect mode it needs to be 1. Override this 18 | * method if you want to implement custom OffsetHelper by extending this class and 19 | * set it with {@link org.vaadin.patrik.FastNavigation#setOffsetHelper(OffsetHelper)} 20 | * 21 | * @since 2.3.10 22 | * 23 | * @param g Grid 24 | * @return Offset 25 | */ 26 | public int calculateOffset(final Grid g) { 27 | int offset = 0; 28 | GridSelectionModel selectionModel = g.getSelectionModel(); 29 | if (selectionModel instanceof MultiSelectionModel 30 | && !this.isGridExtensionPackTableSelectionModel(selectionModel)) { 31 | offset = 1; 32 | } 33 | return offset; 34 | } 35 | 36 | /* 37 | * Offset for GridExpansionPack needs to be zero in MultiselectionMode. 38 | */ 39 | private boolean isGridExtensionPackTableSelectionModel(GridSelectionModel selectionModel) { 40 | return selectionModel.getClass().getName() 41 | .equals("org.vaadin.teemusa.gridextensions.tableselection.TableSelectionModel"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /GridFastNavigation-demo/src/main/java/org/vaadin/patrik/demo/MessageGrid.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.demo; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.vaadin.data.provider.ListDataProvider; 7 | import com.vaadin.ui.Grid; 8 | import com.vaadin.ui.UI; 9 | 10 | public class MessageGrid extends Grid implements MessageLog 11 | { 12 | private static final long serialVersionUID = 1L; 13 | 14 | 15 | private static final int MAX_MESSAGES = 50; 16 | 17 | 18 | List messageList; 19 | ListDataProvider messageData; 20 | 21 | MessageGrid() 22 | { 23 | super("Message Log"); 24 | 25 | messageList = new ArrayList<>(); 26 | messageData = new ListDataProvider(messageList); 27 | 28 | this.getHeaderRow(0).setStyleName("my-background"); 29 | 30 | this.setDataProvider(messageData); 31 | this.addColumn(ServerMessage::getMessage).setCaption("Message").setExpandRatio(1); 32 | this.setSizeFull(); 33 | } 34 | 35 | public void writeOutput(final String msg) 36 | { 37 | UI.getCurrent().access(new Runnable() 38 | { 39 | @Override 40 | public void run() 41 | { 42 | while (messageData.getItems().size() >= MAX_MESSAGES) 43 | { 44 | messageList.remove(0); 45 | messageData.refreshAll(); 46 | } 47 | 48 | ServerMessage message = new ServerMessage(msg); 49 | messageList.add(message); 50 | messageData.refreshAll(); 51 | scrollTo(messageList.size() - 1); 52 | } 53 | }); 54 | } 55 | 56 | public void clear() 57 | { 58 | messageList.clear(); 59 | messageData.refreshAll(); 60 | } 61 | 62 | 63 | } 64 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/java/org/vaadin/patrik/shared/FastNavigationState.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.shared; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | import com.vaadin.shared.communication.SharedState; 7 | 8 | @SuppressWarnings("serial") 9 | public class FastNavigationState extends SharedState { 10 | 11 | public boolean openEditorOnType = true; 12 | 13 | public boolean selectTextOnEditorOpen = true; 14 | 15 | public boolean allowTabRowChange = true; 16 | 17 | public boolean allowArrowRowChange = true; 18 | 19 | public boolean changeColumnAfterLastRow = false; 20 | 21 | public boolean hasFocusListener = false; 22 | 23 | public boolean hasRowFocusListener = false; 24 | 25 | public boolean hasCellFocusListener = false; 26 | 27 | public boolean hasRowEditListener = false; 28 | 29 | public boolean hasCellEditListener = false; 30 | 31 | public boolean hasEditorOpenListener = false; 32 | 33 | public boolean hasEditorMoveListener = false; 34 | 35 | public boolean hasEditorCloseListener = false; 36 | 37 | public boolean hasClickOutListener = false; 38 | 39 | public boolean changeColumnOnEnter = false; 40 | 41 | public boolean openEditorWithSingleClick = true; 42 | 43 | public boolean dispatchEditEventOnBlur = false; 44 | 45 | public boolean saveWithCtrlS = false; 46 | 47 | public boolean rowValidation = false; 48 | 49 | public boolean homeEndEnabled = true; 50 | 51 | public boolean enableSelectedStyle = false; 52 | 53 | public Set openShortcuts = new HashSet(); 54 | 55 | public Set closeShortcuts = new HashSet(); 56 | 57 | public Set saveShortcuts = new HashSet(); 58 | 59 | } 60 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/java/org/vaadin/patrik/events/EditorCloseEvent.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.events; 2 | 3 | import com.vaadin.ui.Component; 4 | import com.vaadin.ui.Grid; 5 | import com.vaadin.ui.components.grid.MultiSelectionModel; 6 | 7 | /** 8 | * EditorCloseEvent is emitted when Editor is closed 9 | * 10 | * @see org.vaadin.patrik.FastNavigation#addEditorCloseListener(org.vaadin.patrik.FastNavigation.EditorCloseListener) 11 | * 12 | * @param Bean type of the Grid where {@link org.vaadin.patrik.FastNavigation} is being used 13 | */ 14 | @SuppressWarnings("serial") 15 | public class EditorCloseEvent extends Component.Event { 16 | 17 | private boolean cancelled; 18 | private int rowIndex; 19 | private int colIndex; 20 | private int offset = 0; 21 | 22 | public EditorCloseEvent(Component source, int row, int col, boolean cancel) { 23 | super(source); 24 | rowIndex = row; 25 | colIndex = col; 26 | cancelled = cancel; 27 | Grid grid = (Grid) source; 28 | if (grid.getSelectionModel() instanceof MultiSelectionModel) offset = 1; 29 | } 30 | 31 | /** 32 | * Was editor cancelled = true or saved = false 33 | * 34 | * @return true if editor was cancelled 35 | */ 36 | public boolean wasCancelled() { 37 | return cancelled; 38 | } 39 | 40 | /** 41 | * Get index of the column where editor was closed 42 | * 43 | * @return Index of the column where editor was closed 44 | */ 45 | public int getRow() { 46 | return rowIndex; 47 | } 48 | 49 | /** 50 | * Get index of the column where editor was closed 51 | * 52 | * @return Index of the column where editor was closed 53 | */ 54 | public int getColumnIndex() { 55 | return colIndex-offset; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /GridFastNavigation-demo/src/main/webapp/VAADIN/themes/demo/styles.scss: -------------------------------------------------------------------------------- 1 | @import "../valo/valo.scss"; 2 | 3 | $gray: #d1d1cf; 4 | $green: #40b527; 5 | $darkgreen: darken($green, 30%); 6 | // $v-grid-row-stripe-background-color: $v-grid-row-background-color !default; 7 | $v-grid-header-background-color: #20a020; 8 | 9 | // Prefix all selectors in your theme with .demo 10 | .demo { 11 | 12 | // Include valo theme styles in your theme 13 | @include valo; 14 | 15 | // You can style your demo app right here 16 | .demoContentLayout { 17 | background-color: $gray; 18 | background-image: url(images/radial-gradient.png); 19 | background-size: 90%; 20 | background-position: center center; 21 | background-repeat: no-repeat; 22 | } 23 | 24 | 25 | .v-grid-row.red > td { 26 | color: red; 27 | } 28 | 29 | .v-grid-editor-cells { 30 | .not-editable { 31 | color: white !important; 32 | .v-textfield { 33 | display: none; 34 | } 35 | .v-datefield { 36 | display: none; 37 | } 38 | .v-checkbox { 39 | display: none; 40 | } 41 | .v-filterselect { 42 | display: none; 43 | } 44 | } 45 | } 46 | 47 | .v-grid { 48 | .v-grid-row-selected > td { 49 | color: black !important; 50 | background: yellow !important; 51 | } 52 | .v-grid-editor-selected { 53 | div { 54 | color: black !important; 55 | background: yellow !important; 56 | } 57 | } 58 | } 59 | 60 | .row-edit-disabled > td { 61 | pointer-events: none; 62 | } 63 | 64 | .my-background { 65 | background-color: blue; 66 | color: white; 67 | font-weight: bold; 68 | } 69 | 70 | // You can also customize your component for the demo 71 | // app, but remember that these styles are not part of 72 | // the component. To include built-in CSS for your component, 73 | // edit client/styles.css under java sources 74 | div.GridFastNavigation { 75 | color: $green; 76 | font-size: 50pt; 77 | font-weight: bold; 78 | text-shadow: 0px 3px 0px $darkgreen, 79 | 0px 14px 10px rgba(0,0,0,0.15), 80 | 0px 24px 2px rgba(0,0,0,0.1), 81 | 0px 34px 30px rgba(0,0,0,0.1); 82 | text-align: center; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/java/org/vaadin/patrik/events/CellEditEvent.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.events; 2 | 3 | import com.vaadin.ui.Component; 4 | import com.vaadin.ui.Grid; 5 | 6 | /** 7 | * CellEditEvent is emitted when value in the Editor has been changed 8 | * 9 | * @see org.vaadin.patrik.FastNavigation#addCellEditListener(org.vaadin.patrik.FastNavigation.CellEditListener) 10 | * 11 | * @param Bean type of the Grid where {@link org.vaadin.patrik.FastNavigation} is being used 12 | */ 13 | @SuppressWarnings("serial") 14 | public class CellEditEvent extends Component.Event { 15 | 16 | private int rowIndex; 17 | private int colIndex; 18 | 19 | private String oldData; 20 | private String newData; 21 | private T item; 22 | 23 | public CellEditEvent(Component source, Integer rowIndex, Integer colIndex, String newData, String oldData, T item) { 24 | super(source); 25 | this.rowIndex = rowIndex; 26 | this.colIndex = colIndex; 27 | this.newData = newData; 28 | this.oldData = oldData; 29 | this.item = item; 30 | Grid grid = (Grid) source; 31 | } 32 | 33 | /** 34 | * Get index of the column which was edited 35 | * 36 | * @return Index of the column which is edited 37 | */ 38 | public int getColumnIndex() { 39 | return colIndex; 40 | } 41 | 42 | /** 43 | * Get old value as string from the Editor 44 | * 45 | * @return Old value as string from the Editor 46 | */ 47 | public String getOldData() { 48 | return oldData; 49 | } 50 | 51 | /** 52 | * Get edited value as string from the Editor 53 | * 54 | * @return Edited value as string from the Editor 55 | */ 56 | public String getNewData() { 57 | return newData; 58 | } 59 | 60 | /** 61 | * Get index of the row which was edited 62 | * 63 | * @return Index of the row which is edited 64 | */ 65 | public int getRowIndex() { 66 | return rowIndex; 67 | } 68 | 69 | /** 70 | * Get item which was edited from underlying datasource 71 | * 72 | * @since 2.1.4 73 | * 74 | * @return Item which is edited 75 | */ 76 | public T getItem() { 77 | return item; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /GridFastNavigation-demo/src/main/java/org/vaadin/patrik/demo/DemoColumns.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.demo; 2 | 3 | import java.util.Date; 4 | import java.util.Random; 5 | 6 | public class DemoColumns 7 | { 8 | private static int row = 0; 9 | private String col1; 10 | private String col2; 11 | private Integer col3; 12 | private Double col4; 13 | private Integer col5; 14 | private Integer col6; 15 | private Date col7; // datetime 16 | private Date col8; 17 | private Boolean col9; 18 | private String col10; 19 | 20 | Random rand = new Random(); 21 | 22 | DemoColumns() 23 | { 24 | col1 = "string 1 " + row; 25 | col2 = "string 2 " + row; 26 | col3 = rand.nextInt(row + 10); 27 | col4 = (double)rand.nextInt(row + 10); 28 | col5 = rand.nextInt(row + 10); 29 | col6 = rand.nextInt(row + 10); 30 | col7 = new Date(); 31 | col8 = new Date(); 32 | col9 = false; 33 | col10 = "medium"; 34 | 35 | row++; 36 | } 37 | 38 | 39 | public String getCol1() 40 | { 41 | return col1; 42 | } 43 | public void setCol1(String col1) 44 | { 45 | this.col1 = col1; 46 | } 47 | public String getCol2() 48 | { 49 | return col2; 50 | } 51 | public void setCol2(String col2) 52 | { 53 | this.col2 = col2; 54 | } 55 | public Integer getCol3() 56 | { 57 | return col3; 58 | } 59 | public void setCol3(Integer col3) 60 | { 61 | this.col3 = col3; 62 | } 63 | public Double getCol4() 64 | { 65 | return col4; 66 | } 67 | public void setCol4(Double col4) 68 | { 69 | this.col4 = col4; 70 | } 71 | public Integer getCol5() 72 | { 73 | return col5; 74 | } 75 | public void setCol5(Integer col5) 76 | { 77 | this.col5 = col5; 78 | } 79 | public Integer getCol6() 80 | { 81 | return col6; 82 | } 83 | public void setCol6(Integer col6) 84 | { 85 | this.col6 = col6; 86 | } 87 | public Date getCol7() 88 | { 89 | return col7; 90 | } 91 | public void setCol7(Date col7) 92 | { 93 | this.col7 = col7; 94 | } 95 | public Date getCol8() 96 | { 97 | return col8; 98 | } 99 | public void setCol8(Date col8) 100 | { 101 | this.col8 = col8; 102 | } 103 | public Boolean getCol9() 104 | { 105 | return col9; 106 | } 107 | public void setCol9(Boolean col9) 108 | { 109 | this.col9 = col9; 110 | } 111 | public String getCol10() 112 | { 113 | return col10; 114 | } 115 | public void setCol10(String col10) 116 | { 117 | this.col10 = col10; 118 | } 119 | 120 | 121 | @Override 122 | public String toString() 123 | { 124 | return "DemoColumns [col1=" + col1 + ", col2=" + col2 + ", col3=" + col3 + ", col4=" + col4 + ", col5=" + col5 125 | + ", col6=" + col6 + ", col7=" + col7 + ", col8=" + col8 + ", col10=" + col9 + ", col10=" + col10 126 | + ", rand=" + rand + "]"; 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/java/org/vaadin/patrik/events/CellFocusEvent.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.events; 2 | 3 | import com.vaadin.ui.Component; 4 | import com.vaadin.ui.Grid; 5 | 6 | /** 7 | * CellFocusEvent is emitted when focused cell is changed in the Grid 8 | * 9 | * @see org.vaadin.patrik.FastNavigation#addCellFocusListener(org.vaadin.patrik.FastNavigation.CellFocusListener) 10 | * @see org.vaadin.patrik.FastNavigation#setFocusedCell(int, int) 11 | * @see org.vaadin.patrik.FastNavigation#setFocusedCell(int, int, boolean) 12 | * 13 | * @param Bean type of the Grid where {@link org.vaadin.patrik.FastNavigation} is being used 14 | */ 15 | @SuppressWarnings("serial") 16 | public class CellFocusEvent extends Component.Event { 17 | 18 | private int row; 19 | private int col; 20 | private boolean rowChanged; 21 | private boolean colChanged; 22 | private T item; 23 | private boolean isUserOriginated; 24 | 25 | public CellFocusEvent(Component source, int row, int col, boolean rowChanged, boolean colChanged, T item, boolean isUserOriginated) { 26 | super(source); 27 | this.row = row; 28 | this.col = col; 29 | this.item = item; 30 | this.rowChanged = rowChanged; 31 | this.colChanged = colChanged; 32 | this.isUserOriginated = isUserOriginated; 33 | Grid grid = (Grid) source; 34 | } 35 | 36 | /** 37 | * Get index of the row which was edited 38 | * 39 | * @return Index of the row which is edited, -1 if focus in Header/Footer 40 | */ 41 | public int getRow() { 42 | return row; 43 | } 44 | 45 | /** 46 | * Return true if the row was changed from the previously known value 47 | * 48 | * @return boolean value 49 | */ 50 | public boolean wasRowChanged() { 51 | return rowChanged; 52 | } 53 | 54 | /** 55 | * Get currently focused column index 56 | * 57 | * @return integer value 58 | */ 59 | public int getColumnIndex() { 60 | return col; 61 | } 62 | 63 | /** 64 | * Return true if the column was changed from the previously known value 65 | * 66 | * @return integer value 67 | */ 68 | public boolean wasColumnChanged() { 69 | return colChanged; 70 | } 71 | 72 | /** 73 | * Get item which where focus is from underlying datasource 74 | * 75 | * @since 2.1.5 76 | * 77 | * @return item where focus is, null if focus in Header/Footer 78 | */ 79 | public T getItem() { 80 | return item; 81 | } 82 | 83 | /** 84 | * Returns true if focus changed via UI, false if opened via setFocusedCell method 85 | * 86 | * @see org.vaadin.patrik.FastNavigation#setFocusedCell(int, int) 87 | * 88 | * @return boolean value 89 | * 90 | * @since 2.6.0 91 | */ 92 | public boolean isUserOriginated() { 93 | return isUserOriginated; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/java/org/vaadin/patrik/client/FocusTracker.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.client; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.google.gwt.animation.client.AnimationScheduler; 7 | import com.google.gwt.animation.client.AnimationScheduler.AnimationCallback; 8 | import com.google.gwt.animation.client.AnimationScheduler.AnimationHandle; 9 | import com.vaadin.client.widgets.Grid; 10 | 11 | /** 12 | * Actively track position of focus in Grid using RequestAnimationFrame 13 | */ 14 | public class FocusTracker { 15 | 16 | public interface FocusListener { 17 | public void focusMoved(int currentRow, int currentCol, int lastRow, 18 | int lastCol, boolean isUserOriginated); 19 | } 20 | 21 | private List listeners; 22 | private Grid grid; 23 | private int currentRow; 24 | private int currentCol; 25 | private int lastRow; 26 | private int lastCol; 27 | private boolean run; 28 | protected AnimationHandle handle; 29 | private boolean isUserOriginated = true; 30 | 31 | public FocusTracker(Grid g) { 32 | this.grid = g; 33 | currentRow = GridViolators.getFocusedRow(g); 34 | currentCol = GridViolators.getFocusedCol(g); 35 | lastRow = currentRow; 36 | lastCol = currentCol; 37 | listeners = new ArrayList(); 38 | run = false; 39 | } 40 | 41 | public void start() { 42 | if(!run) { 43 | run = true; 44 | updateLoop.execute(0); 45 | } 46 | } 47 | 48 | public void stop() { 49 | run = false; 50 | } 51 | 52 | public boolean isRunning() { 53 | return run; 54 | } 55 | 56 | public void reset() { 57 | currentRow = -1; 58 | currentCol = -1; 59 | notifyFocusMoved(); 60 | } 61 | 62 | public void wasProgrammatic() { 63 | isUserOriginated = false; 64 | } 65 | 66 | private void notifyFocusMoved() { 67 | for (FocusListener l : listeners) { 68 | l.focusMoved(currentRow, currentCol, lastRow, lastCol, isUserOriginated); 69 | } 70 | isUserOriginated = true; 71 | } 72 | 73 | public void addListener(FocusListener l) { 74 | listeners.add(l); 75 | } 76 | 77 | public void removeListener(FocusListener l) { 78 | listeners.remove(l); 79 | } 80 | 81 | public void clearListeners() { 82 | listeners.clear(); 83 | } 84 | 85 | private AnimationCallback updateLoop = new AnimationCallback() { 86 | @Override 87 | public void execute(double timestamp) { 88 | 89 | int row = GridViolators.getFocusedRow(grid); 90 | int col = GridViolators.getFocusedCol(grid); 91 | 92 | if (row != currentRow || col != currentCol) { 93 | lastRow = currentRow; 94 | currentRow = row; 95 | lastCol = currentCol; 96 | currentCol = col; 97 | notifyFocusMoved(); 98 | } 99 | 100 | if (run) { 101 | handle = AnimationScheduler.get().requestAnimationFrame(updateLoop); 102 | } 103 | } 104 | }; 105 | 106 | } 107 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/java/org/vaadin/patrik/client/GridViolators.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.client; 2 | 3 | import java.util.Map; 4 | 5 | import com.google.gwt.dom.client.DivElement; 6 | import com.google.gwt.user.client.ui.Widget; 7 | import com.vaadin.client.widgets.Grid; 8 | import com.vaadin.client.widgets.Grid.Column; 9 | import com.vaadin.client.widgets.Grid.Editor; 10 | 11 | public class GridViolators { 12 | 13 | // ======================================================================== 14 | // Violators - access Grid internals irrespective of visibility 15 | // ======================================================================== 16 | 17 | public static native final boolean isEditorReallyActive(Editor editor) /*-{ 18 | var state = editor.@com.vaadin.client.widgets.Grid.Editor::state; 19 | var ordinal = state.@com.vaadin.client.widgets.Grid.Editor.State::ordinal()(); 20 | return ordinal == 3; 21 | }-*/; 22 | 23 | public static native final boolean isEditorReallyClosed(Editor editor) /*-{ 24 | var state = editor.@com.vaadin.client.widgets.Grid.Editor::state; 25 | var ordinal = state.@com.vaadin.client.widgets.Grid.Editor.State::ordinal()(); 26 | return ordinal == 0; 27 | }-*/; 28 | 29 | public static native final Map, Widget> getEditorColumnToWidgetMap(Editor editor) /*-{ 30 | return editor.@com.vaadin.client.widgets.Grid.Editor::columnToWidget; 31 | }-*/; 32 | 33 | public static native final int getFocusedRow(Grid grid) /*-{ 34 | var cfh = grid.@com.vaadin.client.widgets.Grid::cellFocusHandler; 35 | var row = cfh.@com.vaadin.client.widgets.Grid.CellFocusHandler::rowWithFocus; 36 | var contWithFocus= cfh.@com.vaadin.client.widgets.Grid.CellFocusHandler::containerWithFocus; 37 | var escallator = grid.@com.vaadin.client.widgets.Grid::getEscalator()(); 38 | var conBody = escallator.@com.vaadin.client.widgets.Escalator::getBody()(); 39 | if (contWithFocus == conBody) 40 | return row; 41 | else 42 | return -1; 43 | }-*/; 44 | 45 | public static native final int getFocusedCol(Grid grid) /*-{ 46 | var cfh = grid.@com.vaadin.client.widgets.Grid::cellFocusHandler; 47 | var cell = cfh.@com.vaadin.client.widgets.Grid.CellFocusHandler::getFocusedCell()(); 48 | var col = cell.@com.vaadin.client.widget.escalator.Cell::getColumn()(); 49 | return col; 50 | }-*/; 51 | 52 | public static native final int getEditorColumn(Editor editor) /*-{ 53 | return editor.@com.vaadin.client.widgets.Grid.Editor::focusedColumnIndexDOM; 54 | }-*/; 55 | 56 | public static native final void setFocusedCell(Grid grid, int rowIndex, int columnIndexDOM) /*-{ 57 | grid.@com.vaadin.client.widgets.Grid::focusCell(II)(rowIndex,columnIndexDOM); 58 | }-*/; 59 | 60 | public static native final void redrawEditor(Grid grid) /*-{ 61 | var editor = grid.@com.vaadin.client.widgets.Grid::getEditor()(); 62 | editor.@com.vaadin.client.widgets.Grid.Editor::showOverlay()(); 63 | }-*/; 64 | 65 | public static native final DivElement getEditorCellWrapper(Grid grid) /*-{ 66 | var editor = grid.@com.vaadin.client.widgets.Grid::getEditor()(); 67 | return editor.@com.vaadin.client.widgets.Grid.Editor::cellWrapper; 68 | }-*/; 69 | 70 | public static native final DivElement getEditorOverlay(Grid grid) /*-{ 71 | var editor = grid.@com.vaadin.client.widgets.Grid::getEditor()(); 72 | return editor.@com.vaadin.client.widgets.Grid.Editor::editorOverlay; 73 | }-*/; 74 | 75 | public static native final DivElement getFrozenCellWrapper(Grid grid) /*-{ 76 | var editor = grid.@com.vaadin.client.widgets.Grid::getEditor()(); 77 | return editor.@com.vaadin.client.widgets.Grid.Editor::frozenCellWrapper; 78 | }-*/; 79 | 80 | public static native final DivElement getEditorErrorMessage(Grid grid) /*-{ 81 | var editor = grid.@com.vaadin.client.widgets.Grid::getEditor()(); 82 | return editor.@com.vaadin.client.widgets.Grid.Editor::message; 83 | }-*/; 84 | } -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/java/org/vaadin/patrik/events/EditorOpenEvent.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.events; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.vaadin.ui.Component; 7 | import com.vaadin.ui.Grid; 8 | import com.vaadin.ui.Grid.Column; 9 | 10 | /** 11 | * Event used to notify of editor being opened. Can be used 12 | * to disable some columns (in other words: make them read-only) 13 | * 14 | * @see org.vaadin.patrik.FastNavigation#addEditorOpenListener(org.vaadin.patrik.FastNavigation.EditorOpenListener) 15 | * 16 | * @param Bean type of the Grid where {@link org.vaadin.patrik.FastNavigation} is being used 17 | */ 18 | @SuppressWarnings("serial") 19 | public class EditorOpenEvent extends Component.Event { 20 | 21 | private int rowIndex; 22 | private int colIndex; 23 | private T item; 24 | private Integer[] disabledCols; 25 | private Grid grid; 26 | private int keyCode; 27 | private boolean isUserOriginated; 28 | 29 | public EditorOpenEvent(Component source, int row, int col, T item, int keyCode, boolean isUserOriginated) { 30 | super(source); 31 | rowIndex = row; 32 | colIndex = col; 33 | this.item = item; 34 | this.keyCode = keyCode; 35 | this.isUserOriginated = isUserOriginated; 36 | grid = (Grid) source; 37 | } 38 | 39 | /** 40 | * Get key code of the shortcut or key that triggered the opening of the Editor. 41 | * 42 | * @return Key code or -1 if no key event was associated with this opening. 43 | * 44 | * @since 2.6.0 45 | */ 46 | public int getKeyCode() { 47 | return keyCode; 48 | } 49 | 50 | /** 51 | * Get index of the row where editor was opened 52 | * 53 | * @return Index of the row where editor was opened 54 | */ 55 | public int getRow() { 56 | return rowIndex; 57 | } 58 | 59 | /** 60 | * Get index of the column where editor was opened 61 | * 62 | * @return Index of the column where editor was opened 63 | */ 64 | public int getColumnIndex() { 65 | return colIndex; 66 | } 67 | 68 | /** 69 | * Set additional columns that should be disable when Editor opens 70 | * 71 | * @param columns Columns to be set disabled 72 | */ 73 | public void disableColumns(Integer... columns) { 74 | disabledCols = columns; 75 | for (int col : disabledCols) { 76 | if (col < 0 || col > grid.getColumns().size()) { 77 | throw new IllegalArgumentException("Column index "+col+" out of bounds"); 78 | } 79 | } 80 | } 81 | 82 | /** 83 | * Set all columns disabled. Can be used for example if you want to conditionally 84 | * disable editing of a row. 85 | * 86 | * @since 2.3.2 87 | */ 88 | public void disableAllColumns() { 89 | List disabled = new ArrayList<>(); 90 | Integer i=0; 91 | for (Column col : grid.getColumns()) { 92 | disabled.add(i); 93 | i++; 94 | } 95 | Integer[] dis = disabled.toArray(new Integer[0]); 96 | disableColumns(dis); 97 | } 98 | 99 | /** 100 | * Returns the additional columns that should be disable when Editor opens (see: disableColumns). 101 | * This method is used internally. Note, if you have set columns non editable or disabled fields 102 | * via other API's, they are not counted. 103 | * 104 | * @return returns additional columns to be disabled 105 | */ 106 | public Integer[] getDisabledColumns() { 107 | return disabledCols; 108 | } 109 | 110 | /** 111 | * Get item which was opened from underlying datasource 112 | * 113 | * @return Item which is edited 114 | */ 115 | public T getItem() { 116 | return item; 117 | } 118 | 119 | /** 120 | * Returns true if opening was done via UI, false if opened via editRow method 121 | * 122 | * @return boolean value 123 | * 124 | * @since 2.6.0 125 | */ 126 | public boolean isUserOriginated() { 127 | return isUserOriginated; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/java/org/vaadin/patrik/client/Keys.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.client; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | import com.google.gwt.event.dom.client.KeyCodes; 7 | import com.google.gwt.user.client.Event; 8 | 9 | public final class Keys { 10 | private static final Set alphaNumSet; 11 | 12 | private static final Set rowChangeKeys; 13 | 14 | private static final Set colChangeKeys; 15 | 16 | static { 17 | alphaNumSet = new HashSet(); 18 | 19 | for (int i = KeyCodes.KEY_A; i <= KeyCodes.KEY_Z; i++) { 20 | alphaNumSet.add(i); 21 | } 22 | for (int i = KeyCodes.KEY_ZERO; i <= KeyCodes.KEY_NINE; i++) { 23 | alphaNumSet.add(i); 24 | } 25 | for (int i = KeyCodes.KEY_NUM_ZERO; i <= KeyCodes.KEY_NUM_NINE; i++) { 26 | alphaNumSet.add(i); 27 | } 28 | 29 | rowChangeKeys = new HashSet(); 30 | rowChangeKeys.add(KeyCodes.KEY_UP); 31 | rowChangeKeys.add(KeyCodes.KEY_DOWN); 32 | rowChangeKeys.add(KeyCodes.KEY_PAGEUP); 33 | rowChangeKeys.add(KeyCodes.KEY_PAGEDOWN); 34 | 35 | colChangeKeys = new HashSet(); 36 | colChangeKeys.add(KeyCodes.KEY_TAB); 37 | 38 | } 39 | 40 | public static void setEnterBehavior(boolean changeColumnByEnter) { 41 | if (changeColumnByEnter) { 42 | colChangeKeys.add(KeyCodes.KEY_ENTER); 43 | colChangeKeys.add(KeyCodes.KEY_MAC_ENTER); 44 | } else { 45 | rowChangeKeys.add(KeyCodes.KEY_ENTER); 46 | rowChangeKeys.add(KeyCodes.KEY_MAC_ENTER); 47 | } 48 | } 49 | 50 | public static int translateNumKey(int keycode) { 51 | if (keycode >= 96 && keycode <= 105) return keycode - 48; 52 | else return keycode; 53 | } 54 | 55 | public static boolean isUpDownArrowKey(int keycode) { 56 | if(keycode == KeyCodes.KEY_UP || keycode == KeyCodes.KEY_DOWN || keycode == KeyCodes.KEY_PAGEUP || keycode == KeyCodes.KEY_PAGEDOWN) { 57 | return true; 58 | } 59 | return false; 60 | } 61 | 62 | public static boolean isDelKey(int keycode) { 63 | if(keycode == KeyCodes.KEY_DELETE) { 64 | return true; 65 | } 66 | return false; 67 | } 68 | 69 | public static boolean isHomeKey(int keycode) { 70 | if(keycode == KeyCodes.KEY_HOME) { 71 | return true; 72 | } 73 | return false; 74 | } 75 | 76 | public static boolean isEndKey(int keycode) { 77 | if(keycode == KeyCodes.KEY_END) { 78 | return true; 79 | } 80 | return false; 81 | } 82 | 83 | 84 | public static boolean isSpaceKey(int keycode) { 85 | if (keycode == KeyCodes.KEY_SPACE) { 86 | return true; 87 | } 88 | return false; 89 | } 90 | 91 | /** 92 | * Test if keycode is one of the alpha numeric keys [0-9a-zA-Z] 93 | * 94 | * @param keyCode Key code to be tested 95 | * @return true if key is alphanumeric 96 | */ 97 | public static boolean isAlphaNumericKey(int keyCode) { 98 | return alphaNumSet.contains(keyCode); 99 | } 100 | 101 | /** 102 | * Test if keycode is defined as a row change key 103 | * 104 | * @param keyCode Key code to be tested 105 | * @return true if key is one of the row change keys 106 | */ 107 | public static boolean isRowChangeKey(int keyCode) { 108 | return rowChangeKeys.contains(keyCode); 109 | } 110 | 111 | /** 112 | * Test if keycode is defined as a column change key 113 | * 114 | * @param keyCode Key code to be tested 115 | * @return true if key is one of the column change keys 116 | */ 117 | public static boolean isColumnChangeKey(int keyCode) { 118 | return colChangeKeys.contains(keyCode); 119 | } 120 | 121 | 122 | /** 123 | * Test if key event contained a mofifier key (i.e. Ctrl, Shift or Alt) 124 | * 125 | * @param event DOM Event 126 | * @return true if key event contained modifier key 127 | */ 128 | public static boolean isModifierKey(Event event) { 129 | return event.getShiftKey() || event.getCtrlKey() || event.getAltKey(); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/java/org/vaadin/patrik/client/DeleteButtonRendererConnector.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.client; 2 | 3 | import org.vaadin.patrik.shared.DeleteButtonRendererServerRpc; 4 | import org.vaadin.patrik.shared.DeleteButtonRendererState; 5 | 6 | import com.google.gwt.core.shared.GWT; 7 | import com.google.gwt.dom.client.Element; 8 | import com.google.gwt.event.dom.client.ClickEvent; 9 | import com.google.gwt.event.dom.client.ClickHandler; 10 | import com.google.gwt.event.shared.HandlerRegistration; 11 | import com.google.gwt.user.client.Timer; 12 | import com.google.gwt.user.client.ui.Button; 13 | import com.vaadin.client.MouseEventDetailsBuilder; 14 | import com.vaadin.client.communication.RpcProxy; 15 | import com.vaadin.client.communication.StateChangeEvent; 16 | import com.vaadin.client.connectors.ClickableRendererConnector; 17 | import com.vaadin.client.renderers.ClickableRenderer; 18 | import com.vaadin.client.renderers.ClickableRenderer.RendererClickHandler; 19 | import com.vaadin.client.renderers.Renderer; 20 | import com.vaadin.client.widget.grid.RendererCellReference; 21 | import com.vaadin.shared.MouseEventDetails; 22 | import com.vaadin.shared.ui.Connect; 23 | 24 | import elemental.json.JsonObject; 25 | 26 | @Connect(org.vaadin.patrik.DeleteButtonRenderer.class) 27 | public class DeleteButtonRendererConnector extends ClickableRendererConnector { 28 | DeleteButtonRendererServerRpc rpc = RpcProxy.create(DeleteButtonRendererServerRpc.class, this); 29 | 30 | public class DeleteButtonClientRenderer extends ClickableRenderer { 31 | 32 | private boolean htmlContentAllowed = false; 33 | 34 | @Override 35 | public Button createWidget() { 36 | Button b = GWT.create(Button.class); 37 | 38 | b.addClickHandler(new ClickHandler() { 39 | @Override 40 | public void onClick(ClickEvent event) { 41 | Timer t = null; 42 | String style = b.getStyleName(); 43 | if (style != null && style.contains("delete-confirm")) { 44 | // If button is in cofirmed state, timer needs to be stopped 45 | // and click event needs to be emitted 46 | if (t != null) t.cancel(); 47 | b.removeStyleName("delete-confirm"); 48 | if (htmlContentAllowed) { 49 | b.setHTML(getState().delete); 50 | } else { 51 | b.setText(getState().delete); 52 | } 53 | MouseEventDetails mouseEventDetails = MouseEventDetailsBuilder 54 | .buildMouseEventDetails(event.getNativeEvent(), 55 | b.getElement()); 56 | Element e = b.getElement(); 57 | rpc.onClick(e.getPropertyString("rowKey"),mouseEventDetails); 58 | } else { 59 | // At first click we change button to confirm mode, 60 | // change text accordingly and add style name, so 61 | // that accent can be defined in theme 62 | if (htmlContentAllowed) { 63 | b.setHTML(getState().confirm); 64 | } else { 65 | b.setText(getState().confirm); 66 | } 67 | b.setStyleName("delete-confirm"); 68 | // Set timer 10 sec, if not clicked by then, go 69 | // back to normal mode 70 | t = new Timer() { 71 | @Override 72 | public void run() { 73 | b.removeStyleName("delete-confirm"); 74 | if (htmlContentAllowed) { 75 | b.setHTML(getState().delete); 76 | } else { 77 | b.setText(getState().delete); 78 | } 79 | Element e = b.getElement(); 80 | } 81 | }; 82 | t.schedule(10000); 83 | } 84 | event.stopPropagation(); 85 | } 86 | }); 87 | b.setStylePrimaryName("v-nativebutton"); 88 | return b; 89 | } 90 | 91 | public void setHtmlContentAllowed(boolean htmlContentAllowed) { 92 | this.htmlContentAllowed = htmlContentAllowed; 93 | } 94 | 95 | public boolean isHtmlContentAllowed() { 96 | return htmlContentAllowed; 97 | } 98 | 99 | @Override 100 | public void render(RendererCellReference cell, Boolean enable, Button button) { 101 | 102 | Element e = button.getElement(); 103 | 104 | if(e.getPropertyString("rowKey") != getRowKey((JsonObject) cell.getRow())) { 105 | e.setPropertyString("rowKey", 106 | getRowKey((JsonObject) cell.getRow())); 107 | } 108 | 109 | String style = button.getStyleName(); 110 | if (style != null && style.contains("delete-confirm")) { 111 | button.removeStyleName("delete-confirm"); 112 | } 113 | if (htmlContentAllowed) { 114 | button.setHTML(getState().delete); 115 | } else { 116 | button.setText(getState().delete); 117 | } 118 | button.setEnabled(enable); 119 | } 120 | } 121 | 122 | @Override 123 | public DeleteButtonClientRenderer getRenderer() { 124 | return (DeleteButtonClientRenderer) super.getRenderer(); 125 | } 126 | 127 | @Override 128 | protected Renderer createRenderer() { 129 | return new DeleteButtonClientRenderer(); 130 | } 131 | 132 | @Override 133 | protected HandlerRegistration addClickHandler( 134 | RendererClickHandler handler) { 135 | return getRenderer().addClickHandler(handler); 136 | } 137 | 138 | @Override 139 | public DeleteButtonRendererState getState() { 140 | return (DeleteButtonRendererState) super.getState(); 141 | } 142 | 143 | @Override 144 | public void onStateChanged(StateChangeEvent stateChangeEvent) { 145 | super.onStateChanged(stateChangeEvent); 146 | getRenderer().setHtmlContentAllowed(getState().htmlContentAllowed); 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/java/org/vaadin/patrik/DeleteButtonRenderer.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | import org.vaadin.patrik.shared.DeleteButtonRendererServerRpc; 6 | import org.vaadin.patrik.shared.DeleteButtonRendererState; 7 | 8 | import com.vaadin.event.ConnectorEventListener; 9 | import com.vaadin.event.MouseEvents.ClickEvent; 10 | import com.vaadin.shared.MouseEventDetails; 11 | import com.vaadin.shared.Registration; 12 | import com.vaadin.ui.Grid; 13 | import com.vaadin.ui.Grid.Column; 14 | import com.vaadin.ui.renderers.ButtonRenderer; 15 | import com.vaadin.ui.renderers.ClickableRenderer; 16 | import com.vaadin.ui.renderers.ClickableRenderer.RendererClickEvent; 17 | import com.vaadin.util.ReflectTools; 18 | 19 | /** 20 | * This is not maintained anymore. DeleteButtonRenderer has moved to Grid RenderersCollection add-on 21 | * 22 | * @deprecated since 2.1.4 23 | * 24 | * @param Type of the bean 25 | * 26 | * @author Tatu Lund 27 | */ 28 | @Deprecated 29 | public class DeleteButtonRenderer extends ClickableRenderer { 30 | 31 | /** 32 | * An interface for listening to {@link DeleteRendererClickEvent renderer click 33 | * events}. 34 | * 35 | */ 36 | @FunctionalInterface 37 | public interface DeleteRendererClickListener extends ConnectorEventListener { 38 | 39 | static final Method CLICK_METHOD = ReflectTools.findMethod( 40 | DeleteRendererClickListener.class, "click", DeleteRendererClickEvent.class); 41 | 42 | /** 43 | * Called when a rendered button is clicked. 44 | * 45 | * @param event 46 | * the event representing the click 47 | */ 48 | void click(DeleteRendererClickEvent event); 49 | } 50 | 51 | /** 52 | * An event fired when a clickable widget rendered by a DeleteButtonRenderer is 53 | * clicked. 54 | * 55 | * @param 56 | * the item type associated with this click event 57 | */ 58 | public static class DeleteRendererClickEvent extends ClickEvent { 59 | 60 | private final T item; 61 | private final Column column; 62 | 63 | protected DeleteRendererClickEvent(Grid source, T item, 64 | Column column, MouseEventDetails mouseEventDetails) { 65 | super(source, mouseEventDetails); 66 | this.item = item; 67 | this.column = column; 68 | } 69 | 70 | /** 71 | * Returns the item of the row where the click event originated. 72 | * 73 | * @return the item of the clicked row 74 | */ 75 | public T getItem() { 76 | return item; 77 | } 78 | 79 | /** 80 | * Returns the {@link Column} where the click event originated. 81 | * 82 | * @return the column of the click event 83 | */ 84 | public Column getColumn() { 85 | return column; 86 | } 87 | } 88 | 89 | 90 | /** 91 | * Creates a new button renderer 92 | * and e.g. localized Strings for meaning delete and confirm 93 | * 94 | * @param delete 95 | * text meaning delete 96 | * @param confirm 97 | * text meaning confirm 98 | */ 99 | public DeleteButtonRenderer(String delete, String confirm) { 100 | super(Boolean.class, ""); 101 | getState().delete = delete; 102 | getState().confirm = confirm; 103 | setupRpc(); 104 | } 105 | 106 | /** 107 | * Creates a new delete button renderer and adds the given click listener to it 108 | * and e.g. localized Strings for meaning delete and confirm 109 | * 110 | * @param listener 111 | * the click listener to register 112 | * @param delete 113 | * text meaning delete 114 | * @param confirm 115 | * text meaning confirm 116 | */ 117 | public DeleteButtonRenderer(DeleteRendererClickListener listener, 118 | String delete, String confirm) { 119 | this(delete, confirm); 120 | addClickListener(listener); 121 | } 122 | 123 | /** 124 | * Creates a new delete button renderer. 125 | * 126 | * Delete button renderer creates two stage Delete - Confirm button 127 | * When in confirm state "delete-confirm" stylename is set. 128 | */ 129 | public DeleteButtonRenderer() { 130 | this("Delete","Confirm"); 131 | } 132 | 133 | /** 134 | * Creates a new button renderer and adds the given click listener to it. 135 | * 136 | * @param listener 137 | * the click listener to register 138 | */ 139 | public DeleteButtonRenderer(DeleteRendererClickListener listener) { 140 | this(listener, "Delete", "Confirm"); 141 | } 142 | 143 | private void setupRpc() { 144 | registerRpc(new DeleteButtonRendererServerRpc() { 145 | public void onClick(String rowKey, MouseEventDetails mouseEventDetails) { 146 | Grid grid = getParentGrid(); 147 | Object item = grid.getDataCommunicator().getKeyMapper().get(rowKey); 148 | Column column = getParent(); 149 | fireEvent(new DeleteRendererClickEvent(grid,item,column,mouseEventDetails)); 150 | } 151 | }); 152 | } 153 | 154 | @Override 155 | public String getNullRepresentation() { 156 | return super.getNullRepresentation(); 157 | } 158 | 159 | @Override 160 | protected DeleteButtonRendererState getState() { 161 | return (DeleteButtonRendererState) super.getState(); 162 | } 163 | 164 | @Override 165 | protected DeleteButtonRendererState getState(boolean markAsDirty) { 166 | return (DeleteButtonRendererState) super.getState(markAsDirty); 167 | } 168 | 169 | /** 170 | * Sets whether the data should be rendered as HTML (instead of text). 171 | *

172 | * By default everything is rendered as text. 173 | * 174 | * @param htmlContentAllowed 175 | * true to render as HTML, false to 176 | * render as text 177 | */ 178 | public void setHtmlContentAllowed(boolean htmlContentAllowed) { 179 | getState().htmlContentAllowed = htmlContentAllowed; 180 | } 181 | 182 | /** 183 | * Gets whether the data should be rendered as HTML (instead of text). 184 | *

185 | * By default everything is rendered as text. 186 | * 187 | * @return true if the renderer renders a HTML, 188 | * false if the content is rendered as text 189 | */ 190 | public boolean isHtmlContentAllowed() { 191 | return getState(false).htmlContentAllowed; 192 | } 193 | 194 | public Registration addClickListener(DeleteRendererClickListener listener) { 195 | return addListener(DeleteRendererClickEvent.class, listener, 196 | DeleteRendererClickListener.CLICK_METHOD); 197 | } 198 | 199 | 200 | } 201 | -------------------------------------------------------------------------------- /GridFastNavigation-demo/src/main/java/org/vaadin/patrik/demo/DemoUI.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.demo; 2 | 3 | import javax.servlet.annotation.WebServlet; 4 | 5 | import com.vaadin.annotations.PreserveOnRefresh; 6 | import com.vaadin.annotations.Push; 7 | import com.vaadin.annotations.Theme; 8 | import com.vaadin.annotations.Title; 9 | import com.vaadin.annotations.VaadinServletConfiguration; 10 | import com.vaadin.icons.VaadinIcons; 11 | import com.vaadin.server.VaadinRequest; 12 | import com.vaadin.server.VaadinServlet; 13 | import com.vaadin.shared.communication.PushMode; 14 | import com.vaadin.shared.ui.ui.Transport; 15 | import com.vaadin.ui.Button; 16 | import com.vaadin.ui.Grid.SelectionMode; 17 | import com.vaadin.ui.HorizontalLayout; 18 | import com.vaadin.ui.UI; 19 | import com.vaadin.ui.VerticalLayout; 20 | import com.vaadin.ui.themes.ValoTheme; 21 | 22 | //@Push 23 | //@PreserveOnRefresh 24 | @Theme("demo") 25 | @Title("GridFastNavigation Add-on Demo") 26 | @SuppressWarnings("serial") 27 | public class DemoUI extends UI { 28 | 29 | @WebServlet(value = "/*", asyncSupported = true) 30 | @VaadinServletConfiguration(productionMode = false, ui = DemoUI.class, heartbeatInterval=5, closeIdleSessions=true, widgetset = "org.vaadin.patrik.demo.DemoWidgetSet") 31 | public static class Servlet extends VaadinServlet { 32 | } 33 | 34 | @Override 35 | protected void init(VaadinRequest vaadinRequest) { 36 | // getPushConfiguration().setPushMode(PushMode.DISABLED); 37 | final VerticalLayout layout = new VerticalLayout(); 38 | layout.setSizeFull(); 39 | 40 | MessageGrid messageGrid = new MessageGrid(); 41 | DemoFastGrid demoGrid = new DemoFastGrid(messageGrid); 42 | 43 | Button clearButton = new Button("Clear"); 44 | clearButton.addClickListener(e -> { 45 | messageGrid.clear(); 46 | }); 47 | 48 | Button addButton = new Button(); 49 | addButton.setIcon(VaadinIcons.PLUS_CIRCLE); // Add Row 50 | addButton.addClickListener(e -> { 51 | demoGrid.addBlankRow(); 52 | }); 53 | addButton.setDescription("Add a new row"); 54 | 55 | Button rowValidationButton = new Button(); 56 | rowValidationButton.setIcon(VaadinIcons.CHECK_CIRCLE_O); 57 | rowValidationButton.setStyleName(ValoTheme.BUTTON_QUIET); 58 | rowValidationButton.addClickListener(e -> { 59 | demoGrid.getNavigation().setRowValidation(!demoGrid.getNavigation().getRowValidation()); 60 | if (!demoGrid.getNavigation().getRowValidation()) rowValidationButton.setStyleName(ValoTheme.BUTTON_QUIET); 61 | else rowValidationButton.setStyleName(ValoTheme.BUTTON_FRIENDLY); 62 | }); 63 | rowValidationButton.setDescription("Toggle rowValidation"); 64 | 65 | Button rowOpenClickButton = new Button(); 66 | rowOpenClickButton.setIcon(VaadinIcons.FOLDER_OPEN_O); 67 | rowOpenClickButton.setStyleName(ValoTheme.BUTTON_FRIENDLY); 68 | rowOpenClickButton.addClickListener(e -> { 69 | demoGrid.getNavigation().setOpenEditorWithSingleClick(!demoGrid.getNavigation().getOpenEditorWithSingleClick()); 70 | if (!demoGrid.getNavigation().getOpenEditorWithSingleClick()) rowOpenClickButton.setStyleName(ValoTheme.BUTTON_QUIET); 71 | else rowOpenClickButton.setStyleName(ValoTheme.BUTTON_FRIENDLY); 72 | }); 73 | rowOpenClickButton.setDescription("Toggle openEditorWithSingleClick"); 74 | 75 | Button rowOpenByTypingButton = new Button(); 76 | rowOpenByTypingButton.setIcon(VaadinIcons.KEYBOARD_O); 77 | rowOpenByTypingButton.setStyleName(ValoTheme.BUTTON_FRIENDLY); 78 | rowOpenByTypingButton.addClickListener(e -> { 79 | demoGrid.getNavigation().setOpenEditorOnTyping(!demoGrid.getNavigation().getOpenEditorOnTyping()); 80 | if (!demoGrid.getNavigation().getOpenEditorOnTyping()) rowOpenByTypingButton.setStyleName(ValoTheme.BUTTON_QUIET); 81 | else rowOpenByTypingButton.setStyleName(ValoTheme.BUTTON_FRIENDLY); 82 | }); 83 | rowOpenByTypingButton.setDescription("Toggle openEditorOnTyping"); 84 | 85 | Button openEditorButton = new Button(); 86 | openEditorButton.setIcon(VaadinIcons.INPUT); 87 | openEditorButton.setDescription("Open editor at 1,3"); 88 | openEditorButton.setStyleName(ValoTheme.BUTTON_FRIENDLY); 89 | openEditorButton.addClickListener(e->{ 90 | demoGrid.openEditor(); 91 | }); 92 | 93 | Button disableGridEditButton = new Button(); 94 | disableGridEditButton.setIcon(VaadinIcons.PENCIL); 95 | disableGridEditButton.setDescription("Toggle Grid Editing"); 96 | disableGridEditButton.setStyleName(ValoTheme.BUTTON_FRIENDLY); 97 | disableGridEditButton.addClickListener(e->{ 98 | if (demoGrid.getEditor().isEnabled()) { 99 | if (demoGrid.getEditor().isOpen()) { 100 | demoGrid.getEditor().cancel(); 101 | } 102 | demoGrid.getEditor().setEnabled(false); 103 | openEditorButton.setEnabled(false); 104 | disableGridEditButton.setStyleName(ValoTheme.BUTTON_QUIET); 105 | } else { 106 | demoGrid.getEditor().setEnabled(true); 107 | openEditorButton.setEnabled(true); 108 | disableGridEditButton.setStyleName(ValoTheme.BUTTON_FRIENDLY); 109 | } 110 | }); 111 | 112 | Button moveSelectionButton = new Button(); 113 | moveSelectionButton.setIcon(VaadinIcons.BULLSEYE); 114 | moveSelectionButton.setDescription("Toggle select follow"); 115 | moveSelectionButton.setStyleName(ValoTheme.BUTTON_QUIET); 116 | moveSelectionButton.addClickListener(e->{ 117 | if (demoGrid.moveSelection) { 118 | demoGrid.moveSelection = false; 119 | demoGrid.deselectAll(); 120 | demoGrid.setSelectionMode(SelectionMode.NONE); 121 | moveSelectionButton.setStyleName(ValoTheme.BUTTON_QUIET); 122 | } else { 123 | demoGrid.moveSelection = true; 124 | demoGrid.setSelectionMode(SelectionMode.SINGLE); 125 | moveSelectionButton.setStyleName(ValoTheme.BUTTON_FRIENDLY); 126 | } 127 | }); 128 | 129 | Button resetFocusButton = new Button(); 130 | resetFocusButton.setIcon(VaadinIcons.CORNER_UPPER_LEFT); 131 | resetFocusButton.setDescription("Reset focust to 0,1"); 132 | resetFocusButton.setStyleName(ValoTheme.BUTTON_FRIENDLY); 133 | resetFocusButton.addClickListener(e->{ 134 | demoGrid.resetFocus(); 135 | }); 136 | 137 | Button pushButton = new Button("Push"); 138 | pushButton.setDisableOnClick(true); 139 | pushButton.addClickListener(e -> { 140 | getPushConfiguration().setPushMode(PushMode.AUTOMATIC); 141 | getPushConfiguration().setTransport(Transport.LONG_POLLING); 142 | }); 143 | 144 | HorizontalLayout buttons = new HorizontalLayout(); 145 | buttons.addComponents(addButton,rowValidationButton,rowOpenClickButton,rowOpenByTypingButton,disableGridEditButton,moveSelectionButton,resetFocusButton,openEditorButton,pushButton); 146 | 147 | layout.setMargin(true); 148 | layout.setSpacing(true); 149 | layout.addComponent(demoGrid); 150 | layout.addComponent(buttons); 151 | layout.addComponent(messageGrid); 152 | layout.addComponent(clearButton); 153 | layout.setSizeFull(); 154 | layout.setExpandRatio(demoGrid, 10); 155 | layout.setExpandRatio(buttons, 1); 156 | layout.setExpandRatio(clearButton, 1); 157 | layout.setExpandRatio(messageGrid, 6); 158 | 159 | setContent(layout); 160 | } 161 | } -------------------------------------------------------------------------------- /GridFastNavigation-addon/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | org.vaadin.patrik 6 | GridFastNavigation 7 | jar 8 | 2.6.4 9 | GridFastNavigation Add-on 10 | 11 | 12 | UTF-8 13 | 1.8 14 | 1.8 15 | 8.14.3 16 | ${vaadin.version} 17 | 18 | 19 | ${project.version} 20 | 21 | ${project.name} 22 | ${project.organization.name} 23 | Apache License 2.0 24 | ${project.artifactId}-${project.version}.jar 25 | 26 | 27 | 28 | Tatu Lund 29 | https://github.com/tatulund/GridFastNavigation/ 30 | 31 | 32 | 33 | git://github.com/tatulund/GridFastNavigation.git 34 | scm:git:git://github.com/tatulund/GridFastNavigation.git 35 | scm:git:ssh://git@github.com:/tatulund/${ComponentClassName}.git 36 | GridFastNavigation add-on for Vaadin 37 | 38 | 39 | 40 | GitHub 41 | https://github.com/tatulund/GridFastNavigation/issues 42 | 43 | 44 | 45 | 46 | Apache 2 47 | https://www.apache.org/licenses/LICENSE-2.0.txt 48 | repo 49 | 50 | 51 | 52 | 53 | 54 | vaadin-addons 55 | https://maven.vaadin.com/vaadin-addons 56 | 57 | 58 | vaadin-snapshots 59 | https://oss.sonatype.org/content/repositories/vaadin-snapshots/ 60 | 61 | false 62 | 63 | 64 | true 65 | 66 | 67 | 68 | 69 | 70 | 71 | vaadin-snapshots 72 | https://oss.sonatype.org/content/repositories/vaadin-snapshots/ 73 | 74 | false 75 | 76 | 77 | true 78 | 79 | 80 | 81 | 82 | 83 | 84 | com.vaadin 85 | vaadin-server 86 | ${vaadin.version} 87 | 88 | 89 | com.vaadin 90 | vaadin-client 91 | ${vaadin.version} 92 | provided 93 | 94 | 95 | junit 96 | junit 97 | 4.13.1 98 | test 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | org.apache.maven.plugins 107 | maven-compiler-plugin 108 | 3.0 109 | 110 | 111 | 112 | org.apache.maven.plugins 113 | maven-jar-plugin 114 | 2.3.1 115 | 116 | 117 | true 118 | 119 | true 120 | true 121 | 122 | 123 | 124 | 1 125 | ${Vaadin-License-Title} 126 | org.vaadin.patrik.GridFastNavigationWidgetSet 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | org.apache.maven.plugins 148 | maven-source-plugin 149 | 2.2.1 150 | 151 | 152 | attach-sources 153 | 154 | jar 155 | 156 | 157 | 158 | 159 | 160 | 161 | org.apache.maven.plugins 162 | maven-assembly-plugin 163 | 2.2.1 164 | 165 | false 166 | 167 | assembly/assembly.xml 168 | 169 | 170 | 171 | 172 | 173 | single 174 | 175 | install 176 | 177 | 178 | 179 | 180 | 181 | 182 | org.apache.maven.plugins 183 | maven-surefire-plugin 184 | 2.14.1 185 | 186 | 187 | 188 | 190 | 191 | 192 | src/main/java 193 | 194 | rebel.xml 195 | 196 | 197 | 198 | src/main/resources 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | vaadin-prerelease 208 | 209 | false 210 | 211 | 212 | 213 | 214 | vaadin-prereleases 215 | https://maven.vaadin.com/vaadin-prereleases 216 | 217 | 218 | 219 | 220 | vaadin-prereleases 221 | https://maven.vaadin.com/vaadin-prereleases 222 | 223 | 224 | 225 | 226 | 227 | 228 | -------------------------------------------------------------------------------- /GridFastNavigation-demo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | org.vaadin.patrik 6 | GridFastNavigation-demo 7 | war 8 | 2.6.4 9 | GridFastNavigation Add-on Demo 10 | 11 | 12 | UTF-8 13 | 1.8 14 | 1.8 15 | 8.14.3 16 | ${vaadin.version} 17 | 18 | 9.4.36.v20210114 19 | 20 | 21 | 22 | Tatu Lund 23 | https://github.com/tatulund/GridFastNavigation/ 24 | 25 | 26 | 27 | git://github.com/tatulund/GridFastNavigation.git 28 | scm:git:git://github.com/tatulund/GridFastNavigation.git 29 | scm:git:ssh://git@github.com:/tatulund/${ComponentClassName}.git 30 | GridFastNavigation add-on for Vaadin 31 | 32 | 33 | 34 | GitHub 35 | https://github.com/tatulund/GridFastNavigation/issues 36 | 37 | 38 | 39 | 40 | Apache 2 41 | https://www.apache.org/licenses/LICENSE-2.0.txt 42 | repo 43 | 44 | 45 | 46 | 47 | 48 | vaadin-addons 49 | https://maven.vaadin.com/vaadin-addons 50 | 51 | 52 | vaadin-snapshots 53 | https://oss.sonatype.org/content/repositories/vaadin-snapshots/ 54 | 55 | false 56 | 57 | 58 | true 59 | 60 | 61 | 62 | 63 | 64 | 65 | vaadin-snapshots 66 | https://oss.sonatype.org/content/repositories/vaadin-snapshots/ 67 | 68 | false 69 | 70 | 71 | true 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | com.vaadin 80 | vaadin-bom 81 | ${vaadin.version} 82 | pom 83 | import 84 | 85 | 86 | 87 | 88 | 89 | 90 | org.vaadin.patrik 91 | GridFastNavigation 92 | ${project.version} 93 | 94 | 95 | com.vaadin 96 | grid-renderers-collection-addon 97 | 2.2.10 98 | 99 | 100 | org.vaadin.ui 101 | numberfield 102 | 0.2.0 103 | 104 | 105 | org.vaadin 106 | prefixcombobox 107 | 1.4.0 108 | 109 | 110 | com.vaadin 111 | vaadin-push 112 | 113 | 114 | com.vaadin 115 | vaadin-push 116 | 117 | 118 | com.vaadin 119 | vaadin-client-compiler 120 | provided 121 | 122 | 123 | com.vaadin 124 | vaadin-themes 125 | 126 | 127 | javax.servlet 128 | javax.servlet-api 129 | 3.0.1 130 | provided 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | org.apache.maven.plugins 140 | maven-compiler-plugin 141 | 3.0 142 | 143 | 144 | 145 | maven-clean-plugin 146 | 2.4.1 147 | 148 | 149 | 150 | 151 | ${basedir}/src/main/webapp/VAADIN/widgetsets 152 | 153 | 154 | 155 | ${basedir}/src/main/webapp/VAADIN/gwt-unitCache 156 | 157 | 158 | 159 | 160 | 161 | 162 | maven-war-plugin 163 | 2.3 164 | 165 | 166 | **/VAADIN/gwt-unitCache/**, 167 | **/VAADIN/widgetsets/WEB-INF/**, 168 | **/WEB-INF/classes/gwt-unitCache/**, 169 | **/WEB-INF/classes/themes/**, 170 | **/WEB-INF/classes/widgetsets/** 171 | 172 | false 173 | 174 | 175 | 176 | 177 | com.vaadin 178 | vaadin-maven-plugin 179 | ${vaadin.plugin.version} 180 | 181 | -Xmx512M -Xss1024k 182 | ${basedir}/src/main/webapp/VAADIN/widgetsets 183 | ${basedir}/src/main/webapp/VAADIN/widgetsets 184 | true 185 | ${basedir}/target/tmp/gwt-unitCache 186 | false 187 | true 188 | http://localhost:8080/ 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | update-theme 199 | resources 200 | update-widgetset 201 | compile 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | org.eclipse.jetty 211 | jetty-maven-plugin 212 | ${jetty.plugin.version} 213 | 214 | 2 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | org.eclipse.m2e 225 | lifecycle-mapping 226 | 1.0.0 227 | 228 | 229 | 230 | 231 | 232 | com.vaadin 233 | vaadin-maven-plugin 234 | 235 | [${vaadin.plugin.version},) 236 | 237 | 238 | resources 239 | update-widgetset 240 | compile 241 | update-theme 242 | compile-theme 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | vaadin-prerelease 263 | 264 | false 265 | 266 | 267 | 268 | 269 | vaadin-prereleases 270 | http://maven.vaadin.com/vaadin-prereleases 271 | 272 | 273 | 274 | 275 | vaadin-prereleases 276 | http://maven.vaadin.com/vaadin-prereleases 277 | 278 | 279 | 280 | 281 | 282 | 283 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Published on Vaadin Directory](https://img.shields.io/badge/Vaadin%20Directory-published-00b4f0.svg)](https://vaadin.com/directory/component/gridfastnavigation-add-on) 2 | [![Stars on Vaadin Directory](https://img.shields.io/vaadin-directory/star/gridfastnavigation-add-on.svg)](https://vaadin.com/directory/component/gridfastnavigation-add-on) 3 | 4 | # GridFastNavigation Add-on for Vaadin 8 5 | 6 | Do you like the Grid but would want it to appeal more to old Excel jockeys? 7 | Then this add-on is for you! 8 | GridFastNavigation is a compnent extension for Vaadin Grid, which uses the 9 | unbuffered editing mode and alters its keyboard controls to provide a faster 10 | editing experience. 11 | 12 | ## Online demo 13 | [Todo](...) 14 | 15 | ## Download release 16 | 17 | Official releases of this add-on are available at Vaadin Directory. For Maven instructions, download and reviews, go to 18 | https://vaadin.com/directory/component/gridfastnavigation-add-on 19 | 20 | ## JavaDoc 21 | 22 | JavaDoc can be found online at 23 | 24 | https://vaadin.com/directory/component/gridfastnavigation-add-on/api 25 | 26 | ## Building and running demo 27 | 28 | git clone https://github.com/thinwire/GridFastNavigation.git 29 | mvn clean install 30 | cd GridFastNavigation-demo 31 | mvn jetty:run 32 | 33 | To see the demo, navigate to http://localhost:8080/ 34 | 35 | ## Development with Eclipse IDE 36 | 37 | For further development of this add-on, the following tool-chain is recommended: 38 | - Eclipse IDE 39 | - m2e wtp plug-in (install it from Eclipse Marketplace) 40 | - Vaadin Eclipse plug-in (install it from Eclipse Marketplace) 41 | - Chrome browser 42 | 43 | ### Importing project 44 | 45 | Choose File > Import... > Existing Maven Projects 46 | 47 | Note that Eclipse may give "Plugin execution not covered by lifecycle configuration" errors for pom.xml. Use "Permanently mark goal resources in pom.xml as ignored in Eclipse build" quick-fix to mark these errors as permanently ignored in your project. Do not worry, the project still works fine. 48 | 49 | ### Debugging server-side 50 | 51 | If you have not already compiled the widgetset, do it now by running vaadin:install Maven target for GridFastNavigation-root project. 52 | 53 | If you have a JRebel license, it makes on the fly code changes faster. Just add JRebel nature to your GridFastNavigation-demo project by clicking project with right mouse button and choosing JRebel > Add JRebel Nature 54 | 55 | To debug project and make code modifications on the fly in the server-side, right-click the GridFastNavigation-demo project and choose Debug As > Debug on Server. Navigate to http://localhost:8080/ to see the application. 56 | 57 | ### Debugging client-side 58 | 59 | Debugging client side code in the GridFastNavigation-demo project: 60 | - run "mvn vaadin:run-codeserver" on a separate console while the application is running 61 | - activate Super Dev Mode in the debug window of the application or by adding ?superdevmode to the URL 62 | - You can access Java-sources and set breakpoints inside Chrome if you enable source maps from inspector settings. 63 | 64 | ## Release notes 65 | 66 | ### Version 2.6.4 67 | - Adjust timing of the focus and selectAll calls. 68 | 69 | ### Version 2.6.3 70 | - Fix editor column resizing in multiselect mode 71 | 72 | ### Version 2.6.1 73 | - Fixed possible issue when there is empty data set in grid 74 | 75 | ### Version 2.6.0 76 | - Do not change row if Alt Shift or Ctrl pressed (see issue #102) 77 | - Added getKeyCode to EditorOpenEvent (see issue #101) 78 | - Added isUserOriginated to CellFocusEvent and EditorOpenEvent (see issue #99) 79 | 80 | ### Version 2.5.6 81 | - Fixed column sizing issues in when using Frozen columns. 82 | 83 | ### Version 2.5.5 84 | - Fixed NPE in when using Frozen columns. 85 | 86 | ### Version 2.5.4 87 | - Fixed timing issue if editRow(row,col) was called during Grid is updating after dataProvider.refreshAll() 88 | 89 | ### Version 2.5.3 90 | - Added some error handling 91 | 92 | ### Version 2.5.2 93 | - Added method to open Editor at row X in column Y 94 | - Fix: Disabled column styles were not applied dynamically 95 | 96 | ### Version 2.5.1 97 | - Grid froze when last column was not editable, fixes issue #98 98 | 99 | ### Version 2.5.0 100 | - Fixed typo in method name, fixes issue #97 101 | - Fixed hidden column compensation logic, fixes issue #96 102 | - Codebase maintenance 103 | 104 | ### Version 2.4.8 105 | - Add-on also now supports custom editor widgets extending the base set of editors 106 | 107 | ### Version 2.4.7 108 | - Fixed editor cell resizing logic to work also with multiselect mode. 109 | 110 | ### Version 2.4.6 111 | - Added option to enable selected style in Editor on selected row 112 | - Fixed bug in cell focus event not triggered when moving left in multiselect Grid, fixes issue #95 113 | 114 | ### Version 2.4.5 115 | - Fixed missing implementation of CellEditEvent#getOldData(), see issue #91 116 | 117 | ### Version 2.4.4 118 | - Fixed CellFocusEvent#wasRowChanged() and CellFocusEvent#wasColumnChanged() to work correctly 119 | 120 | ### Version 2.4.3 121 | - Added feature: shift+space will select / deselect the row when editor is open, see issue #90 122 | 123 | ### Version 2.4.2 124 | - Fixed bug: In edit events item and row index were not correct or in sync in certain corner cases 125 | 126 | ### Version 2.4.1 127 | - Made OffsetHelper serializable, to address issue #86 128 | - Fixed JavaDocs 129 | 130 | ### Version 2.4.0 131 | - Added support for NativeSelect, ColorPicker and TextArea 132 | - Improved setFocusedCell(..) to address issue #77 133 | 134 | ### Version 2.3.10 135 | - Improvement: issue #82 add helper to deal with custom selection models that change internal column calculation 136 | - Added fault checkign to setFocusedCel due issue #77 and setChangeColumnOnEnter methods 137 | - Improved JavaDocs 138 | 139 | ### Version 2.3.9 140 | - Fix: Grid scroll bar width was not taken into account in Editor resizing 141 | 142 | ### Version 2.3.8 143 | - Further improvement to row validation mode 144 | 145 | ### Version 2.3.7 146 | - Improvement to row validation mode 147 | 148 | ### Version 2.3.6 149 | - Added fix to Editor not resized when browser window is being resized bug. See: https://github.com/vaadin/framework/issues/11148 150 | 151 | ### Version 2.3.5 152 | - Workaround for https://github.com/vaadin/framework/issues/8962 153 | - Workaround for https://github.com/vaadin/framework/issues/7276 154 | 155 | ### Version 2.3.4 156 | - Fixed bug: Problem with sorting, edit event did not return right item (issue #76). 157 | - Improvement: Home/End key behavior can be disabled with setHomeEndEnabled(false) (issue #75) 158 | - New feature: Move focus programmatically with setFocusedCel(row,col) (issue #68) 159 | - Updated demo 160 | 161 | ### Version 2.3.3 162 | - Fixed bug: Client side exception occurred when there were hidden columns (issue #74). 163 | 164 | ### Version 2.3.2 165 | - Added convenience method disableAllColumns() in EditorOpenEvent (issue #27) 166 | - Fixed bug: Editor opening was jammed if there were no editable columns 167 | 168 | ### Version 2.3.1 169 | - Fixed bug: Tabing out of bounds caused client side exception. 170 | 171 | ### Version 2.3.0 172 | - Added preliminary support for hidden columns, fixes issue #71 173 | - Minor improvements in setRowValidation(true) mode 174 | - Updated demo 175 | 176 | ### Version 2.2.4 177 | - Fix bug: Clicking outside Grid when setRowValidation(true) closed editor 178 | 179 | ### Version 2.2.3 180 | - Improvements to full row validation (see issues #69 and #51), with setRowValidation(..) API 181 | 182 | ### Version 2.2.2 183 | - Making possible to have full row validation (see issues #69 and #51) 184 | - Updated demo 185 | 186 | ### Version 2.2.1 187 | - Fixing issue #67: ESC-Click combination froze editor 188 | 189 | ### Version 2.2.0 190 | - Fixed issue #65: Column indeces where off by one with multiselect model, and column disabling did not work 191 | - Changed behavior, FastNavigation will no longer force Editor to be enabled 192 | 193 | ### Version 2.1.20 194 | - Fixed issue #64: RowEditEvent fired after ESC-key 195 | 196 | ### Version 2.1.19 197 | - Fixed issue #63: Numpad input was not filttered properly 198 | 199 | ### Version 2.1.18 200 | - Fixed serialization issue 201 | - Made EditorWidgets.registerHandler(..) public so that it is possible to add your custom editor widget handlers, and added java docs. 202 | - Fix: Selection of the ComboBox textfield was missing 203 | 204 | ### Version 2.1.17 205 | - Fix: Do not fire ClickOutEvent if ComboBox or DateField is open. 206 | 207 | ### Version 2.1.16 208 | - Returned disableColumns(..) to EditorOpenEvent. It can be used to disable additional columns from editing. 209 | 210 | ### Version 2.1.15 211 | - Fix, clickOutListener did not work if FastNavigation was not instantiated with dispatchEditEventOnBlur=true parameter (issue: #54) 212 | 213 | ### Version 2.1.14 214 | - Fix, disabled columns / readonly fields detection logic was flawed (see: issue #55) 215 | - Fixing internal columns handling (see: issue #50) 216 | - Fix, Internal method to get item for events, did NPE with empty DataProvider (see: issue #56) 217 | 218 | ### Version 2.1.13 219 | - Bugfix, cursor up/down did not open ComboBox in IE11 220 | 221 | ### Version 2.1.12 222 | - Fixing issue #52 223 | 224 | ### Version 2.1.11 225 | - Added possibility to add save shortcuts (see: issue #49) 226 | - setSaveWithCtrlS(true) -> CTRL+S does save and close editor 227 | 228 | ### Version 2.1.10 229 | - Solving issue #42: Clarifying setOpenEditorWithSingleClick(true) JavaDoc. Using it will prevent Grid's item click event and selection event getting the click. 230 | 231 | ### Version 2.1.9 232 | - Changed getItem() in selected events to use DataCommunicator, requires Vaadin 8.2+ 233 | 234 | ### Version 2.1.8 235 | - Added ClickOutListener 236 | 237 | ### Version 2.1.7 238 | - Changed widgetset.xml filename, Fixed issues #40, #41 239 | 240 | ### Version 2.1.6 241 | - Modified CellFocusEvent and RowFocusEvent to return -1 if focus is in Header/Footer 242 | - Updated JavaDocs 243 | 244 | ### Version 2.1.5 245 | - Added getItem() to RowFocusEvent and CellFocusEvent 246 | - Minor bugfix, there was regression in fix for issue #35 247 | 248 | ### Version 2.1.4 249 | - Added getItem() CellEditEvent and RowEditEvent (experimental) 250 | - Fixed issue #37: Enter now saves the value when closing the editor in the last cell 251 | - Fixed issue #35: Listeners should be able to be used automatically now. 252 | - DeleteButtonRenderer deprecated, it is now maintained in Grid RenderersCollection add-on. 253 | - Updated the demo. 254 | 255 | ### Version 2.1.3 256 | - Added support for closing Editor and dispatching event when clicking outside of Grid. 257 | 258 | ### Version 2.1.2 259 | - DeleteButtonRenderer, made it boolean type, so that property underneath controls whether the button is enabled or not 260 | 261 | ### Version 2.1.1 262 | - Fixed DeleteButtonRenderer: Html content mode support in client side was missing 263 | - Demo updated 264 | 265 | ### Version 2.1.0 266 | - Added DeleteButtonRenderer 267 | - Demo updated 268 | 269 | ### Version 2.0.0 270 | - First version for Vaadin 8 271 | - Features on par with version 0.5.7 272 | - Demo updated 273 | 274 | ## Issue tracking 275 | 276 | The issues for this add-on are tracked on its github.com page. All bug reports and feature requests are appreciated. 277 | 278 | ## Contributions 279 | 280 | Contributions are welcome, but there are no guarantees that they are accepted as such. Process for contributing is the following: 281 | - Fork this project 282 | - Create an issue to this project about the contribution (bug or feature) if there is no such issue about it already. Try to keep the scope minimal. 283 | - Develop and test the fix or functionality carefully. Only include minimum amount of code needed to fix the issue. 284 | - Refer to the fixed issue in commit 285 | - Send a pull request for the original project 286 | - Comment on the original issue that you have implemented a fix for it 287 | 288 | ## License & Author 289 | 290 | Add-on is distributed under Apache License 2.0. For license terms, see LICENSE.txt. 291 | 292 | GridFastNavigation is written by Patrik Lindström, Tatu Lund and Johannes Tuikkala and maintained by the Tatu. Vaadin 8 migration initial work was contributed by Brett Sutton. 293 | 294 | Major pieces of development of this add-on has been sponsored by multiple Support and Prime customers of Vaadin. See vaadin.com/support and Development on Demand for more details. 295 | 296 | 297 | -------------------------------------------------------------------------------- /GridFastNavigation-demo/src/main/java/org/vaadin/patrik/demo/DemoFastGrid.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.demo; 2 | 3 | import java.text.DateFormat; 4 | import java.text.SimpleDateFormat; 5 | import java.time.OffsetDateTime; 6 | import java.time.ZoneId; 7 | import java.time.ZoneOffset; 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | import java.util.Date; 11 | import java.util.List; 12 | 13 | import org.vaadin.grid.cellrenderers.action.DeleteButtonRenderer; 14 | import org.vaadin.patrik.FastNavigation; 15 | import org.vaadin.prefixcombobox.PrefixComboBox; 16 | import org.vaadin.ui.NumberField; 17 | 18 | import com.vaadin.data.Binder; 19 | import com.vaadin.data.Binder.Binding; 20 | import com.vaadin.data.converter.LocalDateTimeToDateConverter; 21 | import com.vaadin.data.converter.LocalDateToDateConverter; 22 | import com.vaadin.data.converter.StringToIntegerConverter; 23 | import com.vaadin.data.provider.ListDataProvider; 24 | import com.vaadin.data.validator.IntegerRangeValidator; 25 | import com.vaadin.event.ShortcutAction.KeyCode; 26 | import com.vaadin.icons.VaadinIcons; 27 | import com.vaadin.ui.Button; 28 | import com.vaadin.ui.CheckBox; 29 | import com.vaadin.ui.DateField; 30 | import com.vaadin.ui.DateTimeField; 31 | import com.vaadin.ui.Grid; 32 | import com.vaadin.ui.TextField; 33 | import com.vaadin.ui.UI; 34 | import com.vaadin.ui.renderers.DateRenderer; 35 | import com.vaadin.ui.themes.ValoTheme; 36 | 37 | public class DemoFastGrid extends Grid { 38 | private static final long serialVersionUID = 1L; 39 | 40 | final List demoList; 41 | final ListDataProvider demoData; 42 | private FastNavigation nav; 43 | public boolean moveSelection = false; 44 | 45 | private MessageLog messageLog; 46 | private int lastEditedRow = 0; 47 | 48 | DemoFastGrid(MessageLog messageLog) { 49 | super("Fast Navigation Grid"); 50 | this.messageLog = messageLog; 51 | demoList = new ArrayList<>(); 52 | demoData = new ListDataProvider<>(demoList); 53 | 54 | this.setDataProvider(demoData); 55 | this.getHeaderRow(0).setStyleName("my-background"); 56 | 57 | initNavigation(); 58 | 59 | this.getEditor().setEnabled(true); 60 | this.getEditor().setBuffered(false); 61 | 62 | bindColumnsToEditor(); 63 | 64 | // nav.editRow(1, 3); 65 | } 66 | 67 | private void initNavigation() { 68 | 69 | nav = new FastNavigation<>(this, false, true); 70 | nav.setChangeColumnAfterLastRow(true); 71 | nav.setOpenEditorWithSingleClick(true); 72 | 73 | nav.addRowEditListener(event -> { 74 | getDataProvider().refreshAll(); 75 | int rowIndex = event.getRowIndex(); 76 | if (rowIndex >= 0) { 77 | printChangedRow(rowIndex,(DemoColumns) event.getItem()); 78 | } 79 | if (event.getItem() != null && ((DemoColumns) event.getItem()).getCol9() == true) { 80 | this.getColumn("col6").getEditorBinding().getField().setReadOnly(true); 81 | } else { 82 | this.getColumn("col6").getEditorBinding().getField().setReadOnly(false); 83 | } 84 | }); 85 | // If you want to update Grid item values programmatically in Grid and get the updated values 86 | // shown Editor needs to be closed and reopened 87 | // nav.addCellEditListener(event -> { 88 | // int row = event.getRowIndex(); 89 | // if (event.getColumnIndex() == 5) { 90 | // this.getEditor().save(); 91 | // this.getEditor().cancel(); 92 | // DemoColumns item = (DemoColumns) event.getItem(); 93 | // item.setCol6(item.getCol5()+item.getCol6()); 94 | // this.getDataProvider().refreshItem(item); 95 | // this.getEditor().editRow(row); 96 | // } 97 | // }); 98 | nav.addCellEditListener(event -> { 99 | messageLog.writeOutput("Changed '" + event.getOldData() + "' -> '" + event.getNewData()+ "'"); 100 | }); 101 | // Obsolete in Vaadin 8.9+ 102 | // nav.enableEditorSelectedStyle(true); 103 | 104 | DeleteButtonRenderer deleteButton = new DeleteButtonRenderer(clickEvent -> { 105 | if (this.getEditor().isOpen()) 106 | this.getEditor().cancel(); 107 | 108 | demoList.remove(clickEvent.getItem()); 109 | this.getDataProvider().refreshAll(); 110 | },VaadinIcons.TRASH.getHtml()+" Delete",VaadinIcons.CHECK.getHtml()+" Confirm"); 111 | deleteButton.setHtmlContentAllowed(true); 112 | this.addColumn(action -> true,deleteButton).setId("delete").setCaption("Action").setWidth(120).setHidable(true); 113 | 114 | // Open with F2 115 | nav.addEditorOpenShortcut(KeyCode.F2); 116 | messageLog.writeOutput("Editor can also be opened with F2"); 117 | 118 | // Close with F3 119 | nav.addEditorCloseShortcut(KeyCode.F3); 120 | messageLog.writeOutput("Editor can also be closed with F3"); 121 | 122 | nav.setSaveWithCtrlS(true); 123 | 124 | Grid grid = this; 125 | 126 | // Row focus change 127 | nav.addRowFocusListener(event -> { 128 | if (moveSelection) { 129 | if (event.getRow() >= 0) grid.select((DemoColumns) event.getItem()); 130 | else grid.deselectAll(); 131 | } 132 | messageLog.writeOutput("Focus moved to row " + event.getRow()); 133 | }); 134 | messageLog.writeOutput("Added row focus change listener"); 135 | 136 | // Cell focus change 137 | nav.addCellFocusListener(event -> { 138 | int row = event.getRow(); 139 | int col = event.getColumnIndex(); 140 | boolean userOriginated = event.isUserOriginated(); 141 | messageLog.writeOutput("Focus moved to cell [" + row + ", " + col + " ]" + " from UI: "+userOriginated); 142 | // if (event.wasRowChanged() && this.getEditor().isOpen()) { 143 | // this.getEditor().cancel(); 144 | // messageLog.writeOutput("Row was changed"); 145 | // nav.setFocusedCell(row, 1); 146 | // this.getEditor().editRow(row); 147 | // } 148 | }); 149 | messageLog.writeOutput("Added cell focus change listener"); 150 | 151 | // Listening to opening of editor 152 | nav.addEditorOpenListener(event -> { 153 | // Uncomment to demonstrate conditional row editing 154 | // if (((DemoColumns) event.getItem()).getCol9()) event.disableAllColumns(); 155 | // Or 156 | // event.disableColumns(1); 157 | int row = event.getRow(); 158 | boolean userOriginated = event.isUserOriginated(); 159 | int keyCode = event.getKeyCode(); 160 | lastEditedRow = row; 161 | messageLog.writeOutput("Editor opened on row " + row + " at column " + event.getColumnIndex()+ " keyCode: "+keyCode+" from UI: "+userOriginated); 162 | }); 163 | messageLog.writeOutput("Added editor open listener"); 164 | 165 | // Listening to closing of editor 166 | nav.addEditorCloseListener(event -> { 167 | messageLog.writeOutput("Editor closed on row " + event.getRow() + ", column " + event.getColumnIndex() + ", " 168 | + (event.wasCancelled() ? "user cancelled change" : "user saved change")); 169 | }); 170 | messageLog.writeOutput("Added editor close listener"); 171 | 172 | nav.addClickOutListener(event -> { 173 | messageLog.writeOutput("User clicked outside Grid: "+event.getSource().toString()); 174 | }); 175 | } 176 | 177 | /** 178 | * We bind each column to a field (shared by all rows) so that we can edit 179 | * each cell. 180 | */ 181 | private void bindColumnsToEditor() { 182 | TextField col1 = new TextField(); 183 | TextField col3 = new TextField(); 184 | NumberField col4 = new NumberField(); 185 | TextField col5 = new TextField(); 186 | TextField col6 = new TextField(); 187 | DateTimeField col7 = new DateTimeField(); 188 | DateField col8 = new DateField(); 189 | CheckBox col9 = new CheckBox(); 190 | col9.setDescription("Selecting this will disable Col6"); 191 | PrefixComboBox col10 = new PrefixComboBox<>(); 192 | List items = new ArrayList<>(Arrays.asList("small", "medium", "large")); 193 | // col10.setNewItemProvider(value -> { 194 | // items.add(value); 195 | // return Optional.ofNullable(value); 196 | // }); 197 | col10.setEmptySelectionCaption("unsized"); 198 | 199 | col10.setDataProvider(new ListDataProvider(items)); 200 | 201 | Binder binder = this.getEditor().getBinder(); 202 | 203 | // Col1 simple string 204 | Binding col1Binding = binder.forField(col1).asRequired("Empty value not accepted").bind(DemoColumns::getCol1, 205 | DemoColumns::setCol1); 206 | this.addColumn(DemoColumns::getCol1).setCaption("Col1").setExpandRatio(1).setEditorBinding(col1Binding); 207 | 208 | // Col2 non-editable string 209 | this.addColumn(DemoColumns::getCol2).setCaption("No Edits").setId("noedit").setHidable(true).setWidth(150); 210 | 211 | // Col3 Integer 212 | Binding col3Binding = binder.forField(col3).withNullRepresentation("") 213 | .withConverter(new StringToIntegerConverter("Must enter a number")).withValidator(new IntegerRangeValidator("Input integer between 0 and 10",0,10)) 214 | .bind(DemoColumns::getCol3, DemoColumns::setCol3); 215 | this.addColumn(DemoColumns::getCol3).setCaption("Integer").setHidable(true).setWidth(150).setEditorBinding(col3Binding); 216 | 217 | // Col4 Float 218 | Binding col4Binding = binder.forField(col4).withNullRepresentation("") 219 | .withConverter(col4.getConverter("Must enter a number")) 220 | // .withConverter(new StringToFloatConverter("Must enter a number")) 221 | .bind(DemoColumns::getCol4, DemoColumns::setCol4); 222 | this.addColumn(DemoColumns::getCol4).setCaption("Float").setHidable(true).setWidth(100).setEditorBinding(col4Binding); 223 | 224 | // Col 5 Integer 225 | Binding col5Binding = binder.forField(col5).withNullRepresentation("") 226 | .withConverter(new StringToIntegerConverter("Must enter a number")).withValidator(new IntegerRangeValidator("Input integer between 0 and 10",0,10)) 227 | .bind(DemoColumns::getCol5, DemoColumns::setCol5); 228 | this.addColumn(DemoColumns::getCol5).setCaption("Integer (2)").setHidable(true).setWidth(100).setEditorBinding(col5Binding); 229 | 230 | // Col6 Integer(3) 231 | Binding col6Binding = binder.forField(col6).withNullRepresentation("") 232 | .withConverter(new StringToIntegerConverter("Must enter a number")).withValidator(new IntegerRangeValidator("Input integer between 0 and 10",0,10)) 233 | .bind(DemoColumns::getCol6, DemoColumns::setCol6); 234 | this.addColumn(DemoColumns::getCol6).setId("col6").setCaption("Col6").setHidable(true).setWidth(100).setEditorBinding(col6Binding); 235 | 236 | // Col 7 DateTime 237 | // Need a zoneoffset for datetimefield 238 | OffsetDateTime odt = OffsetDateTime.now(ZoneId.systemDefault()); 239 | ZoneOffset zoneOffset = odt.getOffset(); 240 | 241 | SimpleDateFormat dateTimeFormat = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT, 242 | DateFormat.SHORT, UI.getCurrent().getLocale()); 243 | Binding col7Binding = binder.forField(col7) 244 | .withConverter(new LocalDateTimeToDateConverter(zoneOffset)) 245 | .bind(DemoColumns::getCol7, DemoColumns::setCol7); 246 | this.addColumn(DemoColumns::getCol7).setCaption("Date Time").setWidth(180).setEditorBinding(col7Binding) 247 | .setRenderer(new DateRenderer(dateTimeFormat)).setHidable(true); 248 | 249 | // col 8 Date 250 | Binding col8Binding = binder.forField(col8).withConverter(new LocalDateToDateConverter()) 251 | .bind(DemoColumns::getCol8, DemoColumns::setCol8); 252 | 253 | SimpleDateFormat dateFormat = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT, 254 | UI.getCurrent().getLocale()); 255 | this.addColumn(DemoColumns::getCol8).setCaption("Date").setWidth(120).setEditorBinding(col8Binding) 256 | .setRenderer(new DateRenderer(dateFormat)).setHidable(true); 257 | 258 | // Col 9 Boolean 259 | Binding col10Binding = binder.forField(col9).bind(DemoColumns::getCol9, 260 | DemoColumns::setCol9); 261 | this.addColumn(DemoColumns::getCol9).setCaption("Boolean").setHidable(true).setWidth(150).setEditorBinding(col10Binding); 262 | 263 | // Col 10 Combobox. 264 | Binding col11Binding = binder.forField(col10).bind(DemoColumns::getCol10, 265 | DemoColumns::setCol10); 266 | this.addColumn(DemoColumns::getCol10).setCaption("Combobox").setHidable(true).setWidth(150).setEditorBinding(col11Binding); 267 | 268 | this.addComponentColumn(item -> { 269 | Button button = new Button("", VaadinIcons.TRASH); 270 | button.addStyleName(ValoTheme.BUTTON_BORDERLESS); 271 | button.addStyleName(ValoTheme.BUTTON_ICON_ONLY); 272 | button.setWidth(48, Unit.PIXELS); 273 | button.addClickListener(clickEvent -> { 274 | messageLog.writeOutput("Component button click"); 275 | demoList.remove(item); 276 | this.getDataProvider().refreshAll(); 277 | }); 278 | return button; 279 | }).setHandleWidgetEvents(false).setWidth(70); 280 | 281 | for (int i = 0; i < 5; ++i) { 282 | demoList.add(new DemoColumns()); 283 | } 284 | demoData.refreshAll(); 285 | this.addItemClickListener(event -> { 286 | System.out.println("Item click event happens: "+event.getItem().toString()); 287 | }); 288 | this.setSizeFull(); 289 | // nav.setFocusedCell(2, 2, true); 290 | this.select(demoList.get(2)); 291 | // this.setFrozenColumnCount(2); 292 | 293 | this.appendHeaderRow(); 294 | for (Column col : this.getColumns()) { 295 | if (col.getId() == null || (col.getId() != null && !(col.getId().equals("noedit") || col.getId().equals("delete")))) { 296 | CheckBox editable = new CheckBox(); 297 | editable.setValue(true); 298 | editable.addValueChangeListener(event -> { 299 | if (!getEditor().isOpen()) { 300 | if (event.getValue()) { 301 | col.setEditable(true); 302 | } else { 303 | col.setEditable(false); 304 | } 305 | } else if (event.isUserOriginated()) { 306 | editable.setValue(!editable.getValue()); 307 | } 308 | }); 309 | this.getHeaderRow(1).getCell(col).setComponent(editable); 310 | } 311 | } 312 | } 313 | 314 | // Add a blank row to the grid and tell the grid to refresh itself showing 315 | // the new row. 316 | public void addBlankRow() { 317 | // its an unbuffered editor so canceling doesn't lose data just closes 318 | // the 319 | // editor. 320 | if (getEditor().isOpen()) { 321 | this.getEditor().cancel(); 322 | } 323 | demoList.add(new DemoColumns()); 324 | this.recalculateColumnWidths(); 325 | this.getDataProvider().refreshAll(); 326 | int row = this.getDataCommunicator().getDataProviderSize()-1; 327 | nav.editRow(row,0); 328 | } 329 | 330 | public FastNavigation getNavigation() { 331 | return nav; 332 | } 333 | 334 | private void printChangedRow(int rowIndex, DemoColumns rowData) { 335 | if (demoList.size() >= rowIndex) { 336 | // DemoColumns rowData = demoList.get(rowIndex); 337 | messageLog.writeOutput("Row " + rowIndex + " changed to: " + rowData); 338 | } 339 | } 340 | 341 | public void resetFocus() { 342 | nav.setFocusedCell(0, 1, true); 343 | } 344 | 345 | public void openEditor() { 346 | nav.editRow(1, 3); 347 | } 348 | 349 | } 350 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/java/org/vaadin/patrik/client/GridFastNavigationConnector.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.client; 2 | 3 | import java.util.List; 4 | 5 | import org.vaadin.patrik.FastNavigation; 6 | import org.vaadin.patrik.client.EditorStateManager.EditorListener; 7 | import org.vaadin.patrik.client.FocusTracker.FocusListener; 8 | import org.vaadin.patrik.shared.FastNavigationClientRPC; 9 | import org.vaadin.patrik.shared.FastNavigationServerRPC; 10 | import org.vaadin.patrik.shared.FastNavigationState; 11 | 12 | import com.google.gwt.animation.client.AnimationScheduler; 13 | import com.google.gwt.animation.client.AnimationScheduler.AnimationCallback; 14 | import com.google.gwt.core.client.Scheduler; 15 | import com.google.gwt.dom.client.DivElement; 16 | import com.google.gwt.dom.client.Element; 17 | import com.google.gwt.dom.client.NodeList; 18 | import com.google.gwt.dom.client.Style; 19 | import com.google.gwt.dom.client.TableRowElement; 20 | import com.google.gwt.user.client.Window; 21 | import com.google.gwt.user.client.ui.Widget; 22 | import com.vaadin.client.BrowserInfo; 23 | import com.vaadin.client.ComponentConnector; 24 | import com.vaadin.client.ServerConnector; 25 | import com.vaadin.client.annotations.OnStateChange; 26 | import com.vaadin.client.extensions.AbstractExtensionConnector; 27 | import com.vaadin.client.widgets.Grid; 28 | import com.vaadin.client.widgets.Grid.Editor; 29 | import com.vaadin.shared.ui.Connect; 30 | 31 | @SuppressWarnings("serial") 32 | @Connect(FastNavigation.class) 33 | public class GridFastNavigationConnector extends AbstractExtensionConnector { 34 | 35 | private Grid grid; 36 | private EditorStateManager editorManager; 37 | private FocusTracker focusTracker; 38 | private FastNavigationServerRPC rpc; 39 | 40 | // Return -1.0 if Grid has no vertical scroll bar otherwise its width 41 | private double getVerticalScrollBarWidth() { 42 | for (Element e : getGridParts("div")) { 43 | if (e.getClassName().contains("v-grid-scroller-vertical")) { 44 | if (BrowserInfo.get().isIE11() || BrowserInfo.get().isEdge()) { 45 | return e.getClientWidth(); 46 | } else { 47 | return e.getOffsetWidth(); 48 | } 49 | } 50 | } 51 | return -1.0; 52 | } 53 | 54 | // Get elements in Grid by tag name 55 | private Element[] getGridParts(String elem) { 56 | NodeList elems = grid.getElement().getElementsByTagName(elem); 57 | Element[] ary = new Element[elems.getLength()]; 58 | for (int i = 0; i < ary.length; ++i) { 59 | ary[i] = elems.getItem(i); 60 | } 61 | return ary; 62 | } 63 | 64 | private void doEditorScrollOffsetFix() { 65 | try { 66 | DivElement cellWrapper = GridViolators.getEditorCellWrapper(grid); 67 | if (cellWrapper == null) return; 68 | DivElement frozenCellWrapper = GridViolators.getFrozenCellWrapper(grid); 69 | int offset = 0; 70 | double selectionWidth = -1.0; 71 | if (grid.getSelectionColumn().isPresent()) { 72 | offset = 1; 73 | selectionWidth = grid.getColumns().get(0).getWidthActual(); 74 | } 75 | double scrollLeft = grid.getScrollLeft(); 76 | int row = grid.getEditor().getRow(); 77 | TableRowElement rowElement = grid.getEscalator().getBody().getRowElement(row); 78 | int rowLeft = Math.abs(rowElement.getAbsoluteLeft()); 79 | int editorLeft = Math.abs(cellWrapper.getAbsoluteLeft()); 80 | int frozenColumns = grid.getFrozenColumnCount(); 81 | int frozenWidth = 0; 82 | for (int i=0;i 0 && frozenColumns > 0) { 89 | cellWrapper.getStyle().setLeft((frozenWidth - scrollLeft), 90 | Style.Unit.PX); 91 | } else { 92 | if (selectionWidth > 0) { 93 | cellWrapper.getStyle().setLeft((selectionWidth - scrollLeft), 94 | Style.Unit.PX); 95 | } else if (editorLeft != rowLeft) { 96 | cellWrapper.getStyle().setLeft(editorLeft - (scrollLeft + rowLeft), 97 | Style.Unit.PX); 98 | } 99 | } 100 | } catch (IllegalStateException e) { 101 | // IllegalStateException may occur if user has scrolled Grid so 102 | // that Escalator has updated, and row under Editor is no longer 103 | // there 104 | } catch (IndexOutOfBoundsException e) { 105 | } 106 | } 107 | 108 | @Override 109 | @SuppressWarnings("unchecked") 110 | protected void extend(ServerConnector target) { 111 | grid = (Grid) ((ComponentConnector) target).getWidget(); 112 | rpc = getRpcProxy(FastNavigationServerRPC.class); 113 | editorManager = new EditorStateManager(grid,getState()); 114 | focusTracker = new FocusTracker(grid); 115 | editorManager.setConnector(this); 116 | AnimationCallback editorColumnAndWidthFix = new AnimationCallback() { 117 | @Override 118 | public void execute(double timestamp) { 119 | int cols = grid.getVisibleColumns().size(); 120 | int offset = 0; 121 | if (grid.getSelectionColumn().isPresent()) offset = 1; 122 | DivElement editorOverlay = GridViolators.getEditorOverlay(grid); 123 | if (editorOverlay == null) return; 124 | DivElement frozenCellWrapper = GridViolators.getFrozenCellWrapper(grid); 125 | Double scrollerWidth = getVerticalScrollBarWidth(); 126 | Double gridWidth = (double) grid.getOffsetWidth(); 127 | if (scrollerWidth > 0.0) gridWidth = gridWidth - scrollerWidth; 128 | editorOverlay.getStyle().setWidth(gridWidth, Style.Unit.PX); 129 | DivElement cellWrapper = GridViolators.getEditorCellWrapper(grid); 130 | int frozenColumns = grid.getFrozenColumnCount(); 131 | for (int i=0;i 0) { 146 | cellWrapper.getStyle().setLeft((frozenWidth - grid.getScrollLeft()), 147 | Style.Unit.PX); 148 | } 149 | } 150 | }; 151 | AnimationCallback editorColumnWidthFix = new AnimationCallback() { 152 | @Override 153 | public void execute(double timestamp) { 154 | DivElement cellWrapper = GridViolators.getEditorCellWrapper(grid); 155 | if (cellWrapper == null) return; 156 | DivElement frozenCellWrapper = GridViolators.getFrozenCellWrapper(grid); 157 | int cols = grid.getVisibleColumns().size(); 158 | int offset = 0; 159 | if (grid.getSelectionColumn().isPresent()) offset = 1; 160 | int frozenColumns = grid.getFrozenColumnCount(); 161 | for (int i=0;i 0) { 176 | cellWrapper.getStyle().setLeft((frozenWidth - grid.getScrollLeft()), 177 | Style.Unit.PX); 178 | } 179 | } 180 | }; 181 | 182 | grid.addScrollHandler(event -> { 183 | if (grid.isEditorActive()) { 184 | Scheduler.get().scheduleFinally(() -> { 185 | doEditorScrollOffsetFix(); 186 | }); 187 | } 188 | }); 189 | grid.addColumnVisibilityChangeHandler(event -> { 190 | if (grid.isEditorActive()) { 191 | GridViolators.redrawEditor(grid); 192 | AnimationScheduler.get().requestAnimationFrame(editorColumnWidthFix); 193 | } 194 | }); 195 | grid.addColumnResizeHandler(event -> { 196 | if (grid.isEditorActive()) { 197 | AnimationScheduler.get().requestAnimationFrame(editorColumnWidthFix); 198 | } 199 | }); 200 | Window.addResizeHandler(event -> { 201 | if (grid.isEditorActive()) { 202 | AnimationScheduler.get().requestAnimationFrame(editorColumnAndWidthFix); 203 | } 204 | }); 205 | 206 | registerRpc(FastNavigationClientRPC.class, 207 | new FastNavigationClientRPC() { 208 | @Override 209 | public void setDisabledColumns(List indices) { 210 | editorManager.setDisabledColumns(indices); 211 | } 212 | 213 | @Override 214 | public void unlockEditor(int lockId) { 215 | editorManager.externalUnlock(lockId); 216 | } 217 | 218 | @Override 219 | public void validationHasErrors() { 220 | editorManager.moveEditorToError(); 221 | } 222 | 223 | @Override 224 | public void setFocusedCell(int row, int col, boolean wait) { 225 | Scheduler.get().scheduleFinally(new Scheduler.ScheduledCommand() { 226 | @Override 227 | public void execute() { 228 | if (wait) { 229 | Scheduler.get().scheduleFixedDelay(new Scheduler.RepeatingCommand() { 230 | @Override 231 | public boolean execute() { 232 | if (grid.isWorkPending()) return true; 233 | else return false; 234 | } 235 | }, 100); 236 | } 237 | focusTracker.wasProgrammatic(); 238 | GridViolators.setFocusedCell(grid,row,col); 239 | } 240 | }); 241 | } 242 | 243 | @Override 244 | public void closeEditor() { 245 | editorManager.closeEditor(true); 246 | } 247 | 248 | @Override 249 | public void editRow(int rowIndex, int columnIndexDOM) { 250 | Scheduler.get().scheduleDeferred(() -> { 251 | editorManager.openEditor(rowIndex, columnIndexDOM, -1, false); 252 | }); 253 | } 254 | }); 255 | 256 | editorManager.addListener(new EditorListener() { 257 | @Override 258 | public void editorOpened(Grid grid, Editor editor, 259 | int row, int col, int lockId, int keyCode, boolean isUserOriginated) { 260 | editorManager.clearDisabledColumns(); 261 | if(getState().hasEditorOpenListener) { 262 | rpc.editorOpened(row, col, lockId, keyCode, isUserOriginated); 263 | } 264 | Scheduler.get().scheduleFinally(() -> { 265 | doEditorScrollOffsetFix(); 266 | }); 267 | } 268 | 269 | @Override 270 | public void editorClosed(Grid grid, Editor editor, 271 | int row, int col, boolean cancel) { 272 | editorManager.clearDisabledColumns(); 273 | if(getState().hasEditorCloseListener) { 274 | rpc.editorClosed(row, col, cancel); 275 | } 276 | } 277 | 278 | @Override 279 | public void dataChanged(Grid grid, Editor editor, 280 | Widget widget, String newContent, String oldContent, 281 | int row, int col, boolean moved) { 282 | if(getState().hasCellEditListener) { 283 | rpc.cellUpdated(row, col, newContent, oldContent, moved); 284 | } 285 | if(getState().hasRowEditListener) { 286 | rpc.rowUpdated(row, moved); 287 | } 288 | } 289 | 290 | @Override 291 | public void clickOut(Grid grid) { 292 | if(getState().hasClickOutListener) { 293 | rpc.clickOut(); 294 | } 295 | 296 | } 297 | }); 298 | 299 | focusTracker.addListener(new FocusListener() { 300 | @Override 301 | public void focusMoved(int currentRow, int currentCol, int lastRow, 302 | int lastCol, boolean isUserOriginated) { 303 | editorManager.notifyIfDataChanged(lastRow, lastCol, lastRow != currentRow); 304 | editorManager.saveOldContent(currentCol); 305 | if(getState().hasFocusListener) { 306 | rpc.focusUpdated(currentRow, currentCol, isUserOriginated); 307 | } 308 | } 309 | }); 310 | 311 | updateCloseShortcuts(); 312 | updateSaveShortcuts(); 313 | updateOpenShortcuts(); 314 | updateFocusTracking(); 315 | } 316 | 317 | public FocusTracker getFocusTracker() { 318 | return focusTracker; 319 | } 320 | 321 | public void requestValidate(boolean move) { 322 | rpc.forceValidate(move); 323 | } 324 | 325 | @OnStateChange("rowValidation") 326 | void setRowValidation() { 327 | editorManager.setRowValidation(getState().rowValidation); 328 | } 329 | 330 | @OnStateChange("saveWithCtrlS") 331 | void saveWithCtrlS() { 332 | editorManager.setSaveWithCtrlS(getState().saveWithCtrlS); 333 | } 334 | 335 | @OnStateChange("openEditorWithSingleClick") 336 | void openEditorWithSingleClick() { 337 | editorManager.setOpenEditorWithSingleClick(getState().openEditorWithSingleClick); 338 | } 339 | 340 | @OnStateChange("changeColumnAfterLastRow") 341 | void changeColumnAfterLastRow() { 342 | editorManager.setChangeColumnAfterLastRow(getState().changeColumnAfterLastRow); 343 | } 344 | 345 | @OnStateChange("allowArrowRowChange") 346 | void updateArrowKeyBehavior() { 347 | editorManager.setAllowRowChangeWithArrow(getState().allowArrowRowChange); 348 | } 349 | 350 | @OnStateChange("allowTabRowChange") 351 | void updateEditorTabBehavior() { 352 | editorManager.setAllowTabRowChange(getState().allowTabRowChange); 353 | } 354 | 355 | @OnStateChange("openEditorOnType") 356 | void updateEditorOpenOnType() { 357 | editorManager.setOpenEditorByTyping(getState().openEditorOnType); 358 | } 359 | 360 | @OnStateChange("enableSelectedStyle") 361 | void updateEditorSelectedStyleName() { 362 | editorManager.enableSelectedStyle(getState().enableSelectedStyle); 363 | } 364 | 365 | @OnStateChange("selectTextOnEditorOpen") 366 | void updateSelectAll() { 367 | editorManager.setSelectTextOnFocus(getState().selectTextOnEditorOpen); 368 | } 369 | 370 | @OnStateChange("openShortcuts") 371 | void updateOpenShortcuts() { 372 | editorManager.clearOpenShortcuts(); 373 | for (int sc : getState().openShortcuts) { 374 | editorManager.addOpenShortcut(sc); 375 | } 376 | } 377 | 378 | @OnStateChange("closeShortcuts") 379 | void updateCloseShortcuts() { 380 | editorManager.clearCloseShortcuts(); 381 | for (int sc : getState().closeShortcuts) { 382 | editorManager.addCloseShortcut(sc); 383 | } 384 | } 385 | 386 | @OnStateChange("saveShortcuts") 387 | void updateSaveShortcuts() { 388 | editorManager.clearSaveShortcuts(); 389 | for (int sc : getState().saveShortcuts) { 390 | editorManager.addSaveShortcut(sc); 391 | } 392 | } 393 | 394 | @OnStateChange("hasClickOutListener") 395 | void addClickOutListener() { 396 | editorManager.addClickOutListener(); 397 | } 398 | 399 | @OnStateChange("homeEndEnabled") 400 | void setHomeEndEnabled() { 401 | editorManager.setHomeEndEnabled(getState().homeEndEnabled); 402 | } 403 | 404 | @OnStateChange({"hasFocusListener", "hasCellFocusListener", "hasRowFocusListener", "hasRowEditListener", "hasCellEditListener", "hasEditorOpenListener" }) 405 | void updateFocusTracking() { 406 | FastNavigationState state = getState(); 407 | if (state.hasFocusListener || state.hasCellFocusListener || state.hasRowFocusListener || state.hasCellEditListener || state.hasRowEditListener || state.hasEditorOpenListener) { 408 | focusTracker.start(); 409 | if (state.hasEditorOpenListener) editorManager.setWaitForExternalUnlock(state.hasFocusListener); 410 | else editorManager.setWaitForExternalUnlock(false); 411 | } else { 412 | focusTracker.stop(); 413 | } 414 | } 415 | 416 | @Override 417 | public FastNavigationState getState() { 418 | return ((FastNavigationState) super.getState()); 419 | } 420 | } 421 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/java/org/vaadin/patrik/client/EditorWidgets.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik.client; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.logging.Logger; 6 | 7 | import com.google.gwt.user.client.ui.Widget; 8 | import com.vaadin.client.ui.VCheckBox; 9 | import com.vaadin.client.ui.VColorPicker; 10 | import com.vaadin.client.ui.VComboBox; 11 | import com.vaadin.client.ui.VNativeSelect; 12 | import com.vaadin.client.ui.VPopupCalendar; 13 | import com.vaadin.client.ui.VPopupTimeCalendar; 14 | import com.vaadin.client.ui.VTextArea; 15 | import com.vaadin.client.ui.VTextField; 16 | 17 | public class EditorWidgets { 18 | 19 | private static final Logger logger = Logger.getLogger("EditorWidgets"); 20 | 21 | /** 22 | * This is interface for WidgetHandler. Purpose of the WidgetHandlers is to give 23 | * uniform API of Editor widgets for the internal logic of the GridFastNavigation. 24 | * This is necessary since unfortunately there are API differences between 25 | * different editor widgets. 26 | * 27 | * Use registerHandler(..) method to register new handlers 28 | * 29 | * @author Tatu Lund 30 | * 31 | * @param The type parameter, i.e. editor widget class to be wrapped 32 | */ 33 | public interface WidgetHandler { 34 | 35 | /** 36 | * Select the content of the field. If it is not possible implement as NOP. 37 | * 38 | * @param widget The editor widget 39 | */ 40 | void selectAll(T widget); 41 | 42 | /** 43 | * Get value of the editor widget 44 | * 45 | * @param widget The editor widget 46 | * @return Current value of the editor widget 47 | */ 48 | String getValue(T widget); 49 | 50 | /** 51 | * Set the value of the Editor widget 52 | * 53 | * @param widget The editor widget 54 | * @param value Value as string 55 | */ 56 | void setValue(T widget, String value); 57 | 58 | /** 59 | * Force focus to widget 60 | * 61 | * @param widget The editor widget 62 | */ 63 | void focus(T widget); 64 | 65 | /** 66 | * Make the widget editable 67 | * 68 | * @param widget The editor widget 69 | */ 70 | void enable(T widget); 71 | 72 | /** 73 | * Make the widget uneditable 74 | * 75 | * @param widget The editor widget 76 | */ 77 | void disable(T widget); 78 | 79 | /** 80 | * Return true, if it is more natural that with this 81 | * widget cursor up/down should not change Grid row 82 | * 83 | * @param widget The editor widget 84 | * @return return true/false 85 | */ 86 | boolean isUpDownNavAllowed(T widget); 87 | 88 | /** 89 | * Return true if widget is currently editable 90 | * 91 | * @param widget The editor widget 92 | * @return true/false 93 | */ 94 | boolean isReadOnly(T widget); 95 | } 96 | 97 | private static final Map, WidgetHandler> widgetHandlers; 98 | 99 | // 100 | // Magic happens here: statically assign handlers for supported widget types 101 | // This enables support for value revert, append and selectAll 102 | // 103 | 104 | /** 105 | * Register a new widget handler. Purpose 106 | * 107 | * @param Type of the widget 108 | * @param clazz Class name of the editor widget to be registered 109 | * @param handler The handler, implements WidgetHandler interface 110 | */ 111 | public static void registerHandler(Class clazz, 112 | WidgetHandler handler) { 113 | widgetHandlers.put(clazz, handler); 114 | } 115 | 116 | static { 117 | widgetHandlers = new HashMap, WidgetHandler>(); 118 | 119 | registerHandler(VTextField.class, new WidgetHandler() { 120 | @Override 121 | public void selectAll(VTextField widget) { 122 | if (widget.isEnabled()) { 123 | widget.selectAll(); 124 | } 125 | } 126 | 127 | @Override 128 | public String getValue(VTextField widget) { 129 | return widget.getValue(); 130 | } 131 | 132 | @Override 133 | public void setValue(VTextField widget, String value) { 134 | widget.setValue(value); 135 | widget.getElement().blur(); 136 | widget.getElement().focus(); 137 | } 138 | 139 | public void focus(VTextField widget) { 140 | if (widget.isEnabled()) { 141 | widget.getElement().blur(); 142 | widget.getElement().focus(); 143 | } 144 | } 145 | 146 | @Override 147 | public void enable(VTextField widget) { 148 | widget.setEnabled(true); 149 | widget.setReadOnly(false); 150 | } 151 | 152 | @Override 153 | public void disable(VTextField widget) { 154 | widget.setEnabled(false); 155 | widget.setReadOnly(true); 156 | } 157 | 158 | @Override 159 | public boolean isUpDownNavAllowed(VTextField widget) { 160 | return true; 161 | } 162 | 163 | @Override 164 | public boolean isReadOnly(VTextField widget) { 165 | return widget.isReadOnly(); 166 | } 167 | }); 168 | 169 | registerHandler(VTextArea.class, new WidgetHandler() { 170 | @Override 171 | public void selectAll(VTextArea widget) { 172 | if (widget.isEnabled()) { 173 | widget.selectAll(); 174 | } 175 | } 176 | 177 | @Override 178 | public String getValue(VTextArea widget) { 179 | return widget.getValue(); 180 | } 181 | 182 | @Override 183 | public void setValue(VTextArea widget, String value) { 184 | widget.setValue(value); 185 | widget.getElement().blur(); 186 | widget.getElement().focus(); 187 | } 188 | 189 | public void focus(VTextArea widget) { 190 | if (widget.isEnabled()) { 191 | widget.getElement().blur(); 192 | widget.getElement().focus(); 193 | } 194 | } 195 | 196 | @Override 197 | public void enable(VTextArea widget) { 198 | widget.setEnabled(true); 199 | widget.setReadOnly(false); 200 | } 201 | 202 | @Override 203 | public void disable(VTextArea widget) { 204 | widget.setEnabled(false); 205 | widget.setReadOnly(true); 206 | } 207 | 208 | @Override 209 | public boolean isUpDownNavAllowed(VTextArea widget) { 210 | return false; 211 | } 212 | 213 | @Override 214 | public boolean isReadOnly(VTextArea widget) { 215 | return widget.isReadOnly(); 216 | } 217 | }); 218 | 219 | registerHandler(VNativeSelect.class, new WidgetHandler() { 220 | @Override 221 | public void selectAll(VNativeSelect widget) { 222 | } 223 | 224 | @Override 225 | public String getValue(VNativeSelect widget) { 226 | return widget.getListBox().getSelectedValue(); 227 | } 228 | 229 | @Override 230 | public void setValue(VNativeSelect widget, String value) { 231 | widget.setSelectedItem(value); 232 | } 233 | 234 | @Override 235 | public void focus(VNativeSelect widget) { 236 | if (widget.getListBox().isEnabled() ) { 237 | widget.getElement().blur(); 238 | widget.focus(); 239 | } 240 | } 241 | 242 | @Override 243 | public void enable(VNativeSelect widget) { 244 | widget.getListBox().setEnabled(true); 245 | } 246 | 247 | @Override 248 | public void disable(VNativeSelect widget) { 249 | widget.getListBox().setEnabled(false); 250 | } 251 | 252 | @Override 253 | public boolean isUpDownNavAllowed(VNativeSelect widget) { 254 | return false; 255 | } 256 | 257 | @Override 258 | public boolean isReadOnly(VNativeSelect widget) { 259 | return !widget.getListBox().isEnabled(); 260 | } 261 | }); 262 | 263 | registerHandler(VComboBox.class, new WidgetHandler() { 264 | @Override 265 | public void selectAll(VComboBox widget) { 266 | if (!widget.tb.isReadOnly()) { 267 | widget.tb.selectAll(); 268 | } 269 | } 270 | 271 | @Override 272 | public String getValue(VComboBox widget) { 273 | return widget.tb.getValue(); 274 | } 275 | 276 | @Override 277 | public void setValue(VComboBox widget, String value) { 278 | widget.tb.setValue(value); 279 | } 280 | 281 | @Override 282 | public void focus(VComboBox widget) { 283 | if (widget.enabled) { 284 | widget.getElement().blur(); 285 | widget.focus(); 286 | } 287 | } 288 | 289 | @Override 290 | public void enable(VComboBox widget) { 291 | widget.enabled = true; 292 | widget.tb.setEnabled(true); 293 | } 294 | 295 | @Override 296 | public void disable(VComboBox widget) { 297 | widget.enabled = false; 298 | widget.tb.setEnabled(false); 299 | } 300 | 301 | @Override 302 | public boolean isUpDownNavAllowed(VComboBox widget) { 303 | return false; 304 | } 305 | 306 | @Override 307 | public boolean isReadOnly(VComboBox widget) { 308 | return widget.tb.isReadOnly(); 309 | } 310 | }); 311 | 312 | registerHandler(VColorPicker.class, new WidgetHandler() { 313 | @Override 314 | public void selectAll(VColorPicker widget) { 315 | } 316 | 317 | @Override 318 | public String getValue(VColorPicker widget) { 319 | return widget.getColor(); 320 | } 321 | 322 | @Override 323 | public void setValue(VColorPicker widget, String value) { 324 | widget.setColor(value); 325 | } 326 | 327 | @Override 328 | public void focus(VColorPicker widget) { 329 | if (widget.isEnabled()) { 330 | widget.getElement().blur(); 331 | widget.getElement().focus(); 332 | } 333 | } 334 | 335 | @Override 336 | public void enable(VColorPicker widget) { 337 | widget.setEnabled(true); 338 | } 339 | 340 | @Override 341 | public void disable(VColorPicker widget) { 342 | widget.setEnabled(false); 343 | } 344 | 345 | @Override 346 | public boolean isUpDownNavAllowed(VColorPicker widget) { 347 | return false; 348 | } 349 | 350 | @Override 351 | public boolean isReadOnly(VColorPicker widget) { 352 | return !widget.isEnabled(); 353 | } 354 | }); 355 | 356 | registerHandler(VCheckBox.class, new WidgetHandler() { 357 | @Override 358 | public String getValue(VCheckBox widget) { 359 | String value = ""; 360 | if (widget.getValue()) value = "true"; 361 | else value = "false"; 362 | return value; 363 | } 364 | 365 | @Override 366 | public void setValue(VCheckBox widget, String valueText) { 367 | Boolean value = false; 368 | if ("true".equals(valueText)) value = true; 369 | widget.setValue(value); 370 | } 371 | 372 | @Override 373 | public void focus(VCheckBox widget) { 374 | if (widget.isEnabled()) { 375 | widget.getElement().blur(); 376 | widget.getElement().focus(); 377 | } 378 | } 379 | 380 | @Override 381 | public void enable(VCheckBox widget) { 382 | widget.setEnabled(true); 383 | } 384 | 385 | @Override 386 | public void disable(VCheckBox widget) { 387 | widget.setEnabled(false); 388 | } 389 | 390 | @Override 391 | public boolean isUpDownNavAllowed(VCheckBox widget) { 392 | return true; 393 | } 394 | 395 | @Override 396 | public void selectAll(VCheckBox widget) { 397 | // TODO Auto-generated method stub 398 | } 399 | 400 | @Override 401 | public boolean isReadOnly(VCheckBox widget) { 402 | return !widget.isEnabled(); 403 | } 404 | }); 405 | 406 | registerHandler(VPopupCalendar.class, 407 | new WidgetHandler() { 408 | @Override 409 | public void selectAll(VPopupCalendar widget) { 410 | if (widget.isEnabled()) { 411 | widget.text.selectAll(); 412 | } 413 | } 414 | 415 | @Override 416 | public String getValue(VPopupCalendar widget) { 417 | return widget.text.getValue(); 418 | } 419 | 420 | @Override 421 | public void setValue(VPopupCalendar widget, String value) { 422 | widget.text.setValue(value); 423 | } 424 | 425 | @Override 426 | public void focus(VPopupCalendar widget) { 427 | // Only perform blur/focus refocusing if calendar popup 428 | // is not visible 429 | if (widget.isEnabled() 430 | && !widget.calendar.isAttached()) { 431 | widget.getElement().blur(); 432 | widget.getElement().focus(); 433 | } 434 | } 435 | 436 | @Override 437 | public void enable(VPopupCalendar widget) { 438 | widget.setEnabled(true); 439 | widget.setReadonly(false); 440 | } 441 | 442 | @Override 443 | public void disable(VPopupCalendar widget) { 444 | widget.setEnabled(false); 445 | widget.setReadonly(true); 446 | } 447 | 448 | @Override 449 | public boolean isUpDownNavAllowed(VPopupCalendar widget) { 450 | return false; 451 | } 452 | 453 | @Override 454 | public boolean isReadOnly(VPopupCalendar widget) { 455 | return widget.isReadonly(); 456 | } 457 | }); 458 | 459 | registerHandler(VPopupTimeCalendar.class, 460 | new WidgetHandler() { 461 | @Override 462 | public void selectAll(VPopupTimeCalendar widget) { 463 | if (widget.isEnabled()) { 464 | widget.text.selectAll(); 465 | } 466 | } 467 | 468 | @Override 469 | public String getValue(VPopupTimeCalendar widget) { 470 | return widget.text.getValue(); 471 | } 472 | 473 | @Override 474 | public void setValue(VPopupTimeCalendar widget, String value) { 475 | widget.text.setValue(value); 476 | } 477 | 478 | @Override 479 | public void focus(VPopupTimeCalendar widget) { 480 | // Only perform blur/focus refocusing if calendar popup 481 | // is not visible 482 | if (widget.isEnabled() 483 | && !widget.calendar.isAttached()) { 484 | widget.getElement().blur(); 485 | widget.getElement().focus(); 486 | } 487 | } 488 | 489 | @Override 490 | public void enable(VPopupTimeCalendar widget) { 491 | widget.setEnabled(true); 492 | widget.setReadonly(false); 493 | } 494 | 495 | @Override 496 | public void disable(VPopupTimeCalendar widget) { 497 | widget.setEnabled(false); 498 | widget.setReadonly(true); 499 | } 500 | 501 | @Override 502 | public boolean isUpDownNavAllowed(VPopupTimeCalendar widget) { 503 | return false; 504 | } 505 | 506 | @Override 507 | public boolean isReadOnly(VPopupTimeCalendar widget) { 508 | return widget.isReadonly(); 509 | } 510 | }); 511 | 512 | 513 | // TODO: support more widget types! 514 | } 515 | 516 | private static WidgetHandler getHandler(Class widgetClass) { 517 | if (widgetHandlers.containsKey(widgetClass)) { 518 | return widgetHandlers.get(widgetClass); 519 | } else { 520 | for (Class key : widgetHandlers.keySet()) { 521 | // Note: GWT does not support isAssignableFrom 522 | Class cls = widgetClass; 523 | while (cls.getSuperclass() != null) { 524 | if (cls.getSuperclass() == key) { 525 | return widgetHandlers.get(key); 526 | } 527 | cls = cls.getSuperclass(); 528 | } 529 | } 530 | logger.warning("Unhandled widget type " + widgetClass.getSimpleName()); 531 | } 532 | return null; 533 | } 534 | 535 | // 536 | // Public API 537 | // 538 | 539 | @SuppressWarnings("unchecked") 540 | public static void selectAll(T widget) { 541 | if (widget != null) { 542 | WidgetHandler handler = getHandler(widget.getClass()); 543 | if (handler != null) { 544 | ((WidgetHandler) handler).selectAll(widget); 545 | } 546 | } else { 547 | logger.warning("EditorWidgets.selectAll: Widget is null"); 548 | } 549 | } 550 | 551 | @SuppressWarnings("unchecked") 552 | public static String getValue(T widget) { 553 | if (widget != null) { 554 | WidgetHandler handler = getHandler(widget.getClass()); 555 | if (handler != null) { 556 | return ((WidgetHandler) handler).getValue(widget); 557 | } 558 | } else { 559 | logger.warning("EditorWidgets.getValue: Widget is null"); 560 | } 561 | return ""; 562 | } 563 | 564 | @SuppressWarnings("unchecked") 565 | public static void setValue(T widget, String value) { 566 | if (widget != null) { 567 | WidgetHandler handler = getHandler(widget.getClass()); 568 | if (handler != null) { 569 | ((WidgetHandler) handler).setValue(widget, value); 570 | } 571 | } else { 572 | logger.warning("EditorWidgets.setValue: Widget is null"); 573 | } 574 | } 575 | 576 | @SuppressWarnings("unchecked") 577 | public static void focus(T widget) { 578 | if (widget != null) { 579 | WidgetHandler handler = getHandler(widget.getClass()); 580 | if (handler != null) { 581 | ((WidgetHandler) handler).focus(widget); 582 | } 583 | } else { 584 | logger.warning("EditorWidgets.focus: Widget is null"); 585 | } 586 | } 587 | 588 | @SuppressWarnings("unchecked") 589 | public static void enable(T widget) { 590 | if (widget != null) { 591 | WidgetHandler handler = getHandler(widget.getClass()); 592 | if (handler != null) { 593 | ((WidgetHandler) handler).enable(widget); 594 | } 595 | } else { 596 | logger.warning("EditorWidgets.enable: Widget is null"); 597 | } 598 | } 599 | 600 | @SuppressWarnings("unchecked") 601 | public static void disable(T widget) { 602 | if (widget != null) { 603 | WidgetHandler handler = getHandler(widget.getClass()); 604 | if (handler != null) { 605 | ((WidgetHandler) handler).disable(widget); 606 | } 607 | } else { 608 | logger.warning("EditorWidgets.disable: Widget is null"); 609 | } 610 | } 611 | 612 | @SuppressWarnings("unchecked") 613 | public static boolean isUpDownNavAllowed(T widget) { 614 | if (widget != null) { 615 | WidgetHandler handler = getHandler(widget.getClass()); 616 | if (handler != null) { 617 | return ((WidgetHandler) handler).isUpDownNavAllowed(widget); 618 | } 619 | } else { 620 | logger.warning("EditorWidgets.isUpDownNavAllowed: Widget is null"); 621 | } 622 | return true; 623 | } 624 | 625 | @SuppressWarnings("unchecked") 626 | public static boolean isReadOnly(T widget) { 627 | if (widget != null) { 628 | WidgetHandler handler = getHandler(widget.getClass()); 629 | if (handler != null) { 630 | return ((WidgetHandler) handler).isReadOnly(widget); 631 | } 632 | } else { 633 | logger.warning("EditorWidgets.isReadOnly: Widget is null"); 634 | } 635 | return true; 636 | } 637 | } 638 | -------------------------------------------------------------------------------- /GridFastNavigation-addon/src/main/java/org/vaadin/patrik/FastNavigation.java: -------------------------------------------------------------------------------- 1 | package org.vaadin.patrik; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.logging.Logger; 6 | 7 | import org.vaadin.patrik.events.CellEditEvent; 8 | import org.vaadin.patrik.events.CellFocusEvent; 9 | import org.vaadin.patrik.events.ClickOutEvent; 10 | import org.vaadin.patrik.events.EditorCloseEvent; 11 | import org.vaadin.patrik.events.EditorOpenEvent; 12 | import org.vaadin.patrik.events.EventListenerList; 13 | import org.vaadin.patrik.events.Listener; 14 | import org.vaadin.patrik.events.RowEditEvent; 15 | import org.vaadin.patrik.events.RowFocusEvent; 16 | import org.vaadin.patrik.helper.OffsetHelper; 17 | import org.vaadin.patrik.shared.FastNavigationClientRPC; 18 | import org.vaadin.patrik.shared.FastNavigationServerRPC; 19 | import org.vaadin.patrik.shared.FastNavigationState; 20 | 21 | import com.vaadin.data.BinderValidationStatus; 22 | import com.vaadin.server.AbstractExtension; 23 | import com.vaadin.ui.Grid; 24 | 25 | /** 26 | * GridFastNavigation is a component extension for Vaadin Grid, which uses the 27 | * unbuffered editing mode and alters its keyboard controls to provide a faster 28 | * and advanced editing experience. This extension provides also number of other 29 | * improvements to Grid's unbuffered editors and features some bug fixes and 30 | * workarounds. 31 | * 32 | * @author Tatu Lund 33 | * 34 | * @param Bean type of the Grid 35 | */ 36 | @SuppressWarnings("serial") 37 | public class FastNavigation extends AbstractExtension { 38 | 39 | private static Logger _logger = Logger.getLogger("FastNavigation"); 40 | 41 | private OffsetHelper offsetHelper = new OffsetHelper(); 42 | 43 | private static Logger getLogger() { 44 | return _logger; 45 | } 46 | 47 | private Grid grid = null; 48 | 49 | // 50 | // Event interfaces 51 | // 52 | 53 | /** 54 | * RowEditListener is used for observing {@link RowEditEvent}, which is emitted when item has been edited. 55 | * 56 | * @see RowEditEvent 57 | * @see FastNavigation#addRowEditListener(RowEditListener) 58 | */ 59 | public interface RowEditListener extends Listener> { 60 | } 61 | 62 | private final EventListenerList> rowEditListeners = new EventListenerList>(); 63 | 64 | /** 65 | * CellEditListener is used for observing {@link CellEditEvent}, which is emitted when item has been edited. 66 | * 67 | * @see CellEditEvent 68 | * @see FastNavigation#addCellEditListener(CellEditListener) 69 | */ 70 | public interface CellEditListener extends Listener> { 71 | } 72 | 73 | private final EventListenerList> cellEditListeners = new EventListenerList>(); 74 | 75 | /** 76 | * CellFocusListener is used for observing {@link CellFocusEvent}, which is emitted when focused cell changes 77 | * 78 | * @see CellFocusEvent 79 | * @see FastNavigation#addCellFocusListener(CellFocusListener) 80 | */ 81 | public interface CellFocusListener extends Listener> { 82 | } 83 | 84 | private final EventListenerList> cellFocusListeners = new EventListenerList>(); 85 | 86 | /** 87 | * RowFocusListener is used for observing {@link RowFocusEvent}, which is emitted when focused row changes 88 | * 89 | * @see RowFocusEvent 90 | * @see FastNavigation#addRowFocusListener(RowFocusListener) 91 | * 92 | */ 93 | public interface RowFocusListener extends Listener> { 94 | } 95 | 96 | private final EventListenerList> rowFocusListeners = new EventListenerList>(); 97 | 98 | /** 99 | * EditorOpenListener is used for observing {@link EditorOpenEvent}, which is emitted when Editor has been opened. 100 | * Note: Editor is closed and opened when it is moved to a new orw 101 | * 102 | * @see EditorOpenEvent 103 | * @see FastNavigation#addEditorOpenListener(EditorOpenListener) 104 | */ 105 | public interface EditorOpenListener extends Listener> { 106 | } 107 | 108 | private final EventListenerList> editorOpenListeners = new EventListenerList>(); 109 | 110 | /** 111 | * EditorOpenListener is used for observing {@link EditorCloseEvent}, which is emitted when Editor has been closed. 112 | * Note: Editor is closed and opened when it is moved to a new orw 113 | * 114 | * @see EditorCloseEvent 115 | * @see FastNavigation#addEditorCloseListener(EditorCloseListener) 116 | */ 117 | public interface EditorCloseListener extends Listener> { 118 | } 119 | 120 | private final EventListenerList> editorCloseListeners = new EventListenerList>(); 121 | 122 | /** 123 | * ClickOutListener is used for observing {@link ClickOutEvent} which is simulated blur event, and emitted when user clicks outside of the Grid 124 | * 125 | * @since 2.1.8 126 | * 127 | * @see ClickOutEvent 128 | * @see FastNavigation#addClickOutListener(ClickOutListener) 129 | */ 130 | public interface ClickOutListener extends Listener { 131 | } 132 | 133 | private final EventListenerList clickOutListeners = new EventListenerList(); 134 | 135 | 136 | // 137 | // Actual class stuff 138 | // 139 | 140 | // Mirror state value here to avoid unnecessary comms 141 | private boolean hasRowFocusListener = false; 142 | 143 | // Mirror state value here to avoid unnecessary comms 144 | private boolean hasCellFocusListener = false; 145 | 146 | // Information about previously seen focused row 147 | private int lastFocusedRow = 0; 148 | private int lastFocusedCol = 0; 149 | private T editedItem = null; 150 | private int editedRow = -1; 151 | private T previousEditedItem = null; 152 | private int previousEditedRow = -1; 153 | 154 | /** 155 | * Default constructor. Enter key changes the row. 156 | * 157 | * @param g Grid to extend 158 | */ 159 | public FastNavigation(final Grid g) { 160 | setupFastNavigation(g,false,false); 161 | } 162 | 163 | /** 164 | * Alternative constructor to set enter key change column instead of a row. 165 | * 166 | * @param g Grid to extend 167 | * @param changeColumnOnEnter Set Enter key behavior true = Enter changes the column like tab, false = Enter changes the row 168 | */ 169 | public FastNavigation(final Grid g, boolean changeColumnOnEnter) { 170 | setupFastNavigation(g,changeColumnOnEnter,false); 171 | } 172 | 173 | /** 174 | * Alternative constructor to set enter key change column instead of a row. 175 | * 176 | * @param g Grid to extend 177 | * @param changeColumnOnEnter Set Enter key behavior true = Enter changes the column like tab, false = Enter changes the row 178 | * @param dispatchEditEventOnBlur Set Blur event behavior. If set to true, Editor is closed and possible Edit event is dispatched when user clicks outside Grid 179 | */ 180 | public FastNavigation(final Grid g, boolean changeColumnOnEnter, boolean dispatchEditEventOnBlur) { 181 | setupFastNavigation(g,changeColumnOnEnter,dispatchEditEventOnBlur); 182 | } 183 | 184 | // Internal implementation of the constructor 185 | private void setupFastNavigation(final Grid g, boolean changeColumnOnEnter, boolean dispatchEditEventOnBlur) { 186 | getState().changeColumnOnEnter = changeColumnOnEnter; 187 | getState().dispatchEditEventOnBlur = dispatchEditEventOnBlur; 188 | grid = g; 189 | grid.getEditor().setBuffered(false); 190 | 191 | registerRpc(new FastNavigationServerRPC() { 192 | 193 | private T getItemAt(int rowIndex) { 194 | T myBean = null; 195 | if (rowIndex >= 0 && g.getDataCommunicator().getDataProviderSize() > 0) { 196 | List beanList = g.getDataCommunicator().fetchItemsWithRange(rowIndex, 1); 197 | if (!beanList.isEmpty()) { 198 | myBean = beanList.get(0); 199 | } 200 | } 201 | return myBean; 202 | } 203 | 204 | @Override 205 | public void rowUpdated(int rowIndex, boolean moved) { 206 | T item = null; 207 | int row = rowIndex; 208 | if (!moved) { 209 | item = getItemAt(rowIndex); 210 | } else { 211 | item = previousEditedItem; 212 | row = previousEditedRow; 213 | } 214 | rowEditListeners.dispatch(new RowEditEvent(grid, row, item)); 215 | } 216 | 217 | @Override 218 | public void cellUpdated(int rowIndex, int colIndex, String newData, String oldData, boolean moved) { 219 | T item = null; 220 | int row = rowIndex; 221 | if (!moved) { 222 | item = getItemAt(rowIndex); 223 | } else { 224 | item = previousEditedItem; 225 | row = previousEditedRow; 226 | } 227 | int offset = offsetHelper.calculateOffset(g); 228 | cellEditListeners.dispatch(new CellEditEvent(grid, row, colIndex - offset, newData, oldData, item)); 229 | } 230 | 231 | @Override 232 | public void focusUpdated(int rowIndex, int colIndex, boolean isUserOriginated) { 233 | T item = getItemAt(rowIndex); 234 | int offset = offsetHelper.calculateOffset(g); 235 | colIndex = colIndex - offset; // apply offset based on selection mode 236 | if (hasRowFocusListener && rowIndex != lastFocusedRow) { 237 | rowFocusListeners.dispatch(new RowFocusEvent(grid, rowIndex, item)); 238 | } 239 | 240 | if (hasCellFocusListener && (rowIndex != lastFocusedRow || colIndex != lastFocusedCol)) { 241 | cellFocusListeners.dispatch(new CellFocusEvent(grid, rowIndex, colIndex, 242 | lastFocusedRow != rowIndex, 243 | lastFocusedCol != colIndex, item, isUserOriginated)); 244 | } 245 | 246 | lastFocusedRow = rowIndex; 247 | lastFocusedCol = colIndex; 248 | } 249 | 250 | @Override 251 | public void editorOpened(int rowIndex, int colIndex, int lockId, int keyCode, boolean isUserOriginated) { 252 | T item = getItemAt(rowIndex); 253 | int offset = offsetHelper.calculateOffset(grid); 254 | previousEditedItem = editedItem; 255 | previousEditedRow = editedRow; 256 | editedItem = item; 257 | editedRow = rowIndex; 258 | EditorOpenEvent ev = new EditorOpenEvent<>(grid, rowIndex, colIndex - offset, item, keyCode, isUserOriginated); 259 | editorOpenListeners.dispatch(ev); 260 | // Update disabled columns or readonly fields status if changed dynamically 261 | ArrayList disabledColumns = new ArrayList(); 262 | for (int i=0;i(grid, rowIndex, colIndex, wasCancelled)); 289 | } 290 | 291 | @Override 292 | public void clickOut() { 293 | clickOutListeners.dispatch(new ClickOutEvent(grid)); 294 | } 295 | 296 | @Override 297 | public void forceValidate(boolean move) { 298 | BinderValidationStatus status = grid.getEditor().getBinder().validate(); 299 | if (status.hasErrors() && move) getRPC().validationHasErrors(); 300 | } 301 | 302 | }, FastNavigationServerRPC.class); 303 | 304 | extend(grid); 305 | } 306 | 307 | private FastNavigationClientRPC getRPC() { 308 | return getRpcProxy(FastNavigationClientRPC.class); 309 | } 310 | 311 | @Override 312 | public FastNavigationState getState() { 313 | return (FastNavigationState) super.getState(); 314 | } 315 | 316 | 317 | /** 318 | * Set focused cell programmatically and scrolls Grid to target if focus was changed 319 | * 320 | * Note: This method works correctly only after Grid has been fully rendered, since 321 | * otherwise target cell might not be in the DOM yet. 322 | * 323 | * @since 2.3.4 324 | * 325 | * @param row Target row 326 | * @param col Target column 327 | * @throws IllegalArgumentException if row or column is not in acceptable range 328 | */ 329 | public void setFocusedCell(int row, int col) { 330 | setFocusedCell(row,col,false); 331 | } 332 | 333 | 334 | /** 335 | * If enabled Editor on the row that has been selected will be augmented with 336 | * v-grid-editor-selected class name for additional styling. 337 | * 338 | * Note: This feature has been added to Vaadin 8.9 and no need to use with it 339 | * 340 | * @since 2.4.6 341 | * 342 | * @param enabled If true, then editor on selected row will have -selected style 343 | */ 344 | public void enableEditorSelectedStyle(boolean enabled) { 345 | getState().enableSelectedStyle = enabled; 346 | } 347 | 348 | /** 349 | * Set focused cell programmatically and scrolls Grid to target if focus was changed. 350 | * The method attempts to wait until Gird is rendered if wait is set to true. 351 | * 352 | * @since 2.4.0 353 | * 354 | * @param row Target row 355 | * @param col Target column 356 | * @param wait Wait until Grid is ready before setting the focused cell 357 | * @throws IllegalArgumentException if row or column is not in acceptable range 358 | */ 359 | public void setFocusedCell(int row, int col, boolean wait) { 360 | if (col < 0 || col > grid.getColumns().size() || row < 0 || row > grid.getDataCommunicator().getDataProviderSize()) throw new IllegalArgumentException("Target row or column out of boundaries"); 361 | getRPC().setFocusedCell(row, col, wait); 362 | } 363 | 364 | /** 365 | * If set to true (default = true), editor opens with single mouse click. 366 | * 367 | * Note, if this is set to true, Grid's selection listener and item click 368 | * listeners will not get the click. Selection event will work with 369 | * shift + space. 370 | * 371 | * @param enable Boolean value 372 | */ 373 | public void setOpenEditorWithSingleClick(boolean enable) { 374 | getState().openEditorWithSingleClick = enable; 375 | } 376 | 377 | /** 378 | * 379 | * @return true if Editor is set to open with single click 380 | */ 381 | public boolean getOpenEditorWithSingleClick() { 382 | return getState().openEditorWithSingleClick; 383 | } 384 | 385 | /** 386 | * If set to true (default = false), pressing enter on last row will change 387 | * focus to first row and change column to next editable column. Not applicable 388 | * if enter key is set to change column instead of row. 389 | * 390 | * @param enable Boolean value 391 | * @throws IllegalStateException if enter key is set to change column instead of row. 392 | */ 393 | public void setChangeColumnAfterLastRow(boolean enable) { 394 | if (getState().changeColumnOnEnter) throw new IllegalStateException("Cannot set change column after last row if enter is set to change column"); 395 | getState().changeColumnAfterLastRow = enable; 396 | } 397 | 398 | /** 399 | * 400 | * @return true if column is set to change after last row 401 | */ 402 | public boolean getChangeColumnAfterLastRow() { 403 | return getState().changeColumnAfterLastRow; 404 | } 405 | 406 | // 407 | // Tab capture 408 | // 409 | 410 | /** 411 | * If set to true (default = false), FastNavigation will attempt to trigger validation of the 412 | * whole row, and closing of editor is not possible if the validation error indicator is on. 413 | * Also FastNavigation will not jump to first error column. 414 | * 415 | * @since 2.2.2 416 | * 417 | * @param enable Boolean value 418 | */ 419 | public void setRowValidation(boolean enable) { 420 | getState().rowValidation = enable; 421 | } 422 | 423 | /** 424 | * Get status of row validation 425 | * 426 | * @since 2.2.2 427 | * 428 | * @return true if row validation mode is set on 429 | */ 430 | public boolean getRowValidation() { 431 | return getState().rowValidation; 432 | } 433 | 434 | /** 435 | * If set to true, tabbing outside the edge of the current row will wrap the 436 | * focus around and switch to the next/previous row. If false, tabbing will 437 | * wrap around the current row. 438 | * 439 | * @param enable Boolean value 440 | */ 441 | public void setAllowTabToChangeRow(boolean enable) { 442 | getState().allowTabRowChange = enable; 443 | } 444 | 445 | /** 446 | * 447 | * @return true if tabbing on last column changes the row 448 | */ 449 | public boolean getAllowTabToChangeRow() { 450 | return getState().allowTabRowChange; 451 | } 452 | 453 | /** 454 | * If set to true, text is selected when editor is opened 455 | * 456 | * @param enable Boolean value 457 | */ 458 | public void setSelectTextOnEditorOpen(boolean enable) { 459 | getState().selectTextOnEditorOpen = enable; 460 | } 461 | 462 | /** 463 | * 464 | * @return true if text in field is set to be selected when the Editor is being opened 465 | */ 466 | public boolean getSelectTextOnEditorOpen() { 467 | return getState().selectTextOnEditorOpen; 468 | } 469 | 470 | /** 471 | * If set to true, you can use the arrow keys to move the editor up and down 472 | * 473 | * @param enable Boolean value 474 | */ 475 | public void setAllowArrowToChangeRow(boolean enable) { 476 | getState().allowArrowRowChange = enable; 477 | } 478 | 479 | /** 480 | * 481 | * @return true if the arrow keys up and down are set to change the row 482 | */ 483 | public boolean getAllowArrowToChangeRow() { 484 | return getState().allowArrowRowChange; 485 | } 486 | 487 | 488 | /** 489 | * If set to true (=default), home and end keys are used 490 | * to move to first and last row, and shifted home and end 491 | * to corners of the Grid. 492 | * 493 | * @since 2.3.4 494 | * 495 | * @param enable Boolean value 496 | */ 497 | public void setHomeEndEnabled(boolean enable) { 498 | getState().homeEndEnabled = enable; 499 | } 500 | 501 | /** 502 | * 503 | * @return true if home and end keys are enabled 504 | */ 505 | public boolean getHomeEndEnabled() { 506 | return getState().homeEndEnabled; 507 | } 508 | 509 | // 510 | // Editor opening 511 | // 512 | 513 | /** 514 | * If set to true (default), focusing a Grid cell and then pressing an alpha- 515 | * numeric key will open the editor. If false, the editor must be activated 516 | * by double clicking or pressing ENTER or a custom editor opening shortcut key 517 | * 518 | * @param enable Boolean value 519 | */ 520 | public void setOpenEditorOnTyping(boolean enable) { 521 | getState().openEditorOnType = enable; 522 | } 523 | 524 | 525 | /** 526 | * 527 | * @return true if Editor is set to be opened by pressing any key 528 | */ 529 | public boolean getOpenEditorOnTyping() { 530 | return getState().openEditorOnType; 531 | } 532 | 533 | /** 534 | * Add extra Editor opening shortcut 535 | * 536 | * @param code The keycode 537 | */ 538 | public void addEditorOpenShortcut(int code) { 539 | getState().openShortcuts.add(code); 540 | } 541 | 542 | /** 543 | * Remove Editor opening shortcut 544 | * 545 | * @param code The keycode 546 | */ 547 | public void removeEditorOpenShortcut(int code) { 548 | getState().openShortcuts.remove(code); 549 | } 550 | 551 | /** 552 | * Remove all extra Editor opening shortcuts 553 | */ 554 | public void clearEditorOpenShortcuts() { 555 | getState().openShortcuts.clear(); 556 | } 557 | 558 | /** 559 | * Add Editor close/cancel extra shortcut 560 | * 561 | * @param code The keycode 562 | */ 563 | public void addEditorCloseShortcut(int code) { 564 | getState().closeShortcuts.add(code); 565 | } 566 | 567 | /** 568 | * Remove Editor close/cancel extra shortcut 569 | * 570 | * @param code The keycode 571 | */ 572 | public void removeEditorCloseShortcut(int code) { 573 | getState().closeShortcuts.remove(code); 574 | } 575 | 576 | /** 577 | * Remove all extra Editor close/cancel shortcuts 578 | */ 579 | public void clearEditorCloseShortcuts() { 580 | getState().closeShortcuts.clear(); 581 | } 582 | 583 | /** 584 | * Add Editor save extra shortcut 585 | * 586 | * @param code The keycode 587 | */ 588 | public void addEditorSaveShortcut(int code) { 589 | getState().saveShortcuts.add(code); 590 | } 591 | 592 | /** 593 | * Remove Editor save extra shortcut 594 | * 595 | * @param code The keycode 596 | */ 597 | public void removeSaveCloseShortcut(int code) { 598 | getState().saveShortcuts.remove(code); 599 | } 600 | 601 | /** 602 | * Remove all extra Editor save shortcuts 603 | */ 604 | public void clearSaveCloseShortcuts() { 605 | getState().saveShortcuts.clear(); 606 | } 607 | 608 | /** 609 | * Turn on saving by CTRL+S key combination 610 | * 611 | * @since 2.2.11 612 | * 613 | * @param enable Boolean value, true = CTRL+S saving enabled 614 | */ 615 | public void setSaveWithCtrlS(boolean enable) { 616 | getState().saveWithCtrlS = enable; 617 | } 618 | 619 | // 620 | // Event listeners 621 | // 622 | 623 | /** 624 | * Register row edit listener, which is triggered when cell value is being 625 | * changed. Useful to hook e.g. database commit on edit. 626 | * 627 | * @see RowEditEvent 628 | * 629 | * @param listener 630 | * an RowEditListener instance 631 | */ 632 | public void addRowEditListener(RowEditListener listener) { 633 | rowEditListeners.addListener(listener); 634 | getState().hasRowEditListener = true; 635 | } 636 | 637 | /** 638 | * Register cell edit listener, which is triggered when cell value is being 639 | * changed. Useful to hook e.g. database commit on edit. 640 | * 641 | * @see CellEditEvent 642 | * 643 | * @param listener 644 | * an CellEditListener instance 645 | */ 646 | public void addCellEditListener(CellEditListener listener) { 647 | cellEditListeners.addListener(listener); 648 | getState().hasCellEditListener = true; 649 | } 650 | 651 | /** 652 | * Register cell focus listener, which is triggered when focus has 653 | * changed. 654 | * 655 | * @see CellFocusEvent 656 | * 657 | * @param listener 658 | * an CellFocusListener instance 659 | */ 660 | public void addCellFocusListener(CellFocusListener listener) { 661 | cellFocusListeners.addListener(listener); 662 | 663 | getState().hasFocusListener = true; 664 | getState().hasCellFocusListener = true; 665 | hasCellFocusListener = true; 666 | } 667 | 668 | /** 669 | * Register row focus listener, which is triggered when row has 670 | * changed. 671 | * 672 | * @see RowFocusEvent 673 | * 674 | * @param listener 675 | * an RowFocusListener instance 676 | */ 677 | public void addRowFocusListener(RowFocusListener listener) { 678 | rowFocusListeners.addListener(listener); 679 | 680 | getState().hasFocusListener = true; 681 | getState().hasRowFocusListener = true; 682 | hasRowFocusListener = true; 683 | } 684 | 685 | /** 686 | * Register editor open listener, which will let you control which columns 687 | * should be editable on a row-by-row basis as the editor opens. Note, that 688 | * adding this listener will cause the Grid to become disabled until the 689 | * server has processed the event. 690 | * 691 | * @see EditorOpenEvent 692 | * 693 | * @param listener 694 | * an EditorOpenListener instance 695 | */ 696 | public void addEditorOpenListener(EditorOpenListener listener) { 697 | editorOpenListeners.addListener(listener); 698 | 699 | getState().hasEditorOpenListener = true; 700 | } 701 | 702 | /** 703 | * Register editor close listener, which is emitted each time editor is being closed. 704 | * 705 | * @see EditorCloseEvent 706 | * 707 | * @param listener an EditorCloseListener instance 708 | */ 709 | public void addEditorCloseListener(EditorCloseListener listener) { 710 | editorCloseListeners.addListener(listener); 711 | 712 | getState().hasEditorCloseListener = true; 713 | } 714 | 715 | /** 716 | * Register click out listener, which is emitted when user clicks outside the 717 | * grid. This is not true blur event, since it is triggered by mouse only 718 | * 719 | * @since 2.1.8 720 | * 721 | * @see ClickOutEvent 722 | * 723 | * @param listener a ClickOutListener instance 724 | */ 725 | public void addClickOutListener(ClickOutListener listener) { 726 | clickOutListeners.addListener(listener); 727 | 728 | getState().hasClickOutListener = true; 729 | } 730 | 731 | /** 732 | * Get the current OffsetHelper 733 | * 734 | * @since 2.3.10 735 | * 736 | * @see FastNavigation#setOffsetHelper(OffsetHelper) 737 | * 738 | * @return OffsetHelper 739 | */ 740 | public OffsetHelper getOffsetHelper() { 741 | return this.offsetHelper; 742 | } 743 | 744 | /** 745 | * Use {@link OffsetHelper} to overwrite the calculation for internal offset of columns. 746 | * 747 | * @since 2.3.10 748 | * 749 | * @param offsetHelper OffsetHelper instance to be used instead of the default implementation 750 | */ 751 | public void setOffsetHelper(OffsetHelper offsetHelper) { 752 | this.offsetHelper = offsetHelper; 753 | } 754 | 755 | /** 756 | * Open editor on row rowIndex 757 | * 758 | * @since 2.5.2 759 | * 760 | * @param rowIndex Row to edit 761 | * @param columnIndex Target column, if disabled will skip to next enabled 762 | * @throws IllegalArgumentException if rowIndex or columnIndex is not in acceptable range 763 | * @throws IllegalStateException if editor is not enabled 764 | */ 765 | public void editRow(int rowIndex, int columnIndex) { 766 | if (columnIndex > grid.getColumns().size() || columnIndex < 0) { 767 | throw new IllegalArgumentException("Column index "+columnIndex+" is not withing boundaries"); 768 | } else if (rowIndex > grid.getDataCommunicator().getDataProviderSize() || rowIndex < 0) { 769 | throw new IllegalArgumentException("Row index "+rowIndex+" is not withing boundaries"); 770 | } else if (!grid.getEditor().isEnabled()) { 771 | throw new IllegalStateException("Editor is not enabled, cannot open"); 772 | } 773 | getRPC().editRow(rowIndex, columnIndex); 774 | } 775 | 776 | } 777 | --------------------------------------------------------------------------------