001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.io;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.Component;
007import java.awt.Dimension;
008import java.awt.GridBagLayout;
009import java.awt.event.ActionEvent;
010import java.awt.event.ActionListener;
011import java.util.EventObject;
012
013import javax.swing.AbstractAction;
014import javax.swing.ActionMap;
015import javax.swing.JCheckBox;
016import javax.swing.JPanel;
017import javax.swing.JTable;
018import javax.swing.event.CellEditorListener;
019import javax.swing.table.TableCellEditor;
020import javax.swing.table.TableCellRenderer;
021
022import org.openstreetmap.josm.gui.util.CellEditorSupport;
023import org.openstreetmap.josm.tools.GBC;
024
025/**
026 * This class creates a table cell that features two checkboxes, Upload and Save. It
027 * handles everything on its own, in other words it renders itself and also functions
028 * as editor so the checkboxes may be set by the user.
029 *
030 * Intended usage is like this:
031 * <code>
032 * <br>ActionFlagsTableCell aftc = new ActionFlagsTableCell();
033 * <br>col = new TableColumn(0);
034 * <br>col.setCellRenderer(aftc);
035 * <br>col.setCellEditor(aftc);
036 * </code>
037 *
038 * Note: Do not use the same object both as <code>TableCellRenderer</code> and
039 * <code>TableCellEditor</code> - this can mess up the current editor component
040 * by subsequent calls to the renderer (#19995 and #12462).
041 */
042class ActionFlagsTableCell extends JPanel implements TableCellRenderer, TableCellEditor {
043    private final JCheckBox[] checkBoxes = new JCheckBox[2];
044    private final transient CellEditorSupport cellEditorSupport = new CellEditorSupport(this);
045
046    /**
047     * Constructs a new {@code ActionFlagsTableCell}.
048     */
049    ActionFlagsTableCell() {
050        checkBoxes[0] = new JCheckBox(tr("Upload"));
051        checkBoxes[1] = new JCheckBox(tr("Save"));
052        setLayout(new GridBagLayout());
053
054        ActionListener al = e -> cellEditorSupport.fireEditingStopped();
055        ActionMap am = getActionMap();
056        for (final JCheckBox b : checkBoxes) {
057            b.setPreferredSize(new Dimension(b.getPreferredSize().width, 19));
058            b.addActionListener(al);
059            am.put(b.getText(), new AbstractAction() {
060                @Override
061                public void actionPerformed(ActionEvent e) {
062                    b.setSelected(!b.isSelected());
063                    cellEditorSupport.fireEditingStopped();
064                }
065            });
066        }
067    }
068
069    protected void updateCheckboxes(Object v) {
070        if (v != null && checkBoxes[0] != null && checkBoxes[1] != null) {
071            boolean[] values;
072            if (v instanceof SaveLayerInfo) {
073                values = new boolean[2];
074                values[0] = ((SaveLayerInfo) v).isDoUploadToServer();
075                values[1] = ((SaveLayerInfo) v).isDoSaveToFile();
076            } else {
077                values = (boolean[]) v;
078            }
079            checkBoxes[0].setSelected(values[0]);
080            checkBoxes[1].setSelected(values[1]);
081        }
082    }
083
084    private void updatePanel(SaveLayerInfo info) {
085        StringBuilder sb = new StringBuilder(128)
086            .append("<html>")
087            .append(tr("Select which actions to perform for this layer, if you click the leftmost button."));
088        removeAll();
089        if (info != null) {
090            if (info.isUploadable()) {
091                sb.append("<br/>")
092                  .append(tr("Check \"Upload\" to upload the changes to the OSM server."));
093                add(checkBoxes[0], GBC.eol().fill(GBC.HORIZONTAL));
094            }
095            if (info.isSavable()) {
096                sb.append("<br/>")
097                  .append(tr("Check \"Save\" to save the layer to the file specified on the left."));
098                add(checkBoxes[1], GBC.eol().fill(GBC.HORIZONTAL));
099            }
100        }
101        sb.append("</html>");
102        setToolTipText(sb.toString());
103    }
104
105    @Override
106    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
107        return getTableCellEditorComponent(table, value, isSelected, row, column);
108    }
109
110    @Override
111    public void addCellEditorListener(CellEditorListener l) {
112        cellEditorSupport.addCellEditorListener(l);
113    }
114
115    @Override
116    public void cancelCellEditing() {
117        cellEditorSupport.fireEditingCanceled();
118    }
119
120    @Override
121    public Object getCellEditorValue() {
122        boolean[] values = new boolean[2];
123        values[0] = checkBoxes[0].isSelected();
124        values[1] = checkBoxes[1].isSelected();
125        return values;
126    }
127
128    @Override
129    public boolean isCellEditable(EventObject anEvent) {
130        return true;
131    }
132
133    @Override
134    public void removeCellEditorListener(CellEditorListener l) {
135        cellEditorSupport.removeCellEditorListener(l);
136    }
137
138    @Override
139    public boolean shouldSelectCell(EventObject anEvent) {
140        return true;
141    }
142
143    @Override
144    public boolean stopCellEditing() {
145        cellEditorSupport.fireEditingStopped();
146        return true;
147    }
148
149    @Override
150    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
151        updatePanel((SaveLayerInfo) value);
152        updateCheckboxes(value);
153        return this;
154    }
155}