This is an automated email from the git hooks/post-receive script. New commit to branch develop in repository jaxx. See http://git.nuiton.org/jaxx.git commit c63b6756d47a140dc9abfa94dcdce4005fff75c6 Merge: 50c8c53 74be8e0 Author: Tony CHEMIT <chemit@codelutin.com> Date: Sat Mar 21 11:44:13 2015 +0100 fixes #3656: Introduce a API to navigate inside a JFormattedTextField using a mask fixes #3655: [AbsoluteCoordinateEditor] Improve navigation of a coordinate Merge branch 'feature/3655' into develop why this merge is necessary, .../JFormattedTextFieldNavigationManager.java | 346 +++++++++++++++++++++ .../JFormatterTextFieldInternalGroup.java | 92 ++++++ .../JFormatterTextFieldInternalGroups.java | 130 ++++++++ jaxx-widgets-gis/pom.xml | 6 + .../gis/absolute/AbsoluteDdCoordinateEditor.jaxx | 4 +- .../AbsoluteDdCoordinateEditorHandler.java | 3 + .../AbsoluteDmdCoordinateEditorHandler.java | 3 + .../AbsoluteDmsCoordinateEditorHandler.java | 3 + .../absolute/AbsoluteDdCoordinateEditorTest.java | 116 +++++++ .../absolute/AbsoluteDmsCoordinateEditorTest.java | 125 ++++++++ .../src/test/resources/log4j.properties | 1 + 11 files changed, 827 insertions(+), 2 deletions(-) diff --cc jaxx-widgets-common/src/main/java/org/nuiton/jaxx/widgets/jformattedtextfield/JFormattedTextFieldNavigationManager.java index 0000000,e563722..3c65fb2 mode 000000,100644..100644 --- a/jaxx-widgets-common/src/main/java/org/nuiton/jaxx/widgets/jformattedtextfield/JFormattedTextFieldNavigationManager.java +++ b/jaxx-widgets-common/src/main/java/org/nuiton/jaxx/widgets/jformattedtextfield/JFormattedTextFieldNavigationManager.java @@@ -1,0 -1,337 +1,346 @@@ + package org.nuiton.jaxx.widgets.jformattedtextfield; + ++import com.google.common.base.Preconditions; + import org.apache.commons.logging.Log; + import org.apache.commons.logging.LogFactory; + + import javax.swing.JFormattedTextField; + import javax.swing.SwingUtilities; ++import javax.swing.text.MaskFormatter; + import java.awt.event.FocusAdapter; + import java.awt.event.FocusEvent; + import java.awt.event.KeyAdapter; + import java.awt.event.KeyEvent; + import java.awt.event.MouseAdapter; + import java.awt.event.MouseEvent; + import java.util.HashMap; + import java.util.Map; + + /** + * Manager to navigate inside a JFormattedTextField using a MaskFormatter. + * + * Created on 3/21/15. + * + * @author Tony Chemit - chemit@codelutin.com + * @since 2.23 + */ + public class JFormattedTextFieldNavigationManager { + + /** Logger. */ + private static final Log log = LogFactory.getLog(JFormattedTextFieldNavigationManager.class); + + private static final String CLIENT_PROPERTY_NAVIGATION_MANAGER = "JFormattedTextFieldNavigationHandler"; + - public static void install(String pattern, JFormattedTextField component) { ++ public static void install(JFormattedTextField component) { + - JFormatterTextFieldInternalGroups componentPositions = JFormatterTextFieldInternalGroups.create(pattern); ++ JFormattedTextField.AbstractFormatter formatter = component.getFormatter(); ++ ++ Preconditions.checkState(formatter != null, "No formatter found on " + component); ++ Preconditions.checkState(formatter instanceof MaskFormatter, "Formatter " + formatter + " is not a MaskFormatter"); ++ ++ String mask = ((MaskFormatter) formatter).getMask(); ++ ++ JFormatterTextFieldInternalGroups componentPositions = JFormatterTextFieldInternalGroups.create(mask); + + JFormattedTextFieldNavigationManager handler = new JFormattedTextFieldNavigationManager(componentPositions, component); + component.putClientProperty(CLIENT_PROPERTY_NAVIGATION_MANAGER, handler); + handler.install0(component); + + } + + public static void uninstall(JFormattedTextField component) { + + JFormattedTextFieldNavigationManager handler = (JFormattedTextFieldNavigationManager) component.getClientProperty(CLIENT_PROPERTY_NAVIGATION_MANAGER); + try { + handler.uninstall0(component); + } finally { + component.putClientProperty(CLIENT_PROPERTY_NAVIGATION_MANAGER, null); + } + + } + + private final JFormatterTextFieldInternalGroups groups; + + private final KeyAdapter keyAdapter; + + private final MouseAdapter mouseAdapter; + + private final FocusAdapter focusAdapter; + + private final Map<JFormatterTextFieldInternalGroup, SelectComponentAction> actions; + + private JFormatterTextFieldInternalGroup lastGroup = null; + + protected JFormattedTextFieldNavigationManager(JFormatterTextFieldInternalGroups groups, JFormattedTextField component) { + + this.groups = groups; + + this.actions = new HashMap<JFormatterTextFieldInternalGroup, SelectComponentAction>(); + for (JFormatterTextFieldInternalGroup componentPosition : groups) { + actions.put(componentPosition, new SelectComponentAction(component, componentPosition)); + } + + this.keyAdapter = new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + + JFormattedTextField source = (JFormattedTextField) e.getSource(); + int caretPosition = source.getCaretPosition(); + + if (canGoRight(e)) { + + goRight(source, caretPosition); + e.consume(); + + } else if (canTransferFocus(e)) { + + source.transferFocus(); + e.consume(); + + } else if (canGoLeft(e)) { + + goLeft(source, caretPosition); + e.consume(); + + } else if (canTransferFocusBackward(e)) { + + source.transferFocusBackward(); + e.consume(); + + } + + } + + @Override + public void keyReleased(KeyEvent e) { + + JFormattedTextField source = (JFormattedTextField) e.getSource(); + JFormatterTextFieldInternalGroup currentGroup = getCurrentGroup(source); + selectComponent(currentGroup); + + } + + }; + + this.mouseAdapter = new MouseAdapter() { + @Override + public void mouseReleased(MouseEvent e) { + + JFormattedTextField source = (JFormattedTextField) e.getSource(); + JFormatterTextFieldInternalGroup currentGroup = getCurrentGroup(source); + selectComponent(currentGroup); + + } + }; + + this.focusAdapter = new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + + lastGroup = null; + + JFormattedTextField source = (JFormattedTextField) e.getSource(); + JFormatterTextFieldInternalGroup currentGroup = getCurrentGroup(source); + selectComponent(currentGroup); + + } + }; + + } + + protected void gotoComponent(JFormattedTextField source, JFormatterTextFieldInternalGroup group) { + + if (group != null) { + + int startIndex = group.getStartIndex(); + int endIndex = group.getEndIndex(); + if (log.isDebugEnabled()) { + log.debug(String.format("Goto component [%s - %s]", startIndex, endIndex)); + } + source.setCaretPosition(startIndex); + + } + + } + + protected void selectComponent(JFormatterTextFieldInternalGroup currentGroup) { + + if (lastGroup == null || !lastGroup.equals(currentGroup)) { + + if (log.isDebugEnabled()) { + log.debug("New select group: " + currentGroup); + } + lastGroup = currentGroup; + + SelectComponentAction action = actions.get(currentGroup); + SwingUtilities.invokeLater(action); + + } + + } + + protected JFormatterTextFieldInternalGroup getCurrentGroup(JFormattedTextField source) { + + int caretPosition = source.getCaretPosition(); + return groups.getGroupAtPosition(caretPosition); + + } + + protected void goRight(JFormattedTextField source, int caretPosition) { + + if (lastGroup == null || !lastGroup.isLastGroup()) { + + if (log.isDebugEnabled()) { + log.debug("Go right from position " + caretPosition); + } + JFormatterTextFieldInternalGroup currentGroup = groups.getGroupAtPosition(caretPosition); + JFormatterTextFieldInternalGroup nextGroup = currentGroup.getNextGroup(); + gotoComponent(source, nextGroup); + + } + + } + + protected void goLeft(JFormattedTextField source, int caretPosition) { + + if (lastGroup == null || !lastGroup.isFirstGroup()) { + + if (log.isDebugEnabled()) { + log.debug("Go left from position " + caretPosition); + } + JFormatterTextFieldInternalGroup currentGroup = groups.getGroupAtPosition(caretPosition); + JFormatterTextFieldInternalGroup nextGroup = currentGroup.getPreviousGroup(); + gotoComponent(source, nextGroup); + + } + } + + + protected boolean canGoRight(KeyEvent e) { + + boolean result = false; + + boolean noModifiers = e.getModifiers() == 0; + + if (KeyEvent.VK_RIGHT == e.getKeyCode() && noModifiers) { + + // right key go to next component + result = true; + + } else if (KeyEvent.VK_TAB == e.getKeyCode() && noModifiers) { + + // Tab go to next component if not on last one + result = lastGroup == null || !lastGroup.isLastGroup(); + + } + + return result; + + } + + protected boolean canGoLeft(KeyEvent e) { + + boolean result = false; + + if (KeyEvent.VK_LEFT == e.getKeyCode() && e.getModifiers() == 0) { + + // Left key go to previous component + result = true; + + } else if (KeyEvent.VK_TAB == e.getKeyCode() && e.isShiftDown() && !e.isControlDown()) { + + // Shift Tab go to previous component if not on first one + result = lastGroup != null && !lastGroup.isFirstGroup(); + + } + + return result; + + } + + protected boolean canTransferFocus(KeyEvent e) { + + boolean result = false; + + boolean onTabKey = KeyEvent.VK_TAB == e.getKeyCode(); + + if (onTabKey && e.isControlDown() && !e.isShiftDown()) { + + // Ctrl Tab go direct to focus + result = true; + + } else if (onTabKey && e.getModifiers() == 0) { + + // Tab on last component go to focus + result = lastGroup != null && lastGroup.isLastGroup(); + + } + + return result; + + } + + protected boolean canTransferFocusBackward(KeyEvent e) { + + boolean result = false; + + boolean onShiftTabKey = KeyEvent.VK_TAB == e.getKeyCode() && e.isShiftDown(); + + if (onShiftTabKey && e.isControlDown()) { + + // Ctrl Shift Tab go direct to focus backward + result = true; + + } else if (onShiftTabKey) { + + // Shit Tab go to focus backward only if no component selected or on the first one + result = lastGroup == null || lastGroup.isFirstGroup(); + + } + + return result; + + } + + private void install0(JFormattedTextField component) { + + component.setFocusTraversalKeysEnabled(false); + component.addKeyListener(keyAdapter); + component.addMouseListener(mouseAdapter); + component.addFocusListener(focusAdapter); + } + + private void uninstall0(JFormattedTextField component) { + + component.setFocusTraversalKeysEnabled(true); + component.removeKeyListener(keyAdapter); + component.removeMouseListener(mouseAdapter); + component.removeFocusListener(focusAdapter); + + } + + private static class SelectComponentAction implements Runnable { + + private final JFormatterTextFieldInternalGroup group; + + private final JFormattedTextField source; + + private SelectComponentAction(JFormattedTextField source, JFormatterTextFieldInternalGroup group) { + this.group = group; + this.source = source; + } + + @Override + public void run() { + + int startIndex = group.getStartIndex(); + int endIndex = group.getEndIndex() + 1; + if (log.isDebugEnabled()) { + log.debug(String.format("Select group [%s]", group)); + } + source.select(startIndex, endIndex); + + } + } + + } diff --cc jaxx-widgets-gis/src/main/java/org/nuiton/jaxx/widgets/gis/absolute/AbsoluteDdCoordinateEditorHandler.java index b4f408a,322c856..8765bb7 --- a/jaxx-widgets-gis/src/main/java/org/nuiton/jaxx/widgets/gis/absolute/AbsoluteDdCoordinateEditorHandler.java +++ b/jaxx-widgets-gis/src/main/java/org/nuiton/jaxx/widgets/gis/absolute/AbsoluteDdCoordinateEditorHandler.java @@@ -125,6 -128,6 +126,8 @@@ public class AbsoluteDdCoordinateEditor } }); ++ JFormattedTextFieldNavigationManager.install(editor); ++ // When model degree changed, let's push it back in bean model.addPropertyChangeListener( AbsoluteDdCoordinateEditorModel.PROPERTY_DEGREE, diff --cc jaxx-widgets-gis/src/main/java/org/nuiton/jaxx/widgets/gis/absolute/AbsoluteDmdCoordinateEditorHandler.java index e42e078,ccd9af0..250c64d --- a/jaxx-widgets-gis/src/main/java/org/nuiton/jaxx/widgets/gis/absolute/AbsoluteDmdCoordinateEditorHandler.java +++ b/jaxx-widgets-gis/src/main/java/org/nuiton/jaxx/widgets/gis/absolute/AbsoluteDmdCoordinateEditorHandler.java @@@ -133,6 -136,6 +134,8 @@@ public class AbsoluteDmdCoordinateEdito } }); ++ JFormattedTextFieldNavigationManager.install(editor); ++ // When model degre changed, let's push it back in bean model.addPropertyChangeListener( AbsoluteDmdCoordinateEditorModel.PROPERTY_DEGREE, diff --cc jaxx-widgets-gis/src/main/java/org/nuiton/jaxx/widgets/gis/absolute/AbsoluteDmsCoordinateEditorHandler.java index 69060ee,19403e6..991d4be --- a/jaxx-widgets-gis/src/main/java/org/nuiton/jaxx/widgets/gis/absolute/AbsoluteDmsCoordinateEditorHandler.java +++ b/jaxx-widgets-gis/src/main/java/org/nuiton/jaxx/widgets/gis/absolute/AbsoluteDmsCoordinateEditorHandler.java @@@ -131,6 -134,6 +132,8 @@@ public class AbsoluteDmsCoordinateEdito } }); ++ JFormattedTextFieldNavigationManager.install(editor); ++ // When model degre changed, let's push it back in bean model.addPropertyChangeListener( AbsoluteDmsCoordinateEditorModel.PROPERTY_DEGREE, -- To stop receiving notification emails like this one, please contact nuiton.org SCM administrator <admin+scm@nuiton.org>.