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

Last change on this file was 17371, checked in by GerdP, 6 years ago

fix #18533: changeset manager: tags tab: width of column (almost) not changeable

  • use ReadOnlyTableModel as in PropertiesDialog to allow resizing of column width
  • don't allow reordering of column headers in Changeset dialogs (because content doesn't match header if allowed)
  • Property svn:eol-style set to native
File size: 15.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.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.Collection;
15import java.util.Collections;
16import java.util.List;
17import java.util.Objects;
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.JComponent;
26import javax.swing.JOptionPane;
27import javax.swing.JPanel;
28import javax.swing.JPopupMenu;
29import javax.swing.JScrollPane;
30import javax.swing.JSeparator;
31import javax.swing.JTable;
32import javax.swing.JToolBar;
33import javax.swing.SwingConstants;
34import javax.swing.event.ListSelectionEvent;
35import javax.swing.event.ListSelectionListener;
36
37import org.openstreetmap.josm.actions.AbstractShowHistoryAction;
38import org.openstreetmap.josm.actions.AutoScaleAction;
39import org.openstreetmap.josm.actions.HistoryInfoAction;
40import org.openstreetmap.josm.actions.downloadtasks.ChangesetContentDownloadTask;
41import org.openstreetmap.josm.data.osm.Changeset;
42import org.openstreetmap.josm.data.osm.DataSet;
43import org.openstreetmap.josm.data.osm.OsmPrimitive;
44import org.openstreetmap.josm.data.osm.PrimitiveId;
45import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
46import org.openstreetmap.josm.gui.HelpAwareOptionPane;
47import org.openstreetmap.josm.gui.MainApplication;
48import org.openstreetmap.josm.gui.help.HelpUtil;
49import org.openstreetmap.josm.gui.history.HistoryBrowserDialogManager;
50import org.openstreetmap.josm.gui.io.DownloadPrimitivesWithReferrersTask;
51import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
52import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
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.Utils;
57
58/**
59 * The panel which displays the content of a changeset in a scrollable table.
60 *
61 * It listens to property change events for {@link ChangesetCacheManagerModel#CHANGESET_IN_DETAIL_VIEW_PROP}
62 * and updates its view accordingly.
63 * @since 2689
64 */
65public class ChangesetContentPanel extends JPanel implements PropertyChangeListener, ChangesetAware {
66 private JTable tblContent;
67 private ChangesetContentTableModel model;
68 private transient Changeset currentChangeset;
69
70 private DownloadChangesetContentAction actDownloadContentAction;
71 private ShowHistoryAction actShowHistory;
72 private SelectInCurrentLayerAction actSelectInCurrentLayerAction;
73 private ZoomInCurrentLayerAction actZoomInCurrentLayerAction;
74
75 private final HeaderPanel pnlHeader = new HeaderPanel();
76 protected DownloadObjectAction actDownloadObjectAction;
77
78 protected void buildModels() {
79 DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
80 model = new ChangesetContentTableModel(selectionModel);
81 actDownloadContentAction = new DownloadChangesetContentAction(this);
82 actDownloadContentAction.initProperties();
83
84 actDownloadObjectAction = new DownloadObjectAction();
85 model.getSelectionModel().addListSelectionListener(actDownloadObjectAction);
86
87 actShowHistory = new ShowHistoryAction();
88 model.getSelectionModel().addListSelectionListener(actShowHistory);
89
90 actSelectInCurrentLayerAction = new SelectInCurrentLayerAction();
91 model.getSelectionModel().addListSelectionListener(actSelectInCurrentLayerAction);
92 MainApplication.getLayerManager().addActiveLayerChangeListener(actSelectInCurrentLayerAction);
93
94 actZoomInCurrentLayerAction = new ZoomInCurrentLayerAction();
95 model.getSelectionModel().addListSelectionListener(actZoomInCurrentLayerAction);
96 MainApplication.getLayerManager().addActiveLayerChangeListener(actZoomInCurrentLayerAction);
97
98 addComponentListener(
99 new ComponentAdapter() {
100 @Override
101 public void componentShown(ComponentEvent e) {
102 MainApplication.getLayerManager().addAndFireActiveLayerChangeListener(actSelectInCurrentLayerAction);
103 MainApplication.getLayerManager().addAndFireActiveLayerChangeListener(actZoomInCurrentLayerAction);
104 }
105
106 @Override
107 public void componentHidden(ComponentEvent e) {
108 // make sure the listener is unregistered when the panel becomes invisible
109 MainApplication.getLayerManager().removeActiveLayerChangeListener(actSelectInCurrentLayerAction);
110 MainApplication.getLayerManager().removeActiveLayerChangeListener(actZoomInCurrentLayerAction);
111 }
112 }
113 );
114 }
115
116 protected JPanel buildContentPanel() {
117 JPanel pnl = new JPanel(new BorderLayout());
118 tblContent = new JTable(
119 model,
120 new ChangesetContentTableColumnModel(),
121 model.getSelectionModel()
122 );
123 tblContent.setRowSorter(new ChangesetContentTableRowSorter(model));
124 tblContent.addMouseListener(new PopupMenuLauncher(new ChangesetContentTablePopupMenu()));
125 HistoryInfoAction historyAction = MainApplication.getMenu().historyinfo;
126 tblContent.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(historyAction.getShortcut().getKeyStroke(), "historyAction");
127 tblContent.getActionMap().put("historyAction", historyAction);
128 tblContent.getTableHeader().setReorderingAllowed(false);
129 pnl.add(new JScrollPane(tblContent), BorderLayout.CENTER);
130 return pnl;
131 }
132
133 protected JPanel buildActionButtonPanel() {
134 JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT));
135 JToolBar tb = new JToolBar(SwingConstants.VERTICAL);
136 tb.setFloatable(false);
137
138 tb.add(actDownloadContentAction);
139 tb.addSeparator();
140 tb.add(actDownloadObjectAction);
141 tb.add(actShowHistory);
142 tb.addSeparator();
143 tb.add(actSelectInCurrentLayerAction);
144 tb.add(actZoomInCurrentLayerAction);
145
146 pnl.add(tb);
147 return pnl;
148 }
149
150 protected final void build() {
151 setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
152 setLayout(new BorderLayout());
153 buildModels();
154
155 add(pnlHeader, BorderLayout.NORTH);
156 add(buildActionButtonPanel(), BorderLayout.WEST);
157 add(buildContentPanel(), BorderLayout.CENTER);
158 }
159
160 /**
161 * Constructs a new {@code ChangesetContentPanel}.
162 */
163 public ChangesetContentPanel() {
164 build();
165 }
166
167 /**
168 * Replies the changeset content model
169 * @return The model
170 */
171 public ChangesetContentTableModel getModel() {
172 return model;
173 }
174
175 protected void setCurrentChangeset(Changeset cs) {
176 currentChangeset = cs;
177 if (cs == null) {
178 model.populate(null);
179 } else {
180 model.populate(cs.getContent());
181 }
182 actDownloadContentAction.initProperties();
183 pnlHeader.setChangeset(cs);
184 }
185
186 /* ---------------------------------------------------------------------------- */
187 /* interface PropertyChangeListener */
188 /* ---------------------------------------------------------------------------- */
189 @Override
190 public void propertyChange(PropertyChangeEvent evt) {
191 if (!evt.getPropertyName().equals(ChangesetCacheManagerModel.CHANGESET_IN_DETAIL_VIEW_PROP))
192 return;
193 Changeset cs = (Changeset) evt.getNewValue();
194 setCurrentChangeset(cs);
195 }
196
197 private void alertNoPrimitivesTo(Collection<HistoryOsmPrimitive> primitives, String title, String helpTopic) {
198 HelpAwareOptionPane.showOptionDialog(
199 this,
200 trn("<html>The selected object is not available in the current<br>"
201 + "edit layer ''{0}''.</html>",
202 "<html>None of the selected objects is available in the current<br>"
203 + "edit layer ''{0}''.</html>",
204 primitives.size(),
205 Utils.escapeReservedCharactersHTML(MainApplication.getLayerManager().getEditLayer().getName())
206 ),
207 title, JOptionPane.WARNING_MESSAGE, helpTopic
208 );
209 }
210
211 private Set<HistoryOsmPrimitive> getSelectedPrimitives() {
212 return model.getSelectedPrimitives(tblContent);
213 }
214
215 class ChangesetContentTablePopupMenu extends JPopupMenu {
216 ChangesetContentTablePopupMenu() {
217 add(actDownloadContentAction);
218 add(new JSeparator());
219 add(actDownloadObjectAction);
220 add(actShowHistory);
221 add(new JSeparator());
222 add(actSelectInCurrentLayerAction);
223 add(actZoomInCurrentLayerAction);
224 }
225 }
226
227 class ShowHistoryAction extends AbstractShowHistoryAction implements ListSelectionListener {
228
229 ShowHistoryAction() {
230 super();
231 updateEnabledState();
232 }
233
234 protected final void updateEnabledState() {
235 setEnabled(model.hasSelectedPrimitives());
236 }
237
238 @Override
239 public void actionPerformed(ActionEvent e) {
240 Set<HistoryOsmPrimitive> selected = getSelectedPrimitives();
241 if (selected.isEmpty()) return;
242 HistoryBrowserDialogManager.getInstance().showHistory(selected);
243 }
244
245 @Override
246 public void valueChanged(ListSelectionEvent e) {
247 updateEnabledState();
248 }
249 }
250
251 class DownloadObjectAction extends AbstractAction implements ListSelectionListener {
252
253 DownloadObjectAction() {
254 putValue(NAME, tr("Download objects"));
255 new ImageProvider("downloadprimitive").getResource().attachImageIcon(this, true);
256 putValue(SHORT_DESCRIPTION, tr("Download the current version of the selected objects"));
257 updateEnabledState();
258 }
259
260 @Override
261 public void actionPerformed(ActionEvent e) {
262 final List<PrimitiveId> primitiveIds = getSelectedPrimitives().stream().map(HistoryOsmPrimitive::getPrimitiveId)
263 .collect(Collectors.toList());
264 MainApplication.worker.submit(new DownloadPrimitivesWithReferrersTask(false, primitiveIds, true, true, null, null));
265 }
266
267 protected final void updateEnabledState() {
268 setEnabled(model.hasSelectedPrimitives());
269 }
270
271 @Override
272 public void valueChanged(ListSelectionEvent e) {
273 updateEnabledState();
274 }
275 }
276
277 abstract class SelectionBasedAction extends AbstractAction implements ListSelectionListener, ActiveLayerChangeListener {
278
279 protected Set<OsmPrimitive> getTarget() {
280 DataSet ds = MainApplication.getLayerManager().getActiveDataSet();
281 if (isEnabled() && ds != null) {
282 return getSelectedPrimitives().stream()
283 .map(p -> ds.getPrimitiveById(p.getPrimitiveId())).filter(Objects::nonNull).collect(Collectors.toSet());
284 }
285 return Collections.emptySet();
286 }
287
288 public final void updateEnabledState() {
289 setEnabled(MainApplication.getLayerManager().getActiveDataSet() != null && model.hasSelectedPrimitives());
290 }
291
292 @Override
293 public void valueChanged(ListSelectionEvent e) {
294 updateEnabledState();
295 }
296
297 @Override
298 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
299 updateEnabledState();
300 }
301 }
302
303 class SelectInCurrentLayerAction extends SelectionBasedAction {
304
305 SelectInCurrentLayerAction() {
306 putValue(NAME, tr("Select in layer"));
307 new ImageProvider("dialogs", "select").getResource().attachImageIcon(this);
308 putValue(SHORT_DESCRIPTION, tr("Select the corresponding primitives in the current data layer"));
309 updateEnabledState();
310 }
311
312 @Override
313 public void actionPerformed(ActionEvent e) {
314 final Set<OsmPrimitive> target = getTarget();
315 if (target.isEmpty()) {
316 alertNoPrimitivesTo(getSelectedPrimitives(), tr("Nothing to select"),
317 HelpUtil.ht("/Dialog/ChangesetCacheManager#NothingToSelectInLayer"));
318 return;
319 }
320 MainApplication.getLayerManager().getActiveDataSet().setSelected(target);
321 }
322 }
323
324 class ZoomInCurrentLayerAction extends SelectionBasedAction {
325
326 ZoomInCurrentLayerAction() {
327 putValue(NAME, tr("Zoom to in layer"));
328 new ImageProvider("dialogs/autoscale", "selection").getResource().attachImageIcon(this);
329 putValue(SHORT_DESCRIPTION, tr("Zoom to the corresponding objects in the current data layer"));
330 updateEnabledState();
331 }
332
333 @Override
334 public void actionPerformed(ActionEvent e) {
335 final Set<OsmPrimitive> target = getTarget();
336 if (target.isEmpty()) {
337 alertNoPrimitivesTo(getSelectedPrimitives(), tr("Nothing to zoom to"),
338 HelpUtil.ht("/Dialog/ChangesetCacheManager#NothingToZoomTo"));
339 return;
340 }
341 MainApplication.getLayerManager().getActiveDataSet().setSelected(target);
342 AutoScaleAction.zoomToSelection();
343 }
344 }
345
346 private static class HeaderPanel extends JPanel {
347
348 private transient Changeset current;
349
350 HeaderPanel() {
351 build();
352 }
353
354 protected final void build() {
355 setLayout(new FlowLayout(FlowLayout.LEFT));
356 add(new JMultilineLabel(tr("The content of this changeset is not downloaded yet.")));
357 add(new JButton(new DownloadAction()));
358
359 }
360
361 public void setChangeset(Changeset cs) {
362 setVisible(cs != null && cs.getContent() == null);
363 this.current = cs;
364 }
365
366 private class DownloadAction extends AbstractAction {
367 DownloadAction() {
368 putValue(NAME, tr("Download now"));
369 putValue(SHORT_DESCRIPTION, tr("Download the changeset content"));
370 new ImageProvider("dialogs/changeset", "downloadchangesetcontent").getResource().attachImageIcon(this);
371 }
372
373 @Override
374 public void actionPerformed(ActionEvent evt) {
375 if (current == null) return;
376 ChangesetContentDownloadTask task = new ChangesetContentDownloadTask(HeaderPanel.this, current.getId());
377 ChangesetCacheManager.getInstance().runDownloadTask(task);
378 }
379 }
380 }
381
382 @Override
383 public Changeset getCurrentChangeset() {
384 return currentChangeset;
385 }
386}
Note: See TracBrowser for help on using the repository browser.