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