source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetDetailPanel.java @ 10490

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

fix #13003 - IllegalArgumentException: Attempted to remove listener that was not in list (patch by michael2402) - gsoc-core

  • Property svn:eol-style set to native
File size: 16.0 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.trc;
6
7import java.awt.BorderLayout;
8import java.awt.FlowLayout;
9import java.awt.GridBagConstraints;
10import java.awt.GridBagLayout;
11import java.awt.Insets;
12import java.awt.event.ActionEvent;
13import java.awt.event.ComponentAdapter;
14import java.awt.event.ComponentEvent;
15import java.beans.PropertyChangeEvent;
16import java.beans.PropertyChangeListener;
17import java.text.DateFormat;
18import java.util.Collections;
19import java.util.HashSet;
20import java.util.Set;
21
22import javax.swing.AbstractAction;
23import javax.swing.BorderFactory;
24import javax.swing.JLabel;
25import javax.swing.JOptionPane;
26import javax.swing.JPanel;
27import javax.swing.JToolBar;
28
29import org.openstreetmap.josm.Main;
30import org.openstreetmap.josm.actions.AutoScaleAction;
31import org.openstreetmap.josm.actions.downloadtasks.ChangesetHeaderDownloadTask;
32import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;
33import org.openstreetmap.josm.data.osm.Changeset;
34import org.openstreetmap.josm.data.osm.ChangesetCache;
35import org.openstreetmap.josm.data.osm.OsmPrimitive;
36import org.openstreetmap.josm.gui.HelpAwareOptionPane;
37import org.openstreetmap.josm.gui.help.HelpUtil;
38import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
39import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
40import org.openstreetmap.josm.gui.layer.OsmDataLayer;
41import org.openstreetmap.josm.gui.widgets.JosmTextArea;
42import org.openstreetmap.josm.gui.widgets.JosmTextField;
43import org.openstreetmap.josm.io.OnlineResource;
44import org.openstreetmap.josm.tools.ImageProvider;
45import org.openstreetmap.josm.tools.date.DateUtils;
46
47/**
48 * This panel displays the properties of the currently selected changeset in the
49 * {@link ChangesetCacheManager}.
50 *
51 */
52public class ChangesetDetailPanel extends JPanel implements PropertyChangeListener, ChangesetAware {
53
54    // CHECKSTYLE.OFF: SingleSpaceSeparator
55    private final JosmTextField tfID        = new JosmTextField(10);
56    private final JosmTextArea  taComment   = new JosmTextArea(5, 40);
57    private final JosmTextField tfOpen      = new JosmTextField(10);
58    private final JosmTextField tfUser      = new JosmTextField("");
59    private final JosmTextField tfCreatedOn = new JosmTextField(20);
60    private final JosmTextField tfClosedOn  = new JosmTextField(20);
61
62    private final DownloadChangesetContentAction actDownloadChangesetContent = new DownloadChangesetContentAction(this);
63    private final UpdateChangesetAction          actUpdateChangesets         = new UpdateChangesetAction();
64    private final RemoveFromCacheAction          actRemoveFromCache          = new RemoveFromCacheAction();
65    private final SelectInCurrentLayerAction     actSelectInCurrentLayer     = new SelectInCurrentLayerAction();
66    private final ZoomInCurrentLayerAction       actZoomInCurrentLayerAction = new ZoomInCurrentLayerAction();
67    // CHECKSTYLE.ON: SingleSpaceSeparator
68
69    private transient Changeset currentChangeset;
70
71    protected JPanel buildActionButtonPanel() {
72        JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT));
73
74        JToolBar tb = new JToolBar(JToolBar.VERTICAL);
75        tb.setFloatable(false);
76
77        // -- remove from cache action
78        tb.add(actRemoveFromCache);
79        actRemoveFromCache.initProperties(currentChangeset);
80
81        // -- changeset update
82        tb.add(actUpdateChangesets);
83        actUpdateChangesets.initProperties(currentChangeset);
84
85        // -- changeset content download
86        tb.add(actDownloadChangesetContent);
87        actDownloadChangesetContent.initProperties();
88
89        tb.add(actSelectInCurrentLayer);
90        Main.getLayerManager().addActiveLayerChangeListener(actSelectInCurrentLayer);
91
92        tb.add(actZoomInCurrentLayerAction);
93        Main.getLayerManager().addActiveLayerChangeListener(actZoomInCurrentLayerAction);
94
95        addComponentListener(
96                new ComponentAdapter() {
97                    @Override
98                    public void componentShown(ComponentEvent e) {
99                        Main.getLayerManager().addAndFireActiveLayerChangeListener(actSelectInCurrentLayer);
100                        Main.getLayerManager().addAndFireActiveLayerChangeListener(actZoomInCurrentLayerAction);
101                    }
102
103                    @Override
104                    public void componentHidden(ComponentEvent e) {
105                        // make sure the listener is unregistered when the panel becomes invisible
106                        Main.getLayerManager().removeActiveLayerChangeListener(actSelectInCurrentLayer);
107                        Main.getLayerManager().removeActiveLayerChangeListener(actZoomInCurrentLayerAction);
108                    }
109                }
110        );
111
112        pnl.add(tb);
113        return pnl;
114    }
115
116    protected JPanel buildDetailViewPanel() {
117        JPanel pnl = new JPanel(new GridBagLayout());
118
119        GridBagConstraints gc = new GridBagConstraints();
120        gc.anchor = GridBagConstraints.FIRST_LINE_START;
121        gc.insets = new Insets(0, 0, 2, 3);
122
123        //-- id
124        gc.fill = GridBagConstraints.HORIZONTAL;
125        gc.weightx = 0.0;
126        pnl.add(new JLabel(tr("ID:")), gc);
127
128        gc.fill = GridBagConstraints.HORIZONTAL;
129        gc.weightx = 0.0;
130        gc.gridx = 1;
131        pnl.add(tfID, gc);
132        tfID.setEditable(false);
133
134        //-- comment
135        gc.gridx = 0;
136        gc.gridy = 1;
137        gc.fill = GridBagConstraints.HORIZONTAL;
138        gc.weightx = 0.0;
139        pnl.add(new JLabel(tr("Comment:")), gc);
140
141        gc.fill = GridBagConstraints.BOTH;
142        gc.weightx = 1.0;
143        gc.weighty = 1.0;
144        gc.gridx = 1;
145        pnl.add(taComment, gc);
146        taComment.setEditable(false);
147
148        //-- Open/Closed
149        gc.gridx = 0;
150        gc.gridy = 2;
151        gc.fill = GridBagConstraints.HORIZONTAL;
152        gc.weightx = 0.0;
153        gc.weighty = 0.0;
154        pnl.add(new JLabel(tr("Open/Closed:")), gc);
155
156        gc.fill = GridBagConstraints.HORIZONTAL;
157        gc.gridx = 1;
158        pnl.add(tfOpen, gc);
159        tfOpen.setEditable(false);
160
161        //-- Created by:
162        gc.gridx = 0;
163        gc.gridy = 3;
164        gc.fill = GridBagConstraints.HORIZONTAL;
165        gc.weightx = 0.0;
166        pnl.add(new JLabel(tr("Created by:")), gc);
167
168        gc.fill = GridBagConstraints.HORIZONTAL;
169        gc.weightx = 1.0;
170        gc.gridx = 1;
171        pnl.add(tfUser, gc);
172        tfUser.setEditable(false);
173
174        //-- Created On:
175        gc.gridx = 0;
176        gc.gridy = 4;
177        gc.fill = GridBagConstraints.HORIZONTAL;
178        gc.weightx = 0.0;
179        pnl.add(new JLabel(tr("Created on:")), gc);
180
181        gc.fill = GridBagConstraints.HORIZONTAL;
182        gc.gridx = 1;
183        pnl.add(tfCreatedOn, gc);
184        tfCreatedOn.setEditable(false);
185
186        //-- Closed On:
187        gc.gridx = 0;
188        gc.gridy = 5;
189        gc.fill = GridBagConstraints.HORIZONTAL;
190        gc.weightx = 0.0;
191        pnl.add(new JLabel(tr("Closed on:")), gc);
192
193        gc.fill = GridBagConstraints.HORIZONTAL;
194        gc.gridx = 1;
195        pnl.add(tfClosedOn, gc);
196        tfClosedOn.setEditable(false);
197
198        return pnl;
199    }
200
201    protected final void build() {
202        setLayout(new BorderLayout());
203        setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
204        add(buildDetailViewPanel(), BorderLayout.CENTER);
205        add(buildActionButtonPanel(), BorderLayout.WEST);
206    }
207
208    protected void clearView() {
209        tfID.setText("");
210        taComment.setText("");
211        tfOpen.setText("");
212        tfUser.setText("");
213        tfCreatedOn.setText("");
214        tfClosedOn.setText("");
215    }
216
217    protected void updateView(Changeset cs) {
218        String msg;
219        if (cs == null) return;
220        tfID.setText(Integer.toString(cs.getId()));
221        String comment = cs.get("comment");
222        taComment.setText(comment == null ? "" : comment);
223
224        if (cs.isOpen()) {
225            msg = trc("changeset.state", "Open");
226        } else {
227            msg = trc("changeset.state", "Closed");
228        }
229        tfOpen.setText(msg);
230
231        if (cs.getUser() == null) {
232            msg = tr("anonymous");
233        } else {
234            msg = cs.getUser().getName();
235        }
236        tfUser.setText(msg);
237        DateFormat sdf = DateUtils.getDateTimeFormat(DateFormat.SHORT, DateFormat.SHORT);
238
239        tfCreatedOn.setText(cs.getCreatedAt() == null ? "" : sdf.format(cs.getCreatedAt()));
240        tfClosedOn.setText(cs.getClosedAt() == null ? "" : sdf.format(cs.getClosedAt()));
241    }
242
243    /**
244     * Constructs a new {@code ChangesetDetailPanel}.
245     */
246    public ChangesetDetailPanel() {
247        build();
248    }
249
250    protected void setCurrentChangeset(Changeset cs) {
251        currentChangeset = cs;
252        if (cs == null) {
253            clearView();
254        } else {
255            updateView(cs);
256        }
257        actDownloadChangesetContent.initProperties();
258        actUpdateChangesets.initProperties(currentChangeset);
259        actRemoveFromCache.initProperties(currentChangeset);
260        actSelectInCurrentLayer.updateEnabledState();
261        actZoomInCurrentLayerAction.updateEnabledState();
262    }
263
264    /* ---------------------------------------------------------------------------- */
265    /* interface PropertyChangeListener                                             */
266    /* ---------------------------------------------------------------------------- */
267    @Override
268    public void propertyChange(PropertyChangeEvent evt) {
269        if (!evt.getPropertyName().equals(ChangesetCacheManagerModel.CHANGESET_IN_DETAIL_VIEW_PROP))
270            return;
271        setCurrentChangeset((Changeset) evt.getNewValue());
272    }
273
274    /**
275     * The action for removing the currently selected changeset from the changeset cache
276     */
277    class RemoveFromCacheAction extends AbstractAction {
278        RemoveFromCacheAction() {
279            putValue(NAME, tr("Remove from cache"));
280            new ImageProvider("dialogs", "delete").getResource().attachImageIcon(this);
281            putValue(SHORT_DESCRIPTION, tr("Remove the changeset in the detail view panel from the local cache"));
282        }
283
284        @Override
285        public void actionPerformed(ActionEvent evt) {
286            if (currentChangeset == null)
287                return;
288            ChangesetCache.getInstance().remove(currentChangeset);
289        }
290
291        public void initProperties(Changeset cs) {
292            setEnabled(cs != null);
293        }
294    }
295
296    /**
297     * Updates the current changeset from the OSM server
298     *
299     */
300    class UpdateChangesetAction extends AbstractAction {
301        UpdateChangesetAction() {
302            putValue(NAME, tr("Update changeset"));
303            new ImageProvider("dialogs/changeset", "updatechangesetcontent").getResource().attachImageIcon(this);
304            putValue(SHORT_DESCRIPTION, tr("Update the changeset from the OSM server"));
305        }
306
307        @Override
308        public void actionPerformed(ActionEvent evt) {
309            if (currentChangeset == null)
310                return;
311            ChangesetHeaderDownloadTask task = new ChangesetHeaderDownloadTask(
312                    ChangesetDetailPanel.this,
313                    Collections.singleton(currentChangeset.getId())
314            );
315            Main.worker.submit(new PostDownloadHandler(task, task.download()));
316        }
317
318        public void initProperties(Changeset cs) {
319            setEnabled(cs != null && !Main.isOffline(OnlineResource.OSM_API));
320        }
321    }
322
323    /**
324     * Selects the primitives in the content of this changeset in the current data layer.
325     *
326     */
327    class SelectInCurrentLayerAction extends AbstractAction implements ActiveLayerChangeListener {
328
329        SelectInCurrentLayerAction() {
330            putValue(NAME, tr("Select in layer"));
331            new ImageProvider("dialogs", "select").getResource().attachImageIcon(this);
332            putValue(SHORT_DESCRIPTION, tr("Select the primitives in the content of this changeset in the current data layer"));
333            updateEnabledState();
334        }
335
336        protected void alertNoPrimitivesToSelect() {
337            HelpAwareOptionPane.showOptionDialog(
338                    ChangesetDetailPanel.this,
339                    tr("<html>None of the objects in the content of changeset {0} is available in the current<br>"
340                            + "edit layer ''{1}''.</html>",
341                            currentChangeset.getId(),
342                            Main.getLayerManager().getEditLayer().getName()
343                    ),
344                    tr("Nothing to select"),
345                    JOptionPane.WARNING_MESSAGE,
346                    HelpUtil.ht("/Dialog/ChangesetCacheManager#NothingToSelectInLayer")
347            );
348        }
349
350        @Override
351        public void actionPerformed(ActionEvent arg0) {
352            if (!isEnabled())
353                return;
354            OsmDataLayer layer = Main.getLayerManager().getEditLayer();
355            if (layer == null) {
356                return;
357            }
358            Set<OsmPrimitive> target = new HashSet<>();
359            for (OsmPrimitive p: layer.data.allPrimitives()) {
360                if (p.isUsable() && p.getChangesetId() == currentChangeset.getId()) {
361                    target.add(p);
362                }
363            }
364            if (target.isEmpty()) {
365                alertNoPrimitivesToSelect();
366                return;
367            }
368            layer.data.setSelected(target);
369        }
370
371        public void updateEnabledState() {
372            setEnabled(Main.getLayerManager().getEditLayer() != null && currentChangeset != null);
373        }
374
375        @Override
376        public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
377            updateEnabledState();
378        }
379    }
380
381    /**
382     * Zooms to the primitives in the content of this changeset in the current
383     * data layer.
384     *
385     */
386    class ZoomInCurrentLayerAction extends AbstractAction implements ActiveLayerChangeListener {
387
388        ZoomInCurrentLayerAction() {
389            putValue(NAME, tr("Zoom to in layer"));
390            new ImageProvider("dialogs/autoscale", "selection").getResource().attachImageIcon(this);
391            putValue(SHORT_DESCRIPTION, tr("Zoom to the objects in the content of this changeset in the current data layer"));
392            updateEnabledState();
393        }
394
395        protected void alertNoPrimitivesToZoomTo() {
396            HelpAwareOptionPane.showOptionDialog(
397                    ChangesetDetailPanel.this,
398                    tr("<html>None of the objects in the content of changeset {0} is available in the current<br>"
399                            + "edit layer ''{1}''.</html>",
400                            currentChangeset.getId(),
401                            Main.getLayerManager().getEditLayer().getName()
402                    ),
403                    tr("Nothing to zoom to"),
404                    JOptionPane.WARNING_MESSAGE,
405                    HelpUtil.ht("/Dialog/ChangesetCacheManager#NothingToZoomTo")
406            );
407        }
408
409        @Override
410        public void actionPerformed(ActionEvent arg0) {
411            if (!isEnabled())
412                return;
413            OsmDataLayer layer = Main.getLayerManager().getEditLayer();
414            if (layer == null) {
415                return;
416            }
417            Set<OsmPrimitive> target = new HashSet<>();
418            for (OsmPrimitive p: layer.data.allPrimitives()) {
419                if (p.isUsable() && p.getChangesetId() == currentChangeset.getId()) {
420                    target.add(p);
421                }
422            }
423            if (target.isEmpty()) {
424                alertNoPrimitivesToZoomTo();
425                return;
426            }
427            layer.data.setSelected(target);
428            AutoScaleAction.zoomToSelection();
429        }
430
431        public void updateEnabledState() {
432            setEnabled(Main.getLayerManager().getEditLayer() != null && currentChangeset != null);
433        }
434
435        @Override
436        public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
437            updateEnabledState();
438        }
439    }
440
441    @Override
442    public Changeset getCurrentChangeset() {
443        return currentChangeset;
444    }
445}
Note: See TracBrowser for help on using the repository browser.