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