/* * JaamSim Discrete Event Simulation * Copyright (C) 2005-2013 Ausenco Engineering Canada Inc. * Copyright (C) 2016 JaamSim Software Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.jaamsim.ui; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.File; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import javax.swing.AbstractCellEditor; import javax.swing.DefaultComboBoxModel; import javax.swing.DefaultListModel; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JColorChooser; import javax.swing.JComboBox; import javax.swing.JDialog; import javax.swing.JFileChooser; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.ListCellRenderer; import javax.swing.SwingUtilities; import javax.swing.event.CellEditorListener; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableModel; import com.jaamsim.Samples.SampleListInput; import com.jaamsim.StringProviders.StringProvListInput; import com.jaamsim.basicsim.Entity; import com.jaamsim.input.ColourInput; import com.jaamsim.input.ExpressionInput; import com.jaamsim.input.FileInput; import com.jaamsim.input.Input; import com.jaamsim.input.InputAgent; import com.jaamsim.input.InputErrorException; import com.jaamsim.input.Keyword; import com.jaamsim.input.KeywordIndex; import com.jaamsim.input.ListInput; import com.jaamsim.input.Parser; import com.jaamsim.input.StringInput; import com.jaamsim.input.StringListInput; import com.jaamsim.math.Color4d; /** * Class to display information about model objects. <br> * displays the results of getInfo for an object */ public class EditBox extends FrameBox { private static final int ROW_HEIGHT=20; private static final int VALUE_COLUMN=2; public static final String NONE = EditBox.formatEditorText("None"); public static final String REQD = EditBox.formatEditorText("Required Input"); private static EditBox myInstance; // only one instance allowed to be open private Entity currentEntity; private final JTabbedPane jTabbedFrame; private final TableCellRenderer columnRender = new EditBoxColumnRenderer(); private String lastCategory = null; private EditBox() { super( "Input Editor" ); setDefaultCloseOperation(FrameBox.DISPOSE_ON_CLOSE); addWindowListener(FrameBox.getCloseListener("ShowInputEditor")); // Set the preferred size of the panes jTabbedFrame = new JTabbedPane(); jTabbedFrame.addChangeListener(new TabListener()); getContentPane().add(jTabbedFrame); setLocation(GUIFrame.COL2_START, GUIFrame.LOWER_START); setSize(GUIFrame.COL2_WIDTH, GUIFrame.LOWER_HEIGHT); } public synchronized static EditBox getInstance() { if (myInstance == null) myInstance = new EditBox(); return myInstance; } @Override public void setEntity(Entity entity) { if (entity == currentEntity) { // Reset the title in case the entity's name has changed if (currentEntity != null) setTitle("Input Editor - " + currentEntity.getName()); return; } if (currentEntity != null) { int idx = jTabbedFrame.getSelectedIndex(); if (idx > -1) lastCategory = jTabbedFrame.getTitleAt(idx); } jTabbedFrame.removeAll(); currentEntity = entity; if (currentEntity == null) { setTitle("Input Editor"); return; } setTitle("Input Editor - " + currentEntity.getName()); int initialTab = 0; int curTab = 0; for (CategoryInputs each : getInputs(currentEntity)) { EditTableModel mod = new EditTableModel(each); JTable propTable = new EditTable(mod, columnRender); JScrollPane jScrollPane = new JScrollPane(propTable); jScrollPane.getVerticalScrollBar().setUnitIncrement(ROW_HEIGHT); jScrollPane.setColumnHeaderView( propTable.getTableHeader()); jTabbedFrame.addTab(each.category, null, jScrollPane, null); if (each.category.equals(lastCategory)) initialTab = curTab; curTab++; } if (jTabbedFrame.getTabCount() > 0) { jTabbedFrame.setSelectedIndex(initialTab); } } @Override public void updateValues(double simTime) { if(currentEntity == null) return; JTable propTable = (JTable)(((JScrollPane)jTabbedFrame.getSelectedComponent()).getViewport().getComponent(0)); ((EditTableModel)propTable.getModel()).fireTableDataChanged(); } private synchronized static void killInstance() { myInstance = null; } @Override public void dispose() { killInstance(); super.dispose(); } public static final String scanClassForInputDesc(Class<?> curClass, Object obj, Input<?> in) { for (Field f : curClass.getDeclaredFields()) { if (!Input.class.isAssignableFrom(f.getType())) { continue; } try { f.setAccessible(true); if (in != f.get(obj)) { continue; } Keyword key = f.getAnnotation(Keyword.class); if (key == null) return null; return GUIFrame.formatKeywordToolTip(obj.getClass().getSimpleName(), in.getKeyword(), key.description(), key.example(), key.exampleList()); } catch (IllegalArgumentException e) {} catch (IllegalAccessException e) {} catch (SecurityException e) {} } return null; } public static final String getInputDesc(Entity ent, Input<?> in) { Class<?> current = ent.getClass(); while (true) { String desc = scanClassForInputDesc(current, ent, in); if (desc != null) { return desc; } current = current.getSuperclass(); if (current == Object.class) break; } return null; } Entity getCurrentEntity() { return currentEntity; } public static String formatEditorText(String str) { return String.format("<html><i><font color=\"gray\">%s</font></i></html>", str); } public static String formatErrorText(String str) { return String.format("<html><font color=\"red\">%s</font></html>", str); } /** * Handles inputs that are edited in place. * */ public static class StringEditor extends CellEditor { private final JTextField text; public StringEditor(JTable table) { super(table); text = new JTextField(); } @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { setTableInfo(table, row, column); input = (Input<?>)value; String val = input.getValueString(); if (retryString != null) { val = retryString; retryString = null; } text.setText( val ); return text; } @Override public String getValue() { return text.getText(); } @Override public boolean canRetry() { return true; } } /** * Handles file inputs. * */ public static class FileEditor extends CellEditor implements ActionListener { private final JPanel jPanel; private final JTextField text; private final JButton fileButton; private FileInput fileInput; private static File lastDir; // last directory accessed by the file chooser public FileEditor(JTable table) { super(table); jPanel = new JPanel(new BorderLayout()); text = new JTextField(); jPanel.add(text, BorderLayout.WEST); fileButton = new JButton(new ImageIcon( GUIFrame.class.getResource("/resources/images/dropdown.png"))); fileButton.addActionListener(this); fileButton.setActionCommand("button"); jPanel.add(fileButton, BorderLayout.EAST); } private void setFileInput(FileInput in) { fileInput = in; } @Override public String getValue() { return text.getText(); } @Override public void actionPerformed(ActionEvent e) { if("button".equals(e.getActionCommand())) { // Create a file chooser if (lastDir == null) lastDir = InputAgent.getConfigFile(); JFileChooser fileChooser = new JFileChooser(lastDir); // Set the file extension filters FileNameExtensionFilter[] filters = fileInput.getFileNameExtensionFilters(); if (filters.length > 0) { // Turn off the "All Files" filter fileChooser.setAcceptAllFileFilterUsed(false); // Include a separate filter for each extension for (FileNameExtensionFilter f : filters) { fileChooser.addChoosableFileFilter(f); } } // Show the file chooser and wait for selection int returnVal = fileChooser.showDialog(null, "Load"); // Process the selected file if (returnVal == JFileChooser.APPROVE_OPTION) { File file = fileChooser.getSelectedFile(); lastDir = fileChooser.getCurrentDirectory(); text.setText(file.getPath()); } // Apply editing stopCellEditing(); // Focus the cell propTable.requestFocusInWindow(); } } @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { setTableInfo(table, row, column); // set the value input = (FileInput)value; text.setText( input.getValueString() ); // right size for jPanel and its components Dimension dim = new Dimension( table.getColumnModel().getColumn( VALUE_COLUMN ).getWidth() - table.getColumnModel().getColumnMargin(), table.getRowHeight()); jPanel.setPreferredSize(dim); dim = new Dimension(dim.width - (dim.height), dim.height); text.setPreferredSize(dim); dim = new Dimension(dim.height, dim.height); fileButton.setPreferredSize(dim); return jPanel; } } /** * Handles colour inputs. * */ public static class ColorEditor extends CellEditor implements ActionListener { private final JPanel jPanel; private final JTextField text; private final JButton colorButton; private JColorChooser colorChooser; private JDialog dialog; public ColorEditor(JTable table) { super(table); jPanel = new JPanel(new BorderLayout()); text = new JTextField(); jPanel.add(text, BorderLayout.WEST); colorButton = new JButton(new ImageIcon( GUIFrame.class.getResource("/resources/images/dropdown.png"))); colorButton.addActionListener(this); colorButton.setActionCommand("button"); colorButton.setContentAreaFilled(false); jPanel.add(colorButton, BorderLayout.EAST); } @Override public String getValue() { return text.getText(); } @Override public void actionPerformed(ActionEvent e) { if("button".equals(e.getActionCommand())) { if(colorChooser == null || dialog == null) { colorChooser = new JColorChooser(); dialog = JColorChooser.createDialog(jPanel, "Pick a Color", true, //modal colorChooser, this, //OK button listener null); //no CANCEL button listener dialog.setIconImage(GUIFrame.getWindowIcon()); dialog.setAlwaysOnTop(true); } Color4d col = ((ColourInput)input).getValue(); colorChooser.setColor(new Color((float)col.r, (float)col.g, (float)col.b, (float)col.a)); dialog.setLocationRelativeTo((Component)e.getSource()); dialog.setVisible(true); // Apply editing stopCellEditing(); // Focus the cell propTable.requestFocusInWindow(); } else { Color color = colorChooser.getColor(); if (color.getAlpha() == 255) { text.setText( String.format("%d %d %d", color.getRed(), color.getGreen(), color.getBlue() ) ); return; } text.setText( String.format("%d %d %d %d", color.getRed(),color.getGreen(), color.getBlue(), color.getAlpha() ) ); } } @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { setTableInfo(table, row, column); // set the value input = (ColourInput)value; text.setText( input.getValueString() ); // right size for jPanel and its components Dimension dim = new Dimension( table.getColumnModel().getColumn( VALUE_COLUMN ).getWidth() - table.getColumnModel().getColumnMargin(), table.getRowHeight()); jPanel.setPreferredSize(dim); dim = new Dimension(dim.width - (dim.height), dim.height); text.setPreferredSize(dim); dim = new Dimension(dim.height, dim.height); colorButton.setPreferredSize(dim); return jPanel; } } /** * Handles inputs with drop-down menus. * */ public static class DropDownMenuEditor extends CellEditor implements ActionListener { private final JComboBox<String> dropDown; private boolean retrying; public DropDownMenuEditor(JTable table, ArrayList<String> aList) { super(table); dropDown = new JComboBox<>(); dropDown.setEditable(true); DefaultComboBoxModel<String> model = (DefaultComboBoxModel<String>) dropDown.getModel(); // Populate the items in the combo box for(String each: aList) { // Space inside the font name if( each.contains(" ") ) each = String.format("'%s'", each); model.addElement(each); } dropDown.setActionCommand("comboBoxChanged"); dropDown.addActionListener(this); // Now it is safe to listen } @Override public void actionPerformed(ActionEvent e) { // If combo box is edited, this method invokes twice if(e.getActionCommand().equals("comboBoxChanged") && !retrying) stopCellEditing(); } @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { setTableInfo(table, row, column); input = (Input<?>)value; String text = input.getValueString(); if (retryString != null) { text = retryString; retrying = true; } retryString = null; dropDown.setSelectedItem(text); retrying = false; return dropDown; } @Override public String getValue() { return (String)dropDown.getSelectedItem(); } @Override public boolean canRetry() { return true; } } /** * Handles inputs where a list of entities can be selected. * */ public static class ListEditor extends CellEditor implements ActionListener { private final JPanel jPanel; private final JTextField text; private final JButton listButton; private JDialog dialog; private JScrollPane jScroll; private JList<JCheckBox> list; private ArrayList<String> tokens; private DefaultListModel<JCheckBox> listModel; private CheckBoxMouseAdapter checkBoxMouseAdapter; private int i; private boolean caseSensitive; private boolean innerBraces; public ListEditor(JTable table) { super(table); jPanel = new JPanel(new BorderLayout()); text = new JTextField(); jPanel.add(text, BorderLayout.WEST); listButton = new JButton(new ImageIcon( GUIFrame.class.getResource("/resources/images/dropdown.png"))); listButton.addActionListener(this); listButton.setActionCommand("button"); listButton.setContentAreaFilled(false); jPanel.add(listButton, BorderLayout.EAST); caseSensitive = true; innerBraces = false; } @Override public String getValue() { return text.getText(); } @Override public void actionPerformed(ActionEvent e) { // OK button if("OK".equals(e.getActionCommand())) { dialog.setVisible(false); StringBuilder sb = new StringBuilder(); for(int i = 0; i < list.getModel().getSize(); i++) { if(!list.getModel().getElementAt(i).isSelected()) continue; String str = list.getModel().getElementAt(i).getText(); if (innerBraces) sb.append("{ ").append(str).append(" } "); else sb.append(str).append(" "); } text.setText(sb.toString()); } if(! "button".equals(e.getActionCommand())) { return; } if(dialog == null) { dialog = new JDialog(EditBox.getInstance(), "Select items", true); // model dialog.setSize(190, 300); jScroll = new JScrollPane(list); dialog.getContentPane().add(jScroll); // top of the JDialog JButton okButton = new JButton("Ok"); okButton.setActionCommand("OK"); okButton.addActionListener(this); dialog.getContentPane().add("South", okButton); dialog.setIconImage(GUIFrame.getWindowIcon()); dialog.setAlwaysOnTop(true); tokens = new ArrayList<>(); } // break the value into single options tokens.clear(); Parser.tokenize(tokens, text.getText()); if( !caseSensitive ) { for(i = 0; i < tokens.size(); i++ ) { tokens.set(i, tokens.get(i).toUpperCase() ); } } // checkmark according to the value input for(i = 0; i < list.getModel().getSize(); i++) { JCheckBox box = list.getModel().getElementAt(i); box.setSelected(tokens.contains(box.getText())); } dialog.setLocationRelativeTo((Component)e.getSource()); dialog.setVisible(true); // Apply editing stopCellEditing(); // Focus the cell propTable.requestFocusInWindow(); } // Set the items in the list public void setListData(ArrayList<String> aList) { if(list == null) { listModel = new DefaultListModel<>(); list = new JList<>(listModel); // render items as JCheckBox and make clicking work for them list.setCellRenderer( new ListRenderer() ); checkBoxMouseAdapter = new CheckBoxMouseAdapter(); list.addMouseListener(checkBoxMouseAdapter); } listModel.clear(); for(String each: aList) { JCheckBox checkBox = new JCheckBox(each); listModel.addElement(checkBox); } } @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { setTableInfo(table, row, column); input = (Input<?>)value; text.setText( input.getValueString() ); // right size for jPanel and its components in the cell Dimension dim = new Dimension( table.getColumnModel().getColumn( VALUE_COLUMN ).getWidth() - table.getColumnModel().getColumnMargin(), table.getRowHeight()); jPanel.setPreferredSize(dim); dim = new Dimension(dim.width - (dim.height), dim.height); text.setPreferredSize(dim); dim = new Dimension(dim.height, dim.height); listButton.setPreferredSize(dim); return jPanel; } public void setCaseSensitive(boolean bool) { caseSensitive = bool; } public void setInnerBraces(boolean bool) { innerBraces = bool; } } /* * renderer for the JList so it shows its items as JCheckBoxes */ public static class ListRenderer implements ListCellRenderer<JCheckBox> { private JCheckBox checkBox; @Override public Component getListCellRendererComponent(JList<? extends JCheckBox> list, JCheckBox value, int index, boolean isSelected, boolean cellHasFocus) { checkBox = value; if (isSelected) { checkBox.setBackground(list.getSelectionBackground()); checkBox.setForeground(list.getSelectionForeground()); } else { checkBox.setBackground(list.getBackground()); checkBox.setForeground(list.getForeground()); } return checkBox; } } /* * pressing mouse in the JList should select/unselect JCheckBox */ public static class CheckBoxMouseAdapter extends MouseAdapter { private int i; private Object obj; @Override public void mousePressed(MouseEvent e) { i = ((JList<?>)e.getSource()).locationToIndex(e.getPoint()); if(i == -1) return; obj = ((JList<?>)e.getSource()).getModel().getElementAt(i); if (obj instanceof JCheckBox) { JCheckBox checkbox = (JCheckBox) obj; checkbox.setSelected(!checkbox.isSelected()); ((JList<?>)e.getSource()).repaint(); } } } public static abstract class CellEditor extends AbstractCellEditor implements TableCellEditor { protected final JTable propTable; protected Input<?> input; private int row; private int col; private JTable table; protected String retryString; public CellEditor(JTable table) { propTable = table; this.addCellEditorListener(new CellListener()); } @Override public Object getCellEditorValue() { return input; } public String getValue() { return ""; } public boolean canRetry() { return false; } final public int getRow() { return row; } final public int getCol() { return col; } final public JTable getTable() { return table; } final public void setRetry(String retryString) { this.retryString = retryString; } protected void setTableInfo(JTable table, int row, int col) { this.table = table; this.row = row; this.col = col; } } public static class CellListener implements CellEditorListener { @Override public void editingCanceled(ChangeEvent evt) {} @Override public void editingStopped(ChangeEvent evt) { CellEditor editor = (CellEditor)evt.getSource(); Input<?> in = (Input<?>)editor.getCellEditorValue(); final String newValue = editor.getValue(); // The value has not changed if ( in.getValueString().equals(newValue) && in.isValid() ) return; // Adjust the user's entry to standardise the syntax try { String str = newValue.trim(); Class<?> klass = in.getClass(); // Add single quotes to String inputs if (klass == StringInput.class || klass == FileInput.class || klass == ExpressionInput.class) { if (Parser.needsQuoting(str) && !Parser.isQuoted(str)) str = Parser.addQuotes(str); } // Keyword ArrayList<String> tokens = new ArrayList<>(); Parser.tokenize(tokens, str, true); // Parse the keyword inputs KeywordIndex kw = new KeywordIndex(in.getKeyword(), tokens, null); InputAgent.processKeyword(EditBox.getInstance().getCurrentEntity(), kw); in.setValid(true); } catch (InputErrorException exep) { if (editor.canRetry()) { boolean editSelected = GUIFrame.showErrorEditDialog("Input Error", "Input error:", exep, "Do you want to continue editing, or reset the input?"); if (editSelected) { // Any editor that supports retry should implement the following final int row = editor.getRow(); final int col = editor.getCol(); final EditTable table = (EditTable)editor.getTable(); final Input<?> inp = in; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { boolean bool = inp.isValid(); inp.setValid(true); //FIXME required for DropDownMenuEditor table.setRetry(newValue, row, col); table.editCellAt(row, col); inp.setValid(bool); } }); } else { GUIFrame.updateUI(); } return; } GUIFrame.showErrorDialog("Input Error", "Input error:", exep, "Value will be cleared."); GUIFrame.updateUI(); return; } } } private static class TabListener implements ChangeListener { @Override public void stateChanged(ChangeEvent e) { GUIFrame.updateUI(); } } private static class CategoryInputs { final String category; final ArrayList<Input<?>> inputs; final int sequence; CategoryInputs(String cat, int seq, ArrayList<Input<?>> ins) { category = cat; sequence = seq; inputs = ins; } } private static ArrayList<CategoryInputs> getInputs(Entity ent) { // create a list of Inputs sorted by category ArrayList<Input<?>> sortedInputs = new ArrayList<>(); for (Input<?> in : ent.getEditableInputs()) { if (in.getHidden() || in.isSynonym()) continue; int index = sortedInputs.size(); for (int i = sortedInputs.size() - 1; i >= 0; i--) { Input<?> ei = sortedInputs.get(i); if (ei.getCategory().equals(in.getCategory())) { index = i + 1; break; } } sortedInputs.add(index, in); } String cat = ""; ArrayList<CategoryInputs> catInputsList = new ArrayList<>(); ArrayList<Input<?>> inputs = new ArrayList<>(); // assuming that editable inputs of the same category are adjacent for (Input<?> in : sortedInputs) { // the first time entering the loop if (cat.isEmpty()) cat = in.getCategory(); // new category (the previous category inputs is done so add it to the list) if ( !cat.equals(in.getCategory()) ) { add(catInputsList, inputs, cat); inputs = new ArrayList<>(); cat = in.getCategory(); } inputs.add(in); } if (inputs.size() != 0) { add(catInputsList, inputs, cat); } Collections.sort(catInputsList, categorySortOrder); return catInputsList; } private static void add(ArrayList<CategoryInputs> list, ArrayList<Input<?>> inputs, String cat) { int seq = 100; if (cat.equals("Key Inputs")) seq = 0; else if (cat.equals("Thresholds")) seq = 1; else if (cat.equals("Maintenance")) seq = 2; else if (cat.equals("Graphics")) seq = 101; list.add(new CategoryInputs(cat, seq, inputs)); } private static class CategoryComparator implements Comparator<CategoryInputs> { @Override public int compare(CategoryInputs cat0, CategoryInputs cat1) { return Integer.compare(cat0.sequence, cat1.sequence); } } public static final Comparator<CategoryInputs> categorySortOrder = new CategoryComparator(); public static class EditTable extends JTable { static int col1Width = 150; static int col2Width = 100; static int col3Width = 150; private ColorEditor colorEditor; private ListEditor listEditor; private String retryString; private int retryRow; private int retryCol; @Override public boolean isCellEditable( int row, int column ) { return ( column == VALUE_COLUMN ); // Only Value column is editable } public EditTable(EditTableModel mod, TableCellRenderer colRender) { super(mod); this.setRowHeight(ROW_HEIGHT); this.setRowSelectionAllowed(false); getColumnModel().getColumn(0).setWidth(col1Width); getColumnModel().getColumn(1).setWidth(col2Width); getColumnModel().getColumn(2).setWidth(col3Width); getColumnModel().getColumn(0).setCellRenderer(colRender); getColumnModel().getColumn(1).setCellRenderer(colRender); getColumnModel().getColumn(2).setCellRenderer(colRender); this.getTableHeader().setFont(FrameBox.boldFont); this.getTableHeader().setReorderingAllowed(false); } @Override public TableCellEditor getCellEditor(int row, int col) { Input<?> in = (Input<?>)this.getValueAt(row, col); CellEditor ret; ArrayList<String> array = in.getValidOptions(); // 1) Colour input if(in instanceof ColourInput) { if(colorEditor == null) { colorEditor = new ColorEditor(this); } ret = colorEditor; } // 2) File input else if(in instanceof FileInput) { FileEditor fileEditor = new FileEditor(this); fileEditor.setFileInput((FileInput)in); ret = fileEditor; } // 3) Normal text else if(array == null) { ret = new StringEditor(this); } // 4) Multiple selections from a List else if(in instanceof ListInput) { if(listEditor == null) { listEditor = new ListEditor(this); if(in instanceof StringListInput) { listEditor.setCaseSensitive( ((StringListInput)(in)).getCaseSensitive() ); } if (in instanceof SampleListInput || in instanceof StringProvListInput) { listEditor.setInnerBraces(true); } } listEditor.setListData(array); ret = listEditor; } // 5) Single selection from a drop down box else { ret = new DropDownMenuEditor(this, array); } // If the user is going to retry a failed edit, update the editor with the old value if (ret.canRetry() && retryString != null && row == retryRow && col == retryCol ) { ret.setRetry(retryString); } retryString = null; return ret; } @Override public String getToolTipText(MouseEvent e) { java.awt.Point p = e.getPoint(); int rowIndex = rowAtPoint(p); int colIndex = columnAtPoint(p); // not necessary because reordering of tabs is not allowed colIndex = convertColumnIndexToModel(colIndex); // Only the keyword column has tooltip if (colIndex != 0) return null; TableModel model = getModel(); Input<?> in = (Input<?>)model.getValueAt(rowIndex, 0); return EditBox.getInputDesc(EditBox.getInstance().getCurrentEntity(), in); } @Override public void doLayout() { col1Width = getColumnModel().getColumn(0).getWidth(); col2Width = getColumnModel().getColumn(1).getWidth(); col3Width = getColumnModel().getColumn(2).getWidth(); FrameBox.fitTableToLastColumn(this); } public void setRetry(String rs, int row, int col) { retryString = rs; retryRow = row; retryCol = col; } } private static class EditTableModel extends AbstractTableModel { CategoryInputs inputs; EditTableModel(CategoryInputs ci) { inputs = ci; } @Override public int getColumnCount() { return 3; } @Override public String getColumnName(int column) { switch (column) { case 0: return "Keyword"; case 1: return "Default"; case 2: return "Value"; } return "Unknown"; } @Override public int getRowCount() { return inputs.inputs.size(); } @Override public boolean isCellEditable(int row, int column) { return (column == VALUE_COLUMN); // Only Value column is editable } @Override public Object getValueAt(int row, int col) { return inputs.inputs.get(row); } } }