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

Last change on this file since 17206 was 16495, checked in by GerdP, 4 years ago

see #19257: reduce number of ShowHistoryAction implementations
Add AbstractShowHistoryAction with common texts "History" and "Download and show the history of the selected objects" to reduce number of I18N strings. To be used with popup menus and side buttons where selected objects are known.

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