source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/ChangesetDialog.java@ 2657

Last change on this file since 2657 was 2655, checked in by jttt, 14 years ago

Fixed #4161 Major slowdown in recent versions, used correct pattern for listeners realized using CopyOnWriteArrayList

File size: 17.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.BorderLayout;
7import java.awt.FlowLayout;
8import java.awt.event.ActionEvent;
9import java.awt.event.ItemEvent;
10import java.awt.event.ItemListener;
11import java.awt.event.MouseAdapter;
12import java.awt.event.MouseEvent;
13import java.util.HashSet;
14import java.util.List;
15import java.util.Set;
16
17import javax.swing.AbstractAction;
18import javax.swing.DefaultListSelectionModel;
19import javax.swing.JCheckBox;
20import javax.swing.JList;
21import javax.swing.JPanel;
22import javax.swing.JPopupMenu;
23import javax.swing.JScrollPane;
24import javax.swing.JToolBar;
25import javax.swing.ListSelectionModel;
26import javax.swing.SwingUtilities;
27import javax.swing.event.ListSelectionEvent;
28import javax.swing.event.ListSelectionListener;
29
30import org.openstreetmap.josm.Main;
31import org.openstreetmap.josm.actions.AbstractInfoAction;
32import org.openstreetmap.josm.data.osm.Changeset;
33import org.openstreetmap.josm.data.osm.ChangesetCache;
34import org.openstreetmap.josm.data.osm.DataSet;
35import org.openstreetmap.josm.data.osm.OsmPrimitive;
36import org.openstreetmap.josm.data.osm.event.DatasetEventManager;
37import org.openstreetmap.josm.gui.MapFrame;
38import org.openstreetmap.josm.gui.MapView;
39import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetInSelectionListModel;
40import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetListCellRenderer;
41import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetListModel;
42import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetsInActiveDataLayerListModel;
43import org.openstreetmap.josm.gui.dialogs.changeset.DownloadChangesetsTask;
44import org.openstreetmap.josm.gui.help.HelpUtil;
45import org.openstreetmap.josm.gui.io.CloseChangesetTask;
46import org.openstreetmap.josm.tools.ImageProvider;
47import org.openstreetmap.josm.tools.OpenBrowser;
48
49/**
50 * ChangesetDialog is a toggle dialog which displays the current list of changesets.
51 * It either displays
52 * <ul>
53 * <li>the list of changesets the currently selected objects are assigned to</li>
54 * <li>the list of changesets objects in the current data layer are assigend to</li>
55 * </ul>
56 *
57 * The dialog offers actions to download and to close changesets. It can also launch an external
58 * browser with information about a changeset. Furthermore, it can select all objects in
59 * the current data layer being assigned to a specific changeset.
60 *
61 */
62public class ChangesetDialog extends ToggleDialog{
63 //static private final Logger logger = Logger.getLogger(ChangesetDialog.class.getName());
64
65 private ChangesetInSelectionListModel inSelectionModel;
66 private ChangesetsInActiveDataLayerListModel inActiveDataLayerModel;
67 private JList lstInSelection;
68 private JList lstInActiveDataLayer;
69 private JCheckBox cbInSelectionOnly;
70 private JPanel pnlList;
71
72 // the actions
73 private SelectObjectsAction selectObjectsAction;
74 private ReadChangesetsAction readChangesetAction;
75 private ShowChangesetInfoAction showChangsetInfoAction;
76 private CloseOpenChangesetsAction closeChangesetAction;
77
78
79 protected void buildChangesetsLists() {
80 DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
81 inSelectionModel = new ChangesetInSelectionListModel(selectionModel);
82
83 lstInSelection = new JList(inSelectionModel);
84 lstInSelection.setSelectionModel(selectionModel);
85 lstInSelection.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
86 lstInSelection.setCellRenderer(new ChangesetListCellRenderer());
87
88 selectionModel = new DefaultListSelectionModel();
89 inActiveDataLayerModel = new ChangesetsInActiveDataLayerListModel(selectionModel);
90 lstInActiveDataLayer = new JList(inActiveDataLayerModel);
91 lstInActiveDataLayer.setSelectionModel(selectionModel);
92 lstInActiveDataLayer.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
93 lstInActiveDataLayer.setCellRenderer(new ChangesetListCellRenderer());
94
95 ChangesetCache.getInstance().addChangesetCacheListener(inSelectionModel);
96 MapView.addLayerChangeListener(inSelectionModel);
97 DataSet.selListeners.add(inSelectionModel);
98
99 ChangesetCache.getInstance().addChangesetCacheListener(inActiveDataLayerModel);
100
101 DblClickHandler dblClickHandler = new DblClickHandler();
102 lstInSelection.addMouseListener(dblClickHandler);
103 lstInActiveDataLayer.addMouseListener(dblClickHandler);
104
105 PopupMenuLauncher popupMenuLauncher = new PopupMenuLauncher();
106 lstInSelection.addMouseListener(popupMenuLauncher);
107 lstInActiveDataLayer.addMouseListener(popupMenuLauncher);
108 }
109
110 @Override
111 public void tearDown() {
112 ChangesetCache.getInstance().removeChangesetCacheListener(inActiveDataLayerModel);
113 MapView.removeLayerChangeListener(inSelectionModel);
114 DataSet.selListeners.remove(inSelectionModel);
115 }
116
117 @Override
118 public void showNotify() {
119 DatasetEventManager.getInstance().addDatasetListener(inActiveDataLayerModel, true);
120 }
121
122 @Override
123 public void hideNotify() {
124 DatasetEventManager.getInstance().removeDatasetListener(inActiveDataLayerModel);
125 }
126
127 protected JPanel buildFilterPanel() {
128 JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT));
129 pnl.setBorder(null);
130 pnl.add(cbInSelectionOnly = new JCheckBox(tr("For selected objects only")));
131 cbInSelectionOnly.setToolTipText(tr("<html>Select to show changesets for the currently selected objects only.<br>"
132 + "Unselect to show all changesets for objects in the current data layer.</html>"));
133 cbInSelectionOnly.setSelected(Main.pref.getBoolean("changeset-dialog.for-selected-objects-only", false));
134 return pnl;
135 }
136
137 protected JPanel buildListPanel() {
138 buildChangesetsLists();
139 JPanel pnl = new JPanel(new BorderLayout());
140 if (cbInSelectionOnly.isSelected()) {
141 pnl.add(new JScrollPane(lstInSelection));
142 } else {
143 pnl.add(new JScrollPane(lstInActiveDataLayer));
144 }
145 return pnl;
146 }
147
148 protected JPanel buildButtonPanel() {
149 JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT));
150
151 JToolBar tp = new JToolBar(JToolBar.HORIZONTAL);
152 tp.setFloatable(false);
153
154 // -- select objects action
155 selectObjectsAction = new SelectObjectsAction();
156 tp.add(selectObjectsAction);
157 cbInSelectionOnly.addItemListener(selectObjectsAction);
158 lstInActiveDataLayer.getSelectionModel().addListSelectionListener(selectObjectsAction);
159 lstInSelection.getSelectionModel().addListSelectionListener(selectObjectsAction);
160
161 // -- read changesets action
162 readChangesetAction = new ReadChangesetsAction();
163 tp.add(readChangesetAction);
164 cbInSelectionOnly.addItemListener(readChangesetAction);
165 lstInActiveDataLayer.getSelectionModel().addListSelectionListener(readChangesetAction);
166 lstInSelection.getSelectionModel().addListSelectionListener(readChangesetAction);
167
168 // -- close changesets action
169 closeChangesetAction = new CloseOpenChangesetsAction();
170 tp.add(closeChangesetAction);
171 cbInSelectionOnly.addItemListener(closeChangesetAction);
172 lstInActiveDataLayer.getSelectionModel().addListSelectionListener(closeChangesetAction);
173 lstInSelection.getSelectionModel().addListSelectionListener(closeChangesetAction);
174
175 // -- show info action
176 showChangsetInfoAction = new ShowChangesetInfoAction();
177 tp.add(showChangsetInfoAction);
178 cbInSelectionOnly.addItemListener(showChangsetInfoAction);
179 lstInActiveDataLayer.getSelectionModel().addListSelectionListener(showChangsetInfoAction);
180 lstInSelection.getSelectionModel().addListSelectionListener(showChangsetInfoAction);
181
182 pnl.add(tp);
183 return pnl;
184 }
185
186 protected void build() {
187 JPanel pnl = new JPanel(new BorderLayout());
188 pnl.add(buildFilterPanel(), BorderLayout.NORTH);
189 pnl.add(pnlList = buildListPanel(), BorderLayout.CENTER);
190 pnl.add(buildButtonPanel(), BorderLayout.SOUTH);
191 add(pnl, BorderLayout.CENTER);
192
193 cbInSelectionOnly.addItemListener(new FilterChangeHandler());
194
195 HelpUtil.setHelpContext(pnl, HelpUtil.ht("/Dialog/ChangesetListDialog"));
196 }
197
198 protected JList getCurrentChangesetList() {
199 if (cbInSelectionOnly.isSelected())
200 return lstInSelection;
201 return lstInActiveDataLayer;
202 }
203
204 protected ChangesetListModel getCurrentChangesetListModel() {
205 if (cbInSelectionOnly.isSelected())
206 return inSelectionModel;
207 return inActiveDataLayerModel;
208 }
209
210 protected void initWithCurrentData() {
211 if (Main.main.getEditLayer() != null) {
212 inSelectionModel.initFromPrimitives(Main.main.getEditLayer().data.getSelected());
213 inActiveDataLayerModel.initFromDataSet(Main.main.getEditLayer().data);
214 }
215 }
216
217 public ChangesetDialog(MapFrame mapFrame) {
218 super(
219 tr("Changesets"),
220 "changesetdialog",
221 tr("Open the list of changesets in the current layer."),
222 null, /* no keyboard shortcut */
223 200, /* the preferred height */
224 false /* don't show if there is no preference */
225 );
226 build();
227 initWithCurrentData();
228 }
229
230 class DblClickHandler extends MouseAdapter {
231 @Override
232 public void mouseClicked(MouseEvent e) {
233 if (!SwingUtilities.isLeftMouseButton(e) || e.getClickCount() < 2)
234 return;
235 Set<Integer> sel = getCurrentChangesetListModel().getSelectedChangesetIds();
236 if (sel.isEmpty())
237 return;
238 if (Main.main.getCurrentDataSet() == null)
239 return;
240 new SelectObjectsAction().selectObjectsByChangesetIds(Main.main.getCurrentDataSet(), sel);
241 }
242
243 }
244
245 class FilterChangeHandler implements ItemListener {
246 public void itemStateChanged(ItemEvent e) {
247 Main.pref.put("changeset-dialog.for-selected-objects-only", cbInSelectionOnly.isSelected());
248 pnlList.removeAll();
249 if (cbInSelectionOnly.isSelected()) {
250 pnlList.add(new JScrollPane(lstInSelection), BorderLayout.CENTER);
251 } else {
252 pnlList.add(new JScrollPane(lstInActiveDataLayer), BorderLayout.CENTER);
253 }
254 validate();
255 repaint();
256 }
257 }
258
259 /**
260 * Selects objects for the currently selected changesets.
261 */
262 class SelectObjectsAction extends AbstractAction implements ListSelectionListener, ItemListener{
263
264 public SelectObjectsAction() {
265 putValue(NAME, tr("Select"));
266 putValue(SHORT_DESCRIPTION, tr("Select all objects assigned to the currently selected changesets"));
267 putValue(SMALL_ICON, ImageProvider.get("dialogs", "select"));
268 updateEnabledState();
269 }
270
271 public void selectObjectsByChangesetIds(DataSet ds, Set<Integer> ids) {
272 if (ds == null || ids == null)
273 return;
274 Set<OsmPrimitive> sel = new HashSet<OsmPrimitive>();
275 for (OsmPrimitive p: ds.getNodes()) {
276 if (ids.contains(p.getChangesetId())) {
277 sel.add(p);
278 }
279 }
280 for (OsmPrimitive p: ds.getWays()) {
281 if (ids.contains(p.getChangesetId())) {
282 sel.add(p);
283 }
284 }
285 for (OsmPrimitive p: ds.getRelations()) {
286 if (ids.contains(p.getChangesetId())) {
287 sel.add(p);
288 }
289 }
290 ds.setSelected(sel);
291 }
292
293 public void actionPerformed(ActionEvent e) {
294 if (Main.main.getEditLayer() == null)
295 return;
296 ChangesetListModel model = getCurrentChangesetListModel();
297 Set<Integer> sel = model.getSelectedChangesetIds();
298 if (sel.isEmpty())
299 return;
300
301 DataSet ds = Main.main.getEditLayer().data;
302 selectObjectsByChangesetIds(ds,sel);
303 }
304
305 protected void updateEnabledState() {
306 setEnabled(getCurrentChangesetList().getSelectedIndices().length > 0);
307 }
308
309 public void itemStateChanged(ItemEvent arg0) {
310 updateEnabledState();
311
312 }
313
314 public void valueChanged(ListSelectionEvent e) {
315 updateEnabledState();
316 }
317 }
318
319 /**
320 * Downloads selected changesets
321 *
322 */
323 class ReadChangesetsAction extends AbstractAction implements ListSelectionListener, ItemListener{
324 public ReadChangesetsAction() {
325 putValue(NAME, tr("Download"));
326 putValue(SHORT_DESCRIPTION, tr("Download information about the selected changesets from the OSM server"));
327 putValue(SMALL_ICON, ImageProvider.get("download"));
328 updateEnabledState();
329 }
330
331 public void actionPerformed(ActionEvent arg0) {
332 ChangesetListModel model = getCurrentChangesetListModel();
333 Set<Integer> sel = model.getSelectedChangesetIds();
334 if (sel.isEmpty())
335 return;
336 DownloadChangesetsTask task = new DownloadChangesetsTask(sel);
337 Main.worker.submit(task);
338 }
339
340 protected void updateEnabledState() {
341 setEnabled(getCurrentChangesetList().getSelectedIndices().length > 0);
342 }
343
344 public void itemStateChanged(ItemEvent arg0) {
345 updateEnabledState();
346
347 }
348
349 public void valueChanged(ListSelectionEvent e) {
350 updateEnabledState();
351 }
352 }
353
354 /**
355 * Closes the currently selected changesets
356 *
357 */
358 class CloseOpenChangesetsAction extends AbstractAction implements ListSelectionListener, ItemListener {
359 public CloseOpenChangesetsAction() {
360 putValue(NAME, tr("Close open changesets"));
361 putValue(SHORT_DESCRIPTION, tr("Closes the selected open changesets"));
362 putValue(SMALL_ICON, ImageProvider.get("closechangeset"));
363 updateEnabledState();
364 }
365
366 public void actionPerformed(ActionEvent arg0) {
367 List<Changeset> sel = getCurrentChangesetListModel().getSelectedOpenChangesets();
368 if (sel.isEmpty())
369 return;
370 Main.worker.submit(new CloseChangesetTask(sel));
371 }
372
373 protected void updateEnabledState() {
374 setEnabled(getCurrentChangesetListModel().hasSelectedOpenChangesets());
375 }
376
377 public void itemStateChanged(ItemEvent arg0) {
378 updateEnabledState();
379 }
380
381 public void valueChanged(ListSelectionEvent e) {
382 updateEnabledState();
383 }
384 }
385
386 /**
387 * Show information about the currently selected changesets
388 *
389 */
390 class ShowChangesetInfoAction extends AbstractAction implements ListSelectionListener, ItemListener {
391 public ShowChangesetInfoAction() {
392 putValue(NAME, tr("Show info"));
393 putValue(SHORT_DESCRIPTION, tr("Open a web page for each selected changeset"));
394 putValue(SMALL_ICON, ImageProvider.get("about"));
395 updateEnabledState();
396 }
397
398 public void actionPerformed(ActionEvent arg0) {
399 Set<Changeset> sel = getCurrentChangesetListModel().getSelectedChangesets();
400 if (sel.isEmpty())
401 return;
402 if (sel.size() > 10 && ! AbstractInfoAction.confirmLaunchMultiple(sel.size()))
403 return;
404 String baseUrl = AbstractInfoAction.getBaseBrowseUrl();
405 for (Changeset cs: sel) {
406 String url = baseUrl + "/changeset/" + cs.getId();
407 OpenBrowser.displayUrl(
408 url
409 );
410 }
411 }
412
413 protected void updateEnabledState() {
414 setEnabled(getCurrentChangesetList().getSelectedIndices().length > 0);
415 }
416
417 public void itemStateChanged(ItemEvent arg0) {
418 updateEnabledState();
419 }
420
421 public void valueChanged(ListSelectionEvent e) {
422 updateEnabledState();
423 }
424 }
425
426 class PopupMenuLauncher extends MouseAdapter {
427 protected void showPopup(MouseEvent evt) {
428 if (!evt.isPopupTrigger())
429 return;
430 JList lst = getCurrentChangesetList();
431 if (lst.getSelectedIndices().length == 0) {
432 int idx = lst.locationToIndex(evt.getPoint());
433 if (idx >=0) {
434 lst.getSelectionModel().addSelectionInterval(idx, idx);
435 }
436 }
437 ChangesetDialogPopup popup = new ChangesetDialogPopup();
438 popup.show(lst, evt.getX(), evt.getY());
439
440 }
441 @Override
442 public void mouseClicked(MouseEvent evt) {
443 showPopup(evt);
444 }
445 @Override
446 public void mousePressed(MouseEvent evt) {
447 showPopup(evt);
448 }
449 @Override
450 public void mouseReleased(MouseEvent evt) {
451 showPopup(evt);
452 }
453 }
454
455 class ChangesetDialogPopup extends JPopupMenu {
456 public ChangesetDialogPopup() {
457 add(selectObjectsAction);
458 addSeparator();
459 add(readChangesetAction);
460 add(closeChangesetAction);
461 addSeparator();
462 add(showChangsetInfoAction);
463 }
464 }
465}
Note: See TracBrowser for help on using the repository browser.