source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetContentPanel.java@ 16436

Last change on this file since 16436 was 16436, checked in by simon04, 5 years ago

see #19251 - Java 8: use Stream

  • Property svn:eol-style set to native
File size: 17.2 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs.changeset;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trn;
6
7import java.awt.BorderLayout;
8import java.awt.FlowLayout;
9import java.awt.event.ActionEvent;
10import java.awt.event.ComponentAdapter;
11import java.awt.event.ComponentEvent;
12import java.beans.PropertyChangeEvent;
13import java.beans.PropertyChangeListener;
14import java.util.Collection;
15import java.util.Collections;
16import java.util.List;
17import java.util.Objects;
18import java.util.Set;
19import java.util.stream.Collectors;
20
21import javax.swing.AbstractAction;
22import javax.swing.BorderFactory;
23import javax.swing.DefaultListSelectionModel;
24import javax.swing.JButton;
25import javax.swing.JComponent;
26import javax.swing.JOptionPane;
27import javax.swing.JPanel;
28import javax.swing.JPopupMenu;
29import javax.swing.JScrollPane;
30import javax.swing.JSeparator;
31import javax.swing.JTable;
32import javax.swing.JToolBar;
33import javax.swing.SwingConstants;
34import javax.swing.event.ListSelectionEvent;
35import javax.swing.event.ListSelectionListener;
36
37import org.openstreetmap.josm.actions.AutoScaleAction;
38import org.openstreetmap.josm.actions.HistoryInfoAction;
39import org.openstreetmap.josm.actions.downloadtasks.ChangesetContentDownloadTask;
40import org.openstreetmap.josm.data.osm.Changeset;
41import org.openstreetmap.josm.data.osm.DataSet;
42import org.openstreetmap.josm.data.osm.OsmPrimitive;
43import org.openstreetmap.josm.data.osm.PrimitiveId;
44import org.openstreetmap.josm.data.osm.history.History;
45import org.openstreetmap.josm.data.osm.history.HistoryDataSet;
46import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
47import org.openstreetmap.josm.gui.HelpAwareOptionPane;
48import org.openstreetmap.josm.gui.MainApplication;
49import org.openstreetmap.josm.gui.help.HelpUtil;
50import org.openstreetmap.josm.gui.history.HistoryBrowserDialogManager;
51import org.openstreetmap.josm.gui.history.HistoryLoadTask;
52import org.openstreetmap.josm.gui.io.DownloadPrimitivesWithReferrersTask;
53import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
54import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
55import org.openstreetmap.josm.gui.util.GuiHelper;
56import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
57import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
58import org.openstreetmap.josm.tools.ImageProvider;
59import org.openstreetmap.josm.tools.JosmRuntimeException;
60import org.openstreetmap.josm.tools.Utils;
61import org.openstreetmap.josm.tools.bugreport.BugReportExceptionHandler;
62
63/**
64 * The panel which displays the content of a changeset in a scrollable table.
65 *
66 * It listens to property change events for {@link ChangesetCacheManagerModel#CHANGESET_IN_DETAIL_VIEW_PROP}
67 * and updates its view accordingly.
68 * @since 2689
69 */
70public class ChangesetContentPanel extends JPanel implements PropertyChangeListener, ChangesetAware {
71 private JTable tblContent;
72 private ChangesetContentTableModel model;
73 private transient Changeset currentChangeset;
74
75 private DownloadChangesetContentAction actDownloadContentAction;
76 private ShowHistoryAction actShowHistory;
77 private SelectInCurrentLayerAction actSelectInCurrentLayerAction;
78 private ZoomInCurrentLayerAction actZoomInCurrentLayerAction;
79
80 private final HeaderPanel pnlHeader = new HeaderPanel();
81 protected DownloadObjectAction actDownloadObjectAction;
82
83 protected void buildModels() {
84 DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
85 model = new ChangesetContentTableModel(selectionModel);
86 actDownloadContentAction = new DownloadChangesetContentAction(this);
87 actDownloadContentAction.initProperties();
88
89 actDownloadObjectAction = new DownloadObjectAction();
90 model.getSelectionModel().addListSelectionListener(actDownloadObjectAction);
91
92 actShowHistory = new ShowHistoryAction();
93 model.getSelectionModel().addListSelectionListener(actShowHistory);
94
95 actSelectInCurrentLayerAction = new SelectInCurrentLayerAction();
96 model.getSelectionModel().addListSelectionListener(actSelectInCurrentLayerAction);
97 MainApplication.getLayerManager().addActiveLayerChangeListener(actSelectInCurrentLayerAction);
98
99 actZoomInCurrentLayerAction = new ZoomInCurrentLayerAction();
100 model.getSelectionModel().addListSelectionListener(actZoomInCurrentLayerAction);
101 MainApplication.getLayerManager().addActiveLayerChangeListener(actZoomInCurrentLayerAction);
102
103 addComponentListener(
104 new ComponentAdapter() {
105 @Override
106 public void componentShown(ComponentEvent e) {
107 MainApplication.getLayerManager().addAndFireActiveLayerChangeListener(actSelectInCurrentLayerAction);
108 MainApplication.getLayerManager().addAndFireActiveLayerChangeListener(actZoomInCurrentLayerAction);
109 }
110
111 @Override
112 public void componentHidden(ComponentEvent e) {
113 // make sure the listener is unregistered when the panel becomes invisible
114 MainApplication.getLayerManager().removeActiveLayerChangeListener(actSelectInCurrentLayerAction);
115 MainApplication.getLayerManager().removeActiveLayerChangeListener(actZoomInCurrentLayerAction);
116 }
117 }
118 );
119 }
120
121 protected JPanel buildContentPanel() {
122 JPanel pnl = new JPanel(new BorderLayout());
123 tblContent = new JTable(
124 model,
125 new ChangesetContentTableColumnModel(),
126 model.getSelectionModel()
127 );
128 tblContent.setAutoCreateRowSorter(true);
129 tblContent.addMouseListener(new PopupMenuLauncher(new ChangesetContentTablePopupMenu()));
130 HistoryInfoAction historyAction = MainApplication.getMenu().historyinfo;
131 tblContent.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(historyAction.getShortcut().getKeyStroke(), "historyAction");
132 tblContent.getActionMap().put("historyAction", historyAction);
133 pnl.add(new JScrollPane(tblContent), BorderLayout.CENTER);
134 return pnl;
135 }
136
137 protected JPanel buildActionButtonPanel() {
138 JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT));
139 JToolBar tb = new JToolBar(SwingConstants.VERTICAL);
140 tb.setFloatable(false);
141
142 tb.add(actDownloadContentAction);
143 tb.addSeparator();
144 tb.add(actDownloadObjectAction);
145 tb.add(actShowHistory);
146 tb.addSeparator();
147 tb.add(actSelectInCurrentLayerAction);
148 tb.add(actZoomInCurrentLayerAction);
149
150 pnl.add(tb);
151 return pnl;
152 }
153
154 protected final void build() {
155 setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
156 setLayout(new BorderLayout());
157 buildModels();
158
159 add(pnlHeader, BorderLayout.NORTH);
160 add(buildActionButtonPanel(), BorderLayout.WEST);
161 add(buildContentPanel(), BorderLayout.CENTER);
162 }
163
164 /**
165 * Constructs a new {@code ChangesetContentPanel}.
166 */
167 public ChangesetContentPanel() {
168 build();
169 }
170
171 /**
172 * Replies the changeset content model
173 * @return The model
174 */
175 public ChangesetContentTableModel getModel() {
176 return model;
177 }
178
179 protected void setCurrentChangeset(Changeset cs) {
180 currentChangeset = cs;
181 if (cs == null) {
182 model.populate(null);
183 } else {
184 model.populate(cs.getContent());
185 }
186 actDownloadContentAction.initProperties();
187 pnlHeader.setChangeset(cs);
188 }
189
190 /* ---------------------------------------------------------------------------- */
191 /* interface PropertyChangeListener */
192 /* ---------------------------------------------------------------------------- */
193 @Override
194 public void propertyChange(PropertyChangeEvent evt) {
195 if (!evt.getPropertyName().equals(ChangesetCacheManagerModel.CHANGESET_IN_DETAIL_VIEW_PROP))
196 return;
197 Changeset cs = (Changeset) evt.getNewValue();
198 setCurrentChangeset(cs);
199 }
200
201 private void alertNoPrimitivesTo(Collection<HistoryOsmPrimitive> primitives, String title, String helpTopic) {
202 HelpAwareOptionPane.showOptionDialog(
203 this,
204 trn("<html>The selected object is not available in the current<br>"
205 + "edit layer ''{0}''.</html>",
206 "<html>None of the selected objects is available in the current<br>"
207 + "edit layer ''{0}''.</html>",
208 primitives.size(),
209 Utils.escapeReservedCharactersHTML(MainApplication.getLayerManager().getEditLayer().getName())
210 ),
211 title, JOptionPane.WARNING_MESSAGE, helpTopic
212 );
213 }
214
215 private Set<HistoryOsmPrimitive> getSelectedPrimitives() {
216 return model.getSelectedPrimitives(tblContent);
217 }
218
219 class ChangesetContentTablePopupMenu extends JPopupMenu {
220 ChangesetContentTablePopupMenu() {
221 add(actDownloadContentAction);
222 add(new JSeparator());
223 add(actDownloadObjectAction);
224 add(actShowHistory);
225 add(new JSeparator());
226 add(actSelectInCurrentLayerAction);
227 add(actZoomInCurrentLayerAction);
228 }
229 }
230
231 class ShowHistoryAction extends AbstractAction implements ListSelectionListener {
232
233 private final class ShowHistoryTask implements Runnable {
234 private final Collection<HistoryOsmPrimitive> primitives;
235
236 private ShowHistoryTask(Collection<HistoryOsmPrimitive> primitives) {
237 this.primitives = primitives;
238 }
239
240 @Override
241 public void run() {
242 try {
243 for (HistoryOsmPrimitive p : primitives) {
244 final History h = HistoryDataSet.getInstance().getHistory(p.getPrimitiveId());
245 if (h == null) {
246 continue;
247 }
248 GuiHelper.runInEDT(() -> HistoryBrowserDialogManager.getInstance().show(h));
249 }
250 } catch (final JosmRuntimeException | IllegalArgumentException | IllegalStateException e) {
251 GuiHelper.runInEDT(() -> BugReportExceptionHandler.handleException(e));
252 }
253 }
254 }
255
256 ShowHistoryAction() {
257 putValue(NAME, tr("Show history"));
258 new ImageProvider("dialogs", "history").getResource().attachImageIcon(this);
259 putValue(SHORT_DESCRIPTION, tr("Download and show the history of the selected objects"));
260 updateEnabledState();
261 }
262
263 protected List<HistoryOsmPrimitive> filterPrimitivesWithUnloadedHistory(Collection<HistoryOsmPrimitive> primitives) {
264 return primitives.stream()
265 .filter(p -> HistoryDataSet.getInstance().getHistory(p.getPrimitiveId()) == null)
266 .collect(Collectors.toList());
267 }
268
269 public void showHistory(final Collection<HistoryOsmPrimitive> primitives) {
270
271 List<HistoryOsmPrimitive> toLoad = filterPrimitivesWithUnloadedHistory(primitives);
272 if (!toLoad.isEmpty()) {
273 HistoryLoadTask task = new HistoryLoadTask(ChangesetContentPanel.this);
274 for (HistoryOsmPrimitive p: toLoad) {
275 task.add(p);
276 }
277 MainApplication.worker.submit(task);
278 }
279
280 MainApplication.worker.submit(new ShowHistoryTask(primitives));
281 }
282
283 protected final void updateEnabledState() {
284 setEnabled(model.hasSelectedPrimitives());
285 }
286
287 @Override
288 public void actionPerformed(ActionEvent e) {
289 Set<HistoryOsmPrimitive> selected = getSelectedPrimitives();
290 if (selected.isEmpty()) return;
291 showHistory(selected);
292 }
293
294 @Override
295 public void valueChanged(ListSelectionEvent e) {
296 updateEnabledState();
297 }
298 }
299
300 class DownloadObjectAction extends AbstractAction implements ListSelectionListener {
301
302 DownloadObjectAction() {
303 putValue(NAME, tr("Download objects"));
304 new ImageProvider("downloadprimitive").getResource().attachImageIcon(this, true);
305 putValue(SHORT_DESCRIPTION, tr("Download the current version of the selected objects"));
306 updateEnabledState();
307 }
308
309 @Override
310 public void actionPerformed(ActionEvent e) {
311 final List<PrimitiveId> primitiveIds = getSelectedPrimitives().stream().map(HistoryOsmPrimitive::getPrimitiveId)
312 .collect(Collectors.toList());
313 MainApplication.worker.submit(new DownloadPrimitivesWithReferrersTask(false, primitiveIds, true, true, null, null));
314 }
315
316 protected final void updateEnabledState() {
317 setEnabled(model.hasSelectedPrimitives());
318 }
319
320 @Override
321 public void valueChanged(ListSelectionEvent e) {
322 updateEnabledState();
323 }
324 }
325
326 abstract class SelectionBasedAction extends AbstractAction implements ListSelectionListener, ActiveLayerChangeListener {
327
328 protected Set<OsmPrimitive> getTarget() {
329 DataSet ds = MainApplication.getLayerManager().getActiveDataSet();
330 if (isEnabled() && ds != null) {
331 return getSelectedPrimitives().stream()
332 .map(p -> ds.getPrimitiveById(p.getPrimitiveId())).filter(Objects::nonNull).collect(Collectors.toSet());
333 }
334 return Collections.emptySet();
335 }
336
337 public final void updateEnabledState() {
338 setEnabled(MainApplication.getLayerManager().getActiveDataSet() != null && model.hasSelectedPrimitives());
339 }
340
341 @Override
342 public void valueChanged(ListSelectionEvent e) {
343 updateEnabledState();
344 }
345
346 @Override
347 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
348 updateEnabledState();
349 }
350 }
351
352 class SelectInCurrentLayerAction extends SelectionBasedAction {
353
354 SelectInCurrentLayerAction() {
355 putValue(NAME, tr("Select in layer"));
356 new ImageProvider("dialogs", "select").getResource().attachImageIcon(this);
357 putValue(SHORT_DESCRIPTION, tr("Select the corresponding primitives in the current data layer"));
358 updateEnabledState();
359 }
360
361 @Override
362 public void actionPerformed(ActionEvent e) {
363 final Set<OsmPrimitive> target = getTarget();
364 if (target.isEmpty()) {
365 alertNoPrimitivesTo(getSelectedPrimitives(), tr("Nothing to select"),
366 HelpUtil.ht("/Dialog/ChangesetCacheManager#NothingToSelectInLayer"));
367 return;
368 }
369 MainApplication.getLayerManager().getActiveDataSet().setSelected(target);
370 }
371 }
372
373 class ZoomInCurrentLayerAction extends SelectionBasedAction {
374
375 ZoomInCurrentLayerAction() {
376 putValue(NAME, tr("Zoom to in layer"));
377 new ImageProvider("dialogs/autoscale", "selection").getResource().attachImageIcon(this);
378 putValue(SHORT_DESCRIPTION, tr("Zoom to the corresponding objects in the current data layer"));
379 updateEnabledState();
380 }
381
382 @Override
383 public void actionPerformed(ActionEvent e) {
384 final Set<OsmPrimitive> target = getTarget();
385 if (target.isEmpty()) {
386 alertNoPrimitivesTo(getSelectedPrimitives(), tr("Nothing to zoom to"),
387 HelpUtil.ht("/Dialog/ChangesetCacheManager#NothingToZoomTo"));
388 return;
389 }
390 MainApplication.getLayerManager().getActiveDataSet().setSelected(target);
391 AutoScaleAction.zoomToSelection();
392 }
393 }
394
395 private static class HeaderPanel extends JPanel {
396
397 private transient Changeset current;
398
399 HeaderPanel() {
400 build();
401 }
402
403 protected final void build() {
404 setLayout(new FlowLayout(FlowLayout.LEFT));
405 add(new JMultilineLabel(tr("The content of this changeset is not downloaded yet.")));
406 add(new JButton(new DownloadAction()));
407
408 }
409
410 public void setChangeset(Changeset cs) {
411 setVisible(cs != null && cs.getContent() == null);
412 this.current = cs;
413 }
414
415 private class DownloadAction extends AbstractAction {
416 DownloadAction() {
417 putValue(NAME, tr("Download now"));
418 putValue(SHORT_DESCRIPTION, tr("Download the changeset content"));
419 new ImageProvider("dialogs/changeset", "downloadchangesetcontent").getResource().attachImageIcon(this);
420 }
421
422 @Override
423 public void actionPerformed(ActionEvent evt) {
424 if (current == null) return;
425 ChangesetContentDownloadTask task = new ChangesetContentDownloadTask(HeaderPanel.this, current.getId());
426 ChangesetCacheManager.getInstance().runDownloadTask(task);
427 }
428 }
429 }
430
431 @Override
432 public Changeset getCurrentChangeset() {
433 return currentChangeset;
434 }
435}
Note: See TracBrowser for help on using the repository browser.