Changeset 18574 in josm for trunk/src/org/openstreetmap
- Timestamp:
- 2022-10-17T16:22:45+02:00 (2 years ago)
- Location:
- trunk/src/org/openstreetmap/josm/gui
- Files:
-
- 1 added
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java
r18494 r18574 9 9 import java.awt.event.HierarchyEvent; 10 10 import java.awt.event.HierarchyListener; 11 import java.awt.event.MouseAdapter; 12 import java.awt.event.MouseEvent; 11 13 import java.awt.geom.AffineTransform; 12 14 import java.awt.geom.Point2D; … … 21 23 import java.util.Map; 22 24 import java.util.Map.Entry; 25 import java.util.Objects; 23 26 import java.util.Set; 24 27 import java.util.Stack; … … 41 44 import org.openstreetmap.josm.data.osm.BBox; 42 45 import org.openstreetmap.josm.data.osm.DataSet; 46 import org.openstreetmap.josm.data.osm.IPrimitive; 43 47 import org.openstreetmap.josm.data.osm.Node; 44 48 import org.openstreetmap.josm.data.osm.OsmPrimitive; … … 53 57 import org.openstreetmap.josm.data.projection.ProjectionChangeListener; 54 58 import org.openstreetmap.josm.data.projection.ProjectionRegistry; 59 import org.openstreetmap.josm.gui.PrimitiveHoverListener.PrimitiveHoverEvent; 55 60 import org.openstreetmap.josm.gui.help.Helpful; 56 61 import org.openstreetmap.josm.gui.layer.NativeScaleLayer; … … 64 69 import org.openstreetmap.josm.tools.Logging; 65 70 import org.openstreetmap.josm.tools.Utils; 71 import org.openstreetmap.josm.tools.bugreport.BugReportExceptionHandler; 66 72 67 73 /** … … 150 156 } 151 157 158 private final CopyOnWriteArrayList<PrimitiveHoverListener> primitiveHoverListeners = new CopyOnWriteArrayList<>(); 159 private IPrimitive previousHoveredPrimitive; 160 private final PrimitiveHoverMouseListener primitiveHoverMouseListenerHelper = new PrimitiveHoverMouseListener(); 161 162 /** 163 * Removes a primitive hover listener 164 * 165 * @param listener the listener. Ignored if null or already absent. 166 * @since 18574 167 */ 168 public void removePrimitiveHoverListener(PrimitiveHoverListener listener) { 169 primitiveHoverListeners.remove(listener); 170 } 171 172 /** 173 * Adds a primitive hover listener 174 * 175 * @param listener the listener. Ignored if null or already registered. 176 * @since 18574 177 */ 178 public void addPrimitiveHoverListener(PrimitiveHoverListener listener) { 179 if (listener != null) { 180 primitiveHoverListeners.addIfAbsent(listener); 181 } 182 } 183 184 /** 185 * Send a {@link PrimitiveHoverEvent} to registered {@link PrimitiveHoverListener}s 186 * @param e primitive hover event information 187 * @since 18574 188 */ 189 protected void firePrimitiveHovered(PrimitiveHoverEvent e) { 190 GuiHelper.runInEDT(() -> { 191 for (PrimitiveHoverListener l : primitiveHoverListeners) { 192 try { 193 l.primitiveHovered(e); 194 } catch (RuntimeException ex) { 195 Logging.logWithStackTrace(Logging.LEVEL_ERROR, "Error in primitive hover listener", ex); 196 BugReportExceptionHandler.handleException(ex); 197 } 198 } 199 }); 200 } 201 202 private void updateHoveredPrimitive(IPrimitive hovered, MouseEvent e) { 203 if (!Objects.equals(hovered, previousHoveredPrimitive)) { 204 firePrimitiveHovered(new PrimitiveHoverEvent(hovered, previousHoveredPrimitive, e)); 205 previousHoveredPrimitive = hovered; 206 } 207 } 208 152 209 // The only events that may move/resize this map view are window movements or changes to the map view size. 153 210 // We can clean this up more by only recalculating the state on repaint. … … 199 256 addHierarchyListener(hierarchyListener); 200 257 addComponentListener(componentListener); 258 addPrimitiveHoverMouseListeners(); 201 259 super.addNotify(); 202 260 } … … 206 264 removeHierarchyListener(hierarchyListener); 207 265 removeComponentListener(componentListener); 266 removePrimitiveHoverMouseListeners(); 208 267 super.removeNotify(); 268 } 269 270 private void addPrimitiveHoverMouseListeners() { 271 addMouseMotionListener(primitiveHoverMouseListenerHelper); 272 addMouseListener(primitiveHoverMouseListenerHelper); 273 } 274 275 private void removePrimitiveHoverMouseListeners() { 276 removeMouseMotionListener(primitiveHoverMouseListenerHelper); 277 removeMouseListener(primitiveHoverMouseListenerHelper); 209 278 } 210 279 … … 1716 1785 )/512; 1717 1786 } 1787 1788 /** 1789 * Listener for mouse movement events. Used to detect when primitives are being hovered over with the mouse pointer 1790 * so that registered {@link PrimitiveHoverListener}s can be notified. 1791 */ 1792 private class PrimitiveHoverMouseListener extends MouseAdapter { 1793 @Override 1794 public void mouseMoved(MouseEvent e) { 1795 OsmPrimitive hovered = getNearestNodeOrWay(e.getPoint(), isSelectablePredicate, true); 1796 updateHoveredPrimitive(hovered, e); 1797 } 1798 1799 @Override 1800 public void mouseExited(MouseEvent e) { 1801 updateHoveredPrimitive(null, e); 1802 } 1803 } 1718 1804 } -
trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java
r18332 r18574 13 13 import java.awt.event.MouseAdapter; 14 14 import java.awt.event.MouseEvent; 15 import java.beans.PropertyChangeEvent; 16 import java.beans.PropertyChangeListener; 15 17 import java.util.ArrayList; 16 18 import java.util.Arrays; … … 69 71 import org.openstreetmap.josm.data.osm.KeyValueVisitor; 70 72 import org.openstreetmap.josm.data.osm.Node; 71 import org.openstreetmap.josm.data.osm.OsmData;72 73 import org.openstreetmap.josm.data.osm.OsmDataManager; 73 74 import org.openstreetmap.josm.data.osm.OsmPrimitive; … … 84 85 import org.openstreetmap.josm.data.osm.search.SearchCompiler; 85 86 import org.openstreetmap.josm.data.osm.search.SearchSetting; 87 import org.openstreetmap.josm.data.preferences.AbstractProperty.ValueChangeEvent; 88 import org.openstreetmap.josm.data.preferences.AbstractProperty.ValueChangeListener; 86 89 import org.openstreetmap.josm.data.preferences.BooleanProperty; 90 import org.openstreetmap.josm.data.preferences.CachingProperty; 87 91 import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil; 88 92 import org.openstreetmap.josm.gui.ExtendedDialog; 89 93 import org.openstreetmap.josm.gui.MainApplication; 90 94 import org.openstreetmap.josm.gui.PopupMenuHandler; 95 import org.openstreetmap.josm.gui.PrimitiveHoverListener; 91 96 import org.openstreetmap.josm.gui.SideButton; 92 97 import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils; … … 97 102 import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent; 98 103 import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener; 104 import org.openstreetmap.josm.gui.layer.Layer; 99 105 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 100 106 import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset; … … 141 147 */ 142 148 public class PropertiesDialog extends ToggleDialog 143 implements DataSelectionListener, ActiveLayerChangeListener, DataSetListenerAdapter.Listener, TaggingPresetListener { 149 implements DataSelectionListener, ActiveLayerChangeListener, PropertyChangeListener, 150 DataSetListenerAdapter.Listener, TaggingPresetListener, PrimitiveHoverListener { 144 151 private final BooleanProperty PROP_DISPLAY_DISCARDABLE_KEYS = new BooleanProperty("display.discardable-keys", false); 145 152 … … 249 256 250 257 /** 258 * Show tags and relation memberships of objects in the properties dialog when hovering over them with the mouse pointer 259 * @since 18574 260 */ 261 public static final BooleanProperty PROP_PREVIEW_ON_HOVER = new BooleanProperty("propertiesdialog.preview-on-hover", true); 262 private final HoverPreviewPropListener hoverPreviewPropListener = new HoverPreviewPropListener(); 263 264 /** 265 * Always show information for selected objects when something is selected instead of the hovered object 266 * @since 18574 267 */ 268 public static final CachingProperty<Boolean> PROP_PREVIEW_ON_HOVER_PRIORITIZE_SELECTION = 269 new BooleanProperty("propertiesdialog.preview-on-hover.always-show-selected", true).cached(); 270 271 /** 251 272 * Create a new PropertiesDialog 252 273 */ … … 305 326 306 327 TaggingPresets.addListener(this); 328 329 PROP_PREVIEW_ON_HOVER.addListener(hoverPreviewPropListener); 307 330 } 308 331 … … 587 610 SelectionEventManager.getInstance().addSelectionListenerForEdt(this); 588 611 MainApplication.getLayerManager().addActiveLayerChangeListener(this); 612 if (PROP_PREVIEW_ON_HOVER.get()) 613 MainApplication.getMap().mapView.addPrimitiveHoverListener(this); 589 614 for (JosmAction action : josmActions) { 590 615 MainApplication.registerActionShortcut(action); … … 598 623 SelectionEventManager.getInstance().removeSelectionListener(this); 599 624 MainApplication.getLayerManager().removeActiveLayerChangeListener(this); 625 MainApplication.getMap().mapView.removePrimitiveHoverListener(this); 600 626 for (JosmAction action : josmActions) { 601 627 MainApplication.unregisterActionShortcut(action); … … 618 644 super.destroy(); 619 645 TaggingPresets.removeListener(this); 646 PROP_PREVIEW_ON_HOVER.removeListener(hoverPreviewPropListener); 620 647 Container parent = pluginHook.getParent(); 621 648 if (parent != null) { … … 636 663 // Ignore parameter as we do not want to operate always on real selection here, especially in draw mode 637 664 Collection<? extends IPrimitive> newSel = OsmDataManager.getInstance().getInProgressISelection(); 638 int newSelSize = newSel.size(); 665 666 // Temporarily disable listening to primitive mouse hover events while we have a selection as that takes priority 667 if (PROP_PREVIEW_ON_HOVER.get() && PROP_PREVIEW_ON_HOVER_PRIORITIZE_SELECTION.get()) { 668 if (newSel.isEmpty()) { 669 MainApplication.getMap().mapView.addPrimitiveHoverListener(this); 670 } else { 671 MainApplication.getMap().mapView.removePrimitiveHoverListener(this); 672 } 673 } 674 675 updateUi(newSel); 676 } 677 678 @Override 679 public void primitiveHovered(PrimitiveHoverEvent e) { 680 Collection<? extends IPrimitive> selection = OsmDataManager.getInstance().getInProgressISelection(); 681 if (PROP_PREVIEW_ON_HOVER_PRIORITIZE_SELECTION.get() && !selection.isEmpty()) 682 return; 683 684 if (e.getHoveredPrimitive() != null) { 685 updateUi(e.getHoveredPrimitive()); 686 } else { 687 updateUi(selection); 688 } 689 } 690 691 private void autoresizeTagTable() { 692 if (PROP_AUTORESIZE_TAGS_TABLE.get()) { 693 // resize table's columns to fit content 694 TableHelper.computeColumnsWidth(tagTable); 695 } 696 } 697 698 private void updateUi(IPrimitive primitive) { 699 updateUi(primitive == null ? Collections.emptyList() : 700 Collections.singletonList(primitive)); 701 } 702 703 private void updateUi(Collection<? extends IPrimitive> primitives) { 639 704 IRelation<?> selectedRelation = null; 640 705 String selectedTag = editHelper.getChangedKey(); // select last added or last edited key by default … … 645 710 selectedRelation = (IRelation<?>) membershipData.getValueAt(membershipTable.getSelectedRow(), 0); 646 711 } 712 713 updateTagTableData(primitives); 714 updateMembershipTableData(primitives); 715 716 updateMembershipTableVisibility(); 717 updateActionsEnabledState(); 718 updateTagTableVisibility(primitives); 719 720 setupTaginfoNationalActions(primitives); 721 autoresizeTagTable(); 722 723 int selectedIndex; 724 if (selectedTag != null && (selectedIndex = findViewRow(tagTable, tagData, selectedTag)) != -1) { 725 tagTable.changeSelection(selectedIndex, 0, false, false); 726 } else if (selectedRelation != null && (selectedIndex = findViewRow(membershipTable, membershipData, selectedRelation)) != -1) { 727 membershipTable.changeSelection(selectedIndex, 0, false, false); 728 } else if (tagData.getRowCount() > 0) { 729 tagTable.changeSelection(0, 0, false, false); 730 } else if (membershipData.getRowCount() > 0) { 731 membershipTable.changeSelection(0, 0, false, false); 732 } 733 734 updateTitle(primitives); 735 } 736 737 private void updateTagTableData(Collection<? extends IPrimitive> primitives) { 738 int newSelSize = primitives.size(); 647 739 648 740 // re-load tag data … … 654 746 valueCount.clear(); 655 747 Set<TaggingPresetType> types = EnumSet.noneOf(TaggingPresetType.class); 656 for (IPrimitive osm : newSel) {748 for (IPrimitive osm : primitives) { 657 749 types.add(TaggingPresetType.forPrimitive(osm)); 658 750 osm.visitKeys((p, key, value) -> { … … 680 772 } 681 773 774 presets.updatePresets(types, tags, presetHandler); 775 } 776 777 private void updateMembershipTableData(Collection<? extends IPrimitive> primitives) { 682 778 membershipData.setRowCount(0); 683 779 684 780 Map<IRelation<?>, MemberInfo> roles = new HashMap<>(); 685 for (IPrimitive primitive : newSel) {686 for (IPrimitive ref : primitive.getReferrers(true)) {781 for (IPrimitive primitive : primitives) { 782 for (IPrimitive ref : primitive.getReferrers(true)) { 687 783 if (ref instanceof IRelation && !ref.isIncomplete() && !ref.isDeleted()) { 688 784 IRelation<?> r = (IRelation<?>) ref; 689 MemberInfo mi = roles.computeIfAbsent(r, ignore -> new MemberInfo( newSel));785 MemberInfo mi = roles.computeIfAbsent(r, ignore -> new MemberInfo(primitives)); 690 786 int i = 1; 691 787 for (IRelationMember<?> m : r.getMembers()) { … … 708 804 membershipData.addRow(new Object[]{r, roles.get(r)}); 709 805 } 710 711 presets.updatePresets(types, tags, presetHandler); 712 806 } 807 808 private void updateMembershipTableVisibility() { 713 809 membershipTable.getTableHeader().setVisible(membershipData.getRowCount() > 0); 714 810 membershipTable.setVisible(membershipData.getRowCount() > 0); 715 716 OsmData<?, ?, ?, ?> ds = MainApplication.getLayerManager().getActiveData(); 717 boolean isReadOnly = ds != null && ds.isLocked();718 boolean hasSelection = ! newSel.isEmpty();811 } 812 813 private void updateTagTableVisibility(Collection<? extends IPrimitive> primitives) { 814 boolean hasSelection = !primitives.isEmpty(); 719 815 boolean hasTags = hasSelection && tagData.getRowCount() > 0; 720 boolean hasMemberships = hasSelection && membershipData.getRowCount() > 0; 721 addAction.setEnabled(!isReadOnly && hasSelection); 722 editAction.setEnabled(!isReadOnly && (hasTags || hasMemberships)); 723 deleteAction.setEnabled(!isReadOnly && (hasTags || hasMemberships)); 816 724 817 tagTable.setVisible(hasTags); 725 818 tagTable.getTableHeader().setVisible(hasTags); … … 727 820 selectSth.setVisible(!hasSelection); 728 821 pluginHook.setVisible(hasSelection); 729 730 setupTaginfoNationalActions(newSel); 731 autoresizeTagTable(); 732 733 int selectedIndex; 734 if (selectedTag != null && (selectedIndex = findViewRow(tagTable, tagData, selectedTag)) != -1) { 735 tagTable.changeSelection(selectedIndex, 0, false, false); 736 } else if (selectedRelation != null && (selectedIndex = findViewRow(membershipTable, membershipData, selectedRelation)) != -1) { 737 membershipTable.changeSelection(selectedIndex, 0, false, false); 738 } else if (hasTags) { 739 tagTable.changeSelection(0, 0, false, false); 740 } else if (hasMemberships) { 741 membershipTable.changeSelection(0, 0, false, false); 742 } 743 822 } 823 824 private void updateActionsEnabledState() { 825 addAction.updateEnabledState(); 826 editAction.updateEnabledState(); 827 deleteAction.updateEnabledState(); 828 } 829 830 private void updateTitle(Collection<? extends IPrimitive> primitives) { 831 int newSelSize = primitives.size(); 744 832 if (tagData.getRowCount() != 0 || membershipData.getRowCount() != 0) { 745 833 if (newSelSize > 1) { … … 752 840 } else { 753 841 setTitle(tr("Tags/Memberships")); 754 }755 }756 757 private void autoresizeTagTable() {758 if (PROP_AUTORESIZE_TAGS_TABLE.get()) {759 // resize table's columns to fit content760 TableHelper.computeColumnsWidth(tagTable);761 842 } 762 843 } … … 804 885 // it is time to save history of tags 805 886 updateSelection(); 887 888 // Listen for active layer visibility change to enable/disable hover preview 889 // Remove previous listener first (order matters if we are somehow getting a layer change event 890 // switching from one layer to the same layer) 891 Layer prevLayer = e.getPreviousDataLayer(); 892 if (prevLayer != null) { 893 prevLayer.removePropertyChangeListener(this); 894 } 895 896 Layer newLayer = e.getSource().getActiveDataLayer(); 897 if (newLayer != null) { 898 newLayer.addPropertyChangeListener(this); 899 if (newLayer.isVisible()) { 900 MainApplication.getMap().mapView.addPrimitiveHoverListener(this); 901 } else { 902 MainApplication.getMap().mapView.removePrimitiveHoverListener(this); 903 } 904 } 905 } 906 907 @Override 908 public void propertyChange(PropertyChangeEvent e) { 909 if (Layer.VISIBLE_PROP.equals(e.getPropertyName())) { 910 boolean isVisible = (boolean) e.getNewValue(); 911 912 // Disable hover preview when primitives are invisible 913 if (isVisible) { 914 MainApplication.getMap().mapView.addPrimitiveHoverListener(this); 915 } else { 916 MainApplication.getMap().mapView.removePrimitiveHoverListener(this); 917 } 918 } 806 919 } 807 920 … … 1252 1365 } 1253 1366 } 1367 1368 @Override 1369 protected final void updateEnabledState() { 1370 DataSet ds = OsmDataManager.getInstance().getActiveDataSet(); 1371 setEnabled(ds != null && !ds.isLocked() && 1372 !Utils.isEmpty(OsmDataManager.getInstance().getInProgressSelection())); 1373 } 1254 1374 } 1255 1375 … … 1405 1525 } 1406 1526 } 1527 1528 private class HoverPreviewPropListener implements ValueChangeListener<Boolean> { 1529 @Override 1530 public void valueChanged(ValueChangeEvent<? extends Boolean> e) { 1531 if (e.getProperty().get() && isDialogShowing()) { 1532 MainApplication.getMap().mapView.addPrimitiveHoverListener(PropertiesDialog.this); 1533 } else if (!e.getProperty().get()) { 1534 MainApplication.getMap().mapView.removePrimitiveHoverListener(PropertiesDialog.this); 1535 } 1536 } 1537 } 1407 1538 } -
trunk/src/org/openstreetmap/josm/gui/preferences/display/LafPreference.java
r17841 r18574 33 33 import org.openstreetmap.josm.gui.NavigatableComponent; 34 34 import org.openstreetmap.josm.gui.dialogs.ToggleDialog; 35 import org.openstreetmap.josm.gui.dialogs.properties.PropertiesDialog; 35 36 36 37 import org.openstreetmap.josm.gui.preferences.PreferenceSetting; … … 92 93 private final JCheckBox showLocalizedName = new JCheckBox(tr("Show localized name in selection lists")); 93 94 private final JCheckBox modeless = new JCheckBox(tr("Modeless working (Potlatch style)")); 95 private final JCheckBox previewPropsOnHover = new JCheckBox(tr("Preview object properties on mouse hover")); 96 private final JCheckBox previewPrioritizeSelection = new JCheckBox(tr("Prefer showing information for selected objects")); 94 97 private final JCheckBox dynamicButtons = new JCheckBox(tr("Dynamic buttons in side menus")); 95 98 private final JCheckBox isoDates = new JCheckBox(tr("Display ISO dates")); … … 174 177 panel.add(showLocalizedName, GBC.eop().insets(20, 0, 0, 0)); 175 178 panel.add(modeless, GBC.eop().insets(20, 0, 0, 0)); 179 180 previewPropsOnHover.setToolTipText( 181 tr("Show tags and relation memberships of objects in the properties dialog when hovering over them with the mouse pointer")); 182 previewPropsOnHover.setSelected(PropertiesDialog.PROP_PREVIEW_ON_HOVER.get()); 183 panel.add(previewPropsOnHover, GBC.eop().insets(20, 0, 0, 0)); 184 185 previewPrioritizeSelection.setToolTipText( 186 tr("Always show information for selected objects when something is selected instead of the hovered object")); 187 previewPrioritizeSelection.setSelected(PropertiesDialog.PROP_PREVIEW_ON_HOVER_PRIORITIZE_SELECTION.get()); 188 panel.add(previewPrioritizeSelection, GBC.eop().insets(40, 0, 0, 0)); 189 previewPropsOnHover.addActionListener(l -> previewPrioritizeSelection.setEnabled(previewPropsOnHover.isSelected())); 176 190 177 191 dynamicButtons.setToolTipText(tr("Display buttons in right side menus only when mouse is inside the element")); … … 239 253 Config.getPref().putBoolean("osm-primitives.localize-name", showLocalizedName.isSelected()); 240 254 MapFrame.MODELESS.put(modeless.isSelected()); 255 PropertiesDialog.PROP_PREVIEW_ON_HOVER.put(previewPropsOnHover.isSelected()); 256 PropertiesDialog.PROP_PREVIEW_ON_HOVER_PRIORITIZE_SELECTION.put(previewPrioritizeSelection.isSelected()); 241 257 Config.getPref().putBoolean(ToggleDialog.PROP_DYNAMIC_BUTTONS.getKey(), dynamicButtons.isSelected()); 242 258 Config.getPref().putBoolean(DateUtils.PROP_ISO_DATES.getKey(), isoDates.isSelected()); -
trunk/src/org/openstreetmap/josm/gui/tagging/presets/TaggingPreset.java
r18321 r18574 468 468 */ 469 469 public boolean isShowable() { 470 return data.stream().anyMatch(i -> !(i instanceof Optional || i instanceof Space || i instanceof Key)); 470 // Not using streams makes this method effectively allocation free and uses ~40% fewer CPU cycles. 471 for (TaggingPresetItem i : data) { 472 if (!(i instanceof Optional || i instanceof Space || i instanceof Key)) { 473 return true; 474 } 475 } 476 return false; 471 477 } 472 478
Note:
See TracChangeset
for help on using the changeset viewer.