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

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

PMD - Strict Exceptions

  • Property svn:eol-style set to native
File size: 16.8 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.Main;
36import org.openstreetmap.josm.actions.AutoScaleAction;
37import org.openstreetmap.josm.actions.downloadtasks.ChangesetContentDownloadTask;
38import org.openstreetmap.josm.data.osm.Changeset;
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.help.HelpUtil;
46import org.openstreetmap.josm.gui.history.HistoryBrowserDialogManager;
47import org.openstreetmap.josm.gui.history.HistoryLoadTask;
48import org.openstreetmap.josm.gui.io.DownloadPrimitivesWithReferrersTask;
49import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
50import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
51import org.openstreetmap.josm.gui.layer.OsmDataLayer;
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.bugreport.BugReportExceptionHandler;
58
59/**
60 * The panel which displays the content of a changeset in a scrollable table.
61 *
62 * It listens to property change events for {@link ChangesetCacheManagerModel#CHANGESET_IN_DETAIL_VIEW_PROP}
63 * and updates its view accordingly.
64 *
65 */
66public class ChangesetContentPanel extends JPanel implements PropertyChangeListener, ChangesetAware {
67
68    private ChangesetContentTableModel model;
69    private transient Changeset currentChangeset;
70
71    private DownloadChangesetContentAction actDownloadContentAction;
72    private ShowHistoryAction actShowHistory;
73    private SelectInCurrentLayerAction actSelectInCurrentLayerAction;
74    private ZoomInCurrentLayerAction actZoomInCurrentLayerAction;
75
76    private final HeaderPanel pnlHeader = new HeaderPanel();
77    public DownloadObjectAction actDownloadObjectAction;
78
79    protected void buildModels() {
80        DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
81        model = new ChangesetContentTableModel(selectionModel);
82        actDownloadContentAction = new DownloadChangesetContentAction(this);
83        actDownloadContentAction.initProperties();
84
85        actDownloadObjectAction = new DownloadObjectAction();
86        model.getSelectionModel().addListSelectionListener(actDownloadObjectAction);
87
88        actShowHistory = new ShowHistoryAction();
89        model.getSelectionModel().addListSelectionListener(actShowHistory);
90
91        actSelectInCurrentLayerAction = new SelectInCurrentLayerAction();
92        model.getSelectionModel().addListSelectionListener(actSelectInCurrentLayerAction);
93        Main.getLayerManager().addActiveLayerChangeListener(actSelectInCurrentLayerAction);
94
95        actZoomInCurrentLayerAction = new ZoomInCurrentLayerAction();
96        model.getSelectionModel().addListSelectionListener(actZoomInCurrentLayerAction);
97        Main.getLayerManager().addActiveLayerChangeListener(actZoomInCurrentLayerAction);
98
99        addComponentListener(
100                new ComponentAdapter() {
101                    @Override
102                    public void componentShown(ComponentEvent e) {
103                        Main.getLayerManager().addAndFireActiveLayerChangeListener(actSelectInCurrentLayerAction);
104                        Main.getLayerManager().addAndFireActiveLayerChangeListener(actZoomInCurrentLayerAction);
105                    }
106
107                    @Override
108                    public void componentHidden(ComponentEvent e) {
109                        // make sure the listener is unregistered when the panel becomes invisible
110                        Main.getLayerManager().removeActiveLayerChangeListener(actSelectInCurrentLayerAction);
111                        Main.getLayerManager().removeActiveLayerChangeListener(actZoomInCurrentLayerAction);
112                    }
113                }
114        );
115    }
116
117    protected JPanel buildContentPanel() {
118        JPanel pnl = new JPanel(new BorderLayout());
119        JTable tblContent = new JTable(
120                model,
121                new ChangesetContentTableColumnModel(),
122                model.getSelectionModel()
123        );
124        tblContent.addMouseListener(new PopupMenuLauncher(new ChangesetContentTablePopupMenu()));
125        pnl.add(new JScrollPane(tblContent), BorderLayout.CENTER);
126        return pnl;
127    }
128
129    protected JPanel buildActionButtonPanel() {
130        JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT));
131        JToolBar tb = new JToolBar(JToolBar.VERTICAL);
132        tb.setFloatable(false);
133
134        tb.add(actDownloadContentAction);
135        tb.addSeparator();
136        tb.add(actDownloadObjectAction);
137        tb.add(actShowHistory);
138        tb.addSeparator();
139        tb.add(actSelectInCurrentLayerAction);
140        tb.add(actZoomInCurrentLayerAction);
141
142        pnl.add(tb);
143        return pnl;
144    }
145
146    protected final void build() {
147        setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
148        setLayout(new BorderLayout());
149        buildModels();
150
151        add(pnlHeader, BorderLayout.NORTH);
152        add(buildActionButtonPanel(), BorderLayout.WEST);
153        add(buildContentPanel(), BorderLayout.CENTER);
154    }
155
156    /**
157     * Constructs a new {@code ChangesetContentPanel}.
158     */
159    public ChangesetContentPanel() {
160        build();
161    }
162
163    /**
164     * Replies the changeset content model
165     * @return The model
166     */
167    public ChangesetContentTableModel getModel() {
168        return model;
169    }
170
171    protected void setCurrentChangeset(Changeset cs) {
172        currentChangeset = cs;
173        if (cs == null) {
174            model.populate(null);
175        } else {
176            model.populate(cs.getContent());
177        }
178        actDownloadContentAction.initProperties();
179        pnlHeader.setChangeset(cs);
180    }
181
182    /* ---------------------------------------------------------------------------- */
183    /* interface PropertyChangeListener                                             */
184    /* ---------------------------------------------------------------------------- */
185    @Override
186    public void propertyChange(PropertyChangeEvent evt) {
187        if (!evt.getPropertyName().equals(ChangesetCacheManagerModel.CHANGESET_IN_DETAIL_VIEW_PROP))
188            return;
189        Changeset cs = (Changeset) evt.getNewValue();
190        setCurrentChangeset(cs);
191    }
192
193    private void alertNoPrimitivesTo(Collection<HistoryOsmPrimitive> primitives, String title, String helpTopic) {
194        HelpAwareOptionPane.showOptionDialog(
195                this,
196                trn("<html>The selected object is not available in the current<br>"
197                        + "edit layer ''{0}''.</html>",
198                        "<html>None of the selected objects is available in the current<br>"
199                        + "edit layer ''{0}''.</html>",
200                        primitives.size(),
201                        Main.getLayerManager().getEditLayer().getName()
202                ),
203                title, JOptionPane.WARNING_MESSAGE, helpTopic
204        );
205    }
206
207    class ChangesetContentTablePopupMenu extends JPopupMenu {
208        ChangesetContentTablePopupMenu() {
209            add(actDownloadContentAction);
210            add(new JSeparator());
211            add(actDownloadObjectAction);
212            add(actShowHistory);
213            add(new JSeparator());
214            add(actSelectInCurrentLayerAction);
215            add(actZoomInCurrentLayerAction);
216        }
217    }
218
219    class ShowHistoryAction extends AbstractAction implements ListSelectionListener {
220
221        private final class ShowHistoryTask implements Runnable {
222            private final Collection<HistoryOsmPrimitive> primitives;
223
224            private ShowHistoryTask(Collection<HistoryOsmPrimitive> primitives) {
225                this.primitives = primitives;
226            }
227
228            @Override
229            public void run() {
230                try {
231                    for (HistoryOsmPrimitive p : primitives) {
232                        final History h = HistoryDataSet.getInstance().getHistory(p.getPrimitiveId());
233                        if (h == null) {
234                            continue;
235                        }
236                        GuiHelper.runInEDT(() -> HistoryBrowserDialogManager.getInstance().show(h));
237                    }
238                } catch (final JosmRuntimeException | IllegalArgumentException | IllegalStateException e) {
239                    GuiHelper.runInEDT(() -> BugReportExceptionHandler.handleException(e));
240                }
241            }
242        }
243
244        ShowHistoryAction() {
245            putValue(NAME, tr("Show history"));
246            new ImageProvider("dialogs", "history").getResource().attachImageIcon(this);
247            putValue(SHORT_DESCRIPTION, tr("Download and show the history of the selected objects"));
248            updateEnabledState();
249        }
250
251        protected List<HistoryOsmPrimitive> filterPrimitivesWithUnloadedHistory(Collection<HistoryOsmPrimitive> primitives) {
252            List<HistoryOsmPrimitive> ret = new ArrayList<>(primitives.size());
253            for (HistoryOsmPrimitive p: primitives) {
254                if (HistoryDataSet.getInstance().getHistory(p.getPrimitiveId()) == null) {
255                    ret.add(p);
256                }
257            }
258            return ret;
259        }
260
261        public void showHistory(final Collection<HistoryOsmPrimitive> primitives) {
262
263            List<HistoryOsmPrimitive> toLoad = filterPrimitivesWithUnloadedHistory(primitives);
264            if (!toLoad.isEmpty()) {
265                HistoryLoadTask task = new HistoryLoadTask(ChangesetContentPanel.this);
266                for (HistoryOsmPrimitive p: toLoad) {
267                    task.add(p);
268                }
269                Main.worker.submit(task);
270            }
271
272            Main.worker.submit(new ShowHistoryTask(primitives));
273        }
274
275        protected final void updateEnabledState() {
276            setEnabled(model.hasSelectedPrimitives());
277        }
278
279        @Override
280        public void actionPerformed(ActionEvent arg0) {
281            Set<HistoryOsmPrimitive> selected = model.getSelectedPrimitives();
282            if (selected.isEmpty()) return;
283            showHistory(selected);
284        }
285
286        @Override
287        public void valueChanged(ListSelectionEvent e) {
288            updateEnabledState();
289        }
290    }
291
292    class DownloadObjectAction extends AbstractAction implements ListSelectionListener {
293
294        DownloadObjectAction() {
295            putValue(NAME, tr("Download objects"));
296            putValue(SMALL_ICON, ImageProvider.get("downloadprimitive"));
297            putValue(SHORT_DESCRIPTION, tr("Download the current version of the selected objects"));
298            updateEnabledState();
299        }
300
301        @Override
302        public void actionPerformed(ActionEvent arg0) {
303            final List<PrimitiveId> primitiveIds = model.getSelectedPrimitives().stream().map(HistoryOsmPrimitive::getPrimitiveId)
304                    .collect(Collectors.toList());
305            Main.worker.submit(new DownloadPrimitivesWithReferrersTask(false, primitiveIds, true, true, null, null));
306        }
307
308        protected final void updateEnabledState() {
309            setEnabled(model.hasSelectedPrimitives());
310        }
311
312        @Override
313        public void valueChanged(ListSelectionEvent e) {
314            updateEnabledState();
315        }
316    }
317
318    abstract class SelectionBasedAction extends AbstractAction implements ListSelectionListener, ActiveLayerChangeListener {
319
320        protected Set<OsmPrimitive> getTarget() {
321            if (!isEnabled()) {
322                return null;
323            }
324            OsmDataLayer layer = Main.getLayerManager().getEditLayer();
325            if (layer == null) {
326                return null;
327            }
328            Set<OsmPrimitive> target = new HashSet<>();
329            for (HistoryOsmPrimitive p : model.getSelectedPrimitives()) {
330                OsmPrimitive op = layer.data.getPrimitiveById(p.getPrimitiveId());
331                if (op != null) {
332                    target.add(op);
333                }
334            }
335            return target;
336        }
337
338        public final void updateEnabledState() {
339            setEnabled(Main.getLayerManager().getEditLayer() != null && model.hasSelectedPrimitives());
340        }
341
342        @Override
343        public void valueChanged(ListSelectionEvent e) {
344            updateEnabledState();
345        }
346
347        @Override
348        public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
349            updateEnabledState();
350        }
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 arg0) {
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            Main.getLayerManager().getEditLayer().data.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 arg0) {
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            Main.getLayerManager().getEditLayer().data.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.