001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.dialogs.changeset; 003 004import java.beans.PropertyChangeListener; 005import java.beans.PropertyChangeSupport; 006import java.util.ArrayList; 007import java.util.Collection; 008import java.util.Comparator; 009import java.util.List; 010import java.util.Set; 011import java.util.stream.Collectors; 012import java.util.stream.IntStream; 013 014import javax.swing.DefaultListSelectionModel; 015import javax.swing.table.AbstractTableModel; 016 017import org.openstreetmap.josm.data.osm.Changeset; 018import org.openstreetmap.josm.data.osm.ChangesetCache; 019import org.openstreetmap.josm.data.osm.ChangesetCacheEvent; 020import org.openstreetmap.josm.data.osm.ChangesetCacheListener; 021import org.openstreetmap.josm.gui.util.GuiHelper; 022import org.openstreetmap.josm.gui.util.TableHelper; 023 024/** 025 * This is the model for the changeset cache manager dialog. 026 */ 027public class ChangesetCacheManagerModel extends AbstractTableModel implements ChangesetCacheListener { 028 029 /** the name of the property for the currently selected changeset in the detail view */ 030 public static final String CHANGESET_IN_DETAIL_VIEW_PROP = ChangesetCacheManagerModel.class.getName() + ".changesetInDetailView"; 031 032 private final transient List<Changeset> data = new ArrayList<>(); 033 private final DefaultListSelectionModel selectionModel; 034 private transient Changeset changesetInDetailView; 035 private final PropertyChangeSupport support = new PropertyChangeSupport(this); 036 037 /** 038 * Creates a new ChangesetCacheManagerModel that is based on the selectionModel 039 * @param selectionModel A new selection model that should be used. 040 */ 041 public ChangesetCacheManagerModel(DefaultListSelectionModel selectionModel) { 042 this.selectionModel = selectionModel; 043 } 044 045 /** 046 * Adds a property change listener to this model. 047 * @param listener The listener 048 */ 049 public void addPropertyChangeListener(PropertyChangeListener listener) { 050 support.addPropertyChangeListener(listener); 051 } 052 053 /** 054 * Removes a property change listener from this model. 055 * @param listener The listener 056 */ 057 public void removePropertyChangeListener(PropertyChangeListener listener) { 058 support.removePropertyChangeListener(listener); 059 } 060 061 /** 062 * Sets the changeset currently displayed in the detail view. Fires a property change event 063 * for the property {@link #CHANGESET_IN_DETAIL_VIEW_PROP} if necessary. 064 * 065 * @param cs the changeset currently displayed in the detail view. 066 */ 067 public void setChangesetInDetailView(Changeset cs) { 068 Changeset oldValue = changesetInDetailView; 069 changesetInDetailView = cs; 070 if (oldValue != cs) { 071 support.firePropertyChange(CHANGESET_IN_DETAIL_VIEW_PROP, oldValue, changesetInDetailView); 072 } 073 } 074 075 /** 076 * Replies true if there is at least one selected changeset 077 * 078 * @return true if there is at least one selected changeset 079 */ 080 public boolean hasSelectedChangesets() { 081 return selectionModel.getMinSelectionIndex() >= 0; 082 } 083 084 /** 085 * Replies the list of selected changesets 086 * 087 * @return the list of selected changesets 088 */ 089 public List<Changeset> getSelectedChangesets() { 090 List<Changeset> ret = new ArrayList<>(); 091 for (int i = 0; i < data.size(); i++) { 092 Changeset cs = data.get(i); 093 if (selectionModel.isSelectedIndex(i)) { 094 ret.add(cs); 095 } 096 } 097 return ret; 098 } 099 100 /** 101 * Replies a set of ids of the selected changesets 102 * 103 * @return a set of ids of the selected changesets 104 */ 105 public Set<Integer> getSelectedChangesetIds() { 106 return getSelectedChangesets().stream().map(Changeset::getId).collect(Collectors.toSet()); 107 } 108 109 /** 110 * Selects the changesets in <code>selected</code>. 111 * 112 * @param selected the collection of changesets to select. Ignored if empty. 113 */ 114 public void setSelectedChangesets(Collection<Changeset> selected) { 115 GuiHelper.runInEDTAndWait(() -> TableHelper.setSelectedIndices(selectionModel, 116 selected != null ? selected.stream().mapToInt(data::indexOf) : IntStream.empty())); 117 } 118 119 @Override 120 public int getColumnCount() { 121 return 8; 122 } 123 124 @Override 125 public int getRowCount() { 126 return data.size(); 127 } 128 129 @Override 130 public Changeset getValueAt(int row, int column) { 131 return data.get(row); 132 } 133 134 /** 135 * Initializes the data that is displayed using the changeset cache. 136 */ 137 public void init() { 138 ChangesetCache cc = ChangesetCache.getInstance(); 139 List<Changeset> selected = getSelectedChangesets(); 140 data.clear(); 141 data.addAll(cc.getChangesets()); 142 sort(); 143 fireTableDataChanged(); 144 setSelectedChangesets(selected); 145 146 cc.addChangesetCacheListener(this); 147 } 148 149 /** 150 * Destroys and unregisters this model. 151 */ 152 public void tearDown() { 153 ChangesetCache.getInstance().removeChangesetCacheListener(this); 154 } 155 156 /** 157 * Gets the selection model this table is based on. 158 * @return The selection model. 159 */ 160 public DefaultListSelectionModel getSelectionModel() { 161 return selectionModel; 162 } 163 164 protected void sort() { 165 data.sort(Comparator.comparingInt(Changeset::getId).reversed()); 166 } 167 168 /* ------------------------------------------------------------------------------ */ 169 /* interface ChangesetCacheListener */ 170 /* ------------------------------------------------------------------------------ */ 171 @Override 172 public void changesetCacheUpdated(ChangesetCacheEvent event) { 173 List<Changeset> selected = getSelectedChangesets(); 174 data.addAll(event.getAddedChangesets()); 175 data.removeAll(event.getRemovedChangesets()); 176 for (Changeset cs: event.getUpdatedChangesets()) { 177 int idx = data.indexOf(cs); 178 if (idx >= 0) { 179 Changeset mine = data.get(idx); 180 if (mine != cs) { 181 mine.mergeFrom(cs); 182 } 183 } 184 } 185 GuiHelper.runInEDT(() -> { 186 sort(); 187 fireTableDataChanged(); 188 setSelectedChangesets(selected); 189 }); 190 } 191}