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

Last change on this file since 12657 was 12657, checked in by michael2402, 7 years ago

See #14794: Add javadoc for gui/dialogs/relation package.

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