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

Last change on this file since 8677 was 8677, checked in by Don-vip, 9 years ago

fix #11793 - NPE

  • Property svn:eol-style set to native
File size: 12.0 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.Container;
7import java.awt.Dimension;
8import java.awt.KeyboardFocusManager;
9import java.awt.event.ActionEvent;
10import java.awt.event.KeyEvent;
11import java.util.ArrayList;
12import java.util.Arrays;
13import java.util.Collection;
14import java.util.List;
15
16import javax.swing.AbstractAction;
17import javax.swing.JComponent;
18import javax.swing.JPopupMenu;
19import javax.swing.JTable;
20import javax.swing.JViewport;
21import javax.swing.KeyStroke;
22import javax.swing.ListSelectionModel;
23import javax.swing.SwingUtilities;
24import javax.swing.event.ListSelectionEvent;
25import javax.swing.event.ListSelectionListener;
26
27import org.openstreetmap.josm.Main;
28import org.openstreetmap.josm.actions.AutoScaleAction;
29import org.openstreetmap.josm.actions.ZoomToAction;
30import org.openstreetmap.josm.data.osm.OsmPrimitive;
31import org.openstreetmap.josm.data.osm.Relation;
32import org.openstreetmap.josm.data.osm.RelationMember;
33import org.openstreetmap.josm.data.osm.Way;
34import org.openstreetmap.josm.gui.MapView;
35import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
36import org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionType;
37import org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionType.Direction;
38import org.openstreetmap.josm.gui.layer.Layer;
39import org.openstreetmap.josm.gui.layer.OsmDataLayer;
40import org.openstreetmap.josm.gui.util.HighlightHelper;
41import org.openstreetmap.josm.gui.widgets.OsmPrimitivesTable;
42
43public class MemberTable extends OsmPrimitivesTable implements IMemberModelListener {
44
45 /** the additional actions in popup menu */
46 private ZoomToGapAction zoomToGap;
47 private transient HighlightHelper highlightHelper = new HighlightHelper();
48 private boolean highlightEnabled;
49
50 /**
51 * constructor for relation member table
52 *
53 * @param layer the data layer of the relation. Must not be null
54 * @param relation the relation. Can be null
55 * @param model the table model
56 */
57 public MemberTable(OsmDataLayer layer, Relation relation, MemberTableModel model) {
58 super(model, new MemberTableColumnModel(layer.data, relation), model.getSelectionModel());
59 setLayer(layer);
60 model.addMemberModelListener(this);
61 init();
62 }
63
64 /**
65 * initialize the table
66 */
67 protected void init() {
68 MemberRoleCellEditor ce = (MemberRoleCellEditor) getColumnModel().getColumn(0).getCellEditor();
69 setRowHeight(ce.getEditor().getPreferredSize().height);
70 setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
71 setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
72 putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
73
74 // make ENTER behave like TAB
75 //
76 getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
77 KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false), "selectNextColumnCell");
78
79 initHighlighting();
80
81 // install custom navigation actions
82 //
83 getActionMap().put("selectNextColumnCell", new SelectNextColumnCellAction());
84 getActionMap().put("selectPreviousColumnCell", new SelectPreviousColumnCellAction());
85 }
86
87 @Override
88 protected ZoomToAction buildZoomToAction() {
89 return new ZoomToAction(this);
90 }
91
92 @Override
93 protected JPopupMenu buildPopupMenu() {
94 JPopupMenu menu = super.buildPopupMenu();
95 zoomToGap = new ZoomToGapAction();
96 MapView.addLayerChangeListener(zoomToGap);
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 Container c = getParent();
108 while (c != null && !(c instanceof JViewport)) {
109 c = c.getParent();
110 }
111 if (c != null) {
112 Dimension d = super.getPreferredSize();
113 d.width = c.getSize().width;
114 return d;
115 }
116 return super.getPreferredSize();
117 }
118
119 @Override
120 public void makeMemberVisible(int index) {
121 scrollRectToVisible(getCellRect(index, 0, true));
122 }
123
124 private transient ListSelectionListener highlighterListener = new ListSelectionListener() {
125 @Override
126 public void valueChanged(ListSelectionEvent lse) {
127 if (Main.isDisplayingMapView()) {
128 Collection<RelationMember> sel = getMemberTableModel().getSelectedMembers();
129 final List<OsmPrimitive> toHighlight = new ArrayList<>();
130 for (RelationMember r: sel) {
131 if (r.getMember().isUsable()) {
132 toHighlight.add(r.getMember());
133 }
134 }
135 SwingUtilities.invokeLater(new Runnable() {
136 @Override
137 public void run() {
138 if (Main.isDisplayingMapView() && highlightHelper.highlightOnly(toHighlight)) {
139 Main.map.mapView.repaint();
140 }
141 }
142 });
143 }
144 }};
145
146 private void initHighlighting() {
147 highlightEnabled = Main.pref.getBoolean("draw.target-highlight", true);
148 if (!highlightEnabled) return;
149 getMemberTableModel().getSelectionModel().addListSelectionListener(highlighterListener);
150 if (Main.isDisplayingMapView()) {
151 HighlightHelper.clearAllHighlighted();
152 Main.map.mapView.repaint();
153 }
154 }
155
156 /**
157 * Action to be run when the user navigates to the next cell in the table, for instance by
158 * pressing TAB or ENTER. The action alters the standard navigation path from cell to cell: <ul>
159 * <li>it jumps over cells in the first column</li> <li>it automatically add a new empty row
160 * when the user leaves the last cell in the table</li></ul>
161 */
162 class SelectNextColumnCellAction extends AbstractAction {
163 @Override
164 public void actionPerformed(ActionEvent e) {
165 run();
166 }
167
168 public void run() {
169 int col = getSelectedColumn();
170 int row = getSelectedRow();
171 if (getCellEditor() != null) {
172 getCellEditor().stopCellEditing();
173 }
174
175 if (col == 0 && row < getRowCount() - 1) {
176 row++;
177 } else if (row < getRowCount() - 1) {
178 col = 0;
179 row++;
180 } else {
181 // go to next component, no more rows in this table
182 KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
183 manager.focusNextComponent();
184 return;
185 }
186 changeSelection(row, col, false, false);
187 }
188 }
189
190 /**
191 * Action to be run when the user navigates to the previous cell in the table, for instance by
192 * pressing Shift-TAB
193 */
194 private class SelectPreviousColumnCellAction extends AbstractAction {
195
196 @Override
197 public void actionPerformed(ActionEvent e) {
198 int col = getSelectedColumn();
199 int row = getSelectedRow();
200 if (getCellEditor() != null) {
201 getCellEditor().stopCellEditing();
202 }
203
204 if (col <= 0 && row <= 0) {
205 // change nothing
206 } else if (row > 0) {
207 col = 0;
208 row--;
209 }
210 changeSelection(row, col, false, false);
211 }
212 }
213
214 @Override
215 public void unlinkAsListener() {
216 super.unlinkAsListener();
217 MapView.removeLayerChangeListener(zoomToGap);
218 }
219
220 public void stopHighlighting() {
221 if (highlighterListener == null) return;
222 if (!highlightEnabled) return;
223 getMemberTableModel().getSelectionModel().removeListSelectionListener(highlighterListener);
224 highlighterListener = null;
225 if (Main.isDisplayingMapView()) {
226 HighlightHelper.clearAllHighlighted();
227 Main.map.mapView.repaint();
228 }
229 }
230
231 private class SelectPreviousGapAction extends AbstractAction {
232
233 public SelectPreviousGapAction() {
234 putValue(NAME, tr("Select previous Gap"));
235 putValue(SHORT_DESCRIPTION, tr("Select the previous relation member which gives rise to a gap"));
236 }
237
238 @Override
239 public void actionPerformed(ActionEvent e) {
240 int i = getSelectedRow() - 1;
241 while (i >= 0 && getMemberTableModel().getWayConnection(i).linkPrev) {
242 i--;
243 }
244 if (i >= 0) {
245 getSelectionModel().setSelectionInterval(i, i);
246 }
247 }
248 }
249
250 private class SelectNextGapAction extends AbstractAction {
251
252 public SelectNextGapAction() {
253 putValue(NAME, tr("Select next Gap"));
254 putValue(SHORT_DESCRIPTION, tr("Select the next relation member which gives rise to a gap"));
255 }
256
257 @Override
258 public void actionPerformed(ActionEvent e) {
259 int i = getSelectedRow() + 1;
260 while (i < getRowCount() && getMemberTableModel().getWayConnection(i).linkNext) {
261 i++;
262 }
263 if (i < getRowCount()) {
264 getSelectionModel().setSelectionInterval(i, i);
265 }
266 }
267 }
268
269 private class ZoomToGapAction extends AbstractAction implements LayerChangeListener, ListSelectionListener {
270
271 /**
272 * Constructs a new {@code ZoomToGapAction}.
273 */
274 public ZoomToGapAction() {
275 putValue(NAME, tr("Zoom to Gap"));
276 putValue(SHORT_DESCRIPTION, tr("Zoom to the gap in the way sequence"));
277 updateEnabledState();
278 }
279
280 private WayConnectionType getConnectionType() {
281 return getMemberTableModel().getWayConnection(getSelectedRows()[0]);
282 }
283
284 private final Collection<Direction> connectionTypesOfInterest = Arrays.asList(
285 WayConnectionType.Direction.FORWARD, WayConnectionType.Direction.BACKWARD);
286
287 private boolean hasGap() {
288 WayConnectionType connectionType = getConnectionType();
289 return connectionTypesOfInterest.contains(connectionType.direction)
290 && !(connectionType.linkNext && connectionType.linkPrev);
291 }
292
293 @Override
294 public void actionPerformed(ActionEvent e) {
295 WayConnectionType connectionType = getConnectionType();
296 Way way = (Way) getMemberTableModel().getReferredPrimitive(getSelectedRows()[0]);
297 if (!connectionType.linkPrev) {
298 getLayer().data.setSelected(WayConnectionType.Direction.FORWARD.equals(connectionType.direction)
299 ? way.firstNode() : way.lastNode());
300 AutoScaleAction.autoScale("selection");
301 } else if (!connectionType.linkNext) {
302 getLayer().data.setSelected(WayConnectionType.Direction.FORWARD.equals(connectionType.direction)
303 ? way.lastNode() : way.firstNode());
304 AutoScaleAction.autoScale("selection");
305 }
306 }
307
308 private void updateEnabledState() {
309 setEnabled(Main.main != null
310 && Main.main.getEditLayer() == getLayer()
311 && getSelectedRowCount() == 1
312 && hasGap());
313 }
314
315 @Override
316 public void valueChanged(ListSelectionEvent e) {
317 updateEnabledState();
318 }
319
320 @Override
321 public void activeLayerChange(Layer oldLayer, Layer newLayer) {
322 updateEnabledState();
323 }
324
325 @Override
326 public void layerAdded(Layer newLayer) {
327 updateEnabledState();
328 }
329
330 @Override
331 public void layerRemoved(Layer oldLayer) {
332 updateEnabledState();
333 }
334 }
335
336 protected MemberTableModel getMemberTableModel() {
337 return (MemberTableModel) getModel();
338 }
339}
Note: See TracBrowser for help on using the repository browser.