001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.dialogs.changeset; 003 004import java.util.ArrayList; 005import java.util.Collection; 006import java.util.Comparator; 007import java.util.HashSet; 008import java.util.List; 009import java.util.Set; 010 011import javax.swing.DefaultListModel; 012import javax.swing.DefaultListSelectionModel; 013 014import org.openstreetmap.josm.data.osm.Changeset; 015import org.openstreetmap.josm.data.osm.ChangesetCache; 016import org.openstreetmap.josm.data.osm.ChangesetCacheEvent; 017import org.openstreetmap.josm.data.osm.ChangesetCacheListener; 018import org.openstreetmap.josm.data.osm.DataSet; 019import org.openstreetmap.josm.data.osm.OsmPrimitive; 020import org.openstreetmap.josm.data.osm.Storage; 021import org.openstreetmap.josm.gui.util.GuiHelper; 022 023/** 024 * This is the model that backs a list of changesets 025 */ 026public class ChangesetListModel extends DefaultListModel<Changeset> implements ChangesetCacheListener { 027 private final transient List<Changeset> data = new ArrayList<>(); 028 private final transient Storage<Changeset> shownChangesets = new Storage<>(true); 029 private final DefaultListSelectionModel selectionModel; 030 031 /** 032 * Creates a new {@link ChangesetListModel} 033 * @param selectionModel The selection model to use for this list 034 */ 035 public ChangesetListModel(DefaultListSelectionModel selectionModel) { 036 this.selectionModel = selectionModel; 037 } 038 039 /** 040 * Gets the list of changesets that are currently selected 041 * @return The selected changesets 042 */ 043 public Set<Changeset> getSelectedChangesets() { 044 Set<Changeset> ret = new HashSet<>(); 045 for (int i = 0; i < getSize(); i++) { 046 if (selectionModel.isSelectedIndex(i)) { 047 ret.add(data.get(i)); 048 } 049 } 050 return ret; 051 } 052 053 /** 054 * Gets the IDs of the changesets that are selected 055 * @return The selected ids 056 */ 057 public Set<Integer> getSelectedChangesetIds() { 058 Set<Integer> ret = new HashSet<>(); 059 for (int i = 0; i < getSize(); i++) { 060 if (selectionModel.isSelectedIndex(i)) { 061 ret.add(data.get(i).getId()); 062 } 063 } 064 return ret; 065 } 066 067 /** 068 * Sets the changesets to select 069 * @param changesets The changesets 070 */ 071 public void setSelectedChangesets(Collection<Changeset> changesets) { 072 selectionModel.setValueIsAdjusting(true); 073 selectionModel.clearSelection(); 074 if (changesets != null) { 075 for (Changeset cs: changesets) { 076 int idx = data.indexOf(cs); 077 if (idx >= 0) { 078 selectionModel.addSelectionInterval(idx, idx); 079 } 080 } 081 } 082 selectionModel.setValueIsAdjusting(false); 083 } 084 085 protected void setChangesets(Collection<Changeset> changesets) { 086 shownChangesets.clear(); 087 if (changesets != null) { 088 shownChangesets.addAll(changesets); 089 } 090 updateModel(); 091 } 092 093 private void updateModel() { 094 Set<Changeset> sel = getSelectedChangesets(); 095 data.clear(); 096 data.addAll(shownChangesets); 097 ChangesetCache cache = ChangesetCache.getInstance(); 098 for (Changeset cs: data) { 099 if (cache.contains(cs) && cache.get(cs.getId()) != cs) { 100 cs.mergeFrom(cache.get(cs.getId())); 101 } 102 } 103 sort(); 104 fireIntervalAdded(this, 0, getSize()); 105 setSelectedChangesets(sel); 106 } 107 108 /** 109 * Loads this list with the given changesets 110 * @param ids The ids of the changesets to display 111 */ 112 public void initFromChangesetIds(Collection<Integer> ids) { 113 if (ids == null || ids.isEmpty()) { 114 setChangesets(null); 115 return; 116 } 117 Set<Changeset> changesets = new HashSet<>(ids.size()); 118 for (int id: ids) { 119 if (id <= 0) { 120 continue; 121 } 122 changesets.add(new Changeset(id)); 123 } 124 setChangesets(changesets); 125 } 126 127 /** 128 * Loads this list with the given changesets 129 * @param primitives The primitives of which the changesets should be displayed 130 */ 131 public void initFromPrimitives(Collection<? extends OsmPrimitive> primitives) { 132 if (primitives == null) { 133 setChangesets(null); 134 return; 135 } 136 Set<Changeset> changesets = new HashSet<>(); 137 for (OsmPrimitive p: primitives) { 138 if (p.getChangesetId() <= 0) { 139 continue; 140 } 141 changesets.add(new Changeset(p.getChangesetId())); 142 } 143 setChangesets(changesets); 144 } 145 146 /** 147 * Loads this list with the given changesets 148 * @param ds The data set to get all changesets from 149 */ 150 public void initFromDataSet(DataSet ds) { 151 if (ds == null) { 152 setChangesets(null); 153 return; 154 } 155 Set<Changeset> changesets = new HashSet<>(); 156 for (OsmPrimitive p: ds.allPrimitives()) { 157 if (p.getChangesetId() <= 0) { 158 continue; 159 } 160 changesets.add(new Changeset(p.getChangesetId())); 161 } 162 setChangesets(changesets); 163 } 164 165 @Override 166 public Changeset getElementAt(int idx) { 167 return data.get(idx); 168 } 169 170 @Override 171 public int getSize() { 172 return data.size(); 173 } 174 175 protected void sort() { 176 data.sort(Comparator.comparingInt(Changeset::getId).reversed()); 177 } 178 179 /** 180 * Replies true if there is at least one selected open changeset 181 * 182 * @return true if there is at least one selected open changeset 183 */ 184 public boolean hasSelectedOpenChangesets() { 185 return !getSelectedOpenChangesets().isEmpty(); 186 } 187 188 /** 189 * Replies the selected open changesets 190 * 191 * @return the selected open changesets 192 */ 193 public List<Changeset> getSelectedOpenChangesets() { 194 List<Changeset> ret = new ArrayList<>(); 195 for (int i = 0; i < getSize(); i++) { 196 if (selectionModel.isSelectedIndex(i)) { 197 Changeset cs = data.get(i); 198 if (cs.isOpen()) { 199 ret.add(cs); 200 } 201 } 202 } 203 return ret; 204 } 205 206 /* ---------------------------------------------------------------------------- */ 207 /* Interface ChangesetCacheListener */ 208 /* ---------------------------------------------------------------------------- */ 209 @Override 210 public void changesetCacheUpdated(ChangesetCacheEvent event) { 211 Set<Changeset> sel = getSelectedChangesets(); 212 for (Changeset cs: event.getAddedChangesets()) { 213 int idx = data.indexOf(cs); 214 if (idx >= 0 && data.get(idx) != cs) { 215 data.get(idx).mergeFrom(cs); 216 } 217 } 218 for (Changeset cs: event.getUpdatedChangesets()) { 219 int idx = data.indexOf(cs); 220 if (idx >= 0 && data.get(idx) != cs) { 221 data.get(idx).mergeFrom(cs); 222 } 223 } 224 for (Changeset cs: event.getRemovedChangesets()) { 225 int idx = data.indexOf(cs); 226 if (idx >= 0) { 227 // replace with an incomplete changeset 228 data.set(idx, new Changeset(cs.getId())); 229 } 230 } 231 GuiHelper.runInEDT(() -> { 232 fireContentsChanged(this, 0, getSize()); 233 setSelectedChangesets(sel); 234 }); 235 } 236}