PropertySheetPanel.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 17k
Category:

Windows Develop

Development Platform:

Java

  1. /*
  2.  *    This program is free software; you can redistribute it and/or modify
  3.  *    it under the terms of the GNU General Public License as published by
  4.  *    the Free Software Foundation; either version 2 of the License, or
  5.  *    (at your option) any later version.
  6.  *
  7.  *    This program is distributed in the hope that it will be useful,
  8.  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  9.  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  10.  *    GNU General Public License for more details.
  11.  *
  12.  *    You should have received a copy of the GNU General Public License
  13.  *    along with this program; if not, write to the Free Software
  14.  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15.  */
  16. /*
  17.  *    PropertySheet.java
  18.  *    Copyright (C) 1999 Len Trigg
  19.  *
  20.  */
  21. package weka.gui;
  22. import java.util.Hashtable;
  23. import java.util.Vector;
  24. import java.awt.Component;
  25. import java.awt.GridBagLayout;
  26. import java.awt.GridBagConstraints;
  27. import java.awt.BorderLayout;
  28. import java.awt.Color;
  29. import java.awt.Insets;
  30. import java.awt.event.WindowAdapter;
  31. import java.awt.event.WindowEvent;
  32. import java.awt.event.ActionListener;
  33. import java.awt.event.ActionEvent;
  34. import java.awt.Font;
  35. import java.awt.Dimension;
  36. import java.beans.Customizer;
  37. import java.beans.PropertyChangeEvent;
  38. import java.beans.PropertyDescriptor;
  39. import java.beans.MethodDescriptor;
  40. import java.beans.PropertyEditor;
  41. import java.beans.PropertyChangeSupport;
  42. import java.beans.PropertyChangeListener;
  43. import java.beans.IntrospectionException;
  44. import java.beans.BeanInfo;
  45. import java.beans.Introspector;
  46. import java.beans.PropertyEditorManager;
  47. import java.beans.PropertyVetoException;
  48. import java.beans.Beans;
  49. import java.lang.reflect.Method;
  50. import java.lang.reflect.InvocationTargetException;
  51. import javax.swing.JPanel;
  52. import javax.swing.JLabel;
  53. import javax.swing.SwingConstants;
  54. import javax.swing.Box;
  55. import javax.swing.BoxLayout;
  56. import javax.swing.BorderFactory;
  57. import javax.swing.JComponent;
  58. import javax.swing.JButton;
  59. import javax.swing.JFrame;
  60. import javax.swing.JTextArea;
  61. import javax.swing.JScrollPane;
  62. import java.awt.FlowLayout;
  63. /** 
  64.  * Displays a property sheet where (supported) properties of the target
  65.  * object may be edited.
  66.  *
  67.  * @author Len Trigg (trigg@cs.waikato.ac.nz)
  68.  * @version $Revision: 1.10 $
  69.  */
  70. public class PropertySheetPanel extends JPanel
  71.   implements PropertyChangeListener {
  72.   /** The target object being edited */
  73.   private Object m_Target;
  74.   /** Holds properties of the target */
  75.   private PropertyDescriptor m_Properties[];
  76.   /** Holds the methods of the target */
  77.   private MethodDescriptor m_Methods[];
  78.   /** Holds property editors of the object */
  79.   private PropertyEditor m_Editors[];
  80.   /** Holds current object values for each property */
  81.   private Object m_Values[];
  82.   /** Stores GUI components containing each editing component */
  83.   private JComponent m_Views[];
  84.   /** The labels for each property */
  85.   private JLabel m_Labels[];
  86.   /** The tool tip text for each property */
  87.   private String m_TipTexts[];
  88.   /** StringBuffer containing help text for the object being edited */
  89.   private StringBuffer m_HelpText;
  90.   /** Help frame */
  91.   private JFrame m_HelpFrame;
  92.   /** Button to pop up the full help text in a separate frame */
  93.   private JButton m_HelpBut;
  94.   /** A count of the number of properties we have an editor for */
  95.   private int m_NumEditable = 0;
  96.   /**
  97.    * Creates the property sheet panel.
  98.    */
  99.   public PropertySheetPanel() {
  100.     //    setBorder(BorderFactory.createLineBorder(Color.red));
  101.     setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
  102.   }
  103.   /** A support object for handling property change listeners */ 
  104.   private PropertyChangeSupport support = new PropertyChangeSupport(this);
  105.   /**
  106.    * Updates the property sheet panel with a changed property and also passed
  107.    * the event along.
  108.    *
  109.    * @param evt a value of type 'PropertyChangeEvent'
  110.    */
  111.   public void propertyChange(PropertyChangeEvent evt) {
  112.     wasModified(evt); // Let our panel update before guys downstream
  113.     support.firePropertyChange("", null, null);
  114.   }
  115.   /**
  116.    * Adds a PropertyChangeListener.
  117.    *
  118.    * @param l a value of type 'PropertyChangeListener'
  119.    */
  120.   public void addPropertyChangeListener(PropertyChangeListener l) {
  121.     support.addPropertyChangeListener(l);
  122.   }
  123.   /**
  124.    * Removes a PropertyChangeListener.
  125.    *
  126.    * @param l a value of type 'PropertyChangeListener'
  127.    */
  128.   public void removePropertyChangeListener(PropertyChangeListener l) {
  129.     support.removePropertyChangeListener(l);
  130.   }
  131.   /**
  132.    * Sets a new target object for customisation.
  133.    *
  134.    * @param targ a value of type 'Object'
  135.    */
  136.   public synchronized void setTarget(Object targ) {
  137.     // used to offset the components for the properties of targ
  138.     // if there happens to be globalInfo available in targ
  139.     int componentOffset = 0;
  140.     // Close any child windows at this point
  141.     removeAll();
  142.     
  143.     GridBagLayout gbLayout = new GridBagLayout();
  144.     setLayout(gbLayout);
  145.     setVisible(false);
  146.     m_NumEditable = 0;
  147.     m_Target = targ;
  148.     try {
  149.       BeanInfo bi = Introspector.getBeanInfo(m_Target.getClass());
  150.       m_Properties = bi.getPropertyDescriptors();
  151.       m_Methods = bi.getMethodDescriptors();
  152.     } catch (IntrospectionException ex) {
  153.       System.err.println("PropertySheet: Couldn't introspect");
  154.       return;
  155.     }
  156.     int rowHeight = 12;
  157.     JTextArea jt = new JTextArea();
  158.     m_HelpText = null;
  159.     JScrollPane js = null;
  160.     // Look for a globalInfo method that returns a string
  161.     // describing the target
  162.     for (int i = 0;i < m_Methods.length; i++) {
  163.       String name = m_Methods[i].getDisplayName();
  164.       Method meth = m_Methods[i].getMethod();
  165.       if (name.equals("globalInfo")) {
  166. if (meth.getReturnType().equals(String.class)) {
  167.   try {
  168.     Object args[] = { };
  169.     String globalInfo = (String)(meth.invoke(m_Target, args));
  170.             String summary = globalInfo;
  171.             int ci = globalInfo.indexOf('.');
  172.             if (ci != -1) {
  173.               summary = globalInfo.substring(0, ci + 1);
  174.             }
  175.     final String className = targ.getClass().getName();
  176.             m_HelpText = new StringBuffer("NAMEn");
  177.             m_HelpText.append(className).append("nn");
  178.             m_HelpText.append("SYNOPSISn").append(globalInfo).append("nn");
  179.             m_HelpBut = new JButton("More");
  180.     m_HelpBut.setToolTipText("More information about "
  181.                                      + className);
  182.     
  183.     m_HelpBut.addActionListener(new ActionListener() {
  184.               public void actionPerformed(ActionEvent a) {
  185.                 openHelpFrame();
  186.                 m_HelpBut.setEnabled(false);
  187.               }
  188.             });
  189.     jt.setFont(new Font("SansSerif", Font.PLAIN,12));
  190.     jt.setEditable(false);
  191.     jt.setLineWrap(true);
  192.     jt.setWrapStyleWord(true);
  193.     jt.setText(summary);
  194.             jt.setBackground(getBackground());
  195.     rowHeight = 12;
  196.     JPanel jp = new JPanel();
  197.     jp.setBorder(BorderFactory.createCompoundBorder(
  198.  BorderFactory.createTitledBorder("About"),
  199.  BorderFactory.createEmptyBorder(0, 5, 5, 5)
  200.  ));
  201.     jp.setLayout(new BorderLayout());
  202.     jp.add(jt, BorderLayout.CENTER);
  203.             JPanel p2 = new JPanel();
  204.             p2.setLayout(new BorderLayout());
  205.             p2.add(m_HelpBut, BorderLayout.NORTH);
  206.             jp.add(p2, BorderLayout.EAST);
  207.     GridBagConstraints gbConstraints = new GridBagConstraints();
  208.     //     gbConstraints.anchor = GridBagConstraints.EAST;
  209.     gbConstraints.fill = GridBagConstraints.BOTH;
  210.     //     gbConstraints.gridy = 0;     gbConstraints.gridx = 0;
  211.     gbConstraints.gridwidth = 2;
  212.     gbConstraints.insets = new Insets(0,5,0,5);
  213.     gbLayout.setConstraints(jp, gbConstraints);
  214.     add(jp);
  215.     componentOffset = 1;
  216.     break;
  217.   } catch (Exception ex) {
  218.     
  219.   }
  220. }
  221.       }
  222.     }
  223.     m_Editors = new PropertyEditor[m_Properties.length];
  224.     m_Values = new Object[m_Properties.length];
  225.     m_Views = new JComponent[m_Properties.length];
  226.     m_Labels = new JLabel[m_Properties.length];
  227.     m_TipTexts = new String[m_Properties.length];
  228.     boolean firstTip = true;
  229.     for (int i = 0; i < m_Properties.length; i++) {
  230.       // Don't display hidden or expert properties.
  231.       if (m_Properties[i].isHidden() || m_Properties[i].isExpert()) {
  232. continue;
  233.       }
  234.       String name = m_Properties[i].getDisplayName();
  235.       Class type = m_Properties[i].getPropertyType();
  236.       Method getter = m_Properties[i].getReadMethod();
  237.       Method setter = m_Properties[i].getWriteMethod();
  238.       // Only display read/write properties.
  239.       if (getter == null || setter == null) {
  240. continue;
  241.       }
  242.       JComponent view = null;
  243.       try {
  244. Object args[] = { };
  245. Object value = getter.invoke(m_Target, args);
  246. m_Values[i] = value;
  247. PropertyEditor editor = null;
  248. Class pec = m_Properties[i].getPropertyEditorClass();
  249. if (pec != null) {
  250.   try {
  251.     editor = (PropertyEditor)pec.newInstance();
  252.   } catch (Exception ex) {
  253.     // Drop through.
  254.   }
  255. }
  256. if (editor == null) {
  257.   editor = PropertyEditorManager.findEditor(type);
  258. }
  259. m_Editors[i] = editor;
  260. // If we can't edit this component, skip it.
  261. if (editor == null) {
  262.   // If it's a user-defined property we give a warning.
  263.   String getterClass = m_Properties[i].getReadMethod()
  264.     .getDeclaringClass().getName();
  265.   if (getterClass.indexOf("java.") != 0) {
  266.     System.err.println("Warning: Can't find public property editor"
  267.        + " for property "" + name + "" (class ""
  268.        + type.getName() + "").  Skipping.");
  269.   }
  270.   continue;
  271. }
  272. if (editor instanceof GenericObjectEditor) {
  273.   ((GenericObjectEditor) editor).setClassType(type);
  274. }
  275. // Don't try to set null values:
  276. if (value == null) {
  277.   // If it's a user-defined property we give a warning.
  278.   String getterClass = m_Properties[i].getReadMethod()
  279.     .getDeclaringClass().getName();
  280.   if (getterClass.indexOf("java.") != 0) {
  281.     System.err.println("Warning: Property "" + name 
  282.        + "" has null initial value.  Skipping.");
  283.   }
  284.   continue;
  285. }
  286. editor.setValue(value);
  287. // now look for a TipText method for this property
  288.         String tipName = name + "TipText";
  289. for (int j = 0; j < m_Methods.length; j++) {
  290.   String mname = m_Methods[j].getDisplayName();
  291.   Method meth = m_Methods[j].getMethod();
  292.   if (mname.equals(tipName)) {
  293.     if (meth.getReturnType().equals(String.class)) {
  294.       try {
  295. String tempTip = (String)(meth.invoke(m_Target, args));
  296. int ci = tempTip.indexOf('.');
  297. if (ci < 0) {
  298.   m_TipTexts[i] = tempTip;
  299. } else {
  300.   m_TipTexts[i] = tempTip.substring(0, ci);
  301. }
  302.                 if (m_HelpText != null) {
  303.                   if (firstTip) {
  304.                     m_HelpText.append("OPTIONSn");
  305.                     firstTip = false;
  306.                   }
  307.                   m_HelpText.append(name).append(" -- ");
  308.                   m_HelpText.append(tempTip).append("nn");
  309.                   //jt.setText(m_HelpText.toString());
  310.                 }
  311.       } catch (Exception ex) {
  312.       }
  313.       break;
  314.     }
  315.   }
  316. }   
  317. // Now figure out how to display it...
  318. if (editor.isPaintable() && editor.supportsCustomEditor()) {
  319.   view = new PropertyPanel(editor);
  320. } else if (editor.getTags() != null) {
  321.   view = new PropertyValueSelector(editor);
  322. } else if (editor.getAsText() != null) {
  323.   //String init = editor.getAsText();
  324.   view = new PropertyText(editor);
  325. } else {
  326.   System.err.println("Warning: Property "" + name 
  327.      + "" has non-displayabale editor.  Skipping.");
  328.   continue;
  329. }
  330. editor.addPropertyChangeListener(this);
  331.       } catch (InvocationTargetException ex) {
  332. System.err.println("Skipping property " + name
  333.    + " ; exception on target: "
  334.    + ex.getTargetException());
  335. ex.getTargetException().printStackTrace();
  336. continue;
  337.       } catch (Exception ex) {
  338. System.err.println("Skipping property " + name
  339.    + " ; exception: " + ex);
  340. ex.printStackTrace();
  341. continue;
  342.       }
  343.       m_Labels[i] = new JLabel(name, SwingConstants.RIGHT);
  344.       m_Labels[i].setBorder(BorderFactory.createEmptyBorder(10, 10, 0, 5));
  345.       m_Views[i] = view;
  346.       GridBagConstraints gbConstraints = new GridBagConstraints();
  347.       gbConstraints.anchor = GridBagConstraints.EAST;
  348.       gbConstraints.fill = GridBagConstraints.HORIZONTAL;
  349.       gbConstraints.gridy = i+componentOffset;     gbConstraints.gridx = 0;
  350.       gbLayout.setConstraints(m_Labels[i], gbConstraints);
  351.       add(m_Labels[i]);
  352.       JPanel newPanel = new JPanel();
  353.       if (m_TipTexts[i] != null) {
  354. m_Views[i].setToolTipText(m_TipTexts[i]);
  355.       }
  356.       newPanel.setBorder(BorderFactory.createEmptyBorder(10, 5, 0, 10));
  357.       newPanel.setLayout(new BorderLayout());
  358.       newPanel.add(m_Views[i], BorderLayout.CENTER);
  359.       gbConstraints = new GridBagConstraints();
  360.       gbConstraints.anchor = GridBagConstraints.WEST;
  361.       gbConstraints.fill = GridBagConstraints.BOTH;
  362.       gbConstraints.gridy = i+componentOffset;     gbConstraints.gridx = 1;
  363.       gbConstraints.weightx = 100;
  364.       gbLayout.setConstraints(newPanel, gbConstraints);
  365.       add(newPanel);
  366.       m_NumEditable ++;
  367.     }
  368.     if (m_NumEditable == 0) {
  369.       JLabel empty = new JLabel("No editable properties", 
  370.                                 SwingConstants.CENTER);
  371.       Dimension d = empty.getPreferredSize();
  372.       empty.setPreferredSize(new Dimension(d.width * 2, d.height * 2));
  373.       empty.setBorder(BorderFactory.createEmptyBorder(10, 5, 0, 10));
  374.       GridBagConstraints gbConstraints = new GridBagConstraints();
  375.       gbConstraints.anchor = GridBagConstraints.CENTER;
  376.       gbConstraints.fill = GridBagConstraints.HORIZONTAL;
  377.       gbConstraints.gridy = componentOffset;     gbConstraints.gridx = 0;
  378.       gbLayout.setConstraints(empty, gbConstraints);
  379.       add(empty);
  380.     }
  381.     validate();
  382.     setVisible(true);
  383.   }
  384.   protected void openHelpFrame() {
  385.     JTextArea ta = new JTextArea();
  386.     ta.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
  387.     ta.setLineWrap(true);
  388.     ta.setWrapStyleWord(true);
  389.     //ta.setBackground(getBackground());
  390.     ta.setEditable(false);
  391.     ta.setText(m_HelpText.toString());
  392.     ta.setCaretPosition(0);
  393.     final JFrame jf = new JFrame("Information");
  394.     jf.addWindowListener(new WindowAdapter() {
  395.       public void windowClosing(WindowEvent e) {
  396.         jf.dispose();
  397.         if (m_HelpFrame == jf) {
  398.           m_HelpBut.setEnabled(true);
  399.         }
  400.       }
  401.     });
  402.     jf.getContentPane().setLayout(new BorderLayout());
  403.     jf.getContentPane().add(new JScrollPane(ta), BorderLayout.CENTER);
  404.     jf.pack();
  405.     jf.setSize(400, 350);
  406.     jf.setLocation(getTopLevelAncestor().getLocationOnScreen().x 
  407.                    + getTopLevelAncestor().getSize().width,
  408.                    getTopLevelAncestor().getLocationOnScreen().y);
  409.     jf.setVisible(true);
  410.     m_HelpFrame = jf;
  411.   }
  412.   /**
  413.    * Gets the number of editable properties for the current target.
  414.    *
  415.    * @return the number of editable properties.
  416.    */
  417.   public int editableProperties() {
  418.     return m_NumEditable;
  419.   }
  420.   
  421.   /**
  422.    * Updates the propertysheet when a value has been changed (from outside
  423.    * the propertysheet?).
  424.    *
  425.    * @param evt a value of type 'PropertyChangeEvent'
  426.    */
  427.   synchronized void wasModified(PropertyChangeEvent evt) {
  428.     //    System.err.println("wasModified");
  429.     if (evt.getSource() instanceof PropertyEditor) {
  430.       PropertyEditor editor = (PropertyEditor) evt.getSource();
  431.       for (int i = 0 ; i < m_Editors.length; i++) {
  432. if (m_Editors[i] == editor) {
  433.   PropertyDescriptor property = m_Properties[i];
  434.   Object value = editor.getValue();
  435.   m_Values[i] = value;
  436.   Method setter = property.getWriteMethod();
  437.   try {
  438.     Object args[] = { value };
  439.     args[0] = value;
  440.     setter.invoke(m_Target, args);
  441.   } catch (InvocationTargetException ex) {
  442.     if (ex.getTargetException()
  443. instanceof PropertyVetoException) {
  444.       System.err.println("WARNING: Vetoed; reason is: " 
  445.  + ex.getTargetException().getMessage());
  446.     } else {
  447.       System.err.println("InvocationTargetException while updating " 
  448.  + property.getName());
  449.     }
  450.   } catch (Exception ex) {
  451.     System.err.println("Unexpected exception while updating " 
  452.   + property.getName());
  453.   }
  454.   if (m_Views[i] != null && m_Views[i] instanceof PropertyPanel) {
  455.     //System.err.println("Trying to repaint the property canvas");
  456.     m_Views[i].repaint();
  457.     revalidate();
  458.   }
  459.   break;
  460. }
  461.       }
  462.     }
  463.     // Now re-read all the properties and update the editors
  464.     // for any other properties that have changed.
  465.     for (int i = 0; i < m_Properties.length; i++) {
  466.       Object o;
  467.       try {
  468. Method getter = m_Properties[i].getReadMethod();
  469. Method setter = m_Properties[i].getWriteMethod();
  470. if (getter == null || setter == null) {
  471.   // ignore set/get only properties
  472.   continue;
  473. }
  474. Object args[] = { };
  475. o = getter.invoke(m_Target, args);
  476.       } catch (Exception ex) {
  477. o = null;
  478.       }
  479.       if (o == m_Values[i] || (o != null && o.equals(m_Values[i]))) {
  480. // The property is equal to its old value.
  481. continue;
  482.       }
  483.       m_Values[i] = o;
  484.       // Make sure we have an editor for this property...
  485.       if (m_Editors[i] == null) {
  486. continue;
  487.       }
  488.       // The property has changed!  Update the editor.
  489.       m_Editors[i].removePropertyChangeListener(this);
  490.       m_Editors[i].setValue(o);
  491.       m_Editors[i].addPropertyChangeListener(this);
  492.       if (m_Views[i] != null) {
  493. //System.err.println("Trying to repaint " + (i + 1));
  494. m_Views[i].repaint();
  495.       }
  496.     }
  497.     // Make sure the target bean gets repainted.
  498.     if (Beans.isInstanceOf(m_Target, Component.class)) {
  499.       ((Component)(Beans.getInstanceOf(m_Target, Component.class))).repaint();
  500.     }
  501.   }
  502. }