// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.gui.dialogs;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.DefaultListSelectionModel;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.data.SelectionChangedListener;
import org.openstreetmap.josm.data.osm.DataSet;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.data.osm.PrimitiveId;
import org.openstreetmap.josm.data.osm.history.HistoryDataSet;
import org.openstreetmap.josm.data.osm.history.HistoryDataSetListener;
import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
import org.openstreetmap.josm.gui.SideButton;
import org.openstreetmap.josm.gui.help.HelpUtil;
import org.openstreetmap.josm.gui.history.HistoryBrowserDialogManager;
import org.openstreetmap.josm.gui.history.HistoryLoadTask;
import org.openstreetmap.josm.tools.ImageProvider;
import org.openstreetmap.josm.tools.InputMapUtils;
import org.openstreetmap.josm.tools.Shortcut;
/**
* HistoryDialog displays a list of the currently selected primitives and provides
* two actions for (1) (re)loading the history of the selected primitives and (2)
* for launching a history browser for each selected primitive.
*
*/
public class HistoryDialog extends ToggleDialog implements HistoryDataSetListener {
/** the table model */
protected HistoryItemTableModel model;
/** the table with the history items */
protected JTable historyTable;
protected ShowHistoryAction showHistoryAction;
protected ReloadAction reloadAction;
/**
* Constructs a new {@code HistoryDialog}.
*/
public HistoryDialog() {
super(tr("History"), "history", tr("Display the history of all selected items."),
Shortcut.registerShortcut("subwindow:history", tr("Toggle: {0}", tr("History")), KeyEvent.VK_H,
Shortcut.ALT_SHIFT), 150);
build();
HelpUtil.setHelpContext(this, HelpUtil.ht("/Dialog/History"));
}
/**
* builds the GUI
*/
protected void build() {
DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
historyTable = new JTable(
model = new HistoryItemTableModel(selectionModel),
new HistoryTableColumnModel(),
selectionModel
);
historyTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
final TableCellRenderer oldRenderer = historyTable.getTableHeader().getDefaultRenderer();
historyTable.getTableHeader().setDefaultRenderer(new DefaultTableCellRenderer(){
@Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
JComponent c = (JComponent)oldRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (!"".equals(value))
return c;
JLabel l = new JLabel(ImageProvider.get("misc","showhide"));
l.setForeground(c.getForeground());
l.setBackground(c.getBackground());
l.setFont(c.getFont());
l.setBorder(c.getBorder());
l.setOpaque(true);
return l;
}
});
historyTable.addMouseListener(new ShowHistoryMouseAdapter());
historyTable.setTableHeader(null);
createLayout(historyTable, true, Arrays.asList(new SideButton[] {
new SideButton(reloadAction = new ReloadAction()),
new SideButton(showHistoryAction = new ShowHistoryAction())
}));
// wire actions
//
historyTable.getSelectionModel().addListSelectionListener(showHistoryAction);
historyTable.getSelectionModel().addListSelectionListener(reloadAction);
// Show history dialog on Enter/Spacebar
InputMapUtils.addEnterAction(historyTable, showHistoryAction);
InputMapUtils.addSpacebarAction(historyTable, showHistoryAction);
}
@Override
public void showNotify() {
HistoryDataSet.getInstance().addHistoryDataSetListener(this);
DataSet.addSelectionListener(model);
if (Main.main.getCurrentDataSet() == null) {
model.selectionChanged(null);
} else {
model.selectionChanged(Main.main.getCurrentDataSet().getAllSelected());
}
}
@Override
public void hideNotify() {
HistoryDataSet.getInstance().removeHistoryDataSetListener(this);
DataSet.removeSelectionListener(model);
}
/* ----------------------------------------------------------------------------- */
/* interface HistoryDataSetListener */
/* ----------------------------------------------------------------------------- */
@Override
public void historyUpdated(HistoryDataSet source, PrimitiveId primitiveId) {
model.refresh();
}
@Override
public void historyDataSetCleared(HistoryDataSet source) {
model.refresh();
}
/**
* The table model with the history items
*
*/
static class HistoryItemTableModel extends DefaultTableModel implements SelectionChangedListener{
private List<OsmPrimitive> data;
private DefaultListSelectionModel selectionModel;
public HistoryItemTableModel(DefaultListSelectionModel selectionModel) {
data = new ArrayList<>();
this.selectionModel = selectionModel;
}
@Override
public int getRowCount() {
if (data == null)
return 0;
return data.size();
}
@Override
public Object getValueAt(int row, int column) {
return data.get(row);
}
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
protected List<OsmPrimitive> getSelectedPrimitives() {
List<OsmPrimitive> ret = new ArrayList<>();
for (int i=0; i< data.size(); i++) {
if (selectionModel.isSelectedIndex(i)) {
ret.add(data.get(i));
}
}
return ret;
}
protected void selectPrimitives(Collection<OsmPrimitive> primitives) {
for (OsmPrimitive p: primitives) {
int idx = data.indexOf(p);
if (idx < 0) {
continue;
}
selectionModel.addSelectionInterval(idx, idx);
}
}
public void refresh() {
List<OsmPrimitive> selectedPrimitives = getSelectedPrimitives();
data.clear();
if (Main.main.getCurrentDataSet() == null)
return;
for (OsmPrimitive primitive: Main.main.getCurrentDataSet().getAllSelected()) {
if (primitive.isNew()) {
continue;
}
data.add(primitive);
}
fireTableDataChanged();
selectPrimitives(selectedPrimitives);
}
@Override
public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
data.clear();
selectionModel.clearSelection();
if (newSelection != null && !newSelection.isEmpty()) {
for (OsmPrimitive primitive: newSelection) {
if (primitive.isNew()) {
continue;
}
data.add(primitive);
}
}
fireTableDataChanged();
selectionModel.addSelectionInterval(0, data.size()-1);
}
public List<OsmPrimitive> getPrimitives(int [] rows) {
if (rows == null || rows.length == 0) return Collections.emptyList();
List<OsmPrimitive> ret = new ArrayList<>(rows.length);
for (int row: rows) {
ret.add(data.get(row));
}
return ret;
}
public OsmPrimitive getPrimitive(int row) {
return data.get(row);
}
}
/**
* The column model
*/
static class HistoryTableColumnModel extends DefaultTableColumnModel {
protected void createColumns() {
TableColumn col = null;
OsmPrimitivRenderer renderer = new OsmPrimitivRenderer();
// column 0 - History item
col = new TableColumn(0);
col.setHeaderValue(tr("Object with history"));
col.setCellRenderer(renderer);
addColumn(col);
}
public HistoryTableColumnModel() {
createColumns();
}
}
/**
* The action for reloading history information of the currently selected primitives.
*
*/
class ReloadAction extends AbstractAction implements ListSelectionListener {
public ReloadAction() {
putValue(Action.SMALL_ICON, ImageProvider.get("dialogs","refresh"));
putValue(Action.NAME, tr("Reload"));
putValue(Action.SHORT_DESCRIPTION, tr("Reload all currently selected objects and refresh the list."));
updateEnabledState();
}
@Override
public void actionPerformed(ActionEvent e) {
int [] rows = historyTable.getSelectedRows();
if (rows == null || rows.length == 0) return;
List<OsmPrimitive> selectedItems = model.getPrimitives(rows);
HistoryLoadTask task = new HistoryLoadTask();
task.add(selectedItems);
Main.worker.execute(task);
}
protected void updateEnabledState() {
setEnabled(historyTable.getSelectedRowCount() > 0);
}
@Override
public void valueChanged(ListSelectionEvent e) {
updateEnabledState();
}
}
class ShowHistoryMouseAdapter extends MouseAdapter {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
int row = historyTable.rowAtPoint(e.getPoint());
HistoryBrowserDialogManager.getInstance().showHistory(Collections.singletonList(model.getPrimitive(row)));
}
}
}
/**
* The action for showing history information of the current history item.
*/
class ShowHistoryAction extends AbstractAction implements ListSelectionListener {
public ShowHistoryAction() {
putValue(Action.SMALL_ICON, ImageProvider.get("dialogs","history"));
putValue(Action.NAME, tr("Show"));
putValue(Action.SHORT_DESCRIPTION, tr("Display the history of the selected objects."));
updateEnabledState();
}
@Override
public void actionPerformed(ActionEvent e) {
int [] rows = historyTable.getSelectedRows();
if (rows == null || rows.length == 0) return;
HistoryBrowserDialogManager.getInstance().showHistory(model.getPrimitives(rows));
}
protected void updateEnabledState() {
setEnabled(historyTable.getSelectedRowCount() > 0);
}
@Override
public void valueChanged(ListSelectionEvent e) {
updateEnabledState();
}
}
}