Changeset 2448 in josm for trunk/src


Ignore:
Timestamp:
2009-11-14T17:59:10+01:00 (15 years ago)
Author:
Gubaer
Message:

fixed #3352: History doesn't get invalidated on upload?
fixed #3912: Extend history dialog to contain the currently modified version
new: zoom to node in list of nodes in history dialog (popup menu)
new: load history of node from node list in history dialog (popup menu or double click)
fixed: close all history dialogs when the number of layers drop to 0
fixed: implemented equals() and hashCode() on SimplePrimitiveId
fixed: history features now usePrimitiveId instead of long.

Location:
trunk/src/org/openstreetmap/josm
Files:
17 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java

    r2439 r2448  
    811811     * @throws IllegalArgumentException thrown if other is null.
    812812     * @throws DataIntegrityProblemException thrown if either this is new and other is not, or other is new and this is not
    813      * @throws DataIntegrityProblemException thrown if other is new and other.getId() != this.getId()
     813     * @throws DataIntegrityProblemException thrown if other isn't new and other.getId() != this.getId()
    814814     */
    815815    public void mergeFrom(OsmPrimitive other) {
     
    10251025    public abstract void updatePosition();
    10261026
     1027    /**
     1028     * Replies the unique primitive id for this primitive
     1029     *
     1030     * @return the unique primitive id for this primitive
     1031     */
     1032    public PrimitiveId getPrimitiveId() {
     1033        return new SimplePrimitiveId(getUniqueId(), getType());
     1034    }
    10271035}
    10281036
  • trunk/src/org/openstreetmap/josm/data/osm/SimplePrimitiveId.java

    r2399 r2448  
    2020    }
    2121
     22    @Override
     23    public int hashCode() {
     24        final int prime = 31;
     25        int result = 1;
     26        result = prime * result + (int) (id ^ (id >>> 32));
     27        result = prime * result + ((type == null) ? 0 : type.hashCode());
     28        return result;
     29    }
    2230
    23 
     31    @Override
     32    public boolean equals(Object obj) {
     33        if (this == obj)
     34            return true;
     35        if (obj == null)
     36            return false;
     37        if (getClass() != obj.getClass())
     38            return false;
     39        SimplePrimitiveId other = (SimplePrimitiveId) obj;
     40        if (id != other.id)
     41            return false;
     42        if (type == null) {
     43            if (other.type != null)
     44                return false;
     45        } else if (!type.equals(other.type))
     46            return false;
     47        return true;
     48    }
    2449}
  • trunk/src/org/openstreetmap/josm/data/osm/history/History.java

    r2242 r2448  
    99import java.util.Date;
    1010import java.util.List;
    11 import java.util.NoSuchElementException;
    1211
    1312import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
    14 
     13import org.openstreetmap.josm.data.osm.PrimitiveId;
     14import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
     15
     16/**
     17 * Represents the history of an OSM primitive. The history consists
     18 * of a list of object snapshots with a specific version.
     19 *
     20 */
    1521public class History{
    1622    private static interface FilterPredicate {
     
    2531            }
    2632        }
    27         return new History(history.id, out);
    28     }
    29 
     33        return new History(history.id, history.type,out);
     34    }
     35
     36    /** the list of object snapshots */
    3037    private ArrayList<HistoryOsmPrimitive> versions;
    31     long id;
    32 
    33     protected History(long id, List<HistoryOsmPrimitive> versions) {
     38    /** the object id */
     39    private long id;
     40    private OsmPrimitiveType type;
     41
     42    /**
     43     * Creates a new history for an OSM primitive
     44     *
     45     * @param id the id. >0 required.
     46     * @param type the primitive type. Must not be null.
     47     * @param versions a list of versions. Can be null.
     48     * @throws IllegalArgumentException thrown if id <= 0
     49     * @throws IllegalArgumentException if type is null
     50     *
     51     */
     52    protected History(long id, OsmPrimitiveType type, List<HistoryOsmPrimitive> versions) {
     53        if (id <= 0)
     54            throw new IllegalArgumentException(tr("Parameter ''{0}'' > 0 expected, got {1}", "id", id));
     55        if (type == null)
     56            throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null", "type"));
    3457        this.id = id;
     58        this.type = type;
    3559        this.versions = new ArrayList<HistoryOsmPrimitive>();
    36         this.versions.addAll(versions);
     60        if (versions != null) {
     61            this.versions.addAll(versions);
     62        }
    3763    }
    3864
     
    4773                }
    4874        );
    49         return new History(id, copy);
     75        return new History(id, type, copy);
    5076    }
    5177
     
    6086                }
    6187        );
    62         return new History(id, copy);
     88        return new History(id, type,copy);
    6389    }
    6490
     
    139165    public long getId() {
    140166        return id;
     167    }
     168
     169    /**
     170     * Replies the primitive id for this history.
     171     *
     172     * @return the primitive id
     173     */
     174    public PrimitiveId getPrimitmiveId() {
     175        return new SimplePrimitiveId(id, type);
    141176    }
    142177
     
    149184    }
    150185
     186    /**
     187     * Replies the history primitive with version <code>version</code>. null,
     188     * if no such primitive exists.
     189     *
     190     * @param version the version
     191     * @return the history primitive with version <code>version</code>
     192     */
    151193    public HistoryOsmPrimitive getByVersion(long version) {
    152194        for (HistoryOsmPrimitive primitive: versions) {
     
    154196                return primitive;
    155197        }
    156         throw new NoSuchElementException(tr("There's no primitive with version {0} in this history.", version));
     198        return null;
    157199    }
    158200
    159201    public HistoryOsmPrimitive getByDate(Date date) {
    160         sortAscending();
    161 
    162         if (versions.isEmpty())
    163             throw new NoSuchElementException(tr("There's no version valid at date ''{0}'' in this history.", date));
    164         if (get(0).getTimestamp().compareTo(date)> 0)
    165             throw new NoSuchElementException(tr("There's no version valid at date ''{0}'' in this history.", date));
    166         for (int i = 1; i < versions.size();i++) {
    167             if (get(i-1).getTimestamp().compareTo(date) <= 0
    168                     && get(i).getTimestamp().compareTo(date) >= 0)
    169                 return get(i);
    170         }
    171         return getLatest();
     202        History h = sortAscending();
     203
     204        if (h.versions.isEmpty())
     205            return null;
     206        if (h.get(0).getTimestamp().compareTo(date)> 0)
     207            return null;
     208        for (int i = 1; i < h.versions.size();i++) {
     209            if (h.get(i-1).getTimestamp().compareTo(date) <= 0
     210                    && h.get(i).getTimestamp().compareTo(date) >= 0)
     211                return h.get(i);
     212        }
     213        return h.getLatest();
    172214    }
    173215
     
    180222    public HistoryOsmPrimitive getEarliest() {
    181223        if (isEmpty())
    182             throw new NoSuchElementException(tr("No earliest version found. History is empty."));
     224            return null;
    183225        return sortAscending().versions.get(0);
    184226    }
     
    186228    public HistoryOsmPrimitive getLatest() {
    187229        if (isEmpty())
    188             throw new NoSuchElementException(tr("No latest version found. History is empty."));
     230            return null;
    189231        return sortDescending().versions.get(0);
    190232    }
     
    199241
    200242    public OsmPrimitiveType getType() {
    201         if (isEmpty())
    202             throw new NoSuchElementException(tr("No type found. History is empty."));
    203         return versions.get(0).getType();
     243        return type;
    204244    }
    205245}
  • trunk/src/org/openstreetmap/josm/data/osm/history/HistoryDataSet.java

    r2163 r2448  
    66import java.util.ArrayList;
    77import java.util.HashMap;
    8 import java.util.NoSuchElementException;
    98import java.util.concurrent.CopyOnWriteArrayList;
     9import java.util.logging.Logger;
     10
     11import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
     12import org.openstreetmap.josm.data.osm.PrimitiveId;
     13import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
    1014
    1115/**
     
    1519 */
    1620public class HistoryDataSet {
     21    private final static Logger logger = Logger.getLogger(HistoryDataSet.class.getName());
    1722
    1823    /** the unique instance */
     
    3237
    3338    /** the history data */
    34     private HashMap<Long, ArrayList<HistoryOsmPrimitive>> data;
     39    private HashMap<PrimitiveId, ArrayList<HistoryOsmPrimitive>> data;
    3540    private CopyOnWriteArrayList<HistoryDataSetListener> listeners;
    3641
    3742    public HistoryDataSet() {
    38         data = new HashMap<Long, ArrayList<HistoryOsmPrimitive>>();
     43        data = new HashMap<PrimitiveId, ArrayList<HistoryOsmPrimitive>>();
    3944        listeners = new CopyOnWriteArrayList<HistoryDataSetListener>();
    4045    }
     
    5661    }
    5762
    58     protected void fireHistoryUpdated(long id) {
     63    protected void fireHistoryUpdated(SimplePrimitiveId id) {
    5964        for (HistoryDataSetListener l : listeners) {
    6065            l.historyUpdated(this, id);
     
    6469    /**
    6570     * Replies the history primitive for the primitive with id <code>id</code>
    66      * and version <code>version</code>
     71     * and version <code>version</code>. null, if no such primitive exists.
    6772     *
    68      * @param id the id of the primitive
    69      * @param version the version of the primitive
    70      * @return the history primitive for the primitive with id <code>id</code>
    71      * and version <code>version</code>
    72      * @throws NoSuchElementException thrown if this dataset doesn't include the respective
    73      * history primitive
     73     * @param id the id of the primitive. > 0 required.
     74     * @param type the primitive type. Must not be null.
     75     * @param version the version of the primitive. > 0 required
     76     * @return the history primitive for the primitive with id <code>id</code>,
     77     * type <code>type</code>, and version <code>version</code>
    7478     */
    75     public HistoryOsmPrimitive get(long id, long version) throws NoSuchElementException{
    76         ArrayList<HistoryOsmPrimitive> versions = data.get(id);
     79    public HistoryOsmPrimitive get(long id, OsmPrimitiveType type, long version){
     80        if (id <= 0)
     81            throw new IllegalArgumentException(tr("Parameter ''{0}'' > 0 expected, got {1}", "id", id));
     82        if (type == null)
     83            throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null", "type"));
     84        if (version <= 0)
     85            throw new IllegalArgumentException(tr("Parameter ''{0}'' > 0 expected, got {1}", "version", version));
     86
     87        SimplePrimitiveId pid = new SimplePrimitiveId(id, type);
     88        ArrayList<HistoryOsmPrimitive> versions = data.get(pid);
    7789        if (versions == null)
    78             throw new NoSuchElementException(tr("Didn't find an primitive with id {0} in this dataset", id));
    79 
     90            return null;
    8091        for (HistoryOsmPrimitive primitive: versions) {
    8192            if (primitive.matches(id, version))
    8293                return primitive;
    8394        }
    84         throw new NoSuchElementException(tr("Didn't find an primitive with id {0} and version {1} in this dataset", id, version));
     95        return null;
    8596    }
    8697
     
    91102     */
    92103    public void put(HistoryOsmPrimitive primitive) {
    93         if (data.get(primitive.getId()) == null) {
    94             data.put(primitive.getId(), new ArrayList<HistoryOsmPrimitive>());
     104        SimplePrimitiveId id = new SimplePrimitiveId(primitive.getId(), primitive.getType());
     105        if (data.get(id) == null) {
     106            data.put(id, new ArrayList<HistoryOsmPrimitive>());
    95107        }
    96         data.get(primitive.getId()).add(primitive);
    97         fireHistoryUpdated(primitive.getId());
     108        data.get(id).add(primitive);
     109        fireHistoryUpdated(id);
    98110    }
    99111
    100112    /**
    101113     * Replies the history for a given primitive with id <code>id</code>
     114     * and type <code>type</code>.
    102115     *
    103      * @param id the id
    104      * @return the history
     116     * @param id the id the if of the primitive. > 0 required
     117     * @param type the type of the primitive. Must not be null.
     118     * @return the history. null, if there isn't a history for <code>id</code> and
     119     * <code>type</code>.
     120     * @throws IllegalArgumentException thrown if id <= 0
     121     * @throws IllegalArgumentException thrown if type is null
    105122     */
    106     public History getHistory(long id) {
    107         ArrayList<HistoryOsmPrimitive> versions = data.get(id);
     123    public History getHistory(long id, OsmPrimitiveType type) throws IllegalArgumentException{
     124        if (id <= 0)
     125            throw new IllegalArgumentException(tr("Parameter ''{0}'' > 0 expected, got {1}", "id", id));
     126        if (type == null)
     127            throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null", "type"));
     128        SimplePrimitiveId pid = new SimplePrimitiveId(id, type);
     129        return getHistory(pid);
     130    }
     131
     132    /**
     133     * Replies the history for a primitive with id <code>id</code>. null, if no
     134     * such history exists.
     135     *
     136     * @param pid the primitive id. Must not be null.
     137     * @return the history for a primitive with id <code>id</code>. null, if no
     138     * such history exists
     139     * @throws IllegalArgumentException thrown if pid is null
     140     */
     141    public History getHistory(PrimitiveId pid) throws IllegalArgumentException{
     142        if (pid == null)
     143            throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null", "pid"));
     144        ArrayList<HistoryOsmPrimitive> versions = data.get(pid);
    108145        if (versions == null)
    109146            return null;
    110         return new History(id, versions);
     147        return new History(pid.getUniqueId(), pid.getType(), versions);
    111148    }
    112149
     
    119156        if (other == null)
    120157            return;
    121         for (Long id : other.data.keySet()) {
     158        for (PrimitiveId id : other.data.keySet()) {
    122159            this.data.put(id, other.data.get(id));
    123160        }
    124         fireHistoryUpdated(0);
     161        fireHistoryUpdated(null);
    125162    }
    126163}
  • trunk/src/org/openstreetmap/josm/data/osm/history/HistoryDataSetListener.java

    r2019 r2448  
    22package org.openstreetmap.josm.data.osm.history;
    33
     4import org.openstreetmap.josm.data.osm.PrimitiveId;
     5
    46public interface HistoryDataSetListener {
    5     void historyUpdated(HistoryDataSet source, long primitiveId);
     7    void historyUpdated(HistoryDataSet source, PrimitiveId id);
    68}
  • trunk/src/org/openstreetmap/josm/data/osm/history/HistoryOsmPrimitive.java

    r2242 r2448  
    1010
    1111import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
     12import org.openstreetmap.josm.data.osm.PrimitiveId;
     13import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
    1214
    1315/**
     
    4547     * @param user  the user (! null required)
    4648     * @param uid the user id (> 0 required)
    47      * @param changesetId the changeset id (> 0 required)
     49     * @param changesetId the changeset id (may be null if the changeset isn't known)
    4850     * @param timestamp the timestamp (! null required)
    4951     *
     
    5355        ensurePositiveLong(id, "id");
    5456        ensurePositiveLong(version, "version");
    55         if(uid != -1) /* allow -1 for anonymous users */
     57        if(uid != -1) {
    5658            ensurePositiveLong(uid, "uid");
    57         ensurePositiveLong(changesetId, "changesetId");
     59        }
    5860        ensureNotNull(user, "user");
    5961        ensureNotNull(timestamp, "timestamp");
     
    6365        this.user = user;
    6466        this.uid = uid;
     67        // FIXME: restrict to IDs > 0 as soon as OsmPrimitive holds the
     68        // changeset id too
    6569        this.changesetId  = changesetId;
    6670        this.timestamp = timestamp;
     
    7175        return id;
    7276    }
     77
     78    public PrimitiveId getPrimitiveId() {
     79        return new SimplePrimitiveId(id, getType());
     80    }
     81
    7382    public boolean isVisible() {
    7483        return visible;
     
    103112    public int compareTo(HistoryOsmPrimitive o) {
    104113        if (this.id != o.id)
    105             throw new ClassCastException(tr("Can't compare primitive with ID ''{0}'' to primitive with ID ''{1}''.", o.id, this.id));
     114            throw new ClassCastException(tr("Can''t compare primitive with ID ''{0}'' to primitive with ID ''{1}''.", o.id, this.id));
    106115        return new Long(this.version).compareTo(o.version);
    107116    }
     
    121130    public Map<String,String> getTags() {
    122131        return Collections.unmodifiableMap(tags);
     132    }
     133
     134    /**
     135     * Sets the tags for this history primitive. Removes all
     136     * tags if <code>tags</code> is null.
     137     *
     138     * @param tags the tags. May be null.
     139     */
     140    public void setTags(Map<String,String> tags) {
     141        if (tags == null) {
     142            this.tags = new HashMap<String, String>();
     143        } else {
     144            this.tags = new HashMap<String, String>(tags);
     145        }
    123146    }
    124147
  • trunk/src/org/openstreetmap/josm/gui/dialogs/HistoryDialog.java

    r2273 r2448  
    3737import org.openstreetmap.josm.data.osm.DataSet;
    3838import org.openstreetmap.josm.data.osm.OsmPrimitive;
     39import org.openstreetmap.josm.data.osm.PrimitiveId;
    3940import org.openstreetmap.josm.data.osm.history.History;
    4041import org.openstreetmap.josm.data.osm.history.HistoryDataSet;
     
    4243import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
    4344import org.openstreetmap.josm.gui.SideButton;
    44 import org.openstreetmap.josm.gui.history.HistoryBrowserDialog;
    4545import org.openstreetmap.josm.gui.history.HistoryBrowserDialogManager;
    4646import org.openstreetmap.josm.gui.history.HistoryLoadTask;
     
    136136    }
    137137
    138     public void historyUpdated(HistoryDataSet source, long primitiveId) {
     138    public void historyUpdated(HistoryDataSet source, PrimitiveId primitiveId) {
    139139        model.refresh();
    140140    }
     
    206206
    207207        public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
    208             refresh();
     208            data.clear();
     209            selectionModel.clearSelection();
     210            if (newSelection == null || newSelection.isEmpty()) return;
     211            for (OsmPrimitive primitive: newSelection) {
     212                if (primitive.isNew()) {
     213                    continue;
     214                }
     215                data.add(primitive);
     216            }
     217            fireTableDataChanged();
     218            selectionModel.addSelectionInterval(0, data.size()-1);
    209219        }
    210220
     
    297307            ArrayList<OsmPrimitive> ret = new ArrayList<OsmPrimitive>(primitives.size());
    298308            for (OsmPrimitive p: primitives) {
    299                 if (HistoryDataSet.getInstance().getHistory(p.getId()) == null) {
     309                if (HistoryDataSet.getInstance().getHistory(p.getPrimitiveId()) == null) {
    300310                    ret.add(p);
    301311                }
    302312            }
    303313            return ret;
    304         }
    305 
    306         /**
    307          * shows the {@see HistoryBrowserDialog} for a given {@see History}
    308          *
    309          * @param h the history. Must not be null.
    310          * @exception IllegalArgumentException thrown, if h is null
    311          */
    312         protected void showHistory(History h) throws IllegalArgumentException {
    313             if (h == null)
    314                 throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null.", "h"));
    315             if (HistoryBrowserDialogManager.getInstance().existsDialog(h.getId())) {
    316                 HistoryBrowserDialogManager.getInstance().show(h.getId());
    317             } else {
    318                 HistoryBrowserDialog dialog = new HistoryBrowserDialog(h);
    319                 HistoryBrowserDialogManager.getInstance().show(h.getId(), dialog);
    320             }
    321314        }
    322315
     
    332325                public void run() {
    333326                    for (OsmPrimitive p : primitives) {
    334                         History h = HistoryDataSet.getInstance().getHistory(p.getId());
     327                        History h = HistoryDataSet.getInstance().getHistory(p.getPrimitiveId());
    335328                        if (h == null) {
    336329                            continue;
    337330                        }
    338                         showHistory(h);
     331                        HistoryBrowserDialogManager.getInstance().show(h);
    339332                    }
    340333                }
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTable.java

    r2348 r2448  
    2020import javax.swing.event.ListSelectionEvent;
    2121import javax.swing.event.ListSelectionListener;
    22 import javax.swing.table.TableColumnModel;
    2322
    2423import org.openstreetmap.josm.Main;
    2524import org.openstreetmap.josm.actions.AutoScaleAction;
    26 import org.openstreetmap.josm.data.osm.DataSet;
    2725import org.openstreetmap.josm.data.osm.OsmPrimitive;
    2826import org.openstreetmap.josm.gui.layer.Layer;
     
    191189        public ZoomToAction() {
    192190            putValue(NAME, tr("Zoom to"));
    193             putValue(SHORT_DESCRIPTION, tr("Zoom to primitive the first selected member refers to"));
     191            putValue(SHORT_DESCRIPTION, tr("Zoom to the object the first selected member refers to"));
    194192            updateEnabledState();
    195193        }
     
    220218            }
    221219            setEnabled(true);
    222             putValue(SHORT_DESCRIPTION, tr("Zoom to primitive the first selected member refers to"));
     220            putValue(SHORT_DESCRIPTION, tr("Zoom to the object the first selected member refers to"));
    223221        }
    224222
  • trunk/src/org/openstreetmap/josm/gui/help/HelpBrowser.java

    r2308 r2448  
    175175     * @return the current URL
    176176     */
     177
     178
    177179    public String getUrl() {
    178180        return url;
  • trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserDialog.java

    r2318 r2448  
    22package org.openstreetmap.josm.gui.history;
    33
     4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
    45import static org.openstreetmap.josm.tools.I18n.marktr;
    56import static org.openstreetmap.josm.tools.I18n.tr;
     
    1718
    1819import org.openstreetmap.josm.Main;
     20import org.openstreetmap.josm.data.osm.PrimitiveId;
    1921import org.openstreetmap.josm.data.osm.history.History;
    2022import org.openstreetmap.josm.data.osm.history.HistoryDataSet;
     
    2426import org.openstreetmap.josm.gui.help.HelpUtil;
    2527import org.openstreetmap.josm.tools.ImageProvider;
    26 import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
    2728
    2829/**
     
    3536    /** the embedded browser */
    3637    private HistoryBrowser browser;
     38    private CloseAction closeAction;
    3739
    3840    /**
     
    4446        String title = "";
    4547        switch(h.getEarliest().getType()) {
    46             case NODE:  title = marktr("History for node {0}"); break;
    47             case WAY: title = marktr("History for way {0}"); break;
    48             case RELATION:  title = marktr("History for relation {0}"); break;
     48        case NODE:  title = marktr("History for node {0}"); break;
     49        case WAY: title = marktr("History for way {0}"); break;
     50        case RELATION:  title = marktr("History for relation {0}"); break;
    4951        }
    5052        setTitle(tr(
     
    7072        pnl.add(btn);
    7173
    72         btn = new SideButton(new CloseAction());
     74        btn = new SideButton(closeAction = new CloseAction());
    7375        btn.setName("btn.close");
    7476        pnl.add(btn);
     
    106108    }
    107109
    108     public void historyUpdated(HistoryDataSet source, long primitiveId) {
    109         if (primitiveId == browser.getHistory().getId()) {
     110    public void historyUpdated(HistoryDataSet source, PrimitiveId primitiveId) {
     111        if (primitiveId == null) {
     112            browser.populate(source.getHistory(browser.getHistory().getPrimitmiveId()));
     113        } else if (primitiveId.equals(browser.getHistory().getPrimitmiveId())) {
    110114            browser.populate(source.getHistory(primitiveId));
    111         } else if (primitiveId == 0) {
    112             browser.populate(source.getHistory(browser.getHistory().getId()));
    113115        }
     116    }
     117
     118    public void unlinkAsListener() {
     119        getHistoryBrowser().getModel().unlinkAsListener();
    114120    }
    115121
     
    121127        }
    122128
    123         public void actionPerformed(ActionEvent e) {
     129        public void run() {
     130            getHistoryBrowser().getModel().unlinkAsListener();
    124131            HistoryDataSet.getInstance().removeHistoryDataSetListener(HistoryBrowserDialog.this);
    125132            HistoryBrowserDialogManager.getInstance().hide(HistoryBrowserDialog.this);
     133        }
     134
     135        public void actionPerformed(ActionEvent e) {
     136            run();
    126137        }
    127138    }
     
    144155        @Override
    145156        public void windowClosing(WindowEvent e) {
    146             HistoryDataSet.getInstance().removeHistoryDataSetListener(HistoryBrowserDialog.this);
    147             HistoryBrowserDialogManager.getInstance().hide(HistoryBrowserDialog.this);
     157            closeAction.run();
    148158        }
    149159    }
  • trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserDialogManager.java

    r2019 r2448  
    44import java.awt.Dimension;
    55import java.awt.Point;
     6import java.util.ArrayList;
    67import java.util.HashMap;
    78import java.util.Map;
    89
     10import org.openstreetmap.josm.Main;
     11import org.openstreetmap.josm.data.osm.history.History;
     12import org.openstreetmap.josm.gui.layer.Layer;
     13import org.openstreetmap.josm.gui.layer.Layer.LayerChangeListener;
    914import org.openstreetmap.josm.tools.WindowGeometry;
    1015
    11 public class HistoryBrowserDialogManager {
     16public class HistoryBrowserDialogManager implements LayerChangeListener {
    1217    static private HistoryBrowserDialogManager instance;
    1318    static public HistoryBrowserDialogManager getInstance() {
     
    2227    protected HistoryBrowserDialogManager() {
    2328        dialogs = new HashMap<Long, HistoryBrowserDialog>();
     29        Layer.listeners.add(this);
    2430    }
    2531
     
    7985        dialog.dispose();
    8086    }
     87
     88    /**
     89     * Hides and destroys all currently visible history browser dialogs
     90     *
     91     */
     92    public void hideAll() {
     93        ArrayList<HistoryBrowserDialog> dialogs = new ArrayList<HistoryBrowserDialog>();
     94        dialogs.addAll(this.dialogs.values());
     95        for (HistoryBrowserDialog dialog: dialogs) {
     96            dialog.unlinkAsListener();
     97            hide(dialog);
     98        }
     99    }
     100
     101    public void show(History h) {
     102        if (h == null)
     103            return;
     104        if (existsDialog(h.getId())) {
     105            show(h.getId());
     106        } else {
     107            HistoryBrowserDialog dialog = new HistoryBrowserDialog(h);
     108            show(h.getId(), dialog);
     109        }
     110    }
     111
     112    /* ----------------------------------------------------------------------------- */
     113    /* LayerChangeListener                                                           */
     114    /* ----------------------------------------------------------------------------- */
     115    public void activeLayerChange(Layer oldLayer, Layer newLayer) {}
     116    public void layerAdded(Layer newLayer) {}
     117
     118    public void layerRemoved(Layer oldLayer) {
     119        // remove all history browsers if the number of layers drops to 0
     120        //
     121        if (Main.map.mapView.getNumLayers() == 0) {
     122            hideAll();
     123        }
     124    }
    81125}
  • trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserModel.java

    r2243 r2448  
    55
    66import java.util.ArrayList;
     7import java.util.Collection;
    78import java.util.Collections;
    89import java.util.HashSet;
     
    1213import javax.swing.table.DefaultTableModel;
    1314
    14 import org.openstreetmap.josm.data.coor.CoordinateFormat;
    15 import org.openstreetmap.josm.data.coor.LatLon;
     15import org.openstreetmap.josm.Main;
     16import org.openstreetmap.josm.data.osm.DataSetListener;
     17import org.openstreetmap.josm.data.osm.Node;
     18import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1619import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
     20import org.openstreetmap.josm.data.osm.PrimitiveId;
     21import org.openstreetmap.josm.data.osm.Relation;
     22import org.openstreetmap.josm.data.osm.RelationMember;
     23import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
     24import org.openstreetmap.josm.data.osm.Way;
    1725import org.openstreetmap.josm.data.osm.history.History;
    1826import org.openstreetmap.josm.data.osm.history.HistoryNode;
     
    2028import org.openstreetmap.josm.data.osm.history.HistoryRelation;
    2129import org.openstreetmap.josm.data.osm.history.HistoryWay;
     30import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
     31import org.openstreetmap.josm.gui.layer.DataChangeListener;
     32import org.openstreetmap.josm.gui.layer.Layer;
     33import org.openstreetmap.josm.gui.layer.OsmDataLayer;
     34import org.openstreetmap.josm.gui.layer.Layer.LayerChangeListener;
    2235
    2336/**
    2437 * This is the model used by the history browser.
    2538 *
    26  * The state this model manages consists of the following elements:
     39 * The model state consists of the following elements:
    2740 * <ul>
    2841 *   <li>the {@see History} of a specific {@see OsmPrimitive}</li>
     
    4659 * @see HistoryBrowser
    4760 */
    48 public class HistoryBrowserModel extends Observable {
     61public class HistoryBrowserModel extends Observable implements LayerChangeListener, DataSetListener, DataChangeListener {
    4962
    5063    private static Logger logger = Logger.getLogger(HistoryBrowserModel.class.getName());
     
    5467    private HistoryOsmPrimitive reference;
    5568    private HistoryOsmPrimitive current;
     69    /**
     70     * latest isn't a reference of history. It's a clone of the currently edited
     71     * {@see OsmPrimitive} in the current edit layer.
     72     */
     73    private HistoryOsmPrimitive latest;
    5674
    5775    private VersionTableModel versionTableModel;
     
    6381    private RelationMemberTableModel referenceRelationMemberTableModel;
    6482
     83    /**
     84     * constructor
     85     */
    6586    public HistoryBrowserModel() {
    6687        versionTableModel = new VersionTableModel();
     
    7192        currentRelationMemberTableModel = new RelationMemberTableModel(PointInTimeType.CURRENT_POINT_IN_TIME);
    7293        referenceRelationMemberTableModel = new RelationMemberTableModel(PointInTimeType.REFERENCE_POINT_IN_TIME);
    73     }
    74 
     94
     95        if (getEditLayer() != null) {
     96            getEditLayer().data.addDataSetListener(this);
     97            getEditLayer().listenerDataChanged.add(this);
     98        }
     99        Layer.listeners.add(this);
     100
     101    }
     102
     103    /**
     104     * Creates a new history browser model for a given history.
     105     *
     106     * @param history the history. Must not be null.
     107     * @throws IllegalArgumentException thrown if history is null
     108     */
    75109    public HistoryBrowserModel(History history) {
    76110        this();
     111        if (history == null)
     112            throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null", "history"));
    77113        setHistory(history);
     114    }
     115
     116    /**
     117     * Replies the current edit layer; null, if there isn't a current edit layer
     118     * of type {@see OsmDataLayer}.
     119     *
     120     * @return the current edit layer
     121     */
     122    protected OsmDataLayer getEditLayer() {
     123        try {
     124            return Main.map.mapView.getEditLayer();
     125        } catch(NullPointerException e) {
     126            return null;
     127        }
    78128    }
    79129
     
    84134    public History getHistory() {
    85135        return history;
     136    }
     137
     138    protected boolean hasNewNodes(Way way) {
     139        for (Node n: way.getNodes()) {
     140            if (n.isNew()) return true;
     141        }
     142        return false;
     143    }
     144    protected boolean canShowAsLatest(OsmPrimitive primitive) {
     145        if (primitive == null) return false;
     146        if (primitive.isNew()) return false;
     147        if (history == null) return false;
     148        // only show latest of the same version if it is
     149        // modified
     150        if (history.getByVersion(primitive.getVersion()) != null)
     151            return primitive.isModified();
     152
     153        // latest has a higher version than one of the primitives
     154        // in the history (probably because the history got out of sync
     155        // with uploaded data) -> show the primitive as latest
     156        return true;
    86157    }
    87158
     
    97168            current = history.getEarliest();
    98169            reference = history.getEarliest();
     170            setLatest(null);
     171            if (getEditLayer() != null) {
     172                OsmPrimitive p = getEditLayer().data.getPrimitiveById(history.getId(), history.getType());
     173                if (canShowAsLatest(p)) {
     174                    HistoryOsmPrimitive latest = new HistoryPrimitiveBuilder().build(p);
     175                    setLatest(latest);
     176                }
     177            }
    99178        }
    100179        initTagTableModels();
    101180        fireModelChange();
    102181    }
    103 
    104182
    105183    protected void fireModelChange() {
     
    177255    }
    178256
     257    /**
     258     * Sets the {@see HistoryOsmPrimitive} which plays the role of a reference point
     259     * in time (see {@see PointInTimeType}).
     260     *
     261     * @param reference the reference history primitive. Must not be null.
     262     * @throws IllegalArgumentException thrown if reference is null
     263     * @throws IllegalStateException thrown if this model isn't a assigned a history yet
     264     * @throws IllegalArgumentException if reference isn't an history primitive for the history managed by this mode
     265     *
     266     * @see #setHistory(History)
     267     * @see PointInTimeType
     268     */
    179269    public void setReferencePointInTime(HistoryOsmPrimitive reference) throws IllegalArgumentException, IllegalStateException{
    180270        if (reference == null)
     
    196286    }
    197287
     288    /**
     289     * Sets the {@see HistoryOsmPrimitive} which plays the role of the current point
     290     * in time (see {@see PointInTimeType}).
     291     *
     292     * @param reference the reference history primitive. Must not be null.
     293     * @throws IllegalArgumentException thrown if reference is null
     294     * @throws IllegalStateException thrown if this model isn't a assigned a history yet
     295     * @throws IllegalArgumentException if reference isn't an history primitive for the history managed by this mode
     296     *
     297     * @see #setHistory(History)
     298     * @see PointInTimeType
     299     */
    198300    public void setCurrentPointInTime(HistoryOsmPrimitive current) throws IllegalArgumentException, IllegalStateException{
    199301        if (current == null)
     
    252354
    253355    /**
     356     * Returns true if <code>primitive</code> is the latest primitive
     357     * representing the version currently edited in the current data
     358     * layer.
     359     *
     360     * @param primitive the primitive to check
     361     * @return true if <code>primitive</code> is the latest primitive
     362     */
     363    public boolean isLatest(HistoryOsmPrimitive primitive) {
     364        if (primitive == null) return false;
     365        return primitive == latest;
     366    }
     367
     368    /**
    254369     * The table model for the list of versions in the current history
    255370     *
    256371     */
    257     public class VersionTableModel extends DefaultTableModel {
     372    public class VersionTableModel extends DefaultTableModel{
    258373
    259374        private VersionTableModel() {
     
    264379            if (history == null)
    265380                return 0;
    266             return history.getNumVersions();
     381            int ret = history.getNumVersions();
     382            if (latest != null) {
     383                ret++;
     384            }
     385            return ret;
    267386        }
    268387
     
    271390            if(history == null)
    272391                return null;
    273             return history.get(row);
     392            if (row < history.getNumVersions())
     393                return history.get(row);
     394            if (row == history.getNumVersions())
     395                return latest;
     396            return null;
    274397        }
    275398
     
    281404        public void setReferencePointInTime(int row) {
    282405            if (history == null) return;
     406            if (row == history.getNumVersions()) {
     407                if (latest != null) {
     408                    HistoryBrowserModel.this.setReferencePointInTime(latest);
     409                }
     410                return;
     411            }
    283412            if (row < 0 || row > history.getNumVersions()) return;
    284413            HistoryOsmPrimitive reference = history.get(row);
     
    288417        public void setCurrentPointInTime(int row) {
    289418            if (history == null) return;
     419            if (row == history.getNumVersions()) {
     420                if (latest != null) {
     421                    HistoryBrowserModel.this.setCurrentPointInTime(latest);
     422                }
     423                return;
     424            }
    290425            if (row < 0 || row > history.getNumVersions()) return;
    291426            HistoryOsmPrimitive current = history.get(row);
     
    295430        public boolean isReferencePointInTime(int row) {
    296431            if (history == null) return false;
     432            if (row == history.getNumVersions())
     433                return latest == reference;
    297434            if (row < 0 || row > history.getNumVersions()) return false;
    298435            HistoryOsmPrimitive p = history.get(row);
    299             return p.equals(reference);
     436            return p == reference;
    300437        }
    301438
    302439        public HistoryOsmPrimitive getPrimitive(int row) {
    303440            return history.get(row);
     441        }
     442
     443        public boolean isLatest(int row) {
     444            return row >= history.getNumVersions();
     445        }
     446
     447        public OsmPrimitive getLatest() {
     448            if (latest == null) return null;
     449            if (getEditLayer() == null) return null;
     450            OsmPrimitive p = getEditLayer().data.getPrimitiveById(latest.getId(), latest.getType());
     451            return p;
    304452        }
    305453    }
     
    467615                return null;
    468616            return way.getNodes().get(row);
     617        }
     618
     619        public PrimitiveId getNodeId(int row) {
     620            HistoryWay way = getWay();
     621            if (way == null) return null;
     622            if (row > way.getNumNodes()) return null;
     623            return new SimplePrimitiveId(way.getNodeId(row), OsmPrimitiveType.NODE);
    469624        }
    470625
     
    587742        }
    588743    }
     744
     745    protected void setLatest(HistoryOsmPrimitive latest) {
     746        if (latest == null) {
     747            if (this.current == this.latest) {
     748                this.current = history.getLatest();
     749            }
     750            if (this.reference == this.latest) {
     751                this.current = history.getLatest();
     752            }
     753            this.latest = null;
     754        } else {
     755            if (this.current == this.latest) {
     756                this.current = latest;
     757            }
     758            if (this.reference == this.latest) {
     759                this.reference = latest;
     760            }
     761            this.latest = latest;
     762        }
     763        fireModelChange();
     764    }
     765
     766    /**
     767     * Removes this model as listener for data change and layer change
     768     * events.
     769     *
     770     */
     771    public void unlinkAsListener() {
     772        if (getEditLayer() != null) {
     773            getEditLayer().data.removeDataSetListener(this);
     774        }
     775        Layer.listeners.remove(this);
     776
     777    }
     778
     779    /* ---------------------------------------------------------------------- */
     780    /* DataSetListener                                                        */
     781    /* ---------------------------------------------------------------------- */
     782    public void nodeMoved(Node node) {
     783        if (!node.isNew() && node.getId() == history.getId()) {
     784            setLatest(new HistoryPrimitiveBuilder().build(node));
     785        }
     786    }
     787
     788    public void primtivesAdded(Collection<? extends OsmPrimitive> added) {
     789        if (added == null || added.isEmpty()) return;
     790        for (OsmPrimitive p: added) {
     791            if (canShowAsLatest(p)) {
     792                setLatest(new HistoryPrimitiveBuilder().build(p));
     793            }
     794        }
     795    }
     796
     797    public void primtivesRemoved(Collection<? extends OsmPrimitive> removed) {
     798        if (removed == null || removed.isEmpty()) return;
     799        for (OsmPrimitive p: removed) {
     800            if (!p.isNew() && p.getId() == history.getId()) {
     801                setLatest(null);
     802            }
     803        }
     804    }
     805
     806    public void relationMembersChanged(Relation r) {
     807        if (!r.isNew() && r.getId() == history.getId()) {
     808            setLatest(new HistoryPrimitiveBuilder().build(r));
     809        }
     810    }
     811
     812    public void tagsChanged(OsmPrimitive prim) {
     813        if (!prim.isNew() && prim.getId() == history.getId()) {
     814            setLatest(new HistoryPrimitiveBuilder().build(prim));
     815        }
     816    }
     817
     818    public void wayNodesChanged(Way way) {
     819        if (!way.isNew() && way.getId() == history.getId()) {
     820            setLatest(new HistoryPrimitiveBuilder().build(way));
     821        }
     822    }
     823
     824    /* ---------------------------------------------------------------------- */
     825    /* DataChangeListener                                                    */
     826    /* ---------------------------------------------------------------------- */
     827    public void dataChanged(OsmDataLayer l) {
     828        if (l != getEditLayer()) return;
     829        OsmPrimitive primitive = l.data.getPrimitiveById(history.getId(), history.getType());
     830        HistoryOsmPrimitive latest;
     831        if (canShowAsLatest(primitive)) {
     832            latest = new HistoryPrimitiveBuilder().build(primitive);
     833        } else {
     834            latest = null;
     835        }
     836        setLatest(latest);
     837        fireModelChange();
     838    }
     839
     840    /* ---------------------------------------------------------------------- */
     841    /* LayerChangeListener                                                    */
     842    /* ---------------------------------------------------------------------- */
     843    public void activeLayerChange(Layer oldLayer, Layer newLayer) {
     844        if (oldLayer != null && oldLayer instanceof OsmDataLayer) {
     845            OsmDataLayer l = (OsmDataLayer)oldLayer;
     846            l.data.removeDataSetListener(this);
     847            l.listenerDataChanged.remove(this);
     848        }
     849        if (newLayer == null || ! (newLayer instanceof OsmDataLayer)) {
     850            latest = null;
     851            fireModelChange();
     852            return;
     853        }
     854        OsmDataLayer l = (OsmDataLayer)newLayer;
     855        l.data.addDataSetListener(this);
     856        l.listenerDataChanged.add(this);
     857        OsmPrimitive primitive = l.data.getPrimitiveById(history.getId(), history.getType());
     858        HistoryOsmPrimitive latest;
     859        if (canShowAsLatest(primitive)) {
     860            latest = new HistoryPrimitiveBuilder().build(primitive);
     861        } else {
     862            latest = null;
     863        }
     864        setLatest(latest);
     865        fireModelChange();
     866    }
     867
     868    public void layerAdded(Layer newLayer) {}
     869    public void layerRemoved(Layer oldLayer) {}
     870
     871    /**
     872     * Creates a {@see HistoryOsmPrimitive} from a {@see OsmPrimitive}
     873     *
     874     */
     875    class HistoryPrimitiveBuilder extends AbstractVisitor {
     876        private HistoryOsmPrimitive clone;
     877
     878        public void visit(Node n) {
     879            clone = new HistoryNode(n.getId(), n.getVersion(), n.isVisible(),n.getUser().getName(), n.getUser().getId(), 0, n.getTimestamp(), n.getCoor());
     880            clone.setTags(n.getKeys());
     881        }
     882
     883        public void visit(Relation r) {
     884            clone = new HistoryRelation(r.getId(), r.getVersion(), r.isVisible(),r.getUser().getName(), r.getUser().getId(), 0, r.getTimestamp());
     885            clone.setTags(r.getKeys());
     886            HistoryRelation hr = (HistoryRelation)clone;
     887            for (RelationMember rm : r.getMembers()) {
     888                hr.addMember(new org.openstreetmap.josm.data.osm.history.RelationMember(rm.getRole(), rm.getType(), rm.getUniqueId()));
     889            }
     890        }
     891
     892        public void visit(Way w) {
     893            clone = new HistoryWay(w.getId(), w.getVersion(), w.isVisible(),w.getUser().getName(), w.getUser().getId(), 0, w.getTimestamp());
     894            clone.setTags(w.getKeys());
     895            for (Node n: w.getNodes()) {
     896                ((HistoryWay)clone).addNode(n.getUniqueId());
     897            }
     898        }
     899
     900        public HistoryOsmPrimitive build(OsmPrimitive primitive) {
     901            primitive.visit(this);
     902            return clone;
     903        }
     904    }
    589905}
  • trunk/src/org/openstreetmap/josm/gui/history/HistoryLoadTask.java

    r2275 r2448  
    77import java.io.IOException;
    88import java.util.Collection;
    9 import java.util.HashMap;
    10 import java.util.Map;
     9import java.util.HashSet;
    1110
    1211import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1312import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
     13import org.openstreetmap.josm.data.osm.PrimitiveId;
     14import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
    1415import org.openstreetmap.josm.data.osm.history.History;
    1516import org.openstreetmap.josm.data.osm.history.HistoryDataSet;
     
    4546    private boolean cancelled = false;
    4647    private Exception lastException  = null;
    47     private Map<Long, OsmPrimitiveType> toLoad;
     48    private HashSet<PrimitiveId> toLoad;
    4849    private HistoryDataSet loadedData;
    4950
    5051    public HistoryLoadTask() {
    5152        super(tr("Load history"), true);
    52         toLoad = new HashMap<Long, OsmPrimitiveType>();
     53        toLoad = new HashSet<PrimitiveId>();
    5354    }
    5455
     
    6061     * @return this task
    6162     */
    62     public HistoryLoadTask add(long id, OsmPrimitiveType type) {
     63    public HistoryLoadTask add(long id, OsmPrimitiveType type) throws IllegalArgumentException {
    6364        if (id <= 0)
    64             throw new IllegalArgumentException(tr("ID > 0 expected. Got {0}.", id));
     65            throw new IllegalArgumentException(tr("Parameter ''{0}'' > 0 expected. Got {1}.", "id", id));
    6566        if (type == null)
    6667            throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null.", "type"));
    67         if (!toLoad.containsKey(id)) {
    68             toLoad.put(id, type);
    69         }
     68        SimplePrimitiveId pid = new SimplePrimitiveId(id, type);
     69        toLoad.add(pid);
     70        return this;
     71    }
     72
     73    /**
     74     * Adds an object whose history is to be loaded.
     75     *
     76     * @param pid  the primitive id. Must not be null. Id > 0 required.
     77     * @return this task
     78     */
     79    public HistoryLoadTask add(PrimitiveId pid) throws IllegalArgumentException {
     80        if (pid == null)
     81            throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null.", "pid"));
     82        if (pid.getUniqueId() <= 0)
     83            throw new IllegalArgumentException(tr("id in parameter ''{0}'' > 0 expected, got {1}.", "pid", pid.getUniqueId()));
     84        toLoad.add(pid);
    7085        return this;
    7186    }
     
    8196        if (primitive == null)
    8297            throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null.", "primitive"));
    83         if (!toLoad.containsKey(primitive.getId())) {
    84             toLoad.put(primitive.getId(), primitive.getType());
    85         }
     98        toLoad.add(primitive.getPrimitiveId());
    8699        return this;
    87100    }
     
    97110        if (history == null)
    98111            throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null.", "history"));
    99         if (!toLoad.containsKey(history.getId())) {
    100             toLoad.put(history.getId(), history.getEarliest().getType());
    101         }
     112        toLoad.add(history.getPrimitmiveId());
    102113        return this;
    103114    }
     
    116127        if (primitive.getId() <= 0)
    117128            throw new IllegalArgumentException(tr("Object id > 0 expected. Got {0}", primitive.getId()));
    118 
    119         return add(primitive.getId(), OsmPrimitiveType.from(primitive));
     129        toLoad.add(primitive.getPrimitiveId());
     130        return this;
    120131    }
    121132
     
    161172        loadedData = new HistoryDataSet();
    162173        try {
    163             for(Map.Entry<Long, OsmPrimitiveType> entry: toLoad.entrySet()) {
     174            for(PrimitiveId pid: toLoad) {
    164175                if (cancelled) {
    165176                    break;
    166177                }
    167                 if (entry.getKey() == 0) {
    168                     continue;
    169                 }
    170178                String msg = "";
    171                 switch(entry.getValue()) {
    172                     case NODE: msg = marktr("Loading history for node {0}"); break;
    173                     case WAY: msg = marktr("Loading history for way {0}"); break;
    174                     case RELATION: msg = marktr("Loading history for relation {0}"); break;
     179                switch(pid.getType()) {
     180                case NODE: msg = marktr("Loading history for node {0}"); break;
     181                case WAY: msg = marktr("Loading history for way {0}"); break;
     182                case RELATION: msg = marktr("Loading history for relation {0}"); break;
    175183                }
    176184                progressMonitor.indeterminateSubTask(tr(msg,
    177                         Long.toString(entry.getKey())));
     185                        Long.toString(pid.getUniqueId())));
    178186                OsmServerHistoryReader reader = null;
    179187                HistoryDataSet ds = null;
    180188                try {
    181                     reader = new OsmServerHistoryReader(entry.getValue(), entry.getKey());
     189                    reader = new OsmServerHistoryReader(pid.getType(), pid.getUniqueId());
    182190                    ds = reader.parseHistory(progressMonitor.createSubTaskMonitor(1, false));
    183191                } catch(OsmTransferException e) {
  • trunk/src/org/openstreetmap/josm/gui/history/NodeListViewer.java

    r2243 r2448  
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.gui.history;
     3
     4import static org.openstreetmap.josm.tools.I18n.tr;
    35
    46import java.awt.GridBagConstraints;
    57import java.awt.GridBagLayout;
    68import java.awt.Insets;
    7 
     9import java.awt.Point;
     10import java.awt.event.ActionEvent;
     11import java.awt.event.MouseAdapter;
     12import java.awt.event.MouseEvent;
     13
     14import javax.swing.AbstractAction;
    815import javax.swing.JPanel;
     16import javax.swing.JPopupMenu;
    917import javax.swing.JScrollPane;
    1018import javax.swing.JTable;
    1119import javax.swing.ListSelectionModel;
     20
     21import org.openstreetmap.josm.Main;
     22import org.openstreetmap.josm.actions.AutoScaleAction;
     23import org.openstreetmap.josm.data.osm.OsmPrimitive;
     24import org.openstreetmap.josm.data.osm.PrimitiveId;
     25import org.openstreetmap.josm.data.osm.history.History;
     26import org.openstreetmap.josm.data.osm.history.HistoryDataSet;
     27import org.openstreetmap.josm.gui.history.HistoryBrowserModel.NodeListTableModel;
     28import org.openstreetmap.josm.gui.layer.OsmDataLayer;
     29import org.openstreetmap.josm.tools.ImageProvider;
    1230
    1331/**
     
    2846    private AdjustmentSynchronizer adjustmentSynchronizer;
    2947    private SelectionSynchronizer selectionSynchronizer;
     48    private NodeListPopupMenu popupMenu;
    3049
    3150    protected JScrollPane embeddInScrollPane(JTable table) {
     
    4564        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    4665        selectionSynchronizer.participateInSynchronizedSelection(table.getSelectionModel());
     66        table.addMouseListener(new PopupMenuLauncher(table));
     67        table.addMouseListener(new DoubleClickAdapter(table));
    4768        return table;
    4869    }
     
    5677        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    5778        selectionSynchronizer.participateInSynchronizedSelection(table.getSelectionModel());
     79        table.addMouseListener(new PopupMenuLauncher(table));
     80        table.addMouseListener(new DoubleClickAdapter(table));
    5881        return table;
    5982    }
     
    110133        gc.anchor = GridBagConstraints.NORTHWEST;
    111134        add(embeddInScrollPane(buildCurrentNodeListTable()),gc);
     135
     136        popupMenu = new NodeListPopupMenu();
    112137    }
    113138
     
    143168        }
    144169    }
     170
     171
     172    class NodeListPopupMenu extends JPopupMenu {
     173        private ZoomToNodeAction zoomToNodeAction;
     174        private ShowHistoryAction showHistoryAction;
     175
     176        public NodeListPopupMenu() {
     177            zoomToNodeAction = new ZoomToNodeAction();
     178            add(zoomToNodeAction);
     179            showHistoryAction = new ShowHistoryAction();
     180            add(showHistoryAction);
     181        }
     182
     183        public void prepare(PrimitiveId pid){
     184            zoomToNodeAction.setPrimitiveId(pid);
     185            zoomToNodeAction.updateEnabledState();
     186
     187            showHistoryAction.setPrimitiveId(pid);
     188            showHistoryAction.updateEnabledState();
     189        }
     190    }
     191
     192    class ZoomToNodeAction extends AbstractAction {
     193        private PrimitiveId primitiveId;
     194
     195        public ZoomToNodeAction() {
     196            putValue(NAME, tr("Zoom to node"));
     197            putValue(SHORT_DESCRIPTION, tr("Zoom to this node in the current data layer"));
     198            putValue(SMALL_ICON, ImageProvider.get("dialogs", "zoomin"));
     199        }
     200
     201        public void actionPerformed(ActionEvent e) {
     202            if (!isEnabled()) return;
     203            OsmPrimitive p = getPrimitiveToZoom();
     204            if (p!= null) {
     205                getEditLayer().data.setSelected(p.getPrimitiveId());
     206                new AutoScaleAction("selection").autoScale();
     207            }
     208        }
     209
     210        public void setPrimitiveId(PrimitiveId pid) {
     211            this.primitiveId = pid;
     212            updateEnabledState();
     213        }
     214
     215        protected OsmDataLayer getEditLayer() {
     216            try {
     217                return Main.map.mapView.getEditLayer();
     218            } catch(NullPointerException e) {
     219                return null;
     220            }
     221        }
     222
     223        protected OsmPrimitive getPrimitiveToZoom() {
     224            if (primitiveId == null) return null;
     225            OsmPrimitive p = getEditLayer().data.getPrimitiveById(primitiveId);
     226            return p;
     227        }
     228
     229        public void updateEnabledState() {
     230            if (getEditLayer() == null) {
     231                setEnabled(false);
     232                return;
     233            }
     234            setEnabled(getPrimitiveToZoom() != null);
     235        }
     236    }
     237
     238    class ShowHistoryAction extends AbstractAction {
     239        private PrimitiveId primitiveId;
     240
     241        public ShowHistoryAction() {
     242            putValue(NAME, tr("Show history"));
     243            putValue(SHORT_DESCRIPTION, tr("Open a history browser with the history of this node"));
     244            putValue(SMALL_ICON, ImageProvider.get("dialogs", "history"));
     245        }
     246
     247        public void actionPerformed(ActionEvent e) {
     248            if (!isEnabled()) return;
     249            run();
     250        }
     251
     252        public void setPrimitiveId(PrimitiveId pid) {
     253            this.primitiveId = pid;
     254            updateEnabledState();
     255        }
     256
     257        public void run() {
     258            if (HistoryDataSet.getInstance().getHistory(primitiveId) == null) {
     259                Main.worker.submit(new HistoryLoadTask().add(primitiveId));
     260            }
     261            Runnable r = new Runnable() {
     262                public void run() {
     263                    History h = HistoryDataSet.getInstance().getHistory(primitiveId);
     264                    if (h == null)
     265                        return;
     266                    HistoryBrowserDialogManager.getInstance().show(h);
     267                }
     268            };
     269            Main.worker.submit(r);
     270        }
     271
     272        public void updateEnabledState() {
     273            setEnabled(primitiveId != null && primitiveId.getUniqueId() > 0);
     274        }
     275    }
     276
     277    class PopupMenuLauncher extends MouseAdapter {
     278        private JTable table;
     279
     280        public PopupMenuLauncher(JTable table) {
     281            this.table = table;
     282        }
     283
     284        @Override
     285        public void mousePressed(MouseEvent e) {
     286            showPopup(e);
     287        }
     288
     289        @Override
     290        public void mouseReleased(MouseEvent e) {
     291            showPopup(e);
     292        }
     293
     294        private void showPopup(MouseEvent e) {
     295            if (!e.isPopupTrigger()) return;
     296            Point p = e.getPoint();
     297            int row = table.rowAtPoint(p);
     298            NodeListTableModel model = (NodeListTableModel) table.getModel();
     299            PrimitiveId pid = model.getNodeId(row);
     300            popupMenu.prepare(pid);
     301            popupMenu.show(e.getComponent(), e.getX(), e.getY());
     302        }
     303    }
     304
     305    class DoubleClickAdapter extends MouseAdapter {
     306        private JTable table;
     307        private ShowHistoryAction showHistoryAction;
     308
     309        public DoubleClickAdapter(JTable table) {
     310            this.table = table;
     311            showHistoryAction = new ShowHistoryAction();
     312        }
     313
     314        protected NodeListTableModel getModel() {
     315            return (NodeListTableModel)table.getModel();
     316        }
     317
     318        @Override
     319        public void mouseClicked(MouseEvent e) {
     320            if (e.getClickCount() < 2) return;
     321            int row = table.rowAtPoint(e.getPoint());
     322            PrimitiveId pid = getModel().getNodeId(row);
     323            if (pid == null)
     324                return;
     325            showHistoryAction.setPrimitiveId(pid);
     326            showHistoryAction.run();
     327        }
     328    }
    145329}
  • trunk/src/org/openstreetmap/josm/gui/history/TagTableCellRenderer.java

    r2017 r2448  
    66import java.awt.Color;
    77import java.awt.Component;
     8import java.awt.Font;
    89import java.util.logging.Logger;
    910
    1011import javax.swing.JLabel;
    1112import javax.swing.JTable;
     13import javax.swing.UIManager;
    1214import javax.swing.table.TableCellRenderer;
    1315
     
    2022    static private Logger logger = Logger.getLogger(TagTableCellRenderer.class.getName());
    2123
    22     public final static Color BGCOLOR_SELECTED = new Color(143,170,255);
    2324    public final static Color BGCOLOR_DIFFERENCE = new Color(255,197,197);
    2425
    2526    public TagTableCellRenderer() {
    2627        setOpaque(true);
    27         setForeground(Color.BLACK);
    2828    }
    2929
    3030    protected void renderName(String key, HistoryBrowserModel.TagTableModel model, boolean isSelected) {
    3131        String text = key;
    32         Color bgColor = Color.WHITE;
     32        Color bgColor = UIManager.getColor("Table.background");
     33        Color fgColor = UIManager.getColor("Table.foreground");
     34        Font font = UIManager.getFont("Table.font");
    3335        if (! model.hasTag(key)) {
    34             text = tr("<undefined>");
     36            text = tr("not present");
    3537            bgColor = BGCOLOR_DIFFERENCE;
     38            font = font.deriveFont(Font.ITALIC);
    3639        } else if (!model.oppositeHasTag(key)) {
    3740            bgColor = BGCOLOR_DIFFERENCE;
    3841        }
    3942        if (isSelected) {
    40             bgColor = BGCOLOR_SELECTED;
     43            bgColor = UIManager.getColor("Table.backgroundSelected");
     44            fgColor = UIManager.getColor("Table.foregroundSelected");
    4145        }
     46
    4247        setText(text);
    4348        setToolTipText(text);
    4449        setBackground(bgColor);
     50        setForeground(fgColor);
     51        setFont(font);
    4552    }
    4653
    4754    protected void renderValue(String key, HistoryBrowserModel.TagTableModel model, boolean isSelected) {
    4855        String text = "";
    49         Color bgColor = Color.WHITE;
     56        Color bgColor = UIManager.getColor("Table.background");
     57        Color fgColor = UIManager.getColor("Table.foreground");
     58        Font font = UIManager.getFont("Table.font");
    5059        if (! model.hasTag(key)) {
    51             text = tr("<undefined>");
     60            text = tr("not present");
    5261            bgColor = BGCOLOR_DIFFERENCE;
     62            font = font.deriveFont(Font.ITALIC);
    5363        } else {
    5464            text = model.getValue(key);
     
    5868        }
    5969        if (isSelected) {
    60             bgColor = BGCOLOR_SELECTED;
     70            bgColor = UIManager.getColor("Table.backgroundSelected");
     71            fgColor = UIManager.getColor("Table.foregroundSelected");
    6172        }
    6273
     
    6475        setToolTipText(text);
    6576        setBackground(bgColor);
     77        setForeground(fgColor);
     78        setFont(font);
    6679    }
    6780
  • trunk/src/org/openstreetmap/josm/gui/history/VersionInfoPanel.java

    r2318 r2448  
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
     6import java.awt.BorderLayout;
    67import java.awt.FlowLayout;
    78import java.awt.GridBagConstraints;
     
    1718import javax.swing.JPanel;
    1819
     20import org.openstreetmap.josm.Main;
    1921import org.openstreetmap.josm.actions.AbstractInfoAction;
    2022import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
     23import org.openstreetmap.josm.gui.JMultilineLabel;
     24import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    2125import org.openstreetmap.josm.tools.UrlLabel;
    2226
     
    3135    private PointInTimeType pointInTimeType;
    3236    private HistoryBrowserModel model;
    33     private JLabel lblInfo;
     37    private JMultilineLabel lblInfo;
    3438    private UrlLabel lblUser;
    3539    private UrlLabel lblChangeset;
     40    private JPanel pnlUserAndChangeset;
    3641
    3742    protected void build() {
    3843        JPanel pnl1 = new JPanel();
    39         pnl1.setLayout(new FlowLayout(FlowLayout.LEFT));
    40         lblInfo = new JLabel();
    41         lblInfo.setHorizontalAlignment(JLabel.LEFT);
    42         pnl1.add(lblInfo);
     44        pnl1.setLayout(new BorderLayout());
     45        lblInfo = new JMultilineLabel("");
     46        //lblInfo.setHorizontalAlignment(JLabel.LEFT);
     47        pnl1.add(lblInfo, BorderLayout.CENTER);
    4348
    44         JPanel pnl2 = new JPanel();
    45         pnl2.setLayout(new FlowLayout(FlowLayout.LEFT));
     49        pnlUserAndChangeset = new JPanel();
     50        pnlUserAndChangeset.setLayout(new FlowLayout(FlowLayout.LEFT));
    4651        lblUser = new UrlLabel();
    47         pnl2.add(new JLabel(tr("User")));
    48         pnl2.add(lblUser);
    49         pnl2.add(new JLabel(tr("Changeset")));
     52        pnlUserAndChangeset.add(new JLabel(tr("User")));
     53        pnlUserAndChangeset.add(lblUser);
     54        pnlUserAndChangeset.add(new JLabel(tr("Changeset")));
    5055        lblChangeset = new UrlLabel();
    51         pnl2.add(lblChangeset);
     56        pnlUserAndChangeset.add(lblChangeset);
    5257
    5358        setLayout(new GridBagLayout());
     
    5661        gc.fill = GridBagConstraints.HORIZONTAL;
    5762        gc.weightx = 1.0;
    58         gc.weighty = 0.0;
     63        gc.weighty = 1.0;
    5964        add(pnl1, gc);
    6065        gc.gridy = 1;
    61         add(pnl2, gc);
     66        gc.weighty = 0.0;
     67        add(pnlUserAndChangeset, gc);
    6268    }
    6369
     
    6874    }
    6975
     76    protected OsmDataLayer getEditLayer() {
     77        try {
     78            return Main.map.mapView.getEditLayer();
     79        } catch(NullPointerException e) {
     80            return null;
     81        }
     82    }
     83
    7084    protected String getInfoText() {
    7185        HistoryOsmPrimitive primitive = getPrimitive();
    7286        if (primitive == null)
    7387            return "";
    74         String text = tr(
    75                 "<html>Version <strong>{0}</strong> created on <strong>{1}</strong></html>",
    76                 Long.toString(primitive.getVersion()),
    77                 new SimpleDateFormat().format(primitive.getTimestamp())
    78         );
     88        String text;
     89        if (model.isLatest(primitive)) {
     90            text = tr("<html>Version <strong>{0}</strong> currently edited in layer ''{1}''</html>",
     91                    Long.toString(primitive.getVersion()),
     92                    getEditLayer() == null ? tr("unknown") : getEditLayer().getName()
     93            );
     94        } else {
     95            text = tr(
     96                    "<html>Version <strong>{0}</strong> created on <strong>{1}</strong></html>",
     97                    Long.toString(primitive.getVersion()),
     98                    new SimpleDateFormat().format(primitive.getTimestamp())
     99            );
     100        }
    79101        return text;
    80102    }
     
    110132        lblInfo.setText(getInfoText());
    111133
    112         String url = AbstractInfoAction.getBaseBrowseUrl() + "/changeset/" + getPrimitive().getChangesetId();
    113         lblChangeset.setUrl(url);
    114         lblChangeset.setDescription(Long.toString(getPrimitive().getChangesetId()));
     134        if (!model.isLatest(getPrimitive())) {
     135            String url = AbstractInfoAction.getBaseBrowseUrl() + "/changeset/" + getPrimitive().getChangesetId();
     136            lblChangeset.setUrl(url);
     137            lblChangeset.setDescription(Long.toString(getPrimitive().getChangesetId()));
    115138
    116         try {
    117             if (getPrimitive().getUid() != -1) {
    118                 url = AbstractInfoAction.getBaseUserUrl() + "/" +  URLEncoder.encode(getPrimitive().getUser(), "UTF-8").replaceAll("\\+", "%20");
    119                 lblUser.setUrl(url);
    120             } else {
     139            try {
     140                if (getPrimitive().getUid() != -1) {
     141                    url = AbstractInfoAction.getBaseUserUrl() + "/" +  URLEncoder.encode(getPrimitive().getUser(), "UTF-8").replaceAll("\\+", "%20");
     142                    lblUser.setUrl(url);
     143                } else {
     144                    lblUser.setUrl(null);
     145                }
     146            } catch(UnsupportedEncodingException e) {
     147                e.printStackTrace();
    121148                lblUser.setUrl(null);
    122149            }
    123         } catch(UnsupportedEncodingException e) {
    124             e.printStackTrace();
    125             lblUser.setUrl(null);
     150            String username = getPrimitive().getUser();
     151            lblUser.setDescription(username);
     152        } else {
     153            String user = Main.pref.get("osm-server.username");
     154            if (user == null) {
     155                lblUser.setDescription(tr("anonymous"));
     156            } else {
     157                try {
     158                    String url = AbstractInfoAction.getBaseUserUrl() + "/" +  URLEncoder.encode(user, "UTF-8").replaceAll("\\+", "%20");
     159                    lblUser.setUrl(url);
     160                } catch(UnsupportedEncodingException e) {
     161                    e.printStackTrace();
     162                    lblUser.setUrl(null);
     163                }
     164                lblUser.setDescription(user);
     165            }
     166            lblChangeset.setDescription(tr("none"));
     167            lblChangeset.setUrl(null);
    126168        }
    127         String username = getPrimitive().getUser();
    128         lblUser.setDescription(username);
    129169    }
    130170}
  • trunk/src/org/openstreetmap/josm/gui/history/VersionTableCellRenderer.java

    r2250 r2448  
    1515import javax.swing.table.TableCellRenderer;
    1616
     17import org.openstreetmap.josm.Main;
     18import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1719import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
    1820import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
     21import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    1922import org.openstreetmap.josm.tools.ImageProvider;
    2023
     
    4447    }
    4548
    46     protected void renderIcon(HistoryOsmPrimitive primitive) {
    47         ImageIcon icon = null;
    48         if (primitive != null) {
    49             icon = icons.get(primitive.getType());
    50         }
     49    protected void renderIcon(OsmPrimitiveType type) {
     50        ImageIcon icon = type == null? null : icons.get(type);
    5151        setIcon(icon);
    5252    }
    5353
    5454    protected void renderText(HistoryOsmPrimitive primitive) {
    55         // render lable text
     55        // render label text
    5656        //
    5757        StringBuilder sb = new StringBuilder();
     
    8686    }
    8787
     88    protected OsmDataLayer getEditLayer() {
     89        try {
     90            return Main.map.mapView.getEditLayer();
     91        } catch(NullPointerException e) {
     92            return null;
     93        }
     94    }
     95
     96    protected void renderLatestText(OsmPrimitive primitive) {
     97        // -- label text
     98        StringBuffer sb = new StringBuffer();
     99        if (primitive == null) {
     100            setText("");
     101            return;
     102        }
     103        if (primitive.isModified()) {
     104            sb.append("*");
     105        }
     106        sb.append(tr("Version {0} in editor", primitive.getVersion()));
     107        if (primitive.isDeleted()) {
     108            sb.append(tr("[deleted]"));
     109        }
     110        setText(sb.toString());
     111
     112        // -- tooltip text
     113        sb = new StringBuffer();
     114        OsmDataLayer l = getEditLayer();
     115
     116        sb.append(
     117                tr(
     118                        "Version {0} currently edited in data layer ''{1}''",
     119                        primitive.getId(),
     120                        l == null ? tr("unknown") : l.getName()
     121                )
     122        );
     123        setToolTipText(sb.toString());
     124    }
     125
    88126    protected void renderBackground(JTable table, int row, boolean isSelected) {
    89127        Color bgColor = Color.WHITE;
     
    96134    }
    97135
     136    public void renderVersionFromHistory(HistoryOsmPrimitive primitive, JTable table, int row, boolean isSelected) {
     137        renderIcon(primitive.getType());
     138        renderText(primitive);
     139        renderBackground(table, row, isSelected);
     140    }
     141
     142    public void renderLatest(OsmPrimitive primitive, JTable table, int row, boolean isSelected) {
     143        renderIcon(primitive.getType());
     144        renderLatestText(getModel(table).getLatest());
     145        renderBackground(table, row, isSelected);
     146    }
     147
    98148    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
    99149            int row, int column) {
    100 
    101         HistoryOsmPrimitive primitive = (HistoryOsmPrimitive)value;
    102         renderIcon(primitive);
    103         renderText(primitive);
    104         renderBackground(table, row, isSelected);
     150        if (getModel(table).isLatest(row)) {
     151            renderLatest(getModel(table).getLatest(),table, row, isSelected);
     152        } else {
     153            renderVersionFromHistory((HistoryOsmPrimitive)value, table, row, isSelected);
     154        }
    105155        return this;
    106156    }
Note: See TracChangeset for help on using the changeset viewer.