001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.history; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.BorderLayout; 007import java.awt.Dimension; 008import java.util.Arrays; 009 010import javax.swing.JPanel; 011import javax.swing.JScrollPane; 012import javax.swing.JSplitPane; 013import javax.swing.JTabbedPane; 014 015import org.openstreetmap.josm.data.osm.OsmPrimitive; 016import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 017import org.openstreetmap.josm.data.osm.history.History; 018import org.openstreetmap.josm.tools.Destroyable; 019 020/** 021 * HistoryBrowser is an UI component which displays history information about an {@link OsmPrimitive}. 022 * 023 * @since 1709 024 */ 025public class HistoryBrowser extends JPanel implements Destroyable { 026 027 /** the model */ 028 private transient HistoryBrowserModel model; 029 private TagInfoViewer tagInfoViewer; 030 private NodeListViewer nodeListViewer; 031 private RelationMemberListViewer relationMemberListViewer; 032 private CoordinateInfoViewer coordinateInfoViewer; 033 private JTabbedPane tpViewers; 034 035 /** 036 * Constructs a new {@code HistoryBrowser}. 037 */ 038 public HistoryBrowser() { 039 model = new HistoryBrowserModel(); 040 build(); 041 } 042 043 /** 044 * Constructs a new {@code HistoryBrowser}. 045 * @param history the history of an {@link OsmPrimitive} 046 */ 047 public HistoryBrowser(History history) { 048 this(); 049 populate(history); 050 } 051 052 /** 053 * creates the table which shows the list of versions 054 * 055 * @return the panel with the version table 056 */ 057 protected JPanel createVersionTablePanel() { 058 JPanel pnl = new JPanel(new BorderLayout()); 059 pnl.add(new JScrollPane(new VersionTable(model)), BorderLayout.CENTER); 060 return pnl; 061 } 062 063 /** 064 * creates the panel which shows information about two different versions 065 * of the same {@link OsmPrimitive}. 066 * 067 * @return the panel 068 */ 069 protected JPanel createVersionComparePanel() { 070 tpViewers = new JTabbedPane(); 071 072 // create the viewers, but don't add them yet. 073 // see populate() 074 // 075 tagInfoViewer = new TagInfoViewer(model); 076 nodeListViewer = new NodeListViewer(model); 077 relationMemberListViewer = new RelationMemberListViewer(model); 078 coordinateInfoViewer = new CoordinateInfoViewer(model); 079 JPanel pnl = new JPanel(new BorderLayout()); 080 pnl.add(tpViewers, BorderLayout.CENTER); 081 082 tpViewers.addChangeListener(e -> { 083 if (tpViewers.getSelectedComponent() == coordinateInfoViewer) { 084 // while building the component size is not yet known, thus panning does not give reasonable results 085 coordinateInfoViewer.setDisplayToFitMapMarkers(); 086 } 087 }); 088 089 return pnl; 090 } 091 092 /** 093 * builds the GUI 094 */ 095 protected void build() { 096 JPanel left = createVersionTablePanel(); 097 JPanel right = createVersionComparePanel(); 098 setLayout(new BorderLayout()); 099 JSplitPane pane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, left, right); 100 add(pane, BorderLayout.CENTER); 101 102 pane.setOneTouchExpandable(true); 103 pane.setDividerLocation(300); 104 105 Dimension minimumSize = new Dimension(100, 50); 106 left.setMinimumSize(minimumSize); 107 right.setMinimumSize(minimumSize); 108 } 109 110 /** 111 * populates the browser with the history of a specific {@link OsmPrimitive} 112 * 113 * @param history the history 114 */ 115 public void populate(History history) { 116 boolean samePrimitive = model.isSamePrimitive(history); // needs to be before setHistory 117 model.setHistory(history); 118 if (samePrimitive) { 119 // no need to rebuild the UI 120 return; 121 } 122 123 tpViewers.removeAll(); 124 125 tpViewers.add(tagInfoViewer); 126 tpViewers.setTitleAt(0, tr("Tags")); 127 128 if (history.getEarliest().getType() == OsmPrimitiveType.NODE) { 129 tpViewers.add(coordinateInfoViewer); 130 tpViewers.setTitleAt(1, tr("Coordinates")); 131 } else if (history.getEarliest().getType() == OsmPrimitiveType.WAY) { 132 tpViewers.add(nodeListViewer); 133 tpViewers.setTitleAt(1, tr("Nodes")); 134 } else if (history.getEarliest().getType() == OsmPrimitiveType.RELATION) { 135 tpViewers.add(relationMemberListViewer); 136 tpViewers.setTitleAt(1, tr("Members")); 137 } 138 revalidate(); 139 } 140 141 /** 142 * replies the {@link History} currently displayed by this browser 143 * 144 * @return the current history 145 */ 146 public History getHistory() { 147 return model.getHistory(); 148 } 149 150 /** 151 * replies the model used by this browser 152 * @return the model 153 */ 154 public HistoryBrowserModel getModel() { 155 return model; 156 } 157 158 @Override 159 public void destroy() { 160 if (model != null) { 161 model.unlinkAsListener(); 162 model = null; 163 } 164 Arrays.asList(tagInfoViewer, nodeListViewer, relationMemberListViewer, coordinateInfoViewer).stream() 165 .filter(Destroyable.class::isInstance).forEach(Destroyable::destroy); 166 tagInfoViewer = null; 167 nodeListViewer = null; 168 relationMemberListViewer = null; 169 coordinateInfoViewer = null; 170 } 171}