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

Last change on this file since 14547 was 14547, checked in by GerdP, 5 years ago

see #17040 revert r14546 Sometimes tries to remove listener twice

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