Index: trunk/src/org/openstreetmap/josm/actions/HistoryInfoAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/HistoryInfoAction.java	(revision 16122)
+++ trunk/src/org/openstreetmap/josm/actions/HistoryInfoAction.java	(revision 16123)
@@ -7,6 +7,11 @@
 import java.awt.event.ActionEvent;
 import java.awt.event.KeyEvent;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.swing.JTable;
 
 import org.openstreetmap.josm.data.osm.OsmData;
+import org.openstreetmap.josm.data.osm.PrimitiveId;
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.dialogs.OsmIdSelectionDialog;
@@ -38,4 +43,23 @@
     @Override
     public void actionPerformed(ActionEvent ae) {
+        // Generic handling of tables displaying OSM primitives
+        if (ae.getSource() instanceof JTable) {
+            JTable table = (JTable) ae.getSource();
+            Set<PrimitiveId> sel = new HashSet<>();
+            for (int row : table.getSelectedRows()) {
+                for (int col = 0; col < table.getModel().getColumnCount(); col++) {
+                    Object value = table.getModel().getValueAt(row, col);
+                    if (value instanceof PrimitiveId) {
+                        sel.add((PrimitiveId) value);
+                        break;
+                    }
+                }
+            }
+            if (!sel.isEmpty()) {
+                HistoryBrowserDialogManager.getInstance().showHistory(sel);
+                return;
+            }
+        }
+        // Otherwise show history for currently selected objects
         OsmData<?, ?, ?, ?> set = getLayerManager().getActiveData();
         if (set != null && !set.selectionEmpty()) {
Index: trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmChangeTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmChangeTask.java	(revision 16122)
+++ trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmChangeTask.java	(revision 16123)
@@ -144,5 +144,5 @@
             this.toLoad = toLoad;
             this.setChangesetDataNeeded(false);
-            add(toLoad.keySet());
+            addOsmPrimitives(toLoad.keySet());
             // Updating process is done after all history requests have been made
             HistoryDataSet.getInstance().addHistoryDataSetListener(this);
Index: trunk/src/org/openstreetmap/josm/data/osm/history/HistoryDataSet.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/history/HistoryDataSet.java	(revision 16122)
+++ trunk/src/org/openstreetmap/josm/data/osm/history/HistoryDataSet.java	(revision 16123)
@@ -9,4 +9,5 @@
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
@@ -165,12 +166,11 @@
      * @return the history for a primitive with id <code>id</code>. null, if no
      * such history exists
-     * @throws IllegalArgumentException if pid is null
+     * @throws NullPointerException if pid is null
      */
     public History getHistory(PrimitiveId pid) {
-        CheckParameterUtil.ensureParameterNotNull(pid, "pid");
-        List<HistoryOsmPrimitive> versions = data.get(pid);
-        if (versions == null && pid instanceof IPrimitive) {
-            versions = data.get(((IPrimitive) pid).getPrimitiveId());
-        }
+        PrimitiveId key = pid instanceof IPrimitive ? ((IPrimitive) pid).getPrimitiveId()
+                        : pid instanceof HistoryOsmPrimitive ? ((HistoryOsmPrimitive) pid).getPrimitiveId()
+                        : pid;
+        List<HistoryOsmPrimitive> versions = data.get(Objects.requireNonNull(key, "key"));
         if (versions == null)
             return null;
Index: trunk/src/org/openstreetmap/josm/data/osm/history/HistoryOsmPrimitive.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/history/HistoryOsmPrimitive.java	(revision 16122)
+++ trunk/src/org/openstreetmap/josm/data/osm/history/HistoryOsmPrimitive.java	(revision 16123)
@@ -16,5 +16,4 @@
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
 import org.openstreetmap.josm.data.osm.PrimitiveData;
 import org.openstreetmap.josm.data.osm.PrimitiveId;
@@ -32,5 +31,5 @@
  * @since 1670
  */
-public abstract class HistoryOsmPrimitive implements Tagged, Comparable<HistoryOsmPrimitive> {
+public abstract class HistoryOsmPrimitive implements Tagged, Comparable<HistoryOsmPrimitive>, PrimitiveId {
 
     private long id;
@@ -204,9 +203,13 @@
     }
 
-    /**
-     * Returns the primitive type.
-     * @return the primitive type
-     */
-    public abstract OsmPrimitiveType getType();
+    @Override
+    public final long getUniqueId() {
+        return getId();
+    }
+
+    @Override
+    public final boolean isNew() {
+        return false;
+    }
 
     @Override
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetContentPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetContentPanel.java	(revision 16122)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetContentPanel.java	(revision 16123)
@@ -24,4 +24,5 @@
 import javax.swing.DefaultListSelectionModel;
 import javax.swing.JButton;
+import javax.swing.JComponent;
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
@@ -36,4 +37,5 @@
 
 import org.openstreetmap.josm.actions.AutoScaleAction;
+import org.openstreetmap.josm.actions.HistoryInfoAction;
 import org.openstreetmap.josm.actions.downloadtasks.ChangesetContentDownloadTask;
 import org.openstreetmap.josm.data.osm.Changeset;
@@ -127,4 +129,7 @@
         tblContent.setAutoCreateRowSorter(true);
         tblContent.addMouseListener(new PopupMenuLauncher(new ChangesetContentTablePopupMenu()));
+        HistoryInfoAction historyAction = MainApplication.getMenu().historyinfo;
+        tblContent.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(historyAction.getShortcut().getKeyStroke(), "historyAction");
+        tblContent.getActionMap().put("historyAction", historyAction);
         pnl.add(new JScrollPane(tblContent), BorderLayout.CENTER);
         return pnl;
Index: trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserDialogManager.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserDialogManager.java	(revision 16122)
+++ trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserDialogManager.java	(revision 16123)
@@ -4,4 +4,5 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
+import java.awt.Component;
 import java.awt.Dimension;
 import java.awt.Point;
@@ -29,4 +30,5 @@
 import org.openstreetmap.josm.gui.util.WindowGeometry;
 import org.openstreetmap.josm.tools.JosmRuntimeException;
+import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.SubclassFilteredCollection;
 import org.openstreetmap.josm.tools.bugreport.BugReportExceptionHandler;
@@ -57,5 +59,5 @@
     private static HistoryBrowserDialogManager instance;
 
-    private final Map<Long, HistoryBrowserDialog> dialogs;
+    private final Map<Long, HistoryBrowserDialog> dialogs = new HashMap<>();
 
     private final Predicate<PrimitiveId> unloadedHistoryPredicate = new UnloadedHistoryPredicate();
@@ -66,5 +68,4 @@
 
     protected HistoryBrowserDialogManager() {
-        dialogs = new HashMap<>();
         MainApplication.getLayerManager().addLayerChangeListener(this);
     }
@@ -147,12 +148,8 @@
     /**
      * Hides and destroys all currently visible history browser dialogs
-     *
+     * @since 2448
      */
     public void hideAll() {
-        List<HistoryBrowserDialog> dialogs = new ArrayList<>();
-        dialogs.addAll(this.dialogs.values());
-        for (HistoryBrowserDialog dialog: dialogs) {
-            hide(dialog);
-        }
+        dialogs.values().forEach(this::hide);
     }
 
@@ -160,4 +157,5 @@
      * Show history dialog for the given history.
      * @param h History to show
+     * @since 2448
      */
     public void show(History h) {
@@ -167,6 +165,5 @@
             show(h.getId());
         } else {
-            HistoryBrowserDialog dialog = new HistoryBrowserDialog(h);
-            show(h.getId(), dialog);
+            show(h.getId(), new HistoryBrowserDialog(h));
         }
     }
@@ -218,4 +215,14 @@
      */
     public void showHistory(final Collection<? extends PrimitiveId> primitives) {
+        showHistory(MainApplication.getMainFrame(), primitives);
+    }
+
+    /**
+     * Show history dialog(s) for the given primitive(s).
+     * @param parent Parent component for displayed dialog boxes
+     * @param primitives The primitive(s) for which history will be displayed
+     * @since 16123
+     */
+    public void showHistory(Component parent, final Collection<? extends PrimitiveId> primitives) {
         final List<PrimitiveId> realPrimitives = new ArrayList<>(primitives);
         hooks.forEach(h -> h.modifyRequestedIds(realPrimitives));
@@ -223,5 +230,5 @@
         if (notNewPrimitives.isEmpty()) {
             JOptionPane.showMessageDialog(
-                    MainApplication.getMainFrame(),
+                    parent,
                     tr("Please select at least one already uploaded node, way, or relation."),
                     tr("Warning"),
@@ -230,11 +237,7 @@
         }
 
-        Collection<? extends PrimitiveId> toLoad = SubclassFilteredCollection.filter(realPrimitives, unloadedHistoryPredicate);
+        Collection<? extends PrimitiveId> toLoad = SubclassFilteredCollection.filter(notNewPrimitives, unloadedHistoryPredicate);
         if (!toLoad.isEmpty()) {
-            HistoryLoadTask task = new HistoryLoadTask();
-            for (PrimitiveId p : notNewPrimitives) {
-                task.add(p);
-            }
-            MainApplication.worker.submit(task);
+            MainApplication.worker.submit(new HistoryLoadTask(parent).addPrimitiveIds(toLoad));
         }
 
@@ -244,4 +247,5 @@
                     final History h = HistoryDataSet.getInstance().getHistory(p);
                     if (h == null) {
+                        Logging.warn("{0} not found in HistoryDataSet", p);
                         continue;
                     }
Index: trunk/src/org/openstreetmap/josm/gui/history/HistoryLoadTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/history/HistoryLoadTask.java	(revision 16122)
+++ trunk/src/org/openstreetmap/josm/gui/history/HistoryLoadTask.java	(revision 16123)
@@ -11,4 +11,5 @@
 import java.util.HashSet;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
@@ -68,9 +69,8 @@
      * parent for {@link org.openstreetmap.josm.gui.PleaseWaitDialog}.
      * Must not be <code>null</code>.
-     * @throws IllegalArgumentException if parent is <code>null</code>
+     * @throws NullPointerException if parent is <code>null</code>
      */
     public HistoryLoadTask(Component parent) {
-        super(parent, tr("Load history"), true);
-        CheckParameterUtil.ensureParameterNotNull(parent, "parent");
+        super(Objects.requireNonNull(parent, "parent"), tr("Load history"), true);
     }
 
@@ -92,8 +92,7 @@
      * @param primitive the history item
      * @return this task
-     * @throws IllegalArgumentException if primitive is null
+     * @throws NullPointerException if primitive is null
      */
     public HistoryLoadTask add(HistoryOsmPrimitive primitive) {
-        CheckParameterUtil.ensureParameterNotNull(primitive, "primitive");
         return add(primitive.getPrimitiveId());
     }
@@ -104,8 +103,7 @@
      * @param history the history. Must not be null.
      * @return this task
-     * @throws IllegalArgumentException if history is null
+     * @throws NullPointerException if history is null
      */
     public HistoryLoadTask add(History history) {
-        CheckParameterUtil.ensureParameterNotNull(history, "history");
         return add(history.getPrimitiveId());
     }
@@ -116,5 +114,5 @@
      * @param primitive the OSM primitive. Must not be null. primitive.getOsmId() &gt; 0 required.
      * @return this task
-     * @throws IllegalArgumentException if the primitive is null
+     * @throws NullPointerException if the primitive is null
      * @throws IllegalArgumentException if primitive.getOsmId() &lt;= 0
      */
@@ -130,14 +128,25 @@
      * <code>primitive.getId() &gt; 0</code> required.
      * @return this task
-     * @throws IllegalArgumentException if primitives is <code>null</code>
+     * @throws NullPointerException if primitives is null
      * @throws IllegalArgumentException if one of the ids in the collection &lt;= 0
-     */
-    public HistoryLoadTask add(Collection<? extends OsmPrimitive> primitives) {
-        CheckParameterUtil.ensureParameterNotNull(primitives, "primitives");
-        for (OsmPrimitive primitive: primitives) {
-            if (primitive != null) {
-                add(primitive);
-            }
-        }
+     * @since 16123
+     */
+    public HistoryLoadTask addPrimitiveIds(Collection<? extends PrimitiveId> primitives) {
+        primitives.forEach(this::add);
+        return this;
+    }
+
+    /**
+     * Adds a collection of objects to loaded, specified by a collection of OSM primitives.
+     *
+     * @param primitives the OSM primitives. Must not be <code>null</code>.
+     * <code>primitive.getId() &gt; 0</code> required.
+     * @return this task
+     * @throws NullPointerException if primitives is null
+     * @throws IllegalArgumentException if one of the ids in the collection &lt;= 0
+     * @since 16123
+     */
+    public HistoryLoadTask addOsmPrimitives(Collection<? extends OsmPrimitive> primitives) {
+        primitives.forEach(this::add);
         return this;
     }
