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

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

code cleanup / robustness in edit layer handling

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