// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.gui.dialogs.relation;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JPopupMenu;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.actions.AutoScaleAction;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.gui.MapView;
import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
import org.openstreetmap.josm.gui.layer.Layer;
import org.openstreetmap.josm.gui.layer.OsmDataLayer;
public class MemberTable extends JTable implements IMemberModelListener {
/**
* the data layer in whose context relation members are edited in this table
*/
protected OsmDataLayer layer;
/** the popup menu */
protected JPopupMenu popupMenu;
private ZoomToAction zoomToAction;
/**
* constructor
*
* @param model
* @param columnModel
*/
public MemberTable(OsmDataLayer layer, MemberTableModel model) {
super(model, new MemberTableColumnModel(), model.getSelectionModel());
this.layer = layer;
model.addMemberModelListener(this);
init();
}
/**
* initialize the table
*/
protected void init() {
setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
// make ENTER behave like TAB
//
getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false), "selectNextColumnCell");
// install custom navigation actions
//
getActionMap().put("selectNextColumnCell", new SelectNextColumnCellAction());
getActionMap().put("selectPreviousColumnCell", new SelectPreviousColumnCellAction());
addMouseListener(new PopupListener());
addMouseListener(new DblClickHandler());
}
@Override
public Dimension getPreferredSize(){
Container c = getParent();
while(c != null && ! (c instanceof JViewport)) {
c = c.getParent();
}
if (c != null) {
Dimension d = super.getPreferredSize();
d.width = c.getSize().width;
return d;
}
return super.getPreferredSize();
}
public void makeMemberVisible(int index) {
scrollRectToVisible(getCellRect(index, 0, true));
}
/**
* Action to be run when the user navigates to the next cell in the table, for instance by
* pressing TAB or ENTER. The action alters the standard navigation path from cell to cell: <ul>
* <li>it jumps over cells in the first column</li> <li>it automatically add a new empty row
* when the user leaves the last cell in the table</li> <ul>
*
*
*/
class SelectNextColumnCellAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
run();
}
public void run() {
int col = getSelectedColumn();
int row = getSelectedRow();
if (getCellEditor() != null) {
getCellEditor().stopCellEditing();
}
if (col == 0 && row < getRowCount() - 1) {
row++;
} else if (row < getRowCount() - 1) {
col = 0;
row++;
}
changeSelection(row, col, false, false);
}
}
/**
* Action to be run when the user navigates to the previous cell in the table, for instance by
* pressing Shift-TAB
*
*/
class SelectPreviousColumnCellAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
int col = getSelectedColumn();
int row = getSelectedRow();
if (getCellEditor() != null) {
getCellEditor().stopCellEditing();
}
if (col <= 0 && row <= 0) {
// change nothing
} else if (row > 0) {
col = 0;
row--;
}
changeSelection(row, col, false, false);
}
}
/**
* Replies the popup menu for this table
*
* @return the popup menu
*/
protected JPopupMenu getPopUpMenu() {
if (popupMenu == null) {
popupMenu = new JPopupMenu();
zoomToAction = new ZoomToAction();
MapView.addLayerChangeListener(zoomToAction);
getSelectionModel().addListSelectionListener(zoomToAction);
popupMenu.add(zoomToAction);
}
return popupMenu;
}
public void unlinkAsListener() {
MapView.removeLayerChangeListener(zoomToAction);
}
class PopupListener extends MouseAdapter {
@Override
public void mousePressed(MouseEvent e) {
showPopup(e);
}
@Override
public void mouseReleased(MouseEvent e) {
showPopup(e);
}
private void showPopup(MouseEvent e) {
if (e.isPopupTrigger()) {
getPopUpMenu().show(e.getComponent(), e.getX(), e.getY());
}
}
}
class ZoomToAction extends AbstractAction implements LayerChangeListener, ListSelectionListener {
public ZoomToAction() {
putValue(NAME, tr("Zoom to"));
putValue(SHORT_DESCRIPTION, tr("Zoom to the object the first selected member refers to"));
updateEnabledState();
}
public void actionPerformed(ActionEvent e) {
if (! isEnabled())
return;
int rows[] = getSelectedRows();
if (rows == null || rows.length == 0)
return;
int row = rows[0];
OsmPrimitive primitive = getMemberTableModel().getReferredPrimitive(row);
layer.data.setSelected(primitive);
AutoScaleAction action = new AutoScaleAction("selection");
action.autoScale();
}
protected void updateEnabledState() {
if (Main.main == null || Main.main.getEditLayer() != layer) {
setEnabled(false);
putValue(SHORT_DESCRIPTION, tr("Zooming disabled because layer of this relation is not active"));
return;
}
if (getSelectedRowCount() == 0) {
setEnabled(false);
putValue(SHORT_DESCRIPTION, tr("Zooming disabled because there is no selected member"));
return;
}
setEnabled(true);
putValue(SHORT_DESCRIPTION, tr("Zoom to the object the first selected member refers to"));
}
public void valueChanged(ListSelectionEvent e) {
updateEnabledState();
}
public void activeLayerChange(Layer oldLayer, Layer newLayer) {
updateEnabledState();
}
public void layerAdded(Layer newLayer) {
updateEnabledState();
}
public void layerRemoved(Layer oldLayer) {
updateEnabledState();
}
}
protected MemberTableModel getMemberTableModel() {
return (MemberTableModel) getModel();
}
class DblClickHandler extends MouseAdapter {
protected void setSelection(MouseEvent e) {
int row = rowAtPoint(e.getPoint());
if (row < 0) return;
OsmPrimitive primitive = getMemberTableModel().getReferredPrimitive(row);
getMemberTableModel().getLayer().data.setSelected(primitive.getPrimitiveId());
}
protected void addSelection(MouseEvent e) {
int row = rowAtPoint(e.getPoint());
if (row < 0) return;
OsmPrimitive primitive = getMemberTableModel().getReferredPrimitive(row);
getMemberTableModel().getSelectionModel().addSelectionInterval(row, row);
getMemberTableModel().getLayer().data.addSelected(primitive.getPrimitiveId());
}
@Override
public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() > 1) {
if (e.isControlDown()) {
addSelection(e);
} else {
setSelection(e);
}
}
}
}
}