001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.dialogs; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.Component; 007import java.awt.event.ActionEvent; 008import java.awt.event.KeyEvent; 009import java.awt.event.MouseAdapter; 010import java.awt.event.MouseEvent; 011import java.util.ArrayList; 012import java.util.Arrays; 013import java.util.Collection; 014import java.util.Collections; 015import java.util.List; 016 017import javax.swing.AbstractAction; 018import javax.swing.Action; 019import javax.swing.DefaultListSelectionModel; 020import javax.swing.JComponent; 021import javax.swing.JLabel; 022import javax.swing.JTable; 023import javax.swing.ListSelectionModel; 024import javax.swing.event.ListSelectionEvent; 025import javax.swing.event.ListSelectionListener; 026import javax.swing.table.DefaultTableCellRenderer; 027import javax.swing.table.DefaultTableColumnModel; 028import javax.swing.table.DefaultTableModel; 029import javax.swing.table.TableCellRenderer; 030import javax.swing.table.TableColumn; 031 032import org.openstreetmap.josm.Main; 033import org.openstreetmap.josm.data.SelectionChangedListener; 034import org.openstreetmap.josm.data.osm.DataSet; 035import org.openstreetmap.josm.data.osm.OsmPrimitive; 036import org.openstreetmap.josm.data.osm.PrimitiveId; 037import org.openstreetmap.josm.data.osm.history.HistoryDataSet; 038import org.openstreetmap.josm.data.osm.history.HistoryDataSetListener; 039import org.openstreetmap.josm.gui.OsmPrimitivRenderer; 040import org.openstreetmap.josm.gui.SideButton; 041import org.openstreetmap.josm.gui.help.HelpUtil; 042import org.openstreetmap.josm.gui.history.HistoryBrowserDialogManager; 043import org.openstreetmap.josm.gui.history.HistoryLoadTask; 044import org.openstreetmap.josm.io.OnlineResource; 045import org.openstreetmap.josm.tools.ImageProvider; 046import org.openstreetmap.josm.tools.InputMapUtils; 047import org.openstreetmap.josm.tools.Shortcut; 048 049/** 050 * HistoryDialog displays a list of the currently selected primitives and provides 051 * two actions for (1) (re)loading the history of the selected primitives and (2) 052 * for launching a history browser for each selected primitive. 053 * 054 */ 055public class HistoryDialog extends ToggleDialog implements HistoryDataSetListener { 056 057 /** the table model */ 058 protected HistoryItemTableModel model; 059 /** the table with the history items */ 060 protected JTable historyTable; 061 062 protected ShowHistoryAction showHistoryAction; 063 protected ReloadAction reloadAction; 064 065 /** 066 * Constructs a new {@code HistoryDialog}. 067 */ 068 public HistoryDialog() { 069 super(tr("History"), "history", tr("Display the history of all selected items."), 070 Shortcut.registerShortcut("subwindow:history", tr("Toggle: {0}", tr("History")), KeyEvent.VK_H, 071 Shortcut.ALT_SHIFT), 150); 072 build(); 073 HelpUtil.setHelpContext(this, HelpUtil.ht("/Dialog/History")); 074 } 075 076 /** 077 * builds the GUI 078 */ 079 protected void build() { 080 DefaultListSelectionModel selectionModel = new DefaultListSelectionModel(); 081 historyTable = new JTable( 082 model = new HistoryItemTableModel(selectionModel), 083 new HistoryTableColumnModel(), 084 selectionModel 085 ); 086 historyTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 087 final TableCellRenderer oldRenderer = historyTable.getTableHeader().getDefaultRenderer(); 088 historyTable.getTableHeader().setDefaultRenderer(new DefaultTableCellRenderer(){ 089 @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 090 JComponent c = (JComponent)oldRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 091 if (!"".equals(value)) 092 return c; 093 JLabel l = new JLabel(ImageProvider.get("misc","showhide")); 094 l.setForeground(c.getForeground()); 095 l.setBackground(c.getBackground()); 096 l.setFont(c.getFont()); 097 l.setBorder(c.getBorder()); 098 l.setOpaque(true); 099 return l; 100 } 101 }); 102 historyTable.addMouseListener(new ShowHistoryMouseAdapter()); 103 historyTable.setTableHeader(null); 104 105 createLayout(historyTable, true, Arrays.asList(new SideButton[] { 106 new SideButton(reloadAction = new ReloadAction()), 107 new SideButton(showHistoryAction = new ShowHistoryAction()) 108 })); 109 110 // wire actions 111 // 112 historyTable.getSelectionModel().addListSelectionListener(showHistoryAction); 113 historyTable.getSelectionModel().addListSelectionListener(reloadAction); 114 115 // Show history dialog on Enter/Spacebar 116 InputMapUtils.addEnterAction(historyTable, showHistoryAction); 117 InputMapUtils.addSpacebarAction(historyTable, showHistoryAction); 118 } 119 120 @Override 121 public void showNotify() { 122 HistoryDataSet.getInstance().addHistoryDataSetListener(this); 123 DataSet.addSelectionListener(model); 124 if (Main.main.getCurrentDataSet() == null) { 125 model.selectionChanged(null); 126 } else { 127 model.selectionChanged(Main.main.getCurrentDataSet().getAllSelected()); 128 } 129 } 130 131 @Override 132 public void hideNotify() { 133 HistoryDataSet.getInstance().removeHistoryDataSetListener(this); 134 DataSet.removeSelectionListener(model); 135 } 136 137 /* ----------------------------------------------------------------------------- */ 138 /* interface HistoryDataSetListener */ 139 /* ----------------------------------------------------------------------------- */ 140 @Override 141 public void historyUpdated(HistoryDataSet source, PrimitiveId primitiveId) { 142 model.refresh(); 143 } 144 145 @Override 146 public void historyDataSetCleared(HistoryDataSet source) { 147 model.refresh(); 148 } 149 150 /** 151 * The table model with the history items 152 * 153 */ 154 static class HistoryItemTableModel extends DefaultTableModel implements SelectionChangedListener{ 155 private List<OsmPrimitive> data; 156 private DefaultListSelectionModel selectionModel; 157 158 public HistoryItemTableModel(DefaultListSelectionModel selectionModel) { 159 data = new ArrayList<>(); 160 this.selectionModel = selectionModel; 161 } 162 163 @Override 164 public int getRowCount() { 165 if (data == null) 166 return 0; 167 return data.size(); 168 } 169 170 @Override 171 public Object getValueAt(int row, int column) { 172 return data.get(row); 173 } 174 175 @Override 176 public boolean isCellEditable(int row, int column) { 177 return false; 178 } 179 180 protected List<OsmPrimitive> getSelectedPrimitives() { 181 List<OsmPrimitive> ret = new ArrayList<>(); 182 for (int i=0; i< data.size(); i++) { 183 if (selectionModel.isSelectedIndex(i)) { 184 ret.add(data.get(i)); 185 } 186 } 187 return ret; 188 } 189 190 protected void selectPrimitives(Collection<OsmPrimitive> primitives) { 191 for (OsmPrimitive p: primitives) { 192 int idx = data.indexOf(p); 193 if (idx < 0) { 194 continue; 195 } 196 selectionModel.addSelectionInterval(idx, idx); 197 } 198 } 199 200 public void refresh() { 201 List<OsmPrimitive> selectedPrimitives = getSelectedPrimitives(); 202 data.clear(); 203 if (Main.main.getCurrentDataSet() == null) 204 return; 205 for (OsmPrimitive primitive: Main.main.getCurrentDataSet().getAllSelected()) { 206 if (primitive.isNew()) { 207 continue; 208 } 209 data.add(primitive); 210 } 211 fireTableDataChanged(); 212 selectPrimitives(selectedPrimitives); 213 } 214 215 @Override 216 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) { 217 data.clear(); 218 selectionModel.clearSelection(); 219 if (newSelection != null && !newSelection.isEmpty()) { 220 for (OsmPrimitive primitive: newSelection) { 221 if (primitive.isNew()) { 222 continue; 223 } 224 data.add(primitive); 225 } 226 } 227 fireTableDataChanged(); 228 selectionModel.addSelectionInterval(0, data.size()-1); 229 } 230 231 public List<OsmPrimitive> getPrimitives(int [] rows) { 232 if (rows == null || rows.length == 0) return Collections.emptyList(); 233 List<OsmPrimitive> ret = new ArrayList<>(rows.length); 234 for (int row: rows) { 235 ret.add(data.get(row)); 236 } 237 return ret; 238 } 239 240 public OsmPrimitive getPrimitive(int row) { 241 return data.get(row); 242 } 243 } 244 245 /** 246 * The column model 247 */ 248 static class HistoryTableColumnModel extends DefaultTableColumnModel { 249 protected void createColumns() { 250 TableColumn col = null; 251 OsmPrimitivRenderer renderer = new OsmPrimitivRenderer(); 252 // column 0 - History item 253 col = new TableColumn(0); 254 col.setHeaderValue(tr("Object with history")); 255 col.setCellRenderer(renderer); 256 addColumn(col); 257 } 258 259 public HistoryTableColumnModel() { 260 createColumns(); 261 } 262 } 263 264 /** 265 * The action for reloading history information of the currently selected primitives. 266 * 267 */ 268 class ReloadAction extends AbstractAction implements ListSelectionListener { 269 270 public ReloadAction() { 271 putValue(Action.SMALL_ICON, ImageProvider.get("dialogs","refresh")); 272 putValue(Action.NAME, tr("Reload")); 273 putValue(Action.SHORT_DESCRIPTION, tr("Reload all currently selected objects and refresh the list.")); 274 updateEnabledState(); 275 } 276 277 @Override 278 public void actionPerformed(ActionEvent e) { 279 int [] rows = historyTable.getSelectedRows(); 280 if (rows == null || rows.length == 0) return; 281 282 List<OsmPrimitive> selectedItems = model.getPrimitives(rows); 283 HistoryLoadTask task = new HistoryLoadTask(); 284 task.add(selectedItems); 285 Main.worker.execute(task); 286 } 287 288 protected void updateEnabledState() { 289 setEnabled(historyTable.getSelectedRowCount() > 0 && !Main.isOffline(OnlineResource.OSM_API)); 290 } 291 292 @Override 293 public void valueChanged(ListSelectionEvent e) { 294 updateEnabledState(); 295 } 296 } 297 298 class ShowHistoryMouseAdapter extends MouseAdapter { 299 @Override 300 public void mouseClicked(MouseEvent e) { 301 if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) { 302 int row = historyTable.rowAtPoint(e.getPoint()); 303 HistoryBrowserDialogManager.getInstance().showHistory(Collections.singletonList(model.getPrimitive(row))); 304 } 305 } 306 } 307 308 /** 309 * The action for showing history information of the current history item. 310 */ 311 class ShowHistoryAction extends AbstractAction implements ListSelectionListener { 312 public ShowHistoryAction() { 313 putValue(Action.SMALL_ICON, ImageProvider.get("dialogs","history")); 314 putValue(Action.NAME, tr("Show")); 315 putValue(Action.SHORT_DESCRIPTION, tr("Display the history of the selected objects.")); 316 updateEnabledState(); 317 } 318 319 @Override 320 public void actionPerformed(ActionEvent e) { 321 int [] rows = historyTable.getSelectedRows(); 322 if (rows == null || rows.length == 0) return; 323 HistoryBrowserDialogManager.getInstance().showHistory(model.getPrimitives(rows)); 324 } 325 326 protected void updateEnabledState() { 327 setEnabled(historyTable.getSelectedRowCount() > 0); 328 } 329 330 @Override 331 public void valueChanged(ListSelectionEvent e) { 332 updateEnabledState(); 333 } 334 } 335}