[Buix-commits] r862 - in lutinjaxx/trunk: core/src/main/java/jaxx/runtime core/src/main/java/jaxx/runtime/swing core/src/main/java/jaxx/tags core/src/test/java/jaxx/runtime jaxx-swing/src/main/java/jaxx/tags runtime/src/main/java/jaxx/runtime runtime/src/test/java/jaxx
Author: tchemit Date: 2008-10-02 12:28:00 +0000 (Thu, 02 Oct 2008) New Revision: 862 Added: lutinjaxx/trunk/core/src/main/java/jaxx/runtime/DataBindingListener.java lutinjaxx/trunk/core/src/main/java/jaxx/runtime/DataBindingUpdateListener.java lutinjaxx/trunk/core/src/main/java/jaxx/runtime/Util.java lutinjaxx/trunk/core/src/main/java/jaxx/runtime/css/ lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/Item.java lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/JAXXButtonGroup.java lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/JAXXComboBox.java lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/JAXXList.java lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/JAXXTab.java lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/JAXXToggleButton.java lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/JAXXTree.java lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/Spacer.java lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/TabInfo.java lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/TabInfoPropertyChangeListener.java lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/Utils.java lutinjaxx/trunk/core/src/main/java/jaxx/tags/swing/ lutinjaxx/trunk/core/src/test/java/jaxx/runtime/UtilTest.java Removed: lutinjaxx/trunk/jaxx-swing/src/main/java/jaxx/tags/swing/ lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/DataBindingListener.java lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/DataBindingUpdateListener.java lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/Util.java lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/css/ lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/swing/ lutinjaxx/trunk/runtime/src/test/java/jaxx/runtime/ Log: put all together the runtime, core and jaxx-swing modules Copied: lutinjaxx/trunk/core/src/main/java/jaxx/runtime/DataBindingListener.java (from rev 857, lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/DataBindingListener.java) =================================================================== --- lutinjaxx/trunk/core/src/main/java/jaxx/runtime/DataBindingListener.java (rev 0) +++ lutinjaxx/trunk/core/src/main/java/jaxx/runtime/DataBindingListener.java 2008-10-02 12:28:00 UTC (rev 862) @@ -0,0 +1,46 @@ +/* + * Copyright 2006 Ethan Nicholas. All rights reserved. + * Use is subject to license terms. + */ +package jaxx.runtime; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + + +/** + * A <code>PropertyChangeListener</code> which processes a data binding when it receives a + * <code>PropertyChangeEvent</code>. + */ +public class DataBindingListener implements PropertyChangeListener { + private JAXXObject object; + private String dest; + + /** + * Creates a new <code>DataBindingListener</code> which will run the given data binding + * when it receives a <code>PropertyChangeEvent</code>. + * + * @param object the object in which the data binding exists + * @param dest the name of the data binding to run + */ + public DataBindingListener(JAXXObject object, String dest) { + this.object = object; + this.dest = dest; + } + + + /** + * Processes the data binding in response to a <code>PropertyChangeEvent</code>. + * + * @param e the event which triggered the binding + */ + public void propertyChange(PropertyChangeEvent e) { + object.processDataBinding(dest); + + // for now, handle dependency changes by always removing & reapplying + // the binding. We should be more efficient and only do this when it's + // actually necessary + object.removeDataBinding(dest); + object.applyDataBinding(dest); + } +} \ No newline at end of file Copied: lutinjaxx/trunk/core/src/main/java/jaxx/runtime/DataBindingUpdateListener.java (from rev 857, lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/DataBindingUpdateListener.java) =================================================================== --- lutinjaxx/trunk/core/src/main/java/jaxx/runtime/DataBindingUpdateListener.java (rev 0) +++ lutinjaxx/trunk/core/src/main/java/jaxx/runtime/DataBindingUpdateListener.java 2008-10-02 12:28:00 UTC (rev 862) @@ -0,0 +1,46 @@ +/* + * Copyright 2006 Ethan Nicholas. All rights reserved. + * Use is subject to license terms. + */ +package jaxx.runtime; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + + +/** + * A <code>PropertyChangeListener</code> which removes and re-applies a data binding + * when it receives a <code>PropertyChangeEvent</code>. + */ +public class DataBindingUpdateListener implements PropertyChangeListener { + private JAXXObject object; + private String dest; + + /** + * Creates a new <code>DataBindingUpdateListener</code> which will remove and re-apply a + * data binding when it receives a <code>PropertyChangeEvent</code>. + * + * @param object the object in which the data binding exists + * @param dest the name of the data binding to reapply + */ + public DataBindingUpdateListener(JAXXObject object, String dest) { + this.object = object; + this.dest = dest; + } + + + public String getBindingName() { + return dest; + } + + + /** + * Updates the data binding in response to a <code>PropertyChangeEvent</code>. + * + * @param e the event which triggered the binding + */ + public void propertyChange(PropertyChangeEvent e) { + object.removeDataBinding(dest); + object.applyDataBinding(dest); + } +} \ No newline at end of file Copied: lutinjaxx/trunk/core/src/main/java/jaxx/runtime/Util.java (from rev 857, lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/Util.java) =================================================================== --- lutinjaxx/trunk/core/src/main/java/jaxx/runtime/Util.java (rev 0) +++ lutinjaxx/trunk/core/src/main/java/jaxx/runtime/Util.java 2008-10-02 12:28:00 UTC (rev 862) @@ -0,0 +1,253 @@ +package jaxx.runtime; + +import javax.swing.JComponent; +import java.awt.Component; +import java.awt.Dimension; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.lang.ref.WeakReference; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.EventListener; +import java.util.zip.GZIPInputStream; + +public class Util { + // Maps root objects to lists of event listeners + private static Map<Object, WeakReference<List<EventListenerDescriptor>>> eventListeners = new WeakHashMap<Object, WeakReference<List<EventListenerDescriptor>>>(); + private static Map<JAXXObject, WeakReference<List<DataBindingUpdateListener>>> dataBindingUpdateListeners = new WeakHashMap<JAXXObject, WeakReference<List<DataBindingUpdateListener>>>(); + + + private static class EventListenerDescriptor { + Class listenerClass; + String listenerMethodName; + String methodName; + Object eventListener; + } + + + /** + * Decodes the serialized representation of a JAXXObjectDescriptor. The string must be a byte-to-character mapping + * of the binary serialization data for a JAXXObjectDescriptor. See the comments in JAXXCompiler.createJAXXObjectDescriptorField + * for the rationale behind this (admittedly ugly) approach. + * + * @param descriptor descriptor to decode + * @return the dedoced descriptor + */ + public static JAXXObjectDescriptor decodeJAXXObjectDescriptor(String descriptor) { + try { + byte[] data = new byte[descriptor.length()]; + // copy low-order bytes into the array. The high-order bytes should all be zero. + System.arraycopy(descriptor.getBytes(),0,data,0,data.length); + //descriptor.getBytes(0, descriptor.length(), data, 0); + ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(data)); + return (JAXXObjectDescriptor) in.readObject(); + } + catch (IOException e) { + throw new RuntimeException("Internal error: can't-happen error", e); + } + catch (ClassNotFoundException e) { + throw new RuntimeException("Internal error: can't-happen error", e); + } + } + + + public static JAXXObjectDescriptor decodeCompressedJAXXObjectDescriptor(String descriptor) { + try { + byte[] data = new byte[descriptor.length()]; + // copy low-order bytes into the array. The high-order bytes should all be zero. + System.arraycopy(descriptor.getBytes(),0,data,0,data.length); + //descriptor.getBytes(0, descriptor.length(), data, 0); + ObjectInputStream in = new ObjectInputStream(new GZIPInputStream(new ByteArrayInputStream(data))); + return (JAXXObjectDescriptor) in.readObject(); + } + catch (IOException e) { + throw new RuntimeException("Internal error: can't-happen error", e); + } + catch (ClassNotFoundException e) { + throw new RuntimeException("Internal error: can't-happen error", e); + } + } + + + public static Object getEventListener(Class<? extends EventListener> listenerClass, final String listenerMethodName, final Object methodContainer, final String methodName) { + WeakReference<List<EventListenerDescriptor>> ref = eventListeners.get(methodContainer); + List<EventListenerDescriptor> descriptors = ref != null ? ref.get() : null; + if (descriptors == null) { + descriptors = new ArrayList<EventListenerDescriptor>(); + eventListeners.put(methodContainer, new WeakReference<List<EventListenerDescriptor>>(descriptors)); + } else { + for (EventListenerDescriptor descriptor : descriptors) { + if (listenerClass == descriptor.listenerClass && + (listenerMethodName == null ? descriptor.listenerMethodName == null : listenerMethodName.equals(descriptor.listenerMethodName)) && + methodName.equals(descriptor.methodName)) { + return descriptor.eventListener; + } + } + } + + // else we need to create a new listener + final EventListenerDescriptor descriptor = new EventListenerDescriptor(); + descriptor.listenerClass = listenerClass; + descriptor.listenerMethodName = listenerMethodName; + descriptor.methodName = methodName; + try { + final List<Method> listenerMethods = Arrays.asList(listenerClass.getMethods()); + Method listenerMethod = null; + if (listenerMethodName != null) { + for (Method listenerMethod1 : listenerMethods) { + if ((listenerMethod1).getName().equals(listenerMethodName)) { + listenerMethod = listenerMethod1; + break; + } + } + } + if (listenerMethodName != null && listenerMethod == null) + throw new IllegalArgumentException("no method named " + listenerMethodName + " found in class " + listenerClass.getName()); + Class[] parameterTypes = listenerMethods.get(0).getParameterTypes(); + Class<?> methodContainerClass = methodContainer.getClass(); + final Method targetMethod = methodContainerClass.getMethod(methodName, parameterTypes); + descriptor.eventListener = Proxy.newProxyInstance(listenerClass.getClassLoader(), + new Class[]{listenerClass}, + new InvocationHandler() { + public Object invoke(Object proxy, Method method, Object[] args) { + String methodName = method.getName(); + if ((listenerMethodName == null && listenerMethods.contains(method)) || methodName.equals(listenerMethodName)) { + try { + return targetMethod.invoke(methodContainer, args); + } + catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + if (methodName.equals("toString")) { + return toString(); + } + if (methodName.equals("equals")) { + return descriptor.eventListener == args[0]; + } + if (methodName.equals("hashCode")) { + return hashCode(); + } + return null; + } + }); + descriptors.add(descriptor); + return descriptor.eventListener; + } + catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + + public static Object getEventListener(Class<? extends EventListener> listenerClass, final Object methodContainer, final String methodName) { + return getEventListener(listenerClass, null, methodContainer, methodName); + } + + + public static DataBindingUpdateListener getDataBindingUpdateListener(JAXXObject object, String bindingName) { + WeakReference<List<DataBindingUpdateListener>> ref = dataBindingUpdateListeners.get(object); + List<DataBindingUpdateListener> listeners = ref == null ? null : ref.get(); + if (listeners == null) { + listeners = new ArrayList<DataBindingUpdateListener>(); + dataBindingUpdateListeners.put(object, new WeakReference<List<DataBindingUpdateListener>>(listeners)); + } else { + for (DataBindingUpdateListener listener : listeners) { + if (bindingName.equals(listener.getBindingName())) + return listener; + } + } + DataBindingUpdateListener listener = new DataBindingUpdateListener(object, bindingName); + listeners.add(listener); + return listener; + } + + + public static void setComponentWidth(Component component, int width) { + component.setSize(width, component.getHeight()); + if (component instanceof JComponent) { + JComponent jcomponent = (JComponent) component; + jcomponent.setPreferredSize(new Dimension(width, jcomponent.getPreferredSize().height)); + jcomponent.setMinimumSize(new Dimension(width, jcomponent.getPreferredSize().height)); + if (jcomponent.isDisplayable()) + jcomponent.revalidate(); + } + } + + + public static void setComponentHeight(Component component, int height) { + component.setSize(component.getWidth(), height); + if (component instanceof JComponent) { + JComponent jcomponent = (JComponent) component; + jcomponent.setPreferredSize(new Dimension(jcomponent.getPreferredSize().width, height)); + jcomponent.setMinimumSize(new Dimension(jcomponent.getPreferredSize().width, height)); + if (jcomponent.isDisplayable()) + jcomponent.revalidate(); + } + } + + + public static boolean assignment(boolean value, String name, JAXXObject src) { + src.firePropertyChange(name.trim(), null, "dummy value"); + return value; + } + + + public static byte assignment(byte value, String name, JAXXObject src) { + src.firePropertyChange(name.trim(), null, "dummy value"); + return value; + } + + + public static short assignment(short value, String name, JAXXObject src) { + src.firePropertyChange(name.trim(), null, "dummy value"); + return value; + } + + + public static int assignment(int value, String name, JAXXObject src) { + src.firePropertyChange(name.trim(), null, "dummy value"); + return value; + } + + + public static long assignment(long value, String name, JAXXObject src) { + src.firePropertyChange(name.trim(), null, "dummy value"); + return value; + } + + + public static float assignment(float value, String name, JAXXObject src) { + src.firePropertyChange(name.trim(), null, "dummy value"); + return value; + } + + + public static double assignment(double value, String name, JAXXObject src) { + src.firePropertyChange(name.trim(), null, "dummy value"); + return value; + } + + + public static char assignment(char value, String name, JAXXObject src) { + src.firePropertyChange(name.trim(), null, "dummy value"); + return value; + } + + + public static java.lang.Object assignment(java.lang.Object value, String name, JAXXObject src) { + src.firePropertyChange(name.trim(), null, "dummy value"); + return value; + } +} Copied: lutinjaxx/trunk/core/src/main/java/jaxx/runtime/css (from rev 857, lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/css) Copied: lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/Item.java (from rev 857, lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/swing/Item.java) =================================================================== --- lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/Item.java (rev 0) +++ lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/Item.java 2008-10-02 12:28:00 UTC (rev 862) @@ -0,0 +1,192 @@ +/* + * Copyright 2006 Ethan Nicholas. All rights reserved. + * Use is subject to license terms. + */ +package jaxx.runtime.swing; + +import javax.swing.event.SwingPropertyChangeSupport; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.util.ArrayList; +import java.util.List; + +// This needs to be split into two classes, Item and TreeItem +/** + * An item in a component such as <code>JComboBox</code> or <code>JTree</code>. The <code>Item</code> + * class corresponds to the <code><item></code> tag in JAXX source files. + */ +public class Item { + public static final String LABEL_PROPERTY = "label"; + public static final String VALUE_PROPERTY = "value"; + public static final String SELECTED_PROPERTY = "selected"; + + private String id; + private String label; + private Object value; + private boolean selected; + private List<Item> children; + private Item parent; + private PropertyChangeSupport propertyChangeSupport; + + /** + * Creates a new Item. This should only be called from compiled JAXX files. + * + * @param id the item's ID + * @param label the string that should be used to represent the item visually + * @param value the item's actual value + * @param selected <code>true</code> if the item should be selected by default + */ + public Item(String id, String label, Object value, boolean selected) { + this.id = id; + this.label = label; + this.value = value; + this.selected = selected; + } + + /** + * Returns this item's ID. + * + * @return the JAXX ID attribute + */ + public String getId() { + return id; + } + + /** + * Returns the string that should be used to represent the item at display time. If <code>null</code>, + * <code>String.valueOf(getValue())</code> will be used instead. + * + * @return this item's display string + * @see #setLabel + */ + public String getLabel() { + return label; + } + + /** + * Sets the item's display string. If <code>null, String.valueOf(getValue())</code> will be used instead. + * + * @param label the new display string + * @see #getLabel + */ + public void setLabel(String label) { + String oldLabel = this.label; + this.label = label; + firePropertyChange(LABEL_PROPERTY, oldLabel, label); + } + + /** + * Returns the item's actual value as it appears in the component's model. The <code>Item</code> itself is not + * visible from the model, only the value. + * + * @return the item's value + * @see #setValue + */ + public Object getValue() { + return value; + } + + /** + * Sets the item's value as it appears in the component's model. The <code>Item</code> itself is not + * visible from the model, only the value. + * + * @param value the new value + * @see #getValue + */ + public void setValue(Object value) { + Object oldValue = this.value; + this.value = value; + firePropertyChange(VALUE_PROPERTY, oldValue, value); + } + + /** + * Returns <code>true</code> if this item is currently selected. This is a bound property. + * + * @return <code>true</code> if item is selected + * @see #setSelected + */ + public boolean isSelected() { + return selected; + } + + /** + * Sets the item's selection state. This is a bound property. + * + * @param selected the new selection state + * @see #isSelected + */ + public void setSelected(boolean selected) { + boolean oldSelected = this.selected; + this.selected = selected; + firePropertyChange(SELECTED_PROPERTY, oldSelected, selected); + } + + /** + * Adds a new child node (Items can be nested in trees). + * + * @param item the new child item + */ + public void addChild(Item item) { + if (children == null) { + children = new ArrayList<Item>(); + } + children.add(item); + item.parent = this; + } + + /** + * Returns a list of this item's children. + * + * @return a list of all nested child nodes + */ + public List<Item> getChildren() { + if (children == null) { + children = new ArrayList<Item>(); + } + return children; + } + + /** + * Returns the <code>Item</code> containing this <code>Item</code>, or <code>null</code> for a top-level + * <code>Item</code>. + * + * @return the item parent (or null) + */ + public Item getParent() { + return parent; + } + + private PropertyChangeSupport getPropertyChangeSupport() { + if (propertyChangeSupport == null) { + propertyChangeSupport = new SwingPropertyChangeSupport(this); + } + return propertyChangeSupport; + } + + public void addPropertyChangeListener(PropertyChangeListener listener) { + getPropertyChangeSupport().addPropertyChangeListener(listener); + } + + public void addPropertyChangeListener(String property, PropertyChangeListener listener) { + getPropertyChangeSupport().addPropertyChangeListener(property, listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + getPropertyChangeSupport().removePropertyChangeListener(listener); + } + + public void removePropertyChangeListener(String property, PropertyChangeListener listener) { + getPropertyChangeSupport().removePropertyChangeListener(property, listener); + } + + protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { + if (propertyChangeSupport != null) { + getPropertyChangeSupport().firePropertyChange(propertyName, oldValue, newValue); + } + } + + @Override + public String toString() { + return getClass().getName() + "[" + value + "]"; + } +} Copied: lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/JAXXButtonGroup.java (from rev 857, lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/swing/JAXXButtonGroup.java) =================================================================== --- lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/JAXXButtonGroup.java (rev 0) +++ lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/JAXXButtonGroup.java 2008-10-02 12:28:00 UTC (rev 862) @@ -0,0 +1,103 @@ +/* + * Copyright 2006 Ethan Nicholas. All rights reserved. + * Use is subject to license terms. + */ +package jaxx.runtime.swing; + +import javax.swing.AbstractButton; +import javax.swing.ButtonGroup; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.util.Enumeration; + +public class JAXXButtonGroup extends ButtonGroup { + public static final String SELECTED_VALUE_PROPERTY = "selectedValue"; + + private PropertyChangeSupport propertyChangeSupport; + private Object selectedValue; + private ChangeListener changeListener = new ChangeListener() { + public void stateChanged(ChangeEvent e) { + updateSelectedValue(); + } + }; + private static final long serialVersionUID = -2096340516687228691L; + + + @Override + public void add(AbstractButton button) { + super.add(button); + button.addChangeListener(changeListener); + updateSelectedValue(); + } + + @Override + public void remove(AbstractButton button) { + super.remove(button); + button.removeChangeListener(changeListener); + updateSelectedValue(); + } + + + public void updateSelectedValue() { + Enumeration<AbstractButton> e = getElements(); + while (e.hasMoreElements()) { + AbstractButton button = e.nextElement(); + if (button.isSelected()) { + Object selectedValue = button.getClientProperty("$value"); + if (selectedValue != getSelectedValue()) { + setSelectedValue(selectedValue); + } + } + } + } + + + public Object getSelectedValue() { + return selectedValue; + } + + + public void setSelectedValue(Object value) { + Object oldValue = getSelectedValue(); + this.selectedValue = value; + firePropertyChange(oldValue); + } + + + protected PropertyChangeSupport getPropertyChangeSupport() { + if (propertyChangeSupport == null) { + propertyChangeSupport = new PropertyChangeSupport(this); + } + return propertyChangeSupport; + } + + + public void addPropertyChangeListener(PropertyChangeListener listener) { + getPropertyChangeSupport().addPropertyChangeListener(listener); + } + + + public void addPropertyChangeListener(String property, PropertyChangeListener listener) { + getPropertyChangeSupport().addPropertyChangeListener(property, listener); + } + + + public void removePropertyChangeListener(PropertyChangeListener listener) { + getPropertyChangeSupport().removePropertyChangeListener(listener); + } + + + public void removePropertyChangeListener(String property, PropertyChangeListener listener) { + getPropertyChangeSupport().removePropertyChangeListener(property, listener); + } + + + private void firePropertyChange(Object oldValue) { + if (propertyChangeSupport != null) { + getPropertyChangeSupport().firePropertyChange(SELECTED_VALUE_PROPERTY, + oldValue, getSelectedValue()); + } + } +} Copied: lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/JAXXComboBox.java (from rev 857, lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/swing/JAXXComboBox.java) =================================================================== --- lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/JAXXComboBox.java (rev 0) +++ lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/JAXXComboBox.java 2008-10-02 12:28:00 UTC (rev 862) @@ -0,0 +1,176 @@ +/* + * Copyright 2006 Ethan Nicholas. All rights reserved. + * Use is subject to license terms. + */ +package jaxx.runtime.swing; + +import javax.swing.AbstractListModel; +import javax.swing.ComboBoxModel; +import javax.swing.DefaultListCellRenderer; +import javax.swing.JComboBox; +import javax.swing.JList; +import javax.swing.ListModel; +import java.awt.Component; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; +import java.util.List; + +public class JAXXComboBox extends JComboBox { + public class JAXXComboBoxModel extends AbstractListModel implements ComboBoxModel { + private List<Item> items; + private Object selectedItem; + private static final long serialVersionUID = -8940733376638766414L; + + public JAXXComboBoxModel(List<Item> items) { + this.items = items; + + PropertyChangeListener listener = new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent e) { + if (e.getPropertyName().equals(Item.SELECTED_PROPERTY)) { + Item item = (Item) e.getSource(); + int itemIndex = JAXXComboBoxModel.this.items.indexOf(item); + // TODO: fix cut-and-pasting badness + int[] oldSelection = new int[]{getSelectedIndex()}; + int[] newSelection; + int index = -1; + for (int i = 0; i < oldSelection.length; i++) { + if (oldSelection[i] == itemIndex) { + index = i; + break; + } + } + if (item.isSelected()) { + if (index != -1) // it was already selected + { + return; + } + newSelection = new int[oldSelection.length + 1]; + System.arraycopy(oldSelection, 0, newSelection, 0, oldSelection.length); + newSelection[newSelection.length - 1] = itemIndex; + } else { + if (index == -1) // it already wasn't selected + { + return; + } + newSelection = new int[oldSelection.length - 1]; + System.arraycopy(oldSelection, 0, newSelection, 0, index); + System.arraycopy(oldSelection, index + 1, newSelection, index, oldSelection.length - 1 - index); + } + setSelectedIndex(newSelection[0]); + } else { + // TODO: more cut-and-pasting badness + for (int i = 0; i < getSize(); i++) { + if (getElementAt(i) == ((Item) e.getSource()).getValue()) { + fireContentsChanged(JAXXComboBoxModel.this, i, i); + if (getSelectedIndex() == i) { + fireItemStateChanged(new ItemEvent(JAXXComboBox.this, ItemEvent.ITEM_STATE_CHANGED, getElementAt(i), ItemEvent.DESELECTED)); + } + return; + } + } + } + } + }; + for (Item item : items) { + item.addPropertyChangeListener(listener); + } + } + + + public Object getElementAt(int i) { + return items.get(i).getValue(); + } + + + public int getSize() { + return items.size(); + } + + + public Object getSelectedItem() { + return selectedItem; + } + + + public void setSelectedItem(Object selectedItem) { + if ((this.selectedItem != null && !this.selectedItem.equals(selectedItem)) || + this.selectedItem == null && selectedItem != null) { + this.selectedItem = selectedItem; + fireContentsChanged(this, -1, -1); + } + } + } + + + public JAXXComboBox() { + setRenderer(new DefaultListCellRenderer() { + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + ListModel model = list.getModel(); + if (model instanceof JAXXComboBoxModel) { + List/*<Item>*/ items = ((JAXXComboBoxModel) model).items; + Item item = null; + if (index == -1) { + for (Object item1 : items) { + Item testItem = (Item) item1; + if (testItem.getValue() == value) { + item = testItem; + break; + } + } + } else + item = (Item) items.get(index); + + if (item != null) { + String label = item.getLabel(); + if (label != null) { + value = label; + } + } + } + return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + } + }); + + addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + ListModel model = getModel(); + if (model instanceof JAXXComboBoxModel) { + List<Item> items = ((JAXXComboBoxModel) model).items; + for (int i = items.size() - 1; i >= 0; i--) { + boolean selected = getSelectedIndex() == i; + Item item = items.get(i); + if (selected != item.isSelected()) { + item.setSelected(selected); + } + } + } + } + }); + } + + + // this way we can keep it marked protected and still allow code in this file to call it + @Override + protected void fireItemStateChanged(ItemEvent e) { + super.fireItemStateChanged(e); + } + + public void setItems(List<Item> items) { + setModel(new JAXXComboBoxModel(items)); + List<Integer> selectedIndexList = new ArrayList<Integer>(); + for (int i = 0; i < items.size(); i++) + if (items.get(i).isSelected()) { + selectedIndexList.add(i); + } + int[] selectedIndices = new int[selectedIndexList.size()]; + for (int i = 0; i < selectedIndexList.size(); i++) { + selectedIndices[i] = selectedIndexList.get(i); + } + if (selectedIndices.length > 0) { + setSelectedIndex(selectedIndices[0]); + } + } +} \ No newline at end of file Copied: lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/JAXXList.java (from rev 857, lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/swing/JAXXList.java) =================================================================== --- lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/JAXXList.java (rev 0) +++ lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/JAXXList.java 2008-10-02 12:28:00 UTC (rev 862) @@ -0,0 +1,148 @@ +/* + * Copyright 2006 Ethan Nicholas. All rights reserved. + * Use is subject to license terms. + */ +package jaxx.runtime.swing; + +import javax.swing.AbstractListModel; +import javax.swing.DefaultListCellRenderer; +import javax.swing.JList; +import javax.swing.ListModel; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import java.awt.Component; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; +import java.util.List; + +public class JAXXList extends JList { + public class JAXXListModel extends AbstractListModel { + private List<Item> items; + private static final long serialVersionUID = -1598924187490122036L; + + public JAXXListModel(List<Item> items) { + this.items = items; + + PropertyChangeListener listener = new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent e) { + if (e.getPropertyName().equals(Item.SELECTED_PROPERTY)) { + Item item = (Item) e.getSource(); + int itemIndex = JAXXListModel.this.items.indexOf(item); + int[] oldSelection = getSelectedIndices(); + int[] newSelection; + int index = -1; + for (int i = 0; i < oldSelection.length; i++) { + if (oldSelection[i] == itemIndex) { + index = i; + break; + } + } + if (item.isSelected()) { + if (index != -1) // it was already selected + { + return; + } + newSelection = new int[oldSelection.length + 1]; + System.arraycopy(oldSelection, 0, newSelection, 0, oldSelection.length); + newSelection[newSelection.length - 1] = itemIndex; + } else { + if (index == -1) // it already wasn't selected + { + return; + } + newSelection = new int[oldSelection.length - 1]; + System.arraycopy(oldSelection, 0, newSelection, 0, index); + System.arraycopy(oldSelection, index + 1, newSelection, index, oldSelection.length - 1 - index); + } + setSelectedIndices(newSelection); + } else { + for (int i = 0; i < getSize(); i++) { + if (getElementAt(i) == ((Item) e.getSource()).getValue()) { + fireContentsChanged(JAXXListModel.this, i, i); + if (isSelectedIndex(i)) { + fireSelectionValueChanged(i, i, false); + } + return; + } + } + } + } + }; + for (Item item : items) { + item.addPropertyChangeListener(listener); + } + } + + + public Object getElementAt(int i) { + return items.get(i).getValue(); + } + + + public int getSize() { + return items.size(); + } + } + + public JAXXList() { + setCellRenderer(new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + ListModel model = list.getModel(); + if (model instanceof JAXXListModel) { + Item item = ((JAXXListModel) model).items.get(index); + String label = item.getLabel(); + if (label != null) { + value = label; + } + } + return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + } + }); + + addListSelectionListener(new ListSelectionListener() { + public void valueChanged(ListSelectionEvent e) { + ListModel model = getModel(); + if (model instanceof JAXXListModel) { + List<Item> items = ((JAXXListModel) model).items; + for (int i = items.size() - 1; i >= 0; i--) { + boolean selected = isSelectedIndex(i); + Item item = items.get(i); + if (selected != item.isSelected()) { + item.setSelected(selected); + } + } + } + } + }); + } + + + // this way we can keep it marked protected and still allow code in this file to call it + @Override + protected void fireSelectionValueChanged(int firstIndex, int lastIndex, boolean isAdjusting) { + super.fireSelectionValueChanged(firstIndex, lastIndex, isAdjusting); + } + + + public void setSelectedValue(Object value) { + super.setSelectedValue(value, true); + } + + + public void setItems(List<Item> items) { + setModel(new JAXXListModel(items)); + List<Integer> selectedIndexList = new ArrayList<Integer>(); + for (int i = 0; i < items.size(); i++) { + if (items.get(i).isSelected()) { + selectedIndexList.add(i); + } + } + int[] selectedIndices = new int[selectedIndexList.size()]; + for (int i = 0; i < selectedIndexList.size(); i++) { + selectedIndices[i] = selectedIndexList.get(i); + } + setSelectedIndices(selectedIndices); + } +} \ No newline at end of file Copied: lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/JAXXTab.java (from rev 857, lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/swing/JAXXTab.java) =================================================================== --- lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/JAXXTab.java (rev 0) +++ lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/JAXXTab.java 2008-10-02 12:28:00 UTC (rev 862) @@ -0,0 +1,23 @@ +/* +* ##% Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 Code Lutin, +* Tony Chemit +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* ##% */ +package jaxx.runtime.swing; + +/** @author chemit */ +public class JAXXTab extends Table { +} Copied: lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/JAXXToggleButton.java (from rev 857, lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/swing/JAXXToggleButton.java) =================================================================== --- lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/JAXXToggleButton.java (rev 0) +++ lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/JAXXToggleButton.java 2008-10-02 12:28:00 UTC (rev 862) @@ -0,0 +1,94 @@ +package jaxx.runtime.swing; + +public class JAXXToggleButton extends javax.swing.JToggleButton { + + protected String glueText; + protected String normalText; + protected String glueTooltipText; + protected String normalTooltipText; + protected int normalMnemonic; + protected int glueMnemonic; + + protected boolean _init; + + + public String getGlueText() { + return glueText; + } + + public String getNormalText() { + return normalText; + } + + public String getGlueTooltipText() { + return glueTooltipText; + } + + public String getNormalTooltipText() { + return normalTooltipText; + } + + public void setGlueText(String glueText) { + this.glueText = glueText; + + } + + public void setNormalText(String normalText) { + this.normalText = normalText; + + } + + public void setGlueTooltipText(String glueTooltipText) { + this.glueTooltipText = glueTooltipText; + } + + public int getNormalMnemonic() { + return normalMnemonic; + } + + public void setNormalMnemonic(int normalMnemonic) { + this.normalMnemonic = normalMnemonic; + } + + public int getGlueMnemonic() { + return glueMnemonic; + } + + public void setGlueMnemonic(int glueMnemonic) { + this.glueMnemonic = glueMnemonic; + } + + public void setNormalTooltipText(String normalTooltipText) { + this.normalTooltipText = normalTooltipText; + if (!_init) { + init(); + _init = true; + } + } + + @Override + public void setSelected(boolean b) { + super.setSelected(b); + if (isSelected()) { + setText(getGlueText()); + setToolTipText(getGlueTooltipText()); + setMnemonic(getGlueMnemonic()); + } else { + setText(getNormalText()); + setToolTipText(getNormalTooltipText()); + setMnemonic(getNormalMnemonic()); + } + revalidate(); + } + + public void init() { + setSelected(false); + } + + /* end raw body code */ + public JAXXToggleButton() { + super(); + _init = false; + } + +} \ No newline at end of file Copied: lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/JAXXTree.java (from rev 857, lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/swing/JAXXTree.java) =================================================================== --- lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/JAXXTree.java (rev 0) +++ lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/JAXXTree.java 2008-10-02 12:28:00 UTC (rev 862) @@ -0,0 +1,213 @@ +/* + * Copyright 2006 Ethan Nicholas. All rights reserved. + * Use is subject to license terms. + */ +package jaxx.runtime.swing; + +import javax.swing.JTree; +import javax.swing.event.TreeModelEvent; +import javax.swing.event.TreeModelListener; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; +import java.awt.Component; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; +import java.util.List; + +public class JAXXTree extends JTree { + private static final String SYNTHETIC = "<synthetic root node>"; + + public class JAXXTreeModel implements TreeModel { + private Item root; + private List<TreeModelListener> listeners = new ArrayList<TreeModelListener>(); + + public JAXXTreeModel(List<Item> items) { + if (items.size() == 1) { + this.root = items.get(0); + } + else { + this.root = new Item(null, null, SYNTHETIC, false); + for (Item item : items) { + root.addChild(item); + } + } + + PropertyChangeListener listener = new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent e) { + if (e.getPropertyName().equals(Item.SELECTED_PROPERTY)) { + Item item = (Item) e.getSource(); + if (item.isSelected()) { + addSelectionPath(getTreePath(item)); + } + else { + removeSelectionPath(getTreePath(item)); + } + } else { + Item item = (Item) e.getSource(); + boolean root = item.getParent() == null; + TreePath path = !root ? getTreePath(item.getParent()) : null; + fireTreeNodesChanged(new TreeModelEvent(JAXXTreeModel.this, path, + !root ? new int[]{item.getParent().getChildren().indexOf(item)} : null, + new Object[]{item.getValue()})); + } + } + }; + addPropertyChangeListener(root, listener); + } + + + private void addPropertyChangeListener(Item item, PropertyChangeListener listener) { + item.addPropertyChangeListener(listener); + List<Item> children = item.getChildren(); + for (Item aChildren : children) { + addPropertyChangeListener(aChildren, listener); + } + } + + + public void addTreeModelListener(TreeModelListener listener) { + listeners.add(listener); + } + + + /* This is an inefficient implementation, but hand-coded tree structures are unlikely to contain +enough nodes for that to really matter. This could be sped up with caching. */ + private Item findItem(Object value) { + return findItem(root, value); + } + + + private Item findItem(Item node, Object value) { + if (node.getValue() == value) + return node; + else { + List<Item> children = node.getChildren(); + for (Item aChildren : children) { + Item result = findItem(aChildren, value); + if (result != null) + return result; + } + return null; + } + } + + + private TreePath getTreePath(Item node) { + List<Object> path = new ArrayList<Object>(); + while (node != null) { + path.add(0, node.getValue()); + node = node.getParent(); + } + return new TreePath(path.toArray()); + } + + + public Object getChild(Object parent, int index) { + Item node = findItem(parent); + return node.getChildren().get(index).getValue(); + } + + + public int getChildCount(Object parent) { + Item node = findItem(parent); + return node.getChildren().size(); + } + + + public int getIndexOfChild(Object parent, Object child) { + Item node = findItem(parent); + List<Item> children = node.getChildren(); + for (int i = 0,j = children.size();i<j; i++) + if (children.get(i).getValue() == child) { + return i; + } + return -1; + } + + + public Object getRoot() { + return root.getValue(); + } + + + public boolean isLeaf(Object node) { + Item item = findItem(node); + return item != null && item.getChildren().size() == 0; + } + + + public void removeTreeModelListener(TreeModelListener listener) { + listeners.remove(listener); + } + + + public void fireTreeNodesChanged(TreeModelEvent e) { + for (TreeModelListener listener : listeners) { + listener.treeNodesChanged(e); + } + } + + + public void valueForPathChanged(TreePath path, Object newValue) { + } + } + + public JAXXTree() { + setCellRenderer(new DefaultTreeCellRenderer() { + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { + TreeModel model = tree.getModel(); + if (model instanceof JAXXTreeModel) { + Item item = ((JAXXTreeModel) model).findItem(value); + if (item != null) { + String label = item.getLabel(); + if (label != null) { + value = label; + } + } + } + return super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); + } + }); + + addTreeSelectionListener(new TreeSelectionListener() { + public void valueChanged(TreeSelectionEvent e) { + TreeModel model = getModel(); + if (model instanceof JAXXTreeModel) { + scan((JAXXTreeModel) model, ((JAXXTreeModel) model).root); + } + } + + + private void scan(JAXXTreeModel model, Item item) { + TreePath path = model.getTreePath(item); + if (item.isSelected() != isPathSelected(path)) { + item.setSelected(!item.isSelected()); + } + List<Item> children = item.getChildren(); + for (Item aChildren : children) { + scan(model, aChildren); + } + } + }); + } + + + public void setItems(List<Item> items) { + JAXXTreeModel model = new JAXXTreeModel(items); + if (model.getRoot() != null) { + setRootVisible(model.getRoot() != SYNTHETIC); + } + setModel(model); + } + + + public Object getSelectionValue() { + TreePath selectionPath = getSelectionPath(); + return selectionPath != null ? selectionPath.getLastPathComponent() : null; + } +} \ No newline at end of file Copied: lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/Spacer.java (from rev 857, lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/swing/Spacer.java) =================================================================== --- lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/Spacer.java (rev 0) +++ lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/Spacer.java 2008-10-02 12:28:00 UTC (rev 862) @@ -0,0 +1,10 @@ +/* + * Copyright 2006 Ethan Nicholas. All rights reserved. + * Use is subject to license terms. + */ +package jaxx.runtime.swing; + +import javax.swing.JComponent; + +public class Spacer extends JComponent { +} \ No newline at end of file Copied: lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/TabInfo.java (from rev 857, lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/swing/TabInfo.java) =================================================================== --- lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/TabInfo.java (rev 0) +++ lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/TabInfo.java 2008-10-02 12:28:00 UTC (rev 862) @@ -0,0 +1,165 @@ +/* + * Copyright 2006 Ethan Nicholas. All rights reserved. + * Use is subject to license terms. + */ +package jaxx.runtime.swing; + +import javax.swing.Icon; +import javax.swing.event.SwingPropertyChangeSupport; +import java.awt.Color; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; + +public class TabInfo { + public static String BACKGROUND_PROPERTY = "background"; + public static String DISABLED_ICON_PROPERTY = "disabledIcon"; + public static String DISPLAYED_MNEMONIC_INDEX_PROPERTY = "displayedMnemonicIndex"; + public static String ENABLED_PROPERTY = "enabled"; + public static String FOREGROUND_PROPERTY = "foreground"; + public static String ICON_PROPERTY = "icon"; + public static String MNEMONIC_PROPERTY = "mnemonic"; + public static String TITLE_PROPERTY = "title"; + public static String TOOL_TIP_TEXT_PROPERTY = "toolTipText"; + + private String id; + private Color background; + private Icon disabledIcon; + private int displayedMnemonicIndex = -1; + private boolean enabled = true; + private Color foreground; + private Icon icon; + private int mnemonic = -1; + private String title; + private String toolTipText; + + private PropertyChangeSupport propertyChangeSupport; + + public TabInfo() { + } + + public TabInfo(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public Color getBackground() { + return background; + } + + public void setBackground(Color background) { + Color oldValue = this.background; + this.background = background; + firePropertyChange(BACKGROUND_PROPERTY, oldValue, background); + } + + public Icon getDisabledIcon() { + return disabledIcon; + } + + public void setDisabledIcon(Icon disabledIcon) { + Icon oldValue = this.disabledIcon; + this.disabledIcon = disabledIcon; + firePropertyChange(DISABLED_ICON_PROPERTY, oldValue, disabledIcon); + } + + public int getDisplayedMnemonicIndex() { + return displayedMnemonicIndex; + } + + public void setDisplayedMnemonicIndex(int displayedMnemonicIndex) { + int oldValue = this.displayedMnemonicIndex; + this.displayedMnemonicIndex = displayedMnemonicIndex; + firePropertyChange(DISPLAYED_MNEMONIC_INDEX_PROPERTY, oldValue, displayedMnemonicIndex); + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + boolean oldValue = this.enabled; + this.enabled = enabled; + firePropertyChange(ENABLED_PROPERTY, oldValue, enabled); + } + + public Color getForeground() { + return foreground; + } + + public void setForeground(Color foreground) { + Color oldValue = this.foreground; + this.foreground = foreground; + firePropertyChange(FOREGROUND_PROPERTY, oldValue, foreground); + } + + public Icon getIcon() { + return icon; + } + + public void setIcon(Icon icon) { + Icon oldValue = this.icon; + this.icon = icon; + firePropertyChange(ICON_PROPERTY, oldValue, icon); + } + + public int getMnemonic() { + return mnemonic; + } + + public void setMnemonic(int mnemonic) { + int oldValue = this.mnemonic; + this.mnemonic = mnemonic; + firePropertyChange(MNEMONIC_PROPERTY, oldValue, mnemonic); + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + String oldValue = this.title; + this.title = title; + firePropertyChange(TITLE_PROPERTY, oldValue, title); + } + + public String getToolTipText() { + return toolTipText; + } + + public void setToolTipText(String toolTipText) { + String oldValue = this.toolTipText; + this.toolTipText = toolTipText; + firePropertyChange(TOOL_TIP_TEXT_PROPERTY, oldValue, toolTipText); + } + + private PropertyChangeSupport getPropertyChangeSupport() { + if (propertyChangeSupport == null) { + propertyChangeSupport = new SwingPropertyChangeSupport(this); + } + return propertyChangeSupport; + } + + public void addPropertyChangeListener(PropertyChangeListener listener) { + getPropertyChangeSupport().addPropertyChangeListener(listener); + } + + public void addPropertyChangeListener(String property, PropertyChangeListener listener) { + getPropertyChangeSupport().addPropertyChangeListener(property, listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + getPropertyChangeSupport().removePropertyChangeListener(listener); + } + + public void removePropertyChangeListener(String property, PropertyChangeListener listener) { + getPropertyChangeSupport().removePropertyChangeListener(property, listener); + } + + protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { + if (propertyChangeSupport != null) + getPropertyChangeSupport().firePropertyChange(propertyName, oldValue, newValue); + } +} Copied: lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/TabInfoPropertyChangeListener.java (from rev 857, lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/swing/TabInfoPropertyChangeListener.java) =================================================================== --- lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/TabInfoPropertyChangeListener.java (rev 0) +++ lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/TabInfoPropertyChangeListener.java 2008-10-02 12:28:00 UTC (rev 862) @@ -0,0 +1,41 @@ +/* + * Copyright 2006 Ethan Nicholas. All rights reserved. + * Use is subject to license terms. + */ +package jaxx.runtime.swing; + +import javax.swing.Icon; +import javax.swing.JTabbedPane; +import java.awt.Color; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +public class TabInfoPropertyChangeListener implements PropertyChangeListener { + private JTabbedPane tabs; + private int tabIndex; + + public TabInfoPropertyChangeListener(JTabbedPane tabs, int tabIndex) { + this.tabs = tabs; + this.tabIndex = tabIndex; + } + + public void propertyChange(PropertyChangeEvent e) { + String name = e.getPropertyName(); + if (name.equals(TabInfo.TITLE_PROPERTY)) + tabs.setTitleAt(tabIndex, (String) e.getNewValue()); + else if (name.equals(TabInfo.TOOL_TIP_TEXT_PROPERTY)) + tabs.setToolTipTextAt(tabIndex, (String) e.getNewValue()); + else if (name.equals(TabInfo.FOREGROUND_PROPERTY)) + tabs.setForegroundAt(tabIndex, (Color) e.getNewValue()); + else if (name.equals(TabInfo.BACKGROUND_PROPERTY)) + tabs.setBackgroundAt(tabIndex, (Color) e.getNewValue()); + else if (name.equals(TabInfo.MNEMONIC_PROPERTY)) + tabs.setMnemonicAt(tabIndex, (Integer) e.getNewValue()); + else if (name.equals(TabInfo.DISPLAYED_MNEMONIC_INDEX_PROPERTY)) + tabs.setDisplayedMnemonicIndexAt(tabIndex, (Integer) e.getNewValue()); + else if (name.equals(TabInfo.ICON_PROPERTY)) + tabs.setIconAt(tabIndex, (Icon) e.getNewValue()); + else if (name.equals(TabInfo.DISABLED_ICON_PROPERTY)) + tabs.setDisabledIconAt(tabIndex, (Icon) e.getNewValue()); + } +} Copied: lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/Utils.java (from rev 857, lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/swing/Utils.java) =================================================================== --- lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/Utils.java (rev 0) +++ lutinjaxx/trunk/core/src/main/java/jaxx/runtime/swing/Utils.java 2008-10-02 12:28:00 UTC (rev 862) @@ -0,0 +1,59 @@ +/* + * Copyright 2006 Ethan Nicholas. All rights reserved. + * Use is subject to license terms. + */ +package jaxx.runtime.swing; + +import javax.swing.SwingUtilities; +import javax.swing.text.AbstractDocument; +import javax.swing.text.JTextComponent; +import java.lang.reflect.Field; + +public class Utils { + private static Field numReaders; + private static Field notifyingListeners; + + public static void setText(final JTextComponent c, final String text) { + try { + // AbstractDocument deadlocks if we try to acquire a write lock while a read lock is held by the current thread + // If there are any readers, dispatch an invokeLater. This should only happen in the event of circular bindings. + // Similarly, circular bindings can result in an "Attempt to mutate in notification" error, which we deal with + // by checking for the 'notifyingListeners' property. + AbstractDocument document = (AbstractDocument) c.getDocument(); + if (numReaders == null) { + numReaders = AbstractDocument.class.getDeclaredField("numReaders"); + numReaders.setAccessible(true); + } + if (notifyingListeners == null) { + notifyingListeners = AbstractDocument.class.getDeclaredField("notifyingListeners"); + notifyingListeners.setAccessible(true); + } + + if (notifyingListeners.get(document).equals(Boolean.TRUE)) + return; + + if (((Integer) numReaders.get(document)).intValue() > 0) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (!c.getText().equals(text)) + c.setText(text); + } + }); + return; + } + + String oldText = c.getText(); + if (oldText == null || !oldText.equals(text)) + c.setText(text); + } + catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + catch (SecurityException e) { + c.setText(text); + } + } +} Copied: lutinjaxx/trunk/core/src/main/java/jaxx/tags/swing (from rev 857, lutinjaxx/trunk/jaxx-swing/src/main/java/jaxx/tags/swing) Copied: lutinjaxx/trunk/core/src/test/java/jaxx/runtime/UtilTest.java (from rev 857, lutinjaxx/trunk/runtime/src/test/java/jaxx/runtime/UtilTest.java) =================================================================== --- lutinjaxx/trunk/core/src/test/java/jaxx/runtime/UtilTest.java (rev 0) +++ lutinjaxx/trunk/core/src/test/java/jaxx/runtime/UtilTest.java 2008-10-02 12:28:00 UTC (rev 862) @@ -0,0 +1,25 @@ +package jaxx.runtime; + +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +public class UtilTest extends junit.framework.TestCase { + private int count; + + + public void testGetEventListener() { + count = 0; + DocumentListener listener = (DocumentListener) jaxx.runtime.Util.getEventListener(javax.swing.event.DocumentListener.class, this, "incCount"); + listener.insertUpdate(null); + assertEquals(count, 1); + DocumentListener listener2 = (DocumentListener) jaxx.runtime.Util.getEventListener(javax.swing.event.DocumentListener.class, this, "incCount"); + listener2.removeUpdate(null); + assertEquals(count, 2); + //assertTrue("Received two different event listeners despite using identical parameters", listener == listener2); + } + + + public void incCount(DocumentEvent e) { + count++; + } +} \ No newline at end of file Deleted: lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/DataBindingListener.java =================================================================== --- lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/DataBindingListener.java 2008-10-02 12:21:07 UTC (rev 861) +++ lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/DataBindingListener.java 2008-10-02 12:28:00 UTC (rev 862) @@ -1,46 +0,0 @@ -/* - * Copyright 2006 Ethan Nicholas. All rights reserved. - * Use is subject to license terms. - */ -package jaxx.runtime; - -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - - -/** - * A <code>PropertyChangeListener</code> which processes a data binding when it receives a - * <code>PropertyChangeEvent</code>. - */ -public class DataBindingListener implements PropertyChangeListener { - private JAXXObject object; - private String dest; - - /** - * Creates a new <code>DataBindingListener</code> which will run the given data binding - * when it receives a <code>PropertyChangeEvent</code>. - * - * @param object the object in which the data binding exists - * @param dest the name of the data binding to run - */ - public DataBindingListener(JAXXObject object, String dest) { - this.object = object; - this.dest = dest; - } - - - /** - * Processes the data binding in response to a <code>PropertyChangeEvent</code>. - * - * @param e the event which triggered the binding - */ - public void propertyChange(PropertyChangeEvent e) { - object.processDataBinding(dest); - - // for now, handle dependency changes by always removing & reapplying - // the binding. We should be more efficient and only do this when it's - // actually necessary - object.removeDataBinding(dest); - object.applyDataBinding(dest); - } -} \ No newline at end of file Deleted: lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/DataBindingUpdateListener.java =================================================================== --- lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/DataBindingUpdateListener.java 2008-10-02 12:21:07 UTC (rev 861) +++ lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/DataBindingUpdateListener.java 2008-10-02 12:28:00 UTC (rev 862) @@ -1,46 +0,0 @@ -/* - * Copyright 2006 Ethan Nicholas. All rights reserved. - * Use is subject to license terms. - */ -package jaxx.runtime; - -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - - -/** - * A <code>PropertyChangeListener</code> which removes and re-applies a data binding - * when it receives a <code>PropertyChangeEvent</code>. - */ -public class DataBindingUpdateListener implements PropertyChangeListener { - private JAXXObject object; - private String dest; - - /** - * Creates a new <code>DataBindingUpdateListener</code> which will remove and re-apply a - * data binding when it receives a <code>PropertyChangeEvent</code>. - * - * @param object the object in which the data binding exists - * @param dest the name of the data binding to reapply - */ - public DataBindingUpdateListener(JAXXObject object, String dest) { - this.object = object; - this.dest = dest; - } - - - public String getBindingName() { - return dest; - } - - - /** - * Updates the data binding in response to a <code>PropertyChangeEvent</code>. - * - * @param e the event which triggered the binding - */ - public void propertyChange(PropertyChangeEvent e) { - object.removeDataBinding(dest); - object.applyDataBinding(dest); - } -} \ No newline at end of file Deleted: lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/Util.java =================================================================== --- lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/Util.java 2008-10-02 12:21:07 UTC (rev 861) +++ lutinjaxx/trunk/runtime/src/main/java/jaxx/runtime/Util.java 2008-10-02 12:28:00 UTC (rev 862) @@ -1,253 +0,0 @@ -package jaxx.runtime; - -import javax.swing.JComponent; -import java.awt.Component; -import java.awt.Dimension; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.lang.ref.WeakReference; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.WeakHashMap; -import java.util.EventListener; -import java.util.zip.GZIPInputStream; - -public class Util { - // Maps root objects to lists of event listeners - private static Map<Object, WeakReference<List<EventListenerDescriptor>>> eventListeners = new WeakHashMap<Object, WeakReference<List<EventListenerDescriptor>>>(); - private static Map<JAXXObject, WeakReference<List<DataBindingUpdateListener>>> dataBindingUpdateListeners = new WeakHashMap<JAXXObject, WeakReference<List<DataBindingUpdateListener>>>(); - - - private static class EventListenerDescriptor { - Class listenerClass; - String listenerMethodName; - String methodName; - Object eventListener; - } - - - /** - * Decodes the serialized representation of a JAXXObjectDescriptor. The string must be a byte-to-character mapping - * of the binary serialization data for a JAXXObjectDescriptor. See the comments in JAXXCompiler.createJAXXObjectDescriptorField - * for the rationale behind this (admittedly ugly) approach. - * - * @param descriptor descriptor to decode - * @return the dedoced descriptor - */ - public static JAXXObjectDescriptor decodeJAXXObjectDescriptor(String descriptor) { - try { - byte[] data = new byte[descriptor.length()]; - // copy low-order bytes into the array. The high-order bytes should all be zero. - System.arraycopy(descriptor.getBytes(),0,data,0,data.length); - //descriptor.getBytes(0, descriptor.length(), data, 0); - ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(data)); - return (JAXXObjectDescriptor) in.readObject(); - } - catch (IOException e) { - throw new RuntimeException("Internal error: can't-happen error", e); - } - catch (ClassNotFoundException e) { - throw new RuntimeException("Internal error: can't-happen error", e); - } - } - - - public static JAXXObjectDescriptor decodeCompressedJAXXObjectDescriptor(String descriptor) { - try { - byte[] data = new byte[descriptor.length()]; - // copy low-order bytes into the array. The high-order bytes should all be zero. - System.arraycopy(descriptor.getBytes(),0,data,0,data.length); - //descriptor.getBytes(0, descriptor.length(), data, 0); - ObjectInputStream in = new ObjectInputStream(new GZIPInputStream(new ByteArrayInputStream(data))); - return (JAXXObjectDescriptor) in.readObject(); - } - catch (IOException e) { - throw new RuntimeException("Internal error: can't-happen error", e); - } - catch (ClassNotFoundException e) { - throw new RuntimeException("Internal error: can't-happen error", e); - } - } - - - public static Object getEventListener(Class<? extends EventListener> listenerClass, final String listenerMethodName, final Object methodContainer, final String methodName) { - WeakReference<List<EventListenerDescriptor>> ref = eventListeners.get(methodContainer); - List<EventListenerDescriptor> descriptors = ref != null ? ref.get() : null; - if (descriptors == null) { - descriptors = new ArrayList<EventListenerDescriptor>(); - eventListeners.put(methodContainer, new WeakReference<List<EventListenerDescriptor>>(descriptors)); - } else { - for (EventListenerDescriptor descriptor : descriptors) { - if (listenerClass == descriptor.listenerClass && - (listenerMethodName == null ? descriptor.listenerMethodName == null : listenerMethodName.equals(descriptor.listenerMethodName)) && - methodName.equals(descriptor.methodName)) { - return descriptor.eventListener; - } - } - } - - // else we need to create a new listener - final EventListenerDescriptor descriptor = new EventListenerDescriptor(); - descriptor.listenerClass = listenerClass; - descriptor.listenerMethodName = listenerMethodName; - descriptor.methodName = methodName; - try { - final List<Method> listenerMethods = Arrays.asList(listenerClass.getMethods()); - Method listenerMethod = null; - if (listenerMethodName != null) { - for (Method listenerMethod1 : listenerMethods) { - if ((listenerMethod1).getName().equals(listenerMethodName)) { - listenerMethod = listenerMethod1; - break; - } - } - } - if (listenerMethodName != null && listenerMethod == null) - throw new IllegalArgumentException("no method named " + listenerMethodName + " found in class " + listenerClass.getName()); - Class[] parameterTypes = listenerMethods.get(0).getParameterTypes(); - Class<?> methodContainerClass = methodContainer.getClass(); - final Method targetMethod = methodContainerClass.getMethod(methodName, parameterTypes); - descriptor.eventListener = Proxy.newProxyInstance(listenerClass.getClassLoader(), - new Class[]{listenerClass}, - new InvocationHandler() { - public Object invoke(Object proxy, Method method, Object[] args) { - String methodName = method.getName(); - if ((listenerMethodName == null && listenerMethods.contains(method)) || methodName.equals(listenerMethodName)) { - try { - return targetMethod.invoke(methodContainer, args); - } - catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - catch (InvocationTargetException e) { - throw new RuntimeException(e); - } - } - if (methodName.equals("toString")) { - return toString(); - } - if (methodName.equals("equals")) { - return descriptor.eventListener == args[0]; - } - if (methodName.equals("hashCode")) { - return hashCode(); - } - return null; - } - }); - descriptors.add(descriptor); - return descriptor.eventListener; - } - catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } - } - - - public static Object getEventListener(Class<? extends EventListener> listenerClass, final Object methodContainer, final String methodName) { - return getEventListener(listenerClass, null, methodContainer, methodName); - } - - - public static DataBindingUpdateListener getDataBindingUpdateListener(JAXXObject object, String bindingName) { - WeakReference<List<DataBindingUpdateListener>> ref = dataBindingUpdateListeners.get(object); - List<DataBindingUpdateListener> listeners = ref == null ? null : ref.get(); - if (listeners == null) { - listeners = new ArrayList<DataBindingUpdateListener>(); - dataBindingUpdateListeners.put(object, new WeakReference<List<DataBindingUpdateListener>>(listeners)); - } else { - for (DataBindingUpdateListener listener : listeners) { - if (bindingName.equals(listener.getBindingName())) - return listener; - } - } - DataBindingUpdateListener listener = new DataBindingUpdateListener(object, bindingName); - listeners.add(listener); - return listener; - } - - - public static void setComponentWidth(Component component, int width) { - component.setSize(width, component.getHeight()); - if (component instanceof JComponent) { - JComponent jcomponent = (JComponent) component; - jcomponent.setPreferredSize(new Dimension(width, jcomponent.getPreferredSize().height)); - jcomponent.setMinimumSize(new Dimension(width, jcomponent.getPreferredSize().height)); - if (jcomponent.isDisplayable()) - jcomponent.revalidate(); - } - } - - - public static void setComponentHeight(Component component, int height) { - component.setSize(component.getWidth(), height); - if (component instanceof JComponent) { - JComponent jcomponent = (JComponent) component; - jcomponent.setPreferredSize(new Dimension(jcomponent.getPreferredSize().width, height)); - jcomponent.setMinimumSize(new Dimension(jcomponent.getPreferredSize().width, height)); - if (jcomponent.isDisplayable()) - jcomponent.revalidate(); - } - } - - - public static boolean assignment(boolean value, String name, JAXXObject src) { - src.firePropertyChange(name.trim(), null, "dummy value"); - return value; - } - - - public static byte assignment(byte value, String name, JAXXObject src) { - src.firePropertyChange(name.trim(), null, "dummy value"); - return value; - } - - - public static short assignment(short value, String name, JAXXObject src) { - src.firePropertyChange(name.trim(), null, "dummy value"); - return value; - } - - - public static int assignment(int value, String name, JAXXObject src) { - src.firePropertyChange(name.trim(), null, "dummy value"); - return value; - } - - - public static long assignment(long value, String name, JAXXObject src) { - src.firePropertyChange(name.trim(), null, "dummy value"); - return value; - } - - - public static float assignment(float value, String name, JAXXObject src) { - src.firePropertyChange(name.trim(), null, "dummy value"); - return value; - } - - - public static double assignment(double value, String name, JAXXObject src) { - src.firePropertyChange(name.trim(), null, "dummy value"); - return value; - } - - - public static char assignment(char value, String name, JAXXObject src) { - src.firePropertyChange(name.trim(), null, "dummy value"); - return value; - } - - - public static java.lang.Object assignment(java.lang.Object value, String name, JAXXObject src) { - src.firePropertyChange(name.trim(), null, "dummy value"); - return value; - } -}
participants (1)
-
tchemit@users.labs.libre-entreprise.org