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

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

see #8039, see #10456 - support read-only data layers

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