Ticket #3409: wip3409.patch

File wip3409.patch, 44.4 KB (added by xeen, 8 years ago)

suggested patch

  • src/org/openstreetmap/josm/gui/io/SaveFlagCellEditor.java

     
    1 // License: GPL. For details, see LICENSE file.
    2 package org.openstreetmap.josm.gui.io;
    3 
    4 import java.awt.Component;
    5 import java.awt.event.MouseAdapter;
    6 import java.awt.event.MouseEvent;
    7 import java.util.EventObject;
    8 import java.util.concurrent.CopyOnWriteArrayList;
    9 
    10 import javax.swing.JCheckBox;
    11 import javax.swing.JTable;
    12 import javax.swing.event.CellEditorListener;
    13 import javax.swing.event.ChangeEvent;
    14 import javax.swing.table.TableCellEditor;
    15 
    16 /**
    17  * This is {@see TableCellEditor} for a boolean flag. It is used in two table columns of
    18  * {@see SaveLayersTable} and renders the values of {@see SaveLayerInfo#isDoSaveToFile()}
    19  * and {@see SaveLayerInfo#isDoUploadToServer()}
    20  *
    21  */
    22 class SaveFlagCellEditor extends JCheckBox implements TableCellEditor {
    23     private CopyOnWriteArrayList<CellEditorListener> listeners;
    24     private boolean value;
    25 
    26     public SaveFlagCellEditor() {
    27         listeners = new CopyOnWriteArrayList<CellEditorListener>();
    28         addMouseListener(
    29                 new MouseAdapter() {
    30                     @Override
    31                     public void mouseExited(MouseEvent e) {
    32                         stopCellEditing();
    33                     }
    34                 }
    35         );
    36     }
    37 
    38     public void addCellEditorListener(CellEditorListener l) {
    39         if (l != null) {
    40             listeners.addIfAbsent(l);
    41         }
    42     }
    43 
    44     protected void fireEditingCanceled() {
    45         for (CellEditorListener l: listeners) {
    46             l.editingCanceled(new ChangeEvent(this));
    47         }
    48     }
    49 
    50     protected void fireEditingStopped() {
    51         for (CellEditorListener l: listeners) {
    52             l.editingStopped(new ChangeEvent(this));
    53         }
    54     }
    55 
    56     public void cancelCellEditing() {
    57         fireEditingCanceled();
    58     }
    59 
    60     public Object getCellEditorValue() {
    61         return value;
    62     }
    63 
    64     public boolean isCellEditable(EventObject anEvent) {
    65         return true;
    66     }
    67 
    68     public void removeCellEditorListener(CellEditorListener l) {
    69         listeners.remove(l);
    70     }
    71 
    72     public boolean shouldSelectCell(EventObject anEvent) {
    73         return true;
    74     }
    75 
    76     public void setInitialValue(boolean value) {
    77         this.value = value;
    78         setSelected(value);
    79     }
    80 
    81     public boolean stopCellEditing() {
    82         this.value = isSelected();
    83         fireEditingStopped();
    84         return true;
    85     }
    86 
    87     public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
    88         SaveLayerInfo info = (SaveLayerInfo)value;
    89         switch(column) {
    90         case 4: setInitialValue(info.isDoUploadToServer()); break;
    91         case 5: setInitialValue(info.isDoSaveToFile()); break;
    92         }
    93         return this;
    94     }
    95 }
  • src/org/openstreetmap/josm/gui/io/SaveLayersTable.java

     
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.gui.io;
    33
     4import java.awt.Dimension;
    45import java.beans.PropertyChangeEvent;
    56import java.beans.PropertyChangeListener;
    67
     
    1213    public SaveLayersTable(SaveLayersModel model) {
    1314        super(model, new SaveLayersTableColumnModel());
    1415        putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
     16        super.setRowHeight(39);
     17        super.getTableHeader().setPreferredSize(new Dimension(super.getTableHeader().getWidth(), 24));
     18        super.getTableHeader().setReorderingAllowed(false);
    1519    }
    1620
    1721    public void propertyChange(PropertyChangeEvent evt) {
  • src/org/openstreetmap/josm/gui/io/SaveLayersModel.java

     
    2424    private Mode mode;
    2525    private PropertyChangeSupport support;
    2626
     27    // keep in sync with how the columns are ordered in SaveLayersTableColumnModel#build
     28    private static final int columnFilename = 0;
     29    private static final int columnActions = 2;
     30
    2731    public SaveLayersModel() {
    2832        mode = Mode.EDITING_DATA;
    2933        support = new PropertyChangeSupport(this);
     
    8286
    8387    @Override
    8488    public boolean isCellEditable(int row, int column) {
    85         return column == 3 || column == 4 || column == 5;
     89        return column == columnFilename || column == columnActions;
    8690    }
    8791
    8892    @Override
    8993    public void setValueAt(Object value, int row, int column) {
    9094        switch(column) {
    91             case 3 /* file name */:
    92                 this.layerInfo.get(row).setFile((File)value);
    93                 this.layerInfo.get(row).setDoSaveToFile(true);
    94                 break;
    95             case 4 /* upload */: this.layerInfo.get(row).setDoUploadToServer((Boolean)value);break;
    96             case 5 /* save */: this.layerInfo.get(row).setDoSaveToFile((Boolean)value);break;
     95        case columnFilename:
     96            this.layerInfo.get(row).setFile((File)value);
     97            this.layerInfo.get(row).setDoSaveToFile(true);
     98            break;
     99        case columnActions:
     100            boolean[] values = (boolean[]) value;
     101            this.layerInfo.get(row).setDoUploadToServer(values[0]);
     102            this.layerInfo.get(row).setDoSaveToFile(values[1]);
     103            break;
    97104        }
    98105        fireTableDataChanged();
    99106    }
     
    103110    }
    104111
    105112    public List<SaveLayerInfo> getLayersWithoutFilesAndSaveRequest() {
    106         List<SaveLayerInfo> ret =new ArrayList<SaveLayerInfo>();
     113        List<SaveLayerInfo> ret = new ArrayList<SaveLayerInfo>();
    107114        for (SaveLayerInfo info: layerInfo) {
    108115            if (info.isDoSaveToFile() && info.getFile() == null) {
    109116                ret.add(info);
     
    214221        }
    215222        return ret;
    216223    }
    217 
    218224}
  • src/org/openstreetmap/josm/gui/io/ActionFlagsTableCell.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.gui.io;
     3
     4import static org.openstreetmap.josm.tools.I18n.tr;
     5
     6import java.awt.Component;
     7import java.awt.Dimension;
     8import java.awt.GridBagLayout;
     9import java.awt.event.ActionEvent;
     10import java.awt.event.ActionListener;
     11import java.util.EventObject;
     12import java.util.concurrent.CopyOnWriteArrayList;
     13
     14import javax.swing.AbstractAction;
     15import javax.swing.ActionMap;
     16import javax.swing.JCheckBox;
     17import javax.swing.JPanel;
     18import javax.swing.JTable;
     19import javax.swing.event.CellEditorListener;
     20import javax.swing.event.ChangeEvent;
     21import javax.swing.table.TableCellEditor;
     22import javax.swing.table.TableCellRenderer;
     23
     24import org.openstreetmap.josm.tools.GBC;
     25
     26/**
     27 * This class creates a table cell that features two checkboxes, Upload and Save. It
     28 * handles everything on its own, in other words it renders itself and also functions
     29 * as editor so the checkboxes may be set by the user.
     30 *
     31 * Intended usage is like this:
     32 * ActionFlagsTableCell aftc = new ActionFlagsTableCell();
     33 * col = new TableColumn(0);
     34 * col.setCellRenderer(aftc);
     35 * col.setCellEditor(aftc);
     36 */
     37class ActionFlagsTableCell extends JPanel implements TableCellRenderer, TableCellEditor {
     38    protected JCheckBox[] checkBoxes = new JCheckBox[2];
     39    private CopyOnWriteArrayList<CellEditorListener> listeners;
     40
     41    private ActionListener al = new ActionListener() {
     42        public void actionPerformed(ActionEvent e) {
     43            fireEditingStopped();
     44        }
     45    };
     46
     47    public ActionFlagsTableCell() {
     48        super();
     49        listeners = new CopyOnWriteArrayList<CellEditorListener>();
     50
     51        setLayout(new GridBagLayout());
     52        checkBoxes[0] = new JCheckBox(tr("Upload"));
     53        checkBoxes[1] = new JCheckBox(tr("Save"));
     54
     55        ActionMap am = getActionMap();
     56        for(int i=0; i<checkBoxes.length; i++) {
     57            final JCheckBox b = checkBoxes[i];
     58            add(b, GBC.eol().fill(GBC.HORIZONTAL));
     59            b.setPreferredSize(new Dimension(b.getPreferredSize().width, 19));
     60            b.addActionListener(al);
     61            am.put(b.getText(), new AbstractAction() {
     62                public void actionPerformed(ActionEvent e) {
     63                    b.setSelected(!b.isSelected());
     64                    fireEditingStopped();
     65                }
     66            });
     67        }
     68
     69        setToolTipText(tr("<html>Select which actions to perform for this layer, if you click the leftmost button.<br/>Check \"upload\" to upload the changes to the OSM server.<br/>Check \"Save\" to save the layer to the file specified on the left.</html>"));
     70    }
     71
     72    protected void updateCheckboxes(Object v) {
     73        boolean[] values;
     74        if(v instanceof SaveLayerInfo) {
     75            values = new boolean[2];
     76            values[0] = ((SaveLayerInfo) v).isDoUploadToServer();
     77            values[1] = ((SaveLayerInfo) v).isDoSaveToFile();
     78        } else {
     79            values = (boolean[]) v;
     80        }
     81        checkBoxes[0].setSelected(values[0]);
     82        checkBoxes[1].setSelected(values[1]);
     83    }
     84
     85    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
     86        updateCheckboxes(value);
     87        return this;
     88    }
     89
     90    public void addCellEditorListener(CellEditorListener l) {
     91        if (l != null) {
     92            listeners.addIfAbsent(l);
     93        }
     94    }
     95
     96    protected void fireEditingCanceled() {
     97        for (CellEditorListener l: listeners) {
     98            l.editingCanceled(new ChangeEvent(this));
     99        }
     100    }
     101
     102    protected void fireEditingStopped() {
     103        for (CellEditorListener l: listeners) {
     104            l.editingStopped(new ChangeEvent(this));
     105        }
     106    }
     107
     108    public void cancelCellEditing() {
     109        fireEditingCanceled();
     110    }
     111
     112    public Object getCellEditorValue() {
     113        boolean[] values = new boolean[2];
     114        values[0] = checkBoxes[0].isSelected();
     115        values[1] = checkBoxes[1].isSelected();
     116        return values;
     117    }
     118
     119    public boolean isCellEditable(EventObject anEvent) {
     120        return true;
     121    }
     122
     123    public void removeCellEditorListener(CellEditorListener l) {
     124        listeners.remove(l);
     125    }
     126
     127    public boolean shouldSelectCell(EventObject anEvent) {
     128        return true;
     129    }
     130
     131    public boolean stopCellEditing() {
     132        fireEditingStopped();
     133        return true;
     134    }
     135
     136    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
     137        updateCheckboxes(value);
     138        return this;
     139    }
     140}
     141 No newline at end of file
  • src/org/openstreetmap/josm/gui/io/SaveLayersTableColumnModel.java

     
    33
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
     6import java.awt.Component;
     7import java.awt.Dimension;
     8import java.awt.GridBagLayout;
     9
     10import javax.swing.JLabel;
     11import javax.swing.JPanel;
     12import javax.swing.JTable;
    613import javax.swing.table.DefaultTableColumnModel;
    7 import javax.swing.table.TableCellEditor;
    814import javax.swing.table.TableCellRenderer;
    915import javax.swing.table.TableColumn;
    1016
     17import org.openstreetmap.josm.tools.GBC;
     18
    1119class SaveLayersTableColumnModel extends DefaultTableColumnModel {
     20    /** small renderer class that handles the "should be uploaded/saved" texts. */
     21    private static class RecommendedActionsTableCell extends JPanel implements TableCellRenderer {
     22        private final static JPanel pnlEmpty = new JPanel();
     23        private final static JLabel needsUpload = new JLabel(tr("should be uploaded"));
     24        private final static JLabel needsSave = new JLabel(tr("should be saved"));
     25        private final static GBC defaultCellStyle = GBC.eol().fill(GBC.HORIZONTAL).insets(2, 0, 2, 0);
     26
     27        public RecommendedActionsTableCell() {
     28            setLayout(new GridBagLayout());
     29            pnlEmpty.setPreferredSize(new Dimension(1, 19));
     30            needsUpload.setPreferredSize(new Dimension(needsUpload.getPreferredSize().width, 19));
     31            needsSave.setPreferredSize(new Dimension(needsSave.getPreferredSize().width, 19));
     32        }
     33
     34        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
     35                boolean hasFocus, int row, int column) {
     36            SaveLayerInfo info = (SaveLayerInfo)value;
     37            StringBuilder sb = new StringBuilder();
     38            sb.append("<html>");
     39            if (info.getLayer().requiresUploadToServer()) {
     40                add(needsUpload, defaultCellStyle);
     41                sb.append(tr("Layer ''{0}'' has modifications which should be uploaded to the server.", info.getName()));
     42
     43            } else {
     44                add(pnlEmpty, defaultCellStyle);
     45                sb.append(tr("Layer ''{0}'' has no modifications to be uploaded.", info.getName()));
     46            }
     47            sb.append("<br/>");
     48
     49            if (info.getLayer().requiresSaveToFile()) {
     50                add(needsSave, defaultCellStyle);
     51                sb.append(tr("Layer ''{0}'' has modifications which should be saved to its associated file ''{1}''.", info.getName(), info.getFile().toString()));
     52            } else {
     53                add(pnlEmpty, defaultCellStyle);
     54                sb.append(tr("Layer ''{0}'' has no modifications to be saved.", info.getName()));
     55            }
     56            sb.append("</html>");
     57            setToolTipText(sb.toString());
     58            return this;
     59        }
     60    }
     61
    1262    protected void build() {
    1363        TableColumn col = null;
    14         TableCellRenderer renderer = new SaveLayerInfoCellRenderer();
    15         TableCellEditor fileNameEditor = new FilenameCellEditor();
    16         TableCellEditor saveFlagEditor = new SaveFlagCellEditor();
    1764
    18         // column 0 - Layer
    19         col = new TableColumn(0);
    20         col.setHeaderValue(tr("Layer"));
     65        // column 0 - layer name, save path editor
     66        LayerNameAndFilePathTableCell lnafptc = new LayerNameAndFilePathTableCell();
     67        col = new TableColumn(0); // keep in sync with SaveLayersModel#columnFilename
     68        col.setHeaderValue(tr("Layer Name and File Path"));
    2169        col.setResizable(true);
    22         col.setCellRenderer(renderer);
    23         col.setPreferredWidth(100);
     70        col.setCellRenderer(lnafptc);
     71        col.setCellEditor(lnafptc);
     72        col.setPreferredWidth(324);
    2473        addColumn(col);
    2574
    26         // column 1 - Upload required
     75        // column 1 - actions required
    2776        col = new TableColumn(1);
    28         col.setHeaderValue(tr("Should upload?"));
     77        col.setHeaderValue(tr("Recommended Actions"));
    2978        col.setResizable(true);
    30         col.setCellRenderer(renderer);
    31         col.setPreferredWidth(50);
     79        col.setCellRenderer(new RecommendedActionsTableCell());
     80        col.setPreferredWidth(150);
    3281        addColumn(col);
    3382
    34         // column 2 - Save to file required
    35         col = new TableColumn(2);
    36         col.setHeaderValue(tr("Should save?"));
     83        // column 2- actions to take
     84        ActionFlagsTableCell aftc = new ActionFlagsTableCell();
     85        col = new TableColumn(2); // keep in sync with SaveLayersModel#columnActions
     86        col.setHeaderValue(tr("Actions To Take"));
    3787        col.setResizable(true);
    38         col.setCellRenderer(renderer);
    39         col.setPreferredWidth(50);
    40         addColumn(col);
     88        col.setCellRenderer(aftc);
     89        col.setCellEditor(aftc);
     90        col.setPreferredWidth(100);
    4191
    42         // column 3 - filename
    43         col = new TableColumn(3);
    44         col.setHeaderValue(tr("Filename"));
    45         col.setResizable(true);
    46         col.setCellRenderer(renderer);
    47         col.setCellEditor(fileNameEditor);
    48         col.setPreferredWidth(200);
    4992        addColumn(col);
    50 
    51         // column 4 - Upload
    52         col = new TableColumn(4);
    53         col.setHeaderValue(tr("Upload"));
    54         col.setResizable(true);
    55         col.setCellRenderer(renderer);
    56         col.setCellEditor(saveFlagEditor);
    57         col.setPreferredWidth(30);
    58         addColumn(col);
    59 
    60         // column 5 - Save
    61         col = new TableColumn(5);
    62         col.setHeaderValue(tr("Save"));
    63         col.setResizable(true);
    64         col.setCellRenderer(renderer);
    65         col.setCellEditor(saveFlagEditor);
    66         col.setPreferredWidth(30);
    67 
    68         addColumn(col);
    6993    }
    7094
    7195    public SaveLayersTableColumnModel() {
  • src/org/openstreetmap/josm/gui/io/LayerNameAndFilePathTableCell.java

     
    55
    66import java.awt.Color;
    77import java.awt.Component;
     8import java.awt.Dimension;
    89import java.awt.Font;
     10import java.awt.GridBagLayout;
     11import java.awt.event.ActionEvent;
     12import java.awt.event.FocusAdapter;
     13import java.awt.event.FocusEvent;
    914import java.io.File;
     15import java.util.EventObject;
     16import java.util.concurrent.CopyOnWriteArrayList;
    1017
    11 import javax.swing.JCheckBox;
     18import javax.swing.AbstractAction;
     19import javax.swing.BorderFactory;
     20import javax.swing.JButton;
    1221import javax.swing.JLabel;
     22import javax.swing.JPanel;
    1323import javax.swing.JTable;
    14 import javax.swing.UIManager;
     24import javax.swing.JTextField;
     25import javax.swing.event.CellEditorListener;
     26import javax.swing.event.ChangeEvent;
     27import javax.swing.table.TableCellEditor;
    1528import javax.swing.table.TableCellRenderer;
    1629
    17 import org.openstreetmap.josm.io.OsmApi;
     30import org.openstreetmap.josm.actions.SaveActionBase;
     31import org.openstreetmap.josm.tools.GBC;
    1832
    19 /**
    20  * This is a {@see TableCellRenderer} for rendering the various fields of a
    21  * {@see SaveLayerInfo} in the table {@see SaveLayersTable}.
    22  *
    23  *
    24  */
    25 class SaveLayerInfoCellRenderer implements TableCellRenderer {
    26     private JLabel lblRenderer;
    27     private JCheckBox cbRenderer;
    2833
    29     public SaveLayerInfoCellRenderer() {
    30         lblRenderer = new JLabel();
    31         cbRenderer = new JCheckBox();
    32     }
     34class LayerNameAndFilePathTableCell extends JPanel implements TableCellRenderer, TableCellEditor {
     35    private final static Color colorError = new Color(255,197,197);
     36    private final static String separator = System.getProperty("file.separator");
     37    private final static String ellipsis = "…" + separator;
    3338
    34     protected Component prepareLayerNameRenderer(SaveLayerInfo info) {
    35         lblRenderer.setIcon(info.getLayer().getIcon());
    36         lblRenderer.setText(info.getName());
    37         lblRenderer.setToolTipText(info.getLayer().getToolTipText());
    38         return lblRenderer;
     39    private final static JLabel lblLayerName = new JLabel();
     40    private final static JLabel lblFilename = new JLabel("");
     41    private final static JTextField tfFilename = new JTextField();
     42    private final JButton btnFileChooser = new JButton(new LaunchFileChooserAction());
     43
     44    private final static GBC defaultCellStyle = GBC.eol().fill(GBC.HORIZONTAL).insets(2, 0, 2, 0);
     45
     46    private CopyOnWriteArrayList<CellEditorListener> listeners;
     47    private File value;
     48
     49
     50    /** constructor that sets the default on each element **/
     51    public LayerNameAndFilePathTableCell() {
     52        setLayout(new GridBagLayout());
     53
     54        lblLayerName.setPreferredSize(new Dimension(lblLayerName.getPreferredSize().width, 19));
     55        lblLayerName.setFont(lblLayerName.getFont().deriveFont(Font.BOLD));
     56
     57        lblFilename.setPreferredSize(new Dimension(lblFilename.getPreferredSize().width, 19));
     58        lblFilename.setOpaque(true);
     59
     60        tfFilename.setToolTipText(tr("Either edit the path manually in the text field or click the \"...\" button to open a file chooser."));
     61        tfFilename.setPreferredSize(new Dimension(tfFilename.getPreferredSize().width, 19));
     62        tfFilename.addFocusListener(
     63                new FocusAdapter() {
     64                    @Override public void focusGained(FocusEvent e) {
     65                        tfFilename.selectAll();
     66                    }
     67                }
     68        );
     69        // hide border
     70        tfFilename.setBorder(BorderFactory.createLineBorder(getBackground()));
     71
     72        btnFileChooser.setPreferredSize(new Dimension(20, 19));
     73        btnFileChooser.setOpaque(true);
     74
     75        listeners = new CopyOnWriteArrayList<CellEditorListener>();
    3976    }
    4077
    41     protected Component prepareUploadRequiredRenderer(SaveLayerInfo info) {
    42         lblRenderer.setIcon(null);
    43         lblRenderer.setHorizontalAlignment(JLabel.CENTER);
    44         String text = info.getLayer().requiresUploadToServer() ? tr("Yes") : tr("No");
    45         lblRenderer.setText(text);
    46         if (info.getLayer().requiresUploadToServer()) {
    47             lblRenderer.setToolTipText(tr("Layer ''{0}'' has modifications which should be uploaded to the server.", info.getName()));
    48         } else {
    49             lblRenderer.setToolTipText(tr("Layer ''{0}'' has no modifications to be uploaded.", info.getName()));
    50         }
    51         return lblRenderer;
     78    /** renderer used while not editing the file path **/
     79    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
     80            boolean hasFocus, int row, int column) {
     81        removeAll();
     82        SaveLayerInfo info = (SaveLayerInfo)value;
     83        StringBuilder sb = new StringBuilder();
     84        sb.append("<html>");
     85        sb.append(addLblLayerName(info));
     86        sb.append("<br>");
     87        add(btnFileChooser, GBC.std());
     88        sb.append(addLblFilename(info));
     89
     90        sb.append("</html>");
     91        setToolTipText(sb.toString());
     92        return this;
    5293    }
    5394
    54     protected Component prepareSaveToFileRequired(SaveLayerInfo info) {
    55         lblRenderer.setIcon(null);
    56         lblRenderer.setHorizontalAlignment(JLabel.CENTER);
    57         String text = info.getLayer().requiresSaveToFile() ? tr("Yes") : tr("No");
    58         lblRenderer.setText(text);
    59         if (info.getLayer().requiresSaveToFile()) {
    60             lblRenderer.setToolTipText(tr("Layer ''{0}'' has modifications which should be saved to its associated file ''{1}''.", info.getName(), info.getFile().toString()));
    61         } else {
    62             lblRenderer.setToolTipText(tr("Layer ''{0}'' has no modifications to be saved.", info.getName()));
    63         }
    64         return lblRenderer;
     95    /** renderer used while the file path is being edited **/
     96    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected,
     97            int row, int column) {
     98        removeAll();
     99        SaveLayerInfo info = (SaveLayerInfo)value;
     100        value = info.getFile();
     101        tfFilename.setText(value == null ? "" : value.toString());
     102
     103        StringBuilder sb = new StringBuilder();
     104        sb.append("<html>");
     105        sb.append(addLblLayerName(info));
     106        sb.append("<br/>");
     107
     108        add(btnFileChooser, GBC.std());
     109        add(tfFilename, GBC.eol().fill(GBC.HORIZONTAL).insets(1, 0, 0, 0));
     110        tfFilename.selectAll();
     111
     112        sb.append(tfFilename.getToolTipText());
     113        sb.append("</html>");
     114        setToolTipText(sb.toString());
     115        return this;
    65116    }
    66117
    67     protected boolean canWrite(File f) {
     118    private static boolean canWrite(File f) {
    68119        if (f == null) return false;
    69120        if (f.isDirectory()) return false;
    70121        if (f.exists() && f.canWrite()) return true;
     
    73124        return false;
    74125    }
    75126
    76     protected Component prepareFileNameRenderer(SaveLayerInfo info) {
    77         lblRenderer.setIcon(null);
     127    /** adds layer name label to (this) using the given info. Returns tooltip that
     128     * should be added to the panel **/
     129    private String addLblLayerName(SaveLayerInfo info) {
     130        lblLayerName.setIcon(info.getLayer().getIcon());
     131        lblLayerName.setText(info.getName());
     132        add(lblLayerName, defaultCellStyle);
     133        return tr("The bold text is the name of the layer.");
     134    }
     135
     136    /** adds filename label to (this) using the given info. Returns tooltip that
     137     * should be added to the panel */
     138    private String addLblFilename(SaveLayerInfo info) {
     139        String tooltip = "";
     140        boolean error = false;
    78141        if (info.getFile() == null) {
    79             if (!info.isDoSaveToFile()) {
    80                 lblRenderer.setText(tr("No file associated with this layer"));
    81             } else {
    82                 lblRenderer.setBackground(new Color(255,197,197));
    83                 lblRenderer.setText(tr("Please select a file"));
    84             }
    85             lblRenderer.setFont(lblRenderer.getFont().deriveFont(Font.ITALIC));
    86             lblRenderer.setToolTipText(tr("Layer ''{0}'' is not backed by a file", info.getName()));
     142            error = info.isDoSaveToFile();
     143            lblFilename.setText(tr("Click here to choose save path"));
     144            lblFilename.setFont(lblFilename.getFont().deriveFont(Font.ITALIC));
     145            tooltip = tr("Layer ''{0}'' is not backed by a file", info.getName());
    87146        } else {
    88             String text = info.getFile().getName();
    89             String parent = info.getFile().getParent();
    90             if (parent != null) {
    91                 if (parent.length() <= 10) {
    92                     text = info.getFile().getPath();
    93                 } else {
    94                     text = parent.substring(0, 10) + "..." + File.separator + text;
    95                 }
    96             }
    97             lblRenderer.setText(text);
    98             lblRenderer.setToolTipText(info.getFile().getAbsolutePath());
     147            String t = info.getFile().getPath();
     148            lblFilename.setText(makePathFit(t));
     149            tooltip = info.getFile().getAbsolutePath();
    99150            if (info.isDoSaveToFile() && !canWrite(info.getFile())) {
    100                 lblRenderer.setBackground(new Color(255,197,197));
    101                 lblRenderer.setToolTipText(tr("File ''{0}'' is not writable. Please enter another file name.", info.getFile().getPath()));
     151                error = true;
     152                tooltip = tr("File ''{0}'' is not writable. Please enter another file name.", info.getFile().getPath());
    102153            }
    103154        }
    104         return lblRenderer;
     155
     156        lblFilename.setBackground(error ? colorError : getBackground());
     157        btnFileChooser.setBackground(error ? colorError : getBackground());
     158
     159        add(lblFilename, defaultCellStyle);
     160        return tr("Click cell to change the file path.") + "<br/>" + tooltip;
    105161    }
    106162
    107     protected Component prepareUploadRenderer(SaveLayerInfo info){
    108         cbRenderer.setSelected(info.isDoUploadToServer());
    109         lblRenderer.setToolTipText(tr("Select to upload layer ''{0}'' to the server ''{1}''", info.getName(), OsmApi.getOsmApi().getBaseUrl()));
    110         return cbRenderer;
     163    /** makes the given path fit lblFilename, appends ellipsis on the left if it doesn’t fit.
     164     * Idea: /home/user/josm → …/user/josm → …/josm; and take the first one that fits */
     165    static final private String makePathFit(String t) {
     166        boolean hasEllipsis = false;
     167        while(t != null && !t.isEmpty()) {
     168            int txtwidth = lblFilename.getFontMetrics(lblFilename.getFont()).stringWidth(t);
     169            if(txtwidth < lblFilename.getWidth() || t.lastIndexOf(separator) < ellipsis.length()) {
     170                break;
     171            }
     172            // remove ellipsis, if present
     173            t = hasEllipsis ? t.substring(ellipsis.length()) : t;
     174            // cut next block, and re-add ellipsis
     175            t = ellipsis + t.split(separator, 2)[1];
     176            hasEllipsis = true;
     177        }
     178        return t;
    111179    }
    112180
    113     protected Component prepareSaveToFileRenderer(SaveLayerInfo info){
    114         cbRenderer.setSelected(info.isDoSaveToFile());
    115         lblRenderer.setToolTipText(tr("Select to upload layer ''{0}'' to the server ''{1}''", info.getName(), OsmApi.getOsmApi().getBaseUrl()));
    116         return cbRenderer;
     181    public void addCellEditorListener(CellEditorListener l) {
     182        if (l != null) {
     183            listeners.addIfAbsent(l);
     184        }
    117185    }
    118186
    119     protected void resetRenderers() {
    120         lblRenderer.setOpaque(true);
    121         lblRenderer.setBackground(UIManager.getColor("Table.background"));
    122         lblRenderer.setIcon(null);
    123         lblRenderer.setText("");
    124         lblRenderer.setFont(UIManager.getFont("Table.font"));
    125         lblRenderer.setHorizontalAlignment(JLabel.LEFT);
     187    protected void fireEditingCanceled() {
     188        for (CellEditorListener l: listeners) {
     189            l.editingCanceled(new ChangeEvent(this));
     190        }
     191    }
    126192
    127         cbRenderer.setSelected(false);
    128         cbRenderer.setOpaque(true);
    129         cbRenderer.setBackground(UIManager.getColor("Table.background"));
     193    protected void fireEditingStopped() {
     194        for (CellEditorListener l: listeners) {
     195            l.editingStopped(new ChangeEvent(this));
     196        }
    130197    }
    131198
    132     public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
    133             boolean hasFocus, int row, int column) {
     199    public void cancelCellEditing() {
     200        fireEditingCanceled();
     201    }
    134202
    135         resetRenderers();
    136         if (value == null)
    137             return null;
     203    public Object getCellEditorValue() {
     204        return value;
     205    }
    138206
    139         SaveLayerInfo info = (SaveLayerInfo)value;
    140         switch(column) {
    141         case 0: return prepareLayerNameRenderer(info);
    142         case 1: return prepareUploadRequiredRenderer(info);
    143         case 2: return prepareSaveToFileRequired(info);
    144         case 3: return prepareFileNameRenderer(info);
    145         case 4: return prepareUploadRenderer(info);
    146         case 5: return prepareSaveToFileRenderer(info);
     207    public boolean isCellEditable(EventObject anEvent) {
     208        return true;
     209    }
     210
     211    public void removeCellEditorListener(CellEditorListener l) {
     212        listeners.remove(l);
     213    }
     214
     215    public boolean shouldSelectCell(EventObject anEvent) {
     216        return true;
     217    }
     218
     219    public boolean stopCellEditing() {
     220        if (tfFilename.getText() == null || tfFilename.getText().trim().equals("")) {
     221            value = null;
     222        } else {
     223            value = new File(tfFilename.getText());
    147224        }
    148         return null;
     225        fireEditingStopped();
     226        return true;
    149227    }
     228
     229    private class LaunchFileChooserAction extends AbstractAction {
     230        public LaunchFileChooserAction() {
     231            putValue(NAME, "...");
     232            putValue(SHORT_DESCRIPTION, tr("Launch a file chooser to select a file"));
     233        }
     234
     235        public void actionPerformed(ActionEvent e) {
     236            File f = SaveActionBase.createAndOpenSaveFileChooser(tr("Select filename"), "osm");
     237            if (f != null) {
     238                tfFilename.setText(f.toString());
     239                stopCellEditing();
     240            }
     241        }
     242    }
    150243}
  • src/org/openstreetmap/josm/gui/io/SaveLayersDialog.java

     
    88import java.awt.Component;
    99import java.awt.Dimension;
    1010import java.awt.FlowLayout;
     11import java.awt.Graphics2D;
    1112import java.awt.GridBagConstraints;
    1213import java.awt.GridBagLayout;
     14import java.awt.Image;
    1315import java.awt.event.ActionEvent;
    1416import java.awt.event.WindowAdapter;
    1517import java.awt.event.WindowEvent;
     18import java.awt.image.BufferedImage;
    1619import java.beans.PropertyChangeEvent;
    1720import java.beans.PropertyChangeListener;
    1821import java.util.List;
     
    2326
    2427import javax.swing.AbstractAction;
    2528import javax.swing.DefaultListCellRenderer;
     29import javax.swing.ImageIcon;
    2630import javax.swing.JComponent;
    2731import javax.swing.JDialog;
    2832import javax.swing.JLabel;
     
    3236import javax.swing.JScrollPane;
    3337import javax.swing.KeyStroke;
    3438import javax.swing.WindowConstants;
     39import javax.swing.event.TableModelEvent;
     40import javax.swing.event.TableModelListener;
    3541
    3642import org.openstreetmap.josm.Main;
    3743import org.openstreetmap.josm.actions.UploadAction;
     
    4450import org.openstreetmap.josm.tools.ImageProvider;
    4551import org.openstreetmap.josm.tools.WindowGeometry;
    4652
    47 public class SaveLayersDialog extends JDialog {
     53public class SaveLayersDialog extends JDialog implements TableModelListener {
    4854    static public enum UserAction {
    4955        /**
    5056         * save/upload layers was successful, proceed with operation
     
    7581        getContentPane().setLayout(new BorderLayout());
    7682
    7783        model = new SaveLayersModel();
    78         SaveLayersTable table;
    79         JScrollPane pane = new JScrollPane(table = new SaveLayersTable(model));
     84        SaveLayersTable table = new SaveLayersTable(model);
     85        JScrollPane pane = new JScrollPane(table);
    8086        model.addPropertyChangeListener(table);
     87        table.getModel().addTableModelListener(this);
     88
    8189        getContentPane().add(pane, BorderLayout.CENTER);
    8290        getContentPane().add(buildButtonRow(), BorderLayout.SOUTH);
    83         table.getTableHeader().setPreferredSize(new Dimension(table.getTableHeader().getWidth(), 40));
    8491
    8592        addWindowListener(new WindowClosingAdapter());
    8693        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    8794    }
    8895
     96    private SideButton saveAndProceedActionButton = null;
     97
    8998    /**
    9099     * builds the button row
    91100     *
     
    97106
    98107        saveAndProceedAction = new SaveAndProceedAction();
    99108        model.addPropertyChangeListener(saveAndProceedAction);
    100         pnl.add(new SideButton(saveAndProceedAction));
     109        pnl.add(saveAndProceedActionButton = new SideButton(saveAndProceedAction));
    101110
    102111        discardAndProceedAction = new DiscardAndProceedAction();
    103112        model.addPropertyChangeListener(discardAndProceedAction);
     
    326335        }
    327336
    328337        public void initForDiscardAndExit() {
    329             putValue(NAME, tr("Discard and Exit"));
     338            putValue(NAME, tr("Exit now!"));
    330339            putValue(SHORT_DESCRIPTION, tr("Exit JOSM without saving. Unsaved changes are lost."));
    331340            putValue(SMALL_ICON, ImageProvider.get("exit"));
    332341        }
    333342
    334343        public void initForDiscardAndDelete() {
    335             putValue(NAME, tr("Discard and Delete"));
     344            putValue(NAME, tr("Delete now!"));
    336345            putValue(SHORT_DESCRIPTION, tr("Delete layers without saving. Unsaved changes are lost."));
    337346            putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete"));
    338347        }
     
    352361        }
    353362    }
    354363
    355     class SaveAndProceedAction extends AbstractAction implements PropertyChangeListener {
     364    final class SaveAndProceedAction extends AbstractAction implements PropertyChangeListener {
     365        private static final int is = 24; // icon size
     366        private static final String BASE_ICON = "BASE_ICON";
     367        private final Image save = ImageProvider.get("save").getImage();
     368        private final Image upld = ImageProvider.get("upload").getImage();
     369        private final Image saveDis = new BufferedImage(is, is, BufferedImage.TYPE_4BYTE_ABGR);
     370        private final Image upldDis = new BufferedImage(is, is, BufferedImage.TYPE_4BYTE_ABGR);
     371
    356372        public SaveAndProceedAction() {
     373            // get disabled versions of icons
     374            new JLabel(ImageProvider.get("save")).getDisabledIcon().paintIcon(new JPanel(), saveDis.getGraphics(), 0, 0);
     375            new JLabel(ImageProvider.get("upload")).getDisabledIcon().paintIcon(new JPanel(), upldDis.getGraphics(), 0, 0);
    357376            initForSaveAndExit();
    358377        }
    359378
    360379        public void initForSaveAndExit() {
    361             putValue(NAME, tr("Save/Upload and Exit"));
     380            putValue(NAME, tr("Perform actions before exiting"));
    362381            putValue(SHORT_DESCRIPTION, tr("Exit JOSM with saving. Unsaved changes are uploaded and/or saved."));
    363             putValue(SMALL_ICON, ImageProvider.get("exit"));
     382            putValue(BASE_ICON, ImageProvider.get("exit"));
     383            redrawIcon();
    364384        }
    365385
    366386        public void initForSaveAndDelete() {
    367             putValue(NAME, tr("Save/Upload and Delete"));
     387            putValue(NAME, tr("Perform actions before deleting"));
    368388            putValue(SHORT_DESCRIPTION, tr("Save/Upload layers before deleting. Unsaved changes are not lost."));
    369             putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete"));
     389            putValue(BASE_ICON, ImageProvider.get("dialogs", "delete"));
     390            redrawIcon();
    370391        }
    371392
     393        public void redrawIcon() {
     394            try { // Can fail if model is not yet setup properly
     395                Image base = ((ImageIcon) getValue(BASE_ICON)).getImage();
     396                BufferedImage newIco = new BufferedImage(is*3, is, BufferedImage.TYPE_4BYTE_ABGR);
     397                Graphics2D g = newIco.createGraphics();
     398                g.drawImage(model.getLayersToUpload().isEmpty() ? upldDis : upld, is*0, 0, is, is, null);
     399                g.drawImage(model.getLayersToSave().isEmpty()   ? saveDis : save, is*1, 0, is, is, null);
     400                g.drawImage(base,                                                 is*2, 0, is, is, null);
     401                putValue(SMALL_ICON, new ImageIcon(newIco));
     402            } catch(Exception e) {
     403                putValue(SMALL_ICON, getValue(BASE_ICON));
     404            }
     405        }
     406
    372407        public void actionPerformed(ActionEvent e) {
    373408            if (! confirmSaveLayerInfosOK())
    374409                return;
     
    545580            canceled = true;
    546581        }
    547582    }
     583
     584    @Override
     585    public void tableChanged(TableModelEvent arg0) {
     586        boolean dis = model.getLayersToSave().isEmpty() && model.getLayersToUpload().isEmpty();
     587        if(saveAndProceedActionButton != null) {
     588            saveAndProceedActionButton.setEnabled(!dis);
     589        }
     590        saveAndProceedAction.redrawIcon();
     591    }
    548592}
  • src/org/openstreetmap/josm/gui/io/SaveLayerInfoCellRenderer.java

     
    1 // License: GPL. For details, see LICENSE file.
    2 package org.openstreetmap.josm.gui.io;
    3 
    4 import static org.openstreetmap.josm.tools.I18n.tr;
    5 
    6 import java.awt.Color;
    7 import java.awt.Component;
    8 import java.awt.Font;
    9 import java.io.File;
    10 
    11 import javax.swing.JCheckBox;
    12 import javax.swing.JLabel;
    13 import javax.swing.JTable;
    14 import javax.swing.UIManager;
    15 import javax.swing.table.TableCellRenderer;
    16 
    17 import org.openstreetmap.josm.io.OsmApi;
    18 
    19 /**
    20  * This is a {@see TableCellRenderer} for rendering the various fields of a
    21  * {@see SaveLayerInfo} in the table {@see SaveLayersTable}.
    22  *
    23  *
    24  */
    25 class SaveLayerInfoCellRenderer implements TableCellRenderer {
    26     private JLabel lblRenderer;
    27     private JCheckBox cbRenderer;
    28 
    29     public SaveLayerInfoCellRenderer() {
    30         lblRenderer = new JLabel();
    31         cbRenderer = new JCheckBox();
    32     }
    33 
    34     protected Component prepareLayerNameRenderer(SaveLayerInfo info) {
    35         lblRenderer.setIcon(info.getLayer().getIcon());
    36         lblRenderer.setText(info.getName());
    37         lblRenderer.setToolTipText(info.getLayer().getToolTipText());
    38         return lblRenderer;
    39     }
    40 
    41     protected Component prepareUploadRequiredRenderer(SaveLayerInfo info) {
    42         lblRenderer.setIcon(null);
    43         lblRenderer.setHorizontalAlignment(JLabel.CENTER);
    44         String text = info.getLayer().requiresUploadToServer() ? tr("Yes") : tr("No");
    45         lblRenderer.setText(text);
    46         if (info.getLayer().requiresUploadToServer()) {
    47             lblRenderer.setToolTipText(tr("Layer ''{0}'' has modifications which should be uploaded to the server.", info.getName()));
    48         } else {
    49             lblRenderer.setToolTipText(tr("Layer ''{0}'' has no modifications to be uploaded.", info.getName()));
    50         }
    51         return lblRenderer;
    52     }
    53 
    54     protected Component prepareSaveToFileRequired(SaveLayerInfo info) {
    55         lblRenderer.setIcon(null);
    56         lblRenderer.setHorizontalAlignment(JLabel.CENTER);
    57         String text = info.getLayer().requiresSaveToFile() ? tr("Yes") : tr("No");
    58         lblRenderer.setText(text);
    59         if (info.getLayer().requiresSaveToFile()) {
    60             lblRenderer.setToolTipText(tr("Layer ''{0}'' has modifications which should be saved to its associated file ''{1}''.", info.getName(), info.getFile().toString()));
    61         } else {
    62             lblRenderer.setToolTipText(tr("Layer ''{0}'' has no modifications to be saved.", info.getName()));
    63         }
    64         return lblRenderer;
    65     }
    66 
    67     protected boolean canWrite(File f) {
    68         if (f == null) return false;
    69         if (f.isDirectory()) return false;
    70         if (f.exists() && f.canWrite()) return true;
    71         if (!f.exists() && f.getParentFile() != null && f.getParentFile().canWrite())
    72             return true;
    73         return false;
    74     }
    75 
    76     protected Component prepareFileNameRenderer(SaveLayerInfo info) {
    77         lblRenderer.setIcon(null);
    78         if (info.getFile() == null) {
    79             if (!info.isDoSaveToFile()) {
    80                 lblRenderer.setText(tr("No file associated with this layer"));
    81             } else {
    82                 lblRenderer.setBackground(new Color(255,197,197));
    83                 lblRenderer.setText(tr("Please select a file"));
    84             }
    85             lblRenderer.setFont(lblRenderer.getFont().deriveFont(Font.ITALIC));
    86             lblRenderer.setToolTipText(tr("Layer ''{0}'' is not backed by a file", info.getName()));
    87         } else {
    88             String text = info.getFile().getName();
    89             String parent = info.getFile().getParent();
    90             if (parent != null) {
    91                 if (parent.length() <= 10) {
    92                     text = info.getFile().getPath();
    93                 } else {
    94                     text = parent.substring(0, 10) + "..." + File.separator + text;
    95                 }
    96             }
    97             lblRenderer.setText(text);
    98             lblRenderer.setToolTipText(info.getFile().getAbsolutePath());
    99             if (info.isDoSaveToFile() && !canWrite(info.getFile())) {
    100                 lblRenderer.setBackground(new Color(255,197,197));
    101                 lblRenderer.setToolTipText(tr("File ''{0}'' is not writable. Please enter another file name.", info.getFile().getPath()));
    102             }
    103         }
    104         return lblRenderer;
    105     }
    106 
    107     protected Component prepareUploadRenderer(SaveLayerInfo info){
    108         cbRenderer.setSelected(info.isDoUploadToServer());
    109         lblRenderer.setToolTipText(tr("Select to upload layer ''{0}'' to the server ''{1}''", info.getName(), OsmApi.getOsmApi().getBaseUrl()));
    110         return cbRenderer;
    111     }
    112 
    113     protected Component prepareSaveToFileRenderer(SaveLayerInfo info){
    114         cbRenderer.setSelected(info.isDoSaveToFile());
    115         lblRenderer.setToolTipText(tr("Select to upload layer ''{0}'' to the server ''{1}''", info.getName(), OsmApi.getOsmApi().getBaseUrl()));
    116         return cbRenderer;
    117     }
    118 
    119     protected void resetRenderers() {
    120         lblRenderer.setOpaque(true);
    121         lblRenderer.setBackground(UIManager.getColor("Table.background"));
    122         lblRenderer.setIcon(null);
    123         lblRenderer.setText("");
    124         lblRenderer.setFont(UIManager.getFont("Table.font"));
    125         lblRenderer.setHorizontalAlignment(JLabel.LEFT);
    126 
    127         cbRenderer.setSelected(false);
    128         cbRenderer.setOpaque(true);
    129         cbRenderer.setBackground(UIManager.getColor("Table.background"));
    130     }
    131 
    132     public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
    133             boolean hasFocus, int row, int column) {
    134 
    135         resetRenderers();
    136         if (value == null)
    137             return null;
    138 
    139         SaveLayerInfo info = (SaveLayerInfo)value;
    140         switch(column) {
    141         case 0: return prepareLayerNameRenderer(info);
    142         case 1: return prepareUploadRequiredRenderer(info);
    143         case 2: return prepareSaveToFileRequired(info);
    144         case 3: return prepareFileNameRenderer(info);
    145         case 4: return prepareUploadRenderer(info);
    146         case 5: return prepareSaveToFileRenderer(info);
    147         }
    148         return null;
    149     }
    150 }