From b50b854b88b48372785e8823e512310c9b1b75eb Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Thu, 12 Dec 2013 16:52:30 +0100 Subject: [PATCH] Implemented argument configuration for effect engine Former-commit-id: fa5b2e12a856234efca77577782f890681cdf2f5 --- .../org/hyperion/hypercon/HyperConConfig.java | 6 + .../hyperion/hypercon/gui/ConfigPanel.java | 1 + .../hypercon/gui/EffectEnginePanel.java | 85 ++++ .../gui/effectengine/ColorPicker.java | 74 ++++ .../EffectArgumentCellEditor.java | 96 ++++ .../gui/effectengine/EffectArgumentType.java | 53 +++ .../gui/effectengine/EffectEnginePanel.java | 419 ++++++++++++++++++ .../effectengine/NewEffectArgumentDialog.java | 228 ++++++++++ 8 files changed, 962 insertions(+) create mode 100644 src/config-tool/ConfigTool/src/org/hyperion/hypercon/HyperConConfig.java create mode 100644 src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/EffectEnginePanel.java create mode 100644 src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/effectengine/ColorPicker.java create mode 100644 src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/effectengine/EffectArgumentCellEditor.java create mode 100644 src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/effectengine/EffectArgumentType.java create mode 100644 src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/effectengine/EffectEnginePanel.java create mode 100644 src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/effectengine/NewEffectArgumentDialog.java diff --git a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/HyperConConfig.java b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/HyperConConfig.java new file mode 100644 index 00000000..25025f89 --- /dev/null +++ b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/HyperConConfig.java @@ -0,0 +1,6 @@ +package org.hyperion.hypercon; + +public class HyperConConfig { + + public boolean loadDefaultEffect = true; +} diff --git a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/ConfigPanel.java b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/ConfigPanel.java index 5420459b..b757aa18 100644 --- a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/ConfigPanel.java +++ b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/ConfigPanel.java @@ -189,6 +189,7 @@ public class ConfigPanel extends JPanel { mExternalPanel.add(new XbmcPanel(ledString.mMiscConfig)); mExternalPanel.add(new InterfacePanel(ledString.mMiscConfig)); + mExternalPanel.add(new org.hyperion.hypercon.gui.EffectEnginePanel()); mExternalPanel.add(Box.createVerticalGlue()); } return mExternalPanel; diff --git a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/EffectEnginePanel.java b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/EffectEnginePanel.java new file mode 100644 index 00000000..0442aac7 --- /dev/null +++ b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/EffectEnginePanel.java @@ -0,0 +1,85 @@ +package org.hyperion.hypercon.gui; + +import javax.swing.BorderFactory; +import javax.swing.GroupLayout; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; + +public class EffectEnginePanel extends JPanel { + + private JLabel mPathLabel; + private JTextField mPathField; + + private JPanel mBootSequencePanel; + private JCheckBox mBootSequenceCheck; + private JLabel mBootSequenceLabel; + private JTextField mBootSequenceField; + + public EffectEnginePanel() { + super(); + + initialise(); + } + + private void initialise() { + setBorder(BorderFactory.createTitledBorder("Effect Engine")); + + mPathLabel = new JLabel("Directory: "); + add(mPathLabel); + + mPathField = new JTextField(); + add(mPathField); + + add(getBootSequencePanel()); + + GroupLayout layout = new GroupLayout(this); + setLayout(layout); + + layout.setVerticalGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup() + .addComponent(mPathLabel) + .addComponent(mPathField)) + .addComponent(getBootSequencePanel())); + + layout.setHorizontalGroup(layout.createParallelGroup() + .addGroup(layout.createSequentialGroup() + .addComponent(mPathLabel) + .addComponent(mPathField)) + .addComponent(getBootSequencePanel())); + } + + private JPanel getBootSequencePanel() { + if (mBootSequencePanel == null) { + mBootSequencePanel = new JPanel(); + mBootSequencePanel.setBorder(BorderFactory.createTitledBorder("Bootsequence")); + + mBootSequenceCheck = new JCheckBox("Enabled"); + mBootSequencePanel.add(mBootSequenceCheck); + + mBootSequenceLabel = new JLabel("Type:"); + mBootSequencePanel.add(mBootSequenceLabel); + + mBootSequenceField = new JTextField(); + mBootSequencePanel.add(mBootSequenceField); + + GroupLayout layout = new GroupLayout(mBootSequencePanel); + mBootSequencePanel.setLayout(layout); + + layout.setVerticalGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup() + .addComponent(mBootSequenceLabel) + .addComponent(mBootSequenceField)) + .addComponent(mBootSequenceCheck)); + + layout.setHorizontalGroup(layout.createParallelGroup() + .addGroup(layout.createSequentialGroup() + .addComponent(mBootSequenceLabel) + .addComponent(mBootSequenceField)) + .addComponent(mBootSequenceCheck)); + + } + return mBootSequencePanel; + } +} diff --git a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/effectengine/ColorPicker.java b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/effectengine/ColorPicker.java new file mode 100644 index 00000000..387607b0 --- /dev/null +++ b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/effectengine/ColorPicker.java @@ -0,0 +1,74 @@ +package org.hyperion.hypercon.gui.effectengine; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JColorChooser; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPopupMenu; + +public class ColorPicker extends JComponent { + + private JLabel mLabel; + private JButton mButton; + + JPopupMenu mPopup = new JPopupMenu(); + JColorChooser mColorChooser = new JColorChooser(); + + private Color mSelectedColor = Color.RED; + + public ColorPicker() { + super(); + + initialise(); + } + + private void initialise() { + setLayout(new BorderLayout()); + + mLabel = new JLabel("[23, 123, 1]"); + add(mLabel, BorderLayout.CENTER); + + mButton = new JButton(); + mButton.setPreferredSize(new Dimension(25, 25)); + add(mButton, BorderLayout.EAST); + + mPopup.add(mColorChooser); + mButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + Color newColor = JColorChooser.showDialog(ColorPicker.this, "Select a color", mSelectedColor); + if (newColor == null) { + return; + } + setColor(newColor); + } + }); + } + + public void setColor(Color pColor) { + mSelectedColor = pColor; + mLabel.setText(String.format("[%d, %d, %d]", mSelectedColor.getRed(), mSelectedColor.getGreen(), mSelectedColor.getBlue())); + } + + public Color getColor() { + return mSelectedColor; + } + + public static void main(String[] pArgs) { + JFrame frame = new JFrame(); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + frame.setSize(240, 100); + + frame.getContentPane().setLayout(new BorderLayout()); + frame.getContentPane().add(new ColorPicker()); + + frame.setVisible(true); + } +} diff --git a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/effectengine/EffectArgumentCellEditor.java b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/effectengine/EffectArgumentCellEditor.java new file mode 100644 index 00000000..51e07587 --- /dev/null +++ b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/effectengine/EffectArgumentCellEditor.java @@ -0,0 +1,96 @@ +package org.hyperion.hypercon.gui.effectengine; + +import java.awt.Color; +import java.awt.Component; +import java.awt.event.MouseEvent; +import java.text.ParseException; +import java.util.EventObject; + +import javax.swing.AbstractCellEditor; +import javax.swing.JCheckBox; +import javax.swing.JSpinner; +import javax.swing.JSpinner.DefaultEditor; +import javax.swing.JTable; +import javax.swing.JTextField; +import javax.swing.SpinnerNumberModel; +import javax.swing.table.TableCellEditor; + + +public class EffectArgumentCellEditor extends AbstractCellEditor implements TableCellEditor { + + int selEditor = 0; + + private JSpinner mIntegerSpinner = new JSpinner(new SpinnerNumberModel(0, Integer.MIN_VALUE, Integer.MAX_VALUE, 1)); + private JSpinner mDoubleSpinner = new JSpinner(new SpinnerNumberModel(0.0, -Double.MAX_VALUE, Double.MAX_VALUE, 1.0)); + private JCheckBox mBooleanCheck = new JCheckBox(); + private JTextField mStringEditor = new JTextField(); + private ColorPicker mColorChooser = new ColorPicker(); + + @Override + public boolean shouldSelectCell(EventObject anEvent) { + return true; + } + + @Override + public boolean isCellEditable(EventObject anEvent) { + if (anEvent instanceof MouseEvent) { + return ((MouseEvent)anEvent).getClickCount() >= 2; + } + return true; + } + + @Override + public boolean stopCellEditing() { + try { + // Make sure manually editted values are comitted + ((DefaultEditor)mIntegerSpinner.getEditor()).commitEdit(); + ((DefaultEditor)mDoubleSpinner.getEditor()).commitEdit(); + } catch (ParseException e) { + } + return super.stopCellEditing(); + } + + @Override + public Object getCellEditorValue() { + switch (selEditor) { + case 0: + return mIntegerSpinner.getValue(); + case 1: + return mDoubleSpinner.getValue(); + case 2: + return mBooleanCheck.isSelected(); + case 3: + return mStringEditor.getText(); + case 4: + return mColorChooser.getColor(); + } + + return null; + } + + @Override + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + if (value instanceof Integer) { + selEditor = 0; + mIntegerSpinner.setValue((Integer)value); + return mIntegerSpinner; + } else if (value instanceof Double) { + selEditor = 1; + mDoubleSpinner.setValue((Double)value); + return mDoubleSpinner; + } else if (value instanceof Boolean) { + selEditor = 2; + mBooleanCheck.setSelected((Boolean)value); + return mBooleanCheck; + } else if (value instanceof Color) { + selEditor = 4; + mColorChooser.setColor((Color)value); + return mColorChooser; + } + + selEditor = 3; + mStringEditor.setText('"' + value.toString() + '"'); + return mStringEditor; + } + +} diff --git a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/effectengine/EffectArgumentType.java b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/effectengine/EffectArgumentType.java new file mode 100644 index 00000000..f707abb6 --- /dev/null +++ b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/effectengine/EffectArgumentType.java @@ -0,0 +1,53 @@ +package org.hyperion.hypercon.gui.effectengine; + +import java.awt.Color; + +/** + * Enumeration for the type of possible effect-arguments + */ +public enum EffectArgumentType { + /** The 'int' instance of the argument-type, maps to 'int' or {@link java.lang.Integer} */ + int_arg("int"), + /** The 'double' instance of the argument-type, maps to 'double' or {@link java.lang.Double} */ + double_arg("double"), + /** The 'color' instance of the argument-type, maps to {@link java.awt.Color} */ + color_arg("color"), + /** The 'string' instance of the argument-type, maps to {@link java.lang.String} */ + string_arg("string"); + + /** The 'pretty' name of the effect argument type (should be unique) */ + private final String mName; + + /** + * Constructs the EffectArgumentType with the given name + * + * @param pName The name of the type + */ + private EffectArgumentType(final String pName) { + mName = pName; + } + + public Object getDefaultValue() { + switch(this) { + case int_arg: + return 0; + case double_arg: + return 0.0; + case color_arg: + return Color.WHITE; + case string_arg: + return ""; + } + return ""; + } + + /** + * Returns the string representation of the argument-type, which is its set name + * + * @return The name of the EffectArgumentType + */ + @Override + public String toString() { + return mName; + } +} diff --git a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/effectengine/EffectEnginePanel.java b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/effectengine/EffectEnginePanel.java new file mode 100644 index 00000000..a94081ee --- /dev/null +++ b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/effectengine/EffectEnginePanel.java @@ -0,0 +1,419 @@ +package org.hyperion.hypercon.gui.effectengine; + +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.beans.Transient; +import java.util.Vector; + +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.ComboBoxEditor; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.JTextField; +import javax.swing.ListSelectionModel; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableColumn; + +import org.hyperion.hypercon.spec.EffectConfig; +import org.hyperion.hypercon.spec.EffectConfig.EffectArg; +import org.hyperion.hypercon.spec.EffectEngineConfig; + +public class EffectEnginePanel extends JPanel { + /** The Effect Engine configuration */ + private final EffectEngineConfig mEffectEngingeConfig; + + /** The combobox model for selecting an effect configuration */ + private final DefaultComboBoxModel mEffectModel; + + private final DefaultComboBoxModel mPythonScriptModel; + + private JPanel mControlPanel; + private JComboBox mEffectCombo; + private JButton mCloneButton; + private JButton mAddButton; + private JButton mDelButton; + + private JPanel mEffectPanel; + private JLabel mPythonLabel; + private JTextField mPythonField; + private JPanel mEffectArgumentPanel; + private JTable mEffectArgumentTable; + private JPanel mArgumentControlPanel; + private JButton mAddArgumentButton; + private JButton mDelArgumentButton; + + private AbstractTableModel mEffectArgumentTableModel = new AbstractTableModel() { + + @Override + public boolean isCellEditable(int rowIndex, int columnIndex) { + EffectConfig effect = (EffectConfig) mEffectModel.getSelectedItem(); + if (effect == null) { + return false; + } + if (rowIndex == effect.mArgs.size()) { + return columnIndex == 0; + } + return true; + }; + + @Override + public int getColumnCount() { + return 2; + } + + @Override + public String getColumnName(int column) { + switch (column) { + case 0: + return "name"; + case 1: + return "value"; + } + return ""; + }; + + @Override + public int getRowCount() { + EffectConfig effect = (EffectConfig) mEffectModel.getSelectedItem(); + if (effect == null) { + return 0; + } + return effect.mArgs.size(); + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + EffectConfig effect = (EffectConfig) mEffectModel.getSelectedItem(); + if (effect == null) { + return ""; + } + if (rowIndex == effect.mArgs.size()) { + if (columnIndex == 0) { + return "[key]"; + } + return ""; + } + if (columnIndex == 0) { + return effect.mArgs.get(rowIndex).key; + } else if (columnIndex == 1){ + return effect.mArgs.get(rowIndex).value; + + } + return ""; + } + + @Override + public void setValueAt(Object aValue, int rowIndex, int columnIndex) { + EffectConfig effect = (EffectConfig) mEffectModel.getSelectedItem(); + if (effect == null) { + return; + } + + if (rowIndex == effect.mArgs.size()) { + String key = aValue.toString().trim(); + if (key.isEmpty() || key.equals("[key]")) { + return; + } + + effect.mArgs.addElement(new EffectConfig.EffectArg(aValue.toString(), "")); + return; + } + if (columnIndex == 0) { + String key = aValue.toString().trim(); + if (!key.isEmpty()) { + effect.mArgs.get(rowIndex).key = (String)aValue; + } + } else { + effect.mArgs.get(rowIndex).value = aValue; + } + }; + }; + + public EffectEnginePanel(final EffectEngineConfig pEffectEngineConfig) { + super(); + + mEffectEngingeConfig = pEffectEngineConfig; + mEffectModel = new DefaultComboBoxModel(mEffectEngingeConfig.mEffects); + mPythonScriptModel = new DefaultComboBoxModel<>(); + for (EffectConfig effect : mEffectEngingeConfig.mEffects) { + if (mPythonScriptModel.getIndexOf(effect.mScript) >= 0) { + continue; + } + mPythonScriptModel.addElement(effect.mScript); + } + + initialise(); + + effectSelectionChanged(); + } + + private void initialise() { + setLayout(new BorderLayout()); + + add(getControlPanel(), BorderLayout.NORTH); + add(getEffectPanel(), BorderLayout.CENTER); + } + + @Override + @Transient + public Dimension getMaximumSize() { + Dimension maxSize = super.getMaximumSize(); + Dimension prefSize = super.getPreferredSize(); + return new Dimension(maxSize.width, prefSize.height); + } + + private void effectSelectionChanged() { + EffectConfig effect = (EffectConfig)mEffectModel.getSelectedItem(); + + // Enable option for the selected effect or disable if none selected + mEffectPanel.setEnabled(effect != null); + mPythonLabel.setEnabled(effect != null); + mPythonField.setEnabled(effect != null); + mEffectArgumentTable.setEnabled(effect != null); + + + if (effect == null) { + // Clear all fields + mPythonField.setText(""); + mEffectArgumentTableModel.fireTableDataChanged(); + return; + } else { + // Update fields based on the selected effect + mPythonField.setText(effect.mScript); + mEffectArgumentTableModel.fireTableDataChanged(); + } + } + + private JPanel getControlPanel() { + if (mControlPanel == null) { + mControlPanel = new JPanel(); + mControlPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 2, 5)); + mControlPanel.setPreferredSize(new Dimension(150, 25)); + mControlPanel.setLayout(new BoxLayout(mControlPanel, BoxLayout.LINE_AXIS)); + + mEffectCombo = new JComboBox<>(mEffectModel); + mEffectCombo.setEditable(true); + mEffectCombo.setEditor(new ComboBoxEditor() { + private final JTextField mTextField = new JTextField(); + + private EffectConfig mCurrentEffect = null; + + @Override + public void setItem(Object anObject) { + if (anObject instanceof EffectConfig) { + mCurrentEffect = (EffectConfig) anObject; + mTextField.setText(mCurrentEffect.mId); + } + } + + @Override + public void selectAll() { + if (mCurrentEffect == null) { + return; + } + mTextField.selectAll(); + } + + @Override + public Object getItem() { + String newId = mTextField.getText().trim(); + if (newId.isEmpty() || newId.contains("\"")) { + return mCurrentEffect; + } + mCurrentEffect.mId = newId; + return mCurrentEffect; } + + @Override + public Component getEditorComponent() { + return mTextField; + } + + private final Vector mActionListeners = new Vector<>(); + @Override + public void addActionListener(ActionListener l) { + mActionListeners.add(l); + } + @Override + public void removeActionListener(ActionListener l) { + mActionListeners.remove(l); + } + }); + mEffectCombo.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + effectSelectionChanged(); + } + }); + mControlPanel.add(mEffectCombo); + + mCloneButton = new JButton("Clone"); + mCloneButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + EffectConfig effect = (EffectConfig) mEffectModel.getSelectedItem(); + EffectConfig effectClone = effect.clone(); + effectClone.mId += " [clone]"; + mEffectModel.addElement(effectClone); + mEffectModel.setSelectedItem(effectClone); + } + }); + mControlPanel.add(mCloneButton); + + mAddButton = new JButton("Add"); + mAddButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + String newId = JOptionPane.showInputDialog(mAddButton, "Name of the new effect: ", "Effect Name", JOptionPane.QUESTION_MESSAGE); + // Make that an ID is set + if (newId == null || newId.isEmpty()) { + return; + } + // Make sure the ID does not yet exist + for (EffectConfig effect : mEffectEngingeConfig.mEffects) { + if (effect.mId.equalsIgnoreCase(newId)) { + JOptionPane.showMessageDialog(mAddButton, "Given name(" + effect.mId + ") allready exists", "Duplicate effect name", JOptionPane.ERROR_MESSAGE); + return; + } + } + + EffectConfig newConfig = new EffectConfig(); + newConfig.mId = newId; + mEffectModel.addElement(newConfig); + mEffectModel.setSelectedItem(newConfig); + } + }); + mControlPanel.add(mAddButton); + + mDelButton = new JButton("Del"); + mDelButton.setEnabled(mEffectModel.getSize() > 0); + mDelButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (mEffectModel.getSelectedItem() != null) { + mEffectModel.removeElement(mEffectModel.getSelectedItem()); + } + mDelButton.setEnabled(mEffectModel.getSize() > 0); + } + }); + mControlPanel.add(mDelButton); + } + return mControlPanel; + } + + private JPanel getEffectPanel() { + if (mEffectPanel == null) { + mEffectPanel = new JPanel(); + mEffectPanel.setBorder(BorderFactory.createTitledBorder("")); + mEffectPanel.setLayout(new BoxLayout(mEffectPanel, BoxLayout.PAGE_AXIS)); + + JPanel subPanel = new JPanel(new BorderLayout()); + subPanel.setPreferredSize(new Dimension(150, 25)); + subPanel.setMaximumSize(new Dimension(20000, 20)); + mEffectPanel.add(subPanel); + + mPythonLabel = new JLabel("Python: "); + subPanel.add(mPythonLabel, BorderLayout.WEST); + + mPythonField = new JTextField(); + mPythonField.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void removeUpdate(DocumentEvent e) { + update(); + } + @Override + public void insertUpdate(DocumentEvent e) { + update(); + } + @Override + public void changedUpdate(DocumentEvent e) { + update(); + } + private void update() { + EffectConfig effect = (EffectConfig) mEffectModel.getSelectedItem(); + if (effect == null) { + return; + } + effect.mScript = mPythonField.getText(); + } + }); + mPythonField.setMaximumSize(new Dimension(150, 25)); + subPanel.add(mPythonField); + + mEffectArgumentPanel = new JPanel(); + mEffectArgumentPanel.setBorder(BorderFactory.createTitledBorder("Arguments")); + mEffectArgumentPanel.setLayout(new BorderLayout()); + + mEffectArgumentTable = new JTable(mEffectArgumentTableModel); + mEffectArgumentTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + mEffectArgumentPanel.add(new JScrollPane(mEffectArgumentTable), BorderLayout.CENTER); + + mEffectArgumentPanel.add(getArgumentControlPanel(), BorderLayout.SOUTH); + + mEffectPanel.add(mEffectArgumentPanel); + +// mEffectArgumentTable.getColumnModel().getColumn(0).setMaxWidth(100); + TableColumn col = mEffectArgumentTable.getColumnModel().getColumn(1); +// col.setMaxWidth(100); + col.setCellEditor(new EffectArgumentCellEditor()); + col.setCellRenderer(new DefaultTableCellRenderer() { + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + if (value instanceof Color) { + Color color = (Color)value; + value = String.format("[%d, %d, %d]", color.getRed(), color.getGreen(), color.getBlue()); + } + return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + } + }); + } + return mEffectPanel; + } + + private JPanel getArgumentControlPanel() { + if (mArgumentControlPanel == null) { + mArgumentControlPanel = new JPanel(); + + mAddArgumentButton = new JButton("+"); + mAddArgumentButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + EffectArg newArg = NewEffectArgumentDialog.showDialog(); + if (newArg != null) { + ((EffectConfig)mEffectModel.getSelectedItem()).mArgs.add(newArg); + int row = ((EffectConfig)mEffectModel.getSelectedItem()).mArgs.size()-1; + mEffectArgumentTableModel.fireTableRowsInserted(row, row); + } + } + }); + mArgumentControlPanel.add(mAddArgumentButton); + + mDelArgumentButton = new JButton("-"); + mDelArgumentButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + int selRow = mEffectArgumentTable.getSelectedRow(); + if (selRow >= 0) { + ((EffectConfig)mEffectModel.getSelectedItem()).mArgs.remove(selRow); + mEffectArgumentTableModel.fireTableRowsDeleted(selRow, selRow); + } + } + }); + mArgumentControlPanel.add(mDelArgumentButton); + } + return mArgumentControlPanel; + } +} diff --git a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/effectengine/NewEffectArgumentDialog.java b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/effectengine/NewEffectArgumentDialog.java new file mode 100644 index 00000000..05efc046 --- /dev/null +++ b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/effectengine/NewEffectArgumentDialog.java @@ -0,0 +1,228 @@ +package org.hyperion.hypercon.gui.effectengine; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.BorderFactory; +import javax.swing.GroupLayout; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +import org.hyperion.hypercon.spec.EffectConfig.EffectArg; + +/** + * Dialog for specifying the required parameters (name and type) of an effect argument. + */ +public class NewEffectArgumentDialog extends JDialog { + + /** Return mask for cancellation (cancel-button, closing, etc) */ + public static final int CANCEL_OPTION = 1; + /** Return mask for accepting (ok-button) */ + public static final int OK_OPTION = 2; + + /** Panel contains the parameters that require configuration by the user */ + private JPanel mArgumentPanel; + private JLabel mNameLabel; + private JTextField mNameField; + private JLabel mTypeLabel; + private JComboBox mTypeCombo; + + /** Panel contains the controls for the dialog */ + private JPanel mControlPanel; + private JButton mOkButton; + private JButton mCancelButton; + + /** The current return value for the dialog */ + private int mReturnValue; + + /** + * Constructs an empty NewEffectArgumentDialog + */ + public NewEffectArgumentDialog() { + super(); + + initialise(); + + mNameField.addKeyListener(mKeyListener); + mTypeCombo.addKeyListener(mKeyListener); + mOkButton.addKeyListener(mKeyListener); + mCancelButton.addKeyListener(mKeyListener); + } + + /** + * Initialises the dialog, constructing all its sub-panels and components + */ + private void initialise() { + setTitle("New effect argument"); + setResizable(false); + setSize(320,150); + setLayout(new BorderLayout()); + setModal(true); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + + add(getArgumentPanel(), BorderLayout.CENTER); + add(getControlPanel(), BorderLayout.SOUTH); + + mOkAction.setEnabled(false); + } + + /** + * Initialises, if not yet initialised, and returns the argument configuration panel + * + * @return The argument configuration panel ({@link #mArgumentPanel}) + */ + private JPanel getArgumentPanel() { + if (mArgumentPanel == null) { + mArgumentPanel = new JPanel(); + mArgumentPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); + + mNameLabel = new JLabel("Name: "); + mArgumentPanel.add(mNameLabel); + + mNameField = new JTextField(""); + mNameField.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void removeUpdate(DocumentEvent e) { + documentChanged(); + } + + @Override + public void insertUpdate(DocumentEvent e) { + documentChanged(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + documentChanged(); + } + + private void documentChanged() { + String name = mNameField.getText(); + boolean validName = !(name.isEmpty() || name.contains("\"")); + mOkAction.setEnabled(validName); + } + }); + mArgumentPanel.add(mNameField); + + mTypeLabel = new JLabel("Type: "); + mArgumentPanel.add(mTypeLabel); + + mTypeCombo = new JComboBox<>(EffectArgumentType.values()); + mArgumentPanel.add(mTypeCombo); + + GroupLayout layout = new GroupLayout(mArgumentPanel); + layout.setAutoCreateGaps(true); + mArgumentPanel.setLayout(layout); + + layout.setHorizontalGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup() + .addComponent(mNameLabel) + .addComponent(mTypeLabel)) + .addGroup(layout.createParallelGroup() + .addComponent(mNameField) + .addComponent(mTypeCombo))); + + layout.setVerticalGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup() + .addComponent(mNameLabel) + .addComponent(mNameField)) + .addGroup(layout.createParallelGroup() + .addComponent(mTypeLabel) + .addComponent(mTypeCombo))); + } + return mArgumentPanel; + } + + /** + * Initialises, if not yet initialised, and returns the dialog control panel + * + * @return The dialog control panel ({@link #mControlPanel}) + */ + private JPanel getControlPanel() { + if (mControlPanel == null) { + mControlPanel = new JPanel(); + mControlPanel.setLayout(new FlowLayout(FlowLayout.TRAILING)); + + mOkButton = new JButton(mOkAction); + mControlPanel.add(mOkButton); + + mCancelButton = new JButton(mCancelAction); + mControlPanel.add(mCancelButton); + } + return mControlPanel; + } + + /** + * Shows a new instance of the NewEffectArgumentDialog and constructs anew EffectArg. + * + * @return The newly constructed argument (or null if cancelled) + */ + public static EffectArg showDialog() { + NewEffectArgumentDialog dialog = new NewEffectArgumentDialog(); + dialog.mReturnValue = CANCEL_OPTION; + dialog.setVisible(true); + if (dialog.mReturnValue == OK_OPTION) { + String name = dialog.mNameField.getText(); + EffectArgumentType type = (EffectArgumentType) dialog.mTypeCombo.getSelectedItem(); + + return new EffectArg(name, type.getDefaultValue()); + } + + return null; + } + + private final KeyListener mKeyListener = new KeyListener() { + @Override + public void keyTyped(KeyEvent e) { + if (e.getKeyChar() == KeyEvent.VK_ENTER) { + if (mOkAction.isEnabled()) { + mOkAction.actionPerformed(new ActionEvent(e.getSource(), e.getID(), "Ok")); + } + } else if (e.getKeyChar() == KeyEvent.VK_ESCAPE) { + mCancelAction.actionPerformed(new ActionEvent(e.getSource(), e.getID(), "Cancel")); + } + } + @Override + public void keyReleased(KeyEvent e) { + } + @Override + public void keyPressed(KeyEvent e) { + } + }; + + /** Action for handling 'OK' */ + private final Action mOkAction = new AbstractAction("Ok") { + @Override + public void actionPerformed(ActionEvent e) { + mReturnValue = OK_OPTION; + setVisible(false); + } + }; + + /** Action for handling 'CANCEL' */ + private final Action mCancelAction = new AbstractAction("Cancel") { + @Override + public void actionPerformed(ActionEvent e) { + mReturnValue = CANCEL_OPTION; + setVisible(false); + } + }; + + /** + * Test entry-point for showing the dialog + */ + public static void main(String[] pArgs) { + NewEffectArgumentDialog.showDialog(); + } +}