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

Last change on this file was 17717, checked in by simon04, 5 years ago

see #14176 - Migrate Changeset to Instant

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