Tony CHEMIT pushed to branch develop at ultreiaio / ird-observe Commits: 342a2670 by Tony Chemit at 2021-01-04T11:21:21+01:00 Détail ergonomique sur l'arbre - Closes #1723 - - - - - 18699c34 by Tony Chemit at 2021-01-04T11:21:21+01:00 Les marées sont mal classées dans la fenêtre de droite - Closes #1726 - - - - - 10 changed files: - client/datasource/editor/api/src/main/java/fr/ird/observe/client/datasource/editor/api/navigation/NavigationTree.java - client/datasource/editor/api/src/main/java/fr/ird/observe/client/datasource/editor/api/navigation/NavigationTreeSelectionListenerImpl.java - client/datasource/editor/api/src/main/java/fr/ird/observe/client/datasource/editor/api/navigation/NavigationTreeShowPopupHandler.java - client/datasource/editor/api/src/main/java/fr/ird/observe/client/datasource/editor/api/navigation/NavigationUIInitializer.java - models/dto/src/main/java/fr/ird/observe/dto/data/ll/common/TripDto.java - models/dto/src/main/java/fr/ird/observe/dto/data/ps/common/TripDto.java - models/dto/src/main/java/fr/ird/observe/dto/decoration/ObserveI18nLabelsBuilder.java - toolkit/dto/src/main/java/fr/ird/observe/dto/decoration/I18nDecoratorHelper.java - toolkit/dto/src/main/java/fr/ird/observe/dto/decoration/decorators/DataReferenceDecorator.java - toolkit/dto/src/main/java/fr/ird/observe/dto/decoration/decorators/ObserveDecorator.java Changes: ===================================== client/datasource/editor/api/src/main/java/fr/ird/observe/client/datasource/editor/api/navigation/NavigationTree.java ===================================== @@ -32,6 +32,7 @@ import org.jdesktop.swingx.JXTree; import javax.swing.SwingUtilities; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; +import java.util.Arrays; /** * Created on 14/11/16. @@ -77,7 +78,7 @@ public class NavigationTree extends JXTree { * @param node the node to select */ public void selectSafeNode(TreeNode node) { - log.info("try to select node [" + node + "]"); + log.info(String.format("try to select safe node [%s]", node)); TreePath path = new TreePath(getModel().getPathToRoot(node)); getSelectionModel().setSkipCheckPreviousContent(true); try { @@ -89,7 +90,7 @@ public class NavigationTree extends JXTree { } public void reSelectSafeNode(TreeNode node) { - log.info("try to select node [" + node + "]"); + log.info(String.format("try to reselect safe node [%s]", node)); TreePath path = new TreePath(getModel().getPathToRoot(node)); getSelectionModel().clearSelection(); getSelectionModel().setSkipCheckPreviousContent(true); @@ -101,6 +102,14 @@ public class NavigationTree extends JXTree { SwingUtilities.invokeLater(() -> scrollPathToVisible(path)); } + public boolean isRowSelected(int requiredRow) { + int[] selectedRows = getSelectionRows(); + if (selectedRows != null) { + return Arrays.stream(selectedRows).anyMatch(selectedRow -> requiredRow == selectedRow); + } + return false; + } + /** * Selects the given {@code node} in the registered tree. * @@ -111,7 +120,7 @@ public class NavigationTree extends JXTree { log.error("Can't load null node.", new NullPointerException()); return; } - log.info("try to select node [" + node + "]"); + log.info(String.format("try to select node [%s]", node)); TreePath path = new TreePath(getModel().getPathToRoot(node)); setSelectionPath(path); SwingUtilities.invokeLater(() -> scrollPathToVisible(path)); ===================================== client/datasource/editor/api/src/main/java/fr/ird/observe/client/datasource/editor/api/navigation/NavigationTreeSelectionListenerImpl.java ===================================== @@ -34,16 +34,21 @@ import fr.ird.observe.dto.ObserveUtil; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import javax.swing.SwingUtilities; import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.ExpandVetoException; import javax.swing.tree.TreePath; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; import java.util.Objects; import static io.ultreia.java4all.i18n.I18n.t; -class NavigationTreeSelectionListenerImpl implements fr.ird.observe.client.datasource.editor.api.navigation.event.NavigationTreeSelectionListener, TreeSelectionListener { +class NavigationTreeSelectionListenerImpl implements fr.ird.observe.client.datasource.editor.api.navigation.event.NavigationTreeSelectionListener, TreeSelectionListener, MouseListener { private static final Logger log = LogManager.getLogger(NavigationTreeSelectionListenerImpl.class); private final ClientUIContext clientUIContext; @@ -51,6 +56,7 @@ class NavigationTreeSelectionListenerImpl implements fr.ird.observe.client.datas private final NavigationUI ui; private final DataSourceEditorModel dataSourceEditorModel; private final NavigationTree tree; + private int rowToSelect; NavigationTreeSelectionListenerImpl(DataSourceEditorModel dataSourceEditorModel, ContentUIManager contentUIManager, NavigationUI ui, NavigationTree tree) { this.dataSourceEditorModel = Objects.requireNonNull(dataSourceEditorModel); @@ -160,4 +166,108 @@ class NavigationTreeSelectionListenerImpl implements fr.ird.observe.client.datas ObserveUtil.cleanMemory(); } + + + @Override + public void mouseClicked(MouseEvent e) { + if (!tree.isEnabled()) { + return; + } + if (e.isConsumed()) { + return; + } + boolean rightClick = SwingUtilities.isRightMouseButton(e); + + boolean doubleClick = e.getClickCount() == 2; + // get the coordinates of the mouse click + Point p = e.getPoint(); + + int closestRowForLocation = tree.getClosestRowForLocation(e.getX(), e.getY()); + + log.info(String.format("Click on navigation tree: (rightClick? %b, doubleClick? %b) - at row %s (point %s)", rightClick, doubleClick, closestRowForLocation, p)); + + if (e.getClickCount() == 1) { + // need to compute row to select + rowToSelect = -1; + if (tree.isRowSelected(closestRowForLocation)) { + rowToSelect = closestRowForLocation; + } else { + + // try to change selection + + TreePath pathForRow = tree.getPathForRow(closestRowForLocation); + if (pathForRow == null) { + e.consume(); + return; + } + Rectangle pathBounds = tree.getPathBounds(pathForRow); + if (pathBounds != null && pathBounds.getX() > p.getX()) { + // we never acts when point is before the rectangle, because the arrow button may be used for this... + log.info("Cancel click on tree navigation (before path rectangle (probably on arrow button)"); + e.consume(); + return; + } + log.info(String.format("Do select row: %d", closestRowForLocation)); + tree.setSelectionPath(pathForRow); + + if (tree.isRowSelected(closestRowForLocation)) { + rowToSelect = closestRowForLocation; + } + } + } else { + + // re-use previous rowToSelect + if (rowToSelect == -1) { + log.info("Cancel double-click, no previous rowToSelect"); + e.consume(); + return; + } + } + + if (rowToSelect == -1) { + e.consume(); + return; + } + + // Never apply double click, go instable behaviour with existing code +// if (doubleClick) { +// TreePath pathForRow = tree.getPathForRow(rowToSelect); +// Rectangle pathBounds = tree.getPathBounds(pathForRow); +// if (pathBounds != null && pathBounds.getX() < p.getX() && !pathBounds.contains(p)) { +// // when after the rectangle of the path, then do the collapse/expand action (tree does not manage this cas) +// // we never acts when point is before the rectangle, because the arrow button may be used for this... +// boolean expanded = tree.isExpanded(pathForRow); +// SwingUtilities.invokeLater(() -> { +// if (expanded) { +// log.info(String.format("do collapse row: %s", pathBounds)); +// tree.collapsePath(pathForRow); +// } else { +// log.info(String.format("do expand row: %s", pathBounds)); +// tree.expandPath(pathForRow); +// } +// }); +// e.consume(); +// } +// } + } + + @Override + public void mousePressed(MouseEvent e) { + // do nothing + } + + @Override + public void mouseReleased(MouseEvent e) { + // do nothing + } + + @Override + public void mouseEntered(MouseEvent e) { + // do nothing + } + + @Override + public void mouseExited(MouseEvent e) { + // do nothing + } } ===================================== client/datasource/editor/api/src/main/java/fr/ird/observe/client/datasource/editor/api/navigation/NavigationTreeShowPopupHandler.java ===================================== @@ -33,7 +33,6 @@ import javax.swing.AbstractButton; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.SwingUtilities; -import javax.swing.tree.TreePath; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.KeyEvent; @@ -68,11 +67,11 @@ public class NavigationTreeShowPopupHandler implements KeyListener, MouseListene private void showPopup(int row, Point p) { - log.info("Will show popup from row: " + row); + log.info(String.format("Will show popup from row: %d", row)); NavigationNode selectedNode = tree.getNodeForRow(row); - log.info("Found selected node: " + selectedNode); + log.info(String.format("Found selected node: %s", selectedNode)); SwingUtilities.invokeLater(() -> { beforeOpenPopup(selectedNode); @@ -97,7 +96,6 @@ public class NavigationTreeShowPopupHandler implements KeyListener, MouseListene Arrays.stream(selectedContentUI.getInsertPopup().getSubElements()).forEach(a -> new ContentUIMenuAction(popup, (AbstractButton) a).init()); if (popup.getSubElements().length > length) { popup.addSeparator(); -// length = popup.getSubElements().length; } Arrays.stream(selectedContentUI.getConfigurePopup().getSubElements()).forEach(a -> new ContentUIMenuAction(popup, (AbstractButton) a).init()); length = popup.getSubElements().length; @@ -106,22 +104,6 @@ public class NavigationTreeShowPopupHandler implements KeyListener, MouseListene } } - private boolean isRowSelected(int requiredRow) { - boolean result = false; - int[] selectedRows = tree.getSelectionRows(); - if (selectedRows != null) { - for (int selectedRow : selectedRows) { - if (requiredRow == selectedRow) { - - // match - result = true; - break; - } - } - } - return result; - } - private int getLowestSelectedRowCount() { if (tree.isSelectionEmpty()) { throw new IllegalStateException("Can't have empty selection"); @@ -141,24 +123,18 @@ public class NavigationTreeShowPopupHandler implements KeyListener, MouseListene if (!tree.isEnabled()) { return; } - + if (e.isConsumed()) { + return; + } if (e.getKeyCode() == KeyEvent.VK_CONTEXT_MENU && !tree.isSelectionEmpty()) { - // get the lowest selected row int lowestRow = getLowestSelectedRowCount(); - // get the selected column Rectangle r = tree.getRowBounds(lowestRow); - // get the point in the middle lower of the cell Point p = new Point(r.x + r.width / 2, r.y + r.height); - - if (log.isDebugEnabled()) { - log.debug("Row " + lowestRow + " found t point [" + p + "]"); - } - + log.debug(String.format("Row %d found t point [%s]", lowestRow, p)); showPopup(lowestRow, p); - } } @@ -167,60 +143,33 @@ public class NavigationTreeShowPopupHandler implements KeyListener, MouseListene if (!tree.isEnabled()) { return; } - + if (e.isConsumed()) { + return; + } boolean rightClick = SwingUtilities.isRightMouseButton(e); - + if (!rightClick) { + return; + } + boolean doubleClick = e.getClickCount() == 2; + if (doubleClick) { + return; + } // get the coordinates of the mouse click Point p = e.getPoint(); - int closestRowForLocation = tree.getClosestRowForLocation(e.getX(), e.getY()); + log.info(String.format("Point of click: (rightClick? %b, doubleClick? %b) - at row %s (point %s)", true, false, closestRowForLocation, p)); int rowToSelect = -1; - if (isRowSelected(closestRowForLocation)) { - + if (tree.isRowSelected(closestRowForLocation)) { rowToSelect = closestRowForLocation; } - if (rowToSelect == -1) { - - // try to change selection - - TreePath pathForRow = tree.getPathForRow(closestRowForLocation); - if (pathForRow == null) { - e.consume(); - return; - } - tree.setSelectionPath(pathForRow); - - if (isRowSelected(closestRowForLocation)) { - rowToSelect = closestRowForLocation; - } - - } else { - TreePath pathForRow = tree.getPathForRow(rowToSelect); - - Rectangle pathBounds = tree.getPathBounds(pathForRow); - if (e.getClickCount() == 2 && pathBounds != null && !pathBounds.contains(e.getPoint())) { - SwingUtilities.invokeLater(() -> { - if (tree.isExpanded(pathForRow)) { - tree.collapsePath(pathForRow); - } else { - tree.expandPath(pathForRow); - } - }); - e.consume(); - return; - } - - } if (rowToSelect == -1) { return; } - - if (rightClick) { - showPopup(rowToSelect, p); - } + showPopup(rowToSelect, p); + e.consume(); } @Override ===================================== client/datasource/editor/api/src/main/java/fr/ird/observe/client/datasource/editor/api/navigation/NavigationUIInitializer.java ===================================== @@ -123,5 +123,6 @@ class NavigationUIInitializer extends UIInitializerSupport<NavigationUI, UIIniti editor.addNavigationTreeSelectionListener(selectionListener); editor.addTreeWillExpandListener(selectionListener); editor.addTreeSelectionListener(selectionListener); + editor.addMouseListener(selectionListener); } } ===================================== models/dto/src/main/java/fr/ird/observe/dto/data/ll/common/TripDto.java ===================================== @@ -22,7 +22,6 @@ package fr.ird.observe.dto.data.ll.common; * #L% */ -import fr.ird.observe.dto.decoration.I18nDecoratorHelper; import io.ultreia.java4all.bean.spi.GenerateJavaBeanDefinition; import org.nuiton.util.DateUtil; @@ -47,11 +46,6 @@ public class TripDto extends GeneratedTripDto { setNoOfDays(TripDto.createNoOfDay(startDate, endDate)); } - @Override - public String getStartEndDateLabel() { - return I18nDecoratorHelper.getStartEndDateLabel(getStartDate(), getEndDate()); - } - @Override public void setStartDate(Date startDate) { super.setStartDate(startDate == null ? null : DateUtil.getDay(startDate)); ===================================== models/dto/src/main/java/fr/ird/observe/dto/data/ps/common/TripDto.java ===================================== @@ -55,6 +55,6 @@ public class TripDto extends GeneratedTripDto { boolean oldValue = isRouteEmpty(); super.setRoute(route); //FIXME:Dto should be generated by dto template - firePropertyChange("routeEmpty",oldValue, isRouteEmpty()); + firePropertyChange("routeEmpty", oldValue, isRouteEmpty()); } } ===================================== models/dto/src/main/java/fr/ird/observe/dto/decoration/ObserveI18nLabelsBuilder.java ===================================== @@ -147,6 +147,15 @@ public class ObserveI18nLabelsBuilder extends BeanPropertyI18nKeyProducerSupport .build(); } + @Override + public String getI18nPropertyKey(Class type, String property) { + int i = property.indexOf("::"); + if (i>-1) { + property = property.substring(0, i); + } + return super.getI18nPropertyKey(type, property); + } + @Override protected Map<String, String> createMapping() { String idDtoPrefix = "Id."; ===================================== toolkit/dto/src/main/java/fr/ird/observe/dto/decoration/I18nDecoratorHelper.java ===================================== @@ -25,6 +25,8 @@ package fr.ird.observe.dto.decoration; import io.ultreia.java4all.i18n.I18n; import io.ultreia.java4all.i18n.spi.bean.BeanPropertyI18nKeyProducerProvider; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; @@ -100,6 +102,13 @@ public abstract class I18nDecoratorHelper extends BeanPropertyI18nKeyProducerPro public static String getDateLabel(Date date) { return String.format("%1$td/%1$tm/%1$tY", date); } + public static Date getDate(String date) { + try { + return new SimpleDateFormat("dd/MM:yy").parse( date); + } catch (ParseException e) { + return null; + } + } public static String getTimestampLabel(Date date) { return String.format("%1$td/%1$tm/%1$tY %1$tH:%1$tM", date); ===================================== toolkit/dto/src/main/java/fr/ird/observe/dto/decoration/decorators/DataReferenceDecorator.java ===================================== @@ -23,7 +23,6 @@ package fr.ird.observe.dto.decoration.decorators; */ -import fr.ird.observe.dto.WithStartEndDate; import fr.ird.observe.dto.decoration.I18nDecoratorHelper; import fr.ird.observe.dto.reference.DataDtoReference; import fr.ird.observe.dto.reference.DtoReference; @@ -51,13 +50,15 @@ public class DataReferenceDecorator<R extends DataDtoReference> extends ObserveD return (Comparable) I18nDecoratorHelper.getDateLabel((Date) value2); } return value2; - }if (token.endsWith(FORMATTER_TIME)) { + } + if (token.endsWith(FORMATTER_TIME)) { Comparable value2 = getTokenValue(jxcontext, token.substring(0, token.length() - FORMATTER_TIME.length())); if (value2 != null && Date.class.isAssignableFrom(value2.getClass())) { return (Comparable) I18nDecoratorHelper.getTimeLabel((Date) value2); } return value2; - }if (token.endsWith(FORMATTER_TIMESTAMP)) { + } + if (token.endsWith(FORMATTER_TIMESTAMP)) { Comparable value2 = getTokenValue(jxcontext, token.substring(0, token.length() - FORMATTER_TIMESTAMP.length())); if (value2 != null && Date.class.isAssignableFrom(value2.getClass())) { return (Comparable) I18nDecoratorHelper.getTimestampLabel((Date) value2); @@ -66,27 +67,17 @@ public class DataReferenceDecorator<R extends DataDtoReference> extends ObserveD } // assume all values are comparable Comparable<Comparable<?>> value; - Object contextBean = jxcontext.getContextBean(); - if (contextBean instanceof DtoReference) { - String[] tokens = token.split("/"); - value = getValueFromReference(tokens, (DtoReference) contextBean, 0); - if (value == null) { value = (Comparable<Comparable<?>>) getDefaultNullValue(tokens[0]); } - } else { value = super.getTokenValue(jxcontext, token); } - if (token.equals(WithStartEndDate.PROPERTY_START_END_DATE_LABEL)) { - value = super.getTokenValue(jxcontext, token); - } return value; - } @Override @@ -94,4 +85,4 @@ public class DataReferenceDecorator<R extends DataDtoReference> extends ObserveD String result = super.toString(bean); return result == null ? null : result.trim(); } -} +} \ No newline at end of file ===================================== toolkit/dto/src/main/java/fr/ird/observe/dto/decoration/decorators/ObserveDecorator.java ===================================== @@ -52,11 +52,46 @@ public class ObserveDecorator<E> extends MultiJXPathDecorator<E> implements Clon private static final Logger log = LogManager.getLogger(ObserveDecorator.class); public ObserveDecorator(Class<E> internalClass, String expression) { - super(internalClass, expression, DEFAULT_SEPARATOR, DEFAULT_SEPARATOR_REPLACEMENT); + this(internalClass, expression, DEFAULT_SEPARATOR_REPLACEMENT); } public ObserveDecorator(Class<E> internalClass, String expression, String separator) { super(internalClass, expression, DEFAULT_SEPARATOR, separator); + if (getNbToken() == 0) { + return; + } + int nbContext = getNbContext(); + for (int i = 0; i < nbContext; i++) { + Context<E> context = contexts[i]; + String token = context.getFirstProperty(); + if (token.endsWith("::date") || token.endsWith("::time")) { + context.setComparator((o1, o2) -> { + if (o1 == null && o2 == null) { + return 0; + } + if (o1 == null) { + return -1; + } + if (o2 == null) { + return 1; + } + String s1 = o1.toString(); + String s2 = o2.toString(); + Date d1 = I18nDecoratorHelper.getDate(s1); + Date d2 = I18nDecoratorHelper.getDate(s2); + if (d1 == null && d2 == null) { + return 0; + } + if (d1 == null) { + return -1; + } + if (d2 == null) { + return 1; + } + return d1.compareTo(d2); + }); + } + } } @Override @@ -73,13 +108,15 @@ public class ObserveDecorator<E> extends MultiJXPathDecorator<E> implements Clon return (Comparable) I18nDecoratorHelper.getDateLabel((Date) value2); } return value2; - }if (token.endsWith(FORMATTER_TIME)) { + } + if (token.endsWith(FORMATTER_TIME)) { Comparable value2 = getTokenValue(jxcontext, token.substring(0, token.length() - FORMATTER_TIME.length())); if (value2 != null && Date.class.isAssignableFrom(value2.getClass())) { return (Comparable) I18nDecoratorHelper.getTimeLabel((Date) value2); } return value2; - }if (token.endsWith(FORMATTER_TIMESTAMP)) { + } + if (token.endsWith(FORMATTER_TIMESTAMP)) { Comparable value2 = getTokenValue(jxcontext, token.substring(0, token.length() - FORMATTER_TIMESTAMP.length())); if (value2 != null && Date.class.isAssignableFrom(value2.getClass())) { return (Comparable) I18nDecoratorHelper.getTimestampLabel((Date) value2); @@ -89,58 +126,39 @@ public class ObserveDecorator<E> extends MultiJXPathDecorator<E> implements Clon // assume all values are comparable Comparable<Comparable<?>> value; try { - String[] tokens = token.split("/"); - Object value0 = jxcontext.getValue(tokens[0]); - if (value0 instanceof DtoReference) { - value = getValueFromReference(tokens, (DtoReference) value0, 1); - } else { - value = (Comparable<Comparable<?>>) jxcontext.getValue(token); } - if (value == null) { value = (Comparable<Comparable<?>>) getDefaultNullValue(token); } - } catch (Exception e) { value = (Comparable<Comparable<?>>) getDefaultUndefinedValue(token); } - return value; } protected Comparable<Comparable<?>> getValueFromReference(String[] tokens, DtoReference referenceBean, int startIndex) { - for (int i = startIndex, max = tokens.length - 1; i < max; i++) { - if (referenceBean.getPropertyNames().contains(tokens[i])) { - Serializable propertyValue = referenceBean.getPropertyValue(tokens[i]); - if (!(propertyValue instanceof DtoReference)) { - return (Comparable<Comparable<?>>) getDefaultUndefinedValue(StringUtils.join(tokens, "/")); - } - referenceBean = (DtoReference) propertyValue; } } - - Comparable<Comparable<?>> value = null; - + Comparable<Comparable<?>> value; String lastToken = tokens[tokens.length - 1]; if (referenceBean.getPropertyNames().contains(lastToken)) { value = referenceBean.getPropertyValue(lastToken); } else { value = referenceBean.get(lastToken); } - return value; } @@ -157,6 +175,4 @@ public class ObserveDecorator<E> extends MultiJXPathDecorator<E> implements Clon } return t("observe.common.none"); } - - } View it on GitLab: https://gitlab.com/ultreiaio/ird-observe/-/compare/964a7953db1d31bda68fe9d84... -- View it on GitLab: https://gitlab.com/ultreiaio/ird-observe/-/compare/964a7953db1d31bda68fe9d84... You're receiving this email because of your account on gitlab.com.