/* * Copyright (C) 2012 Dr. John Lindsay <jlindsay@uoguelph.ca> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package whitebox.ui.plugin_dialog; import javax.swing.border.*; import whitebox.interfaces.DialogComponent; import whitebox.interfaces.Communicator; import javax.swing.table.AbstractTableModel; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.text.*; import java.awt.Toolkit; import java.text.NumberFormat; import java.text.ParseException; import java.util.Locale; import java.io.File; import java.util.ArrayList; import whitebox.structures.ExtensionFileFilter; /** * * @author Dr. John Lindsay email: jlindsay@uoguelph.ca */ public class DialogWeightedMultiFile extends JPanel implements ActionListener, DialogComponent { public static final String[] columnNames = { "Cost?", "File Name", "", "Weight", "" }; protected JTable table; protected JScrollPane scroller; //protected WeightedMultiFileTableModel tableModel; private int numArgs = 4; private String name; private String description; private Communicator hostDialog = null; private boolean showCheckbox = true; private String pathSep = File.separator; private boolean acceptAllFiles = false; private ArrayList<ExtensionFileFilter> filters = new ArrayList<ExtensionFileFilter>(); private String workingDirectory; private int fileNameColumnNum = 1; public DialogWeightedMultiFile(Communicator host) { this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); this.setMaximumSize(new Dimension(2500, 180)); this.setPreferredSize(new Dimension(350, 180)); hostDialog = host; } private void createUI() { try { Border border = BorderFactory.createEmptyBorder(5, 5, 5, 5); this.setBorder(border); MyTableModel model = new MyTableModel(); table = new JTable(model); if (showCheckbox) { table.getColumn("Cost?").setMaxWidth(45); } table.getColumn("Weight").setPreferredWidth(45); table.getColumn("Weight").setMaxWidth(65); table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE); table.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 1) { JTable target = (JTable) e.getSource(); int row = target.getSelectedRow(); int column = target.getSelectedColumn(); if (column == fileNameColumnNum && target.getValueAt(row, column).equals("")) { openFile(target); } // do some action } } }); //Create the scroll pane and add the table to it. JScrollPane scrollPane = new JScrollPane(table); //Set up real input validation for the integer column. setUpEditors(table); scroller = new javax.swing.JScrollPane(table); table.setPreferredScrollableViewportSize(new java.awt.Dimension(500, 300)); setLayout(new BorderLayout()); this.add(scroller, BorderLayout.CENTER); } catch (Exception e) { System.out.println(e.getCause()); } } private void setUpEditors(JTable table) { //Set up the editor for the integer cells. final FloatNumberField floatField = new FloatNumberField(0, 5); floatField.setHorizontalAlignment(FloatNumberField.RIGHT); DefaultCellEditor floatEditor = new DefaultCellEditor(floatField) { //Override DefaultCellEditor's getCellEditorValue method //to return an Integer, not a String: @Override public Object getCellEditorValue() { return new Float(floatField.getValue()); } }; table.setDefaultEditor(Integer.class, floatEditor); } private void openFile(JTable table) { int row = table.getSelectedRow(); int col = fileNameColumnNum; JFileChooser fc = new JFileChooser(); fc.setFileSelectionMode(JFileChooser.FILES_ONLY); fc.setMultiSelectionEnabled(true); MyTableModel model = (MyTableModel) table.getModel(); fc.setAcceptAllFileFilterUsed(acceptAllFiles); for (int i = 0; i < filters.size(); i++) { fc.setFileFilter(filters.get(i)); } workingDirectory = hostDialog.getWorkingDirectory(); fc.setCurrentDirectory(new File(workingDirectory)); int result = fc.showOpenDialog(this); File[] files = null; if (result == JFileChooser.APPROVE_OPTION) { files = fc.getSelectedFiles(); String fileDirectory = files[0].getParentFile() + pathSep; if (!fileDirectory.equals(workingDirectory)) { hostDialog.setWorkingDirectory(fileDirectory); } //String shortFileName; //int j, k; for (int a = 0; a < files.length; a++) { //j = files[a].toString().lastIndexOf(pathSep); //k = files[a].toString().lastIndexOf("."); //shortFileName = files[a].toString().substring(j + 1, k); //model.add(model.getSize(), shortFileName); if (row == model.getRowCount()) { model.addEmptyRow(); } model.setValueAt(files[a].toString(), row, col); row++; } } } @Override public String getValue() { String retString = ""; String str; for (int row = 0; row < table.getModel().getRowCount(); row++) { str = (String)table.getModel().getValueAt(row, fileNameColumnNum); if (!str.trim().equals("")) { for (int col = 0; col < table.getModel().getColumnCount(); col++) { str = table.getModel().getValueAt(row, col).toString(); if (row == 0 && col == 0) { if (!str.trim().equals("")) { retString += str; } else { retString += "not specified"; } } else { if (!str.trim().equals("")) { retString += ";" + str; } else { retString += ";not specified"; } } } } } return retString; } @Override public String getComponentName() { return name; } @Override public boolean getOptionalStatus() { return false; } @Override public boolean setArgs(String[] args) { try { // first make sure that there are the right number of args if (args.length != numArgs) { return false; } name = args[0]; description = args[1]; showCheckbox = Boolean.parseBoolean(args[2]); if (showCheckbox) { fileNameColumnNum = 1; } else { fileNameColumnNum = 0; } setFilters(args[3]); createUI(); String descriptionAndInstruction = description + ". Press the right-arrow " + "key when in the last column to add a new row."; this.setToolTipText(descriptionAndInstruction); table.setToolTipText(descriptionAndInstruction); return true; } catch (Exception e) { return false; } } @Override public String[] getArgsDescriptors() { String[] argsDescriptors = new String[numArgs]; argsDescriptors[0] = "String name"; argsDescriptors[1] = "String description"; argsDescriptors[2] = "Show cost checkbox"; argsDescriptors[3] = "File extension filters"; return argsDescriptors; } // private boolean validateValue(String val) { // String ret = ""; // try { // if (!val.equals("")) { // Double dbl = Double.parseDouble(val); // } // return true; // } catch (Exception e) { // return false; // } // // } @Override public void actionPerformed(ActionEvent e) { Object source = e.getSource(); String actionCommand = e.getActionCommand(); if (actionCommand.equals("open")) { } } // public void highlightLastRow(int row) { // int lastrow = table.getModel().getRowCount(); // if (row == lastrow - 1) { // table.setRowSelectionInterval(lastrow - 1, lastrow - 1); // } else { // table.setRowSelectionInterval(row + 1, row + 1); // } // // table.setColumnSelectionInterval(0, 0); // } // class InteractiveRenderer extends DefaultTableCellRenderer { // // protected int interactiveColumn; // // public InteractiveRenderer(int interactiveColumn) { // this.interactiveColumn = interactiveColumn; // } // // @Override // public Component getTableCellRendererComponent(JTable table, // Object value, boolean isSelected, boolean hasFocus, int row, // int column) { // Component c = super.getTableCellRendererComponent(table, value, // isSelected, hasFocus, row, column); // if (column == interactiveColumn && hasFocus) { // if ((DialogWeightedMultiFile.this.tableModel.getRowCount() - 1) == row // && !DialogWeightedMultiFile.this.tableModel.hasEmptyRow()) { // DialogWeightedMultiFile.this.tableModel.addEmptyRow(); // } // // highlightLastRow(row); // } // // return c; // } // } private void setFilters(String filterStr) { try { // filters are delimited by a pipe '|' String[] str1 = filterStr.split("\\|"); String filterDescription; for (int i = 0; i < str1.length; i++) { // the description and extension(s) are delimited by commas. String[] str2 = str1[i].split(","); filterDescription = str2[0].trim(); if (!filterDescription.toLowerCase().contains("all files")) { String[] extensions = new String[str2.length - 1]; for (int j = 1; j < str2.length; j++) { extensions[j - 1] = str2[j].trim(); } ExtensionFileFilter eff = new ExtensionFileFilter(filterDescription, extensions); filters.add(eff); } else { acceptAllFiles = true; } } } catch (Exception e) { System.out.println(e.getStackTrace()); } } @Override public String getLabel() { return name; } @Override public void setLabel(String label) { this.name = label; } class MyTableModel extends AbstractTableModel { private String[] columnNames = {"Cost?", "File Name", "Weight"}; private Object[][] data = { {false, "", new Float(0.0)} }; private boolean[] columnsVisible = new boolean[3]; public MyTableModel() { if (showCheckbox) { columnsVisible[0] = true; } else { columnsVisible[0] = false; } columnsVisible[1] = true; columnsVisible[2] = true; } @Override public int getColumnCount() { int n = 0; for (int i = 0; i < columnsVisible.length; i++) { if (columnsVisible[i]) { n++; } } return n; //columnNames.length; } @Override public int getRowCount() { return data.length; } @Override public String getColumnName(int col) { return columnNames[getNumber(col)]; //col]; } @Override public Object getValueAt(int row, int col) { return data[row][getNumber(col)]; //col]; } /** * This functiun converts a column number in the table * to the right number of the datas. */ protected int getNumber(int col) { int n = col; // right number to return int i = 0; do { if (!(columnsVisible[i])) { n++; } i++; } while (i < n); // If we are on an invisible column, // we have to go one step further while (!(columnsVisible[n])) { n++; } return n; } /* * JTable uses this method to determine the default renderer/ * editor for each cell. If we didn't implement this method, * then the last column would contain text ("true"/"false"), * rather than a check box. */ @Override public Class getColumnClass(int c) { return getValueAt(0, c).getClass(); } @Override public boolean isCellEditable(int row, int col) { return true; } @Override public void setValueAt(Object value, int row, int col) { data[row][getNumber(col)] = value; if (getNumber(col) == 1) { if (!hasEmptyRow()) { addEmptyRow(); } } } public boolean hasEmptyRow() { if (data[getRowCount() - 1][1].equals("")) { return true; } else { return false; } } public void addEmptyRow() { int nRows = getRowCount(); Object[][] data2 = new Object[nRows + 1][3]; for (int i = 0; i < nRows; i++) { System.arraycopy(data[i], 0, data2[i], 0, 3); } data2[nRows][0] = false; data2[nRows][1] = ""; data2[nRows][2] = new Float(0.0); data = data2.clone(); this.fireTableDataChanged(); } } } class FloatNumberField extends JTextField { private Toolkit toolkit; private NumberFormat decimalFormatter; public FloatNumberField(int value, int columns) { super(columns); toolkit = Toolkit.getDefaultToolkit(); decimalFormatter = NumberFormat.getNumberInstance(Locale.getDefault()); decimalFormatter.setParseIntegerOnly(false); setValue(value); } public int getValue() { int retVal = 0; try { retVal = decimalFormatter.parse(getText()).intValue(); } catch (ParseException e) { // This should never happen because insertString allows // only properly formatted data to get in the field. } return retVal; } public void setValue(int value) { setText(decimalFormatter.format(value)); } @Override protected Document createDefaultModel() { return new WholeNumberDocument(); } protected class WholeNumberDocument extends PlainDocument { @Override public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { char[] source = str.toCharArray(); char[] result = new char[source.length]; int j = 0; for (int i = 0; i < result.length; i++) { if (Character.isDigit(source[i])) { result[j++] = source[i]; } else { toolkit.beep(); } } super.insertString(offs, new String(result, 0, j), a); } } }