eva2/src/eva2/gui/PropertySheetPanel.java
Fabian Becker d2059a5852 Major update! Some related projects might be broken from now on - if so, let me know via email.
This is the first of a series of commits to manifest the new graphical user interface of EvA2.

This commit includes (incomplete list)

- New Icons that are used in the GUI
- A completely refactored GUI with a one-window layout.
- Plots/Text Output will now appear on a JDesktopPane
- Configuration can be done via the sidebar (not yet completely functional)
- New layouts for TabbedPane (currently not active) and JButton (vertical text)
- Code cleanup / removal of TRACE variable
- More classes are now using the Logging facility.

refs #8, #10, #14
fixes #12
2012-05-14 13:57:39 +00:00

1144 lines
42 KiB
Java

package eva2.gui;
/*
* Title: EvA2
* Description:
* Copyright: Copyright (c) 2003
* Company: University of Tuebingen, Computer Architecture
* @author Holger Ulmer, Felix Streichert, Hannes Planatscher
* @version: $Revision: 319 $
* $Date: 2007-12-05 11:29:32 +0100 (Wed, 05 Dec 2007) $
* $Author: mkron $
*/
import eva2.tools.EVAHELP;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.EventObject;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
import javax.swing.event.CellEditorListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;
/**
* There are some trick methods interpreted here. Check EvA2Notes.txt.
*/
public class PropertySheetPanel extends JPanel implements PropertyChangeListener {
public final static Logger LOGGER = Logger.getLogger(eva2.EvAInfo.defaultLogger);
/**
* The target object being edited.
*/
private Object targetObject;
/**
* Holds properties of the target.
*/
private PropertyDescriptor propertyDescriptors[];
/**
* Holds the methods of the target.
*/
private MethodDescriptor methodDescriptors[];
/**
* Holds property editors of the object.
*/
private PropertyEditor propertyEditors[];
/**
* Holds current object values for each property.
*/
private Object objectValues[];
/**
* Stores GUI components containing each editing component.
*/
private JComponent views[];
private JComponent viewWrappers[];
/**
* The labels for each property.
*/
private JLabel propertyLabels[];
/**
* The tool tip text for each property.
*/
private String toolTips[];
/**
* StringBuffer containing help text for the object being edited
*/
// private StringBuffer m_HelpText;
private String className;
/**
* Button to pop up the full help text in a separate frame.
*/
private JButton helpButton;
/**
* A count of the number of properties we have an editor for.
*/
private int numEditableProperties = 0;
/**
* How long should a tip text line be (translated to HTML).
*/
private int tipTextLineLen = 50;
/**
* A support object for handling property change listeners.
*/
private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
/**
* set true to use the GOE by default if no other editor is registered.
*/
// If true, tool tips are used up to the first point only.
boolean stripToolTipToFirstPoint = false;
private JTable propertyTable;
private DefaultTableModel propertyTableModel;
/**
* Creates the property sheet panel.
*/
public PropertySheetPanel() {
if (propertyChangeSupport == null) {
propertyChangeSupport = new PropertyChangeSupport(this);
}
}
/**
* Updates the property sheet panel with a changed property and also passed
* the event along.
*
* @param evt a value of type 'PropertyChangeEvent'
*/
public void propertyChange(PropertyChangeEvent evt) {
wasModified(evt); // Let our panel update before guys downstream
propertyChangeSupport.removePropertyChangeListener(this);
propertyChangeSupport.firePropertyChange("", null, targetObject);
propertyChangeSupport.addPropertyChangeListener(this);
}
public void addPropertyChangeListener(PropertyChangeListener l) {
if (propertyChangeSupport == null) {
propertyChangeSupport = new PropertyChangeSupport(this);
}
propertyChangeSupport.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(PropertyChangeListener l) {
if (propertyChangeSupport == null) {
propertyChangeSupport = new PropertyChangeSupport(this);
}
propertyChangeSupport.removePropertyChangeListener(l);
}
/**
* Create a fitting viewer component for an editor instance. If none can be
* identified, null is returned.
*
* @param editor
* @return
*/
public static JComponent getView(PropertyEditor editor) {
JComponent view = null;
// Now figure out how to display it...
if (editor.isPaintable() && editor.supportsCustomEditor()) {
view = new PropertyPanel(editor);
} else {
String[] tags = editor.getTags();
if (tags != null) {
if ((tags.length == 2) && (tags[0].equals("True")) && (tags[1].equals("False"))) {
view = new PropertyBoolSelector(editor);
} else {
view = new PropertyValueSelector(editor);
}
} else {
if (editor.getAsText() != null) {
view = new PropertyText(editor);
} else {
view = null;
}
}
}
return view;
}
/**
* Sets a new target object for customisation.
*
* @param targ a value of type 'Object'
*/
public synchronized void setTarget(Object targ) {
propertyTableModel = new DefaultTableModel();
propertyTableModel.addColumn("Key");
propertyTableModel.addColumn("Value");
propertyTable = new JTable(propertyTableModel);
propertyTable.setDefaultRenderer(Object.class, new PropertyCellRenderer());
propertyTable.setDefaultEditor(Object.class, new PropertyCellEditor());
propertyTable.setRowHeight(20);
// Close any child windows at this point
removeAll();
setLayout(new GridBagLayout());
setVisible(false);
numEditableProperties = 0;
targetObject = targ;
try {
BeanInfo bi = Introspector.getBeanInfo(targetObject.getClass());
propertyDescriptors = bi.getPropertyDescriptors();
methodDescriptors = bi.getMethodDescriptors();
} catch (IntrospectionException ex) {
System.err.println("PropertySheetPanel.setTarget(): Couldn't introspect");
return;
}
int rowHeight = 12;
GridBagConstraints gbConstraints = new GridBagConstraints();
gbConstraints.fill = GridBagConstraints.BOTH;
// Look for a globalInfo method that returns a string
// describing the target
int methsFound = 0; // dont loop too long, so count until all found
for (MethodDescriptor methodDescriptor : methodDescriptors) {
String name = methodDescriptor.getDisplayName();
Method meth = methodDescriptor.getMethod();
if (name.equals("globalInfo")) {
JPanel infoPanel = makeInfoPanel(meth, targ, rowHeight);
if (infoPanel != null) {
gbConstraints.gridx = 0;
gbConstraints.gridy = 0;
gbConstraints.fill = GridBagConstraints.HORIZONTAL;
gbConstraints.anchor = GridBagConstraints.PAGE_START;
add(infoPanel, gbConstraints);
}
methsFound++;
} // end if (name.equals("globalInfo")) {
else if (name.equals("hideHideable")) {
Object args[] = {};
try {
meth.invoke(targetObject, args);
} catch (Exception ex) {
}
methsFound++;
} else if (name.equals("customPropertyOrder")) {
methsFound++;
reorderProperties(meth);
}
if (methsFound == 3) {
break; // small speed-up
}
} // end for (int i = 0; i < m_Methods.length; i++) {
// restore hide states of all properties
// GenericObjectEditor.setHideProperties(m_Target.getClass(), hideStateBackup);
// Now lets search for the individual properties, their
// values, views and editors...
propertyEditors = new PropertyEditor[propertyDescriptors.length];
// collect property values if possible
objectValues = getValues(targetObject, propertyDescriptors, true, false, true);
views = new JComponent[propertyDescriptors.length];
viewWrappers = new JComponent[propertyDescriptors.length];
propertyLabels = new JLabel[propertyDescriptors.length];
toolTips = new String[propertyDescriptors.length];
for (int i = 0; i < propertyDescriptors.length; i++) {
// For each property do this
// Don't display hidden or expert properties.
// if (m_Properties[i].isHidden() || m_Properties[i].isExpert()) continue;
// we now look at hidden properties, they can be shown or hidden dynamically (MK)
String name = propertyDescriptors[i].getDisplayName();
if (objectValues[i] == null) {
continue; // expert, hidden, or no getter/setter available
}
JComponent newView = null;
try {
propertyEditors[i] = makeEditor(propertyDescriptors[i], name, objectValues[i]);
if (propertyEditors[i] == null) {
continue;
}
toolTips[i] = BeanInspector.getToolTipText(name, methodDescriptors, targetObject, stripToolTipToFirstPoint, tipTextLineLen);
newView = getView(propertyEditors[i]);
if (newView == null) {
System.err.println("Warning: Property \"" + name + "\" has non-displayabale editor. Skipping.");
continue;
}
} catch (Exception ex) {
System.out.println("Skipping property " + name + " ; exception: " + ex);
ex.printStackTrace();
continue;
} // end try
// Add some specific display for some greeks here
name = translateGreek(name);
name = eva2.tools.StringTools.humaniseCamelCase(name);
propertyTableModel.addRow(new Object[]{name, newView});
}
JScrollPane scrollableTable = new JScrollPane(propertyTable, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
gbConstraints.gridx = 0;
gbConstraints.gridy = 1;
gbConstraints.weightx = 1.0;
gbConstraints.weighty = 1.0;
gbConstraints.fill = GridBagConstraints.BOTH;
scrollableTable.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
add(scrollableTable, gbConstraints);
validate();
setVisible(true);
}
public static PropertyDescriptor[] getProperties(Object target) {
BeanInfo bi;
try {
bi = Introspector.getBeanInfo(target.getClass());
} catch (IntrospectionException e) {
e.printStackTrace();
return null;
}
return bi.getPropertyDescriptors();
}
public static String[] getPropertyNames(Object target) {
return getNames(getProperties(target));
}
public static Object[] getPropertyValues(Object target, boolean omitExpert, boolean omitHidden, boolean onlySetAndGettable) {
return getValues(target, getProperties(target), omitExpert, omitHidden, onlySetAndGettable);
}
public static String[] getNames(PropertyDescriptor[] props) {
String[] names = new String[props.length];
for (int i = 0; i < props.length; i++) {
names[i] = props[i].getDisplayName();
}
return names;
}
/**
* Cycle the properties and request the value of each in an array. Null
* values may indicate missing getter/setter, expert flag or hidden flag set
* depending on the parameters. Note that to show hidden properties
* dynamically, views may need be constructed for them anyways, so do not
* omit them here.
*
* @param props
* @param omitExpert
* @param omitHidden
* @param onlySetAndGettable
* @return
*/
public static Object[] getValues(Object target, PropertyDescriptor[] props, boolean omitExpert, boolean omitHidden, boolean onlySetAndGettable) {
Object[] values = new Object[props.length];
for (int i = 0; i < props.length; i++) {
// For each property do this
// Don't display hidden or expert properties.
// if (m_Properties[i].isHidden() || m_Properties[i].isExpert()) continue;
// we now look at hidden properties, they can be shown or hidden dynamically (MK)
String name = props[i].getDisplayName();
if (props[i].isExpert() && omitExpert) {
continue;
}
if (props[i].isHidden() && omitHidden) {
continue; // TOOD this might be a problem - hidden values which can be shown dynamically will need a viewer even if hidden
}
Method getter = props[i].getReadMethod();
Method setter = props[i].getWriteMethod();
// Only display read/write properties.
if (onlySetAndGettable && (getter == null || setter == null)) {
continue;
}
Object args[] = {};
Object value = null;
try {
value = getter.invoke(target, args);
} catch (Exception ex) {
System.out.println("Exception on getting value for property " + name + " on target " + target.toString());
ex.printStackTrace();
values[i] = null;
}
// PropertyEditor editor = null;
//Class pec = m_Properties[i].getPropertyEditorClass();
values[i] = value;
} // end for each property
return values;
}
/**
* Create a label to be shown if no other properties are shown.
*
* @param componentOffset
* @param gbLayout
* @return
*/
private JLabel createDummyLabel(int componentOffset, GridBagLayout gbLayout) {
JLabel empty = new JLabel("No editable properties", SwingConstants.CENTER);
Dimension d = empty.getPreferredSize();
empty.setPreferredSize(new Dimension(d.width * 2, d.height * 2));
empty.setBorder(BorderFactory.createEmptyBorder(10, 5, 0, 10));
GridBagConstraints gbConstraints = new GridBagConstraints();
gbConstraints.anchor = GridBagConstraints.CENTER;
gbConstraints.fill = GridBagConstraints.HORIZONTAL;
gbConstraints.gridy = componentOffset;
gbConstraints.gridx = 0;
gbLayout.setConstraints(empty, gbConstraints);
return empty;
}
private PropertyEditor makeEditor(PropertyDescriptor property, String name, Object value) {
PropertyEditor editor = PropertyEditorProvider.findEditor(property, value);
if (editor == null) {
return null;
}
// Don't try to set null values:
if (value == null) {
// If it's a user-defined property we give a warning.
String getterClass = property.getReadMethod().getDeclaringClass().getName();
if (getterClass.indexOf("java.") != 0) {
System.out.println("Warning: Property \"" + name + "\" of class " + targetObject.getClass() + " has null initial value. Skipping.");
}
return null;
}
editor.setValue(value);
// System.out.println("PSP editor class: " + editor.getClass());
editor.addPropertyChangeListener(this);
return editor;
}
private void addLabelView(int componentOffset, GridBagLayout gbLayout,
int i, String name, JComponent newView) {
propertyLabels[i] = makeLabel(name);
views[i] = newView;
viewWrappers[i] = new JPanel();
viewWrappers[i].setLayout(new BorderLayout());
gbLayout.setConstraints(propertyLabels[i], makeLabelConstraints(i + componentOffset));
add(propertyLabels[i]);
JPanel newPanel = makeViewPanel(toolTips[i], propertyLabels[i], views[i], viewWrappers[i]);
gbLayout.setConstraints(newPanel, makeViewConstraints(i + componentOffset));
add(newPanel);
}
private JLabel makeLabel(String name) {
JLabel label = new JLabel(name, SwingConstants.RIGHT);
label.setBorder(BorderFactory.createEmptyBorder(10, 10, 0, 5));
return label;
}
private static JPanel makeViewPanel(String tipText, JLabel label,
JComponent view, JComponent viewWrapper) {
JPanel newPanel = new JPanel();
if (tipText != null) {
label.setToolTipText(tipText);
view.setToolTipText(tipText);
}
newPanel.setBorder(BorderFactory.createEmptyBorder(10, 5, 0, 10));
newPanel.setLayout(new BorderLayout());
// @todo: Streiche here i could add the ViewWrapper
viewWrapper.add(view, BorderLayout.CENTER);
newPanel.add(viewWrapper, BorderLayout.CENTER);
return newPanel;
}
private GridBagConstraints makeLabelConstraints(int componentIndex) {
GridBagConstraints gbConstraints = new GridBagConstraints();
gbConstraints.anchor = GridBagConstraints.EAST;
gbConstraints.fill = GridBagConstraints.HORIZONTAL;
gbConstraints.gridy = componentIndex;
gbConstraints.gridx = 0;
return gbConstraints;
}
private GridBagConstraints makeViewConstraints(int componentIndex) {
GridBagConstraints gbConstraints = new GridBagConstraints();
gbConstraints.anchor = GridBagConstraints.WEST;
gbConstraints.fill = GridBagConstraints.BOTH;
gbConstraints.gridy = componentIndex;
gbConstraints.gridx = 1;
gbConstraints.weightx = 100;
return gbConstraints;
}
/**
* Be sure to give a clone
*
* @param oldProps
* @param meth
* @return
*/
private PropertyDescriptor[] reorderProperties(Method meth) {
// Mathematics.revertArray(oldProps, newProps);
Object[] args = {};
Object retV = null;
PropertyDescriptor[] newProps = null;
try {
retV = meth.invoke(targetObject, args); // should return String[] to be interpreted as a list of ordered properties
} catch (Exception ex) {
}
if (retV != null) {
try {
if (retV.getClass().isArray()) { // reorder the properties
String[] swProps = (String[]) retV;
PropertyDescriptor[] oldProps = propertyDescriptors.clone();
newProps = new PropertyDescriptor[oldProps.length];
//int findFirst=findFirstProp(props[0], oldProps);
int firstNonNull = 0;
for (int i = 0; i < oldProps.length; i++) {
if (i < swProps.length) {
int pInOld = findProp(oldProps, swProps[i]);
newProps[i] = oldProps[pInOld];
oldProps[pInOld] = null;
} else {
firstNonNull = findFirstNonNullAfter(oldProps, firstNonNull);
newProps[i] = oldProps[firstNonNull];
firstNonNull++;
}
}
propertyDescriptors = newProps;
}
} catch (Exception e) {
System.err.println("Error during reordering properties: " + e.getMessage());
return propertyDescriptors;
}
}
return newProps;
}
/**
* Find the first non-null entry in an Array at or after the given index and
* return its index. If only null entries are found, -1 is returned.
*
* @param arr
* @param firstLook
* @return
*/
private int findFirstNonNullAfter(PropertyDescriptor[] arr,
int firstLook) {
for (int i = firstLook; i < arr.length; i++) {
if (arr[i] != null) {
return i;
}
}
return -1;
}
/**
* Find a string property in an array and return its index or -1 if not
* found.
*
* @param oldProps
* @param string
* @return
*/
private int findProp(PropertyDescriptor[] oldProps, String string) {
for (int i = 0; i < oldProps.length; i++) {
if (oldProps[i] == null) {
continue;
}
String name = oldProps[i].getDisplayName();
if (name.compareTo(string) == 0) {
return i;
}
}
System.err.println("Error, property not found: " + string);
return -1;
}
private JPanel makeInfoPanel(Method meth, Object targ, int rowHeight) {
if (meth.getReturnType().equals(String.class)) {
try {
Object args[] = {};
String globalInfo = (String) (meth.invoke(targetObject, args));
className = targ.getClass().getName();
helpButton = new JButton("Help");
helpButton.setToolTipText("More information about " + className);
helpButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
openHelpFrame();
}
});
JTextArea infoTextArea = new JTextArea();
infoTextArea.setText(globalInfo);
infoTextArea.setFont(new Font("SansSerif", Font.PLAIN, rowHeight));
infoTextArea.setEditable(false);
infoTextArea.setLineWrap(true);
infoTextArea.setWrapStyleWord(true);
infoTextArea.setBackground(getBackground());
//infoTextArea.setSize(infoTextArea.getPreferredSize());
JPanel infoPanel = new JPanel();
infoPanel.setBorder(BorderFactory.createTitledBorder("Info"));
infoPanel.setLayout(new BorderLayout());
infoPanel.add(infoTextArea, BorderLayout.CENTER);
if (HtmlDemo.resourceExists(getHelpFileName())) {
// this means that the expected URL really exists
infoPanel.add(helpButton, BorderLayout.LINE_END);
} else {
LOGGER.log(Level.FINE, "Not adding help button because of missing {0}", getHelpFileName());
}
return infoPanel;
} catch (Exception ex) {
}
}
return null;
}
private String translateGreek(String name) {
// Add some specific display for some greeks here
if (name.equalsIgnoreCase("alpha")) {
return "\u03B1";
}
if (name.equalsIgnoreCase("beta")) {
return "\u03B2";
}
if (name.equalsIgnoreCase("gamma")) {
return "\u03B3";
}
if (name.equalsIgnoreCase("gammab")) {
return "\u0393";
}
if (name.equalsIgnoreCase("delta")) {
return "\u03B4";
}
if (name.equalsIgnoreCase("deltab")) {
return "\u0394";
}
if ((name.equalsIgnoreCase("epsi")) || (name.equalsIgnoreCase("epsilon"))) {
return "\u03B5";
}
if (name.equalsIgnoreCase("zeta")) {
return "\u03B6";
}
if (name.equalsIgnoreCase("theta")) {
return "\u03D1";
}
if (name.equalsIgnoreCase("thetab")) {
return "\u0398";
}
if (name.equalsIgnoreCase("iota")) {
return "\u03B9";
}
if (name.equalsIgnoreCase("kappa")) {
return "\u03BA";
}
if (name.equalsIgnoreCase("lambda")) {
return "\u03BB";
}
if (name.equalsIgnoreCase("lambdab")) {
return "\u039B";
}
if (name.equalsIgnoreCase("rho")) {
return "\u03C1";
}
if (name.equalsIgnoreCase("sigma")) {
return "\u03C3";
}
if (name.equalsIgnoreCase("sigmab")) {
return "\u03A3";
}
if (name.equalsIgnoreCase("tau")) {
return "\u03C4";
}
if (name.equalsIgnoreCase("upsilon")) {
return "\u03C5";
}
if (name.equalsIgnoreCase("upsilonb")) {
return "\u03D2";
}
if (name.equalsIgnoreCase("omega")) {
return "\u03C9";
}
if (name.equalsIgnoreCase("omegab")) {
return "\u03A9";
}
// these are too small
if (name.equalsIgnoreCase("eta")) {
return "\u03B7";
}
if (name.equalsIgnoreCase("psi")) {
return "\u03C8";
}
if (name.equalsIgnoreCase("psib")) {
return "\u03A8";
}
if (name.equalsIgnoreCase("phi")) {
return "\u03D5";
}
if (name.equalsIgnoreCase("phib")) {
return "\u03A6";
}
if (name.equalsIgnoreCase("chi")) {
return "\u03C7";
}
if ((name.equalsIgnoreCase("mu")) || (name.equalsIgnoreCase("my")) || (name.equalsIgnoreCase("myu"))) {
return "\u03BC";
}
if (name.equalsIgnoreCase("nu")) {
return "\u03BD";
}
if (name.equalsIgnoreCase("xi")) {
return "\u03BE";
}
if (name.equalsIgnoreCase("xib")) {
return "\u039E";
}
if (name.equalsIgnoreCase("pi")) {
return "\u03C0";
}
if (name.equalsIgnoreCase("pib")) {
return "\u03A0";
}
return name;
}
/**
* Get the html help file name.
*
* @return
*/
protected String getHelpFileName() {
return EVAHELP.cutClassName(className) + ".html";
}
/**
* This method opens a help frame.
*/
protected void openHelpFrame() {
HtmlDemo temp = new HtmlDemo(getHelpFileName());
temp.show();
}
/**
* Gets the number of editable properties for the current target.
*
* @return the number of editable properties.
*/
public int editableProperties() {
return numEditableProperties;
}
/**
* Return true if the modification was successful.
*
* @param i
* @param newValue
* @return
*/
synchronized boolean updateValue(int i, Object newValue) {
PropertyDescriptor property = propertyDescriptors[i];
Method getter = propertyDescriptors[i].getReadMethod();
objectValues[i] = newValue;
Method setter = property.getWriteMethod();
// @todo: Streiche so something was changed, i could check if i have to change the editor
PropertyEditor tmpEdit = null;
// the findEditor method using properties may retrieve a primitive editor, the other one, for obscure reasons, cant.
// so Ill use the mightier first.
tmpEdit = PropertyEditorProvider.findEditor(propertyDescriptors[i], newValue);
if (tmpEdit == null) {
tmpEdit = PropertyEditorProvider.findEditor(propertyDescriptors[i].getPropertyType());
}
if (tmpEdit.getClass() != propertyEditors[i].getClass()) {
objectValues[i] = newValue;
propertyEditors[i] = tmpEdit;
if (tmpEdit instanceof GenericObjectEditor) {
((GenericObjectEditor) tmpEdit).setClassType(propertyDescriptors[i].getPropertyType());
}
propertyEditors[i].setValue(newValue);
JComponent newView = null;
newView = getView(tmpEdit);
if (newView == null) {
System.err.println("Warning: Property \"" + propertyDescriptors[i].getDisplayName() + "\" has non-displayabale editor. Skipping.");
return false;
}
propertyEditors[i].addPropertyChangeListener(this);
views[i] = newView;
if (toolTips[i] != null) {
views[i].setToolTipText(toolTips[i]);
}
viewWrappers[i].removeAll();
viewWrappers[i].setLayout(new BorderLayout());
viewWrappers[i].add(views[i], BorderLayout.CENTER);
viewWrappers[i].repaint();
}
// System.out.println("Value: "+value +" / m_Values[i]: " + m_Values[i]);
// Now try to update the target with the new value of the property
// and allow the target to do some changes to the value, therefore
// reread the new value from the target
try {
Object args[] = {newValue};
args[0] = newValue;
Object args2[] = {};
// setting the current value to the target object
setter.invoke(targetObject, args);
// i could also get the new value
//value = getter.invoke(m_Target, args2);
// Now i'm reading the set value from the target to my local values
objectValues[i] = getter.invoke(targetObject, args2);
if (newValue instanceof Integer) {
// This could check whether i have to set the value back to
// the editor, this would allow to check myu and lambda
// why shouldn't i do this for every property!?
// System.out.println("value: "+((Integer)value).intValue());
// System.out.println(" m_Values[i]: "+ ((Integer) m_Values[i]).intValue());
if (((Integer) newValue).intValue() != ((Integer) objectValues[i]).intValue()) {
propertyEditors[i].setValue(objectValues[i]);
}
}
} catch (InvocationTargetException ex) {
if (ex.getTargetException() instanceof PropertyVetoException) {
System.out.println("PropertySheetPanel.wasModified(): WARNING: Vetoed; reason is: " + ex.getTargetException().getMessage());
} else {
System.out.println("PropertySheetPanel.wasModified(): InvocationTargetException while updating " + property.getName());
System.out.println("PropertySheetPanel.wasModified(): " + ex.getMessage());
ex.printStackTrace();
}
} catch (Exception ex) {
System.out.println("PropertySheetPanel.wasModified(): Unexpected exception while updating " + property.getName());
}
//revalidate();
if (views[i] != null && views[i] instanceof PropertyPanel) {
//System.err.println("Trying to repaint the property canvas");
views[i].repaint();
revalidate();
}
return true;
}
/**
* Updates the propertysheet when a value has been changed (from outside the
* propertysheet?).
*
* @param evt a value of type 'PropertyChangeEvent'
*/
synchronized void wasModified(PropertyChangeEvent evt) {
int propIndex = -1;
if (evt.getSource() instanceof PropertyEditor) {
PropertyEditor editor = (PropertyEditor) evt.getSource();
for (int i = 0; i < propertyEditors.length; i++) {
if (propertyEditors[i] == editor) {
propIndex = i;
if (wasModified(i, editor.getValue(), true)) {
break;
}
}
}
if (propIndex == -1) {
System.err.println("error: could not identify event editor! (PropertySheetPanel)");
}
} else {
System.err.println("unknown event source! (PropertySheetPanel)");
}
}
/**
* Updates the propertysheet when a value has been changed (from outside the
* propertysheet?).
*
* @param evt a value of type 'PropertyChangeEvent'
*/
synchronized boolean wasModified(int propIndex, Object value, boolean followDependencies) {
if (!updateValue(propIndex, value)) {
return false;
}
boolean doRepaint = false;
for (int i = 0; i < propertyEditors.length; i++) { // check the views for out-of-date information. this is different than checking the editors
if (i != propIndex) {
if (updateFieldView(i)) {
doRepaint = true;
}
}// end if (m_Editors[i] == editor) {
} // end for (int i = 0 ; i < m_Editors.length; i++) {
if (doRepaint) { // some components have been hidden or reappeared
// MK this finally seems to work right, with a scroll pane, too.
Container p = this;
while (p != null && (!p.getSize().equals(p.getPreferredSize()))) {
p.setSize(p.getPreferredSize());
p = p.getParent();
}
}
// Now re-read all the properties and update the editors
// for any other properties that have changed.
for (int i = 0; i < propertyDescriptors.length; i++) {
Object o;
Method getter = null;
if (propertyEditors[i] == null) {
continue; /// TODO: MK: Im not quite sure this is all good, but it avoids a latency problem
}
try {
getter = propertyDescriptors[i].getReadMethod();
Object args[] = {};
o = getter.invoke(targetObject, args);
} catch (Exception ex) {
o = null;
System.err.println(ex.getMessage());
ex.printStackTrace();
}
if ((o != null) && o == objectValues[i] && (BeanInspector.isJavaPrimitive(o.getClass()))) {
// The property is equal to its old value.
continue;
}
if (o != null && o.equals(objectValues[i])) {
// The property is equal to its old value.
continue;
}
objectValues[i] = o;
// Make sure we have an editor for this property...
if (propertyEditors[i] == null) {
continue;
}
// The property has changed! Update the editor.
propertyEditors[i].removePropertyChangeListener(this);
propertyEditors[i].setValue(o);
propertyEditors[i].addPropertyChangeListener(this);
if (views[i] != null) {
//System.out.println("Trying to repaint " + (i + 1));
views[i].repaint();
}
}
if (followDependencies) {
// Handle the special method getGOEPropertyUpdateLinks which returns a list of pairs
// of strings indicating that on an update of the i-th property, the i+1-th property
// should be updated. This is useful for changes within sub-classes of the target
// which are not directly displayed in this panel but in sub-panels (and there have an own view etc.)
Object o = BeanInspector.callIfAvailable(targetObject, "getGOEPropertyUpdateLinks", null);
if ((o != null) && (o instanceof String[])) {
maybeTriggerUpdates(propIndex, (String[]) o);
}
}
// Make sure the target bean gets repainted.
if (Beans.isInstanceOf(targetObject, Component.class)) {
//System.out.println("Beans.getInstanceOf repaint ");
((Component) (Beans.getInstanceOf(targetObject, Component.class))).repaint();
}
return true;
}
/**
* Check a property for consistency with the object data and update the view
* if necessary. Return true if a repaint is necessary.
*
* @param i
* @return
*/
private boolean updateFieldView(int i) {
// looking at another field (not changed explicitly, maybe implicitely
boolean valChanged = false;
boolean doRepaint = false;
Object args[] = {};
Method getter = propertyDescriptors[i].getReadMethod();
if (propertyDescriptors[i].isHidden() || propertyDescriptors[i].isExpert()) {
if ((propertyLabels[i] != null) && (propertyLabels[i].isVisible())) {
// something is set to hidden but was visible up to now
viewWrappers[i].setVisible(false);
views[i].setVisible(false);
propertyLabels[i].setVisible(false);
doRepaint = true;
}
return doRepaint;
} else {
if ((propertyLabels[i] != null) && !(propertyLabels[i].isVisible())) {
// something is invisible but set to not hidden in the mean time
viewWrappers[i].setVisible(true);
views[i].setVisible(true);
propertyLabels[i].setVisible(true);
doRepaint = true;
}
}
try { // check if view i is up to date and in sync with the value of the getter
if (views[i] != null) {
Object val = getter.invoke(targetObject, args);
if (views[i] instanceof PropertyBoolSelector) {
valChanged = (((PropertyBoolSelector) views[i]).isSelected() != ((Boolean) val));
if (valChanged) {
((PropertyBoolSelector) views[i]).setSelected(((Boolean) val));
}
} else if (views[i] instanceof PropertyText) {
valChanged = !(((PropertyText) views[i]).getText()).equals(val.toString());
if (valChanged) {
((PropertyText) views[i]).setText(val.toString());
}
} else if (views[i] instanceof PropertyPanel) {
valChanged = false;//!((PropertyPanel)m_Views[i]).equals(value);
// disregard whole panels and hope for the best
} else if (views[i] instanceof PropertyValueSelector) {
//changed = !((SelectedTag)val).isSelectedString((String)((PropertyValueSelector)m_Views[i]).getSelectedItem());
// interestingly there seems to be an implicit update of the ValueSelector, possible changes
// are already applied, all we need to see it is a repaint
views[i].repaint();
} else {
System.out.println("Warning: Property \"" + i
+ "\" not recognized. Skipping.");
}
}
} catch (Exception exc) {
System.err.println("Exception in PropertySheetPanel");
}
return doRepaint;
}
/**
* Check the given link list and trigger updates of indicated properties.
*
* @param propIndex
* @param links
*/
private void maybeTriggerUpdates(int propIndex, String[] links) {
int max = links.length;
if (max % 2 == 1) {
System.err.println("Error in PropertySheetPanel:maybeTriggerUpdates: odd number of strings provided!");
max -= 1;
}
for (int i = 0; i < max; i += 2) {
if (links[i].equals(propertyDescriptors[propIndex].getName())) {
updateLinkedProperty(links[i + 1]);
}
}
}
private void updateLinkedProperty(String propName) {
for (int i = 0; i < propertyDescriptors.length; i++) {
if (propertyDescriptors[i].getName().equals(propName)) {
Method getter = propertyDescriptors[i].getReadMethod();
Object val = null;
try {
val = getter.invoke(targetObject, (Object[]) null);
} catch (Exception e) {
val = null;
e.printStackTrace();
}
if (val != null) {
propertyEditors[i].setValue(val);
} else {
System.err.println("Error in PropertySheetPanel:updateLinkedProperty");
}
return;
}
}
}
}
class PropertyTableModel extends DefaultTableModel implements TableModel {
}
class PropertyCellRenderer implements TableCellRenderer {
JLabel empty = new JLabel();
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (value == null) {
return empty;
} else if (value instanceof String) {
return new JLabel(value.toString());
} else if (value instanceof eva2.gui.PropertyPanel) {
PropertyPanel propertyPanel = (PropertyPanel) value;
return propertyPanel;
} else if (value instanceof PropertyText) {
return (PropertyText) value;
} else if (value instanceof PropertyBoolSelector) {
return (PropertyBoolSelector) value;
} else if (value instanceof PropertyValueSelector) {
return (PropertyValueSelector) value;
}
throw new UnsupportedOperationException("Not supported yet.");
}
}
class PropertyCellEditor implements TableCellEditor {
private JLabel empty = new JLabel();
private Object value;
@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
this.value = value;
Component component;
if (value == null) {
component = empty;
} else if (value instanceof String) {
component = new JLabel(value.toString());
} else if (value instanceof eva2.gui.PropertyPanel) {
component = (PropertyPanel) value;
} else if (value instanceof PropertyText) {
component = (PropertyText) value;
} else if (value instanceof PropertyBoolSelector) {
component = (PropertyBoolSelector) value;
} else if (value instanceof PropertyValueSelector) {
component = (PropertyValueSelector) value;
} else {
throw new UnsupportedOperationException("Not supported yet.");
}
if (isSelected) {
component.setForeground(table.getSelectionForeground());
component.setBackground(table.getSelectionBackground());
} else {
component.setForeground(table.getForeground());
component.setBackground(table.getBackground());
}
return component;
}
@Override
public Object getCellEditorValue() {
return value;
}
@Override
public boolean isCellEditable(final EventObject anEvent) {
/* Event should always be triggered by a JTable */
JTable sourceTable = (JTable) anEvent.getSource();
int selectedColumn = sourceTable.getSelectedColumn();
String columnName = sourceTable.getColumnName(selectedColumn);
/* If the columnName equals Key it holds the keys */
if ("Key".equals(columnName)) {
return false;
}
return true;
}
@Override
public boolean shouldSelectCell(EventObject anEvent) {
return true;
}
@Override
public boolean stopCellEditing() {
return true;
}
@Override
public void cancelCellEditing() {
}
@Override
public void addCellEditorListener(CellEditorListener l) {
}
@Override
public void removeCellEditorListener(CellEditorListener l) {
}
}