source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTable.java@ 16438

Last change on this file since 16438 was 16438, checked in by simon04, 4 years ago

see #19251 - Java 8: use Stream

  • Property svn:eol-style set to native
File size: 10.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs.relation;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Dimension;
7import java.awt.GraphicsEnvironment;
8import java.awt.event.ActionEvent;
9import java.util.Collection;
10import java.util.EnumSet;
11import java.util.Set;
12import java.util.stream.Collectors;
13
14import javax.swing.AbstractAction;
15import javax.swing.DropMode;
16import javax.swing.JPopupMenu;
17import javax.swing.JTable;
18import javax.swing.ListSelectionModel;
19import javax.swing.SwingUtilities;
20import javax.swing.event.ListSelectionEvent;
21import javax.swing.event.ListSelectionListener;
22
23import org.openstreetmap.josm.actions.AutoScaleAction;
24import org.openstreetmap.josm.actions.AutoScaleAction.AutoScaleMode;
25import org.openstreetmap.josm.actions.ZoomToAction;
26import org.openstreetmap.josm.data.osm.OsmPrimitive;
27import org.openstreetmap.josm.data.osm.Relation;
28import org.openstreetmap.josm.data.osm.RelationMember;
29import org.openstreetmap.josm.data.osm.Way;
30import org.openstreetmap.josm.gui.MainApplication;
31import org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionType;
32import org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionType.Direction;
33import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
34import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
35import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
36import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
37import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
38import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
39import org.openstreetmap.josm.gui.layer.OsmDataLayer;
40import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager;
41import org.openstreetmap.josm.gui.util.HighlightHelper;
42import org.openstreetmap.josm.gui.widgets.OsmPrimitivesTable;
43import org.openstreetmap.josm.spi.preferences.Config;
44
45/**
46 * The table of members a selected relation has.
47 */
48public class MemberTable extends OsmPrimitivesTable implements IMemberModelListener {
49
50 /** the additional actions in popup menu */
51 private ZoomToGapAction zoomToGap;
52 private final transient HighlightHelper highlightHelper = new HighlightHelper();
53 private boolean highlightEnabled;
54
55 /**
56 * constructor for relation member table
57 *
58 * @param layer the data layer of the relation. Must not be null
59 * @param relation the relation. Can be null
60 * @param model the table model
61 */
62 public MemberTable(OsmDataLayer layer, Relation relation, MemberTableModel model) {
63 super(model, new MemberTableColumnModel(AutoCompletionManager.of(layer.data), relation), model.getSelectionModel());
64 setLayer(layer);
65 model.addMemberModelListener(this);
66
67 MemberRoleCellEditor ce = (MemberRoleCellEditor) getColumnModel().getColumn(0).getCellEditor();
68 setRowHeight(ce.getEditor().getPreferredSize().height);
69 setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
70 setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
71 putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
72
73 installCustomNavigation(0);
74 initHighlighting();
75
76 if (!GraphicsEnvironment.isHeadless()) {
77 setTransferHandler(new MemberTransferHandler());
78 setFillsViewportHeight(true); // allow drop on empty table
79 if (!GraphicsEnvironment.isHeadless()) {
80 setDragEnabled(true);
81 }
82 setDropMode(DropMode.INSERT_ROWS);
83 }
84 }
85
86 @Override
87 protected ZoomToAction buildZoomToAction() {
88 return new ZoomToAction(this);
89 }
90
91 @Override
92 protected JPopupMenu buildPopupMenu() {
93 JPopupMenu menu = super.buildPopupMenu();
94 zoomToGap = new ZoomToGapAction();
95 registerListeners();
96 menu.addSeparator();
97 getSelectionModel().addListSelectionListener(zoomToGap);
98 menu.add(zoomToGap);
99 menu.addSeparator();
100 menu.add(new SelectPreviousGapAction());
101 menu.add(new SelectNextGapAction());
102 return menu;
103 }
104
105 @Override
106 public Dimension getPreferredSize() {
107 return getPreferredFullWidthSize();
108 }
109
110 @Override
111 public void makeMemberVisible(int index) {
112 scrollRectToVisible(getCellRect(index, 0, true));
113 }
114
115 private transient ListSelectionListener highlighterListener = lse -> {
116 if (MainApplication.isDisplayingMapView()) {
117 Collection<RelationMember> sel = getMemberTableModel().getSelectedMembers();
118 final Set<OsmPrimitive> toHighlight = sel.stream()
119 .filter(r -> r.getMember().isUsable())
120 .map(RelationMember::getMember)
121 .collect(Collectors.toSet());
122 SwingUtilities.invokeLater(() -> {
123 if (MainApplication.isDisplayingMapView() && highlightHelper.highlightOnly(toHighlight)) {
124 MainApplication.getMap().mapView.repaint();
125 }
126 });
127 }
128 };
129
130 private void initHighlighting() {
131 highlightEnabled = Config.getPref().getBoolean("draw.target-highlight", true);
132 if (!highlightEnabled) return;
133 getMemberTableModel().getSelectionModel().addListSelectionListener(highlighterListener);
134 clearAllHighlighted();
135 }
136
137 @Override
138 public void registerListeners() {
139 MainApplication.getLayerManager().addLayerChangeListener(zoomToGap);
140 MainApplication.getLayerManager().addActiveLayerChangeListener(zoomToGap);
141 super.registerListeners();
142 }
143
144 @Override
145 public void unregisterListeners() {
146 super.unregisterListeners();
147 MainApplication.getLayerManager().removeLayerChangeListener(zoomToGap);
148 MainApplication.getLayerManager().removeActiveLayerChangeListener(zoomToGap);
149 }
150
151 /**
152 * Stops highlighting of selected objects.
153 */
154 public void stopHighlighting() {
155 if (highlighterListener == null) return;
156 if (!highlightEnabled) return;
157 getMemberTableModel().getSelectionModel().removeListSelectionListener(highlighterListener);
158 highlighterListener = null;
159 clearAllHighlighted();
160 }
161
162 private static void clearAllHighlighted() {
163 if (MainApplication.isDisplayingMapView()) {
164 HighlightHelper.clearAllHighlighted();
165 MainApplication.getMap().mapView.repaint();
166 }
167 }
168
169 private class SelectPreviousGapAction extends AbstractAction {
170
171 SelectPreviousGapAction() {
172 putValue(NAME, tr("Select previous Gap"));
173 putValue(SHORT_DESCRIPTION, tr("Select the previous relation member which gives rise to a gap"));
174 }
175
176 @Override
177 public void actionPerformed(ActionEvent e) {
178 int i = getSelectedRow() - 1;
179 while (i >= 0 && getMemberTableModel().getWayConnection(i).linkPrev) {
180 i--;
181 }
182 if (i >= 0) {
183 getSelectionModel().setSelectionInterval(i, i);
184 getMemberTableModel().fireMakeMemberVisible(i);
185 }
186 }
187 }
188
189 private class SelectNextGapAction extends AbstractAction {
190
191 SelectNextGapAction() {
192 putValue(NAME, tr("Select next Gap"));
193 putValue(SHORT_DESCRIPTION, tr("Select the next relation member which gives rise to a gap"));
194 }
195
196 @Override
197 public void actionPerformed(ActionEvent e) {
198 int i = getSelectedRow() + 1;
199 while (i < getRowCount() && getMemberTableModel().getWayConnection(i).linkNext) {
200 i++;
201 }
202 if (i < getRowCount()) {
203 getSelectionModel().setSelectionInterval(i, i);
204 getMemberTableModel().fireMakeMemberVisible(i);
205 }
206 }
207 }
208
209 private class ZoomToGapAction extends AbstractAction implements LayerChangeListener, ActiveLayerChangeListener, ListSelectionListener {
210
211 /**
212 * Constructs a new {@code ZoomToGapAction}.
213 */
214 ZoomToGapAction() {
215 putValue(NAME, tr("Zoom to Gap"));
216 putValue(SHORT_DESCRIPTION, tr("Zoom to the gap in the way sequence"));
217 updateEnabledState();
218 }
219
220 private WayConnectionType getConnectionType() {
221 return getMemberTableModel().getWayConnection(getSelectedRows()[0]);
222 }
223
224 private final Collection<Direction> connectionTypesOfInterest = EnumSet.of(
225 WayConnectionType.Direction.FORWARD, WayConnectionType.Direction.BACKWARD);
226
227 private boolean hasGap() {
228 WayConnectionType connectionType = getConnectionType();
229 return connectionTypesOfInterest.contains(connectionType.direction)
230 && !(connectionType.linkNext && connectionType.linkPrev);
231 }
232
233 @Override
234 public void actionPerformed(ActionEvent e) {
235 WayConnectionType connectionType = getConnectionType();
236 Way way = (Way) getMemberTableModel().getReferredPrimitive(getSelectedRows()[0]);
237 if (!connectionType.linkPrev) {
238 getLayer().data.setSelected(WayConnectionType.Direction.FORWARD == connectionType.direction
239 ? way.firstNode() : way.lastNode());
240 AutoScaleAction.autoScale(AutoScaleMode.SELECTION);
241 } else if (!connectionType.linkNext) {
242 getLayer().data.setSelected(WayConnectionType.Direction.FORWARD == connectionType.direction
243 ? way.lastNode() : way.firstNode());
244 AutoScaleAction.autoScale(AutoScaleMode.SELECTION);
245 }
246 }
247
248 private void updateEnabledState() {
249 setEnabled(MainApplication.getLayerManager().getEditLayer() == getLayer()
250 && getSelectedRowCount() == 1
251 && hasGap());
252 }
253
254 @Override
255 public void valueChanged(ListSelectionEvent e) {
256 updateEnabledState();
257 }
258
259 @Override
260 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
261 updateEnabledState();
262 }
263
264 @Override
265 public void layerAdded(LayerAddEvent e) {
266 updateEnabledState();
267 }
268
269 @Override
270 public void layerRemoving(LayerRemoveEvent e) {
271 updateEnabledState();
272 }
273
274 @Override
275 public void layerOrderChanged(LayerOrderChangeEvent e) {
276 // Do nothing
277 }
278 }
279
280 protected MemberTableModel getMemberTableModel() {
281 return (MemberTableModel) getModel();
282 }
283}
Note: See TracBrowser for help on using the repository browser.