diff options
Diffstat (limited to 'javax/swing')
61 files changed, 8643 insertions, 1345 deletions
diff --git a/javax/swing/AbstractAction.java b/javax/swing/AbstractAction.java index da65bdd1d..bd3167e1e 100644 --- a/javax/swing/AbstractAction.java +++ b/javax/swing/AbstractAction.java @@ -79,7 +79,7 @@ public abstract class AbstractAction */ public AbstractAction() { - this(""); // TODO: default name + this(null); } /** @@ -90,7 +90,7 @@ public abstract class AbstractAction */ public AbstractAction(String name) { - this(name, null); // TODO: default icon?? + this(name, null); } /** @@ -174,7 +174,7 @@ public abstract class AbstractAction public void putValue(String key, Object value) { Object old = getValue(key); - if (old != value) + if (old == null || !old.equals(value)) { store.put(key, value); firePropertyChange(key, old, value); diff --git a/javax/swing/BoxLayout.java b/javax/swing/BoxLayout.java index ebc0b4c21..408dea934 100644 --- a/javax/swing/BoxLayout.java +++ b/javax/swing/BoxLayout.java @@ -335,8 +335,15 @@ public class BoxLayout implements LayoutManager2, Serializable checkTotalRequirements(); Insets i = container.getInsets(); - return new Dimension(xTotal.maximum + i.left + i.right, - yTotal.maximum + i.top + i.bottom); + int xDim = xTotal.maximum + i.left + i.right; + int yDim = yTotal.maximum + i.top + i.bottom; + + // Check for overflow + if (xDim < xTotal.maximum) + xDim = Integer.MAX_VALUE; + if (yDim < yTotal.maximum) + yDim = Integer.MAX_VALUE; + return new Dimension(xDim, yDim); } } diff --git a/javax/swing/JComponent.java b/javax/swing/JComponent.java index 6f9c9dcf8..b1ad855f2 100644 --- a/javax/swing/JComponent.java +++ b/javax/swing/JComponent.java @@ -45,7 +45,6 @@ import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.EventQueue; -import java.awt.FlowLayout; import java.awt.FocusTraversalPolicy; import java.awt.Font; import java.awt.Graphics; @@ -621,7 +620,6 @@ public abstract class JComponent extends Container implements Serializable public JComponent() { super(); - super.setLayout(new FlowLayout()); setDropTarget(new DropTarget()); defaultLocale = Locale.getDefault(); debugGraphicsOptions = DebugGraphics.NONE_OPTION; @@ -1296,7 +1294,7 @@ public abstract class JComponent extends Container implements Serializable { Dimension prefSize = null; if (preferredSize != null) - prefSize = preferredSize; + prefSize = new Dimension(preferredSize); else if (ui != null) { @@ -1307,12 +1305,7 @@ public abstract class JComponent extends Container implements Serializable if (prefSize == null) prefSize = super.getPreferredSize(); - // make sure that prefSize is not smaller than minSize - if (minimumSize != null && prefSize != null - && (minimumSize.width > prefSize.width - || minimumSize.height > prefSize.height)) - prefSize = new Dimension(Math.max(minimumSize.width, prefSize.width), - Math.max(minimumSize.height, prefSize.height)); + return prefSize; } @@ -2740,7 +2733,7 @@ public abstract class JComponent extends Container implements Serializable */ public void updateUI() { - System.out.println("update UI not overwritten in class: " + this); + // Nothing to do here. } public static Locale getDefaultLocale() @@ -3271,7 +3264,7 @@ public abstract class JComponent extends Container implements Serializable Rectangle target = SwingUtilities.convertRectangle(found, currentClip, newParent); - if (target.contains(parRect) || target.intersects(parRect)) + if (! target.intersection(parRect).equals(target)) { found = newParent; currentClip = target; @@ -3287,10 +3280,12 @@ public abstract class JComponent extends Container implements Serializable boolean skip = true; for (int i = children.length - 1; i >= 0; i--) { + boolean nextSkip = skip; if (children[i] == parent) - skip = false; + nextSkip = false; if (skip) continue; + skip = nextSkip; Component c = children[i]; Rectangle compBounds = c.getBounds(); // If the component completely overlaps the clip in question, we diff --git a/javax/swing/JEditorPane.java b/javax/swing/JEditorPane.java index 6e99a9595..3560ffd57 100644 --- a/javax/swing/JEditorPane.java +++ b/javax/swing/JEditorPane.java @@ -43,8 +43,10 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; +import java.io.StringReader; import java.net.MalformedURLException; import java.net.URL; +import java.util.HashMap; import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleHyperlink; @@ -88,6 +90,7 @@ import javax.swing.text.html.HTMLEditorKit; * * @author original author unknown * @author Roman Kennke (roman@kennke.org) + * @author Anthony Balkissoon abalkiss at redhat dot com */ public class JEditorPane extends JTextComponent { @@ -507,9 +510,16 @@ public class JEditorPane extends JTextComponent private EditorKit editorKit; boolean focus_root; + + // A mapping between content types and registered EditorKit types + static HashMap registerMap; + + // A mapping between content types and used EditorKits + HashMap editorMap; public JEditorPane() { + init(); setEditorKit(createDefaultEditorKit()); } @@ -520,24 +530,69 @@ public class JEditorPane extends JTextComponent public JEditorPane(String type, String text) { + init(); setEditorKit(createEditorKitForContentType(type)); setText(text); } public JEditorPane(URL url) throws IOException { - this(); + init (); + setEditorKit (createEditorKitForContentType("text/html"));; setPage(url); } + + /** + * Called by the constructors to set up the default bindings for content + * types and EditorKits. + */ + void init() + { + editorMap = new HashMap(); + registerMap = new HashMap(); + registerEditorKitForContentType("application/rtf", + "javax.swing.text.rtf.RTFEditorKit"); + registerEditorKitForContentType("text/plain", + "javax.swing.JEditorPane$PlainEditorKit"); + registerEditorKitForContentType("text/html", + "javax.swing.text.html.HTMLEditorKit"); + registerEditorKitForContentType("text/rtf", + "javax.swing.text.rtf.RTFEditorKit"); + } protected EditorKit createDefaultEditorKit() { return new PlainEditorKit(); } + /** + * Creates and returns an EditorKit that is appropriate for the given + * content type. This is created using the default recognized types + * plus any EditorKit types that have been registered. + * + * @see #registerEditorKitForContentType(String, String) + * @see #registerEditorKitForContentType(String, String, ClassLoader) + * @param type the content type + * @return an EditorKit for use with the given content type + */ public static EditorKit createEditorKitForContentType(String type) { - return new PlainEditorKit(); + // TODO: Have to handle the case where a ClassLoader was specified + // when the EditorKit was registered + EditorKit e = null; + String className = (String)registerMap.get(type); + if (className != null) + { + try + { + e = (EditorKit) Class.forName(className).newInstance(); + } + catch (Exception e2) + { + // TODO: Not sure what to do here. + } + } + return e; } /** @@ -586,14 +641,44 @@ public class JEditorPane extends JTextComponent return editorKit; } + /** + * Returns the class name of the EditorKit associated with the given + * content type. + * + * @since 1.3 + * @param type the content type + * @return the class name of the EditorKit associated with this content type + */ public static String getEditorKitClassNameForContentType(String type) { - return "text/plain"; + return (String) registerMap.get(type); } + /** + * Returns the EditorKit to use for the given content type. If an + * EditorKit has been explicitly set via + * <code>setEditorKitForContentType</code> + * then it will be returned. Otherwise an attempt will be made to create + * an EditorKit from the default recognzied content types or any + * EditorKits that have been registered. If none can be created, a + * PlainEditorKit is created. + * + * @see #registerEditorKitForContentType(String, String) + * @see #registerEditorKitForContentType(String, String, ClassLoader) + * @param type the content type + * @return an appropriate EditorKit for the given content type + */ public EditorKit getEditorKitForContentType(String type) { - return editorKit; + // First check if an EditorKit has been explicitly set. + EditorKit e = (EditorKit) editorMap.get(type); + // Then check to see if we can create one. + if (e == null) + e = createEditorKitForContentType(type); + // Otherwise default to PlainEditorKit. + if (e == null) + e = new PlainEditorKit(); + return e; } /** @@ -677,12 +762,17 @@ public class JEditorPane extends JTextComponent } /** - * Establishes the default bindings of type to classname. + * Establishes a binding between type and classname. This enables + * us to create an EditorKit later for the given content type. + * + * @param type the content type + * @param classname the name of the class that is associated with this + * content type */ public static void registerEditorKitForContentType(String type, String classname) { - // TODO: Implement this properly. + registerMap.put(type, classname); } /** @@ -702,6 +792,7 @@ public class JEditorPane extends JTextComponent public void replaceSelection(String content) { // TODO: Implement this properly. + super.replaceSelection(content); } /** @@ -749,9 +840,14 @@ public class JEditorPane extends JTextComponent accessibleContext = null; } + /** + * Explicitly sets an EditorKit to be used for the given content type. + * @param type the content type + * @param k the EditorKit to use for the given content type + */ public void setEditorKitForContentType(String type, EditorKit k) { - // FIXME: editorKitCache.put(type, kit); + editorMap.put(type, k); } /** @@ -781,9 +877,36 @@ public class JEditorPane extends JTextComponent } } + /** + * Sets the text of the JEditorPane. The argument <code>t</code> + * is expected to be in the format of the current EditorKit. This removes + * the content of the current document and uses the EditorKit to read in the + * new text. This allows the EditorKit to handle the String rather than just + * inserting in plain text. + * + * @param t the text to display in this JEditorPane + */ public void setText(String t) { - super.setText(t); + try + { + // Remove the current content. + Document doc = getDocument(); + doc.remove(0, doc.getLength()); + if (t == null || t == "") + return; + + // Let the EditorKit read the text into the Document. + getEditorKit().read(new StringReader(t), doc, 0); + } + catch (BadLocationException ble) + { + // TODO: Don't know what to do here. + } + catch (IOException ioe) + { + // TODO: Don't know what to do here. + } } /** diff --git a/javax/swing/JFileChooser.java b/javax/swing/JFileChooser.java index fcd0aff65..3a9d6a01f 100644 --- a/javax/swing/JFileChooser.java +++ b/javax/swing/JFileChooser.java @@ -40,6 +40,7 @@ package javax.swing; import java.awt.Component; import java.awt.Frame; import java.awt.HeadlessException; +import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; @@ -657,7 +658,8 @@ public class JFileChooser extends JComponent implements Accessible retval = ERROR_OPTION; - d.pack(); + Insets i = d.getInsets(); + d.setSize(500 + i.top + i.bottom, d.getPreferredSize().height); d.show(); return retval; } @@ -681,7 +683,8 @@ public class JFileChooser extends JComponent implements Accessible retval = ERROR_OPTION; - d.pack(); + Insets i = d.getInsets(); + d.setSize(500 + i.top + i.bottom, d.getPreferredSize().height); d.show(); return retval; } @@ -707,7 +710,8 @@ public class JFileChooser extends JComponent implements Accessible retval = ERROR_OPTION; - d.pack(); + Insets i = d.getInsets(); + d.setSize(500 + i.top + i.bottom, d.getPreferredSize().height); d.show(); return retval; } diff --git a/javax/swing/JFormattedTextField.java b/javax/swing/JFormattedTextField.java index 9890df200..761955d6d 100644 --- a/javax/swing/JFormattedTextField.java +++ b/javax/swing/JFormattedTextField.java @@ -40,15 +40,21 @@ package javax.swing; import java.awt.event.FocusEvent; import java.io.Serializable; +import java.text.DateFormat; import java.text.Format; +import java.text.NumberFormat; import java.text.ParseException; import java.util.Date; +import javax.swing.text.AbstractDocument; import javax.swing.text.DateFormatter; import javax.swing.text.DefaultFormatter; +import javax.swing.text.DefaultFormatterFactory; import javax.swing.text.Document; import javax.swing.text.DocumentFilter; +import javax.swing.text.InternationalFormatter; import javax.swing.text.NavigationFilter; +import javax.swing.text.NumberFormatter; /** * A text field that makes use of a formatter to display and edit a specific @@ -62,6 +68,7 @@ import javax.swing.text.NavigationFilter; * formatting of the value of the JFormattedTextField. * * @author Michael Koch + * @author Anthony Balkissoon abalkiss at redhat dot com * * @since 1.4 */ @@ -85,58 +92,184 @@ public class JFormattedTextField extends JTextField //Do nothing here. } + /** + * Clones the AbstractFormatter and removes the association to any + * particular JFormattedTextField. + * + * @return a clone of this formatter with no association to any particular + * JFormattedTextField + * @throws CloneNotSupportedException if the Object's class doesn't support + * the {@link Cloneable} interface + */ protected Object clone () throws CloneNotSupportedException { - throw new InternalError ("not implemented"); + // Clone this formatter. + AbstractFormatter newFormatter = (AbstractFormatter)super.clone(); + + // And remove the association to the JFormattedTextField. + newFormatter.textField = null; + return newFormatter; } + /** + * Returns a custom set of Actions that this formatter supports. Should + * be subclassed by formatters that have a custom set of Actions. + * + * @return <code>null</code>. Should be subclassed by formatters that want + * to install custom Actions on the JFormattedTextField. + */ protected Action[] getActions () { - return textField.getActions(); + return null; } + /** + * Gets the DocumentFilter for this formatter. Should be subclassed + * by formatters wishing to install a filter that oversees Document + * mutations. + * + * @return <code>null</code>. Should be subclassed by formatters + * that want to restrict Document mutations. + */ protected DocumentFilter getDocumentFilter () { - throw new InternalError ("not implemented"); + // Subclasses should override this if they want to install a + // DocumentFilter. + return null; } + /** + * Returns the JFormattedTextField on which this formatter is + * currently installed. + * + * @return the JFormattedTextField on which this formatter is currently + * installed + */ protected JFormattedTextField getFormattedTextField () { return textField; } + /** + * Gets the NavigationFilter for this formatter. Should be subclassed + * by formatters (such as {@link DefaultFormatter}) that wish to + * restrict where the cursor can be placed within the text field. + * + * @return <code>null</code>. Subclassed by formatters that want to restrict + * cursor location within the JFormattedTextField. + */ protected NavigationFilter getNavigationFilter () { - return textField.getNavigationFilter(); + // This should be subclassed if the formatter wants to install + // a NavigationFilter on the JFormattedTextField. + return null; } + /** + * Installs this formatter on the specified JFormattedTextField. This + * converts the current value to a displayable String and displays it, + * and installs formatter specific Actions from <code>getActions</code>. + * It also installs a DocumentFilter and NavigationFilter on the + * JFormattedTextField. + * <p> + * If there is a <code>ParseException</code> this sets the text to an + * empty String and marks the text field in an invalid state. + * + * @param textField the JFormattedTextField on which to install this + * formatter + */ public void install(JFormattedTextField textField) { + // Uninstall the current textfield. if (this.textField != null) - uninstall(); + uninstall(); this.textField = textField; + + // Install some state on the text field, including display text, + // DocumentFilter, NavigationFilter, and formatter specific Actions. + if (textField != null) + { + try + { + // Set the text of the field. + textField.setText(valueToString(textField.getValue())); + Document doc = textField.getDocument(); + + // Set the DocumentFilter for the field's Document. + if (doc instanceof AbstractDocument) + ((AbstractDocument)doc).setDocumentFilter(getDocumentFilter()); + + // Set the NavigationFilter. + textField.setNavigationFilter(getNavigationFilter()); + + // Set the Formatter Actions + // FIXME: Have to add the actions from getActions() + } + catch (ParseException pe) + { + // Set the text to an empty String and mark the field as invalid. + textField.setText(""); + setEditValid(false); + } + } } + /** + * Clears the state installed on the JFormattedTextField by the formatter. + * This resets the DocumentFilter, NavigationFilter, and any additional + * Actions (returned by <code>getActions()</code>). + */ public void uninstall () { + // Set the DocumentFilter for the field's Document. + Document doc = textField.getDocument(); + if (doc instanceof AbstractDocument) + ((AbstractDocument)doc).setDocumentFilter(null); + textField.setNavigationFilter(null); + // FIXME: Have to remove the Actions from getActions() this.textField = null; } + /** + * Invoke this method when invalid values are entered. This forwards the + * call to the JFormattedTextField. + */ protected void invalidEdit () { textField.invalidEdit(); } + /** + * This method updates the <code>editValid</code> property of + * JFormattedTextField. + * + * @param valid the new state for the <code>editValid</code> property + */ protected void setEditValid (boolean valid) { textField.editValid = valid; } + /** + * Parses <code>text</code> to return a corresponding Object. + * + * @param text the String to parse + * @return an Object that <code>text</code> represented + * @throws ParseException if there is an error in the conversion + */ public abstract Object stringToValue (String text) throws ParseException; + /** + * Returns a String to be displayed, based on the Object + * <code>value</code>. + * + * @param value the Object from which to generate a String + * @return a String to be displayed + * @throws ParseException if there is an error in the conversion + */ public abstract String valueToString (Object value) throws ParseException; } @@ -155,88 +288,177 @@ public class JFormattedTextField extends JTextField public abstract AbstractFormatter getFormatter (JFormattedTextField tf); } - static class FormatterFactoryWrapper extends AbstractFormatterFactory - { - AbstractFormatter formatter; - - public FormatterFactoryWrapper(AbstractFormatter formatter) - { - this.formatter = formatter; - } - - public AbstractFormatter getFormatter(JFormattedTextField tf) - { - return formatter; - } - } - + /** The possible focusLostBehavior options **/ public static final int COMMIT = 0; public static final int COMMIT_OR_REVERT = 1; public static final int REVERT = 2; public static final int PERSIST = 3; + /** The most recent valid and committed value **/ private Object value; + + /** The behaviour for when this text field loses focus **/ private int focusLostBehavior = COMMIT_OR_REVERT; + + /** The formatter factory currently being used **/ private AbstractFormatterFactory formatterFactory; + + /** The formatter currently being used **/ + private AbstractFormatter formatter; + // Package-private to avoid an accessor method. boolean editValid = true; + /** + * Creates a JFormattedTextField with no formatter factory. + * <code>setValue</code> or <code>setFormatterFactory</code> will + * properly configure this text field to edit a particular type + * of value. + */ public JFormattedTextField () { this((AbstractFormatterFactory) null, null); } + /** + * Creates a JFormattedTextField that can handle the specified Format. + * An appopriate AbstractFormatter and AbstractFormatterFactory will + * be created for the specified Format. + * + * @param format the Format that this JFormattedTextField should be able + * to handle + */ public JFormattedTextField (Format format) { - throw new InternalError ("not implemented"); + this (); + setFormatterFactory(getAppropriateFormatterFactory(format)); } + /** + * Creates a JFormattedTextField with the specified formatter. This will + * create a {@link DefaultFormatterFactory} with this formatter as the default + * formatter. + * + * @param formatter the formatter to use for this JFormattedTextField + */ public JFormattedTextField (AbstractFormatter formatter) { - this(new FormatterFactoryWrapper(formatter), null); + this(new DefaultFormatterFactory (formatter)); } + /** + * Creates a JFormattedTextField with the specified formatter factory. + * + * @param factory the formatter factory to use for this JFormattedTextField + */ public JFormattedTextField (AbstractFormatterFactory factory) { - this(factory, null); + setFormatterFactory(factory); } + /** + * Creates a JFormattedTextField with the specified formatter factory and + * initial value. + * + * @param factory the initial formatter factory for this JFormattedTextField + * @param value the initial value for the text field + */ public JFormattedTextField (AbstractFormatterFactory factory, Object value) - { - this.formatterFactory = factory; - this.value = value; + { + setFormatterFactory(factory); + setValue(value); } + /** + * Creates a JFormattedTextField with the specified value. This creates a + * formatter and formatterFactory that are appropriate for the value. + * + * @param value the initial value for this JFormattedTextField + */ public JFormattedTextField (Object value) { - this.value = value; + setValue(value); + } + + /** + * Returns an AbstractFormatterFactory that will give an appropriate + * AbstractFormatter for the given Format. + * @param format the Format to match with an AbstractFormatter. + * @return a DefaultFormatterFactory whose defaultFormatter is appropriate + * for the given Format. + */ + private AbstractFormatterFactory getAppropriateFormatterFactory (Format format) + { + AbstractFormatter newFormatter; + if (format instanceof DateFormat) + newFormatter = new DateFormatter((DateFormat)format); + else if (format instanceof NumberFormat) + newFormatter = new NumberFormatter ((NumberFormat)format); + else + newFormatter = new InternationalFormatter(format); + + return new DefaultFormatterFactory(newFormatter); } + /** + * Forces the current value from the editor to be set as the current + * value. If there is no current formatted this has no effect. + * + * @throws ParseException if the formatter cannot format the current value + */ public void commitEdit () throws ParseException { - throw new InternalError ("not implemented"); + if (formatter == null) + return; + // Note: this code is a lot like setValue except that we don't want + // to create a new formatter. + Object oldValue = this.value; + + this.value = formatter.stringToValue(getText());; + editValid = true; + + firePropertyChange("value", oldValue, this.value); } + /** + * Gets the command list supplied by the UI augmented by the specific + * Actions for JFormattedTextField. + * + * @return an array of Actions that this text field supports + */ public Action[] getActions () { // FIXME: Add JFormattedTextField specific actions + // These are related to committing or cancelling edits. return super.getActions(); } + /** + * Returns the behaviour of this JFormattedTextField upon losing focus. This + * is one of <code>COMMIT</code>, <code>COMMIT_OR_REVERT</code>, + * <code>PERSIST</code>, or <code>REVERT</code>. + * @return the behaviour upon losing focus + */ public int getFocusLostBehavior() { return focusLostBehavior; } + /** + * Returns the current formatter used for this JFormattedTextField. + * @return the current formatter used for this JFormattedTextField + */ public AbstractFormatter getFormatter () { - if (formatterFactory == null) - return null; - - return formatterFactory.getFormatter(this); + return formatter; } - + + /** + * Returns the factory currently used to generate formatters for this + * JFormattedTextField. + * @return the factory currently used to generate formatters + */ public AbstractFormatterFactory getFormatterFactory () { return formatterFactory; @@ -247,31 +469,61 @@ public class JFormattedTextField extends JTextField return "FormattedTextFieldUI"; } + /** + * Returns the last valid value. This may not be the value currently shown + * in the text field depending on whether or not the formatter commits on + * valid edits and allows invalid input to be temporarily displayed. + * @return the last committed valid value + */ public Object getValue () { return value; } + /** + * This method is used to provide feedback to the user when an invalid value + * is input during editing. + */ protected void invalidEdit () { UIManager.getLookAndFeel().provideErrorFeedback(this); } + /** + * Returns true if the current value being edited is valid. This property is + * managed by the current formatted. + * @return true if the value being edited is valid. + */ public boolean isEditValid () { return editValid; } + /** + * Processes focus events. This is overridden because we may want to + * change the formatted depending on whether or not this field has + * focus. + * + * @param evt the FocusEvent + */ protected void processFocusEvent (FocusEvent evt) { - // it's safe to simply call super for now, until it gets clear - // what this method is supposed to do - // throw new InternalError ("not implemented"); super.processFocusEvent(evt); + // Let the formatterFactory change the formatter for this text field + // based on whether or not it has focus. + setFormatter (formatterFactory.getFormatter(this)); } - + + /** + * Associates this JFormattedTextField with a Document and propagates + * a PropertyChange event to each listener. + * + * @param newDocument the Document to associate with this text field + */ public void setDocument(Document newDocument) { + // FIXME: This method should do more than this. Must do some handling + // of the DocumentListeners. Document oldDocument = getDocument(); if (oldDocument == newDocument) @@ -280,6 +532,16 @@ public class JFormattedTextField extends JTextField super.setDocument(newDocument); } + /** + * Sets the behaviour of this JFormattedTextField upon losing focus. + * This must be <code>COMMIT</code>, <code>COMMIT_OR_REVERT</code>, + * <code>PERSIST</code>, or <code>REVERT</code> or an + * IllegalArgumentException will be thrown. + * + * @param behavior + * @throws IllegalArgumentException if <code>behaviour</code> is not + * one of the above + */ public void setFocusLostBehavior(int behavior) { if (behavior != COMMIT @@ -291,20 +553,38 @@ public class JFormattedTextField extends JTextField this.focusLostBehavior = behavior; } + /** + * Sets the formatter for this JFormattedTextField. Normally the formatter + * factory will take care of this, or calls to setValue will also make sure + * that the formatter is set appropriately. + * + * @param formatter the AbstractFormatter to use for formatting the value for + * this JFormattedTextField + */ protected void setFormatter (AbstractFormatter formatter) { AbstractFormatter oldFormatter = null; - if (formatterFactory != null) - oldFormatter = formatterFactory.getFormatter(this); + oldFormatter = this.formatter; - if (oldFormatter == formatter) - return; + if (oldFormatter != null) + oldFormatter.uninstall(); + + this.formatter = formatter; + + if (formatter != null) + formatter.install(this); - setFormatterFactory(new FormatterFactoryWrapper(formatter)); firePropertyChange("formatter", oldFormatter, formatter); } + /** + * Sets the factory from which this JFormattedTextField should obtain + * its formatters. + * + * @param factory the AbstractFormatterFactory that will be used to generate + * formatters for this JFormattedTextField + */ public void setFormatterFactory (AbstractFormatterFactory factory) { if (formatterFactory == factory) @@ -313,55 +593,56 @@ public class JFormattedTextField extends JTextField AbstractFormatterFactory oldFactory = formatterFactory; formatterFactory = factory; firePropertyChange("formatterFactory", oldFactory, factory); + + // Now set the formatter according to our new factory. + if (formatterFactory != null) + setFormatter(formatterFactory.getFormatter(this)); + else + setFormatter(null); } + /** + * Sets the value that will be formatted and displayed. + * + * @param newValue the value to be formatted and displayed + */ public void setValue (Object newValue) { if (value == newValue) return; - // format value - AbstractFormatter formatter = createFormatter(newValue); - try - { - setText(formatter.valueToString(newValue)); - } - catch (ParseException ex) - { - // TODO: what should we do with this? - } - Object oldValue = value; value = newValue; + + // If there is no formatterFactory then make one. + if (formatterFactory == null) + setFormatterFactory(createFormatterFactory(newValue)); + + // Set the formatter appropriately. This is because there may be a new + // formatterFactory from the line above, or we may want a new formatter + // depending on the type of newValue (or if newValue is null). + setFormatter (formatterFactory.getFormatter(this)); firePropertyChange("value", oldValue, newValue); } /** - * A helper method that attempts to create a formatter that is suitable - * to format objects of the type like <code>value</code>. + * A helper method that attempts to create a formatter factory that is + * suitable to format objects of the type like <code>value</code>. * - * If <code>formatterFactory</code> is not null and the returned formatter - * is also not <code>null</code> then this formatter is used. Otherwise we - * try to create one based on the type of <code>value</code>. + * @param value an object which should be formatted by the formatter factory. * - * @param value an object which should be formatted by the formatter - * - * @return a formatter able to format objects of the class of + * @return a formatter factory able to format objects of the class of * <code>value</code> */ - AbstractFormatter createFormatter(Object value) + AbstractFormatterFactory createFormatterFactory(Object value) { AbstractFormatter formatter = null; - if (formatterFactory != null - && formatterFactory.getFormatter(this) != null) - formatter = formatterFactory.getFormatter(this); - else - { - if (value instanceof Date) - formatter = new DateFormatter(); - else - formatter = new DefaultFormatter(); - } - return formatter; + if (value instanceof Date) + formatter = new DateFormatter(); + else if (value instanceof Number) + formatter = new NumberFormatter(); + else + formatter = new DefaultFormatter(); + return new DefaultFormatterFactory(formatter); } } diff --git a/javax/swing/JInternalFrame.java b/javax/swing/JInternalFrame.java index 479294b13..948988c24 100644 --- a/javax/swing/JInternalFrame.java +++ b/javax/swing/JInternalFrame.java @@ -1628,7 +1628,6 @@ public class JInternalFrame extends JComponent implements Accessible, { if (! isVisible()) { - moveToFront(); super.show(); JDesktopPane pane = getDesktopPane(); diff --git a/javax/swing/JLayeredPane.java b/javax/swing/JLayeredPane.java index 31222778a..b087637b1 100644 --- a/javax/swing/JLayeredPane.java +++ b/javax/swing/JLayeredPane.java @@ -436,7 +436,12 @@ public class JLayeredPane extends JComponent implements Accessible // should have found it throw new IllegalArgumentException(); - super.swapComponents (curr, targ); + if (curr == 0) + super.swapComponents(curr, targ); + else + while (curr > 0) + super.swapComponents (curr, --curr); + revalidate(); repaint(); } diff --git a/javax/swing/JList.java b/javax/swing/JList.java index c089aae10..df2f3f668 100644 --- a/javax/swing/JList.java +++ b/javax/swing/JList.java @@ -1070,15 +1070,16 @@ public class JList extends JComponent implements Accessible, Scrollable layoutOrientation = VERTICAL; opaque = true; valueIsAdjusting = false; - visibleRowCount = 8; + visibleRowCount = 7; cellRenderer = new DefaultListCellRenderer(); listListener = new ListListener(); setModel(new DefaultListModel()); setSelectionModel(createSelectionModel()); - setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - + setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + setLayout(null); + updateUI(); } @@ -1256,14 +1257,17 @@ public class JList extends JComponent implements Accessible, Scrollable * * @param a A number in the half-open range <code>[0, x)</code> where * <code>x = getModel.getSize()</code>, indicating the index of an - * element in the list to select. + * element in the list to select. When < 0 the selection is cleared. * * @see #setSelectionMode * @see #selectionModel */ public void setSelectedIndex(int a) { - selectionModel.setSelectionInterval(a, a); + if (a < 0) + selectionModel.clearSelection(); + else + selectionModel.setSelectionInterval(a, a); } /** diff --git a/javax/swing/JMenuItem.java b/javax/swing/JMenuItem.java index c87a4dc2b..c3c10d6ef 100644 --- a/javax/swing/JMenuItem.java +++ b/javax/swing/JMenuItem.java @@ -117,6 +117,13 @@ public class JMenuItem extends AbstractButton implements Accessible, super(); super.setAction(action); init(null, null); + if (action != null) + { + setName((String) action.getValue(Action.NAME)); + setAccelerator((KeyStroke) action.getValue(Action.ACCELERATOR_KEY)); + setMnemonic(((Integer) action.getValue(Action.MNEMONIC_KEY)).intValue()); + setActionCommand((String) action.getValue(Action.ACTION_COMMAND_KEY)); + } } /** @@ -273,8 +280,9 @@ public class JMenuItem extends AbstractButton implements Accessible, if (! (this instanceof JMenu) && action != null) { setAccelerator((KeyStroke) (action.getValue(Action.ACCELERATOR_KEY))); - super.registerKeyboardAction(action, accelerator, - JComponent.WHEN_IN_FOCUSED_WINDOW); + if (accelerator != null) + super.registerKeyboardAction(action, accelerator, + JComponent.WHEN_IN_FOCUSED_WINDOW); } } diff --git a/javax/swing/JProgressBar.java b/javax/swing/JProgressBar.java index 0de9115dc..abca3e7ae 100644 --- a/javax/swing/JProgressBar.java +++ b/javax/swing/JProgressBar.java @@ -262,7 +262,8 @@ public class JProgressBar extends JComponent implements SwingConstants, { this.model = model; changeListener = createChangeListener(); - model.addChangeListener(changeListener); + if (model != null) + model.addChangeListener(changeListener); updateUI(); } diff --git a/javax/swing/JTable.java b/javax/swing/JTable.java index 14f00e654..fe456cee4 100644 --- a/javax/swing/JTable.java +++ b/javax/swing/JTable.java @@ -927,7 +927,47 @@ public class JTable // TODO Auto-generated method stub return null; } - + + /** + * Returns the accessible row at the specified index. + * + * @param index the index for which to query the row + * + * @return the row number at the specified table index + */ + public int getAccessibleRowAtIndex(int index) + { + // TODO: Back this up by a Mauve test and update API docs accordingly. + return index / getColumnCount(); + } + + /** + * Returns the accessible column at the specified index. + * + * @param index the index for which to query the column + * + * @return the column number at the specified table index + */ + public int getAccessibleColumnAtIndex(int index) + { + // TODO: Back this up by a Mauve test and update API docs accordingly. + return index % getColumnCount(); + } + + /** + * Returns the accessible child index at the specified column and row. + * + * @param row the row + * @param column the column + * + * @return the index of the accessible child at the specified row and + * column + */ + public int getAccessibleIndexAt(int row, int column) + { + // TODO: Back this up by a Mauve test and update API docs accordingly. + return row * getColumnCount() + column; + } } /** * Handles property changes from the <code>TableColumn</code>s of this @@ -1464,6 +1504,12 @@ public class JTable new TableColumnPropertyChangeHandler(); /** + * Whether cell editors should receive keyboard focus when the table is + * activated. + */ + private boolean surrendersFocusOnKeystroke = false; + + /** * Creates a new <code>JTable</code> instance. */ public JTable () @@ -3281,4 +3327,37 @@ public class JTable revalidate(); repaint(); } + + /** + * Sets whether cell editors of this table should receive keyboard focus + * when the editor is activated by a keystroke. The default setting is + * <code>false</code> which means that the table should keep the keyboard + * focus until the cell is selected by a mouse click. + * + * @param value the value to set + * + * @since 1.4 + */ + public void setSurrendersFocusOnKeystroke(boolean value) + { + // TODO: Implement functionality of this property (in UI impl). + surrendersFocusOnKeystroke = value; + } + + /** + * Returns whether cell editors of this table should receive keyboard focus + * when the editor is activated by a keystroke. The default setting is + * <code>false</code> which means that the table should keep the keyboard + * focus until the cell is selected by a mouse click. + * + * @return whether cell editors of this table should receive keyboard focus + * when the editor is activated by a keystroke + * + * @since 1.4 + */ + public boolean getSurrendersFocusOnKeystroke() + { + // TODO: Implement functionality of this property (in UI impl). + return surrendersFocusOnKeystroke; + } } diff --git a/javax/swing/JTextArea.java b/javax/swing/JTextArea.java index 2fa185b62..9b50febe3 100644 --- a/javax/swing/JTextArea.java +++ b/javax/swing/JTextArea.java @@ -217,7 +217,11 @@ public class JTextArea extends JTextComponent public JTextArea(Document doc, String text, int rows, int columns) { setDocument(doc == null ? createDefaultModel() : doc); - setText(text); + // Only explicitly setText() when there is actual text since + // setText() might be overridden and not expected to be called + // from the constructor (as in JEdit). + if (text != null) + setText(text); setRows(rows); setColumns(columns); } diff --git a/javax/swing/JTextPane.java b/javax/swing/JTextPane.java index a2aebd4ca..7c95d7682 100644 --- a/javax/swing/JTextPane.java +++ b/javax/swing/JTextPane.java @@ -40,6 +40,7 @@ package javax.swing; import java.awt.Component; +import javax.swing.text.AbstractDocument; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.Caret; @@ -151,38 +152,34 @@ public class JTextPane { Caret caret = getCaret(); StyledDocument doc = getStyledDocument(); + AttributeSet a = getInputAttributes().copyAttributes(); + if (doc == null) + return; int dot = caret.getDot(); int mark = caret.getMark(); - // If content is empty delete selection. - if (content == null) - { - caret.setDot(dot); - return; - } + int p0 = Math.min (dot, mark); + int p1 = Math.max (dot, mark); try { - int start = getSelectionStart(); - int end = getSelectionEnd(); - int contentLength = content.length(); - - // Remove selected text. - if (dot != mark) - doc.remove(start, end - start); - - // Insert new text. - doc.insertString(start, content, null); - // Set attributes for inserted text - doc.setCharacterAttributes(start, contentLength, getInputAttributes(), - true); - + if (doc instanceof AbstractDocument) + ((AbstractDocument)doc).replace(p0, p1 - p0, content, a); + else + { + // Remove selected text. + if (dot != mark) + doc.remove(p0, p1 - p0); + // Insert new text. + if (content != null && content.length() > 0) + doc.insertString(p0, content, a); + } } catch (BadLocationException e) { - throw new AssertionError - ("No BadLocationException should be thrown here"); + throw new AssertionError + ("No BadLocationException should be thrown here"); } } diff --git a/javax/swing/JTree.java b/javax/swing/JTree.java index bd71035dd..d566f5351 100644 --- a/javax/swing/JTree.java +++ b/javax/swing/JTree.java @@ -1481,6 +1481,7 @@ public class JTree extends JComponent implements Scrollable, Accessible setRootVisible(true); setModel(model); setSelectionModel(new EmptySelectionModel()); + selectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); } /** diff --git a/javax/swing/JViewport.java b/javax/swing/JViewport.java index 285a7bf59..3a1632730 100644 --- a/javax/swing/JViewport.java +++ b/javax/swing/JViewport.java @@ -424,8 +424,9 @@ public class JViewport extends JComponent implements Accessible public void setView(Component v) { - if (viewListener != null) - getView().removeComponentListener(viewListener); + Component currView = getView(); + if (viewListener != null && currView != null) + currView.removeComponentListener(viewListener); if (v != null) { @@ -786,6 +787,9 @@ public class JViewport extends JComponent implements Accessible */ void paintSimple(Graphics g) { + // We need to call this to properly clear the background. + paintComponent(g); + Point pos = getViewPosition(); Component view = getView(); boolean translated = false; diff --git a/javax/swing/LookAndFeel.java b/javax/swing/LookAndFeel.java index 74dff9dba..da57e561b 100644 --- a/javax/swing/LookAndFeel.java +++ b/javax/swing/LookAndFeel.java @@ -300,11 +300,11 @@ public abstract class LookAndFeel /** * Returns a string that displays and identifies this object's properties. * - * @return the string "LookAndFeel" + * @return string containing the description and class name. */ public String toString() { - return "LookAndFeel"; + return getDescription() + " " + getClass().getName(); } /** diff --git a/javax/swing/Popup.java b/javax/swing/Popup.java index cbb243e28..203ee3c9b 100644 --- a/javax/swing/Popup.java +++ b/javax/swing/Popup.java @@ -41,6 +41,7 @@ package javax.swing; import java.awt.Component; import java.awt.FlowLayout; import java.awt.Point; +import java.awt.Rectangle; /** @@ -291,7 +292,9 @@ public class Popup */ public void hide() { + Rectangle bounds = panel.getBounds(); layeredPane.remove(panel); + layeredPane.repaint(bounds.x, bounds.y, bounds.width, bounds.height); } } } diff --git a/javax/swing/RepaintManager.java b/javax/swing/RepaintManager.java index 022122b87..385fe7925 100644 --- a/javax/swing/RepaintManager.java +++ b/javax/swing/RepaintManager.java @@ -534,7 +534,14 @@ public class RepaintManager } for (Iterator i = workInvalidComponents.iterator(); i.hasNext(); ) { - JComponent comp = (JComponent) i.next(); + Component comp = (Component) i.next(); + // Find validate root. + while (!(comp instanceof JComponent) + || !((JComponent) comp).isValidateRoot() + && comp.getParent() != null) + comp = comp.getParent(); + + // Validate the validate root. if (! (comp.isVisible() && comp.isShowing())) continue; comp.validate(); diff --git a/javax/swing/SwingUtilities.java b/javax/swing/SwingUtilities.java index 2d737eeb5..f50f26b63 100644 --- a/javax/swing/SwingUtilities.java +++ b/javax/swing/SwingUtilities.java @@ -1395,4 +1395,30 @@ public class SwingUtilities else return null; } + + /** + * Processes key bindings for the component that is associated with the + * key event. Note that this method does not make sense for + * JComponent-derived components, except when + * {@link JComponent#processKeyEvent(KeyEvent)} is overridden and super is + * not called. + * + * This method searches through the component hierarchy of the component's + * top-level container to find a <code>JComponent</code> that has a binding + * for the key event in the WHEN_IN_FOCUSED_WINDOW scope. + * + * @param ev the key event + * + * @return <code>true</code> if a binding has been found and processed, + * <code>false</code> otherwise + * + * @since 1.4 + */ + public static boolean processKeyBindings(KeyEvent ev) + { + Component c = ev.getComponent(); + KeyStroke s = KeyStroke.getKeyStrokeForEvent(ev); + KeyboardManager km = KeyboardManager.getManager(); + return km.processKeyStroke(c, s, ev); + } } diff --git a/javax/swing/TransferHandler.java b/javax/swing/TransferHandler.java index 486411092..6115043ed 100644 --- a/javax/swing/TransferHandler.java +++ b/javax/swing/TransferHandler.java @@ -1,5 +1,5 @@ /* TransferHandler.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -43,6 +43,7 @@ import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.event.ActionEvent; import java.awt.event.InputEvent; +import java.awt.Toolkit; import java.io.Serializable; public class TransferHandler implements Serializable @@ -62,6 +63,13 @@ public class TransferHandler implements Serializable TransferHandler transferHandler = component.getTransferHandler(); Clipboard clipboard = getClipboard(component); + if (clipboard == null) + { + // Access denied! + Toolkit.getDefaultToolkit().beep(); + return; + } + if (command.equals(COMMAND_COPY)) transferHandler.exportToClipboard(component, clipboard, COPY); else if (command.equals(COMMAND_CUT)) @@ -76,8 +84,8 @@ public class TransferHandler implements Serializable } /** - * Get the system cliboard. If not available, create and return the VM-local - * clipboard. + * Get the system cliboard or null if the caller isn't allowed to + * access the system clipboard. * * @param component a component, used to get the toolkit. * @return the clipboard @@ -85,22 +93,13 @@ public class TransferHandler implements Serializable private static Clipboard getClipboard(JComponent component) { try - { - SecurityManager sm = System.getSecurityManager(); - if (sm != null) - sm.checkSystemClipboardAccess(); - - // We may access the system clipboard. - return component.getToolkit().getSystemClipboard(); - } - catch (Exception e) - { - // We may not access system clipboard. - // Create VM-local clipboard if none exists yet. - if (clipboard == null) - clipboard = new Clipboard("Clipboard"); - return clipboard; - } + { + return component.getToolkit().getSystemClipboard(); + } + catch (SecurityException se) + { + return null; + } } } @@ -119,12 +118,6 @@ public class TransferHandler implements Serializable private static Action cutAction = new TransferAction(COMMAND_CUT); private static Action pasteAction = new TransferAction(COMMAND_PASTE); - /** - * Clipboard if system clipboard may not be used. - * Package-private to avoid an accessor method. - */ - static Clipboard clipboard; - private int sourceActions; private Icon visualRepresentation; diff --git a/javax/swing/UIDefaults.java b/javax/swing/UIDefaults.java index 19283aed8..e627be785 100644 --- a/javax/swing/UIDefaults.java +++ b/javax/swing/UIDefaults.java @@ -54,6 +54,7 @@ import java.util.ResourceBundle; import javax.swing.border.Border; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.InputMapUIResource; /** * UIDefaults is a database where all settings and interface bindings are @@ -95,10 +96,14 @@ public class UIDefaults extends Hashtable<Object, Object> } public Object createValue(UIDefaults table) { - InputMap im = new InputMap (); + InputMapUIResource im = new InputMapUIResource (); for (int i = 0; 2*i+1 < bind.length; ++i) { - im.put (KeyStroke.getKeyStroke ((String) bind[2*i]), + Object curr = bind[2*i]; + if (curr instanceof KeyStroke) + im.put((KeyStroke) curr, bind[2*i+1]); + else + im.put(KeyStroke.getKeyStroke((String) curr), bind[2*i+1]); } return im; diff --git a/javax/swing/ViewportLayout.java b/javax/swing/ViewportLayout.java index 884f7cb27..54935a89f 100644 --- a/javax/swing/ViewportLayout.java +++ b/javax/swing/ViewportLayout.java @@ -143,32 +143,32 @@ public class ViewportLayout implements LayoutManager, Serializable Dimension viewMinimum = view.getMinimumSize(); Point portLowerRight = new Point(portBounds.x + portBounds.width, portBounds.y + portBounds.height); - + // vertical implementation of the above rules + if ((! (view instanceof Scrollable) + || ((Scrollable) view).getScrollableTracksViewportHeight()) + && viewPref.height < portBounds.height) + viewPref.height = portBounds.height; + if (portBounds.height >= viewMinimum.height) - { - portBounds.y = 0; - if ( !(view instanceof Scrollable) || ((Scrollable)view).getScrollableTracksViewportHeight()) - viewPref.height = portBounds.height; - } + portBounds.y = 0; else { - viewPref.height = viewMinimum.height; int overextension = portLowerRight.y - viewPref.height; if (overextension > 0) portBounds.y -= overextension; } // horizontal implementation of the above rules + if ((! (view instanceof Scrollable) + || ((Scrollable) view).getScrollableTracksViewportWidth()) + && viewPref.width < portBounds.width) + viewPref.width = portBounds.width; + if (portBounds.width >= viewMinimum.width) - { - portBounds.x = 0; - if ( !(view instanceof Scrollable) || ((Scrollable)view).getScrollableTracksViewportWidth()) - viewPref.width = portBounds.width; - } + portBounds.x = 0; else { - viewPref.width = viewMinimum.width; int overextension = portLowerRight.x - viewPref.width; if (overextension > 0) portBounds.x -= overextension; diff --git a/javax/swing/event/EventListenerList.java b/javax/swing/event/EventListenerList.java index 111e4bca4..332aeba95 100644 --- a/javax/swing/event/EventListenerList.java +++ b/javax/swing/event/EventListenerList.java @@ -228,7 +228,7 @@ public class EventListenerList count = getListenerCount(c); result = (EventListener[]) Array.newInstance(c, count); f = 0; - for (int i = 0; i < listenerList.length; i += 2) + for (int i = listenerList.length - 2; i >= 0; i -= 2) if (listenerList[i] == c) result[f++] = (EventListener) listenerList[i + 1]; diff --git a/javax/swing/plaf/basic/BasicComboBoxUI.java b/javax/swing/plaf/basic/BasicComboBoxUI.java index 78ceccb05..288a8d89f 100644 --- a/javax/swing/plaf/basic/BasicComboBoxUI.java +++ b/javax/swing/plaf/basic/BasicComboBoxUI.java @@ -294,8 +294,7 @@ public class BasicComboBoxUI extends ComboBoxUI comboBox.addPropertyChangeListener(propertyChangeListener); focusListener = createFocusListener(); - comboBox.addFocusListener(focusListener); - listBox.addFocusListener(focusListener); + editor.addFocusListener(focusListener); itemListener = createItemListener(); comboBox.addItemListener(itemListener); @@ -572,6 +571,7 @@ public class BasicComboBoxUI extends ComboBoxUI { arrowButton.setEnabled(comboBox.isEnabled()); arrowButton.setFont(comboBox.getFont()); + arrowButton.setFocusable(false); } /** @@ -624,12 +624,14 @@ public class BasicComboBoxUI extends ComboBoxUI public void setPopupVisible(JComboBox c, boolean v) { if (v) - { - popup.show(); - popup.getList().requestFocus(); - } + popup.show(); else popup.hide(); + + if (comboBox.isEditable()) + editor.requestFocus(); + else + comboBox.requestFocus(); } /** diff --git a/javax/swing/plaf/basic/BasicComboPopup.java b/javax/swing/plaf/basic/BasicComboPopup.java index 73979bb89..08dab7f9f 100644 --- a/javax/swing/plaf/basic/BasicComboPopup.java +++ b/javax/swing/plaf/basic/BasicComboPopup.java @@ -442,6 +442,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { list.setModel(comboBox.getModel()); list.setVisibleRowCount(comboBox.getMaximumRowCount()); + list.setFocusable(false); installListListeners(); } diff --git a/javax/swing/plaf/basic/BasicFileChooserUI.java b/javax/swing/plaf/basic/BasicFileChooserUI.java index 198529a99..105319f7d 100644 --- a/javax/swing/plaf/basic/BasicFileChooserUI.java +++ b/javax/swing/plaf/basic/BasicFileChooserUI.java @@ -37,8 +37,6 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import java.awt.Color; -import java.awt.Component; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.MouseAdapter; @@ -58,14 +56,10 @@ import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JFileChooser; -import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JTextField; -import javax.swing.ListCellRenderer; -import javax.swing.SwingConstants; import javax.swing.SwingUtilities; -import javax.swing.Timer; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.event.ListSelectionEvent; @@ -143,17 +137,22 @@ public class BasicFileChooserUI extends FileChooserUI */ public void actionPerformed(ActionEvent e) { - Object obj = new String(parentPath + getFileName()); + Object obj = null; + if (parentPath != null) + obj = new String(parentPath + getFileName()); + else + obj = filechooser.getSelectedFile(); if (obj != null) { - File f = filechooser.getFileSystemView().createFileObject( - obj.toString()); - if (filechooser.isTraversable(f) - && filechooser.isDirectorySelectionEnabled()) - filechooser.setCurrentDirectory(f); + File f = filechooser.getFileSystemView().createFileObject(obj.toString()); + File currSelected = filechooser.getSelectedFile(); + if (filechooser.isTraversable(f)) + { + filechooser.setCurrentDirectory(currSelected); + filechooser.rescanCurrentDirectory(); + } else { - filechooser.setSelectedFile(f); filechooser.approveSelection(); closeDialog(); } @@ -306,6 +305,8 @@ public class BasicFileChooserUI extends FileChooserUI */ public void actionPerformed(ActionEvent e) { + filechooser.setSelectedFile(null); + filechooser.setSelectedFiles(null); filechooser.cancelSelection(); closeDialog(); } @@ -373,11 +374,12 @@ public class BasicFileChooserUI extends FileChooserUI */ public void mouseClicked(MouseEvent e) { - if (list.getSelectedValue() == null) + Object p = list.getSelectedValue(); + if (p == null) return; FileSystemView fsv = filechooser.getFileSystemView(); - if (e.getClickCount() >= 2 && - list.getSelectedValue().toString().equals(lastSelected.toString())) + if (e.getClickCount() >= 2 && lastSelected != null && + p.toString().equals(lastSelected.toString())) { File f = fsv.createFileObject(lastSelected.toString()); if (filechooser.isTraversable(f)) @@ -394,8 +396,19 @@ public class BasicFileChooserUI extends FileChooserUI } else { - String path = list.getSelectedValue().toString(); + String path = p.toString(); File f = fsv.createFileObject(path); + filechooser.setSelectedFile(f); + + if (filechooser.isMultiSelectionEnabled()) + { + int[] inds = list.getSelectedIndices(); + File[] allFiles = new File[inds.length]; + for (int i = 0; i < inds.length; i++) + allFiles[i] = (File) list.getModel().getElementAt(inds[i]); + filechooser.setSelectedFiles(allFiles); + } + if (filechooser.isTraversable(f)) { setDirectorySelected(true); @@ -408,7 +421,11 @@ public class BasicFileChooserUI extends FileChooserUI } lastSelected = path; parentPath = path.substring(0, path.lastIndexOf("/") + 1); - setFileName(path.substring(path.lastIndexOf("/") + 1)); + if (f.isFile()) + setFileName(path.substring(path.lastIndexOf("/") + 1)); + else if (filechooser.getFileSelectionMode() == + JFileChooser.DIRECTORIES_ONLY) + setFileName(path); } } @@ -652,7 +669,7 @@ public class BasicFileChooserUI extends FileChooserUI JButton accept; /** An optional accessory panel. */ - JPanel accessoryPanel; + JPanel accessoryPanel = new JPanel(); /** A property change listener. */ PropertyChangeListener propertyChangeListener; @@ -722,46 +739,6 @@ public class BasicFileChooserUI extends FileChooserUI private UpdateAction updateAction; // -- end private -- - private class ListLabelRenderer extends JLabel implements ListCellRenderer - { - /** DOCUMENT ME! */ - final Color selected = new Color(153, 204, 255); - - /** - * Creates a new ListLabelRenderer object. - */ - public ListLabelRenderer() - { - super(); - setOpaque(true); - } - - /** - * DOCUMENT ME! - * - * @param list DOCUMENT ME! - * @param value DOCUMENT ME! - * @param index DOCUMENT ME! - * @param isSelected DOCUMENT ME! - * @param cellHasFocus DOCUMENT ME! - * - * @return DOCUMENT ME! - */ - public Component getListCellRendererComponent(JList list, Object value, - int index, - boolean isSelected, - boolean cellHasFocus) - { - setHorizontalAlignment(SwingConstants.LEFT); - File file = (File) value; - setText(filechooser.getName(file)); - setIcon(filechooser.getIcon(file)); - setBackground(isSelected ? selected : Color.WHITE); - setForeground(Color.BLACK); - - return this; - } - } /** * Closes the dialog. @@ -972,28 +949,28 @@ public class BasicFileChooserUI extends FileChooserUI acceptAllFileFilterText = defaults.getString("FileChooser.acceptAllFileFilterText"); cancelButtonText = "Cancel"; cancelButtonToolTipText = "Abort file chooser dialog"; - cancelButtonMnemonic = defaults.getInt("FileChooser.cancelButtonMnemonic"); + cancelButtonMnemonic = new Integer((String) UIManager.get("FileChooser.cancelButtonMnemonic")).intValue(); directoryOpenButtonText = "Open"; directoryOpenButtonToolTipText = "Open selected directory"; directoryOpenButtonMnemonic - = defaults.getInt("FileChooser.directoryOpenButtonMnemonic"); + = new Integer((String) UIManager.get("FileChooser.directoryOpenButtonMnemonic")).intValue(); helpButtonText = "Help"; helpButtonToolTipText = "FileChooser help"; - helpButtonMnemonic = defaults.getInt("FileChooser.helpButtonMnemonic"); + helpButtonMnemonic = new Integer((String) UIManager.get("FileChooser.helpButtonMnemonic")).intValue(); openButtonText = "Open"; openButtonToolTipText = "Open selected file"; - openButtonMnemonic = defaults.getInt("FileChooser.openButtonMnemonic"); + openButtonMnemonic = new Integer((String) UIManager.get("FileChooser.openButtonMnemonic")).intValue(); saveButtonText = "Save"; saveButtonToolTipText = "Save selected file"; - saveButtonMnemonic = UIManager.getInt("FileChooser.saveButtonMnemonic"); + saveButtonMnemonic = new Integer((String) UIManager.get("FileChooser.saveButtonMnemonic")).intValue(); updateButtonText = "Update"; updateButtonToolTipText = "Update directory listing"; - updateButtonMnemonic = defaults.getInt("FileChooser.updateButtonMnemonic"); + updateButtonMnemonic = new Integer((String) UIManager.get("FileChooser.updateButtonMnemonic")).intValue(); } /** diff --git a/javax/swing/plaf/basic/BasicHTML.java b/javax/swing/plaf/basic/BasicHTML.java new file mode 100644 index 000000000..b9891e144 --- /dev/null +++ b/javax/swing/plaf/basic/BasicHTML.java @@ -0,0 +1,154 @@ +/* BasicHTML.java -- Provides HTML support to ComponentUI implementations + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.plaf.basic; + +import java.io.IOException; +import java.io.StringReader; + +import javax.swing.JComponent; +import javax.swing.text.BadLocationException; +import javax.swing.text.Element; +import javax.swing.text.View; +import javax.swing.text.ViewFactory; +import javax.swing.text.html.HTMLDocument; +import javax.swing.text.html.HTMLEditorKit; + +/** + * Provides support for HTML rendering to {@link javax.swing.plaf.ComponentUI} + * implementations. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class BasicHTML +{ + + /** + * The key that is used to store a HTML view in a JComponent's client + * properties. + */ + public static final String propertyKey = "html"; + + /** + * The key that is used to store the document base in a JComponent's client + * properties. The document base is used to resolve relative references + * in HTML. + */ + public static final String documentBaseKey = "html.base"; + + /** + * Creates a new instance of BasicHTML. This should not be necessary since + * all methods in this class are static. + */ + public BasicHTML() + { + // Nothing to do here. + } + + /** + * Creates a {@link View} instance that can be used by the component + * <code>c</code> to render the HTML string <code>html</code>. + * + * @param c the component that needs to render the HTML string + * @param html the HTML string to be rendered + * + * @return a view that can render the HTML string + */ + public static View createHTMLView(JComponent c, String html) + { + // TODO: This might be wrong. Lets see if it turns out good when + // the javax.swing.text.html package is in a good shape. + HTMLDocument doc = new HTMLDocument(); + HTMLEditorKit kit = new HTMLEditorKit(); + StringReader reader = new StringReader(html); + try + { + kit.read(reader, doc, 0); + } + catch (IOException ex) + { + AssertionError err = new AssertionError("unexpected IOException"); + err.initCause(ex); + throw err; + } + catch (BadLocationException ex) + { + AssertionError err = + new AssertionError("unexpected BadLocationException"); + err.initCause(ex); + throw err; + } + ViewFactory vf = kit.getViewFactory(); + Element root = doc.getDefaultRootElement(); + View view = vf.create(root); + return view; + } + + /** + * Returns <code>true</code> if <code>s</code> is HTML, <code>false</code> + * otherwise. + * + * @param s the string to test + * + * @return <code>true</code> if <code>s</code> is HTML, <code>false</code> + * otherwise + */ + public static boolean isHTMLString(String s) + { + // We consider a string to be HTML if it contains both the '<' and '>' + // character at least once. + return s.contains("<") && s.contains(">"); + } + + /** + * Stores a HTML renderer in <code>c</code>'s client property if + * <code>text</code> is HTML, otherwise it clears the corresponding client + * property. This is useful for {@link java.swing.plaf.ComponentUI} + * implementations that are shared between it's components. + * + * @param c the component to update the renderer for + * @param text the string to be rendered + */ + public static void updateRenderer(JComponent c, String text) + { + if (isHTMLString(text)) + c.putClientProperty(propertyKey, createHTMLView(c, text)); + else + c.putClientProperty(propertyKey, null); + } +} diff --git a/javax/swing/plaf/basic/BasicListUI.java b/javax/swing/plaf/basic/BasicListUI.java index 7a6319274..593e7780f 100644 --- a/javax/swing/plaf/basic/BasicListUI.java +++ b/javax/swing/plaf/basic/BasicListUI.java @@ -41,13 +41,11 @@ package javax.swing.plaf.basic; import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; +import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; -import java.awt.event.ComponentListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.MouseEvent; @@ -61,7 +59,6 @@ import javax.swing.DefaultListSelectionModel; import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JList; -import javax.swing.JViewport; import javax.swing.KeyStroke; import javax.swing.ListCellRenderer; import javax.swing.ListModel; @@ -87,21 +84,6 @@ public class BasicListUI extends ListUI { /** - * A helper class which listens for {@link ComponentEvent}s from - * the JList. - */ - private class ComponentHandler extends ComponentAdapter { - - /** - * Called when the component is hidden. Invalidates the internal - * layout. - */ - public void componentResized(ComponentEvent ev) { - BasicListUI.this.damageLayout(); - } - } - - /** * A helper class which listens for {@link FocusEvent}s * from the JList. */ @@ -153,7 +135,7 @@ public class BasicListUI extends ListUI */ public void contentsChanged(ListDataEvent e) { - BasicListUI.this.damageLayout(); + list.revalidate(); } /** @@ -163,7 +145,7 @@ public class BasicListUI extends ListUI */ public void intervalAdded(ListDataEvent e) { - BasicListUI.this.damageLayout(); + list.revalidate(); } /** @@ -173,7 +155,7 @@ public class BasicListUI extends ListUI */ public void intervalRemoved(ListDataEvent e) { - BasicListUI.this.damageLayout(); + list.revalidate(); } } @@ -569,20 +551,19 @@ public class BasicListUI extends ListUI } // Update the updateLayoutStateNeeded flag. if (e.getPropertyName().equals("model")) - updateLayoutStateNeeded += modelChanged; + updateLayoutStateNeeded |= modelChanged; else if (e.getPropertyName().equals("selectionModel")) - updateLayoutStateNeeded += selectionModelChanged; + updateLayoutStateNeeded |= selectionModelChanged; else if (e.getPropertyName().equals("font")) - updateLayoutStateNeeded += fontChanged; + updateLayoutStateNeeded |= fontChanged; else if (e.getPropertyName().equals("fixedCellWidth")) - updateLayoutStateNeeded += fixedCellWidthChanged; + updateLayoutStateNeeded |= fixedCellWidthChanged; else if (e.getPropertyName().equals("fixedCellHeight")) - updateLayoutStateNeeded += fixedCellHeightChanged; + updateLayoutStateNeeded |= fixedCellHeightChanged; else if (e.getPropertyName().equals("prototypeCellValue")) - updateLayoutStateNeeded += prototypeCellValueChanged; + updateLayoutStateNeeded |= prototypeCellValueChanged; else if (e.getPropertyName().equals("cellRenderer")) - updateLayoutStateNeeded += cellRendererChanged; - BasicListUI.this.damageLayout(); + updateLayoutStateNeeded |= cellRendererChanged; } } @@ -648,11 +629,6 @@ public class BasicListUI extends ListUI /** The property change listener listening to the list. */ protected PropertyChangeListener propertyChangeListener; - - /** The component listener that receives notification for resizing the - * JList component.*/ - private ComponentListener componentListener; - /** Saved reference to the list this UI was created for. */ protected JList list; @@ -745,13 +721,12 @@ public class BasicListUI extends ListUI int maxIndex = Math.max(index1, index2); Point loc = indexToLocation(list, minIndex); Rectangle bounds = new Rectangle(loc.x, loc.y, cellWidth, - getRowHeight(minIndex)); - + getCellHeight(minIndex)); for (int i = minIndex + 1; i <= maxIndex; i++) { Point hiLoc = indexToLocation(list, i); Rectangle hibounds = new Rectangle(hiLoc.x, hiLoc.y, cellWidth, - getRowHeight(i)); + getCellHeight(i)); bounds = bounds.union(hibounds); } @@ -759,6 +734,29 @@ public class BasicListUI extends ListUI } /** + * Calculates the maximum cell height. + * + * @param index the index of the cell + * + * @return the maximum cell height + */ + private int getCellHeight(int index) + { + int height = cellHeight; + if (height <= 0) + { + if (list.getLayoutOrientation() == JList.VERTICAL) + height = getRowHeight(index); + else + { + for (int j = 0; j < cellHeights.length; j++) + height = Math.max(height, cellHeights[j]); + } + } + return height; + } + + /** * Calculate the Y coordinate of the upper edge of a particular row, * considering the Y coordinate <code>0</code> to occur at the top of the * list. @@ -811,7 +809,7 @@ public class BasicListUI extends ListUI // Update the layout if necessary. maybeUpdateLayoutState(); - int index = list.getModel().getSize() - 1;; + int index = list.getModel().getSize() - 1; // If a fixed cell height is set, then we can work more efficient. if (cellHeight > 0) @@ -891,18 +889,6 @@ public class BasicListUI extends ListUI } /** - * Marks the current layout as damaged and requests revalidation from the - * JList. - * This is package-private to avoid an accessor method. - * - * @see #updateLayoutStateNeeded - */ - void damageLayout() - { - updateLayoutStateNeeded = 1; - } - - /** * Calls {@link #updateLayoutState} if {@link #updateLayoutStateNeeded} * is nonzero, then resets {@link #updateLayoutStateNeeded} to zero. */ @@ -975,12 +961,6 @@ public class BasicListUI extends ListUI if (propertyChangeListener == null) propertyChangeListener = createPropertyChangeListener(); list.addPropertyChangeListener(propertyChangeListener); - - // FIXME: Are these two really needed? At least they are not documented. - //keyListener = new KeyHandler(); - componentListener = new ComponentHandler(); - list.addComponentListener(componentListener); - //list.addKeyListener(keyListener); } /** @@ -992,7 +972,6 @@ public class BasicListUI extends ListUI list.getModel().removeListDataListener(listDataListener); list.removeListSelectionListener(listSelectionListener); list.removeMouseListener(mouseInputListener); - //list.removeKeyListener(keyListener); list.removeMouseMotionListener(mouseInputListener); list.removePropertyChangeListener(propertyChangeListener); } @@ -1080,33 +1059,62 @@ public class BasicListUI extends ListUI */ public Dimension getPreferredSize(JComponent c) { + maybeUpdateLayoutState(); int size = list.getModel().getSize(); - if (size == 0) - return new Dimension(0, 0); int visibleRows = list.getVisibleRowCount(); int layoutOrientation = list.getLayoutOrientation(); - Rectangle bounds = getCellBounds(list, 0, list.getModel().getSize() - 1); - Dimension retVal = bounds.getSize(); - Component parent = list.getParent(); - if ((visibleRows == -1) && (parent instanceof JViewport)) - { - JViewport viewport = (JViewport) parent; - if (layoutOrientation == JList.HORIZONTAL_WRAP) + int h; + int w; + int maxCellHeight = cellHeight; + if (maxCellHeight <= 0) + { + for (int i = 0; i < cellHeights.length; i++) + maxCellHeight = Math.max(maxCellHeight, cellHeights[i]); + } + if (layoutOrientation == JList.HORIZONTAL_WRAP) + { + if (visibleRows > 0) { - int h = viewport.getSize().height; - int cellsPerCol = h / cellHeight; - int w = size / cellsPerCol * cellWidth; - retVal = new Dimension(w, h); + // We cast to double here to force double divisions. + double modelSize = size; + int neededColumns = (int) Math.ceil(modelSize / visibleRows); + int adjustedRows = (int) Math.ceil(modelSize / neededColumns); + h = maxCellHeight * adjustedRows; + w = cellWidth * neededColumns; } - else if (layoutOrientation == JList.VERTICAL_WRAP) + else { - int w = viewport.getSize().width; - int cellsPerRow = Math.max(w / cellWidth, 1); - int h = size / cellsPerRow * cellHeight; - retVal = new Dimension(w, h); + int neededColumns = Math.min(1, list.getWidth() / cellWidth); + h = size / neededColumns * maxCellHeight; + w = neededColumns * cellWidth; } } + else if (layoutOrientation == JList.VERTICAL_WRAP) + { + if (visibleRows > 0) + h = visibleRows * maxCellHeight; + else + h = Math.max(list.getHeight(), maxCellHeight); + int neededColumns = h / maxCellHeight; + w = cellWidth * neededColumns; + } + else + { + if (list.getFixedCellWidth() > 0) + w = list.getFixedCellWidth(); + else + w = cellWidth; + if (list.getFixedCellHeight() > 0) + // FIXME: We need to add some cellVerticalMargins here, according + // to the specs. + h = list.getFixedCellHeight() * size; + else + h = maxCellHeight * size; + } + Insets insets = list.getInsets(); + Dimension retVal = new Dimension(w + insets.left + insets.right, + h + insets.top + insets.bottom); return retVal; } @@ -1155,9 +1163,9 @@ public class BasicListUI extends ListUI int lead = sel.getLeadSelectionIndex(); Rectangle clip = g.getClipBounds(); - int startIndex = list.locationToIndex(new Point(clip.x, clip.y)); - int endIndex = list.locationToIndex(new Point(clip.x + clip.width, - clip.y + clip.height)); + int startIndex = locationToIndex(list, new Point(clip.x, clip.y)); + int endIndex = locationToIndex(list, new Point(clip.x + clip.width, + clip.y + clip.height)); for (int row = startIndex; row <= endIndex; ++row) { @@ -1172,13 +1180,13 @@ public class BasicListUI extends ListUI * location lies outside the bounds of the list, the greatest index in the * list model is returned. * - * @param list the list which on which the computation is based on + * @param l the list which on which the computation is based on * @param location the coordinates * * @return the index of the list item that is located at the given * coordinates or <code>-1</code> if the list model is empty */ - public int locationToIndex(JList list, Point location) + public int locationToIndex(JList l, Point location) { int layoutOrientation = list.getLayoutOrientation(); int index = -1; @@ -1189,52 +1197,34 @@ public class BasicListUI extends ListUI break; case JList.HORIZONTAL_WRAP: // determine visible rows and cells per row - int visibleRows = list.getVisibleRowCount(); + int maxCellHeight = getCellHeight(0); + int visibleRows = list.getHeight() / maxCellHeight; int cellsPerRow = -1; int numberOfItems = list.getModel().getSize(); - Dimension listDim = list.getSize(); - if (visibleRows <= 0) - { - try - { - cellsPerRow = listDim.width / cellWidth; - } - catch (ArithmeticException ex) - { - cellsPerRow = 1; - } - } - else - { - cellsPerRow = numberOfItems / visibleRows + 1; - } + cellsPerRow = numberOfItems / visibleRows + 1; // determine index for the given location int cellsPerColumn = numberOfItems / cellsPerRow + 1; int gridX = Math.min(location.x / cellWidth, cellsPerRow - 1); - int gridY = Math.min(location.y / cellHeight, cellsPerColumn); + int gridY = Math.min(location.y / maxCellHeight, cellsPerColumn); index = gridX + gridY * cellsPerRow; break; case JList.VERTICAL_WRAP: // determine visible rows and cells per column - int visibleRows2 = list.getVisibleRowCount(); - if (visibleRows2 <= 0) - { - Dimension listDim2 = list.getSize(); - visibleRows2 = listDim2.height / cellHeight; - } + int maxCellHeight2 = getCellHeight(0); + int visibleRows2 = list.getHeight() / maxCellHeight2; int numberOfItems2 = list.getModel().getSize(); int cellsPerRow2 = numberOfItems2 / visibleRows2 + 1; int gridX2 = Math.min(location.x / cellWidth, cellsPerRow2 - 1); - int gridY2 = Math.min(location.y / cellHeight, visibleRows2); + int gridY2 = Math.min(location.y / maxCellHeight2, visibleRows2); index = gridY2 + gridX2 * visibleRows2; break; } return index; } - public Point indexToLocation(JList list, int index) + public Point indexToLocation(JList l, int index) { int layoutOrientation = list.getLayoutOrientation(); Point loc = null; @@ -1245,40 +1235,31 @@ public class BasicListUI extends ListUI break; case JList.HORIZONTAL_WRAP: // determine visible rows and cells per row - int visibleRows = list.getVisibleRowCount(); + int maxCellHeight = getCellHeight(0); + int visibleRows = list.getHeight() / maxCellHeight; int numberOfCellsPerRow = -1; - if (visibleRows <= 0) - { - Dimension listDim = list.getSize(); - numberOfCellsPerRow = Math.max(listDim.width / cellWidth, 1); - } - else - { - int numberOfItems = list.getModel().getSize(); - numberOfCellsPerRow = numberOfItems / visibleRows + 1; - } + int numberOfItems = list.getModel().getSize(); + numberOfCellsPerRow = numberOfItems / visibleRows + 1; + // compute coordinates inside the grid int gridX = index % numberOfCellsPerRow; int gridY = index / numberOfCellsPerRow; int locX = gridX * cellWidth; - int locY = gridY * cellHeight; + int locY; + locY = gridY * maxCellHeight; loc = new Point(locX, locY); break; case JList.VERTICAL_WRAP: // determine visible rows and cells per column - int visibleRows2 = list.getVisibleRowCount(); - if (visibleRows2 <= 0) - { - Dimension listDim2 = list.getSize(); - visibleRows2 = listDim2.height / cellHeight; - } + int maxCellHeight2 = getCellHeight(0); + int visibleRows2 = list.getHeight() / maxCellHeight2; // compute coordinates inside the grid if (visibleRows2 > 0) { int gridY2 = index % visibleRows2; int gridX2 = index / visibleRows2; int locX2 = gridX2 * cellWidth; - int locY2 = gridY2 * cellHeight; + int locY2 = gridY2 * maxCellHeight2; loc = new Point(locX2, locY2); } else diff --git a/javax/swing/plaf/basic/BasicLookAndFeel.java b/javax/swing/plaf/basic/BasicLookAndFeel.java index 13c78add6..2cbca5d30 100644 --- a/javax/swing/plaf/basic/BasicLookAndFeel.java +++ b/javax/swing/plaf/basic/BasicLookAndFeel.java @@ -41,16 +41,26 @@ package javax.swing.plaf.basic; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; -import java.awt.event.InputEvent; -import java.awt.event.KeyEvent; +import java.awt.event.ActionEvent; +import java.io.IOException; +import java.io.InputStream; import java.io.Serializable; import java.util.Enumeration; import java.util.ResourceBundle; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.Clip; +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.UnsupportedAudioFileException; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ActionMap; import javax.swing.BorderFactory; import javax.swing.KeyStroke; import javax.swing.LookAndFeel; import javax.swing.UIDefaults; +import javax.swing.UIManager; import javax.swing.border.BevelBorder; import javax.swing.border.Border; import javax.swing.plaf.BorderUIResource; @@ -59,7 +69,6 @@ import javax.swing.plaf.DimensionUIResource; import javax.swing.plaf.FontUIResource; import javax.swing.plaf.IconUIResource; import javax.swing.plaf.InsetsUIResource; -import javax.swing.text.JTextComponent; /** * BasicLookAndFeel @@ -68,8 +77,68 @@ import javax.swing.text.JTextComponent; public abstract class BasicLookAndFeel extends LookAndFeel implements Serializable { + /** + * An action that can play an audio file. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class AudioAction extends AbstractAction + { + /** + * The UIDefaults key that specifies the sound. + */ + Object key; + + /** + * Creates a new AudioAction. + * + * @param key the key that describes the audio action, normally a filename + * of an audio file relative to the current package + */ + AudioAction(Object key) + { + this.key = key; + } + + /** + * Plays the sound represented by this action. + * + * @param event the action event that triggers this audio action + */ + public void actionPerformed(ActionEvent event) + { + // We only can handle strings for now. + if (key instanceof String) + { + String name = UIManager.getString(key); + InputStream stream = getClass().getResourceAsStream(name); + try + { + Clip clip = AudioSystem.getClip(); + AudioInputStream audioStream = + AudioSystem.getAudioInputStream(stream); + clip.open(audioStream); + } + catch (LineUnavailableException ex) + { + // Nothing we can do about it. + } + catch (IOException ex) + { + // Nothing we can do about it. + } + catch (UnsupportedAudioFileException e) + { + // Nothing we can do about it. + } + } + } + } + static final long serialVersionUID = -6096995660290287879L; + private ActionMap audioActionMap; + /** * Creates a new instance of the Basic look and feel. */ @@ -148,7 +217,6 @@ public abstract class BasicLookAndFeel extends LookAndFeel "TextPaneUI", "javax.swing.plaf.basic.BasicTextPaneUI", "TextAreaUI", "javax.swing.plaf.basic.BasicTextAreaUI", "TextFieldUI", "javax.swing.plaf.basic.BasicTextFieldUI", - "TextPaneUI", "javax.swing.plaf.basic.BasicTextPaneUI", "ToggleButtonUI", "javax.swing.plaf.basic.BasicToggleButtonUI", "ToolBarSeparatorUI", "javax.swing.plaf.basic.BasicToolBarSeparatorUI", "ToolBarUI", "javax.swing.plaf.basic.BasicToolBarUI", @@ -265,12 +333,12 @@ public abstract class BasicLookAndFeel extends LookAndFeel } }, "Button.darkShadow", new ColorUIResource(Color.BLACK), - "Button.focusInputMap", new UIDefaults.LazyInputMap(new Object[] { - "SPACE", "pressed", - "released SPACE", "released" - }), "Button.font", new FontUIResource("Dialog", Font.PLAIN, 12), "Button.foreground", new ColorUIResource(Color.BLACK), + "Button.focusInputMap", new UIDefaults.LazyInputMap(new Object[] { + KeyStroke.getKeyStroke("SPACE"), "pressed", + KeyStroke.getKeyStroke("released SPACE"), "released" + }), "Button.highlight", new ColorUIResource(Color.WHITE), "Button.light", new ColorUIResource(Color.LIGHT_GRAY), "Button.margin", new InsetsUIResource(2, 14, 2, 14), @@ -281,8 +349,8 @@ public abstract class BasicLookAndFeel extends LookAndFeel "CheckBox.border", new BorderUIResource.CompoundBorderUIResource(null, null), "CheckBox.focusInputMap", new UIDefaults.LazyInputMap(new Object[] { - "SPACE", "pressed", - "released SPACE", "released" + KeyStroke.getKeyStroke("SPACE"), "pressed", + KeyStroke.getKeyStroke("released SPACE"), "released" }), "CheckBox.font", new FontUIResource("Dialog", Font.PLAIN, 12), "CheckBox.foreground", new ColorUIResource(darkShadow), @@ -342,12 +410,12 @@ public abstract class BasicLookAndFeel extends LookAndFeel "ColorChooser.okText", "OK", "ColorChooser.previewText", "Preview", "ColorChooser.resetText", "Reset", - "ColorChooser.rgbBlueMnemonic", new Integer(66), + "ColorChooser.rgbBlueMnemonic", "66", "ColorChooser.rgbBlueText", "Blue", - "ColorChooser.rgbGreenMnemonic", new Integer(71), + "ColorChooser.rgbGreenMnemonic", "78", "ColorChooser.rgbGreenText", "Green", "ColorChooser.rgbNameText", "RGB", - "ColorChooser.rgbRedMnemonic", new Integer(82), + "ColorChooser.rgbRedMnemonic", "68", "ColorChooser.rgbRedText", "Red", "ColorChooser.sampleText", "Sample Text Sample Text", "ColorChooser.swatchesDefaultRecentColor", new ColorUIResource(light), @@ -403,20 +471,63 @@ public abstract class BasicLookAndFeel extends LookAndFeel "EditorPane.font", new FontUIResource("Serif", Font.PLAIN, 12), "EditorPane.foreground", new ColorUIResource(Color.black), "EditorPane.inactiveForeground", new ColorUIResource(Color.gray), - "EditorPane.keyBindings", new JTextComponent.KeyBinding[] { - new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_UP, - 0), "caret-up"), - new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, - 0), "caret-down"), - new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, - 0), "page-up"), - new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, - 0), "page-down"), - new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, - 0), "insert-break"), - new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, - 0), "insert-tab") - }, + "EditorPane.focusInputMap", new UIDefaults.LazyInputMap(new Object[] { + KeyStroke.getKeyStroke("shift UP"), "selection-up", + KeyStroke.getKeyStroke("ctrl RIGHT"), "caret-next-word", + KeyStroke.getKeyStroke("shift ctrl LEFT"), "selection-previous-word", + KeyStroke.getKeyStroke("shift KP_UP"), "selection-up", + KeyStroke.getKeyStroke("DOWN"), "caret-down", + KeyStroke.getKeyStroke("shift ctrl T"), "previous-link-action", + KeyStroke.getKeyStroke("ctrl LEFT"), "caret-previous-word", + KeyStroke.getKeyStroke("CUT"), "cut-to-clipboard", + KeyStroke.getKeyStroke("END"), "caret-end-line", + KeyStroke.getKeyStroke("shift PAGE_UP"), "selection-page-up", + KeyStroke.getKeyStroke("KP_UP"), "caret-up", + KeyStroke.getKeyStroke("DELETE"), "delete-next", + KeyStroke.getKeyStroke("ctrl HOME"), "caret-begin", + KeyStroke.getKeyStroke("shift LEFT"), "selection-backward", + KeyStroke.getKeyStroke("ctrl END"), "caret-end", + KeyStroke.getKeyStroke("BACK_SPACE"), "delete-previous", + KeyStroke.getKeyStroke("shift ctrl RIGHT"), "selection-next-word", + KeyStroke.getKeyStroke("LEFT"), "caret-backward", + KeyStroke.getKeyStroke("KP_LEFT"), "caret-backward", + KeyStroke.getKeyStroke("shift KP_RIGHT"), "selection-forward", + KeyStroke.getKeyStroke("ctrl SPACE"), "activate-link-action", + KeyStroke.getKeyStroke("ctrl H"), "delete-previous", + KeyStroke.getKeyStroke("ctrl BACK_SLASH"), "unselect", + KeyStroke.getKeyStroke("ENTER"), "insert-break", + KeyStroke.getKeyStroke("shift HOME"), "selection-begin-line", + KeyStroke.getKeyStroke("RIGHT"), "caret-forward", + KeyStroke.getKeyStroke("shift ctrl PAGE_UP"), "selection-page-left", + KeyStroke.getKeyStroke("shift DOWN"), "selection-down", + KeyStroke.getKeyStroke("PAGE_DOWN"), "page-down", + KeyStroke.getKeyStroke("shift KP_LEFT"), "selection-backward", + KeyStroke.getKeyStroke("shift ctrl O"), "toggle-componentOrientation", + KeyStroke.getKeyStroke("ctrl X"), "cut-to-clipboard", + KeyStroke.getKeyStroke("shift ctrl PAGE_DOWN"), "selection-page-right", + KeyStroke.getKeyStroke("ctrl C"), "copy-to-clipboard", + KeyStroke.getKeyStroke("ctrl KP_RIGHT"), "caret-next-word", + KeyStroke.getKeyStroke("shift END"), "selection-end-line", + KeyStroke.getKeyStroke("ctrl KP_LEFT"), "caret-previous-word", + KeyStroke.getKeyStroke("HOME"), "caret-begin-line", + KeyStroke.getKeyStroke("ctrl V"), "paste-from-clipboard", + KeyStroke.getKeyStroke("KP_DOWN"), "caret-down", + KeyStroke.getKeyStroke("ctrl A"), "select-all", + KeyStroke.getKeyStroke("shift RIGHT"), "selection-forward", + KeyStroke.getKeyStroke("shift ctrl END"), "selection-end", + KeyStroke.getKeyStroke("COPY"), "copy-to-clipboard", + KeyStroke.getKeyStroke("shift ctrl KP_LEFT"), "selection-previous-word", + KeyStroke.getKeyStroke("ctrl T"), "next-link-action", + KeyStroke.getKeyStroke("shift KP_DOWN"), "selection-down", + KeyStroke.getKeyStroke("TAB"), "insert-tab", + KeyStroke.getKeyStroke("UP"), "caret-up", + KeyStroke.getKeyStroke("shift ctrl HOME"), "selection-begin", + KeyStroke.getKeyStroke("shift PAGE_DOWN"), "selection-page-down", + KeyStroke.getKeyStroke("KP_RIGHT"), "caret-forward", + KeyStroke.getKeyStroke("shift ctrl KP_RIGHT"), "selection-next-word", + KeyStroke.getKeyStroke("PAGE_UP"), "page-up", + KeyStroke.getKeyStroke("PASTE"), "paste-from-clipboard" + }), "EditorPane.margin", new InsetsUIResource(3, 3, 3, 3), "EditorPane.selectionBackground", new ColorUIResource(Color.black), "EditorPane.selectionForeground", new ColorUIResource(Color.white), @@ -424,51 +535,74 @@ public abstract class BasicLookAndFeel extends LookAndFeel "FileChooser.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] { "ESCAPE", "cancelSelection" }), - "FileChooser.cancelButtonMnemonic", new Integer(67), + "FileChooser.cancelButtonMnemonic", "67", "FileChooser.cancelButtonText", "Cancel", "FileChooser.cancelButtonToolTipText", "Abort file chooser dialog", - // XXX Don't use gif -// "FileChooser.detailsViewIcon", new IconUIResource(new ImageIcon("icons/DetailsView.gif")), "FileChooser.directoryDescriptionText", "Directory", "FileChooser.fileDescriptionText", "Generic File", - "FileChooser.helpButtonMnemonic", new Integer(72), + "FileChooser.directoryOpenButtonMnemonic", "79", + "FileChooser.helpButtonMnemonic", "72", "FileChooser.helpButtonText", "Help", "FileChooser.helpButtonToolTipText", "FileChooser help", - // XXX Don't use gif -// "FileChooser.homeFolderIcon", new IconUIResource(new ImageIcon("icons/HomeFolder.gif")), - // XXX Don't use gif -// "FileChooser.listViewIcon", new IconUIResource(new ImageIcon("icons/ListView.gif")), "FileChooser.newFolderErrorSeparator", ":", "FileChooser.newFolderErrorText", "Error creating new folder", - // XXX Don't use gif -// "FileChooser.newFolderIcon", new IconUIResource(new ImageIcon("icons/NewFolder.gif")), - "FileChooser.openButtonMnemonic", new Integer(79), + "FileChooser.openButtonMnemonic", "79", "FileChooser.openButtonText", "Open", "FileChooser.openButtonToolTipText", "Open selected file", - "FileChooser.saveButtonMnemonic", new Integer(83), + "FileChooser.saveButtonMnemonic", "83", "FileChooser.saveButtonText", "Save", "FileChooser.saveButtonToolTipText", "Save selected file", - // XXX Don't use gif -// "FileChooser.upFolderIcon", new IconUIResource(new ImageIcon("icons/UpFolder.gif")), - "FileChooser.updateButtonMnemonic", new Integer(85), + "FileChooser.updateButtonMnemonic", "85", "FileChooser.updateButtonText", "Update", "FileChooser.updateButtonToolTipText", "Update directory listing", - // XXX Don't use gif -// "FileView.computerIcon", new IconUIResource(new ImageIcon("icons/Computer.gif")), - // XXX Don't use gif -// "FileView.directoryIcon", new IconUIResource(new ImageIcon("icons/Directory.gif")), - // XXX Don't use gif -// "FileView.fileIcon", new IconUIResource(new ImageIcon("icons/File.gif")), - // XXX Don't use gif -// "FileView.floppyDriveIcon", new IconUIResource(new ImageIcon("icons/Floppy.gif")), - // XXX Don't use gif -// "FileView.hardDriveIcon", new IconUIResource(new ImageIcon("icons/HardDrive.gif")), "FocusManagerClassName", "TODO", "FormattedTextField.background", new ColorUIResource(light), "FormattedTextField.caretForeground", new ColorUIResource(Color.black), + "FormattedTextField.margin", new InsetsUIResource(0, 0, 0, 0), + "FormattedTextField.caretBlinkRate", new Integer(500), "FormattedTextField.font", new FontUIResource("SansSerif", Font.PLAIN, 12), "FormattedTextField.foreground", new ColorUIResource(Color.black), + "FormattedTextField.focusInputMap", new UIDefaults.LazyInputMap(new Object[] { + KeyStroke.getKeyStroke("KP_UP"), "increment", + KeyStroke.getKeyStroke("END"), "caret-end-line", + KeyStroke.getKeyStroke("shift ctrl O"), "toggle-componentOrientation", + KeyStroke.getKeyStroke("shift KP_LEFT"), "selection-backward", + KeyStroke.getKeyStroke("shift RIGHT"), "selection-forward", + KeyStroke.getKeyStroke("KP_DOWN"), "decrement", + KeyStroke.getKeyStroke("HOME"), "caret-begin-line", + KeyStroke.getKeyStroke("ctrl V"), "paste-from-clipboard", + KeyStroke.getKeyStroke("ctrl H"), "delete-previous", + KeyStroke.getKeyStroke("KP_LEFT"), "caret-backward", + KeyStroke.getKeyStroke("LEFT"), "caret-backward", + KeyStroke.getKeyStroke("ctrl X"), "cut-to-clipboard", + KeyStroke.getKeyStroke("KP_RIGHT"), "caret-forward", + KeyStroke.getKeyStroke("UP"), "increment", + KeyStroke.getKeyStroke("shift ctrl KP_RIGHT"), "selection-next-word", + KeyStroke.getKeyStroke("COPY"), "copy-to-clipboard", + KeyStroke.getKeyStroke("shift HOME"), "selection-begin-line", + KeyStroke.getKeyStroke("ESCAPE"), "reset-field-edit", + KeyStroke.getKeyStroke("RIGHT"), "caret-forward", + KeyStroke.getKeyStroke("shift ctrl LEFT"), "selection-previous-word", + KeyStroke.getKeyStroke("ctrl KP_LEFT"), "caret-previous-word", + KeyStroke.getKeyStroke("DOWN"), "decrement", + KeyStroke.getKeyStroke("ctrl KP_RIGHT"), "caret-next-word", + KeyStroke.getKeyStroke("PASTE"), "paste-from-clipboard", + KeyStroke.getKeyStroke("shift ctrl RIGHT"), "selection-next-word", + KeyStroke.getKeyStroke("ctrl BACK_SLASH"), "unselect", + KeyStroke.getKeyStroke("ctrl A"), "select-all", + KeyStroke.getKeyStroke("shift KP_RIGHT"), "selection-forward", + KeyStroke.getKeyStroke("CUT"), "cut-to-clipboard", + KeyStroke.getKeyStroke("ctrl LEFT"), "caret-previous-word", + KeyStroke.getKeyStroke("BACK_SPACE"), "delete-previous", + KeyStroke.getKeyStroke("shift ctrl KP_LEFT"), "selection-previous-word", + KeyStroke.getKeyStroke("ctrl C"), "copy-to-clipboard", + KeyStroke.getKeyStroke("shift END"), "selection-end-line", + KeyStroke.getKeyStroke("ctrl RIGHT"), "caret-next-word", + KeyStroke.getKeyStroke("DELETE"), "delete-next", + KeyStroke.getKeyStroke("ENTER"), "notify-field-accept", + KeyStroke.getKeyStroke("shift LEFT"), "selection-backward" + }), "FormattedTextField.inactiveBackground", new ColorUIResource(light), "FormattedTextField.inactiveForeground", new ColorUIResource(Color.gray), "FormattedTextField.selectionBackground", @@ -504,7 +638,6 @@ public abstract class BasicLookAndFeel extends LookAndFeel "InternalFrame.borderLight", new ColorUIResource(Color.LIGHT_GRAY), "InternalFrame.borderShadow", new ColorUIResource(Color.GRAY), "InternalFrame.closeIcon", BasicIconFactory.createEmptyFrameIcon(), - // FIXME: Set a nice icon for InternalFrames here. "InternalFrame.icon", new UIDefaults.LazyValue() { @@ -533,67 +666,67 @@ public abstract class BasicLookAndFeel extends LookAndFeel "List.background", new ColorUIResource(Color.white), "List.border", new BasicBorders.MarginBorder(), "List.focusInputMap", new UIDefaults.LazyInputMap(new Object[] { - "ctrl DOWN", "selectNextRowChangeLead", - "shift UP", "selectPreviousRowExtendSelection", - "ctrl RIGHT", "selectNextColumnChangeLead", - "shift ctrl LEFT", "selectPreviousColumnExtendSelection", - "shift KP_UP", "selectPreviousRowChangeLead", - "DOWN", "selectNextRow", - "ctrl UP", "selectPreviousRowChangeLead", - "ctrl LEFT", "selectPreviousColumnChangeLead", - "CUT", "cut", - "END", "selectLastRow", - "shift PAGE_UP","scrollUpExtendSelection", - "KP_UP", "selectPreviousRow", - "shift ctrl UP", "selectPreviousRowExtendSelection", - "ctrl HOME", "selectFirstRowChangeLead", - "shift LEFT", "selectPreviousColumnExtendSelection", - "ctrl END", "selectLastRowChangeLead", - "ctrl PAGE_DOWN", "scrollDownChangeLead", - "shift ctrl RIGHT", "selectNextColumnExtendSelection", - "LEFT", "selectPreviousColumn", - "ctrl PAGE_UP", "scrollUpChangeLead", - "KP_LEFT", "selectPreviousColumn", - "shift KP_RIGHT", "selectNextColumnExtendSelection", - "SPACE", "addToSelection", - "ctrl SPACE", "toggleAndAnchor", - "shift SPACE", "extendTo", - "shift ctrl SPACE", "moveSelectionTo", - "shift ctrl DOWN", "selectNextRowExtendSelection", - "ctrl BACK_SLASH", "clearSelection", - "shift HOME", "selectFirstRowExtendSelection", - "RIGHT", "selectNextColumn", - "shift ctrl PAGE_UP", "scrollUpExtendSelection", - "shift DOWN", "selectNextRowExtendSelection", - "PAGE_DOWN", "scrollDown", - "shift ctrl KP_UP", "selectPreviousRowExtendSelection", - "shift KP_LEFT", "selectPreviousColumnExtendSelection", - "ctrl X", "cut", - "shift ctrl PAGE_DOWN", "scrollDownExtendSelection", - "ctrl SLASH", "selectAll", - "ctrl C", "copy", - "ctrl KP_RIGHT", "selectNextColumnChangeLead", - "shift END", "selectLastRowExtendSelection", - "shift ctrl KP_DOWN", "selectNextRowExtendSelection", - "ctrl KP_LEFT", "selectPreviousColumnChangeLead", - "HOME", "selectFirstRow", - "ctrl V", "paste", - "KP_DOWN", "selectNextRow", - "ctrl KP_DOWN", "selectNextRowChangeLead", - "shift RIGHT", "selectNextColumnExtendSelection", - "ctrl A", "selectAll", - "shift ctrl END", "selectLastRowExtendSelection", - "COPY", "copy", - "ctrl KP_UP", "selectPreviousRowChangeLead", - "shift ctrl KP_LEFT", "selectPreviousColumnExtendSelection", - "shift KP_DOWN", "selectNextRowExtendSelection", - "UP", "selectPreviousRow", - "shift ctrl HOME", "selectFirstRowExtendSelection", - "shift PAGE_DOWN", "scrollDownExtendSelection", - "KP_RIGHT", "selectNextColumn", - "shift ctrl KP_RIGHT", "selectNextColumnExtendSelection", - "PAGE_UP", "scrollUp", - "PASTE", "paste" + KeyStroke.getKeyStroke("ctrl DOWN"), "selectNextRowChangeLead", + KeyStroke.getKeyStroke("shift UP"), "selectPreviousRowExtendSelection", + KeyStroke.getKeyStroke("ctrl RIGHT"), "selectNextColumnChangeLead", + KeyStroke.getKeyStroke("shift ctrl LEFT"), "selectPreviousColumnExtendSelection", + KeyStroke.getKeyStroke("shift KP_UP"), "selectPreviousRowExtendSelection", + KeyStroke.getKeyStroke("DOWN"), "selectNextRow", + KeyStroke.getKeyStroke("ctrl UP"), "selectPreviousRowChangeLead", + KeyStroke.getKeyStroke("ctrl LEFT"), "selectPreviousColumnChangeLead", + KeyStroke.getKeyStroke("CUT"), "cut", + KeyStroke.getKeyStroke("END"), "selectLastRow", + KeyStroke.getKeyStroke("shift PAGE_UP"), "scrollUpExtendSelection", + KeyStroke.getKeyStroke("KP_UP"), "selectPreviousRow", + KeyStroke.getKeyStroke("shift ctrl UP"), "selectPreviousRowExtendSelection", + KeyStroke.getKeyStroke("ctrl HOME"), "selectFirstRowChangeLead", + KeyStroke.getKeyStroke("shift LEFT"), "selectPreviousColumnExtendSelection", + KeyStroke.getKeyStroke("ctrl END"), "selectLastRowChangeLead", + KeyStroke.getKeyStroke("ctrl PAGE_DOWN"), "scrollDownChangeLead", + KeyStroke.getKeyStroke("shift ctrl RIGHT"), "selectNextColumnExtendSelection", + KeyStroke.getKeyStroke("LEFT"), "selectPreviousColumn", + KeyStroke.getKeyStroke("ctrl PAGE_UP"), "scrollUpChangeLead", + KeyStroke.getKeyStroke("KP_LEFT"), "selectPreviousColumn", + KeyStroke.getKeyStroke("shift KP_RIGHT"), "selectNextColumnExtendSelection", + KeyStroke.getKeyStroke("SPACE"), "addToSelection", + KeyStroke.getKeyStroke("ctrl SPACE"), "toggleAndAnchor", + KeyStroke.getKeyStroke("shift SPACE"), "extendTo", + KeyStroke.getKeyStroke("shift ctrl SPACE"), "moveSelectionTo", + KeyStroke.getKeyStroke("shift ctrl DOWN"), "selectNextRowExtendSelection", + KeyStroke.getKeyStroke("ctrl BACK_SLASH"), "clearSelection", + KeyStroke.getKeyStroke("shift HOME"), "selectFirstRowExtendSelection", + KeyStroke.getKeyStroke("RIGHT"), "selectNextColumn", + KeyStroke.getKeyStroke("shift ctrl PAGE_UP"), "scrollUpExtendSelection", + KeyStroke.getKeyStroke("shift DOWN"), "selectNextRowExtendSelection", + KeyStroke.getKeyStroke("PAGE_DOWN"), "scrollDown", + KeyStroke.getKeyStroke("shift ctrl KP_UP"), "selectPreviousRowExtendSelection", + KeyStroke.getKeyStroke("shift KP_LEFT"), "selectPreviousColumnExtendSelection", + KeyStroke.getKeyStroke("ctrl X"), "cut", + KeyStroke.getKeyStroke("shift ctrl PAGE_DOWN"), "scrollDownExtendSelection", + KeyStroke.getKeyStroke("ctrl SLASH"), "selectAll", + KeyStroke.getKeyStroke("ctrl C"), "copy", + KeyStroke.getKeyStroke("ctrl KP_RIGHT"), "selectNextColumnChangeLead", + KeyStroke.getKeyStroke("shift END"), "selectLastRowExtendSelection", + KeyStroke.getKeyStroke("shift ctrl KP_DOWN"), "selectNextRowExtendSelection", + KeyStroke.getKeyStroke("ctrl KP_LEFT"), "selectPreviousColumnChangeLead", + KeyStroke.getKeyStroke("HOME"), "selectFirstRow", + KeyStroke.getKeyStroke("ctrl V"), "paste", + KeyStroke.getKeyStroke("KP_DOWN"), "selectNextRow", + KeyStroke.getKeyStroke("ctrl KP_DOWN"), "selectNextRowChangeLead", + KeyStroke.getKeyStroke("shift RIGHT"), "selectNextColumnExtendSelection", + KeyStroke.getKeyStroke("ctrl A"), "selectAll", + KeyStroke.getKeyStroke("shift ctrl END"), "selectLastRowExtendSelection", + KeyStroke.getKeyStroke("COPY"), "copy", + KeyStroke.getKeyStroke("ctrl KP_UP"), "selectPreviousRowChangeLead", + KeyStroke.getKeyStroke("shift ctrl KP_LEFT"), "selectPreviousColumnExtendSelection", + KeyStroke.getKeyStroke("shift KP_DOWN"), "selectNextRowExtendSelection", + KeyStroke.getKeyStroke("UP"), "selectPreviousRow", + KeyStroke.getKeyStroke("shift ctrl HOME"), "selectFirstRowExtendSelection", + KeyStroke.getKeyStroke("shift PAGE_DOWN"), "scrollDownExtendSelection", + KeyStroke.getKeyStroke("KP_RIGHT"), "selectNextColumn", + KeyStroke.getKeyStroke("shift ctrl KP_RIGHT"), "selectNextColumnExtendSelection", + KeyStroke.getKeyStroke("PAGE_UP"), "scrollUp", + KeyStroke.getKeyStroke("PASTE"), "paste" }), "List.font", new FontUIResource("Dialog", Font.PLAIN, 12), "List.foreground", new ColorUIResource(Color.black), @@ -603,6 +736,7 @@ public abstract class BasicLookAndFeel extends LookAndFeel new BorderUIResource. LineBorderUIResource(new ColorUIResource(Color.yellow)), "Menu.acceleratorFont", new FontUIResource("Dialog", Font.PLAIN, 12), + "Menu.crossMenuMnemonic", Boolean.TRUE, "Menu.acceleratorForeground", new ColorUIResource(darkShadow), "Menu.acceleratorSelectionForeground", new ColorUIResource(Color.white), "Menu.arrowIcon", BasicIconFactory.getMenuArrowIcon(), @@ -627,6 +761,10 @@ public abstract class BasicLookAndFeel extends LookAndFeel "ENTER", "return", "SPACE", "return" }, + "Menu.menuPopupOffsetX", new Integer(0), + "Menu.menuPopupOffsetY", new Integer(0), + "Menu.submenuPopupOffsetX", new Integer(0), + "Menu.submenuPopupOffsetY", new Integer(0), "Menu.selectionBackground", new ColorUIResource(Color.black), "Menu.selectionForeground", new ColorUIResource(Color.white), "MenuBar.background", new ColorUIResource(light), @@ -638,7 +776,7 @@ public abstract class BasicLookAndFeel extends LookAndFeel "MenuBar.windowBindings", new Object[] { "F10", "takeFocus" }, - "MenuItem.acceleratorDelimiter", "-", + "MenuItem.acceleratorDelimiter", "+", "MenuItem.acceleratorFont", new FontUIResource("Dialog", Font.PLAIN, 12), "MenuItem.acceleratorForeground", new ColorUIResource(darkShadow), "MenuItem.acceleratorSelectionForeground", @@ -657,15 +795,10 @@ public abstract class BasicLookAndFeel extends LookAndFeel new BorderUIResource.EmptyBorderUIResource(0, 0, 0, 0), "OptionPane.buttonAreaBorder", new BorderUIResource.EmptyBorderUIResource(0, 0, 0, 0), + "OptionPane.buttonClickThreshhold", new Integer(500), "OptionPane.cancelButtonText", "Cancel", - // XXX Don't use gif -// "OptionPane.errorIcon", -// new IconUIResource(new ImageIcon("icons/Error.gif")), "OptionPane.font", new FontUIResource("Dialog", Font.PLAIN, 12), "OptionPane.foreground", new ColorUIResource(darkShadow), - // XXX Don't use gif -// "OptionPane.informationIcon", -// new IconUIResource(new ImageIcon("icons/Inform.gif")), "OptionPane.messageAreaBorder", new BorderUIResource.EmptyBorderUIResource(0, 0, 0, 0), "OptionPane.messageForeground", new ColorUIResource(darkShadow), @@ -674,12 +807,6 @@ public abstract class BasicLookAndFeel extends LookAndFeel BasicOptionPaneUI.MinimumHeight), "OptionPane.noButtonText", "No", "OptionPane.okButtonText", "OK", - // XXX Don't use gif -// "OptionPane.questionIcon", -// new IconUIResource(new ImageIcon("icons/Question.gif")), - // XXX Don't use gif -// "OptionPane.warningIcon", -// new IconUIResource(new ImageIcon("icons/Warn.gif")), "OptionPane.windowBindings", new Object[] { "ESCAPE", "close" }, @@ -692,14 +819,45 @@ public abstract class BasicLookAndFeel extends LookAndFeel null, null), "PasswordField.caretBlinkRate", new Integer(500), "PasswordField.caretForeground", new ColorUIResource(Color.black), - "PasswordField.font", new FontUIResource("Dialog", Font.PLAIN, 12), + "PasswordField.font", new FontUIResource("MonoSpaced", Font.PLAIN, 12), "PasswordField.foreground", new ColorUIResource(Color.black), "PasswordField.inactiveBackground", new ColorUIResource(light), "PasswordField.inactiveForeground", new ColorUIResource(Color.gray), - "PasswordField.keyBindings", new JTextComponent.KeyBinding[] { - new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, - 0), - "notify-field-accept")}, + "PasswordField.focusInputMap", new UIDefaults.LazyInputMap(new Object[] { + KeyStroke.getKeyStroke("END"), "caret-end-line", + KeyStroke.getKeyStroke("shift ctrl O"), "toggle-componentOrientation", + KeyStroke.getKeyStroke("shift KP_LEFT"), "selection-backward", + KeyStroke.getKeyStroke("shift RIGHT"), "selection-forward", + KeyStroke.getKeyStroke("HOME"), "caret-begin-line", + KeyStroke.getKeyStroke("ctrl V"), "paste-from-clipboard", + KeyStroke.getKeyStroke("ctrl H"), "delete-previous", + KeyStroke.getKeyStroke("KP_LEFT"), "caret-backward", + KeyStroke.getKeyStroke("LEFT"), "caret-backward", + KeyStroke.getKeyStroke("ctrl X"), "cut-to-clipboard", + KeyStroke.getKeyStroke("KP_RIGHT"), "caret-forward", + KeyStroke.getKeyStroke("shift ctrl KP_RIGHT"), "selection-end-line", + KeyStroke.getKeyStroke("COPY"), "copy-to-clipboard", + KeyStroke.getKeyStroke("shift HOME"), "selection-begin-line", + KeyStroke.getKeyStroke("RIGHT"), "caret-forward", + KeyStroke.getKeyStroke("shift ctrl LEFT"), "selection-begin-line", + KeyStroke.getKeyStroke("ctrl KP_LEFT"), "caret-begin-line", + KeyStroke.getKeyStroke("ctrl KP_RIGHT"), "caret-end-line", + KeyStroke.getKeyStroke("PASTE"), "paste-from-clipboard", + KeyStroke.getKeyStroke("shift ctrl RIGHT"), "selection-end-line", + KeyStroke.getKeyStroke("ctrl BACK_SLASH"), "unselect", + KeyStroke.getKeyStroke("ctrl A"), "select-all", + KeyStroke.getKeyStroke("shift KP_RIGHT"), "selection-forward", + KeyStroke.getKeyStroke("CUT"), "cut-to-clipboard", + KeyStroke.getKeyStroke("ctrl LEFT"), "caret-begin-line", + KeyStroke.getKeyStroke("BACK_SPACE"), "delete-previous", + KeyStroke.getKeyStroke("shift ctrl KP_LEFT"), "selection-begin-line", + KeyStroke.getKeyStroke("ctrl C"), "copy-to-clipboard", + KeyStroke.getKeyStroke("shift END"), "selection-end-line", + KeyStroke.getKeyStroke("ctrl RIGHT"), "caret-end-line", + KeyStroke.getKeyStroke("DELETE"), "delete-next", + KeyStroke.getKeyStroke("ENTER"), "notify-field-accept", + KeyStroke.getKeyStroke("shift LEFT"), "selection-backward" + }), "PasswordField.margin", new InsetsUIResource(0, 0, 0, 0), "PasswordField.selectionBackground", new ColorUIResource(Color.black), "PasswordField.selectionForeground", new ColorUIResource(Color.white), @@ -723,8 +881,8 @@ public abstract class BasicLookAndFeel extends LookAndFeel null), "RadioButton.darkShadow", new ColorUIResource(shadow), "RadioButton.focusInputMap", new UIDefaults.LazyInputMap(new Object[] { - "SPACE", "pressed", - "released SPACE", "released" + KeyStroke.getKeyStroke("SPACE"), "pressed", + KeyStroke.getKeyStroke("released SPACE"), "released" }), "RadioButton.font", new FontUIResource("Dialog", Font.PLAIN, 12), "RadioButton.foreground", new ColorUIResource(darkShadow), @@ -818,18 +976,20 @@ public abstract class BasicLookAndFeel extends LookAndFeel "Slider.background", new ColorUIResource(light), "Slider.focus", new ColorUIResource(shadow), "Slider.focusInputMap", new UIDefaults.LazyInputMap(new Object[] { - "PAGE_UP", "positiveBlockIncrement", - "PAGE_DOWN", "negativeBlockIncrement", - "END", "maxScroll", - "HOME", "minScroll", - "LEFT", "negativeUnitIncrement", - "KP_UP", "positiveUnitIncrement", - "KP_DOWN", "negativeUnitIncrement", - "UP", "positiveUnitIncrement", - "RIGHT", "positiveUnitIncrement", - "KP_LEFT", "negativeUnitIncrement", - "DOWN", "negativeUnitIncrement", - "KP_RIGHT", "positiveUnitIncrement" + "ctrl PAGE_DOWN", "negativeBlockIncrement", + "PAGE_DOWN", "negativeBlockIncrement", + "PAGE_UP", "positiveBlockIncrement", + "ctrl PAGE_UP", "positiveBlockIncrement", + "KP_RIGHT", "positiveUnitIncrement", + "DOWN", "negativeUnitIncrement", + "KP_LEFT", "negativeUnitIncrement", + "RIGHT", "positiveUnitIncrement", + "KP_DOWN", "negativeUnitIncrement", + "UP", "positiveUnitIncrement", + "KP_UP", "positiveUnitIncrement", + "LEFT", "negativeUnitIncrement", + "HOME", "minScroll", + "END", "maxScroll" }), "Slider.focusInsets", new InsetsUIResource(2, 2, 2, 2), "Slider.foreground", new ColorUIResource(light), @@ -840,6 +1000,9 @@ public abstract class BasicLookAndFeel extends LookAndFeel "Slider.tickHeight", new Integer(12), "Spinner.background", new ColorUIResource(light), "Spinner.foreground", new ColorUIResource(light), + "Spinner.arrowButtonSize", new DimensionUIResource(16, 5), + "Spinner.editorBorderPainted", Boolean.FALSE, + "Spinner.font", new FontUIResource("MonoSpaced", Font.PLAIN, 12), "SplitPane.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] { "F6", "toggleFocus", "F8", "startResize", @@ -857,7 +1020,7 @@ public abstract class BasicLookAndFeel extends LookAndFeel "SplitPane.background", new ColorUIResource(light), "SplitPane.border", new BasicBorders.SplitPaneBorder(null, null), "SplitPane.darkShadow", new ColorUIResource(shadow), - "SplitPane.dividerSize", new Integer(10), + "SplitPane.dividerSize", new Integer(7), "SplitPane.highlight", new ColorUIResource(highLight), "SplitPane.shadow", new ColorUIResource(shadow), "TabbedPane.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] { @@ -871,16 +1034,16 @@ public abstract class BasicLookAndFeel extends LookAndFeel "TabbedPane.darkShadow", new ColorUIResource(shadow), "TabbedPane.focus", new ColorUIResource(darkShadow), "TabbedPane.focusInputMap", new UIDefaults.LazyInputMap(new Object[] { - "LEFT", "navigateLeft", - "KP_UP", "navigateUp", - "ctrl DOWN", "requestFocusForVisibleComponent", - "UP", "navigateUp", - "KP_DOWN", "navigateDown", - "RIGHT", "navigateRight", - "KP_LEFT", "navigateLeft", - "ctrl KP_DOWN", "requestFocusForVisibleComponent", - "KP_RIGHT", "navigateRight", - "DOWN", "navigateDown" + KeyStroke.getKeyStroke("ctrl DOWN"), "requestFocusForVisibleComponent", + KeyStroke.getKeyStroke("KP_UP"), "navigateUp", + KeyStroke.getKeyStroke("LEFT"), "navigateLeft", + KeyStroke.getKeyStroke("ctrl KP_DOWN"), "requestFocusForVisibleComponent", + KeyStroke.getKeyStroke("UP"), "navigateUp", + KeyStroke.getKeyStroke("KP_DOWN"), "navigateDown", + KeyStroke.getKeyStroke("KP_LEFT"), "navigateLeft", + KeyStroke.getKeyStroke("RIGHT"), "navigateRight", + KeyStroke.getKeyStroke("KP_RIGHT"), "navigateRight", + KeyStroke.getKeyStroke("DOWN"), "navigateDown" }), "TabbedPane.font", new FontUIResource("Dialog", Font.PLAIN, 12), "TabbedPane.foreground", new ColorUIResource(darkShadow), @@ -888,10 +1051,10 @@ public abstract class BasicLookAndFeel extends LookAndFeel "TabbedPane.light", new ColorUIResource(highLight), "TabbedPane.selectedTabPadInsets", new InsetsUIResource(2, 2, 2, 1), "TabbedPane.shadow", new ColorUIResource(shadow), - "TabbedPane.tabbedPaneTabAreaInsets", new InsetsUIResource(3, 2, 1, 2), - "TabbedPane.tabbedPaneTabInsets", new InsetsUIResource(1, 4, 1, 4), "TabbedPane.tabbedPaneContentBorderInsets", new InsetsUIResource(3, 2, 1, 2), "TabbedPane.tabbedPaneTabPadInsets", new InsetsUIResource(1, 1, 1, 1), + "TabbedPane.tabAreaInsets", new InsetsUIResource(3, 2, 0, 2), + "TabbedPane.tabInsets", new InsetsUIResource(0, 4, 1, 4), "TabbedPane.tabRunOverlay", new Integer(2), "TabbedPane.textIconGap", new Integer(4), "Table.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] { @@ -976,32 +1139,73 @@ public abstract class BasicLookAndFeel extends LookAndFeel "Table.selectionBackground", new ColorUIResource(new ColorUIResource(0, 0, 128)), "Table.selectionForeground", new ColorUIResource(new ColorUIResource(255, 255, 255)), "TableHeader.background", new ColorUIResource(new ColorUIResource(192, 192, 192)), - "TableHeader.cellBorder", new BorderUIResource.BevelBorderUIResource(0), "TableHeader.font", new FontUIResource("Dialog", Font.PLAIN, 12), "TableHeader.foreground", new ColorUIResource(new ColorUIResource(0, 0, 0)), - "TextArea.background", new ColorUIResource(light), - "TextArea.border", - new BorderUIResource(BasicBorders.getMarginBorder()), + "TextArea.background", new ColorUIResource(light), + "TextArea.border", new BorderUIResource(BasicBorders.getMarginBorder()), "TextArea.caretBlinkRate", new Integer(500), "TextArea.caretForeground", new ColorUIResource(Color.black), "TextArea.font", new FontUIResource("MonoSpaced", Font.PLAIN, 12), "TextArea.foreground", new ColorUIResource(Color.black), "TextArea.inactiveForeground", new ColorUIResource(Color.gray), - "TextArea.keyBindings", new JTextComponent.KeyBinding[] { - new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_UP, - 0), "caret-up"), - new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, - 0), "caret-down"), - new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, - 0), "page-up"), - new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, - 0), "page-down"), - new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, - 0), "insert-break"), - new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, - 0), "insert-tab") - }, + "TextArea.focusInputMap", new UIDefaults.LazyInputMap(new Object[] { + KeyStroke.getKeyStroke("shift UP"), "selection-up", + KeyStroke.getKeyStroke("ctrl RIGHT"), "caret-next-word", + KeyStroke.getKeyStroke("shift ctrl LEFT"), "selection-previous-word", + KeyStroke.getKeyStroke("shift KP_UP"), "selection-up", + KeyStroke.getKeyStroke("DOWN"), "caret-down", + KeyStroke.getKeyStroke("shift ctrl T"), "previous-link-action", + KeyStroke.getKeyStroke("ctrl LEFT"), "caret-previous-word", + KeyStroke.getKeyStroke("CUT"), "cut-to-clipboard", + KeyStroke.getKeyStroke("END"), "caret-end-line", + KeyStroke.getKeyStroke("shift PAGE_UP"), "selection-page-up", + KeyStroke.getKeyStroke("KP_UP"), "caret-up", + KeyStroke.getKeyStroke("DELETE"), "delete-next", + KeyStroke.getKeyStroke("ctrl HOME"), "caret-begin", + KeyStroke.getKeyStroke("shift LEFT"), "selection-backward", + KeyStroke.getKeyStroke("ctrl END"), "caret-end", + KeyStroke.getKeyStroke("BACK_SPACE"), "delete-previous", + KeyStroke.getKeyStroke("shift ctrl RIGHT"), "selection-next-word", + KeyStroke.getKeyStroke("LEFT"), "caret-backward", + KeyStroke.getKeyStroke("KP_LEFT"), "caret-backward", + KeyStroke.getKeyStroke("shift KP_RIGHT"), "selection-forward", + KeyStroke.getKeyStroke("ctrl SPACE"), "activate-link-action", + KeyStroke.getKeyStroke("ctrl H"), "delete-previous", + KeyStroke.getKeyStroke("ctrl BACK_SLASH"), "unselect", + KeyStroke.getKeyStroke("ENTER"), "insert-break", + KeyStroke.getKeyStroke("shift HOME"), "selection-begin-line", + KeyStroke.getKeyStroke("RIGHT"), "caret-forward", + KeyStroke.getKeyStroke("shift ctrl PAGE_UP"), "selection-page-left", + KeyStroke.getKeyStroke("shift DOWN"), "selection-down", + KeyStroke.getKeyStroke("PAGE_DOWN"), "page-down", + KeyStroke.getKeyStroke("shift KP_LEFT"), "selection-backward", + KeyStroke.getKeyStroke("shift ctrl O"), "toggle-componentOrientation", + KeyStroke.getKeyStroke("ctrl X"), "cut-to-clipboard", + KeyStroke.getKeyStroke("shift ctrl PAGE_DOWN"), "selection-page-right", + KeyStroke.getKeyStroke("ctrl C"), "copy-to-clipboard", + KeyStroke.getKeyStroke("ctrl KP_RIGHT"), "caret-next-word", + KeyStroke.getKeyStroke("shift END"), "selection-end-line", + KeyStroke.getKeyStroke("ctrl KP_LEFT"), "caret-previous-word", + KeyStroke.getKeyStroke("HOME"), "caret-begin-line", + KeyStroke.getKeyStroke("ctrl V"), "paste-from-clipboard", + KeyStroke.getKeyStroke("KP_DOWN"), "caret-down", + KeyStroke.getKeyStroke("ctrl A"), "select-all", + KeyStroke.getKeyStroke("shift RIGHT"), "selection-forward", + KeyStroke.getKeyStroke("shift ctrl END"), "selection-end", + KeyStroke.getKeyStroke("COPY"), "copy-to-clipboard", + KeyStroke.getKeyStroke("shift ctrl KP_LEFT"), "selection-previous-word", + KeyStroke.getKeyStroke("ctrl T"), "next-link-action", + KeyStroke.getKeyStroke("shift KP_DOWN"), "selection-down", + KeyStroke.getKeyStroke("TAB"), "insert-tab", + KeyStroke.getKeyStroke("UP"), "caret-up", + KeyStroke.getKeyStroke("shift ctrl HOME"), "selection-begin", + KeyStroke.getKeyStroke("shift PAGE_DOWN"), "selection-page-down", + KeyStroke.getKeyStroke("KP_RIGHT"), "caret-forward", + KeyStroke.getKeyStroke("shift ctrl KP_RIGHT"), "selection-next-word", + KeyStroke.getKeyStroke("PAGE_UP"), "page-up", + KeyStroke.getKeyStroke("PASTE"), "paste-from-clipboard" + }), "TextArea.margin", new InsetsUIResource(0, 0, 0, 0), "TextArea.selectionBackground", new ColorUIResource(Color.black), "TextArea.selectionForeground", new ColorUIResource(Color.white), @@ -1017,17 +1221,41 @@ public abstract class BasicLookAndFeel extends LookAndFeel "TextField.inactiveForeground", new ColorUIResource(Color.GRAY), "TextField.light", new ColorUIResource(highLight), "TextField.highlight", new ColorUIResource(light), - "TextField.keyBindings", new JTextComponent.KeyBinding[] { - new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, - 0), - "notify-field-accept"), - new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, - InputEvent.SHIFT_DOWN_MASK), - "selection-backward"), - new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, - InputEvent.SHIFT_DOWN_MASK), - "selection-forward"), - }, + "TextField.focusInputMap", new UIDefaults.LazyInputMap(new Object[] { + KeyStroke.getKeyStroke("ENTER"), "notify-field-accept", + KeyStroke.getKeyStroke("LEFT"), "caret-backward", + KeyStroke.getKeyStroke("RIGHT"), "caret-forward", + KeyStroke.getKeyStroke("BACK_SPACE"), "delete-previous", + KeyStroke.getKeyStroke("ctrl X"), "cut-to-clipboard", + KeyStroke.getKeyStroke("ctrl C"), "copy-to-clipboard", + KeyStroke.getKeyStroke("ctrl V"), "paste-from-clipboard", + KeyStroke.getKeyStroke("shift LEFT"), "selection-backward", + KeyStroke.getKeyStroke("shift RIGHT"), "selection-forward", + KeyStroke.getKeyStroke("HOME"), "caret-begin-line", + KeyStroke.getKeyStroke("END"), "caret-end-line", + KeyStroke.getKeyStroke("DELETE"), "delete-next", + KeyStroke.getKeyStroke("shift ctrl O"), "toggle-componentOrientation", + KeyStroke.getKeyStroke("shift KP_LEFT"), "selection-backward", + KeyStroke.getKeyStroke("ctrl H"), "delete-previous", + KeyStroke.getKeyStroke("KP_LEFT"), "caret-backward", + KeyStroke.getKeyStroke("KP_RIGHT"), "caret-forward", + KeyStroke.getKeyStroke("shift ctrl KP_RIGHT"), "selection-next-word", + KeyStroke.getKeyStroke("COPY"), "copy-to-clipboard", + KeyStroke.getKeyStroke("shift HOME"), "selection-begin-line", + KeyStroke.getKeyStroke("shift ctrl LEFT"), "selection-previous-word", + KeyStroke.getKeyStroke("ctrl KP_LEFT"), "caret-previous-word", + KeyStroke.getKeyStroke("ctrl KP_RIGHT"), "caret-next-word", + KeyStroke.getKeyStroke("PASTE"), "paste-from-clipboard", + KeyStroke.getKeyStroke("shift ctrl RIGHT"), "selection-next-word", + KeyStroke.getKeyStroke("ctrl BACK_SLASH"), "unselect", + KeyStroke.getKeyStroke("ctrl A"), "select-all", + KeyStroke.getKeyStroke("shift KP_RIGHT"), "selection-forward", + KeyStroke.getKeyStroke("CUT"), "cut-to-clipboard", + KeyStroke.getKeyStroke("ctrl LEFT"), "caret-previous-word", + KeyStroke.getKeyStroke("shift ctrl KP_LEFT"), "selection-previous-word", + KeyStroke.getKeyStroke("shift END"), "selection-end-line", + KeyStroke.getKeyStroke("ctrl RIGHT"), "caret-next-word" + }), "TextField.margin", new InsetsUIResource(0, 0, 0, 0), "TextField.selectionBackground", new ColorUIResource(Color.black), "TextField.selectionForeground", new ColorUIResource(Color.white), @@ -1038,20 +1266,63 @@ public abstract class BasicLookAndFeel extends LookAndFeel "TextPane.font", new FontUIResource("Serif", Font.PLAIN, 12), "TextPane.foreground", new ColorUIResource(Color.black), "TextPane.inactiveForeground", new ColorUIResource(Color.gray), - "TextPane.keyBindings", new JTextComponent.KeyBinding[] { - new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_UP, - 0), "caret-up"), - new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, - 0), "caret-down"), - new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, - 0), "page-up"), - new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, - 0), "page-down"), - new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, - 0), "insert-break"), - new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, - 0), "insert-tab") - }, + "TextPane.focusInputMap", new UIDefaults.LazyInputMap(new Object[] { + KeyStroke.getKeyStroke("shift UP"), "selection-up", + KeyStroke.getKeyStroke("ctrl RIGHT"), "caret-next-word", + KeyStroke.getKeyStroke("shift ctrl LEFT"), "selection-previous-word", + KeyStroke.getKeyStroke("shift KP_UP"), "selection-up", + KeyStroke.getKeyStroke("DOWN"), "caret-down", + KeyStroke.getKeyStroke("shift ctrl T"), "previous-link-action", + KeyStroke.getKeyStroke("ctrl LEFT"), "caret-previous-word", + KeyStroke.getKeyStroke("CUT"), "cut-to-clipboard", + KeyStroke.getKeyStroke("END"), "caret-end-line", + KeyStroke.getKeyStroke("shift PAGE_UP"), "selection-page-up", + KeyStroke.getKeyStroke("KP_UP"), "caret-up", + KeyStroke.getKeyStroke("DELETE"), "delete-next", + KeyStroke.getKeyStroke("ctrl HOME"), "caret-begin", + KeyStroke.getKeyStroke("shift LEFT"), "selection-backward", + KeyStroke.getKeyStroke("ctrl END"), "caret-end", + KeyStroke.getKeyStroke("BACK_SPACE"), "delete-previous", + KeyStroke.getKeyStroke("shift ctrl RIGHT"), "selection-next-word", + KeyStroke.getKeyStroke("LEFT"), "caret-backward", + KeyStroke.getKeyStroke("KP_LEFT"), "caret-backward", + KeyStroke.getKeyStroke("shift KP_RIGHT"), "selection-forward", + KeyStroke.getKeyStroke("ctrl SPACE"), "activate-link-action", + KeyStroke.getKeyStroke("ctrl H"), "delete-previous", + KeyStroke.getKeyStroke("ctrl BACK_SLASH"), "unselect", + KeyStroke.getKeyStroke("ENTER"), "insert-break", + KeyStroke.getKeyStroke("shift HOME"), "selection-begin-line", + KeyStroke.getKeyStroke("RIGHT"), "caret-forward", + KeyStroke.getKeyStroke("shift ctrl PAGE_UP"), "selection-page-left", + KeyStroke.getKeyStroke("shift DOWN"), "selection-down", + KeyStroke.getKeyStroke("PAGE_DOWN"), "page-down", + KeyStroke.getKeyStroke("shift KP_LEFT"), "selection-backward", + KeyStroke.getKeyStroke("shift ctrl O"), "toggle-componentOrientation", + KeyStroke.getKeyStroke("ctrl X"), "cut-to-clipboard", + KeyStroke.getKeyStroke("shift ctrl PAGE_DOWN"), "selection-page-right", + KeyStroke.getKeyStroke("ctrl C"), "copy-to-clipboard", + KeyStroke.getKeyStroke("ctrl KP_RIGHT"), "caret-next-word", + KeyStroke.getKeyStroke("shift END"), "selection-end-line", + KeyStroke.getKeyStroke("ctrl KP_LEFT"), "caret-previous-word", + KeyStroke.getKeyStroke("HOME"), "caret-begin-line", + KeyStroke.getKeyStroke("ctrl V"), "paste-from-clipboard", + KeyStroke.getKeyStroke("KP_DOWN"), "caret-down", + KeyStroke.getKeyStroke("ctrl A"), "select-all", + KeyStroke.getKeyStroke("shift RIGHT"), "selection-forward", + KeyStroke.getKeyStroke("shift ctrl END"), "selection-end", + KeyStroke.getKeyStroke("COPY"), "copy-to-clipboard", + KeyStroke.getKeyStroke("shift ctrl KP_LEFT"), "selection-previous-word", + KeyStroke.getKeyStroke("ctrl T"), "next-link-action", + KeyStroke.getKeyStroke("shift KP_DOWN"), "selection-down", + KeyStroke.getKeyStroke("TAB"), "insert-tab", + KeyStroke.getKeyStroke("UP"), "caret-up", + KeyStroke.getKeyStroke("shift ctrl HOME"), "selection-begin", + KeyStroke.getKeyStroke("shift PAGE_DOWN"), "selection-page-down", + KeyStroke.getKeyStroke("KP_RIGHT"), "caret-forward", + KeyStroke.getKeyStroke("shift ctrl KP_RIGHT"), "selection-next-word", + KeyStroke.getKeyStroke("PAGE_UP"), "page-up", + KeyStroke.getKeyStroke("PASTE"), "paste-from-clipboard" + }), "TextPane.margin", new InsetsUIResource(3, 3, 3, 3), "TextPane.selectionBackground", new ColorUIResource(Color.black), "TextPane.selectionForeground", new ColorUIResource(Color.white), @@ -1063,8 +1334,8 @@ public abstract class BasicLookAndFeel extends LookAndFeel new BorderUIResource.CompoundBorderUIResource(null, null), "ToggleButton.darkShadow", new ColorUIResource(shadow), "ToggleButton.focusInputMap", new UIDefaults.LazyInputMap(new Object[] { - "SPACE", "pressed", - "released SPACE", "released" + KeyStroke.getKeyStroke("SPACE"), "pressed", + KeyStroke.getKeyStroke("released SPACE"), "released" }), "ToggleButton.font", new FontUIResource("Dialog", Font.PLAIN, 12), "ToggleButton.foreground", new ColorUIResource(darkShadow), @@ -1095,7 +1366,7 @@ public abstract class BasicLookAndFeel extends LookAndFeel "ToolBar.foreground", new ColorUIResource(darkShadow), "ToolBar.highlight", new ColorUIResource(highLight), "ToolBar.light", new ColorUIResource(highLight), - "ToolBar.separatorSize", new DimensionUIResource(20, 20), + "ToolBar.separatorSize", new DimensionUIResource(10, 10), "ToolBar.shadow", new ColorUIResource(shadow), "ToolTip.background", new ColorUIResource(light), "ToolTip.border", new BorderUIResource.LineBorderUIResource(Color.lightGray), @@ -1106,72 +1377,145 @@ public abstract class BasicLookAndFeel extends LookAndFeel }), "Tree.background", new ColorUIResource(new Color(255, 255, 255)), "Tree.changeSelectionWithFocus", Boolean.TRUE, -// "Tree.closedIcon", new IconUIResource(new ImageIcon("icons/TreeClosed.png")), -// "Tree.collapsedIcon", new IconUIResource(new ImageIcon("icons/TreeCollapsed.png")), "Tree.drawsFocusBorderAroundIcon", Boolean.FALSE, "Tree.editorBorder", new BorderUIResource.LineBorderUIResource(Color.lightGray), "Tree.focusInputMap", new UIDefaults.LazyInputMap(new Object[] { - "shift PAGE_DOWN", "scrollDownExtendSelection", - "PAGE_DOWN", "scrollDownChangeSelection", - "END", "selectLast", - "ctrl KP_UP", "selectPreviousChangeLead", - "shift END", "selectLastExtendSelection", - "HOME", "selectFirst", - "ctrl END", "selectLastChangeLead", - "ctrl SLASH", "selectAll", - "LEFT", "selectParent", - "shift HOME", "selectFirstExtendSelection", - "UP", "selectPrevious", - "ctrl KP_DOWN", "selectNextChangeLead", - "RIGHT", "selectChild", - "ctrl HOME", "selectFirstChangeLead", - "DOWN", "selectNext", - "ctrl KP_LEFT", "scrollLeft", - "shift UP", "selectPreviousExtendSelection", - "F2", "startEditing", - "ctrl LEFT", "scrollLeft", - "ctrl KP_RIGHT","scrollRight", - "ctrl UP", "selectPreviousChangeLead", - "shift DOWN", "selectNextExtendSelection", - "ENTER", "toggle", - "KP_UP", "selectPrevious", - "KP_DOWN", "selectNext", - "ctrl RIGHT", "scrollRight", - "KP_LEFT", "selectParent", - "KP_RIGHT", "selectChild", - "ctrl DOWN", "selectNextChangeLead", - "ctrl A", "selectAll", - "shift KP_UP", "selectPreviousExtendSelection", - "shift KP_DOWN","selectNextExtendSelection", - "ctrl SPACE", "toggleSelectionPreserveAnchor", - "ctrl shift PAGE_UP", "scrollUpExtendSelection", - "ctrl BACK_SLASH", "clearSelection", - "shift SPACE", "extendSelection", - "ctrl PAGE_UP", "scrollUpChangeLead", - "shift PAGE_UP","scrollUpExtendSelection", - "SPACE", "toggleSelectionPreserveAnchor", - "ctrl shift PAGE_DOWN", "scrollDownExtendSelection", - "PAGE_UP", "scrollUpChangeSelection", - "ctrl PAGE_DOWN", "scrollDownChangeLead" + KeyStroke.getKeyStroke("ctrl DOWN"), "selectNextChangeLead", + KeyStroke.getKeyStroke("shift UP"), "selectPreviousExtendSelection", + KeyStroke.getKeyStroke("ctrl RIGHT"), "scrollRight", + KeyStroke.getKeyStroke("shift KP_UP"), "selectPreviousExtendSelection", + KeyStroke.getKeyStroke("DOWN"), "selectNext", + KeyStroke.getKeyStroke("ctrl UP"), "selectPreviousChangeLead", + KeyStroke.getKeyStroke("ctrl LEFT"), "scrollLeft", + KeyStroke.getKeyStroke("CUT"), "cut", + KeyStroke.getKeyStroke("END"), "selectLast", + KeyStroke.getKeyStroke("shift PAGE_UP"), "scrollUpExtendSelection", + KeyStroke.getKeyStroke("KP_UP"), "selectPrevious", + KeyStroke.getKeyStroke("shift ctrl UP"), "selectPreviousExtendSelection", + KeyStroke.getKeyStroke("ctrl HOME"), "selectFirstChangeLead", + KeyStroke.getKeyStroke("ctrl END"), "selectLastChangeLead", + KeyStroke.getKeyStroke("ctrl PAGE_DOWN"), "scrollDownChangeLead", + KeyStroke.getKeyStroke("LEFT"), "selectParent", + KeyStroke.getKeyStroke("ctrl PAGE_UP"), "scrollUpChangeLead", + KeyStroke.getKeyStroke("KP_LEFT"), "selectParent", + KeyStroke.getKeyStroke("SPACE"), "addToSelection", + KeyStroke.getKeyStroke("ctrl SPACE"), "toggleAndAnchor", + KeyStroke.getKeyStroke("shift SPACE"), "extendTo", + KeyStroke.getKeyStroke("shift ctrl SPACE"), "moveSelectionTo", + KeyStroke.getKeyStroke("ADD"), "expand", + KeyStroke.getKeyStroke("ctrl BACK_SLASH"), "clearSelection", + KeyStroke.getKeyStroke("shift ctrl DOWN"), "selectNextExtendSelection", + KeyStroke.getKeyStroke("shift HOME"), "selectFirstExtendSelection", + KeyStroke.getKeyStroke("RIGHT"), "selectChild", + KeyStroke.getKeyStroke("shift ctrl PAGE_UP"), "scrollUpExtendSelection", + KeyStroke.getKeyStroke("shift DOWN"), "selectNextExtendSelection", + KeyStroke.getKeyStroke("PAGE_DOWN"), "scrollDownChangeSelection", + KeyStroke.getKeyStroke("shift ctrl KP_UP"), "selectPreviousExtendSelection", + KeyStroke.getKeyStroke("SUBTRACT"), "collapse", + KeyStroke.getKeyStroke("ctrl X"), "cut", + KeyStroke.getKeyStroke("shift ctrl PAGE_DOWN"), "scrollDownExtendSelection", + KeyStroke.getKeyStroke("ctrl SLASH"), "selectAll", + KeyStroke.getKeyStroke("ctrl C"), "copy", + KeyStroke.getKeyStroke("ctrl KP_RIGHT"), "scrollRight", + KeyStroke.getKeyStroke("shift END"), "selectLastExtendSelection", + KeyStroke.getKeyStroke("shift ctrl KP_DOWN"), "selectNextExtendSelection", + KeyStroke.getKeyStroke("ctrl KP_LEFT"), "scrollLeft", + KeyStroke.getKeyStroke("HOME"), "selectFirst", + KeyStroke.getKeyStroke("ctrl V"), "paste", + KeyStroke.getKeyStroke("KP_DOWN"), "selectNext", + KeyStroke.getKeyStroke("ctrl A"), "selectAll", + KeyStroke.getKeyStroke("ctrl KP_DOWN"), "selectNextChangeLead", + KeyStroke.getKeyStroke("shift ctrl END"), "selectLastExtendSelection", + KeyStroke.getKeyStroke("COPY"), "copy", + KeyStroke.getKeyStroke("ctrl KP_UP"), "selectPreviousChangeLead", + KeyStroke.getKeyStroke("shift KP_DOWN"), "selectNextExtendSelection", + KeyStroke.getKeyStroke("UP"), "selectPrevious", + KeyStroke.getKeyStroke("shift ctrl HOME"), "selectFirstExtendSelection", + KeyStroke.getKeyStroke("shift PAGE_DOWN"), "scrollDownExtendSelection", + KeyStroke.getKeyStroke("KP_RIGHT"), "selectChild", + KeyStroke.getKeyStroke("F2"), "startEditing", + KeyStroke.getKeyStroke("PAGE_UP"), "scrollUpChangeSelection", + KeyStroke.getKeyStroke("PASTE"), "paste" }), "Tree.font", new FontUIResource("Dialog", Font.PLAIN, 12), "Tree.foreground", new ColorUIResource(Color.black), "Tree.hash", new ColorUIResource(new Color(128, 128, 128)), "Tree.leftChildIndent", new Integer(7), "Tree.rightChildIndent", new Integer(13), - "Tree.rowHeight", new Integer(0), + "Tree.rowHeight", new Integer(16), "Tree.scrollsOnExpand", Boolean.TRUE, "Tree.selectionBackground", new ColorUIResource(Color.black), "Tree.nonSelectionBackground", new ColorUIResource(new Color(255, 255, 255)), "Tree.selectionBorderColor", new ColorUIResource(Color.black), "Tree.selectionBorder", new BorderUIResource.LineBorderUIResource(Color.black), "Tree.selectionForeground", new ColorUIResource(new Color(255, 255, 255)), - "Tree.textBackground", new ColorUIResource(new Color(192, 192, 192)), - "Tree.textForeground", new ColorUIResource(new Color(0, 0, 0)), "Viewport.background", new ColorUIResource(light), "Viewport.foreground", new ColorUIResource(Color.black), "Viewport.font", new FontUIResource("Dialog", Font.PLAIN, 12) }; defaults.putDefaults(uiDefaults); } -} // class BasicLookAndFeel + + /** + * Returns the <code>ActionMap</code> that stores all the actions that are + * responsibly for rendering auditory cues. + * + * @return the action map that stores all the actions that are + * responsibly for rendering auditory cues + * + * @see #createAudioAction + * @see #playSound + * + * @since 1.4 + */ + protected ActionMap getAudioActionMap() + { + if (audioActionMap != null) + audioActionMap = new ActionMap(); + return audioActionMap; + } + + /** + * Creates an <code>Action</code> that can play an auditory cue specified by + * the key. The UIDefaults value for the key is normally a String that points + * to an audio file relative to the current package. + * + * @param key a UIDefaults key that specifies the sound + * + * @return an action that can play the sound + * + * @see #playSound + * + * @since 1.4 + */ + protected Action createAudioAction(Object key) + { + return new AudioAction(key); + } + + /** + * Plays the sound of the action if it is listed in + * <code>AuditoryCues.playList</code>. + * + * @param audioAction the audio action to play + */ + protected void playSound(Action audioAction) + { + if (audioAction instanceof AudioAction) + { + Object[] playList = (Object[]) UIManager.get("AuditoryCues.playList"); + for (int i = 0; i < playList.length; ++i) + { + if (playList[i].equals(((AudioAction) audioAction).key)) + { + ActionEvent ev = new ActionEvent(this, + ActionEvent.ACTION_PERFORMED, + (String) playList[i]); + audioAction.actionPerformed(ev); + break; + } + } + } + } + +} diff --git a/javax/swing/plaf/basic/BasicMenuItemUI.java b/javax/swing/plaf/basic/BasicMenuItemUI.java index c8754a3e0..63f0ce206 100644 --- a/javax/swing/plaf/basic/BasicMenuItemUI.java +++ b/javax/swing/plaf/basic/BasicMenuItemUI.java @@ -206,7 +206,10 @@ public class BasicMenuItemUI extends MenuItemUI map.remove((KeyStroke)e.getOldValue()); else map = new ComponentInputMapUIResource(menuItem); - map.put((KeyStroke)e.getNewValue(), "doClick"); + + KeyStroke accelerator = (KeyStroke) e.getNewValue(); + if (accelerator != null) + map.put(accelerator, "doClick"); } } } @@ -485,7 +488,9 @@ public class BasicMenuItemUI extends MenuItemUI InputMap focusedWindowMap = SwingUtilities.getUIInputMap(menuItem, JComponent.WHEN_IN_FOCUSED_WINDOW); if (focusedWindowMap == null) focusedWindowMap = new ComponentInputMapUIResource(menuItem); - focusedWindowMap.put(menuItem.getAccelerator(), "doClick"); + KeyStroke accelerator = menuItem.getAccelerator(); + if (accelerator != null) + focusedWindowMap.put(accelerator, "doClick"); SwingUtilities.replaceUIInputMap(menuItem, JComponent.WHEN_IN_FOCUSED_WINDOW, focusedWindowMap); ActionMap UIActionMap = SwingUtilities.getUIActionMap(menuItem); @@ -555,17 +560,16 @@ public class BasicMenuItemUI extends MenuItemUI // Menu item is considered to be highlighted when it is selected. // But we don't want to paint the background of JCheckBoxMenuItems ButtonModel mod = menuItem.getModel(); - if ((menuItem.isSelected() && checkIcon == null) || (mod != null && - mod.isArmed()) - && (menuItem.getParent() instanceof MenuElement)) + if (menuItem.isContentAreaFilled()) { - if (menuItem.isContentAreaFilled()) - { - g.setColor(selectionBackground); - g.fillRect(0, 0, menuItem.getWidth(), menuItem.getHeight()); - } - } - + if ((menuItem.isSelected() && checkIcon == null) || (mod != null && + mod.isArmed()) + && (menuItem.getParent() instanceof MenuElement)) + g.setColor(selectionBackground); + else + g.setColor(bgColor); + g.fillRect(0, 0, menuItem.getWidth(), menuItem.getHeight()); + } } /** @@ -608,7 +612,7 @@ public class BasicMenuItemUI extends MenuItemUI FontMetrics fm = g.getFontMetrics(f); SwingUtilities.calculateInnerArea(m, br); SwingUtilities.calculateInsetArea(br, m.getInsets(), vr); - paintBackground(g, m, m.getBackground()); + paintBackground(g, m, background); /* * MenuItems insets are equal to menuItems margin, space between text and diff --git a/javax/swing/plaf/basic/BasicScrollPaneUI.java b/javax/swing/plaf/basic/BasicScrollPaneUI.java index 808ed2763..71671b799 100644 --- a/javax/swing/plaf/basic/BasicScrollPaneUI.java +++ b/javax/swing/plaf/basic/BasicScrollPaneUI.java @@ -505,7 +505,8 @@ public class BasicScrollPaneUI extends ScrollPaneUI JViewport oldViewport = (JViewport) ev.getOldValue(); oldViewport.removeChangeListener(viewportChangeListener); JViewport newViewport = (JViewport) ev.getNewValue(); - oldViewport.addChangeListener(viewportChangeListener); + newViewport.addChangeListener(viewportChangeListener); + syncScrollPaneWithViewport(); } } diff --git a/javax/swing/plaf/basic/BasicTabbedPaneUI.java b/javax/swing/plaf/basic/BasicTabbedPaneUI.java index 1ee041097..a8f52cef6 100644 --- a/javax/swing/plaf/basic/BasicTabbedPaneUI.java +++ b/javax/swing/plaf/basic/BasicTabbedPaneUI.java @@ -1549,9 +1549,9 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants textIconGap = UIManager.getInt("TabbedPane.textIconGap"); tabRunOverlay = UIManager.getInt("TabbedPane.tabRunOverlay"); - tabInsets = UIManager.getInsets("TabbedPane.tabbedPaneTabInsets"); + tabInsets = UIManager.getInsets("TabbedPane.tabInsets"); selectedTabPadInsets = UIManager.getInsets("TabbedPane.tabbedPaneTabPadInsets"); - tabAreaInsets = UIManager.getInsets("TabbedPane.tabbedPaneTabAreaInsets"); + tabAreaInsets = UIManager.getInsets("TabbedPane.tabAreaInsets"); contentBorderInsets = UIManager.getInsets("TabbedPane.tabbedPaneContentBorderInsets"); calcRect = new Rectangle(); diff --git a/javax/swing/plaf/basic/BasicTextUI.java b/javax/swing/plaf/basic/BasicTextUI.java index be668671b..fc3889484 100644 --- a/javax/swing/plaf/basic/BasicTextUI.java +++ b/javax/swing/plaf/basic/BasicTextUI.java @@ -46,15 +46,20 @@ import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.Shape; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; +import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.InputMap; import javax.swing.JComponent; +import javax.swing.KeyStroke; import javax.swing.LookAndFeel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; @@ -62,6 +67,7 @@ import javax.swing.UIManager; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.plaf.ActionMapUIResource; +import javax.swing.plaf.InputMapUIResource; import javax.swing.plaf.TextUI; import javax.swing.plaf.UIResource; import javax.swing.text.BadLocationException; @@ -615,15 +621,25 @@ public abstract class BasicTextUI extends TextUI */ protected Keymap createKeymap() { + JTextComponent.KeyBinding[] bindings = null; String prefix = getPropertyPrefix(); - JTextComponent.KeyBinding[] bindings = - (JTextComponent.KeyBinding[]) UIManager.get(prefix + ".keyBindings"); + InputMapUIResource m = (InputMapUIResource) UIManager.get(prefix + ".focusInputMap"); + if (m != null) + { + KeyStroke[] keys = m.keys(); + int len = keys.length; + bindings = new JTextComponent.KeyBinding[len]; + for (int i = 0; i < len; i++) + { + KeyStroke curr = keys[i]; + bindings[i] = new JTextComponent.KeyBinding(curr, + (String) m.get(curr)); + } + } if (bindings == null) { bindings = new JTextComponent.KeyBinding[0]; - // FIXME: Putting something into the defaults map is certainly wrong. - // Must be fixed somehow. - UIManager.put(prefix + ".keyBindings", bindings); + UIManager.put(prefix + ".focusInputMap", bindings); } Keymap km = JTextComponent.addKeymap(getKeymapName(), @@ -636,18 +652,45 @@ public abstract class BasicTextUI extends TextUI * Installs the keyboard actions on the text components. */ protected void installKeyboardActions() - { - // load any bindings for the older Keymap interface + { + // load key bindings for the older interface Keymap km = JTextComponent.getKeymap(getKeymapName()); if (km == null) km = createKeymap(); textComponent.setKeymap(km); // load any bindings for the newer InputMap / ActionMap interface - SwingUtilities.replaceUIInputMap(textComponent, - JComponent.WHEN_FOCUSED, + SwingUtilities.replaceUIInputMap(textComponent, JComponent.WHEN_FOCUSED, getInputMap(JComponent.WHEN_FOCUSED)); - SwingUtilities.replaceUIActionMap(textComponent, getActionMap()); + SwingUtilities.replaceUIActionMap(textComponent, createActionMap()); + + ActionMap parentActionMap = new ActionMapUIResource(); + Action[] actions = textComponent.getActions(); + for (int j = 0; j < actions.length; j++) + { + Action currAction = actions[j]; + parentActionMap.put(currAction.getValue(Action.NAME), currAction); + } + + SwingUtilities.replaceUIActionMap(textComponent, parentActionMap); + } + + /** + * Creates an ActionMap to be installed on the text component. + * + * @return an ActionMap to be installed on the text component + */ + ActionMap createActionMap() + { + Action[] actions = textComponent.getActions(); + ActionMap am = new ActionMapUIResource(); + for (int i = 0; i < actions.length; ++i) + { + String name = (String) actions[i].getValue(Action.NAME); + if (name != null) + am.put(name, actions[i]); + } + return am; } /** @@ -674,46 +717,6 @@ public abstract class BasicTextUI extends TextUI } /** - * Returns the ActionMap to be installed on the text component. - * - * @return the ActionMap to be installed on the text component - */ - // FIXME: The UIDefaults have no entries for .actionMap, so this should - // be handled somehow different. - ActionMap getActionMap() - { - String prefix = getPropertyPrefix(); - ActionMap am = (ActionMap) UIManager.get(prefix + ".actionMap"); - if (am == null) - { - am = createActionMap(); - // FIXME: Putting something in the UIDefaults map is certainly wrong. - // However, the whole method seems wrong and must be replaced by - // something that is less wrong. - UIManager.put(prefix + ".actionMap", am); - } - return am; - } - - /** - * Creates an ActionMap to be installed on the text component. - * - * @return an ActionMap to be installed on the text component - */ - ActionMap createActionMap() - { - Action[] actions = textComponent.getActions(); - ActionMap am = new ActionMapUIResource(); - for (int i = 0; i < actions.length; ++i) - { - String name = (String) actions[i].getValue(Action.NAME); - if (name != null) - am.put(name, actions[i]); - } - return am; - } - - /** * Uninstalls this TextUI from the text component. * * @param component the text component to uninstall the UI from diff --git a/javax/swing/plaf/basic/BasicTreeUI.java b/javax/swing/plaf/basic/BasicTreeUI.java index 4100442a0..3bb803a56 100644 --- a/javax/swing/plaf/basic/BasicTreeUI.java +++ b/javax/swing/plaf/basic/BasicTreeUI.java @@ -112,7 +112,6 @@ import javax.swing.tree.TreeSelectionModel; * the Basic look and feel. * * @see javax.swing.JTree - * * @author Lillian Angel (langel@redhat.com) * @author Sascha Brawer (brawer@dandelis.ch) */ @@ -173,7 +172,7 @@ public class BasicTreeUI extends TreeUI /** Size needed to completely display all the nodes. */ protected Dimension preferredSize; - + /** Minimum size needed to completely display all the nodes. */ protected Dimension preferredMinSize; @@ -225,7 +224,7 @@ public class BasicTreeUI extends TreeUI /** Set to true if the editor has a different size than the renderer. */ protected boolean editorHasDifferentSize; - + /** The action listener for the editor's Timer. */ Timer editorTimer = new EditorUpdateTimer(); @@ -234,29 +233,38 @@ public class BasicTreeUI extends TreeUI /** The action bound to KeyStrokes. */ TreeAction action; - + /** Boolean to keep track of editing. */ boolean isEditing; - + /** The current path of the visible nodes in the tree. */ TreePath currentVisiblePath; - + /** The gap between the icon and text. */ int gap = 4; - - /** Default row height, if none was set. */ - int rowHeight = 20; + + /** The max height of the nodes in the tree. */ + int maxHeight = 0; /** Listeners */ private PropertyChangeListener propertyChangeListener; + private FocusListener focusListener; + private TreeSelectionListener treeSelectionListener; + private MouseListener mouseListener; + private KeyListener keyListener; + private PropertyChangeListener selectionModelPropertyChangeListener; + private ComponentListener componentListener; + CellEditorListener cellEditorListener; + private TreeExpansionListener treeExpansionListener; + private TreeModelListener treeModelListener; /** @@ -437,7 +445,7 @@ public class BasicTreeUI extends TreeUI protected void setRowHeight(int rowHeight) { if (rowHeight == 0) - rowHeight = this.rowHeight; + rowHeight = Math.max(getMaxHeight(tree), 20); treeState.setRowHeight(rowHeight); } @@ -625,19 +633,49 @@ public class BasicTreeUI extends TreeUI */ public Rectangle getPathBounds(JTree tree, TreePath path) { - Rectangle bounds = null; int row = -1; Object cell = null; if (path != null) { row = getRowForPath(tree, path); cell = path.getLastPathComponent(); - bounds = new Rectangle(0, row * getRowHeight(), 0, 0); } - return nodeDimensions.getNodeDimensions(cell, row, - getLevel(cell), + return nodeDimensions.getNodeDimensions(cell, row, getLevel(cell), tree.isExpanded(path), - bounds); + new Rectangle()); + } + + /** + * Returns the max height of all the nodes in the tree. + * + * @param tree - + * the current tree + * @return the max height. + */ + private int getMaxHeight(JTree tree) + { + if (maxHeight != 0) + return maxHeight; + + Icon e = UIManager.getIcon("Tree.openIcon"); + Icon c = UIManager.getIcon("Tree.closedIcon"); + Icon l = UIManager.getIcon("Tree.leafIcon"); + int rc = getRowCount(tree); + int iconHeight = 0; + + for (int row = 0; row < rc; row++) + { + if (isLeaf(row)) + iconHeight = l.getIconHeight(); + else if (tree.isExpanded(row)) + iconHeight = e.getIconHeight(); + else + iconHeight = c.getIconHeight(); + + maxHeight = Math.max(maxHeight, iconHeight + gap); + } + + return maxHeight; } /** @@ -684,7 +722,7 @@ public class BasicTreeUI extends TreeUI { if (dest.equals(nodes[row])) return row; - row++; + row++; } } return -1; @@ -720,7 +758,7 @@ public class BasicTreeUI extends TreeUI */ public TreePath getClosestPathForLocation(JTree tree, int x, int y) { - int row = Math.round(y / getRowHeight()); + int row = Math.round(y / getMaxHeight(tree)); TreePath path = getPathForRow(tree, row); // no row is visible at this node @@ -977,10 +1015,12 @@ public class BasicTreeUI extends TreeUI protected TreeCellEditor createDefaultCellEditor() { if (currentCellRenderer != null) - return new DefaultTreeCellEditor(tree, + return new DefaultTreeCellEditor( + tree, (DefaultTreeCellRenderer) currentCellRenderer, cellEditor); - return new DefaultTreeCellEditor(tree, + return new DefaultTreeCellEditor( + tree, (DefaultTreeCellRenderer) createDefaultCellRenderer(), cellEditor); } @@ -1034,7 +1074,8 @@ public class BasicTreeUI extends TreeUI protected void uninstallKeyboardActions() { action = null; - tree.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).setParent(null); + tree.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).setParent( + null); tree.getActionMap().setParent(null); } @@ -1094,7 +1135,7 @@ public class BasicTreeUI extends TreeUI { Enumeration expanded = tree.getExpandedDescendants(path); while (expanded.hasMoreElements()) - treeState.setExpandedState(((TreePath) expanded.nextElement()), true); + treeState.setExpandedState(((TreePath) expanded.nextElement()), true); } /** @@ -1125,7 +1166,7 @@ public class BasicTreeUI extends TreeUI protected void updateCellEditor() { if (tree.isEditable() && cellEditor == null) - setCellEditor(createDefaultCellEditor()); + setCellEditor(createDefaultCellEditor()); createdCellEditor = true; } @@ -1136,10 +1177,10 @@ public class BasicTreeUI extends TreeUI { if (tree != null) { - if(tree.getCellRenderer() == null) - { - if(currentCellRenderer == null) - currentCellRenderer = createDefaultCellRenderer(); + if (tree.getCellRenderer() == null) + { + if (currentCellRenderer == null) + currentCellRenderer = createDefaultCellRenderer(); tree.setCellRenderer(currentCellRenderer); } } @@ -1186,9 +1227,13 @@ public class BasicTreeUI extends TreeUI bounds.width += getCurrentControlIcon(curr).getIconWidth(); maxWidth = Math.max(maxWidth, bounds.x + bounds.width); } - preferredSize = new Dimension(maxWidth, (getRowHeight() * path.length)); + + maxHeight = 0; + maxHeight = getMaxHeight(tree); + preferredSize = new Dimension(maxWidth, (maxHeight * path.length)); } - else preferredSize = new Dimension(0, 0); + else + preferredSize = new Dimension(0, 0); validCachedPreferredSize = true; } @@ -1201,7 +1246,6 @@ public class BasicTreeUI extends TreeUI protected void pathWasExpanded(TreePath path) { validCachedPreferredSize = false; - tree.revalidate(); tree.repaint(); } @@ -1211,7 +1255,6 @@ public class BasicTreeUI extends TreeUI protected void pathWasCollapsed(TreePath path) { validCachedPreferredSize = false; - tree.revalidate(); tree.repaint(); } @@ -1345,19 +1388,12 @@ public class BasicTreeUI extends TreeUI installComponents(); installKeyboardActions(); installListeners(); - + setCellEditor(createDefaultCellEditor()); createdCellEditor = true; isEditing = false; - TreeModel mod = tree.getModel(); - setModel(mod); - if (mod != null) - { - TreePath path = new TreePath(mod.getRoot()); - if (!tree.isExpanded(path)) - toggleExpandState(path); - } + setModel(tree.getModel()); treeSelectionModel = tree.getSelectionModel(); completeUIInstall(); @@ -1406,8 +1442,7 @@ public class BasicTreeUI extends TreeUI public void paint(Graphics g, JComponent c) { JTree tree = (JTree) c; - if (currentVisiblePath == null) - updateCurrentVisiblePath(); + updateCurrentVisiblePath(); Rectangle clip = g.getClipBounds(); Insets insets = tree.getInsets(); @@ -1451,7 +1486,7 @@ public class BasicTreeUI extends TreeUI endRow = beginRow; beginRow = temp; } - + for (int i = beginRow; i < endRow; i++) { TreePath path = getPathForRow(tree, i); @@ -1508,8 +1543,8 @@ public class BasicTreeUI extends TreeUI */ public Dimension getPreferredSize(JComponent c, boolean checkConsistancy) { - // FIXME: checkConsistancy not implemented, c not used - if(!validCachedPreferredSize) + // FIXME: checkConsistancy not implemented, c not used + if (!validCachedPreferredSize) updateCachedPreferredSize(); return preferredSize; } @@ -1524,7 +1559,7 @@ public class BasicTreeUI extends TreeUI */ public Dimension getMinimumSize(JComponent c) { - Dimension min = getPreferredMinSize(); + Dimension min = getPreferredMinSize(); if (min == null) return new Dimension(); return min; @@ -1636,7 +1671,7 @@ public class BasicTreeUI extends TreeUI tree.add(editingComponent.getParent()); editingComponent.getParent().validate(); validCachedPreferredSize = false; - tree.revalidate(); + ((JTextField) editingComponent).requestFocusInWindow(false); editorTimer.start(); return true; @@ -1682,12 +1717,13 @@ public class BasicTreeUI extends TreeUI { boolean cntlClick = false; int row = getRowForPath(tree, path); - + if (!isLeaf(row)) { Rectangle bounds = getPathBounds(tree, path); - if (hasControlIcons() && (mouseX < bounds.x) + if (hasControlIcons() + && (mouseX < bounds.x) && (mouseX > (bounds.x - getCurrentControlIcon(path).getIconWidth() - gap))) cntlClick = true; } @@ -1725,7 +1761,6 @@ public class BasicTreeUI extends TreeUI tree.collapsePath(path); else tree.expandPath(path); - updateCurrentVisiblePath(); } /** @@ -1739,8 +1774,7 @@ public class BasicTreeUI extends TreeUI */ protected boolean isToggleSelectionEvent(MouseEvent event) { - return (tree.getSelectionModel().getSelectionMode() == - TreeSelectionModel.SINGLE_TREE_SELECTION); + return (tree.getSelectionModel().getSelectionMode() == TreeSelectionModel.SINGLE_TREE_SELECTION); } /** @@ -1754,8 +1788,7 @@ public class BasicTreeUI extends TreeUI */ protected boolean isMultiSelectEvent(MouseEvent event) { - return (tree.getSelectionModel().getSelectionMode() == - TreeSelectionModel.CONTIGUOUS_TREE_SELECTION); + return (tree.getSelectionModel().getSelectionMode() == TreeSelectionModel.CONTIGUOUS_TREE_SELECTION); } /** @@ -1834,8 +1867,7 @@ public class BasicTreeUI extends TreeUI * are pressed for the JTree. The actionPerformed method is called when a key * that has been registered for the JTree is received. */ - class TreeAction - extends AbstractAction + class TreeAction extends AbstractAction { /** @@ -1877,7 +1909,7 @@ public class BasicTreeUI extends TreeUI else if (e.getActionCommand().equals("toggle")) { if (tree.isEditing()) - tree.stopEditing(); + tree.stopEditing(); else { Object last = lead.getLastPathComponent(); @@ -1903,8 +1935,7 @@ public class BasicTreeUI extends TreeUI * to the true receiver after altering the actionCommand property of the * event. */ - private static class ActionListenerProxy - extends AbstractAction + private static class ActionListenerProxy extends AbstractAction { ActionListener target; @@ -1929,9 +1960,7 @@ public class BasicTreeUI extends TreeUI /** * The timer that updates the editor component. */ - private class EditorUpdateTimer - extends Timer - implements ActionListener + private class EditorUpdateTimer extends Timer implements ActionListener { /** * Creates a new EditorUpdateTimer object with a default delay of 0.3 @@ -1975,8 +2004,8 @@ public class BasicTreeUI extends TreeUI /** * Updates the preferred size when scrolling, if necessary. */ - public class ComponentHandler extends ComponentAdapter - implements ActionListener + public class ComponentHandler extends ComponentAdapter implements + ActionListener { /** * Timer used when inside a scrollpane and the scrollbar is adjusting @@ -2006,8 +2035,8 @@ public class BasicTreeUI extends TreeUI } /** - * Creates, if necessary, and starts a Timer to check if needed to resize the - * bounds + * Creates, if necessary, and starts a Timer to check if needed to resize + * the bounds */ protected void startTimer() { @@ -2082,7 +2111,6 @@ public class BasicTreeUI extends TreeUI tree.requestFocusInWindow(false); editorTimer.stop(); validCachedPreferredSize = false; - tree.revalidate(); tree.repaint(); } @@ -2113,7 +2141,6 @@ public class BasicTreeUI extends TreeUI editorTimer.stop(); isEditing = false; validCachedPreferredSize = false; - tree.revalidate(); tree.repaint(); } }// CellEditorHandler @@ -2121,8 +2148,7 @@ public class BasicTreeUI extends TreeUI /** * Repaints the lead selection row when focus is lost/grained. */ - public class FocusHandler - implements FocusListener + public class FocusHandler implements FocusListener { /** * Constructor @@ -2161,8 +2187,7 @@ public class BasicTreeUI extends TreeUI * This is used to get multiple key down events to appropriately genereate * events. */ - public class KeyHandler - extends KeyAdapter + public class KeyHandler extends KeyAdapter { /** Key code that is being generated for. */ protected Action repeatKeyAction; @@ -2247,7 +2272,7 @@ public class BasicTreeUI extends TreeUI boolean cntlClick = isLocationInExpandControl(path, click.x, click.y); boolean isLeaf = isLeaf(row); - + TreeCellRenderer tcr = getCellRenderer(); Icon icon; if (isLeaf) @@ -2256,17 +2281,17 @@ public class BasicTreeUI extends TreeUI icon = UIManager.getIcon("Tree.openIcon"); else icon = UIManager.getIcon("Tree.closedIcon"); - + if (tcr instanceof DefaultTreeCellRenderer) { - Icon tmp = ((DefaultTreeCellRenderer) tcr).getIcon(); - if (tmp != null) - icon = tmp; + Icon tmp = ((DefaultTreeCellRenderer) tcr).getIcon(); + if (tmp != null) + icon = tmp; } - + // add gap*2 for the space before and after the text if (icon != null) - bounds.width += icon.getIconWidth() + gap*2; + bounds.width += icon.getIconWidth() + gap * 2; boolean inBounds = bounds.contains(click.x, click.y); if ((inBounds || cntlClick) && tree.isVisible(path)) @@ -2275,9 +2300,9 @@ public class BasicTreeUI extends TreeUI { selectPath(tree, path); if (e.getClickCount() == 2 && !isLeaf(row)) - toggleExpandState(path); + toggleExpandState(path); } - + if (cntlClick) { handleExpandControlClick(path, click.x, click.y); @@ -2455,8 +2480,7 @@ public class BasicTreeUI extends TreeUI * BasicTreeUI method. X location does not include insets, that is handled in * getPathBounds. */ - public class NodeDimensionsHandler - extends AbstractLayoutCache.NodeDimensions + public class NodeDimensionsHandler extends AbstractLayoutCache.NodeDimensions { /** * Constructor @@ -2467,10 +2491,10 @@ public class BasicTreeUI extends TreeUI } /** - * Returns, by reference in bounds, the size and x origin to place value at. - * The calling method is responsible for determining the Y location. - * If bounds is null, a newly created Rectangle should be returned, - * otherwise the value should be placed in bounds and returned. + * Returns, by reference in bounds, the size and x origin to place value at. + * The calling method is responsible for determining the Y location. If + * bounds is null, a newly created Rectangle should be returned, otherwise + * the value should be placed in bounds and returned. * * @param cell * the value to be represented @@ -2498,8 +2522,10 @@ public class BasicTreeUI extends TreeUI { size.x = getRowX(row, depth); size.width = SwingUtilities.computeStringWidth(fm, s); - size.height = fm.getHeight(); + size.height = getMaxHeight(tree); + size.y = size.height * row; } + return size; } @@ -2520,8 +2546,7 @@ public class BasicTreeUI extends TreeUI * PropertyChangeListener for the tree. Updates the appropriate varaible, or * TreeState, based on what changes. */ - public class PropertyChangeHandler - implements PropertyChangeListener + public class PropertyChangeHandler implements PropertyChangeListener { /** @@ -2544,8 +2569,6 @@ public class BasicTreeUI extends TreeUI if ((event.getPropertyName()).equals("rootVisible")) { validCachedPreferredSize = false; - updateCurrentVisiblePath(); - tree.revalidate(); tree.repaint(); } } @@ -2555,8 +2578,8 @@ public class BasicTreeUI extends TreeUI * Listener on the TreeSelectionModel, resets the row selection if any of the * properties of the model change. */ - public class SelectionModelPropertyChangeHandler - implements PropertyChangeListener + public class SelectionModelPropertyChangeHandler implements + PropertyChangeListener { /** @@ -2583,8 +2606,7 @@ public class BasicTreeUI extends TreeUI /** * ActionListener that invokes cancelEditing when action performed. */ - public class TreeCancelEditingAction - extends AbstractAction + public class TreeCancelEditingAction extends AbstractAction { /** @@ -2621,8 +2643,7 @@ public class BasicTreeUI extends TreeUI /** * Updates the TreeState in response to nodes expanding/collapsing. */ - public class TreeExpansionHandler - implements TreeExpansionListener + public class TreeExpansionHandler implements TreeExpansionListener { /** @@ -2642,8 +2663,6 @@ public class BasicTreeUI extends TreeUI public void treeExpanded(TreeExpansionEvent event) { validCachedPreferredSize = false; - updateCurrentVisiblePath(); - tree.revalidate(); tree.repaint(); } @@ -2656,8 +2675,6 @@ public class BasicTreeUI extends TreeUI public void treeCollapsed(TreeExpansionEvent event) { validCachedPreferredSize = false; - updateCurrentVisiblePath(); - tree.revalidate(); tree.repaint(); } }// TreeExpansionHandler @@ -2666,8 +2683,7 @@ public class BasicTreeUI extends TreeUI * TreeHomeAction is used to handle end/home actions. Scrolls either the first * or last cell to be visible based on direction. */ - public class TreeHomeAction - extends AbstractAction + public class TreeHomeAction extends AbstractAction { /** direction is either home or end */ @@ -2713,8 +2729,7 @@ public class BasicTreeUI extends TreeUI * TreeIncrementAction is used to handle up/down actions. Selection is moved * up or down based on direction. */ - public class TreeIncrementAction - extends AbstractAction + public class TreeIncrementAction extends AbstractAction { /** Specifies the direction to adjust the selection by. */ @@ -2746,7 +2761,7 @@ public class BasicTreeUI extends TreeUI if (e.getActionCommand().equals("selectPreviousChangeLead")) { Object prev = getPreviousVisibleNode(last); - + if (prev != null) { TreePath newPath = new TreePath(getPathToRoot(prev, 0)); @@ -2767,7 +2782,7 @@ public class BasicTreeUI extends TreeUI else if (e.getActionCommand().equals("selectPrevious")) { Object prev = getPreviousVisibleNode(last); - + if (prev != null) { TreePath newPath = new TreePath(getPathToRoot(prev, 0)); @@ -2777,7 +2792,7 @@ public class BasicTreeUI extends TreeUI else if (e.getActionCommand().equals("selectNext")) { Object next = getNextVisibleNode(last); - + if (next != null) { TreePath newPath = new TreePath(getPathToRoot(next, 0)); @@ -2847,8 +2862,6 @@ public class BasicTreeUI extends TreeUI public void treeNodesChanged(TreeModelEvent e) { validCachedPreferredSize = false; - updateCurrentVisiblePath(); - tree.revalidate(); tree.repaint(); } @@ -2863,8 +2876,6 @@ public class BasicTreeUI extends TreeUI public void treeNodesInserted(TreeModelEvent e) { validCachedPreferredSize = false; - updateCurrentVisiblePath(); - tree.revalidate(); tree.repaint(); } @@ -2882,8 +2893,6 @@ public class BasicTreeUI extends TreeUI public void treeNodesRemoved(TreeModelEvent e) { validCachedPreferredSize = false; - updateCurrentVisiblePath(); - tree.revalidate(); tree.repaint(); } @@ -2902,9 +2911,7 @@ public class BasicTreeUI extends TreeUI if (e.getPath().length == 1 && !e.getPath()[0].equals(treeModel.getRoot())) tree.expandPath(new TreePath(treeModel.getRoot())); - updateCurrentVisiblePath(); validCachedPreferredSize = false; - tree.revalidate(); tree.repaint(); } }// TreeModelHandler @@ -3102,7 +3109,7 @@ public class BasicTreeUI extends TreeUI return true; return false; } - + /** * Returns control icon. It is null if the LookAndFeel does not implements the * control icons. Package private for use in inner classes. @@ -3127,10 +3134,9 @@ public class BasicTreeUI extends TreeUI */ Object getParent(Object root, Object node) { - if (root == null || node == null || - root.equals(node)) + if (root == null || node == null || root.equals(node)) return null; - + if (node instanceof TreeNode) return ((TreeNode) node).getParent(); return findNode(root, node); @@ -3163,7 +3169,7 @@ public class BasicTreeUI extends TreeUI } return null; } - + /** * Get previous visible node in the tree. Package private for use in inner * classes. @@ -3182,8 +3188,8 @@ public class BasicTreeUI extends TreeUI while (i < nodes.length && !node.equals(nodes[i])) i++; // return the next node - if (i-1 >= 0) - return nodes[i-1]; + if (i - 1 >= 0) + return nodes[i - 1]; } return null; } @@ -3191,7 +3197,7 @@ public class BasicTreeUI extends TreeUI /** * Returns the next node in the tree Package private for use in inner classes. * - * @param curr - + * @param curr - * current node * @return the next node in the tree */ @@ -3208,7 +3214,7 @@ public class BasicTreeUI extends TreeUI node = getParent(treeModel.getRoot(), node); } while (sibling == null && node != null); - + return sibling; } @@ -3250,7 +3256,7 @@ public class BasicTreeUI extends TreeUI * Returns the next sibling in the tree Package private for use in inner * classes. * - * @param node - + * @param node - * current node * @return the next sibling in the tree */ @@ -3270,7 +3276,7 @@ public class BasicTreeUI extends TreeUI return treeModel.getChild(parent, index); } - + /** * Returns the previous sibling in the tree Package private for use in inner * classes. @@ -3309,15 +3315,13 @@ public class BasicTreeUI extends TreeUI { if (path != null) { - if (tree.getSelectionModel().getSelectionMode() == - TreeSelectionModel.SINGLE_TREE_SELECTION) + if (tree.getSelectionModel().getSelectionMode() == TreeSelectionModel.SINGLE_TREE_SELECTION) { tree.getSelectionModel().clearSelection(); tree.addSelectionPath(path); tree.setLeadSelectionPath(path); } - else if (tree.getSelectionModel().getSelectionMode() == - TreeSelectionModel.CONTIGUOUS_TREE_SELECTION) + else if (tree.getSelectionModel().getSelectionMode() == TreeSelectionModel.CONTIGUOUS_TREE_SELECTION) { // TODO } @@ -3325,8 +3329,8 @@ public class BasicTreeUI extends TreeUI { tree.addSelectionPath(path); tree.setLeadSelectionPath(path); - tree.getSelectionModel().setSelectionMode - (TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION); + tree.getSelectionModel().setSelectionMode( + TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION); } } } @@ -3351,7 +3355,8 @@ public class BasicTreeUI extends TreeUI return new Object[depth]; } - Object[] path = getPathToRoot(getParent(treeModel.getRoot(), node), depth + 1); + Object[] path = getPathToRoot(getParent(treeModel.getRoot(), node), + depth + 1); path[path.length - depth - 1] = node; return path; } @@ -3374,7 +3379,7 @@ public class BasicTreeUI extends TreeUI Object root = treeModel.getRoot(); if (!tree.isRootVisible() && tree.isExpanded(new TreePath(root))) count--; - + do { current = getParent(root, current); @@ -3441,28 +3446,32 @@ public class BasicTreeUI extends TreeUI * @param x * is the center position in x-direction * @param y - * is the center position in y-direction + * is the center position in y-direction */ protected void drawCentered(Component c, Graphics g, Icon icon, int x, int y) { x -= icon.getIconWidth() / 2; y -= icon.getIconHeight() / 2; - + if (x < 0) x = 0; if (y < 0) y = 0; - + icon.paintIcon(c, g, x, y); } - + /** * Draws a dashed horizontal line. * - * @param g - the graphics configuration. - * @param y - the y location to start drawing at - * @param x1 - the x location to start drawing at - * @param x2 - the x location to finish drawing at + * @param g - + * the graphics configuration. + * @param y - + * the y location to start drawing at + * @param x1 - + * the x location to start drawing at + * @param x2 - + * the x location to finish drawing at */ protected void drawDashedHorizontalLine(Graphics g, int y, int x1, int x2) { @@ -3470,14 +3479,18 @@ public class BasicTreeUI extends TreeUI for (int i = x1; i < x2; i += 2) g.drawLine(i, y, i + 1, y); } - + /** * Draws a dashed vertical line. * - * @param g - the graphics configuration. - * @param x - the x location to start drawing at - * @param y1 - the y location to start drawing at - * @param y2 - the y location to finish drawing at + * @param g - + * the graphics configuration. + * @param x - + * the x location to start drawing at + * @param y1 - + * the y location to start drawing at + * @param y2 - + * the y location to finish drawing at */ protected void drawDashedVerticalLine(Graphics g, int x, int y1, int y2) { @@ -3485,72 +3498,89 @@ public class BasicTreeUI extends TreeUI for (int i = y1; i < y2; i += 2) g.drawLine(x, i, x, i + 1); } - + /** - * Paints the expand (toggle) part of a row. The receiver should NOT modify + * Paints the expand (toggle) part of a row. The receiver should NOT modify * clipBounds, or insets. * - * @param g - the graphics configuration - * @param clipBounds - - * @param insets - - * @param bounds - bounds of expand control - * @param path - path to draw control for - * @param row - row to draw control for - * @param isExpanded - is the row expanded - * @param hasBeenExpanded - has the row already been expanded - * @param isLeaf - is the path a leaf + * @param g - + * the graphics configuration + * @param clipBounds - + * @param insets - + * @param bounds - + * bounds of expand control + * @param path - + * path to draw control for + * @param row - + * row to draw control for + * @param isExpanded - + * is the row expanded + * @param hasBeenExpanded - + * has the row already been expanded + * @param isLeaf - + * is the path a leaf */ protected void paintExpandControl(Graphics g, Rectangle clipBounds, Insets insets, Rectangle bounds, - TreePath path, int row, - boolean isExpanded, boolean hasBeenExpanded, - boolean isLeaf) + TreePath path, int row, boolean isExpanded, + boolean hasBeenExpanded, boolean isLeaf) { if (shouldPaintExpandControl(path, row, isExpanded, hasBeenExpanded, isLeaf)) { Icon icon = getCurrentControlIcon(path); int iconW = icon.getIconWidth(); - int x = bounds.x - rightChildIndent + iconW/2; + int x = bounds.x - rightChildIndent + iconW / 2; if (x + iconW > bounds.x) x = bounds.x - rightChildIndent - gap; - icon.paintIcon(tree, g, x, bounds.y + bounds.height/2 - icon.getIconHeight()/2); + icon.paintIcon(tree, g, x, bounds.y + bounds.height / 2 + - icon.getIconHeight() / 2); } } /** - * Paints the horizontal part of the leg. The receiver should NOT modify - * clipBounds, or insets. - * NOTE: parentRow can be -1 if the root is not visible. - * - * @param g - the graphics configuration - * @param clipBounds - - * @param insets - - * @param bounds - bounds of the cell - * @param path - path to draw leg for - * @param row - row to start drawing at - * @param isExpanded - is the row expanded - * @param hasBeenExpanded - has the row already been expanded - * @param isLeaf - is the path a leaf + * Paints the horizontal part of the leg. The receiver should NOT modify + * clipBounds, or insets. NOTE: parentRow can be -1 if the root is not + * visible. + * + * @param g - + * the graphics configuration + * @param clipBounds - + * @param insets - + * @param bounds - + * bounds of the cell + * @param path - + * path to draw leg for + * @param row - + * row to start drawing at + * @param isExpanded - + * is the row expanded + * @param hasBeenExpanded - + * has the row already been expanded + * @param isLeaf - + * is the path a leaf */ protected void paintHorizontalPartOfLeg(Graphics g, Rectangle clipBounds, Insets insets, Rectangle bounds, TreePath path, int row, - boolean isExpanded, boolean hasBeenExpanded, + boolean isExpanded, + boolean hasBeenExpanded, boolean isLeaf) { if (row != 0) - paintHorizontalLine(g, tree, bounds.y + bounds.height/2, bounds.x - gap - 2, - bounds.x); + paintHorizontalLine(g, tree, bounds.y + bounds.height / 2, bounds.x - gap + - 2, bounds.x); } - + /** - * Paints the vertical part of the leg. The receiver should NOT modify + * Paints the vertical part of the leg. The receiver should NOT modify * clipBounds, insets. * - * @param g - the graphics configuration. - * @param clipBounds - - * @param insets - - * @param path - the path to draw the vertical part for. + * @param g - + * the graphics configuration. + * @param clipBounds - + * @param insets - + * @param path - + * the path to draw the vertical part for. */ protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds, Insets insets, TreePath path) @@ -3564,56 +3594,65 @@ public class BasicTreeUI extends TreeUI if (numChild > 0 && tree.isExpanded(currPath)) { Rectangle bounds = getPathBounds(tree, currPath); - Rectangle lastChildBounds = getPathBounds(tree, - new TreePath(getPathToRoot( - treeModel.getChild(curr, numChild - 1), - 0))); - paintVerticalLine(g, tree, bounds.x + gap + 2, bounds.y + - bounds.height - 2, lastChildBounds.y + - lastChildBounds.height/2); + Rectangle lastChildBounds = getPathBounds( + tree, + new TreePath( + getPathToRoot( + treeModel.getChild( + curr, + numChild - 1), + 0))); + paintVerticalLine(g, tree, bounds.x + gap + 2, bounds.y + + bounds.height - 2, + lastChildBounds.y + lastChildBounds.height / 2); } } } /** - * Paints the renderer part of a row. The receiver should NOT modify clipBounds, - * or insets. + * Paints the renderer part of a row. The receiver should NOT modify + * clipBounds, or insets. * - * @param g - the graphics configuration - * @param clipBounds - - * @param insets - - * @param bounds - bounds of expand control - * @param path - path to draw control for - * @param row - row to draw control for - * @param isExpanded - is the row expanded - * @param hasBeenExpanded - has the row already been expanded - * @param isLeaf - is the path a leaf - */ - protected void paintRow(Graphics g, Rectangle clipBounds, - Insets insets, Rectangle bounds, - TreePath path, int row, + * @param g - + * the graphics configuration + * @param clipBounds - + * @param insets - + * @param bounds - + * bounds of expand control + * @param path - + * path to draw control for + * @param row - + * row to draw control for + * @param isExpanded - + * is the row expanded + * @param hasBeenExpanded - + * has the row already been expanded + * @param isLeaf - + * is the path a leaf + */ + protected void paintRow(Graphics g, Rectangle clipBounds, Insets insets, + Rectangle bounds, TreePath path, int row, boolean isExpanded, boolean hasBeenExpanded, boolean isLeaf) { boolean selected = tree.isPathSelected(path); boolean hasIcons = false; Object node = path.getLastPathComponent(); - + if (tree.isVisible(path)) { if (!validCachedPreferredSize) updateCachedPreferredSize(); - - - paintExpandControl(g, clipBounds, insets, bounds, path, row, isExpanded, hasBeenExpanded, isLeaf); - + + paintExpandControl(g, clipBounds, insets, bounds, path, row, + isExpanded, hasBeenExpanded, isLeaf); + if (row != 0) bounds.x += gap; bounds.width = preferredSize.width + bounds.x; - if (editingComponent != null && editingPath != null && isEditing(tree) && node.equals(editingPath.getLastPathComponent())) - { + { rendererPane.paintComponent(g, editingComponent.getParent(), null, bounds); } @@ -3622,9 +3661,12 @@ public class BasicTreeUI extends TreeUI TreeCellRenderer dtcr = tree.getCellRenderer(); if (dtcr == null) dtcr = createDefaultCellRenderer(); - + Component c = dtcr.getTreeCellRendererComponent(tree, node, - selected, isExpanded, isLeaf, row, tree.hasFocus()); + selected, + isExpanded, isLeaf, + row, + tree.hasFocus()); rendererPane.paintComponent(g, c, c.getParent(), bounds); } } @@ -3637,16 +3679,21 @@ public class BasicTreeUI extends TreeUI { // TODO: Implement this properly. } - + /** * Returns true if the expand (toggle) control should be drawn for the * specified row. * - * @param path - current path to check for. - * @param row - current row to check for. - * @param isExpanded - true if the path is expanded - * @param hasBeenExpanded - true if the path has been expanded already - * @param isLeaf - true if the row is a lead + * @param path - + * current path to check for. + * @param row - + * current row to check for. + * @param isExpanded - + * true if the path is expanded + * @param hasBeenExpanded - + * true if the path has been expanded already + * @param isLeaf - + * true if the row is a lead */ protected boolean shouldPaintExpandControl(TreePath path, int row, boolean isExpanded, @@ -3656,10 +3703,9 @@ public class BasicTreeUI extends TreeUI Object node = path.getLastPathComponent(); return (!isLeaf && getLevel(node) != 0 && hasControlIcons()); } - + /** - * Updates the cached current TreePath of all visible - * nodes in the tree. + * Updates the cached current TreePath of all visible nodes in the tree. */ void updateCurrentVisiblePath() { @@ -3667,19 +3713,22 @@ public class BasicTreeUI extends TreeUI return; Object next = treeModel.getRoot(); + if (next == null) + return; + TreePath rootPath = new TreePath(next); Rectangle bounds = getPathBounds(tree, rootPath); - + // If root is not a valid size to be visible, or is // not visible and the tree is expanded, then the next node acts // as the root - if ((bounds.width == 0 && bounds.height == 0) || (!isRootVisible() - && tree.isExpanded(new TreePath(next)))) + if ((bounds.width == 0 && bounds.height == 0) + || (!isRootVisible() && tree.isExpanded(new TreePath(next)))) { next = getNextNode(next); rootPath = new TreePath(next); } - + Object root = next; TreePath current = null; while (next != null) @@ -3688,7 +3737,7 @@ public class BasicTreeUI extends TreeUI current = rootPath; else current = current.pathByAddingChild(next); - + do { TreePath path = new TreePath(getPathToRoot(next, 0)); @@ -3712,19 +3761,23 @@ public class BasicTreeUI extends TreeUI } } } - while (next != null && - !tree.isVisible(new TreePath(getPathToRoot(next, 0)))); + while (next != null + && !tree.isVisible(new TreePath(getPathToRoot(next, 0)))); } currentVisiblePath = current; tree.setVisibleRowCount(getRowCount(tree)); - if (tree.getSelectionModel() != null && tree.getSelectionCount() == 0 && - currentVisiblePath != null) - selectPath(tree, new TreePath(getPathToRoot(currentVisiblePath. - getPathComponent(0), 0))); + if (tree.getSelectionModel() != null && tree.getSelectionCount() == 0 + && currentVisiblePath != null) + selectPath( + tree, + new TreePath( + getPathToRoot( + currentVisiblePath.getPathComponent(0), + 0))); } - + /** * Get next visible node in the currentVisiblePath. Package private for use in * inner classes. @@ -3743,8 +3796,8 @@ public class BasicTreeUI extends TreeUI while (i < nodes.length && !node.equals(nodes[i])) i++; // return the next node - if (i+1 < nodes.length) - return nodes[i+1]; + if (i + 1 < nodes.length) + return nodes[i + 1]; } return null; } diff --git a/javax/swing/plaf/metal/MetalFileChooserUI.java b/javax/swing/plaf/metal/MetalFileChooserUI.java index d6ede95c4..65e54cf42 100644 --- a/javax/swing/plaf/metal/MetalFileChooserUI.java +++ b/javax/swing/plaf/metal/MetalFileChooserUI.java @@ -48,8 +48,11 @@ import java.awt.LayoutManager; import java.awt.Rectangle; import java.awt.Window; import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.text.NumberFormat; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; @@ -73,8 +76,11 @@ import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; +import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.JToggleButton; +import javax.swing.JViewport; +import javax.swing.ListModel; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; import javax.swing.UIManager; @@ -85,6 +91,12 @@ import javax.swing.filechooser.FileSystemView; import javax.swing.filechooser.FileView; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicFileChooserUI; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.DefaultTableModel; + +import java.sql.Date; + +import java.text.DateFormat; import java.util.List; @@ -98,6 +110,145 @@ public class MetalFileChooserUI { /** + * A renderer for the files and directories in the file chooser table. + */ + class TableFileRenderer + extends DefaultTableCellRenderer + { + + /** + * Creates a new renderer. + */ + public TableFileRenderer() + { + super(); + } + + /** + * Returns a component that can render the specified value. + * + * @param table the table + * @param value the string value of the cell + * @param isSelected is the item selected? + * @param hasFocus does the item have the focus? + * @param row the row + * @param column the column + * + * @return The renderer. + */ + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, int row, int column) + { + if (column == 0) + { + FileView v = getFileView(getFileChooser()); + ListModel lm = fileList.getModel(); + if (row < lm.getSize()) + setIcon(v.getIcon((File) lm.getElementAt(row))); + } + else + setIcon(null); + + setText(value.toString()); + setOpaque(true); + setEnabled(table.isEnabled()); + setFont(fileList.getFont()); + + if (startEditing && column == 0 || !isSelected) + { + setBackground(table.getBackground()); + setForeground(table.getForeground()); + } + else + { + setBackground(table.getSelectionBackground()); + setForeground(table.getSelectionForeground()); + } + + if (hasFocus) + setBorder(UIManager.getBorder("Table.focusCellHighlightBorder")); + else + setBorder(noFocusBorder); + + return this; + } + } + + /** + * ActionListener for the list view. + */ + class ListViewActionListener implements ActionListener + { + + /** + * This method is invoked when an action occurs. + * + * @param e - + * the <code>ActionEvent</code> that occurred + */ + public void actionPerformed(ActionEvent e) + { + if (!listView) + { + int[] index = fileTable.getSelectedRows(); + listView = true; + JFileChooser fc = getFileChooser(); + fc.remove(fileTablePanel); + createList(fc); + + fileList.getSelectionModel().clearSelection(); + if (index.length > 0) + for (int i = 0; i < index.length; i++) + fileList.getSelectionModel().addSelectionInterval(index[i], index[i]); + + fc.add(fileListPanel, BorderLayout.CENTER); + fc.revalidate(); + fc.repaint(); + } + } + } + + /** + * ActionListener for the details view. + */ + class DetailViewActionListener implements ActionListener + { + + /** + * This method is invoked when an action occurs. + * + * @param e - + * the <code>ActionEvent</code> that occurred + */ + public void actionPerformed(ActionEvent e) + { + if (listView) + { + int[] index = fileList.getSelectedIndices(); + JFileChooser fc = getFileChooser(); + listView = false; + fc.remove(fileListPanel); + + if (fileTable == null) + createDetailsView(fc); + else + updateTable(); + + fileTable.getSelectionModel().clearSelection(); + if (index.length > 0) + { + for (int i = 0; i < index.length; i++) + fileTable.getSelectionModel().addSelectionInterval(index[i], index[i]); + } + + fc.add(fileTablePanel, BorderLayout.CENTER); + fc.revalidate(); + fc.repaint(); + } + } + } + + /** * A property change listener. */ class MetalFileChooserPropertyChangeListener @@ -122,14 +273,40 @@ public class MetalFileChooserUI String n = e.getPropertyName(); if (n.equals(JFileChooser.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY)) { + int mode = -1; if (filechooser.isMultiSelectionEnabled()) - fileList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + mode = ListSelectionModel.MULTIPLE_INTERVAL_SELECTION; else - fileList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + mode = ListSelectionModel.SINGLE_SELECTION; + + if (listView) + fileList.setSelectionMode(mode); + else + fileTable.setSelectionMode(mode); } else if (n.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) { File file = filechooser.getSelectedFile(); + + if (file != null + && filechooser.getDialogType() == JFileChooser.SAVE_DIALOG) + { + if (file.isDirectory() && filechooser.isTraversable(file)) + { + directoryLabel = look; + dirLabel.setText(directoryLabel); + filechooser.setApproveButtonText(openButtonText); + filechooser.setApproveButtonToolTipText(openButtonToolTipText); + } + else if (file.isFile()) + { + directoryLabel = save; + dirLabel.setText(directoryLabel); + filechooser.setApproveButtonText(saveButtonText); + filechooser.setApproveButtonToolTipText(saveButtonToolTipText); + } + } + if (file == null) setFileName(null); else @@ -138,18 +315,38 @@ public class MetalFileChooserUI index = getModel().indexOf(file); if (index >= 0) { - fileList.setSelectedIndex(index); - fileList.ensureIndexIsVisible(index); - fileList.revalidate(); - fileList.repaint(); + if (listView) + { + fileList.setSelectedIndex(index); + fileList.ensureIndexIsVisible(index); + fileList.revalidate(); + fileList.repaint(); + } + else + { + fileTable.getSelectionModel().addSelectionInterval(index, index); + fileTable.scrollRectToVisible(fileTable.getCellRect(index, 0, true)); + fileTable.revalidate(); + fileTable.repaint(); + } } } else if (n.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)) { - fileList.clearSelection(); - fileList.revalidate(); - fileList.repaint(); + if (listView) + { + fileList.clearSelection(); + fileList.revalidate(); + fileList.repaint(); + } + else + { + fileTable.clearSelection(); + fileTable.revalidate(); + fileTable.repaint(); + } + setDirectorySelected(false); File currentDirectory = filechooser.getCurrentDirectory(); setDirectory(currentDirectory); @@ -225,7 +422,25 @@ public class MetalFileChooserUI if (n.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY) || n.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY) || n.equals(JFileChooser.FILE_HIDING_CHANGED_PROPERTY)) - rescanCurrentDirectory(filechooser); + { + // Remove editing component + if (fileTable != null) + fileTable.removeAll(); + if (fileList != null) + fileList.removeAll(); + startEditing = false; + + // Set text on button back to original. + if (filechooser.getDialogType() == JFileChooser.SAVE_DIALOG) + { + directoryLabel = save; + dirLabel.setText(directoryLabel); + filechooser.setApproveButtonText(saveButtonText); + filechooser.setApproveButtonToolTipText(saveButtonToolTipText); + } + + rescanCurrentDirectory(filechooser); + } filechooser.revalidate(); filechooser.repaint(); @@ -665,6 +880,7 @@ public class MetalFileChooserUI editFile = null; fc = getFileChooser(); lastSelected = null; + startEditing = false; } /** @@ -674,25 +890,23 @@ public class MetalFileChooserUI */ public void mouseClicked(MouseEvent e) { - if (e.getClickCount() == 1) + if (e.getClickCount() == 1 && e.getButton() == MouseEvent.BUTTON1) { int index = list.locationToIndex(e.getPoint()); File[] sf = fc.getSelectedFiles(); if ((!fc.isMultiSelectionEnabled() || (sf != null && sf.length <= 1)) - && index >= 0 && editFile == null && list.isSelectedIndex(index)) + && index >= 0 && !startEditing && list.isSelectedIndex(index)) { Object tmp = list.getModel().getElementAt(index); if (lastSelected != null && lastSelected.equals(tmp)) editFile(index); lastSelected = tmp; } - else if (editFile != null) - { + else completeEditing(); - editFile = null; - lastSelected = null; - } } + else + completeEditing(); } /** @@ -701,50 +915,289 @@ public class MetalFileChooserUI * @param index - * the current index of the item in the list to be edited. */ - private void editFile(int index) + void editFile(int index) { - list.ensureIndexIsVisible(index); + Rectangle bounds = list.getCellBounds(index, index); + list.scrollRectToVisible(bounds); editFile = (File) list.getModel().getElementAt(index); if (editFile.canWrite()) { - Rectangle bounds = list.getCellBounds(index, index); - Icon icon = getFileView(fc).getIcon(editFile); + startEditing = true; editField = new JTextField(editFile.getName()); - // FIXME: add action listener for typing - // FIXME: painting for textfield is messed up when typing - list.add(editField); - editField.requestFocus(); - editField.selectAll(); + editField.addActionListener(new EditingActionListener()); + Icon icon = getFileView(fc).getIcon(editFile); if (icon != null) - bounds.x += icon.getIconWidth() + 4; + { + int padding = icon.getIconWidth() + 4; + bounds.x += padding; + bounds.width -= padding; + } editField.setBounds(bounds); + + list.add(editField); + + editField.requestFocus(); + editField.selectAll(); } else - { - editField = null; - editFile = null; - lastSelected = null; - } + completeEditing(); + list.repaint(); } /** * Completes the editing. */ - private void completeEditing() + void completeEditing() { - if (editField != null) + if (editField != null && editFile != null) { String text = editField.getText(); - if (text != null && !text.equals("")) - editFile.renameTo(new File(text)); + if (text != null && text != "" && !text.equals(fc.getName(editFile))) + if (editFile.renameTo + (fc.getFileSystemView().createFileObject + (fc.getCurrentDirectory(), text))) + rescanCurrentDirectory(fc); list.remove(editField); - list.revalidate(); - list.repaint(); } + startEditing = false; + editFile = null; + lastSelected = null; + editField = null; + list.repaint(); + } + + /** + * ActionListener for the editing text field. + */ + class EditingActionListener implements ActionListener + { + + /** + * This method is invoked when an action occurs. + * + * @param e - + * the <code>ActionEvent</code> that occurred + */ + public void actionPerformed(ActionEvent e) + { + if (e.getActionCommand().equals("notify-field-accept")) + completeEditing(); + else if (editField != null) + { + list.remove(editField); + startEditing = false; + editFile = null; + lastSelected = null; + editField = null; + list.repaint(); + } + } } } + /** + * A mouse listener for the {@link JFileChooser}. + * This listener is used for the table + */ + private class TableClickListener extends MouseAdapter + { + + /** Stores instance of the table */ + JTable table; + + /** Stores instance of the file chooser */ + JFileChooser fc; + + /** The last selected file. */ + Object lastSelected = null; + + /** + * Stores the current file that is being edited. + * It is null if nothing is currently being edited. + */ + File editFile; + + /** The textfield used for editing. */ + JTextField editField; + + /** + * Creates a new listener. + * + * @param table + * the directory/file table + * @param fc + * the JFileChooser + */ + public TableClickListener(JTable table, JFileChooser fc) + { + this.table = table; + this.fc = fc; + lastSelected = fileList.getSelectedValue(); + setDirectorySelected(false); + startEditing = false; + editFile = null; + editField = null; + } + + /** + * Receives notification of a mouse click event. + * + * @param e + * the event. + */ + public void mouseClicked(MouseEvent e) + { + int row = table.getSelectedRow(); + Object selVal = fileList.getModel().getElementAt(row); + if (selVal == null) + return; + FileSystemView fsv = fc.getFileSystemView(); + if (e.getClickCount() == 1 && + selVal.equals(lastSelected) && + e.getButton() == MouseEvent.BUTTON1) + { + File[] sf = fc.getSelectedFiles(); + if ((!fc.isMultiSelectionEnabled() || (sf != null && sf.length <= 1)) + && !startEditing) + { + editFile = (File) selVal; + editFile(row); + } + } + else if (e.getClickCount() >= 2 && + selVal.equals(lastSelected)) + { + if (startEditing) + completeEditing(); + File f = fsv.createFileObject(lastSelected.toString()); + if (fc.isTraversable(f)) + { + fc.setCurrentDirectory(f); + fc.rescanCurrentDirectory(); + } + else + { + fc.setSelectedFile(f); + fc.approveSelection(); + closeDialog(); + } + } + else + { + if (startEditing) + completeEditing(); + String path = selVal.toString(); + File f = fsv.createFileObject(path); + fc.setSelectedFile(f); + if (fc.isTraversable(f)) + { + setDirectorySelected(true); + setDirectory(f); + } + else + { + setDirectorySelected(false); + setDirectory(null); + } + lastSelected = selVal; + if (f.isFile()) + setFileName(path.substring(path.lastIndexOf("/") + 1)); + else if (fc.getFileSelectionMode() == JFileChooser.DIRECTORIES_ONLY) + setFileName(path); + } + fileTable.repaint(); + } + + /** + * Sets up the text editor for the current file. + * + * @param row - + * the current row of the item in the list to be edited. + */ + void editFile(int row) + { + Rectangle bounds = table.getCellRect(row, 0, true); + table.scrollRectToVisible(bounds); + if (editFile.canWrite()) + { + startEditing = true; + editField = new JTextField(editFile.getName()); + editField.addActionListener(new EditingActionListener()); + + // Need to adjust y pos + bounds.y = row * table.getRowHeight(); + editField.setBounds(bounds); + + table.add(editField); + + editField.requestFocus(); + editField.selectAll(); + } + else + completeEditing(); + table.repaint(); + } + + /** + * Completes the editing. + */ + void completeEditing() + { + if (editField != null && editFile != null) + { + String text = editField.getText(); + if (text != null && text != "" && !text.equals(fc.getName(editFile))) + if (editFile.renameTo + (fc.getFileSystemView().createFileObject + (fc.getCurrentDirectory(), text))) + rescanCurrentDirectory(fc); + table.remove(editField); + } + startEditing = false; + editFile = null; + editField = null; + table.repaint(); + } + + /** + * ActionListener for the editing text field. + */ + class EditingActionListener implements ActionListener + { + + /** + * This method is invoked when an action occurs. + * + * @param e - + * the <code>ActionEvent</code> that occurred + */ + public void actionPerformed(ActionEvent e) + { + if (e.getActionCommand().equals("notify-field-accept")) + completeEditing(); + else if (editField != null) + { + table.remove(editField); + startEditing = false; + editFile = null; + editField = null; + table.repaint(); + } + } + } + + /** + * Closes the dialog. + */ + public void closeDialog() + { + Window owner = SwingUtilities.windowForComponent(fc); + if (owner instanceof JDialog) + ((JDialog) owner).dispose(); + } + } + /** The text for a label describing the directory combo box. */ private String directoryLabel; @@ -783,10 +1236,16 @@ public class MetalFileChooserUI private JButton approveButton; /** The file list. */ - private JList fileList; + JList fileList; + + /** The file table. */ + JTable fileTable; /** The panel containing the file list. */ - private JPanel fileListPanel; + JPanel fileListPanel; + + /** The panel containing the file table. */ + JPanel fileTablePanel; /** The filter combo box model. */ private FilterComboBoxModel filterModel; @@ -794,6 +1253,30 @@ public class MetalFileChooserUI /** The action map. */ private ActionMap actionMap; + /** True if currently in list view. */ + boolean listView; + + /** True if we can or have started editing a cell. */ + boolean startEditing; + + /** The scrollpane used for the table and list. */ + JScrollPane scrollPane; + + /** The text for the label when saving. */ + String save; + + /** The text for the label when opening a directory. */ + String look; + + /** The label for the file combo box. */ + JLabel dirLabel; + + /** Listeners. */ + ListSelectionListener listSelList; + MouseListener doubleClickList; + SingleClickListener singleClickList; + TableClickListener tableClickList; + /** * A factory method that returns a UI delegate for the specified * component. @@ -839,21 +1322,25 @@ public class MetalFileChooserUI { fc.setLayout(new BorderLayout()); topPanel = new JPanel(new BorderLayout()); - topPanel.add(new JLabel(directoryLabel), BorderLayout.WEST); + dirLabel = new JLabel(directoryLabel); + topPanel.add(dirLabel, BorderLayout.WEST); this.controls = new JPanel(); addControlButtons(); JPanel dirPanel = new JPanel(new VerticalMidLayout()); - dirPanel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0)); directoryModel = createDirectoryComboBoxModel(fc); directoryComboBox = new JComboBox(directoryModel); directoryComboBox.setRenderer(createDirectoryComboBoxRenderer(fc)); dirPanel.add(directoryComboBox); topPanel.add(dirPanel); topPanel.add(controls, BorderLayout.EAST); + topPanel.setBorder(BorderFactory.createEmptyBorder(8, 8, 0, 8)); fc.add(topPanel, BorderLayout.NORTH); - fileListPanel = createList(fc); - fc.add(fileListPanel); + + JPanel list = createList(fc); + list.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); + fc.add(list, BorderLayout.CENTER); + JPanel bottomPanel = getBottomPanel(); filterModel = createFilterComboBoxModel(); JComboBox fileFilterCombo = new JComboBox(filterModel); @@ -861,34 +1348,37 @@ public class MetalFileChooserUI fileTextField = new JTextField(); JPanel fileNamePanel = new JPanel(new VerticalMidLayout()); + fileNamePanel.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 5)); fileNamePanel.add(fileTextField); JPanel row1 = new JPanel(new BorderLayout()); row1.add(new JLabel(this.fileLabel), BorderLayout.WEST); row1.add(fileNamePanel); bottomPanel.add(row1); - JPanel filterPanel = new JPanel(new VerticalMidLayout()); - filterPanel.add(fileFilterCombo); JPanel row2 = new JPanel(new BorderLayout()); row2.add(new JLabel(this.filterLabel), BorderLayout.WEST); - row2.add(filterPanel); + row2.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5)); + row2.add(fileFilterCombo); bottomPanel.add(row2); JPanel buttonPanel = new JPanel(new ButtonLayout()); - buttonPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 0)); approveButton = new JButton(getApproveSelectionAction()); approveButton.setText(getApproveButtonText(fc)); approveButton.setToolTipText(getApproveButtonToolTipText(fc)); approveButton.setMnemonic(getApproveButtonMnemonic(fc)); buttonPanel.add(approveButton); + buttonPanel.setBorder(BorderFactory.createEmptyBorder(8, 0, 0, 0)); JButton cancelButton = new JButton(getCancelSelectionAction()); cancelButton.setText(cancelButtonText); cancelButton.setToolTipText(cancelButtonToolTipText); cancelButton.setMnemonic(cancelButtonMnemonic); buttonPanel.add(cancelButton); - bottomPanel.add(buttonPanel); + bottomPanel.add(buttonPanel, BorderLayout.SOUTH); + bottomPanel.setBorder(BorderFactory.createEmptyBorder(0, 8, 8, 8)); fc.add(bottomPanel, BorderLayout.SOUTH); + + fc.add(getAccessoryPanel(), BorderLayout.EAST); } /** @@ -902,6 +1392,8 @@ public class MetalFileChooserUI fc.remove(bottomPanel); bottomPanel = null; fc.remove(fileListPanel); + fc.remove(fileTablePanel); + fileTablePanel = null; fileListPanel = null; fc.remove(topPanel); topPanel = null; @@ -944,7 +1436,13 @@ public class MetalFileChooserUI protected void installStrings(JFileChooser fc) { super.installStrings(fc); - directoryLabel = "Look In: "; + look = "Look In: "; + save = "Save In: "; + if (fc.getDialogType() == JFileChooser.SAVE_DIALOG) + directoryLabel = save; + else + directoryLabel = look; + fileLabel = "File Name: "; filterLabel = "Files of Type: "; @@ -981,10 +1479,13 @@ public class MetalFileChooserUI protected void installListeners(JFileChooser fc) { directoryComboBox.setAction(new DirectoryComboBoxAction()); - fileList.addListSelectionListener(createListSelectionListener(fc)); - fileList.addMouseListener(this.createDoubleClickListener(fc, fileList)); - fileList.addMouseListener(new SingleClickListener(fileList)); fc.addPropertyChangeListener(filterModel); + listSelList = createListSelectionListener(fc); + doubleClickList = this.createDoubleClickListener(fc, fileList); + singleClickList = new SingleClickListener(fileList); + fileList.addListSelectionListener(listSelList); + fileList.addMouseListener(doubleClickList); + fileList.addMouseListener(singleClickList); super.installListeners(fc); } @@ -992,6 +1493,13 @@ public class MetalFileChooserUI { super.uninstallListeners(fc); fc.removePropertyChangeListener(filterModel); + directoryComboBox.setAction(null); + fileList.removeListSelectionListener(listSelList); + fileList.removeMouseListener(doubleClickList); + fileList.removeMouseListener(singleClickList); + + if (fileTable != null) + fileTable.removeMouseListener(tableClickList); } protected ActionMap getActionMap() @@ -1010,7 +1518,7 @@ public class MetalFileChooserUI { ActionMap map = new ActionMap(); map.put("approveSelection", getApproveSelectionAction()); - map.put("cancelSelection", null); // FIXME: implement this one + map.put("cancelSelection", getCancelSelectionAction()); map.put("Go Up", getChangeToParentDirectoryAction()); return map; } @@ -1024,13 +1532,25 @@ public class MetalFileChooserUI */ protected JPanel createList(JFileChooser fc) { - JPanel panel = new JPanel(new BorderLayout()); - fileList = new JList(getModel()); - fileList.setLayoutOrientation(JList.VERTICAL_WRAP); - fileList.setVisibleRowCount(0); - fileList.setCellRenderer(new FileRenderer()); - panel.add(new JScrollPane(fileList)); - return panel; + if (fileList == null) + { + fileListPanel = new JPanel(new BorderLayout()); + fileList = new JList(getModel()); + scrollPane = new JScrollPane(fileList); + scrollPane.setVerticalScrollBarPolicy + (JScrollPane.VERTICAL_SCROLLBAR_NEVER); + fileList.setLayoutOrientation(JList.VERTICAL_WRAP); + fileList.setCellRenderer(new FileRenderer()); + } + else + { + fileList.setModel(getModel()); + fileListPanel.removeAll(); + scrollPane.getViewport().setView(fileList); + } + fileListPanel.add(scrollPane); + + return fileListPanel; } /** @@ -1042,13 +1562,108 @@ public class MetalFileChooserUI */ protected JPanel createDetailsView(JFileChooser fc) { - // FIXME: implement this. The details view is a panel containing a table - // inside a JScrollPane - it gets displayed when the user clicks on the - // "details" button. - return new JPanel(); + fileTablePanel = new JPanel(new BorderLayout()); + + Object[] cols = new Object[] {"Name", "Size", "Modified"}; + Object[][] rows = new Object[fileList.getModel().getSize()][3]; + + fileTable = new JTable(new DefaultTableModel(rows, cols)); + + if (fc.isMultiSelectionEnabled()) + fileTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + else + fileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + fileTable.setShowGrid(false); + fileTable.setColumnSelectionAllowed(false); + fileTable.setDefaultRenderer(Object.class, new TableFileRenderer()); + + tableClickList = new TableClickListener(fileTable, fc); + fileTable.addMouseListener(tableClickList); + + return updateTable(); + } + + /** + * Sets the values in the table, and puts it in the panel. + * + * @return the panel containing the table. + */ + JPanel updateTable() + { + DefaultTableModel mod = (DefaultTableModel) fileTable.getModel(); + ListModel lm = fileList.getModel(); + DateFormat dt = DateFormat.getDateTimeInstance(DateFormat.SHORT, + DateFormat.SHORT); + File curr = null; + int size = lm.getSize(); + int rc = mod.getRowCount(); + + // If there are not enough rows + for (int x = rc; x < size; x++) + mod.addRow(new Object[3]); + + for (int i = 0; i < size; i++) + { + curr = (File) lm.getElementAt(i); + fileTable.setValueAt(curr.getName(), i, 0); + fileTable.setValueAt(formatSize(curr.length()), i, 1); + fileTable.setValueAt(dt.format(new Date(curr.lastModified())), i, 2); + } + + // If there are too many rows + while (rc > size) + mod.removeRow(--rc); + + scrollPane.getViewport().setView(fileTable); + scrollPane.setColumnHeaderView(fileTable.getTableHeader()); + + fileTablePanel.removeAll(); + fileTablePanel.add(scrollPane); + + return fileTablePanel; } /** + * Formats bytes into the appropriate size. + * + * @param bytes - + * the number of bytes to convert + * @return a string representation of the size + */ + private String formatSize(long bytes) + { + NumberFormat nf = NumberFormat.getNumberInstance(); + long mb = (long) Math.pow(2, 20); + long kb = (long) Math.pow(2, 10); + long gb = (long) Math.pow(2, 30); + double size = 0; + String id = ""; + + if ((bytes / gb) >= 1) + { + size = (double) bytes / (double) gb; + id = "GB"; + } + else if ((bytes / mb) >= 1) + { + size = (double) bytes / (double) mb; + id = "MB"; + } + else if ((bytes / kb) >= 1) + { + size = (double) bytes / (double) kb; + id = "KB"; + } + else + { + size = bytes; + id = "Bytes"; + } + + return nf.format(size) + " " + id; + } + /** * Creates a listener that monitors selections in the directory/file list * and keeps the {@link JFileChooser} component up to date. * @@ -1070,10 +1685,12 @@ public class MetalFileChooserUI */ public Dimension getPreferredSize(JComponent c) { - // FIXME: not likely to be a fixed value - return new Dimension(500, 326); + Dimension tp = topPanel.getPreferredSize(); + Dimension bp = bottomPanel.getPreferredSize(); + Dimension fl = fileListPanel.getPreferredSize(); + return new Dimension(fl.width, tp.height + bp.height + fl.height); } - + /** * Returns the minimum size for the file chooser component. * @@ -1081,8 +1698,10 @@ public class MetalFileChooserUI */ public Dimension getMinimumSize(JComponent c) { - // FIXME: not likely to be a fixed value - return new Dimension(506, 326); + Dimension tp = topPanel.getMinimumSize(); + Dimension bp = bottomPanel.getMinimumSize(); + Dimension fl = fileListPanel.getMinimumSize(); + return new Dimension(fl.width, tp.height + bp.height + fl.height); } /** @@ -1179,15 +1798,17 @@ public class MetalFileChooserUI newFolderButton.setMargin(new Insets(0, 0, 0, 0)); controls.add(newFolderButton); - JToggleButton listButton = new JToggleButton(); - listButton.setIcon(this.listViewIcon); + JToggleButton listButton = new JToggleButton(this.listViewIcon); listButton.setMargin(new Insets(0, 0, 0, 0)); - // FIXME: this button needs an action that handles a click + listButton.addActionListener(new ListViewActionListener()); + listButton.setSelected(true); + listView = true; controls.add(listButton); JToggleButton detailButton = new JToggleButton(this.detailsViewIcon); detailButton.setMargin(new Insets(0, 0, 0, 0)); - // FIXME: this button needs an action that handles a click + detailButton.addActionListener(new DetailViewActionListener()); + detailButton.setSelected(false); controls.add(detailButton); ButtonGroup buttonGroup = new ButtonGroup(); @@ -1195,26 +1816,29 @@ public class MetalFileChooserUI buttonGroup.add(detailButton); } + /** + * Removes all the buttons from the control panel. + */ protected void removeControlButtons() { controls.removeAll(); + controls.revalidate(); + controls.repaint(); } - public void ensureFileIsVisible(JFileChooser fc, File f) - { - // FIXME: do something here - probably this figures out whether the - // list or table view is current, and forwards the request to the - // appropriate one... - super.ensureFileIsVisible(fc, f); - } - + /** + * Updates the current directory. + * + * @param the file chooser to update. + */ public void rescanCurrentDirectory(JFileChooser fc) { - // FIXME: this will need to take into account whether the list view or - // the table view is current directoryModel.setSelectedItem(fc.getCurrentDirectory()); getModel().validateFileCache(); - fileList.revalidate(); + if (!listView) + updateTable(); + else + createList(fc); } /** @@ -1239,28 +1863,15 @@ public class MetalFileChooserUI { fileTextField.setText(filename); } - - protected void setDirectorySelected(boolean directorySelected) - { - // FIXME: do something here - super.setDirectorySelected(directorySelected); - } - - public String getDirectoryName() - { - // FIXME: do something here - return super.getDirectoryName(); - } - - public void setDirectoryName(String dirname) - { - // FIXME: do something here - super.setDirectoryName(dirname); - } + /** + * DOCUMENT ME!! + * + * @param e - DOCUMENT ME! + */ public void valueChanged(ListSelectionEvent e) { - // FIXME: implement + // FIXME: Not sure what we should be doing here, if anything. } /** diff --git a/javax/swing/plaf/metal/MetalLookAndFeel.java b/javax/swing/plaf/metal/MetalLookAndFeel.java index 30dc6c8c6..5acccd6e4 100644 --- a/javax/swing/plaf/metal/MetalLookAndFeel.java +++ b/javax/swing/plaf/metal/MetalLookAndFeel.java @@ -81,8 +81,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public MetalLookAndFeel() { - if (theme == null) - createDefaultTheme(); + createDefaultTheme(); } /** @@ -90,7 +89,8 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ protected void createDefaultTheme() { - setCurrentTheme(new DefaultMetalTheme()); + if (theme == null) + setCurrentTheme(new DefaultMetalTheme()); } /** @@ -124,7 +124,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public String getDescription() { - return "Metal look and feel"; + return "The Java(tm) Look and Feel"; } /** @@ -134,7 +134,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public String getID() { - return "MetalLookAndFeel"; + return "Metal"; } /** @@ -144,7 +144,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public String getName() { - return "MetalLookAndFeel"; + return "Metal"; } public UIDefaults getDefaults() @@ -154,7 +154,8 @@ public class MetalLookAndFeel extends BasicLookAndFeel LAF_defaults = super.getDefaults(); // add custom theme entries to the table - theme.addCustomEntriesToTable(LAF_defaults); + if (theme != null) + theme.addCustomEntriesToTable(LAF_defaults); } // Returns the default values for this look and feel. @@ -168,7 +169,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getAcceleratorForeground() { - return theme.getAcceleratorForeground(); + if (theme != null) + return theme.getAcceleratorForeground(); + return null; } /** @@ -179,7 +182,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getAcceleratorSelectedForeground() { - return theme.getAcceleratorSelectedForeground(); + if (theme != null) + return theme.getAcceleratorSelectedForeground(); + return null; } /** @@ -189,7 +194,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getBlack() { - return theme.getBlack(); + if (theme != null) + return theme.getBlack(); + return null; } /** @@ -199,7 +206,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getControl() { - return theme.getControl(); + if (theme != null) + return theme.getControl(); + return null; } /** @@ -210,7 +219,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getControlDarkShadow() { - return theme.getControlDarkShadow(); + if (theme != null) + return theme.getControlDarkShadow(); + return null; } /** @@ -220,7 +231,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getControlDisabled() { - return theme.getControlDisabled(); + if (theme != null) + return theme.getControlDisabled(); + return null; } /** @@ -231,7 +244,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getControlHighlight() { - return theme.getControlHighlight(); + if (theme != null) + return theme.getControlHighlight(); + return null; } /** @@ -242,7 +257,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getControlInfo() { - return theme.getControlInfo(); + if (theme != null) + return theme.getControlInfo(); + return null; } /** @@ -253,7 +270,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getControlShadow() { - return theme.getControlShadow(); + if (theme != null) + return theme.getControlShadow(); + return null; } /** @@ -263,7 +282,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getControlTextColor() { - return theme.getControlTextColor(); + if (theme != null) + return theme.getControlTextColor(); + return null; } /** @@ -273,7 +294,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static FontUIResource getControlTextFont() { - return theme.getControlTextFont(); + if (theme != null) + return theme.getControlTextFont(); + return null; } /** @@ -284,7 +307,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getDesktopColor() { - return theme.getDesktopColor(); + if (theme != null) + return theme.getDesktopColor(); + return null; } /** @@ -295,7 +320,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getFocusColor() { - return theme.getFocusColor(); + if (theme != null) + return theme.getFocusColor(); + return null; } /** @@ -306,7 +333,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getHighlightedTextColor() { - return theme.getHighlightedTextColor(); + if (theme != null) + return theme.getHighlightedTextColor(); + return null; } /** @@ -317,7 +346,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getInactiveControlTextColor() { - return theme.getInactiveControlTextColor(); + if (theme != null) + return theme.getInactiveControlTextColor(); + return null; } /** @@ -328,7 +359,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getInactiveSystemTextColor() { - return theme.getInactiveSystemTextColor(); + if (theme != null) + return theme.getInactiveSystemTextColor(); + return null; } /** @@ -340,7 +373,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getMenuBackground() { - return theme.getMenuBackground(); + if (theme != null) + return theme.getMenuBackground(); + return null; } /** @@ -353,7 +388,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getMenuDisabledForeground() { - return theme.getMenuDisabledForeground(); + if (theme != null) + return theme.getMenuDisabledForeground(); + return null; } /** @@ -366,7 +403,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getMenuForeground() { - return theme.getMenuForeground(); + if (theme != null) + return theme.getMenuForeground(); + return null; } /** @@ -379,7 +418,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getMenuSelectedBackground() { - return theme.getMenuSelectedBackground(); + if (theme != null) + return theme.getMenuSelectedBackground(); + return null; } /** @@ -392,7 +433,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getMenuSelectedForeground() { - return theme.getMenuSelectedForeground(); + if (theme != null) + return theme.getMenuSelectedForeground(); + return null; } /** @@ -402,7 +445,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static FontUIResource getMenuTextFont() { - return theme.getMenuTextFont(); + if (theme != null) + return theme.getMenuTextFont(); + return null; } /** @@ -412,7 +457,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getPrimaryControl() { - return theme.getPrimaryControl(); + if (theme != null) + return theme.getPrimaryControl(); + return null; } /** @@ -423,7 +470,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getPrimaryControlDarkShadow() { - return theme.getPrimaryControlDarkShadow(); + if (theme != null) + return theme.getPrimaryControlDarkShadow(); + return null; } /** @@ -434,7 +483,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getPrimaryControlHighlight() { - return theme.getPrimaryControlHighlight(); + if (theme != null) + return theme.getPrimaryControlHighlight(); + return null; } /** @@ -445,7 +496,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getPrimaryControlInfo() { - return theme.getPrimaryControlInfo(); + if (theme != null) + return theme.getPrimaryControlInfo(); + return null; } /** @@ -456,7 +509,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getPrimaryControlShadow() { - return theme.getPrimaryControlShadow(); + if (theme != null) + return theme.getPrimaryControlShadow(); + return null; } /** @@ -466,7 +521,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getSeparatorBackground() { - return theme.getSeparatorBackground(); + if (theme != null) + return theme.getSeparatorBackground(); + return null; } /** @@ -476,7 +533,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getSeparatorForeground() { - return theme.getSeparatorForeground(); + if (theme != null) + return theme.getSeparatorForeground(); + return null; } /** @@ -486,7 +545,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static FontUIResource getSubTextFont() { - return theme.getSubTextFont(); + if (theme != null) + return theme.getSubTextFont(); + return null; } /** @@ -496,7 +557,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getSystemTextColor() { - return theme.getSystemTextColor(); + if (theme != null) + return theme.getSystemTextColor(); + return null; } /** @@ -506,7 +569,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static FontUIResource getSystemTextFont() { - return theme.getSystemTextFont(); + if (theme != null) + return theme.getSystemTextFont(); + return null; } /** @@ -516,7 +581,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getTextHighlightColor() { - return theme.getTextHighlightColor(); + if (theme != null) + return theme.getTextHighlightColor(); + return null; } /** @@ -526,7 +593,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getUserTextColor() { - return theme.getUserTextColor(); + if (theme != null) + return theme.getUserTextColor(); + return null; } /** @@ -536,7 +605,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static FontUIResource getUserTextFont() { - return theme.getUserTextFont(); + if (theme != null) + return theme.getUserTextFont(); + return null; } /** @@ -546,7 +617,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getWhite() { - return theme.getWhite(); + if (theme != null) + return theme.getWhite(); + return null; } /** @@ -556,7 +629,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getWindowBackground() { - return theme.getWindowBackground(); + if (theme != null) + return theme.getWindowBackground(); + return null; } /** @@ -566,7 +641,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getWindowTitleBackground() { - return theme.getWindowTitleBackground(); + if (theme != null) + return theme.getWindowTitleBackground(); + return null; } /** @@ -578,7 +655,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static FontUIResource getWindowTitleFont() { - return theme.getWindowTitleFont(); + if (theme != null) + return theme.getWindowTitleFont(); + return null; } /** @@ -588,7 +667,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getWindowTitleForeground() { - return theme.getWindowTitleForeground(); + if (theme != null) + return theme.getWindowTitleForeground(); + return null; } /** @@ -599,7 +680,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getWindowTitleInactiveBackground() { - return theme.getWindowTitleInactiveBackground(); + if (theme != null) + return theme.getWindowTitleInactiveBackground(); + return null; } /** @@ -610,7 +693,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getWindowTitleInactiveForeground() { - return theme.getWindowTitleInactiveForeground(); + if (theme != null) + return theme.getWindowTitleInactiveForeground(); + return null; } /** @@ -851,7 +936,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel "EditorPane.background", getWindowBackground(), "EditorPane.caretForeground", getUserTextColor(), - "EditorPane.font", new FontUIResource("Dialog", Font.PLAIN, 12), + "EditorPane.font", new FontUIResource("Dialog", Font.BOLD, 12), "EditorPane.foreground", getUserTextColor(), "EditorPane.inactiveForeground", getInactiveSystemTextColor(), "EditorPane.selectionBackground", getTextHighlightColor(), @@ -878,7 +963,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel MetalIconFactory.getFileChooserHomeFolderIcon(), "FileChooser.detailsViewIcon", MetalIconFactory.getFileChooserDetailViewIcon(), - + "FileChooser.fileNameLabelMnemonic", new Integer(78), + "FileChooser.filesOfTypeLabelMnemonic",new Integer(84), + "FileChooser.lookInLabelMnemonic", new Integer(73), "FileView.computerIcon", MetalIconFactory.getTreeComputerIcon(), "FileView.directoryIcon", MetalIconFactory.getTreeFolderIcon(), "FileView.fileIcon", MetalIconFactory.getTreeLeafIcon(), @@ -896,15 +983,20 @@ public class MetalLookAndFeel extends BasicLookAndFeel "InternalFrame.icon", MetalIconFactory.getInternalFrameDefaultMenuIcon(), "InternalFrame.closeIcon", MetalIconFactory.getInternalFrameCloseIcon(16), + "InternalFrame.closeSound", "sounds/FrameClose.wav", "InternalFrame.inactiveTitleBackground", getWindowTitleInactiveBackground(), "InternalFrame.inactiveTitleForeground", getWindowTitleInactiveForeground(), "InternalFrame.maximizeIcon", MetalIconFactory.getInternalFrameMaximizeIcon(16), + "InternalFrame.maximizeSound", "sounds/FrameMaximize.wav", "InternalFrame.iconifyIcon", MetalIconFactory.getInternalFrameMinimizeIcon(16), + "InternalFrame.minimizeSound", "sounds/FrameMinimize.wav", "InternalFrame.paletteBorder", new MetalBorders.PaletteBorder(), "InternalFrame.paletteCloseIcon", new MetalIconFactory.PaletteCloseIcon(), "InternalFrame.paletteTitleHeight", new Integer(11), + "InternalFrame.restoreDownSound", "sounds/FrameRestoreDown.wav", + "InternalFrame.restoreUpSound", "sounds/FrameRestoreUp.wav", "Label.background", getControl(), "Label.disabledForeground", getInactiveSystemTextColor(), @@ -927,12 +1019,14 @@ public class MetalLookAndFeel extends BasicLookAndFeel "Menu.background", getMenuBackground(), "Menu.border", new MetalBorders.MenuItemBorder(), "Menu.borderPainted", Boolean.TRUE, - "Menu.checkIcon", MetalIconFactory.getMenuItemCheckIcon(), + "MenuItem.commandSound", "sounds/MenuItemCommand.wav", "Menu.disabledForeground", getMenuDisabledForeground(), "Menu.font", getControlTextFont(), "Menu.foreground", getMenuForeground(), "Menu.selectionBackground", getMenuSelectedBackground(), "Menu.selectionForeground", getMenuSelectedForeground(), + "Menu.submenuPopupOffsetX", new Integer(-4), + "Menu.submenuPopupOffsetY", new Integer(-3), "MenuBar.background", getMenuBackground(), "MenuBar.border", new MetalBorders.MenuBarBorder(), @@ -941,12 +1035,14 @@ public class MetalLookAndFeel extends BasicLookAndFeel "MenuBar.highlight", getControlHighlight(), "MenuBar.shadow", getControlShadow(), + "MenuItem.acceleratorDelimiter", "-", "MenuItem.acceleratorFont", new FontUIResource("Dialog", Font.PLAIN, 10), "MenuItem.acceleratorForeground", getAcceleratorForeground(), "MenuItem.acceleratorSelectionForeground", getAcceleratorSelectedForeground(), "MenuItem.arrowIcon", MetalIconFactory.getMenuItemArrowIcon(), "MenuItem.background", getMenuBackground(), "MenuItem.border", new MetalBorders.MenuItemBorder(), + "MenuItem.borderPainted", Boolean.TRUE, "MenuItem.disabledForeground", getMenuDisabledForeground(), "MenuItem.font", getControlTextFont(), "MenuItem.foreground", getMenuForeground(), @@ -954,6 +1050,10 @@ public class MetalLookAndFeel extends BasicLookAndFeel "MenuItem.selectionForeground", getMenuSelectedForeground(), "OptionPane.background", getControl(), + "OptionPane.errorSound", "sounds/OptionPaneError.wav", + "OptionPane.informationSound", "sounds/OptionPaneInformation.wav", + "OptionPane.questionSound", "sounds/OptionPaneQuestion.wav", + "OptionPane.warningSound", "sounds/OptionPaneWarning.wav", "OptionPane.errorDialog.border.background", new ColorUIResource(153, 51, 51), "OptionPane.errorDialog.titlePane.background", new ColorUIResource(255, 153, 153), "OptionPane.errorDialog.titlePane.foreground", new ColorUIResource(51, 0, 0), @@ -977,6 +1077,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel new BorderUIResource(MetalBorders.getTextFieldBorder()), "PasswordField.caretForeground", getUserTextColor(), "PasswordField.foreground", getUserTextColor(), + "PasswordField.font", new FontUIResource("Dialog", Font.PLAIN, 12), "PasswordField.inactiveBackground", getControl(), "PasswordField.inactiveForeground", getInactiveSystemTextColor(), "PasswordField.selectionBackground", getTextHighlightColor(), @@ -986,6 +1087,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel "PopupMenu.border", new MetalBorders.PopupMenuBorder(), "PopupMenu.font", new FontUIResource("Dialog", Font.BOLD, 12), "PopupMenu.foreground", getMenuForeground(), + "PopupMenu.popupSound", "sounds/PopupMenuPopup.wav", "ProgressBar.background", getControl(), "ProgressBar.border", new BorderUIResource.LineBorderUIResource(getControlDarkShadow(), 1), @@ -1021,6 +1123,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel "RadioButtonMenuItem.borderPainted", Boolean.TRUE, "RadioButtonMenuItem.checkIcon", MetalIconFactory.getRadioButtonMenuItemIcon(), + "RadioButtonMenuItem.commandSound", "sounds/MenuItemCommand.wav", "RadioButtonMenuItem.disabledForeground", getMenuDisabledForeground(), "RadioButtonMenuItem.font", MetalLookAndFeel.getControlTextFont(), "RadioButtonMenuItem.foreground", getMenuForeground(), @@ -1030,6 +1133,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel "RadioButtonMenuItem.selectionForeground", MetalLookAndFeel.getMenuSelectedForeground(), + "ScrollBar.allowsAbsolutePositioning", Boolean.TRUE, "ScrollBar.background", getControl(), "ScrollBar.darkShadow", getControlDarkShadow(), "ScrollBar.foreground", getControl(), @@ -1065,6 +1169,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel "Slider.verticalThumbIcon", MetalIconFactory.getVerticalSliderThumbIcon(), + "Spinner.arrowButtonInsets", new InsetsUIResource(0, 0, 0, 0), "Spinner.background", getControl(), "Spinner.font", new FontUIResource("Dialog", Font.BOLD, 12), "Spinner.foreground", getControl(), @@ -1072,6 +1177,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel "SplitPane.background", getControl(), "SplitPane.darkShadow", getControlDarkShadow(), "SplitPane.dividerFocusColor", getPrimaryControl(), + "SplitPane.dividerSize", new Integer(10), "SplitPane.highlight", getControlHighlight(), "SplitPane.shadow", getControlShadow(), @@ -1174,6 +1280,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel "ToolTip.font", new FontUIResource("Dialog", Font.PLAIN, 12), "ToolTip.foreground", getPrimaryControlInfo(), "ToolTip.foregroundInactive", getControlDarkShadow(), + "ToolTip.hideAccelerator", Boolean.FALSE, "Tree.background", getWindowBackground(), "Tree.closedIcon", MetalIconFactory.getTreeFolderIcon(), diff --git a/javax/swing/plaf/metal/MetalRadioButtonUI.java b/javax/swing/plaf/metal/MetalRadioButtonUI.java index de71fe8e5..9fb960f68 100644 --- a/javax/swing/plaf/metal/MetalRadioButtonUI.java +++ b/javax/swing/plaf/metal/MetalRadioButtonUI.java @@ -176,7 +176,7 @@ public class MetalRadioButtonUI protected void paintFocus(Graphics g, Rectangle t, Dimension d) { g.setColor(focusColor); - g.drawRect(t.x - 1, t.y + 2, t.width + 2, t.height - 4); + g.drawRect(t.x - 1, t.y - 1, t.width + 2, t.height + 2); } } diff --git a/javax/swing/plaf/metal/MetalSplitPaneDivider.java b/javax/swing/plaf/metal/MetalSplitPaneDivider.java index e9adfe46e..34a964cb3 100644 --- a/javax/swing/plaf/metal/MetalSplitPaneDivider.java +++ b/javax/swing/plaf/metal/MetalSplitPaneDivider.java @@ -105,7 +105,7 @@ class MetalSplitPaneDivider extends BasicSplitPaneDivider /** * This helper class acts as the Layout Manager for the divider. */ - protected class MetalDividerLayout implements LayoutManager + public class MetalDividerLayout implements LayoutManager { /** The right button. */ BasicArrowButton rb; @@ -116,7 +116,7 @@ class MetalSplitPaneDivider extends BasicSplitPaneDivider /** * Creates a new DividerLayout object. */ - protected MetalDividerLayout() + public MetalDividerLayout() { // Nothing to do here } @@ -132,7 +132,7 @@ class MetalSplitPaneDivider extends BasicSplitPaneDivider // Nothing to do here, constraints are set depending on // orientation in layoutContainer } - + /** * This method is called to lay out the container. * diff --git a/javax/swing/text/AbstractDocument.java b/javax/swing/text/AbstractDocument.java index 76e758c6d..395f39e4c 100644 --- a/javax/swing/text/AbstractDocument.java +++ b/javax/swing/text/AbstractDocument.java @@ -541,11 +541,13 @@ public abstract class AbstractDocument implements Document, Serializable writeLock(); UndoableEdit undo = content.insertString(offset, text); + if (undo != null) + event.addEdit(undo); + insertUpdate(event, attributes); writeUnlock(); - if (event.modified) - fireInsertUpdate(event); + fireInsertUpdate(event); if (undo != null) fireUndoableEditUpdate(new UndoableEditEvent(this, undo)); } @@ -1327,7 +1329,14 @@ public abstract class AbstractDocument implements Document, Serializable */ public Object getAttribute(Object key) { - return attributes.getAttribute(key); + Object result = attributes.getAttribute(key); + if (result == null && element_parent != null) + { + AttributeSet parentSet = element_parent.getAttributes(); + if (parentSet != null) + result = parentSet.getAttribute(key); + } + return result; } /** @@ -1905,6 +1914,15 @@ public abstract class AbstractDocument implements Document, Serializable // XXX - Fully qualify ElementChange to work around gcj bug #2499. return (DocumentEvent.ElementChange) changes.get(elem); } + + /** + * Returns a String description of the change event. This returns the + * toString method of the Vector of edits. + */ + public String toString() + { + return edits.toString(); + } } /** diff --git a/javax/swing/text/DefaultCaret.java b/javax/swing/text/DefaultCaret.java index de512c37f..0d00bf8d5 100644 --- a/javax/swing/text/DefaultCaret.java +++ b/javax/swing/text/DefaultCaret.java @@ -829,9 +829,12 @@ public class DefaultCaret extends Rectangle public void setDot(int dot) { if (dot >= 0) - { + { + Document doc = textComponent.getDocument(); + if (doc != null) + this.dot = Math.min(dot, doc.getLength()); + this.dot = Math.max(this.dot, 0); this.mark = dot; - this.dot = dot; handleHighlight(); adjustVisibility(this); appear(); diff --git a/javax/swing/text/DefaultEditorKit.java b/javax/swing/text/DefaultEditorKit.java index fa8c050b0..88094b898 100644 --- a/javax/swing/text/DefaultEditorKit.java +++ b/javax/swing/text/DefaultEditorKit.java @@ -38,8 +38,10 @@ exception statement from your version. */ package javax.swing.text; +import java.awt.Point; import java.awt.Toolkit; import java.awt.event.ActionEvent; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -698,6 +700,53 @@ public class DefaultEditorKit extends EditorKit new InsertContentAction(), new InsertTabAction(), new PasteAction(), + new TextAction(beginLineAction) + { + public void actionPerformed(ActionEvent event) + { + JTextComponent t = getTextComponent(event); + try + { + // TODO: There is a more efficent solution, but + // viewToModel doesn't work properly. + Point p = t.modelToView(t.getCaret().getDot()).getLocation(); + int cur = t.getCaretPosition(); + int y = p.y; + while (y == p.y && cur > 0) + y = t.modelToView(--cur).getLocation().y; + if (cur != 0) + cur++; + t.setCaretPosition(cur); + } + catch (BadLocationException ble) + { + // Do nothing here. + } + } + }, + new TextAction(endLineAction) + { + public void actionPerformed(ActionEvent event) + { + JTextComponent t = getTextComponent(event); + try + { + Point p = t.modelToView(t.getCaret().getDot()).getLocation(); + int cur = t.getCaretPosition(); + int y = p.y; + int length = t.getDocument().getLength(); + while (y == p.y && cur < length) + y = t.modelToView(++cur).getLocation().y; + if (cur != length) + cur--; + t.setCaretPosition(cur); + } + catch (BadLocationException ble) + { + // Nothing to do here + } + } + }, new TextAction(deleteNextCharAction) { public void actionPerformed(ActionEvent event) @@ -899,7 +948,7 @@ public class DefaultEditorKit extends EditorKit content.append("\n"); } - document.insertString(offset, content.toString(), + document.insertString(offset, content.substring(0, content.length() - 1), SimpleAttributeSet.EMPTY); } diff --git a/javax/swing/text/DefaultFormatter.java b/javax/swing/text/DefaultFormatter.java index 1b339366d..dfeac12b7 100644 --- a/javax/swing/text/DefaultFormatter.java +++ b/javax/swing/text/DefaultFormatter.java @@ -400,6 +400,8 @@ public class DefaultFormatter extends JFormattedTextField.AbstractFormatter public String valueToString(Object value) throws ParseException { + if (value == null) + return ""; return value.toString(); } diff --git a/javax/swing/text/DefaultFormatterFactory.java b/javax/swing/text/DefaultFormatterFactory.java new file mode 100644 index 000000000..84a1676d2 --- /dev/null +++ b/javax/swing/text/DefaultFormatterFactory.java @@ -0,0 +1,280 @@ +/* DefaultFormatterFactory.java -- FIXME: briefly describe file purpose + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text; + +import java.io.Serializable; + +import javax.swing.JFormattedTextField; +import javax.swing.JFormattedTextField.AbstractFormatter; +import javax.swing.JFormattedTextField.AbstractFormatterFactory; + +/** + * This class is Swing's only concrete implementation of + * JFormattedTextField.AbstractFormatterFactory. It holds several + * formatters and determines the best one to be used based on the + * passed-in value from the text field. + * + * @author Anthony Balkissoon abalkiss at redhat dot com + * @since 1.4 + */ +public class DefaultFormatterFactory extends AbstractFormatterFactory implements + Serializable +{ + /** + * The default formatter. + **/ + AbstractFormatter defaultFormatter; + + /** + * The formatter to use when the JFormattedTextField has focus and either the + * value isn't null or the value is null but no <code>nullFormatter</code> + * has been specified. + */ + AbstractFormatter editFormatter; + + /** + * The formatter to use when the JFormattedTextField doesn't havefocus and + * either the value isn't null or the value is null but no + * <code>nullFormatter</code> has been specified. + */ + AbstractFormatter displayFormatter; + + /** + * The formatter to use when the value of the JFormattedTextField is null. + */ + AbstractFormatter nullFormatter; + + /** + * Creates a DefaultFormatterFactory with no formatters + */ + public DefaultFormatterFactory() + { + // Nothing to be done here. + } + + /** + * Creates a new DefaultFormatterFactory with the specified formatters. + * @param defaultFormat the formatter to use if no other appropriate non-null + * formatted can be found. + */ + public DefaultFormatterFactory(AbstractFormatter defaultFormat) + { + defaultFormatter = defaultFormat; + } + + /** + * Creates a new DefaultFormatterFactory with the specified formatters. + * @param defaultFormat the formatter to use if no other appropriate non-null + * formatted can be found. + * @param displayFormat the formatter to use if the JFormattedTextField + * doesn't have focus and either the value is not null or the value is null + * but no <code>nullFormatter</code> has been specified. + */ + public DefaultFormatterFactory(AbstractFormatter defaultFormat, + AbstractFormatter displayFormat) + { + defaultFormatter = defaultFormat; + displayFormatter = displayFormat; + } + + /** + * Creates a new DefaultFormatterFactory with the specified formatters. + * @param defaultFormat the formatter to use if no other appropriate non-null + * formatted can be found. + * @param displayFormat the formatter to use if the JFormattedTextField + * doesn't have focus and either the value is not null or the value is null + * but no <code>nullFormatter</code> has been specified. + * @param editFormat the formatter to use if the JFormattedTextField has + * focus and either the value is not null or the value is null but not + * <code>nullFormatter</code> has been specified. + */ + public DefaultFormatterFactory(AbstractFormatter defaultFormat, + AbstractFormatter displayFormat, + AbstractFormatter editFormat) + { + defaultFormatter = defaultFormat; + displayFormatter = displayFormat; + editFormatter = editFormat; + } + + /** + * Creates a new DefaultFormatterFactory with the specified formatters. + * @param defaultFormat the formatter to use if no other appropriate non-null + * formatted can be found. + * @param displayFormat the formatter to use if the JFormattedTextField + * doesn't have focus and either the value is not null or the value is null + * but no <code>nullFormatter</code> has been specified. + * @param editFormat the formatter to use if the JFormattedTextField has + * focus and either the value is not null or the value is null but not + * <code>nullFormatter</code> has been specified. + * @param nullFormat the formatter to use when the value of the + * JFormattedTextField is null. + */ + public DefaultFormatterFactory(AbstractFormatter defaultFormat, + AbstractFormatter displayFormat, + AbstractFormatter editFormat, + AbstractFormatter nullFormat) + { + defaultFormatter = defaultFormat; + displayFormatter = displayFormat; + editFormatter = editFormat; + nullFormatter = nullFormat; + } + + /** + * Returns the formatted to be used if no other appropriate non-null + * formatter can be found. + * @return the formatted to be used if no other appropriate non-null + * formatter can be found. + */ + public AbstractFormatter getDefaultFormatter() + { + return defaultFormatter; + } + + /** + * Sets the formatted to be used if no other appropriate non-null formatter + * can be found. + * @param defaultFormatter the formatted to be used if no other appropriate + * non-null formatter can be found. + */ + public void setDefaultFormatter(AbstractFormatter defaultFormatter) + { + this.defaultFormatter = defaultFormatter; + } + + /** + * Gets the <code>displayFormatter</code>. This is the formatter to use if + * the JFormattedTextField is not being edited and either the value is not + * null or the value is null and no <code>nullFormatter<code> has been + * specified. + * @return the formatter to use if + * the JFormattedTextField is not being edited and either the value is not + * null or the value is null and no <code>nullFormatter<code> has been + * specified. + */ + public AbstractFormatter getDisplayFormatter() + { + return displayFormatter; + } + + /** + * Sets the <code>displayFormatter</code>. This is the formatter to use if + * the JFormattedTextField is not being edited and either the value is not + * null or the value is null and no <code>nullFormatter<code> has been + * specified. + * @param displayFormatter the formatter to use. + */ + public void setDisplayFormatter(AbstractFormatter displayFormatter) + { + this.displayFormatter = displayFormatter; + } + + /** + * Gets the <code>editFormatter</code>. This is the formatter to use if the + * JFormattedTextField is being edited and either the value is not null or + * the value is null and no <code>nullFormatter<code> has been specified. + * @return the formatter to use if the JFormattedTextField is being edited + * and the value is not null or the value is null but no nullFormatted has + * been specified. + */ + public AbstractFormatter getEditFormatter() + { + return editFormatter; + } + + /** + * Sets the <code>editFormatter</code>. This is the formatter to use if the + * JFormattedTextField is being edited and either the value is not null or + * the value is null and no <code>nullFormatter<code> has been specified. + * @param editFormatter the formatter to use. + */ + public void setEditFormatter(AbstractFormatter editFormatter) + { + this.editFormatter = editFormatter; + } + + /** + * Gets the formatter to use if the value of the JFormattedTextField is null. + * @return the formatter to use for null values. + */ + public AbstractFormatter getNullFormatter() + { + return nullFormatter; + } + + /** + * Sets the <code>nullFormatter</code>. This is the formatter to use if the + * value of the JFormattedTextField is null. + * @param nullFormatter the formatter to use for null values. + */ + public void setNullFormatter(AbstractFormatter nullFormatter) + { + this.nullFormatter = nullFormatter; + } + + /** + * Returns the appropriate formatter based on the state of + * <code>tf</code>. If <code>tf<code> is null we return null, otherwise + * we return one of the following: + * 1. Returns <code>nullFormatter</code> if <code>tf.getValue()</code> is + * null and <code>nullFormatter</code> is not. + * 2. Returns <code>editFormatter</code> if <code>tf.hasFocus()</code> is + * true and <code>editFormatter</code> is not null. + * 3. Returns <code>displayFormatter</code> if <code>tf.hasFocus()</code> is + * false and <code>displayFormatter</code> is not null. + * 4. Otherwise returns <code>defaultFormatter</code>. + */ + public AbstractFormatter getFormatter(JFormattedTextField tf) + { + if (tf == null) + return null; + + if (tf.getValue() == null && nullFormatter != null) + return nullFormatter; + + if (tf.hasFocus() && editFormatter != null) + return editFormatter; + + if (!tf.hasFocus() && displayFormatter != null) + return displayFormatter; + + return defaultFormatter; + } +} diff --git a/javax/swing/text/DefaultStyledDocument.java b/javax/swing/text/DefaultStyledDocument.java index 49ab14e54..1df77b44d 100644 --- a/javax/swing/text/DefaultStyledDocument.java +++ b/javax/swing/text/DefaultStyledDocument.java @@ -48,6 +48,7 @@ import java.util.Vector; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; +import javax.swing.event.UndoableEditEvent; import javax.swing.undo.AbstractUndoableEdit; import javax.swing.undo.UndoableEdit; @@ -428,6 +429,9 @@ public class DefaultStyledDocument extends AbstractDocument /** Holds the length of structural changes. */ private int length; + + /** Holds the end offset for structural changes. **/ + private int endOffset; /** * The number of inserted end tags. This is a counter which always gets @@ -485,6 +489,85 @@ public class DefaultStyledDocument extends AbstractDocument } /** + * Updates the element structure of the document in response to removal of + * content. It removes the affected {@link Element}s from the document + * structure. + * + * This method sets some internal parameters and delegates the work + * to {@link #removeUpdate}. + * + * @param offs the offset from which content is remove + * @param len the length of the removed content + * @param ev the document event that records the changes + */ + public void remove(int offs, int len, DefaultDocumentEvent ev) + { + offset = offs; + length = len; + documentEvent = ev; + removeUpdate(); + } + + /** + * Updates the element structure of the document in response to removal of + * content. It removes the affected {@link Element}s from the document + * structure. + */ + protected void removeUpdate() + { + int startParagraph = root.getElementIndex(offset); + int endParagraph = root.getElementIndex(offset + length); + Element[] empty = new Element[0]; + int removeStart = -1; + int removeEnd = -1; + for (int i = startParagraph; i < endParagraph; i++) + { + Element paragraph = root.getElement(i); + int contentStart = paragraph.getElementIndex(offset); + int contentEnd = paragraph.getElementIndex(offset + length); + if (contentStart == paragraph.getStartOffset() + && contentEnd == paragraph.getEndOffset()) + { + // In this case we only need to remove the whole paragraph. We + // do this in one go after this loop and only record the indices + // here. + if (removeStart == -1) + { + removeStart = i; + removeEnd = i; + } + else + removeEnd = i; + } + else + { + // In this case we remove a couple of child elements from this + // paragraph. + int removeLen = contentEnd - contentStart; + Element[] removed = new Element[removeLen]; + for (int j = contentStart; j < contentEnd; j++) + removed[j] = paragraph.getElement(j); + ((BranchElement) paragraph).replace(contentStart, removeLen, + empty); + documentEvent.addEdit(new ElementEdit(paragraph, contentStart, + removed, empty)); + } + } + // Now we remove paragraphs from the root that have been tagged for + // removal. + if (removeStart != -1) + { + int removeLen = removeEnd - removeStart; + Element[] removed = new Element[removeLen]; + for (int i = removeStart; i < removeEnd; i++) + removed[i] = root.getElement(i); + ((BranchElement) root).replace(removeStart, removeLen, empty); + documentEvent.addEdit(new ElementEdit(root, removeStart, removed, + empty)); + } + } + + /** * Modifies the element structure so that the specified interval starts * and ends at an element boundary. Content and paragraph elements * are split and created as necessary. @@ -675,8 +758,11 @@ public class DefaultStyledDocument extends AbstractDocument public void insert(int offset, int length, ElementSpec[] data, DefaultDocumentEvent ev) { + if (length == 0) + return; this.offset = offset; this.length = length; + this.endOffset = offset + length; documentEvent = ev; // Push the root and the paragraph at offset onto the element stack. elementStack.clear(); @@ -703,9 +789,11 @@ public class DefaultStyledDocument extends AbstractDocument { case ElementSpec.StartTagType: numStartTags++; + documentEvent.modified = true; break; case ElementSpec.EndTagType: numEndTags++; + documentEvent.modified = true; break; default: insertContentTag(data[i]); @@ -799,7 +887,7 @@ public class DefaultStyledDocument extends AbstractDocument } return ret; } - + /** * Inserts a content element into the document structure. * @@ -810,6 +898,7 @@ public class DefaultStyledDocument extends AbstractDocument prepareContentInsertion(); int len = tag.getLength(); int dir = tag.getDirection(); + AttributeSet tagAtts = tag.getAttributes(); if (dir == ElementSpec.JoinPreviousDirection) { // The mauve tests to this class show that a JoinPrevious insertion @@ -824,66 +913,77 @@ public class DefaultStyledDocument extends AbstractDocument Element current = paragraph.getElement(currentIndex); Element next = paragraph.getElement(currentIndex + 1); + if (next == null) + return; + Element newEl1 = createLeafElement(paragraph, current.getAttributes(), - current.getStartOffset(), + current.getStartOffset(), offset); Element newEl2 = createLeafElement(paragraph, - current.getAttributes(), + current.getAttributes(), offset, next.getEndOffset()); - Element[] add = new Element[] {newEl1, newEl2}; - Element[] remove = new Element[] {current, next}; + Element[] add = new Element[] { newEl1, newEl2 }; + Element[] remove = new Element[] { current, next }; paragraph.replace(currentIndex, 2, add); - // Add this action to the document event. addEdit(paragraph, currentIndex, remove, add); } - else + else if (dir == ElementSpec.JoinFractureDirection) + { + // TODO: What should be done here? + } + else if (dir == ElementSpec.OriginateDirection) { BranchElement paragraph = (BranchElement) elementStack.peek(); int index = paragraph.getElementIndex(offset); Element current = paragraph.getElement(index); - + Element[] added; - Element[] removed; + Element[] removed = new Element[] {current}; Element[] splitRes = split(current, offset, length); - // Special case for when offset == startOffset or offset == endOffset. if (splitRes[0] == null) { added = new Element[2]; - added[0] = createLeafElement(paragraph, tag.getAttributes(), - offset, offset + length); + added[0] = createLeafElement(paragraph, tagAtts, + offset, endOffset); added[1] = splitRes[1]; removed = new Element[0]; index++; } else if (current.getStartOffset() == offset) - { + { + // This is if the new insertion happens immediately before + // the <code>current</code> Element. In this case there are 2 + // resulting Elements. added = new Element[2]; - added[0] = createLeafElement(paragraph, tag.getAttributes(), - offset, offset + length); + added[0] = createLeafElement(paragraph, tagAtts, offset, + endOffset); added[1] = splitRes[1]; - removed = new Element[] { current }; } - else if (current.getEndOffset() - length == offset) + else if (current.getEndOffset() == endOffset) { + // This is if the new insertion happens right at the end of + // the <code>current</code> Element. In this case there are + // 2 resulting Elements. added = new Element[2]; added[0] = splitRes[0]; - added[1] = createLeafElement(paragraph, tag.getAttributes(), - offset, offset + length); - removed = new Element[] { current }; + added[1] = createLeafElement(paragraph, tagAtts, offset, + endOffset); } else { + // This is if the new insertion is in the middle of the + // <code>current</code> Element. In this case + // there will be 3 resulting Elements. added = new Element[3]; added[0] = splitRes[0]; - added[1] = createLeafElement(paragraph, tag.getAttributes(), - offset, offset + length); + added[1] = createLeafElement(paragraph, tagAtts, offset, + endOffset); added[2] = splitRes[1]; - removed = new Element[] { current }; - } + } paragraph.replace(index, removed.length, added); addEdit(paragraph, index, removed, added); } @@ -1014,7 +1114,7 @@ public class DefaultStyledDocument extends AbstractDocument */ public String getName() { - return "section"; + return SectionElementName; } } @@ -1135,8 +1235,7 @@ public class DefaultStyledDocument extends AbstractDocument // Use createBranchElement() and createLeafElement instead. SectionElement section = new SectionElement(); - BranchElement paragraph = - (BranchElement) createBranchElement(section, null); + BranchElement paragraph = new BranchElement(section, null); paragraph.setResolveParent(getStyle(StyleContext.DEFAULT_STYLE)); tmp = new Element[1]; tmp[0] = paragraph; @@ -1233,7 +1332,11 @@ public class DefaultStyledDocument extends AbstractDocument { Element paragraph = getParagraphElement(position); AttributeSet attributes = paragraph.getAttributes(); - return (Style) attributes.getResolveParent(); + AttributeSet a = attributes.getResolveParent(); + // If the resolve parent is not of type Style, we return null. + if (a instanceof Style) + return (Style) a; + return null; } /** @@ -1302,50 +1405,54 @@ public class DefaultStyledDocument extends AbstractDocument AttributeSet attributes, boolean replace) { - DefaultDocumentEvent ev = - new DefaultDocumentEvent(offset, length, - DocumentEvent.EventType.CHANGE); - - // Modify the element structure so that the interval begins at an element - // start and ends at an element end. - buffer.change(offset, length, ev); - - Element root = getDefaultRootElement(); - // Visit all paragraph elements within the specified interval - int paragraphCount = root.getElementCount(); - for (int pindex = 0; pindex < paragraphCount; pindex++) + // Exit early if length is 0, so no DocumentEvent is created or fired. + if (length == 0) + return; + try { - Element paragraph = root.getElement(pindex); - // Skip paragraphs that lie outside the interval. - if ((paragraph.getStartOffset() > offset + length) - || (paragraph.getEndOffset() < offset)) - continue; - - // Visit content elements within this paragraph - int contentCount = paragraph.getElementCount(); - for (int cindex = 0; cindex < contentCount; cindex++) + // Must obtain a write lock for this method. writeLock() and + // writeUnlock() should always be in try/finally block to make + // sure that locking happens in a balanced manner. + writeLock(); + DefaultDocumentEvent ev = + new DefaultDocumentEvent( + offset, + length, + DocumentEvent.EventType.CHANGE); + + // Modify the element structure so that the interval begins at an + // element + // start and ends at an element end. + buffer.change(offset, length, ev); + + Element root = getDefaultRootElement(); + // Visit all paragraph elements within the specified interval + int end = offset + length; + Element curr; + for (int pos = offset; pos < end; ) { - Element content = paragraph.getElement(cindex); - // Skip content that lies outside the interval. - if ((content.getStartOffset() > offset + length) - || (content.getEndOffset() < offset)) - continue; - - if (content instanceof AbstractElement) - { - AbstractElement el = (AbstractElement) content; - if (replace) - el.removeAttributes(el); - el.addAttributes(attributes); - } - else - throw new AssertionError("content elements are expected to be" - + "instances of " - + "javax.swing.text.AbstractDocument.AbstractElement"); + // Get the CharacterElement at offset pos. + curr = getCharacterElement(pos); + if (pos == curr.getEndOffset()) + break; + + MutableAttributeSet a = (MutableAttributeSet) curr.getAttributes(); + ev.addEdit(new AttributeUndoableEdit(curr, attributes, replace)); + // If replace is true, remove all the old attributes. + if (replace) + a.removeAttributes(a); + // Add all the new attributes. + a.addAttributes(attributes); + // Increment pos so we can check the next CharacterElement. + pos = curr.getEndOffset(); } + fireChangedUpdate(ev); + fireUndoableEditUpdate(new UndoableEditEvent(this, ev)); + } + finally + { + writeUnlock(); } - - fireChangedUpdate(ev); } /** @@ -1357,14 +1464,36 @@ public class DefaultStyledDocument extends AbstractDocument public void setLogicalStyle(int position, Style style) { Element el = getParagraphElement(position); - if (el instanceof AbstractElement) - { - AbstractElement ael = (AbstractElement) el; - ael.setResolveParent(style); - } - else - throw new AssertionError("paragraph elements are expected to be" - + "instances of javax.swing.text.AbstractDocument.AbstractElement"); + // getParagraphElement doesn't return null but subclasses might so + // we check for null here. + if (el == null) + return; + try + { + writeLock(); + if (el instanceof AbstractElement) + { + AbstractElement ael = (AbstractElement) el; + ael.setResolveParent(style); + int start = el.getStartOffset(); + int end = el.getEndOffset(); + DefaultDocumentEvent ev = + new DefaultDocumentEvent ( + start, + end - start, + DocumentEvent.EventType.CHANGE); + // FIXME: Add an UndoableEdit to this event and fire it. + fireChangedUpdate(ev); + } + else + throw new + AssertionError("paragraph elements are expected to be" + + "instances of AbstractDocument.AbstractElement"); + } + finally + { + writeUnlock(); + } } /** @@ -1380,15 +1509,47 @@ public class DefaultStyledDocument extends AbstractDocument AttributeSet attributes, boolean replace) { - int index = offset; - while (index < offset + length) + try { - AbstractElement par = (AbstractElement) getParagraphElement(index); - AttributeContext ctx = getAttributeContext(); - if (replace) - par.removeAttributes(par); - par.addAttributes(attributes); - index = par.getElementCount(); + // Must obtain a write lock for this method. writeLock() and + // writeUnlock() should always be in try/finally blocks to make + // sure that locking occurs in a balanced manner. + writeLock(); + + // Create a DocumentEvent to use for changedUpdate(). + DefaultDocumentEvent ev = + new DefaultDocumentEvent ( + offset, + length, + DocumentEvent.EventType.CHANGE); + + // Have to iterate through all the _paragraph_ elements that are + // contained or partially contained in the interval + // (offset, offset + length). + Element rootElement = getDefaultRootElement(); + int startElement = rootElement.getElementIndex(offset); + int endElement = rootElement.getElementIndex(offset + length - 1); + if (endElement < startElement) + endElement = startElement; + + for (int i = startElement; i <= endElement; i++) + { + Element par = rootElement.getElement(i); + MutableAttributeSet a = (MutableAttributeSet) par.getAttributes(); + // Add the change to the DocumentEvent. + ev.addEdit(new AttributeUndoableEdit(par, attributes, replace)); + // If replace is true remove the old attributes. + if (replace) + a.removeAttributes(a); + // Add the new attributes. + a.addAttributes(attributes); + } + fireChangedUpdate(ev); + fireUndoableEditUpdate(new UndoableEditEvent(this, ev)); + } + finally + { + writeUnlock(); } } @@ -1402,9 +1563,14 @@ public class DefaultStyledDocument extends AbstractDocument protected void insertUpdate(DefaultDocumentEvent ev, AttributeSet attr) { super.insertUpdate(ev, attr); + // If the attribute set is null, use an empty attribute set. + if (attr == null) + attr = SimpleAttributeSet.EMPTY; int offset = ev.getOffset(); int length = ev.getLength(); int endOffset = offset + length; + AttributeSet paragraphAttributes = + getParagraphElement(endOffset).getAttributes(); Segment txt = new Segment(); try { @@ -1419,66 +1585,104 @@ public class DefaultStyledDocument extends AbstractDocument int len = 0; Vector specs = new Vector(); - + ElementSpec finalStartTag = null; + short finalStartDirection = ElementSpec.OriginateDirection; + boolean prevCharWasNewline = false; Element prev = getCharacterElement(offset); - Element next = getCharacterElement(endOffset); + Element next = getCharacterElement(endOffset); + Element prevParagraph = getParagraphElement(offset); + Element paragraph = getParagraphElement(endOffset); + + int segmentEnd = txt.offset + txt.count; + + // Check to see if we're inserting immediately after a newline. + if (offset > 0) + { + try + { + String s = getText(offset - 1, 1); + if (s.equals("\n")) + { + finalStartDirection = + handleInsertAfterNewline(specs, offset, endOffset, + prevParagraph, + paragraph, + paragraphAttributes); + + prevCharWasNewline = true; + // Find the final start tag from the ones just created. + for (int i = 0; i < specs.size(); i++) + if (((ElementSpec) specs.get(i)).getType() + == ElementSpec.StartTagType) + finalStartTag = (ElementSpec)specs.get(i); + } + } + catch (BadLocationException ble) + { + // This shouldn't happen. + AssertionError ae = new AssertionError(); + ae.initCause(ble); + throw ae; + } + } - for (int i = offset; i < endOffset; ++i) + + for (int i = txt.offset; i < segmentEnd; ++i) { len++; if (txt.array[i] == '\n') { - ElementSpec spec = new ElementSpec(attr, ElementSpec.ContentType, - len); - - // If we are at the last index, then check if we could probably be - // joined with the next element. - if (i == endOffset - 1) - { - if (next.getAttributes().isEqual(attr)) - spec.setDirection(ElementSpec.JoinNextDirection); - } - // If we are at the first new element, then check if it could be - // joined with the previous element. - else if (specs.size() == 0) - { - if (prev.getAttributes().isEqual(attr)) - spec.setDirection(ElementSpec.JoinPreviousDirection); - } - - specs.add(spec); + // Add the ElementSpec for the content. + specs.add(new ElementSpec(attr, ElementSpec.ContentType, len)); // Add ElementSpecs for the newline. - ElementSpec endTag = new ElementSpec(null, ElementSpec.EndTagType); - specs.add(endTag); - ElementSpec startTag = new ElementSpec(null, + specs.add(new ElementSpec(null, ElementSpec.EndTagType)); + finalStartTag = new ElementSpec(paragraphAttributes, ElementSpec.StartTagType); - startTag.setDirection(ElementSpec.JoinFractureDirection); - specs.add(startTag); - + specs.add(finalStartTag); len = 0; - offset += len; } } // Create last element if last character hasn't been a newline. - if (len > 0) + if (len > 0) + specs.add(new ElementSpec(attr, ElementSpec.ContentType, len)); + + // Set the direction of the last spec of type StartTagType. + // If we are inserting after a newline then this value comes from + // handleInsertAfterNewline. + if (finalStartTag != null) { - ElementSpec spec = new ElementSpec(attr, ElementSpec.ContentType, len); - // If we are at the first new element, then check if it could be - // joined with the previous element. - if (specs.size() == 0) + if (prevCharWasNewline) + finalStartTag.setDirection(finalStartDirection); + else if (prevParagraph.getEndOffset() != endOffset) + finalStartTag.setDirection(ElementSpec.JoinFractureDirection); + else { - if (prev.getAttributes().isEqual(attr)) - spec.setDirection(ElementSpec.JoinPreviousDirection); + // If there is an element AFTER this one, then set the + // direction to JoinNextDirection. + Element parent = prevParagraph.getParentElement(); + int index = parent.getElementIndex(offset); + if (index + 1 < parent.getElementCount() + && !parent.getElement(index + 1).isLeaf()) + finalStartTag.setDirection(ElementSpec.JoinNextDirection); } - // Check if we could probably be joined with the next element. - else if (next.getAttributes().isEqual(attr)) - spec.setDirection(ElementSpec.JoinNextDirection); - - specs.add(spec); } - + + // If we are at the last index, then check if we could probably be + // joined with the next element. + ElementSpec last = (ElementSpec) specs.lastElement(); + if (next.getAttributes().isEqual(attr) + && last.getType() == ElementSpec.ContentType) + last.setDirection(ElementSpec.JoinNextDirection); + + // If we are at the first new element, then check if it could be + // joined with the previous element. + ElementSpec first = (ElementSpec) specs.firstElement(); + if (prev.getAttributes().isEqual(attr) + && first.getType() == ElementSpec.ContentType) + first.setDirection(ElementSpec.JoinPreviousDirection); + ElementSpec[] elSpecs = (ElementSpec[]) specs.toArray(new ElementSpec[specs.size()]); @@ -1486,6 +1690,47 @@ public class DefaultStyledDocument extends AbstractDocument } /** + * A helper method to set up the ElementSpec buffer for the special + * case of an insertion occurring immediately after a newline. + * @param specs the ElementSpec buffer to initialize. + */ + short handleInsertAfterNewline(Vector specs, int offset, int endOffset, + Element prevParagraph, Element paragraph, + AttributeSet a) + { + if (prevParagraph.getParentElement() == paragraph.getParentElement()) + { + specs.add(new ElementSpec(a, ElementSpec.EndTagType)); + specs.add(new ElementSpec(a, ElementSpec.StartTagType)); + if (prevParagraph.getEndOffset() != endOffset) + return ElementSpec.JoinFractureDirection; + // If there is an Element after this one, use JoinNextDirection. + Element parent = paragraph.getParentElement(); + if (parent.getElementCount() > parent.getElementIndex(offset) + 1) + return ElementSpec.JoinNextDirection; + } + else + { + // TODO: What to do here? + } + return ElementSpec.OriginateDirection; + } + + /** + * Updates the document structure in response to text removal. This is + * forwarded to the {@link ElementBuffer} of this document. Any changes to + * the document structure are added to the specified document event and + * sent to registered listeners. + * + * @param ev the document event that records the changes to the document + */ + protected void removeUpdate(DefaultDocumentEvent ev) + { + super.removeUpdate(ev); + buffer.remove(ev.getOffset(), ev.getLength(), ev); + } + + /** * Returns an enumeration of all style names. * * @return an enumeration of all style names @@ -1506,6 +1751,35 @@ public class DefaultStyledDocument extends AbstractDocument // Nothing to do here. This is intended to be overridden by subclasses. } + void printElements (Element start, int pad) + { + for (int i = 0; i < pad; i++) + System.out.print(" "); + if (pad == 0) + System.out.println ("ROOT ELEMENT ("+start.getStartOffset()+", "+start.getEndOffset()+")"); + else if (start instanceof AbstractDocument.BranchElement) + System.out.println ("BranchElement ("+start.getStartOffset()+", "+start.getEndOffset()+")"); + else + { + { + try + { + System.out.println ("LeafElement ("+start.getStartOffset()+", " + + start.getEndOffset()+"): "+ + start.getDocument(). + getText(start.getStartOffset(), + start.getEndOffset() - + start.getStartOffset())); + } + catch (BadLocationException ble) + { + } + } + } + for (int i = 0; i < start.getElementCount(); i ++) + printElements (start.getElement(i), pad+3); + } + /** * Inserts a bulk of structured content at once. * @@ -1515,37 +1789,58 @@ public class DefaultStyledDocument extends AbstractDocument protected void insert(int offset, ElementSpec[] data) throws BadLocationException { - writeLock(); - // First we insert the content. - int index = offset; - for (int i = 0; i < data.length; i++) + try { - ElementSpec spec = data[i]; - if (spec.getArray() != null && spec.getLength() > 0) + // writeLock() and writeUnlock() should always be in a try/finally + // block so that locking balance is guaranteed even if some + // exception is thrown. + writeLock(); + + // First we collect the content to be inserted. + StringBuffer contentBuffer = new StringBuffer(); + for (int i = 0; i < data.length; i++) { - String insertString = new String(spec.getArray(), spec.getOffset(), - spec.getLength()); - content.insertString(index, insertString); + // Collect all inserts into one so we can get the correct + // ElementEdit + ElementSpec spec = data[i]; + if (spec.getArray() != null && spec.getLength() > 0) + contentBuffer.append(spec.getArray(), spec.getOffset(), + spec.getLength()); } - index += spec.getLength(); + + int length = contentBuffer.length(); + + // If there was no content inserted then exit early. + if (length == 0) + return; + + UndoableEdit edit = content.insertString(offset, + contentBuffer.toString()); + + // Create the DocumentEvent with the ElementEdit added + DefaultDocumentEvent ev = + new DefaultDocumentEvent(offset, + length, + DocumentEvent.EventType.INSERT); + ev.addEdit(edit); + + for (int i = 0; i < data.length; i++) + { + ElementSpec spec = data[i]; + AttributeSet atts = spec.getAttributes(); + if (atts != null) + insertUpdate(ev, atts); + } + + // Finally we must update the document structure and fire the insert + // update event. + buffer.insert(offset, length, data, ev); + fireInsertUpdate(ev); } - // Update the view structure. - DefaultDocumentEvent ev = new DefaultDocumentEvent(offset, index - offset, - DocumentEvent.EventType.INSERT); - for (int i = 0; i < data.length; i++) + finally { - ElementSpec spec = data[i]; - AttributeSet atts = spec.getAttributes(); - if (atts != null) - insertUpdate(ev, atts); + writeUnlock(); } - - // Finally we must update the document structure and fire the insert update - // event. - buffer.insert(offset, index - offset, data, ev); - if (ev.modified) - fireInsertUpdate(ev); - writeUnlock(); } /** @@ -1573,4 +1868,9 @@ public class DefaultStyledDocument extends AbstractDocument throw err; } } + + static boolean attributeSetsAreSame (AttributeSet a, AttributeSet b) + { + return (a == null && b == null) || (a != null && a.isEqual(b)); + } } diff --git a/javax/swing/text/GapContent.java b/javax/swing/text/GapContent.java index 1e62a4695..c0f9beae2 100644 --- a/javax/swing/text/GapContent.java +++ b/javax/swing/text/GapContent.java @@ -64,6 +64,7 @@ import javax.swing.undo.UndoableEdit; public class GapContent implements AbstractDocument.Content, Serializable { + /** * A {@link Position} implementation for <code>GapContent</code>. */ @@ -449,7 +450,7 @@ public class GapContent // We store the actual array index in the GapContentPosition. The real // offset is then calculated in the GapContentPosition. int mark = offset; - if (offset > gapStart) + if (offset >= gapStart) mark += gapEnd - gapStart; GapContentPosition pos = new GapContentPosition(mark); @@ -584,8 +585,9 @@ public class GapContent { if (gapStart != position) shiftGap(position); + // Remove content - if (rmSize > 0) + if (rmSize > 0) shiftGapEndUp(gapEnd + rmSize); // If gap is too small, enlarge the gap. diff --git a/javax/swing/text/InternationalFormatter.java b/javax/swing/text/InternationalFormatter.java index a7ed18166..ba3cffaba 100644 --- a/javax/swing/text/InternationalFormatter.java +++ b/javax/swing/text/InternationalFormatter.java @@ -228,6 +228,8 @@ public class InternationalFormatter public String valueToString(Object value) throws ParseException { + if (value == null) + return ""; if (format != null) return format.format(value); else diff --git a/javax/swing/text/JTextComponent.java b/javax/swing/text/JTextComponent.java index 87f884986..afa1f24a6 100644 --- a/javax/swing/text/JTextComponent.java +++ b/javax/swing/text/JTextComponent.java @@ -380,12 +380,18 @@ public abstract class JTextComponent extends JComponent public KeyStroke[] allKeys() { KeyStroke[] superKeys = super.allKeys(); - KeyStroke[] mapKeys = map.getBoundKeyStrokes(); - KeyStroke[] bothKeys = new KeyStroke[superKeys.length + mapKeys.length]; - for (int i = 0; i < superKeys.length; ++i) + KeyStroke[] mapKeys = map.getBoundKeyStrokes(); + int skl = 0; + int mkl = 0; + if (superKeys != null) + skl = superKeys.length; + if (mapKeys != null) + mkl = mapKeys.length; + KeyStroke[] bothKeys = new KeyStroke[skl + mkl]; + for (int i = 0; i < skl; ++i) bothKeys[i] = superKeys[i]; - for (int i = 0; i < mapKeys.length; ++i) - bothKeys[i + superKeys.length] = mapKeys[i]; + for (int i = 0; i < mkl; ++i) + bothKeys[i + skl] = mapKeys[i]; return bothKeys; } } @@ -906,49 +912,16 @@ public abstract class JTextComponent extends JComponent public JTextComponent() { Keymap defkeymap = getKeymap(DEFAULT_KEYMAP); - boolean creatingKeymap = false; if (defkeymap == null) { defkeymap = addKeymap(DEFAULT_KEYMAP, null); defkeymap.setDefaultAction(new DefaultEditorKit.DefaultKeyTypedAction()); - creatingKeymap = true; } setFocusable(true); setEditable(true); enableEvents(AWTEvent.KEY_EVENT_MASK); updateUI(); - - // need to do this after updateUI() - if (creatingKeymap) - loadKeymap( - defkeymap, - new KeyBinding[] { - new KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), - DefaultEditorKit.backwardAction), - new KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), - DefaultEditorKit.forwardAction), - new KeyBinding(KeyStroke.getKeyStroke("typed \b"), - DefaultEditorKit.deletePrevCharAction), - new KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_X, - KeyEvent.CTRL_DOWN_MASK), - DefaultEditorKit.cutAction), - new KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_C, - KeyEvent.CTRL_DOWN_MASK), - DefaultEditorKit.copyAction), - new KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_V, - KeyEvent.CTRL_DOWN_MASK), - DefaultEditorKit.pasteAction), - new KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, - KeyEvent.SHIFT_DOWN_MASK), - DefaultEditorKit.selectionBackwardAction), - new KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, - KeyEvent.SHIFT_DOWN_MASK), - DefaultEditorKit.selectionForwardAction), - new KeyBinding(KeyStroke.getKeyStroke("typed \u007f"), - DefaultEditorKit.deleteNextCharAction) - }, - getActions()); } public void setDocument(Document newDoc) diff --git a/javax/swing/text/StyleContext.java b/javax/swing/text/StyleContext.java index 4dfca6de1..c93adf895 100644 --- a/javax/swing/text/StyleContext.java +++ b/javax/swing/text/StyleContext.java @@ -362,9 +362,8 @@ public class StyleContext public boolean isEqual(AttributeSet attr) { - return attr != null - && attr.containsAttributes(this) - && this.containsAttributes(attr); + return getAttributeCount() == attr.getAttributeCount() + && this.containsAttributes(attr); } public String toString() diff --git a/javax/swing/text/StyledEditorKit.java b/javax/swing/text/StyledEditorKit.java index e71f992b5..c4eef4463 100644 --- a/javax/swing/text/StyledEditorKit.java +++ b/javax/swing/text/StyledEditorKit.java @@ -67,7 +67,7 @@ public class StyledEditorKit extends DefaultEditorKit */ public UnderlineAction() { - super("TODO"); // TODO: Figure out name for this action. + super("font-underline"); } /** @@ -97,7 +97,7 @@ public class StyledEditorKit extends DefaultEditorKit */ public ItalicAction() { - super("TODO"); // TODO: Figure out correct name of this Action. + super("font-italic"); } /** @@ -127,7 +127,7 @@ public class StyledEditorKit extends DefaultEditorKit */ public BoldAction() { - super("TODO"); // TODO: Figure out correct name of this Action. + super("font-bold"); } /** @@ -585,8 +585,26 @@ public class StyledEditorKit extends DefaultEditorKit public Action[] getActions() { Action[] actions1 = super.getActions(); - Action[] myActions = new Action[] { new BoldAction(), new ItalicAction(), - new UnderlineAction() }; + Action[] myActions = new Action[] { + new FontSizeAction("font-size-8", 8), + new FontSizeAction("font-size-10", 10), + new FontSizeAction("font-size-12", 12), + new FontSizeAction("font-size-14", 14), + new FontSizeAction("font-size-16", 16), + new FontSizeAction("font-size-18", 18), + new FontSizeAction("font-size-24", 24), + new FontSizeAction("font-size-36", 36), + new FontSizeAction("font-size-48", 48), + new FontFamilyAction("font-family-Serif", "Serif"), + new FontFamilyAction("font-family-Monospaced", "Monospaced"), + new FontFamilyAction("font-family-SansSerif", "SansSerif"), + new AlignmentAction("left-justify", StyleConstants.ALIGN_LEFT), + new AlignmentAction("center-justify", StyleConstants.ALIGN_CENTER), + new AlignmentAction("right-justify", StyleConstants.ALIGN_RIGHT), + new BoldAction(), + new ItalicAction(), + new UnderlineAction() + }; return TextAction.augmentList(actions1, myActions); } @@ -696,9 +714,8 @@ public class StyledEditorKit extends DefaultEditorKit protected void createInputAttributes(Element element, MutableAttributeSet set) { - AttributeSet atts = element.getAttributes(); - set.removeAttributes(set); // FIXME: Filter out component, icon and element name attributes. - set.addAttributes(atts); + set.removeAttributes(set); + set.addAttributes(element.getAttributes()); } } diff --git a/javax/swing/text/TableView.java b/javax/swing/text/TableView.java new file mode 100644 index 000000000..d3113b82b --- /dev/null +++ b/javax/swing/text/TableView.java @@ -0,0 +1,465 @@ +/* TableView.java -- A view impl for tables inside styled text + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text; + +import java.awt.Rectangle; +import java.awt.Shape; + +import javax.swing.SizeRequirements; +import javax.swing.event.DocumentEvent; + +/** + * A {@link View} implementation for rendering tables inside styled text. + * Tables are rendered as vertical boxes (see {@link BoxView}). These boxes + * have a number of child views, which are the rows of the table. These are + * horizontal boxes containing the actuall cells of the table. These cells + * can be arbitrary view implementations and are fetched via the + * {@link ViewFactory} returned by {@link View#getViewFactory}. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class TableView + extends BoxView +{ + + /** + * A view implementation that renders a row of a <code>TableView</code>. + * This is implemented as a horizontal box that contains the actual cells + * of the table. + * + * @author Roman Kennke (kennke@aicas.com) + */ + public class TableRow + extends BoxView + { + /** + * Creates a new instance of <code>TableRow</code>. + * + * @param el the element for which to create a row view + */ + public TableRow(Element el) + { + super(el, X_AXIS); + } + + /** + * Replaces some child views with a new set of child views. This is + * implemented to call the superclass behaviour and invalidates the row + * grid so that rows and columns will be recalculated. + * + * @param offset the start offset at which to replace views + * @param length the number of views to remove + * @param views the new set of views + */ + public void replace(int offset, int length, View[] views) + { + super.replace(offset, length, views); + layoutChanged(X_AXIS); + } + + /** + * Lays out the box's child views along the major axis. This is + * reimplemented so that the child views all have the width of their + * column. + * + * @param targetSpan the total span of the view + * @param axis the axis that is laid out + * @param offsets an array that holds the offsets of the child views after + * this method returned + * @param spans an array that holds the spans of the child views after this + * method returned + */ + protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, + int[] spans) + { + // TODO: Maybe prepare columnSpans and columnOffsets. + + // Some sanity checks. If these preconditions are not met, then the + // following code will not work. Also, there must be something + // seriously wrong then. + assert(offsets.length == columnOffsets.length); + assert(spans.length == columnSpans.length); + assert(offsets.length == spans.length); + for (int i = 0; i < offsets.length; ++i) + { + offsets[i] = columnOffsets[i]; + spans[i] = columnSpans[i]; + } + } + + /** + * Lays out the box's child views along the minor axis (the orthogonal axis + * to the major axis). This is reimplemented to call the super behaviour + * and then adjust the span of the child views that span multiple rows. + * + * @param targetSpan the total span of the view + * @param axis the axis that is laid out + * @param offsets an array that holds the offsets of the child views after + * this method returned + * @param spans an array that holds the spans of the child views after this + * method returned + */ + protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, + int[] spans) + { + // FIXME: Figure out how to fetch the row heights from the TableView's + // element. + super.layoutMajorAxis(targetSpan, axis, offsets, spans); + } + + /** + * Determines the resizeability of this view along the specified axis. + * + * @param axis the axis of which to fetch the resizability + * + * @return the resize weight or <= 0 if this view is not resizable + * + * @throws IllegalArgumentException when an illegal axis is specified + */ + public int getResizeWeight(int axis) + { + // TODO: Figure out if this is ok. I would think so, but better test + // this. + return 0; + } + + /** + * Returns the child view that represents the specified position in the + * model. This is reimplemented because in this view we do not necessarily + * have a one to one mapping of child elements to child views. + * + * @param pos the model position for which to query the view + * @param a the allocation of this view + * + * @return the view that corresponds to the specified model position or + * <code>null</code> if there is none + */ + protected View getViewAtPosition(int pos, Rectangle a) + { + // FIXME: Do not call super here. Instead walk through the child views + // and look for a range that contains the given position. + return super.getViewAtPosition(pos, a); + } + } + + /** + * This class is deprecated and not used anymore. Table cells are + * rendered by an arbitrary <code>View</code> implementation. + * + * @author Roman Kennke (kennke@aicas.com) + * + * @deprecated Table cells are now rendered by an arbitrary <code>View</code> + * implementation. + */ + public class TableCell + extends BoxView + { + + /** + * The row number of this cell. + */ + private int row; + + /** + * The column number of this cell. + */ + private int column; + + /** + * Creates a new instance. + * + * @param el the element + * + * @deprecated Table cells are now rendered by an arbitrary + * <code>View</code> implementation. + */ + public TableCell(Element el) + { + super(el, X_AXIS); + } + + /** + * Returns the number of columns that this cell spans. + * + * @return the number of columns that this cell spans + * + * @deprecated Table cells are now rendered by an arbitrary + * <code>View</code> implementation. + */ + public int getColumnCount() + { + // TODO: Figure out if this is right. However, this is not so important + // since this class isn't used anyway (except maybe be application code + // that still uses this deprecated class). + return 1; + } + + /** + * Returns the number of rows that this cell spans. + * + * @return the number of rows that this cell spans + * + * @deprecated Table cells are now rendered by an arbitrary + * <code>View</code> implementation. + */ + public int getRowCount() + { + // TODO: Figure out if this is right. However, this is not so important + // since this class isn't used anyway (except maybe be application code + // that still uses this deprecated class). + return 1; + } + + /** + * Sets the grid location of this table cell. + * + * @param r the row of this cell + * @param c the column of this cell + * + * @deprecated Table cells are now rendered by an arbitrary + * <code>View</code> implementation. + */ + public void setGridLocation(int r, int c) + { + row = r; + column = c; + } + + /** + * Returns the row number of this cell. + * + * @return the row number of this cell + * + * @deprecated Table cells are now rendered by an arbitrary + * <code>View</code> implementation. + */ + public int getGridRow() + { + return row; + } + + /** + * Returns the column number of this cell. + * + * @return the column number of this cell + * + * @deprecated Table cells are now rendered by an arbitrary + * <code>View</code> implementation. + */ + public int getGridColumn() + { + return column; + } + } + + /** + * The offsets of the columns of this table. Package private to avoid + * synthetic accessor methods. + */ + int[] columnOffsets; + + /** + * The spans of the columns of this table. Package private to avoid + * synthetic accessor methods. + */ + int[] columnSpans; + + /** + * The size requirements of the columns. + */ + private SizeRequirements[] columnRequirements; + + /** + * Creates a new instance of <code>TableView</code>. + * + * @param el the element for which to create a table view + */ + public TableView(Element el) + { + super(el, Y_AXIS); + int numChildren = el.getElementCount(); + View[] rows = new View[numChildren]; + for (int i = 0; i < numChildren; ++i) + { + Element rowEl = el.getElement(i); + TableRow rowView = createTableRow(rowEl); + rows[i] = rowView; + } + replace(0, 0, rows); + } + + /** + * Replaces a number of child views with a set of new child views. This is + * implemented to call the superclass behaviour and invalidate the layout. + * + * @param offset the offset at which to replace child views + * @param length the number of child views to remove + * @param views the new set of views + */ + public void replace(int offset, int length, View[] views) + { + super.replace(offset, length, views); + layoutChanged(Y_AXIS); + } + + /** + * Creates a view for a table row. + * + * @param el the element that represents the table row + * + * @return a view for rendering the table row + */ + protected TableRow createTableRow(Element el) + { + return new TableRow(el); + } + + /** + * Creates a view for a table cell. This method is deprecated and not used + * anymore. + * + * @param el the element that represents the table cell + * + * @return a view for rendering the table cell + * + * @deprecated Table cells are now rendered by an arbitrary + * <code>View</code> implementation. + */ + protected TableCell createTableCell(Element el) + { + return new TableCell(el); + } + + protected void forwardUpdate(DocumentEvent.ElementChange ec, DocumentEvent e, + Shape a, ViewFactory vf) + { + // TODO: Figure out what to do here. + } + + /** + * Lays out the columns to fit within the specified target span. + * + * @param targetSpan the total span for the columns + * @param offsets an array that holds the offsets of the columns when this + * method returns + * @param spans an array that holds the spans of the columns when this method + * returns + * @param reqs the size requirements for each column + */ + protected void layoutColumns(int targetSpan, int[] offsets, int spans[], + SizeRequirements[] reqs) + { + // TODO: Figure out what exactly to do here. + } + + /** + * Lays out the child views along the minor axis of the table (that is the + * horizontal axis). This is implemented to call {@link #layoutColumns} to + * layout the column layout of this table, and then forward to the superclass + * to actually lay out the rows. + * + * @param targetSpan the available span along the minor (horizontal) axis + * @param axis the axis + * @param offsets an array that holds the offsets of the columns when this + * method returns + * @param spans an array that holds the spans of the columns when this method + * returns + */ + protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, + int[] spans) + { + // TODO: Prepare size requirements for the columns. + layoutColumns(targetSpan, columnOffsets, columnSpans, columnRequirements); + super.layoutMinorAxis(targetSpan, axis, offsets, spans); + } + + /** + * Calculates the requirements of this view for the minor (== horizontal) + * axis. + * + * This is reimplemented to calculate the requirements as the sum of the + * size requirements of the columns. + * + * @param axis the axis + * @param req the size requirements object to use, if <code>null</code> a new + * one will be created + */ + protected SizeRequirements calculateMinorAxisRequirements(int axis, + SizeRequirements req) + { + // TODO: Maybe prepare columnRequirements. + SizeRequirements res = req; + if (res == null) + res = new SizeRequirements(); + else + { + res.alignment = 0.5f; + res.maximum = 0; + res.minimum = 0; + res.preferred = 0; + } + + for (int i = 0; i < columnRequirements.length; ++i) + { + res.minimum += columnRequirements[i].minimum; + res.preferred += columnRequirements[i].preferred; + res.maximum += columnRequirements[i].maximum; + // TODO: Do we have to handle alignment somehow? + } + return res; + } + + /** + * Returns the child view that represents the specified position in the + * model. This is reimplemented because in this view we do not necessarily + * have a one to one mapping of child elements to child views. + * + * @param pos the model position for which to query the view + * @param a the allocation of this view + * + * @return the view that corresponds to the specified model position or + * <code>null</code> if there is none + */ + protected View getViewAtPosition(int pos, Rectangle a) + { + // FIXME: Do not call super here. Instead walk through the child views + // and look for a range that contains the given position. + return super.getViewAtPosition(pos, a); + } +} diff --git a/javax/swing/text/View.java b/javax/swing/text/View.java index 2de85446f..b835842bc 100644 --- a/javax/swing/text/View.java +++ b/javax/swing/text/View.java @@ -447,9 +447,11 @@ public abstract class View implements SwingConstants protected void updateLayout(DocumentEvent.ElementChange ec, DocumentEvent ev, Shape shape) { - Rectangle b = shape.getBounds(); - if (ec != null) - preferenceChanged(this, true, true); + if (ec != null && shape != null) + preferenceChanged(null, true, true); + Container c = getContainer(); + if (c != null) + c.repaint(); } /** diff --git a/javax/swing/text/html/BlockView.java b/javax/swing/text/html/BlockView.java new file mode 100644 index 000000000..6274e7b17 --- /dev/null +++ b/javax/swing/text/html/BlockView.java @@ -0,0 +1,301 @@ +/* BlockView.java -- + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text.html; + +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.Shape; + +import javax.swing.SizeRequirements; +import javax.swing.event.DocumentEvent; +import javax.swing.text.AttributeSet; +import javax.swing.text.BoxView; +import javax.swing.text.Element; +import javax.swing.text.View; +import javax.swing.text.ViewFactory; + +/** + * @author Lillian Angel <langel@redhat.com> + */ +public class BlockView extends BoxView +{ + + /** + * Creates a new view that represents an html box. + * This can be used for a number of elements. + * + * @param elem - the element to create a view for + * @param axis - either View.X_AXIS or View.Y_AXIS + */ + public BlockView(Element elem, int axis) + { + super(elem, axis); + } + + /** + * Creates the parent view for this. It is called before + * any other methods, if the parent view is working properly. + * Implemented to forward to the superclass and call + * setPropertiesFromAttributes to set the paragraph + * properties. + * + * @param parent - the new parent, or null if the view + * is being removed from a parent it was added to. + */ + public void setParent(View parent) + { + super.setParent(parent); + + if (parent != null) + setPropertiesFromAttributes(); + } + + /** + * Calculates the requirements along the major axis. + * This is implemented to call the superclass and then + * adjust it if the CSS width or height attribute is specified + * and applicable. + * + * @param axis - the axis to check the requirements for. + * @param r - the SizeRequirements. If null, one is created. + * @return the new SizeRequirements object. + */ + protected SizeRequirements calculateMajorAxisRequirements(int axis, + SizeRequirements r) + { + SizeRequirements sr = super.calculateMajorAxisRequirements(axis, r); + // FIXME: adjust it if the CSS width or height attribute is specified + // and applicable + return sr; + } + + /** + * Calculates the requirements along the minor axis. + * This is implemented to call the superclass and then + * adjust it if the CSS width or height attribute is specified + * and applicable. + * + * @param axis - the axis to check the requirements for. + * @param r - the SizeRequirements. If null, one is created. + * @return the new SizeRequirements object. + */ + protected SizeRequirements calculateMinorAxisRequirements(int axis, + SizeRequirements r) + { + SizeRequirements sr = super.calculateMinorAxisRequirements(axis, r); + // FIXME: adjust it if the CSS width or height attribute is specified + // and applicable. + return sr; + } + + /** + * Lays out the box along the minor axis (the axis that is + * perpendicular to the axis that it represents). The results + * of the layout are placed in the given arrays which are + * the allocations to the children along the minor axis. + * + * @param targetSpan - the total span given to the view, also + * used to layout the children. + * @param axis - the minor axis + * @param offsets - the offsets from the origin of the view for + * all the child views. This is a return value and is filled in by this + * function. + * @param spans - the span of each child view. This is a return value and is + * filled in by this function. + */ + protected void layoutMinorAxis(int targetSpan, int axis, + int[] offsets, int[] spans) + { + // FIXME: Not implemented. + super.layoutMinorAxis(targetSpan, axis, offsets, spans); + } + + /** + * Paints using the given graphics configuration and shape. + * This delegates to the css box painter to paint the + * border and background prior to the interior. + * + * @param g - Graphics configuration + * @param a - the Shape to render into. + */ + public void paint(Graphics g, Shape a) + { + Rectangle rect = (Rectangle) a; + // FIXME: not fully implemented + getStyleSheet().getBoxPainter(getAttributes()).paint(g, rect.x, rect.y, + rect.width, + rect.height, this); + super.paint(g, a); + } + + /** + * Fetches the attributes to use when painting. + * + * @return the attributes of this model. + */ + public AttributeSet getAttributes() + { + return getStyleSheet().getViewAttributes(this); + } + + /** + * Gets the resize weight. + * + * @param axis - the axis to get the resize weight for. + * @return the resize weight. + * @throws IllegalArgumentException - for an invalid axis + */ + public int getResizeWeight(int axis) throws IllegalArgumentException + { + // Can't resize the Y_AXIS + if (axis == Y_AXIS) + return 0; + if (axis == X_AXIS) + return 1; + throw new IllegalArgumentException("Invalid Axis"); + } + + /** + * Gets the alignment. + * + * @param axis - the axis to get the alignment for. + * @return the alignment. + */ + public float getAlignment(int axis) + { + if (axis == X_AXIS) + return 0.0F; + if (axis == Y_AXIS) + { + if (getViewCount() == 0) + return 0.0F; + float prefHeight = getPreferredSpan(Y_AXIS); + float firstRowHeight = getView(0).getPreferredSpan(Y_AXIS); + return (firstRowHeight / 2.F) / prefHeight; + } + throw new IllegalArgumentException("Invalid Axis"); + } + + /** + * Gives notification from the document that attributes were + * changed in a location that this view is responsible for. + * + * @param ev - the change information + * @param a - the current shape of the view + * @param f - the factory to use to rebuild if the view has children. + */ + public void changedUpdate(DocumentEvent ev, + Shape a, ViewFactory f) + { + super.changedUpdate(ev, a, f); + + // If more elements were added, then need to set the properties for them + int currPos = ev.getOffset(); + if (currPos <= getStartOffset() && (currPos + ev.getLength()) >= getEndOffset()) + setPropertiesFromAttributes(); + } + + /** + * Determines the preferred span along the axis. + * + * @param axis - the view to get the preferred span for. + * @return the span the view would like to be painted into >=0/ + * The view is usually told to paint into the span that is returned, + * although the parent may choose to resize or break the view. + * @throws IllegalArgumentException - for an invalid axis + */ + public float getPreferredSpan(int axis) throws IllegalArgumentException + { + if (axis == X_AXIS || axis == Y_AXIS) + return super.getPreferredSpan(axis); + throw new IllegalArgumentException("Invalid Axis"); + } + + /** + * Determines the minimum span along the axis. + * + * @param axis - the axis to get the minimum span for. + * @return the span the view would like to be painted into >=0/ + * The view is usually told to paint into the span that is returned, + * although the parent may choose to resize or break the view. + * @throws IllegalArgumentException - for an invalid axis + */ + public float getMinimumSpan(int axis) throws IllegalArgumentException + { + if (axis == X_AXIS || axis == Y_AXIS) + return super.getMinimumSpan(axis); + throw new IllegalArgumentException("Invalid Axis"); + } + + /** + * Determines the maximum span along the axis. + * + * @param axis - the axis to get the maximum span for. + * @return the span the view would like to be painted into >=0/ + * The view is usually told to paint into the span that is returned, + * although the parent may choose to resize or break the view. + * @throws IllegalArgumentException - for an invalid axis + */ + public float getMaximumSpan(int axis) throws IllegalArgumentException + { + if (axis == X_AXIS || axis == Y_AXIS) + return super.getMaximumSpan(axis); + throw new IllegalArgumentException("Invalid Axis"); + } + + /** + * Updates any cached values that come from attributes. + */ + protected void setPropertiesFromAttributes() + { + // FIXME: Not implemented (need to use StyleSheet). + } + + /** + * Gets the default style sheet. + * + * @return the style sheet + */ + protected StyleSheet getStyleSheet() + { + StyleSheet styleSheet = new StyleSheet(); + styleSheet.importStyleSheet(getClass().getResource(HTMLEditorKit.DEFAULT_CSS)); + return styleSheet; + } +} diff --git a/javax/swing/text/html/CSS.java b/javax/swing/text/html/CSS.java index 029ad26f8..c248e758e 100644 --- a/javax/swing/text/html/CSS.java +++ b/javax/swing/text/html/CSS.java @@ -37,6 +37,7 @@ exception statement from your version. */ package javax.swing.text.html; +import java.io.Serializable; import java.util.HashMap; /** @@ -46,7 +47,7 @@ import java.util.HashMap; * * @author Roman Kennke (kennke@aicas.com) */ -public class CSS +public class CSS implements Serializable { /** * Returns an array of all CSS attributes. diff --git a/javax/swing/text/html/CSSParser.java b/javax/swing/text/html/CSSParser.java new file mode 100644 index 000000000..0bf76eb80 --- /dev/null +++ b/javax/swing/text/html/CSSParser.java @@ -0,0 +1,568 @@ +/* CSSParser.java -- + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text.html; + +import java.io.*; + +/** + * Parses a CSS document. This works by way of a delegate that implements the + * CSSParserCallback interface. The delegate is notified of the following + * events: + * - Import statement: handleImport + * - Selectors handleSelector. This is invoked for each string. For example if + * the Reader contained p, bar , a {}, the delegate would be notified 4 times, + * for 'p,' 'bar' ',' and 'a'. + * - When a rule starts, startRule + * - Properties in the rule via the handleProperty. This + * is invoked one per property/value key, eg font size: foo;, would cause the + * delegate to be notified once with a value of 'font size'. + * - Values in the rule via the handleValue, this is notified for the total value. + * - When a rule ends, endRule + * + * @author Lillian Angel (langel@redhat.com) + */ +class CSSParser +{ + + /** + * Receives all information about the CSS document structure while parsing it. + * The methods are invoked by parser. + */ + static interface CSSParserCallback + { + /** + * Handles the import statment in the document. + * + * @param imp - the import string + */ + public abstract void handleImport(String imp); + + /** + * Called when the start of a rule is encountered. + */ + public abstract void startRule(); + + /** + * Called when the end of a rule is encountered. + */ + public abstract void endRule(); + + /** + * Handles the selector of a rule. + * + * @param selector - the selector in the rule + */ + public abstract void handleSelector(String selector); + + /** + * Handles the properties in the document. + * + * @param property - the property in the document. + */ + public abstract void handleProperty(String property); + + /** + * Handles the values in the document. + * + * @param value - the value to handle. + */ + public abstract void handleValue(String value); + + } + + /** + * The identifier of the rule. + */ + private static final int IDENTIFIER = 1; + + /** + * The open bracket. + */ + private static final int BRACKET_OPEN = 2; + + /** + * The close bracket. + */ + private static final int BRACKET_CLOSE = 3; + + /** + * The open brace. + */ + private static final int BRACE_OPEN = 4; + + /** + * The close brace. + */ + private static final int BRACE_CLOSE = 5; + + /** + * The open parentheses. + */ + private static final int PAREN_OPEN = 6; + + /** + * The close parentheses. + */ + private static final int PAREN_CLOSE = 7; + + /** + * The end of the document. + */ + private static final int END = -1; + + /** + * The character mapping in the document. + */ + // FIXME: What is this used for? + private static final char[] charMapping = null; + + /** + * Set to true if one character has been read ahead. + */ + private boolean didPushChar; + + /** + * The read ahead character. + */ + private int pushedChar; + + /** + * Temporary place to hold identifiers. + */ + private StringBuffer unitBuffer; + + /** + * Used to indicate blocks. + */ + private int[] unitStack; + + /** + * Number of valid blocks. + */ + private int stackCount; + + /** + * Holds the incoming CSS rules. + */ + private Reader reader; + + /** + * Set to true when the first non @ rule is encountered. + */ + private boolean encounteredRuleSet; + + /** + * The call back used to parse. + */ + private CSSParser.CSSParserCallback callback; + + /** + * nextToken() inserts the string here. + */ + private char[] tokenBuffer; + + /** + * Current number of chars in tokenBufferLength. + */ + private int tokenBufferLength; + + /** + * Set to true if any whitespace is read. + */ + private boolean readWS; + + /** + * Constructor + */ + CSSParser() + { + unitBuffer = new StringBuffer(); + tokenBuffer = new char[10]; + } + + /** + * Appends a character to the token buffer. + * + * @param c - the character to append + */ + private void append(char c) + { + if (tokenBuffer.length >= tokenBufferLength) + { + char[] temp = new char[tokenBufferLength * 2]; + if (tokenBuffer != null) + System.arraycopy(tokenBuffer, 0, temp, 0, tokenBufferLength); + + temp[tokenBufferLength] = c; + tokenBuffer = temp; + } + else + tokenBuffer[tokenBufferLength] = c; + tokenBufferLength++; + } + + /** + * Fetches the next token. + * + * @param c - the character to fetch. + * @return the location + * @throws IOException - any i/o error encountered while reading + */ + private int nextToken(char c) throws IOException + { + readWS = false; + int next = readWS(); + + switch (next) + { + case '\"': + if (tokenBufferLength > 0) + tokenBufferLength--; + return IDENTIFIER; + case '\'': + if (tokenBufferLength > 0) + tokenBufferLength--; + return IDENTIFIER; + case '(': + return PAREN_OPEN; + case ')': + return PAREN_CLOSE; + case '{': + return BRACE_OPEN; + case '}': + return BRACE_CLOSE; + case '[': + return BRACKET_OPEN; + case ']': + return BRACKET_CLOSE; + case -1: + return END; + default: + pushChar(next); + getIdentifier(c); + return IDENTIFIER; + } + } + + /** + * Reads a character from the stream. + * + * @return the number of characters read or -1 if end of stream is reached. + * @throws IOException - any i/o encountered while reading + */ + private int readChar() throws IOException + { + if (didPushChar) + { + didPushChar = false; + return pushedChar; + } + return reader.read(); + } + + /** + * Parses the the contents of the reader using the + * callback. + * + * @param reader - the reader to read from + * @param callback - the callback instance + * @param parsingDeclaration - true if parsing a declaration + * @throws IOException - any i/o error from the reader + */ + void parse(Reader reader, CSSParser.CSSParserCallback callback, + boolean parsingDeclaration) + throws IOException + { + this.reader = reader; + this.callback = callback; + + try + { + if (!parsingDeclaration) + while(getNextStatement()); + else + parseDeclarationBlock(); + } + catch (IOException ioe) + { + // Nothing to do here. + } + } + + /** + * Skips any white space, returning the character after the white space. + * + * @return the character after the whitespace + * @throws IOException - any i/o error from the reader + */ + private int readWS() throws IOException + { + int next = readChar(); + while (Character.isWhitespace((char) next)) + { + readWS = true; + int tempNext = readChar(); + if (tempNext == END) + return next; + next = tempNext; + } + + // Its all whitespace + return END; + } + + /** + * Gets the next statement, returning false if the end is reached. + * A statement is either an At-rule, or a ruleset. + * + * @return false if the end is reached + * @throws IOException - any i/o error from the reader + */ + private boolean getNextStatement() throws IOException + { + int c = nextToken((char) 0); + switch (c) + { + case PAREN_OPEN: + case BRACE_OPEN: + case BRACKET_OPEN: + parseTillClosed(c); + break; + case BRACKET_CLOSE: + case BRACE_CLOSE: + case PAREN_CLOSE: + throw new IOException("Not a proper statement."); + case IDENTIFIER: + if (tokenBuffer[0] == ('@')) + parseAtRule(); + else + parseRuleSet(); + break; + case END: + return false; + } + return true; + } + + /** + * Parses an @ rule, stopping at a matching brace pair, or ;. + * + * @throws IOException - any i/o error from the reader + */ + private void parseAtRule() throws IOException + { + // An At-Rule begins with the "@" character followed immediately by a keyword. + // Following the keyword separated by a space is an At-rule statement appropriate + // to the At-keyword used. If the At-Rule is a simple declarative statement + // (charset, import, fontdef), it is terminated by a semi-colon (";".) + // If the At-Rule is a conditional or informative statement (media, page, font-face), + // it is followed by optional arguments and then a style declaration block inside matching + // curly braces ("{", "}".) At-Rules are sometimes nestable, depending on the context. + // If any part of an At-Rule is not understood, it should be ignored. + + // FIXME: Not Implemented + // call handleimport + } + + /** + * Parses the next rule set, which is a selector followed by a declaration + * block. + * + * @throws IOException - any i/o error from the reader + */ + private void parseRuleSet() throws IOException + { + // call parseDeclarationBlock + // call parse selectors + // call parse identifiers + // call startrule/endrule + // FIXME: Not Implemented + } + + /** + * Parses a set of selectors, returning false if the end of the stream is + * reached. + * + * @return false if the end of stream is reached + * @throws IOException - any i/o error from the reader + */ + private boolean parseSelectors() throws IOException + { + // FIXME: Not Implemented + // call handleselector + return false; + } + + /** + * Parses a declaration block. Which a number of declarations followed by a + * })]. + * + * @throws IOException - any i/o error from the reader + */ + private void parseDeclarationBlock() throws IOException + { + // call parseDeclaration + // FIXME: Not Implemented + } + + /** + * Parses a single declaration, which is an identifier a : and another identifier. + * This returns the last token seen. + * + * @returns the last token + * @throws IOException - any i/o error from the reader + */ + private int parseDeclaration() throws IOException + { + // call handleValue + // FIXME: Not Implemented + return 0; + } + + /** + * Parses identifiers until c is encountered, returning the ending token, + * which will be IDENTIFIER if c is found. + * + * @param c - the stop character + * @param wantsBlocks - true if blocks are wanted + * @return the ending token + * @throws IOException - any i/o error from the reader + */ + private int parseIdentifiers(char c, boolean wantsBlocks) throws IOException + { + // FIXME: Not implemented + // call handleproperty? + return 0; + } + + /** + * Parses till a matching block close is encountered. This is only appropriate + * to be called at the top level (no nesting). + * + * @param i - FIXME + * @throws IOException - any i/o error from the reader + */ + private void parseTillClosed(int i) throws IOException + { + // FIXME: Not Implemented + } + + /** + * Gets an identifier, returning true if the length of the string is greater + * than 0, stopping when c, whitespace, or one of {}()[] is hit. + * + * @param c - the stop character + * @return returns true if the length of the string > 0 + * @throws IOException - any i/o error from the reader + */ + private boolean getIdentifier(char c) throws IOException + { + // FIXME: Not Implemented + return false; + } + + /** + * Reads till c is encountered, escaping characters as necessary. + * + * @param c - the stop character + * @throws IOException - any i/o error from the reader + */ + private void readTill(char c) throws IOException + { + // FIXME: Not Implemented + } + + /** + * Parses a comment block. + * + * @throws IOException - any i/o error from the reader + */ + private void readComment() throws IOException + { + // Should ignore comments. Read until end of comment. + // FIXME: Not implemented + } + + /** + * Called when a block start is encountered ({[. + * + * @param start of block + */ + private void startBlock(int start) + { + // FIXME: Not Implemented + } + + /** + * Called when an end block is encountered )]} + * + * @param end of block + */ + private void endBlock(int end) + { + // FIXME: Not Implemented + } + + /** + * Checks if currently in a block. + * + * @return true if currently in a block. + */ + private boolean inBlock() + { + // FIXME: Not Implemented + return false; + } + + /** + * Supports one character look ahead, this will throw if called twice in a row. + * + * @param c - the character to push. + * @throws IOException - if called twice in a row + */ + private void pushChar(int c) throws IOException + { + if (didPushChar) + throw new IOException("pushChar called twice."); + didPushChar = true; + pushedChar = c; + } +} + +
\ No newline at end of file diff --git a/javax/swing/text/html/HTMLDocument.java b/javax/swing/text/html/HTMLDocument.java index d048a04e6..5b2452b32 100644 --- a/javax/swing/text/html/HTMLDocument.java +++ b/javax/swing/text/html/HTMLDocument.java @@ -40,17 +40,32 @@ package javax.swing.text.html; import java.net.URL; +import java.io.IOException; + +import java.util.HashMap; +import java.util.Stack; +import java.util.Vector; + +import javax.swing.event.DocumentEvent; +import javax.swing.event.UndoableEditEvent; import javax.swing.text.AbstractDocument; import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; import javax.swing.text.DefaultStyledDocument; import javax.swing.text.Element; import javax.swing.text.ElementIterator; +import javax.swing.text.GapContent; +import javax.swing.text.MutableAttributeSet; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.StyleConstants; import javax.swing.text.html.HTML.Tag; /** - * TODO: This class is not yet completetely implemented. - * + * TODO: Add more comments here + * * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org) + * @author Anthony Balkissoon (abalkiss@redhat.com) + * @author Lillian Angel (langel@redhat.com) */ public class HTMLDocument extends DefaultStyledDocument { @@ -60,6 +75,195 @@ public class HTMLDocument extends DefaultStyledDocument public static final String AdditionalComments = "AdditionalComments"; URL baseURL = null; boolean preservesUnknownTags = true; + int tokenThreshold = Integer.MAX_VALUE; + HTMLEditorKit.Parser parser; + StyleSheet styleSheet; + AbstractDocument.Content content; + + /** + * Constructs an HTML document using the default buffer size and a default + * StyleSheet. + */ + public HTMLDocument() + { + this(null); + } + + /** + * Constructs an HTML document with the default content storage + * implementation and the specified style/attribute storage mechanism. + * + * @param styles - the style sheet + */ + public HTMLDocument(StyleSheet styles) + { + this(new GapContent(BUFFER_SIZE_DEFAULT), styles); + } + + /** + * Constructs an HTML document with the given content storage implementation + * and the given style/attribute storage mechanism. + * + * @param c - the document's content + * @param styles - the style sheet + */ + public HTMLDocument(AbstractDocument.Content c, StyleSheet styles) + { + this.content = c; + if (styles == null) + { + styles = new StyleSheet(); + styles.importStyleSheet(getClass().getResource(HTMLEditorKit. + DEFAULT_CSS)); + } + this.styleSheet = styles; + } + + /** + * Gets the style sheet with the document display rules (CSS) that were specified + * in the HTML document. + * + * @return - the style sheet + */ + public StyleSheet getStyleSheet() + { + return styleSheet; + } + + /** + * Replaces the contents of the document with the given element specifications. + * This is called before insert if the loading is done in bursts. This is the + * only method called if loading the document entirely in one burst. + * + * @param data - the date that replaces the content of the document + */ + protected void create(DefaultStyledDocument.ElementSpec[] data) + { + // FIXME: Not implemented + System.out.println("create not implemented"); + super.create(data); + } + + /** + * This method creates a root element for the new document. + * + * @return the new default root + */ + protected AbstractDocument.AbstractElement createDefaultRoot() + { + // FIXME: Not implemented + System.out.println("createDefaultRoot not implemented"); + return super.createDefaultRoot(); + } + + /** + * This method returns an HTMLDocument.RunElement object attached to + * parent representing a run of text from p0 to p1. The run has + * attributes described by a. + * + * @param parent - the parent element + * @param a - the attributes for the element + * @param p0 - the beginning of the range >= 0 + * @param p1 - the end of the range >= p0 + * @return the new element + */ + protected Element createLeafElement(Element parent, AttributeSet a, int p0, + int p1) + { + // FIXME: Not implemented + System.out.println("createLeafElement not implemented"); + return super.createLeafElement(parent, a, p0, p1); + } + + /** This method returns an HTMLDocument.BlockElement object representing the + * attribute set a and attached to parent. + * + * @param parent - the parent element + * @param a - the attributes for the element + * @return the new element + */ + protected Element createBranchElement(Element parent, AttributeSet a) + { + // FIXME: Not implemented + System.out.println("createBranchElement not implemented"); + return super.createBranchElement(parent, a); + } + + /** + * Inserts new elements in bulk. This is how elements get created in the + * document. The parsing determines what structure is needed and creates the + * specification as a set of tokens that describe the edit while leaving the + * document free of a write-lock. This method can then be called in bursts by + * the reader to acquire a write-lock for a shorter duration (i.e. while the + * document is actually being altered). + * + * @param offset - the starting offset + * @param data - the element data + * @throws BadLocationException - if the given position does not + * represent a valid location in the associated document. + */ + protected void insert(int offset, DefaultStyledDocument.ElementSpec[] data) + throws BadLocationException + { + super.insert(offset, data); + } + + /** + * Updates document structure as a result of text insertion. This will happen + * within a write lock. This implementation simply parses the inserted content + * for line breaks and builds up a set of instructions for the element buffer. + * + * @param chng - a description of the document change + * @param attr - the attributes + */ + protected void insertUpdate(AbstractDocument.DefaultDocumentEvent chng, + AttributeSet attr) + { + // FIXME: Not implemented + System.out.println("insertUpdate not implemented"); + super.insertUpdate(chng, attr); + } + + /** + * Returns the parser used by this HTMLDocument to insert HTML. + * + * @return the parser used by this HTMLDocument to insert HTML. + */ + public HTMLEditorKit.Parser getParser() + { + return parser; + } + + /** + * Sets the parser used by this HTMLDocument to insert HTML. + * + * @param p the parser to use + */ + public void setParser (HTMLEditorKit.Parser p) + { + parser = p; + } + /** + * Sets the number of tokens to buffer before trying to display the + * Document. + * + * @param n the number of tokens to buffer + */ + public void setTokenThreshold (int n) + { + tokenThreshold = n; + } + + /** + * Returns the number of tokens that are buffered before the document + * is rendered. + * + * @return the number of tokens buffered + */ + public int getTokenThreshold () + { + return tokenThreshold; + } /** * Returns the location against which to resolve relative URLs. @@ -79,7 +283,7 @@ public class HTMLDocument extends DefaultStyledDocument public void setBase(URL u) { baseURL = u; - //TODO: also set the base of the StyleSheet + styleSheet.setBase(u); } /** @@ -259,10 +463,1133 @@ public class HTMLDocument extends DefaultStyledDocument return null; } + /** + * Gets the name of the element. + * + * @return the name of the element if it exists, null otherwise. + */ public String getName() { - //FIXME: this is supposed to do something different from the super class - return super.getName(); + return (String) getAttribute(StyleConstants.NameAttribute); + } + } + + /** + * RunElement represents a section of text that has a set of + * HTML character level attributes assigned to it. + */ + public class RunElement extends AbstractDocument.LeafElement + { + + /** + * Constructs an element that has no children. It represents content + * within the document. + * + * @param parent - parent of this + * @param a - elements attributes + * @param start - the start offset >= 0 + * @param end - the end offset + */ + public RunElement(Element parent, AttributeSet a, int start, int end) + { + super(parent, a, start, end); + } + + /** + * Gets the name of the element. + * + * @return the name of the element if it exists, null otherwise. + */ + public String getName() + { + return (String) getAttribute(StyleConstants.NameAttribute); + } + + /** + * Gets the resolving parent. HTML attributes do not inherit at the + * model level, so this method returns null. + * + * @return null + */ + public AttributeSet getResolveParent() + { + return null; + } + } + + /** + * A reader to load an HTMLDocument with HTML structure. + * + * @author Anthony Balkissoon abalkiss at redhat dot com + */ + public class HTMLReader extends HTMLEditorKit.ParserCallback + { + /** Holds the current character attribute set **/ + protected MutableAttributeSet charAttr = new SimpleAttributeSet(); + + protected Vector parseBuffer = new Vector(); + + /** A stack for character attribute sets **/ + Stack charAttrStack = new Stack(); + + /** A mapping between HTML.Tag objects and the actions that handle them **/ + HashMap tagToAction; + + /** Tells us whether we've received the '</html>' tag yet **/ + boolean endHTMLEncountered = false; + + /** Variables related to the constructor with explicit insertTag **/ + int popDepth, pushDepth, offset; + HTML.Tag insertTag; + boolean insertTagEncountered = false; + + /** A temporary variable that helps with the printing out of debug information **/ + boolean debug = false; + + void print (String line) + { + if (debug) + System.out.println (line); + } + + public class TagAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. By default this does nothing. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + // Nothing to do here. + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. By default does nothing. + */ + public void end(HTML.Tag t) + { + // Nothing to do here. + } + } + + public class BlockAction extends TagAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + // Tell the parse buffer to open a new block for this tag. + blockOpen(t, a); + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + { + // Tell the parse buffer to close this block. + blockClose(t); + } + } + + public class CharacterAction extends TagAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + // Put the old attribute set on the stack. + pushCharacterStyle(); + + // And create the new one by adding the attributes in <code>a</code>. + if (a != null) + charAttr.addAttribute(t, a.copyAttributes()); + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + { + popCharacterStyle(); + } + } + + public class FormAction extends SpecialAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + // FIXME: Implement. + print ("FormAction.start not implemented"); + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + { + // FIXME: Implement. + print ("FormAction.end not implemented"); + } + } + + public class HiddenAction extends TagAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + // FIXME: Implement. + print ("HiddenAction.start not implemented"); + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + { + // FIXME: Implement. + print ("HiddenAction.end not implemented"); + } + } + + public class IsindexAction extends TagAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + // FIXME: Implement. + print ("IsindexAction.start not implemented"); + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + { + // FIXME: Implement. + print ("IsindexAction.end not implemented"); + } + } + + public class ParagraphAction extends BlockAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + // FIXME: Implement. + print ("ParagraphAction.start not implemented"); + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + { + // FIXME: Implement. + print ("ParagraphAction.end not implemented"); + } + } + + public class PreAction extends BlockAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + // FIXME: Implement. + print ("PreAction.start not implemented"); + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + { + // FIXME: Implement. + print ("PreAction.end not implemented"); + } + } + + public class SpecialAction extends TagAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + // FIXME: Implement. + print ("SpecialAction.start not implemented"); + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + { + // FIXME: Implement. + print ("SpecialAction.end not implemented"); + } + } + + class AreaAction extends TagAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + // FIXME: Implement. + print ("AreaAction.start not implemented"); + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + { + // FIXME: Implement. + print ("AreaAction.end not implemented"); + } + } + + class BaseAction extends TagAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + // FIXME: Implement. + print ("BaseAction.start not implemented"); + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + { + // FIXME: Implement. + print ("BaseAction.end not implemented"); + } + } + + class HeadAction extends BlockAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + // FIXME: Implement. + print ("HeadAction.start not implemented: "+t); + super.start(t, a); + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + { + // FIXME: Implement. + print ("HeadAction.end not implemented: "+t); + super.end(t); + } + } + + class LinkAction extends TagAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + // FIXME: Implement. + print ("LinkAction.start not implemented"); + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + { + // FIXME: Implement. + print ("LinkAction.end not implemented"); + } + } + + class MapAction extends TagAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + // FIXME: Implement. + print ("MapAction.start not implemented"); + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + { + // FIXME: Implement. + print ("MapAction.end not implemented"); + } + } + + class MetaAction extends TagAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + // FIXME: Implement. + print ("MetaAction.start not implemented"); + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + { + // FIXME: Implement. + print ("MetaAction.end not implemented"); + } + } + + class StyleAction extends TagAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + // FIXME: Implement. + print ("StyleAction.start not implemented"); + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + { + // FIXME: Implement. + print ("StyleAction.end not implemented"); + } + } + + class TitleAction extends TagAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + // FIXME: Implement. + print ("TitleAction.start not implemented"); + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + { + // FIXME: Implement. + print ("TitleAction.end not implemented"); + } + } + + public HTMLReader(int offset) + { + this (offset, 0, 0, null); + } + + public HTMLReader(int offset, int popDepth, int pushDepth, + HTML.Tag insertTag) + { + print ("HTMLReader created with pop: "+popDepth + + " push: "+pushDepth + " offset: "+offset + + " tag: "+insertTag); + this.insertTag = insertTag; + this.offset = offset; + this.popDepth = popDepth; + this.pushDepth = pushDepth; + initTags(); + } + + void initTags() + { + tagToAction = new HashMap(72); + CharacterAction characterAction = new CharacterAction(); + HiddenAction hiddenAction = new HiddenAction(); + AreaAction areaAction = new AreaAction(); + BaseAction baseAction = new BaseAction(); + BlockAction blockAction = new BlockAction(); + SpecialAction specialAction = new SpecialAction(); + ParagraphAction paragraphAction = new ParagraphAction(); + HeadAction headAction = new HeadAction(); + FormAction formAction = new FormAction(); + IsindexAction isindexAction = new IsindexAction(); + LinkAction linkAction = new LinkAction(); + MapAction mapAction = new MapAction(); + PreAction preAction = new PreAction(); + MetaAction metaAction = new MetaAction(); + StyleAction styleAction = new StyleAction(); + TitleAction titleAction = new TitleAction(); + + + tagToAction.put(HTML.Tag.A, characterAction); + tagToAction.put(HTML.Tag.ADDRESS, characterAction); + tagToAction.put(HTML.Tag.APPLET, hiddenAction); + tagToAction.put(HTML.Tag.AREA, areaAction); + tagToAction.put(HTML.Tag.B, characterAction); + tagToAction.put(HTML.Tag.BASE, baseAction); + tagToAction.put(HTML.Tag.BASEFONT, characterAction); + tagToAction.put(HTML.Tag.BIG, characterAction); + tagToAction.put(HTML.Tag.BLOCKQUOTE, blockAction); + tagToAction.put(HTML.Tag.BODY, blockAction); + tagToAction.put(HTML.Tag.BR, specialAction); + tagToAction.put(HTML.Tag.CAPTION, blockAction); + tagToAction.put(HTML.Tag.CENTER, blockAction); + tagToAction.put(HTML.Tag.CITE, characterAction); + tagToAction.put(HTML.Tag.CODE, characterAction); + tagToAction.put(HTML.Tag.DD, blockAction); + tagToAction.put(HTML.Tag.DFN, characterAction); + tagToAction.put(HTML.Tag.DIR, blockAction); + tagToAction.put(HTML.Tag.DIV, blockAction); + tagToAction.put(HTML.Tag.DL, blockAction); + tagToAction.put(HTML.Tag.DT, paragraphAction); + tagToAction.put(HTML.Tag.EM, characterAction); + tagToAction.put(HTML.Tag.FONT, characterAction); + tagToAction.put(HTML.Tag.FORM, blockAction); + tagToAction.put(HTML.Tag.FRAME, specialAction); + tagToAction.put(HTML.Tag.FRAMESET, blockAction); + tagToAction.put(HTML.Tag.H1, paragraphAction); + tagToAction.put(HTML.Tag.H2, paragraphAction); + tagToAction.put(HTML.Tag.H3, paragraphAction); + tagToAction.put(HTML.Tag.H4, paragraphAction); + tagToAction.put(HTML.Tag.H5, paragraphAction); + tagToAction.put(HTML.Tag.H6, paragraphAction); + tagToAction.put(HTML.Tag.HEAD, headAction); + tagToAction.put(HTML.Tag.HR, specialAction); + tagToAction.put(HTML.Tag.HTML, blockAction); + tagToAction.put(HTML.Tag.I, characterAction); + tagToAction.put(HTML.Tag.IMG, specialAction); + tagToAction.put(HTML.Tag.INPUT, formAction); + tagToAction.put(HTML.Tag.ISINDEX, isindexAction); + tagToAction.put(HTML.Tag.KBD, characterAction); + tagToAction.put(HTML.Tag.LI, blockAction); + tagToAction.put(HTML.Tag.LINK, linkAction); + tagToAction.put(HTML.Tag.MAP, mapAction); + tagToAction.put(HTML.Tag.MENU, blockAction); + tagToAction.put(HTML.Tag.META, metaAction); + tagToAction.put(HTML.Tag.NOFRAMES, blockAction); + tagToAction.put(HTML.Tag.OBJECT, specialAction); + tagToAction.put(HTML.Tag.OL, blockAction); + tagToAction.put(HTML.Tag.OPTION, formAction); + tagToAction.put(HTML.Tag.P, paragraphAction); + tagToAction.put(HTML.Tag.PARAM, hiddenAction); + tagToAction.put(HTML.Tag.PRE, preAction); + tagToAction.put(HTML.Tag.SAMP, characterAction); + tagToAction.put(HTML.Tag.SCRIPT, hiddenAction); + tagToAction.put(HTML.Tag.SELECT, formAction); + tagToAction.put(HTML.Tag.SMALL, characterAction); + tagToAction.put(HTML.Tag.STRIKE, characterAction); + tagToAction.put(HTML.Tag.S, characterAction); + tagToAction.put(HTML.Tag.STRONG, characterAction); + tagToAction.put(HTML.Tag.STYLE, styleAction); + tagToAction.put(HTML.Tag.SUB, characterAction); + tagToAction.put(HTML.Tag.SUP, characterAction); + tagToAction.put(HTML.Tag.TABLE, blockAction); + tagToAction.put(HTML.Tag.TD, blockAction); + tagToAction.put(HTML.Tag.TEXTAREA, formAction); + tagToAction.put(HTML.Tag.TH, blockAction); + tagToAction.put(HTML.Tag.TITLE, titleAction); + tagToAction.put(HTML.Tag.TR, blockAction); + tagToAction.put(HTML.Tag.TT, characterAction); + tagToAction.put(HTML.Tag.U, characterAction); + tagToAction.put(HTML.Tag.UL, blockAction); + tagToAction.put(HTML.Tag.VAR, characterAction); + } + + /** + * Pushes the current character style onto the stack. + * + */ + protected void pushCharacterStyle() + { + charAttrStack.push(charAttr); + } + + /** + * Pops a character style off of the stack and uses it as the + * current character style. + * + */ + protected void popCharacterStyle() + { + if (!charAttrStack.isEmpty()) + charAttr = (MutableAttributeSet) charAttrStack.pop(); + } + + /** + * Registers a given tag with a given Action. All of the well-known tags + * are registered by default, but this method can change their behaviour + * or add support for custom or currently unsupported tags. + * + * @param t the Tag to register + * @param a the Action for the Tag + */ + protected void registerTag(HTML.Tag t, HTMLDocument.HTMLReader.TagAction a) + { + tagToAction.put (t, a); + } + + /** + * This is the last method called on the HTMLReader, allowing any pending + * changes to be flushed to the HTMLDocument. + */ + public void flush() throws BadLocationException + { + DefaultStyledDocument.ElementSpec[] elements; + elements = new DefaultStyledDocument.ElementSpec[parseBuffer.size()]; + parseBuffer.copyInto(elements); + parseBuffer.removeAllElements(); + insert(offset, elements); + offset += HTMLDocument.this.getLength() - offset; + } + + /** + * This method is called by the parser to indicate a block of + * text was encountered. Should insert the text appropriately. + * + * @param data the text that was inserted + * @param pos the position at which the text was inserted + */ + public void handleText(char[] data, int pos) + { + if (data != null && data.length > 0) + addContent(data, 0, data.length); + } + + /** + * This method is called by the parser and should route the call to + * the proper handler for the tag. + * + * @param t the HTML.Tag + * @param a the attribute set + * @param pos the position at which the tag was encountered + */ + public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) + { + // Don't call the Action if we've already seen </html>. + if (endHTMLEncountered) + return; + + TagAction action = (TagAction) tagToAction.get(t); + if (action != null) + action.start(t, a); + } + + /** + * This method called by parser to handle a comment block. + * + * @param data the comment + * @param pos the position at which the comment was encountered + */ + public void handleComment(char[] data, int pos) + { + // Don't call the Action if we've already seen </html>. + if (endHTMLEncountered) + return; + + TagAction action = (TagAction) tagToAction.get(HTML.Tag.COMMENT); + if (action != null) + { + action.start(HTML.Tag.COMMENT, new SimpleAttributeSet()); + action.end (HTML.Tag.COMMENT); + } + } + + /** + * This method is called by the parser and should route the call to + * the proper handler for the tag. + * + * @param t the HTML.Tag + * @param pos the position at which the tag was encountered + */ + public void handleEndTag(HTML.Tag t, int pos) + { + // Don't call the Action if we've already seen </html>. + if (endHTMLEncountered) + return; + + // If this is the </html> tag we need to stop calling the Actions + if (t == HTML.Tag.HTML) + endHTMLEncountered = true; + + TagAction action = (TagAction) tagToAction.get(t); + if (action != null) + action.end(t); + } + + /** + * This is a callback from the parser that should be routed to the + * appropriate handler for the tag. + * + * @param t the HTML.Tag that was encountered + * @param a the attribute set + * @param pos the position at which the tag was encountered + */ + public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos) + { + // Don't call the Action if we've already seen </html>. + if (endHTMLEncountered) + return; + + TagAction action = (TagAction) tagToAction.get (t); + if (action != null) + { + action.start(t, a); + action.end(t); + } + } + + /** + * This is invoked after the stream has been parsed but before it has been + * flushed. + * + * @param eol one of \n, \r, or \r\n, whichever was encountered the most in + * parsing the stream + * @since 1.3 + */ + public void handleEndOfLineString(String eol) + { + // FIXME: Implement. + print ("HTMLReader.handleEndOfLineString not implemented yet"); + } + + /** + * Adds the given text to the textarea document. Called only when we are + * within a textarea. + * + * @param data the text to add to the textarea + */ + protected void textAreaContent(char[] data) + { + // FIXME: Implement. + print ("HTMLReader.textAreaContent not implemented yet"); + } + + /** + * Adds the given text that was encountered in a <PRE> element. + * + * @param data the text + */ + protected void preContent(char[] data) + { + // FIXME: Implement + print ("HTMLReader.preContent not implemented yet"); + } + + /** + * Instructs the parse buffer to create a block element with the given + * attributes. + * + * @param t the tag that requires opening a new block + * @param attr the attribute set for the new block + */ + protected void blockOpen(HTML.Tag t, MutableAttributeSet attr) + { + printBuffer(); + DefaultStyledDocument.ElementSpec element; + element = new DefaultStyledDocument.ElementSpec(attr.copyAttributes(), + DefaultStyledDocument.ElementSpec.StartTagType); + parseBuffer.addElement(element); + printBuffer(); + } + + /** + * Instructs the parse buffer to close the block element associated with + * the given HTML.Tag + * + * @param t the HTML.Tag that is closing its block + */ + protected void blockClose(HTML.Tag t) + { + printBuffer(); + DefaultStyledDocument.ElementSpec element; + element = new DefaultStyledDocument.ElementSpec(null, + DefaultStyledDocument.ElementSpec.EndTagType); + parseBuffer.addElement(element); + printBuffer(); } + + /** + * Adds text to the appropriate context using the current character + * attribute set. + * + * @param data the text to add + * @param offs the offset at which to add it + * @param length the length of the text to add + */ + protected void addContent(char[] data, int offs, int length) + { + addContent(data, offs, length, true); + } + + /** + * Adds text to the appropriate context using the current character + * attribute set, and possibly generating an IMPLIED Tag if necessary. + * + * @param data the text to add + * @param offs the offset at which to add it + * @param length the length of the text to add + * @param generateImpliedPIfNecessary whether or not we should generate + * an HTML.Tag.IMPLIED tag if necessary + */ + protected void addContent(char[] data, int offs, int length, + boolean generateImpliedPIfNecessary) + { + // Copy the attribute set, don't use the same object because + // it may change + AttributeSet attributes = null; + if (charAttr != null) + attributes = charAttr.copyAttributes(); + + DefaultStyledDocument.ElementSpec element; + element = new DefaultStyledDocument.ElementSpec(attributes, + DefaultStyledDocument.ElementSpec.ContentType, + data, offs, length); + + printBuffer(); + // Add the element to the buffer + parseBuffer.addElement(element); + printBuffer(); + + if (parseBuffer.size() > HTMLDocument.this.getTokenThreshold()) + { + try + { + flush(); + } + catch (BadLocationException ble) + { + // TODO: what to do here? + } + } + } + + /** + * Adds content that is specified in the attribute set. + * + * @param t the HTML.Tag + * @param a the attribute set specifying the special content + */ + protected void addSpecialElement(HTML.Tag t, MutableAttributeSet a) + { + // FIXME: Implement + print ("HTMLReader.addSpecialElement not implemented yet"); + } + + void printBuffer() + { + print ("\n*********BUFFER**********"); + for (int i = 0; i < parseBuffer.size(); i ++) + print (" "+parseBuffer.get(i)); + print ("***************************"); + } + } + + /** + * Gets the reader for the parser to use when loading the document with HTML. + * + * @param pos - the starting position + * @return - the reader + */ + public HTMLEditorKit.ParserCallback getReader(int pos) + { + return new HTMLReader(pos); + } + + /** + * Gets the reader for the parser to use when loading the document with HTML. + * + * @param pos - the starting position + * @param popDepth - the number of EndTagTypes to generate before inserting + * @param pushDepth - the number of StartTagTypes with a direction + * of JoinNextDirection that should be generated before inserting, + * but after the end tags have been generated. + * @param insertTag - the first tag to start inserting into document + * @return - the reader + */ + public HTMLEditorKit.ParserCallback getReader(int pos, + int popDepth, + int pushDepth, + HTML.Tag insertTag) + { + return new HTMLReader(pos, popDepth, pushDepth, insertTag); + } + + /** + * Gets the child element that contains the attribute with the value or null. + * Not thread-safe. + * + * @param e - the element to begin search at + * @param attribute - the desired attribute + * @param value - the desired value + * @return the element found with the attribute and value specified or null + * if it is not found. + */ + public Element getElement(Element e, Object attribute, Object value) + { + if (e != null) + { + if (e.getAttributes().containsAttribute(attribute, value)) + return e; + + int count = e.getElementCount(); + for (int j = 0; j < count; j++) + { + Element child = e.getElement(j); + if (child.getAttributes().containsAttribute(attribute, value)) + return child; + + Element grandChild = getElement(child, attribute, value); + if (grandChild != null) + return grandChild; + } + } + return null; + } + + /** + * Returns the element that has the given id Attribute. If it is not found, + * null is returned. This method works on an Attribute, not a character tag. + * This is not thread-safe. + * + * @param attrId - the Attribute id to look for + * @return the element that has the given id. + */ + public Element getElement(String attrId) + { + Element root = getDefaultRootElement(); + return getElement(root, HTML.getAttributeKey(attrId) , attrId); + } + + /** + * Replaces the children of the given element with the contents of + * the string. The document must have an HTMLEditorKit.Parser set. + * This will be seen as at least two events, n inserts followed by a remove. + * + * @param elem - the brance element whose children will be replaced + * @param htmlText - the string to be parsed and assigned to element. + * @throws BadLocationException + * @throws IOException + * @throws IllegalArgumentException - if elem is a leaf + * @throws IllegalStateException - if an HTMLEditorKit.Parser has not been set + */ + public void setInnerHTML(Element elem, String htmlText) + throws BadLocationException, IOException + { + if (elem.isLeaf()) + throw new IllegalArgumentException("Element is a leaf"); + if (parser == null) + throw new IllegalStateException("Parser has not been set"); + // FIXME: Not implemented fully, use InsertHTML* in HTMLEditorKit? + System.out.println("setInnerHTML not implemented"); + } + + /** + * Replaces the given element in the parent with the string. When replacing + * a leaf, this will attempt to make sure there is a newline present if one is + * needed. This may result in an additional element being inserted. + * This will be seen as at least two events, n inserts followed by a remove. + * The HTMLEditorKit.Parser must be set. + * + * @param elem - the branch element whose parent will be replaced + * @param htmlText - the string to be parsed and assigned to elem + * @throws BadLocationException + * @throws IOException + * @throws IllegalStateException - if parser is not set + */ + public void setOuterHTML(Element elem, String htmlText) + throws BadLocationException, IOException + { + if (parser == null) + throw new IllegalStateException("Parser has not been set"); + // FIXME: Not implemented fully, use InsertHTML* in HTMLEditorKit? + System.out.println("setOuterHTML not implemented"); + } + + /** + * Inserts the string before the start of the given element. + * The parser must be set. + * + * @param elem - the element to be the root for the new text. + * @param htmlText - the string to be parsed and assigned to elem + * @throws BadLocationException + * @throws IOException + * @throws IllegalStateException - if parser has not been set + */ + public void insertBeforeStart(Element elem, String htmlText) + throws BadLocationException, IOException + { + if (parser == null) + throw new IllegalStateException("Parser has not been set"); + // FIXME: Not implemented fully, use InsertHTML* in HTMLEditorKit? + System.out.println("insertBeforeStart not implemented"); + } + + /** + * Inserts the string at the end of the element. If elem's children + * are leaves, and the character at elem.getEndOffset() - 1 is a newline, + * then it will be inserted before the newline. The parser must be set. + * + * @param elem - the element to be the root for the new text + * @param htmlText - the text to insert + * @throws BadLocationException + * @throws IOException + * @throws IllegalStateException - if parser is not set + */ + public void insertBeforeEnd(Element elem, String htmlText) + throws BadLocationException, IOException + { + if (parser == null) + throw new IllegalStateException("Parser has not been set"); + // FIXME: Not implemented fully, use InsertHTML* in HTMLEditorKit? + System.out.println("insertBeforeEnd not implemented"); + } + + /** + * Inserts the string after the end of the given element. + * The parser must be set. + * + * @param elem - the element to be the root for the new text + * @param htmlText - the text to insert + * @throws BadLocationException + * @throws IOException + * @throws IllegalStateException - if parser is not set + */ + public void insertAfterEnd(Element elem, String htmlText) + throws BadLocationException, IOException + { + if (parser == null) + throw new IllegalStateException("Parser has not been set"); + // FIXME: Not implemented fully, use InsertHTML* in HTMLEditorKit? + System.out.println("insertAfterEnd not implemented"); + } + + /** + * Inserts the string at the start of the element. + * The parser must be set. + * + * @param elem - the element to be the root for the new text + * @param htmlText - the text to insert + * @throws BadLocationException + * @throws IOException + * @throws IllegalStateException - if parser is not set + */ + public void insertAfterStart(Element elem, String htmlText) + throws BadLocationException, IOException + { + if (parser == null) + throw new IllegalStateException("Parser has not been set"); + // FIXME: Not implemented fully, use InsertHTML* in HTMLEditorKit? + System.out.println("insertAfterStart not implemented"); + } + + /** + * This method sets the attributes associated with the paragraph containing + * offset. If replace is false, s is merged with existing attributes. The + * length argument determines how many characters are affected by the new + * attributes. This is often the entire paragraph. + * + * @param offset - + * the offset into the paragraph (must be at least 0) + * @param length - + * the number of characters affected (must be at least 0) + * @param s - + * the attributes + * @param replace - + * whether to replace existing attributes, or merge them + */ + public void setParagraphAttributes(int offset, int length, AttributeSet s, + boolean replace) + { + // FIXME: Not implemented. + System.out.println("setParagraphAttributes not implemented"); + super.setParagraphAttributes(offset, length, s, replace); + } + + /** + * This method flags a change in the document. + * + * @param e - the Document event + */ + protected void fireChangedUpdate(DocumentEvent e) + { + // FIXME: Not implemented. + System.out.println("fireChangedUpdate not implemented"); + super.fireChangedUpdate(e); + } + + /** + * This method fires an event intended to be caught by Undo listeners. It + * simply calls the super version inherited from DefaultStyledDocument. With + * this method, an HTML editor could easily provide undo support. + * + * @param e - the UndoableEditEvent + */ + protected void fireUndoableEditUpdate(UndoableEditEvent e) + { + super.fireUndoableEditUpdate(e); } } diff --git a/javax/swing/text/html/HTMLEditorKit.java b/javax/swing/text/html/HTMLEditorKit.java index a52d96c74..79e620327 100644 --- a/javax/swing/text/html/HTMLEditorKit.java +++ b/javax/swing/text/html/HTMLEditorKit.java @@ -38,28 +38,563 @@ exception statement from your version. */ package javax.swing.text.html; + +import java.awt.event.ActionEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionListener; +import java.awt.Cursor; + import java.io.IOException; import java.io.Reader; import java.io.Serializable; +import java.io.StringReader; +import java.io.Writer; +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; + +import javax.swing.Action; +import javax.swing.JEditorPane; +import javax.swing.text.AbstractDocument; import javax.swing.text.BadLocationException; +import javax.swing.text.BoxView; +import javax.swing.text.ComponentView; import javax.swing.text.Document; +import javax.swing.text.EditorKit; +import javax.swing.text.Element; +import javax.swing.text.IconView; +import javax.swing.text.LabelView; import javax.swing.text.MutableAttributeSet; +import javax.swing.text.ParagraphView; +import javax.swing.text.StyleConstants; +import javax.swing.text.StyleContext; import javax.swing.text.StyledEditorKit; +import javax.swing.text.TextAction; +import javax.swing.text.View; +import javax.swing.text.ViewFactory; import javax.swing.text.html.parser.ParserDelegator; /** - * This class is NOT implemented. This file currently holds only - * declarations of the two enclosing classes, necessary for testing - * the implemented javax.swing.text.html.parser package. - * - * @author No authorship is taken, implement the class and be! - * TODO: replace this header after implementing the class. + * @author Lillian Angel (langel at redhat dot com) */ public class HTMLEditorKit extends StyledEditorKit - implements Serializable, Cloneable + implements Serializable, Cloneable, Accessible { + + /** + * Fires the hyperlink events on the associated component + * when needed. + */ + public static class LinkController + extends MouseAdapter + implements MouseMotionListener, Serializable + { + + /** + * Constructor + */ + public LinkController() + { + super(); + } + + /** + * Dispatched when the mouse is clicked. If the component + * is read-only, then the clicked event is used to drive an + * attempt to follow the reference specified by a link + * + * @param e - the mouse event + */ + public void mouseClicked(MouseEvent e) + { + /* + These MouseInputAdapter methods generate mouse appropriate events around + hyperlinks (entering, exiting, and activating). + */ + // FIXME: Not implemented. + } + + /** + * Dispatched when the mouse is dragged on a component. + * + * @param e - the mouse event. + */ + public void mouseDragged(MouseEvent e) + { + /* + These MouseInputAdapter methods generate mouse appropriate events around + hyperlinks (entering, exiting, and activating). + */ + // FIXME: Not implemented. + } + + /** + * Dispatched when the mouse cursor has moved into the component. + * + * @param e - the mouse event. + */ + public void mouseMoved(MouseEvent e) + { + /* + These MouseInputAdapter methods generate mouse appropriate events around + hyperlinks (entering, exiting, and activating). + */ + // FIXME: Not implemented. + } + + /** + * If the given position represents a link, then linkActivated is called + * on the JEditorPane. Implemented to forward to the method with the same + * name, but pos == editor == -1. + * + * @param pos - the position + * @param editor - the editor pane + */ + protected void activateLink(int pos, + JEditorPane editor) + { + /* + This method creates and fires a HyperlinkEvent if the document is an + instance of HTMLDocument and the href tag of the link is not null. + */ + // FIXME: Not implemented. + } + } + + /** + * This class is used to insert a string of HTML into an existing + * document. At least 2 HTML.Tags need to be supplied. The first Tag (parentTag) + * identifies the parent in the document to add the elements to. The second, (addTag), + * identifies that the first tag should be added to the document as seen in the string. + * The parser will generate all appropriate (opening/closing tags_ even if they are not + * in the HTML string passed in. + */ + public static class InsertHTMLTextAction + extends HTMLTextAction + { + + /** + * Tag in HTML to start adding tags from. + */ + protected HTML.Tag addTag; + + /** + * Alternate tag in HTML to start adding tags from if parentTag is + * not found and alternateParentTag is not found. + */ + protected HTML.Tag alternateAddTag; + + /** + * Alternate tag to check if parentTag is not found. + */ + protected HTML.Tag alternateParentTag; + + /** + * HTML to insert. + */ + protected String html; + + /** + * Tag to check for in the document. + */ + protected HTML.Tag parentTag; + + /** + * Initializes all fields. + * + * @param name - the name of the document. + * @param html - the html to insert + * @param parentTag - the parent tag to check for + * @param addTag - the tag to start adding from + */ + public InsertHTMLTextAction(String name, String html, + HTML.Tag parentTag, HTML.Tag addTag) + { + this(name, html, parentTag, addTag, null, null); + } + + /** + * Initializes all fields and calls super + * + * @param name - the name of the document. + * @param html - the html to insert + * @param parentTag - the parent tag to check for + * @param addTag - the tag to start adding from + * @param alternateParentTag - the alternate parent tag + * @param alternateAddTag - the alternate add tag + */ + public InsertHTMLTextAction(String name, String html, HTML.Tag parentTag, + HTML.Tag addTag, HTML.Tag alternateParentTag, + HTML.Tag alternateAddTag) + { + super(name); + // Fields are for easy access when the action is applied to an actual + // document. + this.html = html; + this.parentTag = parentTag; + this.addTag = addTag; + this.alternateParentTag = alternateParentTag; + this.alternateAddTag = alternateAddTag; + } + + /** + * HTMLEditorKit.insertHTML is called. If an exception is + * thrown, it is wrapped in a RuntimeException and thrown. + * + * @param editor - the editor to use to get the editorkit + * @param doc - + * the Document to insert the HTML into. + * @param offset - + * where to begin inserting the HTML. + * @param html - + * the String to insert + * @param popDepth - + * the number of ElementSpec.EndTagTypes to generate before + * inserting + * @param pushDepth - + * the number of ElementSpec.StartTagTypes with a direction of + * ElementSpec.JoinNextDirection that should be generated before + * @param addTag - + * the first tag to start inserting into document + */ + protected void insertHTML(JEditorPane editor, HTMLDocument doc, int offset, + String html, int popDepth, int pushDepth, + HTML.Tag addTag) + { + try + { + super.getHTMLEditorKit(editor).insertHTML(doc, offset, html, + popDepth, pushDepth, addTag); + } + catch (IOException e) + { + throw (RuntimeException) new RuntimeException("Parser is null.").initCause(e); + } + catch (BadLocationException ex) + { + throw (RuntimeException) new RuntimeException("BadLocationException: " + + offset).initCause(ex); + } + } + + /** + * Invoked when inserting at a boundary. Determines the number of pops, + * and then the number of pushes that need to be performed. The it calls + * insertHTML. + * + * @param editor - + * the editor to use to get the editorkit + * @param doc - + * the Document to insert the HTML into. + * @param offset - + * where to begin inserting the HTML. + * @param insertElement - + * the element to insert + * @param html - + * the html to insert + * @param parentTag - + * the parent tag + * @param addTag - + * the first tag + */ + protected void insertAtBoundary(JEditorPane editor, + HTMLDocument doc, int offset, + Element insertElement, + String html, HTML.Tag parentTag, + HTML.Tag addTag) + { + /* + As its name implies, this protected method is used when HTML is inserted at a + boundary. (A boundary in this case is an offset in doc that exactly matches the + beginning offset of the parentTag.) It performs the extra work required to keep + the tag stack in shape and then calls insertHTML(). The editor and doc argu- + ments are the editor pane and document where the HTML should go. The offset + argument represents the cursor location or selection start in doc. The insert- + Element and parentTag arguments are used to calculate the proper number of + tag pops and pushes before inserting the HTML (via html and addTag, which are + passed directly to insertHTML()). + */ + // FIXME: not implemented + } + + /** + * Invoked when inserting at a boundary. Determines the number of pops, + * and then the number of pushes that need to be performed. The it calls + * insertHTML. + * + * @param editor - the editor to use to get the editorkit + * @param doc - + * the Document to insert the HTML into. + * @param offset - + * where to begin inserting the HTML. + * @param insertElement - the element to insert + * @param html - the html to insert + * @param parentTag - the parent tag + * @param addTag - the first tag + * + * @deprecated as of v1.3, use insertAtBoundary + */ + protected void insertAtBoundry(JEditorPane editor, + HTMLDocument doc, + int offset, Element insertElement, + String html, HTML.Tag parentTag, + HTML.Tag addTag) + { + insertAtBoundary(editor, doc, offset, insertElement, + html, parentTag, addTag); + } + + /** + * Inserts the HTML. + * + * @param ae - the action performed + */ + public void actionPerformed(ActionEvent ae) + { + Object source = ae.getSource(); + if (source instanceof JEditorPane) + { + JEditorPane pane = ((JEditorPane) source); + Document d = pane.getDocument(); + if (d instanceof HTMLDocument) + insertHTML(pane, (HTMLDocument) d, 0, html, 0, 0, addTag); + // FIXME: is this correct parameters? + } + // FIXME: else not implemented + } + } + + /** + * Abstract Action class that helps inserting HTML into an existing document. + */ + public abstract static class HTMLTextAction + extends StyledEditorKit.StyledTextAction + { + + /** + * Constructor + */ + public HTMLTextAction(String name) + { + super(name); + } + + /** + * Gets the HTMLDocument from the JEditorPane. + * + * @param e - the editor pane + * @return the html document. + */ + protected HTMLDocument getHTMLDocument(JEditorPane e) + { + Document d = e.getDocument(); + if (d instanceof HTMLDocument) + return (HTMLDocument) d; + throw new IllegalArgumentException("Document is not a HTMLDocument."); + } + + /** + * Gets the HTMLEditorKit + * + * @param e - the JEditorPane to get the HTMLEditorKit from. + * @return the HTMLEditorKit + */ + protected HTMLEditorKit getHTMLEditorKit(JEditorPane e) + { + EditorKit d = e.getEditorKit(); + if (d instanceof HTMLEditorKit) + return (HTMLEditorKit) d; + throw new IllegalArgumentException("EditorKit is not a HTMLEditorKit."); + } + + /** + * Returns an array of Elements that contain the offset. + * The first elements corresponds to the roots of the doc. + * + * @param doc - the document to get the Elements from. + * @param offset - the offset the Elements must contain + * @return an array of all the elements containing the offset. + */ + protected Element[] getElementsAt(HTMLDocument doc, + int offset) + { + return getElementsAt(doc.getDefaultRootElement(), offset, 0); + } + + /** + * Helper function to get all elements using recursion. + */ + private Element[] getElementsAt(Element root, int offset, int depth) + { + Element[] elements = null; + if (root != null) + { + if (root.isLeaf()) + { + elements = new Element[depth + 1]; + elements[depth] = root; + return elements; + } + elements = getElementsAt(root.getElement(root.getElementIndex(offset)), + offset, depth + 1); + elements[depth] = root; + } + return elements; + } + + /** + * Returns the number of elements, starting at the deepest point, needed + * to get an element representing tag. -1 if no elements are found, 0 if + * the parent of the leaf at offset represents the tag. + * + * @param doc - + * the document to search + * @param offset - + * the offset to check + * @param tag - + * the tag to look for + * @return - the number of elements needed to get an element representing + * tag. + */ + protected int elementCountToTag(HTMLDocument doc, + int offset, HTML.Tag tag) + { + Element root = doc.getDefaultRootElement(); + int num = -1; + Element next = root.getElement(root.getElementIndex(offset)); + + while (!next.isLeaf()) + { + num++; + if (next.getAttributes(). + getAttribute(StyleConstants.NameAttribute).equals(tag)) + return num; + next = next.getElement(next.getElementIndex(offset)); + } + return num; + } + + /** + * Gets the deepest element at offset with the + * matching tag. + * + * @param doc - the document to search + * @param offset - the offset to check for + * @param tag - the tag to match + * @return - the element that is found, null if not found. + */ + protected Element findElementMatchingTag(HTMLDocument doc, + int offset, HTML.Tag tag) + { + Element element = doc.getDefaultRootElement(); + Element tagElement = null; + + while (element != null) + { + Object otag = element.getAttributes().getAttribute( + StyleConstants.NameAttribute); + if (otag instanceof HTML.Tag && otag.equals(tag)) + tagElement = element; + element = element.getElement(element.getElementIndex(offset)); + } + + return tagElement; + } + } + + /** + * A {@link ViewFactory} that is able to create {@link View}s for + * the <code>Element</code>s that are supported. + */ + public static class HTMLFactory + implements ViewFactory + { + + /** + * Constructor + */ + public HTMLFactory() + { + // Do Nothing here. + } + + /** + * Creates a {@link View} for the specified <code>Element</code>. + * + * @param element the <code>Element</code> to create a <code>View</code> + * for + * @return the <code>View</code> for the specified <code>Element</code> + * or <code>null</code> if the type of <code>element</code> is + * not supported + */ + public View create(Element element) + { + View view = null; + Object attr = element.getAttributes().getAttribute( + StyleConstants.NameAttribute); + if (attr instanceof HTML.Tag) + { + HTML.Tag tag = (HTML.Tag) attr; + + if (tag.equals(HTML.Tag.IMPLIED) || tag.equals(HTML.Tag.P) + || tag.equals(HTML.Tag.H1) || tag.equals(HTML.Tag.H2) + || tag.equals(HTML.Tag.H3) || tag.equals(HTML.Tag.H4) + || tag.equals(HTML.Tag.H5) || tag.equals(HTML.Tag.H6) + || tag.equals(HTML.Tag.DT)) + view = new ParagraphView(element); + else if (tag.equals(HTML.Tag.LI) || tag.equals(HTML.Tag.DL) + || tag.equals(HTML.Tag.DD) || tag.equals(HTML.Tag.BODY) + || tag.equals(HTML.Tag.HTML) || tag.equals(HTML.Tag.CENTER) + || tag.equals(HTML.Tag.DIV) + || tag.equals(HTML.Tag.BLOCKQUOTE) + || tag.equals(HTML.Tag.PRE)) + view = new BlockView(element, View.Y_AXIS); + + // FIXME: Uncomment when the views have been implemented + /* else if (tag.equals(HTML.Tag.CONTENT)) + view = new InlineView(element); + else if (tag.equals(HTML.Tag.MENU) || tag.equals(HTML.Tag.DIR) + || tag.equals(HTML.Tag.UL) || tag.equals(HTML.Tag.OL)) + view = new ListView(element); + else if (tag.equals(HTML.Tag.IMG)) + view = new ImageView(element); + else if (tag.equals(HTML.Tag.HR)) + view = new HRuleView(element); + else if (tag.equals(HTML.Tag.BR)) + view = new BRView(element); + else if (tag.equals(HTML.Tag.TABLE)) + view = new TableView(element); + else if (tag.equals(HTML.Tag.INPUT) || tag.equals(HTML.Tag.SELECT) + || tag.equals(HTML.Tag.TEXTAREA)) + view = new FormView(element); + else if (tag.equals(HTML.Tag.OBJECT)) + view = new ObjectView(element); + else if (tag.equals(HTML.Tag.FRAMESET)) + view = new FrameSetView(element); + else if (tag.equals(HTML.Tag.FRAME)) + view = new FrameView(element); */ + } + + if (view == null) + { + String name = element.getName(); + if (name.equals(AbstractDocument.ContentElementName)) + view = new LabelView(element); + else if (name.equals(AbstractDocument.ParagraphElementName)) + view = new ParagraphView(element); + else if (name.equals(AbstractDocument.SectionElementName)) + view = new BoxView(element, View.Y_AXIS); + else if (name.equals(StyleConstants.ComponentElementName)) + view = new ComponentView(element); + else if (name.equals(StyleConstants.IconElementName)) + view = new IconView(element); + } + return view; + } + } + /** * The abstract HTML parser declaration. */ @@ -76,9 +611,7 @@ public class HTMLEditorKit * @throws IOException, normally if the reader throws one. */ public abstract void parse(Reader reader, ParserCallback callback, - boolean ignoreCharSet - ) - throws IOException; + boolean ignoreCharSet) throws IOException; } /** @@ -96,11 +629,19 @@ public class HTMLEditorKit public static final Object IMPLIED = "_implied_"; /** + * Constructor + */ + public ParserCallback() + { + // Nothing to do here. + } + + /** * The parser calls this method after it finishes parsing the document. */ public void flush() throws BadLocationException { - // TODO: What to do here, if anything? + // Nothing to do here. } /** @@ -110,7 +651,7 @@ public class HTMLEditorKit */ public void handleComment(char[] comment, int position) { - // TODO: What to do here, if anything? + // Nothing to do here. } /** @@ -121,7 +662,7 @@ public class HTMLEditorKit */ public void handleEndOfLineString(String end_of_line) { - // TODO: What to do here, if anything? + // Nothing to do here. } /** @@ -133,7 +674,7 @@ public class HTMLEditorKit */ public void handleEndTag(HTML.Tag tag, int position) { - // TODO: What to do here, if anything? + // Nothing to do here. } /** @@ -144,7 +685,7 @@ public class HTMLEditorKit */ public void handleError(String message, int position) { - // TODO: What to do here, if anything? + // Nothing to do here. } /** @@ -157,7 +698,7 @@ public class HTMLEditorKit public void handleSimpleTag(HTML.Tag tag, MutableAttributeSet attributes, int position) { - // TODO: What to do here, if anything? + // Nothing to do here. } /** @@ -168,10 +709,9 @@ public class HTMLEditorKit * @param position The tag position in the text being parsed */ public void handleStartTag(HTML.Tag tag, MutableAttributeSet attributes, - int position - ) + int position) { - // TODO: What to do here, if anything? + // Nothing to do here. } /** @@ -181,7 +721,7 @@ public class HTMLEditorKit */ public void handleText(char[] text, int position) { - // TODO: What to do here, if anything? + // Nothing to do here. } } @@ -255,7 +795,83 @@ public class HTMLEditorKit * The "ident paragraph right" action. */ public static final String PARA_INDENT_RIGHT = "html-para-indent-right"; - + + /** + * Actions for HTML + */ + private static final Action[] defaultActions = { + // FIXME: Add default actions for html + }; + + /** + * The current style sheet. + */ + StyleSheet styleSheet; + + /** + * The ViewFactory for HTMLFactory. + */ + HTMLFactory viewFactory; + + /** + * The Cursor for links. + */ + Cursor linkCursor; + + /** + * The default cursor. + */ + Cursor defaultCursor; + + /** + * The parser. + */ + Parser parser; + + /** + * The mouse listener used for links. + */ + LinkController mouseListener; + + /** + * Style context for this editor. + */ + StyleContext styleContext; + + /** The content type */ + String contentType = "text/html"; + + /** The input attributes defined by default.css */ + MutableAttributeSet inputAttributes; + + /** The editor pane used. */ + JEditorPane editorPane; + + /** + * Constructs an HTMLEditorKit, creates a StyleContext, and loads the style sheet. + */ + public HTMLEditorKit() + { + super(); + styleContext = new StyleContext(); + styleSheet = new StyleSheet(); + styleSheet.importStyleSheet(getClass().getResource(DEFAULT_CSS)); + // FIXME: Set inputAttributes with default.css + } + + /** + * Gets a factory suitable for producing views of any + * models that are produced by this kit. + * + * @return the view factory suitable for producing views. + */ + public ViewFactory getViewFactory() + { + if (viewFactory == null) + viewFactory = new HTMLFactory(); + return viewFactory; + } + /** * Create a text storage model for this type of editor. * @@ -263,18 +879,298 @@ public class HTMLEditorKit */ public Document createDefaultDocument() { - HTMLDocument document = new HTMLDocument(); + HTMLDocument document = new HTMLDocument(getStyleSheet()); + document.setParser(getParser()); return document; } /** * Get the parser that this editor kit uses for reading HTML streams. This * method can be overridden to use the alternative parser. - * + * * @return the HTML parser (by default, {@link ParserDelegator}). */ protected Parser getParser() { - return new ParserDelegator(); + if (parser == null) + parser = new ParserDelegator(); + return parser; + } + + /** + * Inserts HTML into an existing document. + * + * @param doc - the Document to insert the HTML into. + * @param offset - where to begin inserting the HTML. + * @param html - the String to insert + * @param popDepth - the number of ElementSpec.EndTagTypes + * to generate before inserting + * @param pushDepth - the number of ElementSpec.StartTagTypes + * with a direction of ElementSpec.JoinNextDirection that + * should be generated before + * @param insertTag - the first tag to start inserting into document + * @throws IOException - on any I/O error + * @throws BadLocationException - if pos represents an invalid location + * within the document + */ + public void insertHTML(HTMLDocument doc, int offset, String html, + int popDepth, int pushDepth, HTML.Tag insertTag) + throws BadLocationException, IOException + { + Parser parser = getParser(); + if (offset < 0 || offset > doc.getLength()) + throw new BadLocationException("Bad location", offset); + if (parser == null) + throw new IOException("Parser is null."); + + ParserCallback pc = ((HTMLDocument) doc).getReader + (offset, popDepth, pushDepth, insertTag); + + // FIXME: What should ignoreCharSet be set to? + + // parser.parse inserts html into the buffer + parser.parse(new StringReader(html), pc, false); + pc.flush(); + } + + /** + * Inserts content from the given stream. Inserting HTML into a non-empty + * document must be inside the body Element, if you do not insert into + * the body an exception will be thrown. When inserting into a non-empty + * document all tags outside of the body (head, title) will be dropped. + * + * @param in - the stream to read from + * @param doc - the destination for the insertion + * @param pos - the location in the document to place the content + * @throws IOException - on any I/O error + * @throws BadLocationException - if pos represents an invalid location + * within the document + */ + public void read(Reader in, Document doc, int pos) throws IOException, + BadLocationException + { + if (doc instanceof HTMLDocument) + { + Parser parser = getParser(); + if (pos < 0 || pos > doc.getLength()) + throw new BadLocationException("Bad location", pos); + if (parser == null) + throw new IOException("Parser is null."); + + HTMLDocument hd = ((HTMLDocument) doc); + hd.setBase(editorPane.getPage()); + ParserCallback pc = hd.getReader(pos); + + // FIXME: What should ignoreCharSet be set to? + + // parser.parse inserts html into the buffer + parser.parse(in, pc, false); + pc.flush(); + } + else + // read in DefaultEditorKit is called. + // the string is inserted in the document as usual. + super.read(in, doc, pos); + } + + /** + * Writes content from a document to the given stream in + * an appropriate format. + * + * @param out - the stream to write to + * @param doc - the source for the write + * @param pos - the location in the document to get the content. + * @param len - the amount to write out + * @throws IOException - on any I/O error + * @throws BadLocationException - if pos represents an invalid location + * within the document + */ + public void write(Writer out, Document doc, int pos, int len) + throws IOException, BadLocationException + { + if (doc instanceof HTMLDocument) + { + // FIXME: Not implemented. Use HTMLWriter. + out.write(doc.getText(pos, len)); + } + else + super.write(out, doc, pos, len); + } + + /** + * Gets the content type that the kit supports. + * This kit supports the type text/html. + * + * @returns the content type supported. + */ + public String getContentType() + { + return contentType; + } + + /** + * Creates a copy of the editor kit. + * + * @return a copy of this. + */ + public Object clone() + { + // FIXME: Need to clone all fields + return (HTMLEditorKit) super.clone(); + } + + /** + * Copies the key/values in elements AttributeSet into set. + * This does not copy component, icon, or element names attributes. + * This is called anytime the caret moves over a different location. + * + * @param element - the element to create the input attributes for. + * @param set - the set to copy the values into. + */ + protected void createInputAttributes(Element element, + MutableAttributeSet set) + { + set.removeAttributes(set); + set.addAttributes(element.getAttributes()); + // FIXME: Not fully implemented. + } + + /** + * Called when this is installed into the JEditorPane. + * + * @param c - the JEditorPane installed into. + */ + public void install(JEditorPane c) + { + super.install(c); + mouseListener = new LinkController(); + c.addMouseListener(mouseListener); + editorPane = c; + // FIXME: need to set up hyperlinklistener object + } + + /** + * Called when the this is removed from the JEditorPane. + * It unregisters any listeners. + * + * @param c - the JEditorPane being removed from. + */ + public void deinstall(JEditorPane c) + { + super.deinstall(c); + c.removeMouseListener(mouseListener); + mouseListener = null; + editorPane = null; + } + + /** + * Gets the AccessibleContext associated with this. + * + * @return the AccessibleContext for this. + */ + public AccessibleContext getAccessibleContext() + { + // FIXME: Should return an instance of + // javax.swing.text.html.AccessibleHTML$RootHTMLAccessibleContext + // Not implemented yet. + return null; + } + + /** + * Gets the action list. This list is supported by the superclass + * augmented by the collection of actions defined locally for style + * operations. + * + * @return an array of all the actions + */ + public Action[] getActions() + { + return TextAction.augmentList(super.getActions(), defaultActions); + } + + /** + * Returns the default cursor. + * + * @return the default cursor + */ + public Cursor getDefaultCursor() + { + if (defaultCursor == null) + defaultCursor = Cursor.getDefaultCursor(); + return defaultCursor; + } + + /** + * Returns the cursor for links. + * + * @return the cursor for links. + */ + public Cursor getLinkCursor() + { + if (linkCursor == null) + linkCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); + return linkCursor; + } + + /** + * Sets the Cursor for links. + * + * @param cursor - the new cursor for links. + */ + public void setLinkCursor(Cursor cursor) + { + linkCursor = cursor; + } + + /** + * Sets the default cursor. + * + * @param cursor - the new default cursor. + */ + public void setDefaultCursor(Cursor cursor) + { + defaultCursor = cursor; + } + + /** + * Gets the input attributes used for the styled editing actions. + * + * @return the attribute set + */ + public MutableAttributeSet getInputAttributes() + { + return inputAttributes; + } + + /** + * Get the set of styles currently being used to render the HTML elements. + * By default the resource specified by DEFAULT_CSS gets loaded, and is + * shared by all HTMLEditorKit instances. + * + * @return the style sheet. + */ + public StyleSheet getStyleSheet() + { + if (styleSheet == null) + { + styleSheet = new StyleSheet(); + styleSheet.importStyleSheet(getClass().getResource(DEFAULT_CSS)); + } + return styleSheet; + } + + /** + * Set the set of styles to be used to render the various HTML elements. + * These styles are specified in terms of CSS specifications. Each document + * produced by the kit will have a copy of the sheet which it can add the + * document specific styles to. By default, the StyleSheet specified is shared + * by all HTMLEditorKit instances. + * + * @param s - the new style sheet + */ + public void setStyleSheet(StyleSheet s) + { + styleSheet = s; } + } diff --git a/javax/swing/text/html/StyleSheet.java b/javax/swing/text/html/StyleSheet.java new file mode 100644 index 000000000..2466a2808 --- /dev/null +++ b/javax/swing/text/html/StyleSheet.java @@ -0,0 +1,937 @@ +/* StyleSheet.java -- + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text.html; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics; + +import java.io.IOException; +import java.io.Reader; +import java.io.Serializable; +import java.io.StringReader; + +import java.net.MalformedURLException; +import java.net.URL; + +import java.util.Enumeration; +import java.util.Vector; + +import javax.swing.text.AttributeSet; +import javax.swing.text.Element; +import javax.swing.text.MutableAttributeSet; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.Style; +import javax.swing.text.StyleContext; +import javax.swing.text.View; + + +/** + * This class adds support for defining the visual characteristics of HTML views + * being rendered. This enables views to be customized by a look-and-feel, mulitple + * views over the same model can be rendered differently. Each EditorPane has its + * own StyleSheet, but by default one sheet will be shared by all of the HTMLEditorKit + * instances. An HTMLDocument can also have a StyleSheet, which holds specific CSS + * specs. + * + * In order for Views to store less state and therefore be more lightweight, + * the StyleSheet can act as a factory for painters that handle some of the + * rendering tasks. Since the StyleSheet may be used by views over multiple + * documents the HTML attributes don't effect the selector being used. + * + * The rules are stored as named styles, and other information is stored to + * translate the context of an element to a rule. + * + * @author Lillian Angel (langel@redhat.com) + */ +public class StyleSheet extends StyleContext +{ + + /** The base URL */ + URL base; + + /** Base font size (int) */ + int baseFontSize; + + /** The style sheets stored. */ + StyleSheet[] styleSheet; + + /** + * Constructs a StyleSheet. + */ + public StyleSheet() + { + super(); + baseFontSize = 4; // Default font size from CSS + } + + /** + * Gets the style used to render the given tag. The element represents the tag + * and can be used to determine the nesting, where the attributes will differ + * if there is nesting inside of elements. + * + * @param t - the tag to translate to visual attributes + * @param e - the element representing the tag + * @return the set of CSS attributes to use to render the tag. + */ + public Style getRule(HTML.Tag t, Element e) + { + // FIXME: Not implemented. + return null; + } + + /** + * Gets the rule that best matches the selector. selector is a space + * separated String of element names. The attributes of the returned + * Style will change as rules are added and removed. + * + * @param selector - the element names separated by spaces + * @return the set of CSS attributes to use to render + */ + public Style getRule(String selector) + { + // FIXME: Not implemented. + return null; + } + + /** + * Adds a set if rules to the sheet. The rules are expected to be in valid + * CSS format. This is called as a result of parsing a <style> tag + * + * @param rule - the rule to add to the sheet + */ + public void addRule(String rule) + { + CssParser cp = new CssParser(); + try + { + cp.parse(base, new StringReader(rule), false, false); + } + catch (IOException io) + { + // Do nothing here. + } + } + + /** + * Translates a CSS declaration into an AttributeSet. This is called + * as a result of encountering an HTML style attribute. + * + * @param decl - the declaration to get + * @return the AttributeSet representing the declaration + */ + public AttributeSet getDeclaration(String decl) + { + if (decl == null) + return SimpleAttributeSet.EMPTY; + // FIXME: Not implemented. + return null; + } + + /** + * Loads a set of rules that have been specified in terms of CSS grammar. + * If there are any conflicts with existing rules, the new rule is added. + * + * @param in - the stream to read the CSS grammar from. + * @param ref - the reference URL. It is the location of the stream, it may + * be null. All relative URLs specified in the stream will be based upon this + * parameter. + * @throws IOException - For any IO error while reading + */ + public void loadRules(Reader in, URL ref) throws IOException + { + CssParser cp = new CssParser(); + cp.parse(ref, in, false, false); + } + + /** + * Gets a set of attributes to use in the view. This is a set of + * attributes that can be used for View.getAttributes + * + * @param v - the view to get the set for + * @return the AttributeSet to use in the view. + */ + public AttributeSet getViewAttributes(View v) + { + // FIXME: Not implemented. + return null; + } + + /** + * Removes a style previously added. + * + * @param nm - the name of the style to remove + */ + public void removeStyle(String nm) + { + // FIXME: Not implemented. + super.removeStyle(nm); + } + + /** + * Adds the rules from ss to those of the receiver. ss's rules will + * override the old rules. An added StyleSheet will never override the rules + * of the receiving style sheet. + * + * @param ss - the new StyleSheet. + */ + public void addStyleSheet(StyleSheet ss) + { + if (styleSheet == null) + styleSheet = new StyleSheet[] {ss}; + else + System.arraycopy(new StyleSheet[] {ss}, 0, styleSheet, + styleSheet.length, 1); + } + + /** + * Removes ss from those of the receiver + * + * @param ss - the StyleSheet to remove. + */ + public void removeStyleSheet(StyleSheet ss) + { + if (styleSheet.length == 1 && styleSheet[0].equals(ss)) + styleSheet = null; + else + { + for (int i = 0; i < styleSheet.length; i++) + { + StyleSheet curr = styleSheet[i]; + if (curr.equals(ss)) + { + StyleSheet[] tmp = new StyleSheet[styleSheet.length - 1]; + if (i != 0 && i != (styleSheet.length - 1)) + { + System.arraycopy(styleSheet, 0, tmp, 0, i); + System.arraycopy(styleSheet, i + 1, tmp, i, + styleSheet.length - i - 1); + } + else if (i == 0) + System.arraycopy(styleSheet, 1, tmp, 0, styleSheet.length - 1); + else + System.arraycopy(styleSheet, 0, tmp, 0, styleSheet.length - 1); + + styleSheet = tmp; + break; + } + } + } + } + + /** + * Returns an array of the linked StyleSheets. May return null. + * + * @return - An array of the linked StyleSheets. + */ + public StyleSheet[] getStyleSheets() + { + return styleSheet; + } + + /** + * Imports a style sheet from the url. The rules are directly added to the + * receiver. + * + * @param url - the URL to import the StyleSheet from. + */ + public void importStyleSheet(URL url) + { + // FIXME: Not implemented + } + + /** + * Sets the base url. All import statements that are relative, will be + * relative to base. + * + * @param base - + * the base URL. + */ + public void setBase(URL base) + { + this.base = base; + } + + /** + * Gets the base url. + * + * @return - the base + */ + public URL getBase() + { + return base; + } + + /** + * Adds a CSS attribute to the given set. + * + * @param attr - the attribute set + * @param key - the attribute to add + * @param value - the value of the key + */ + public void addCSSAttribute(MutableAttributeSet attr, CSS.Attribute key, + String value) + { + attr.addAttribute(key, value); + } + + /** + * Adds a CSS attribute to the given set. + * This method parses the value argument from HTML based on key. + * Returns true if it finds a valid value for the given key, + * and false otherwise. + * + * @param attr - the attribute set + * @param key - the attribute to add + * @param value - the value of the key + * @return true if a valid value was found. + */ + public boolean addCSSAttributeFromHTML(MutableAttributeSet attr, CSS.Attribute key, + String value) + { + // FIXME: Need to parse value from HTML based on key. + attr.addAttribute(key, value); + return attr.containsAttribute(key, value); + } + + /** + * Converts a set of HTML attributes to an equivalent set of CSS attributes. + * + * @param htmlAttrSet - the set containing the HTML attributes. + * @return the set of CSS attributes + */ + public AttributeSet translateHTMLToCSS(AttributeSet htmlAttrSet) + { + // FIXME: Not implemented. + return null; + } + + /** + * Adds an attribute to the given set and returns a new set. This is implemented + * to convert StyleConstants attributes to CSS before forwarding them to the superclass. + * The StyleConstants attribute do not have corresponding CSS entry, the attribute + * is stored (but will likely not be used). + * + * @param old - the old set + * @param key - the non-null attribute key + * @param value - the attribute value + * @return the updated set + */ + public AttributeSet addAttribute(AttributeSet old, Object key, + Object value) + { + // FIXME: Not implemented. + return super.addAttribute(old, key, value); + } + + /** + * Adds a set of attributes to the element. If any of these attributes are + * StyleConstants, they will be converted to CSS before forwarding to the + * superclass. + * + * @param old - the old set + * @param attr - the attributes to add + * @return the updated attribute set + */ + public AttributeSet addAttributes(AttributeSet old, AttributeSet attr) + { + // FIXME: Not implemented. + return super.addAttributes(old, attr); + } + + /** + * Removes an attribute from the set. If the attribute is a + * StyleConstants, it will be converted to CSS before forwarding to the + * superclass. + * + * @param old - the old set + * @param key - the non-null attribute key + * @return the updated set + */ + public AttributeSet removeAttribute(AttributeSet old, Object key) + { + // FIXME: Not implemented. + return super.removeAttribute(old, key); + } + + /** + * Removes an attribute from the set. If any of the attributes are + * StyleConstants, they will be converted to CSS before forwarding to the + * superclass. + * + * @param old - the old set + * @param attrs - the attributes to remove + * @return the updated set + */ + public AttributeSet removeAttributes(AttributeSet old, AttributeSet attrs) + { + // FIXME: Not implemented. + return super.removeAttributes(old, attrs); + } + + /** + * Removes a set of attributes for the element. If any of the attributes is a + * StyleConstants, they will be converted to CSS before forwarding to the + * superclass. + * + * @param old - the old attribute set + * @param names - the attribute names + * @return the update attribute set + */ + public AttributeSet removeAttributes(AttributeSet old, Enumeration names) + { + // FIXME: Not implemented. + return super.removeAttributes(old, names); + } + + /** + * Creates a compact set of attributes that might be shared. This is a hook + * for subclasses that want to change the behaviour of SmallAttributeSet. + * + * @param a - the set of attributes to be represented in the compact form. + * @return the set of attributes created + */ + protected StyleContext.SmallAttributeSet createSmallAttributeSet(AttributeSet a) + { + return super.createSmallAttributeSet(a); + } + + /** + * Creates a large set of attributes. This set is not shared. This is a hook + * for subclasses that want to change the behaviour of the larger attribute + * storage format. + * + * @param a - the set of attributes to be represented in the larger form. + * @return the large set of attributes. + */ + protected MutableAttributeSet createLargeAttributeSet(AttributeSet a) + { + return super.createLargeAttributeSet(a); + } + + /** + * Gets the font to use for the given set. + * + * @param a - the set to get the font for. + * @return the font for the set + */ + public Font getFont(AttributeSet a) + { + return super.getFont(a); + } + + /** + * Takes a set of attributes and turns it into a foreground + * color specification. This is used to specify things like, brigher, more hue + * etc. + * + * @param a - the set to get the foreground color for + * @return the foreground color for the set + */ + public Color getForeground(AttributeSet a) + { + return super.getForeground(a); + } + + /** + * Takes a set of attributes and turns it into a background + * color specification. This is used to specify things like, brigher, more hue + * etc. + * + * @param a - the set to get the background color for + * @return the background color for the set + */ + public Color getBackground(AttributeSet a) + { + return super.getBackground(a); + } + + /** + * Gets the box formatter to use for the given set of CSS attributes. + * + * @param a - the given set + * @return the box formatter + */ + public BoxPainter getBoxPainter(AttributeSet a) + { + return new BoxPainter(a); + } + + /** + * Gets the list formatter to use for the given set of CSS attributes. + * + * @param a - the given set + * @return the list formatter + */ + public ListPainter getListPainter(AttributeSet a) + { + return new ListPainter(a); + } + + /** + * Sets the base font size between 1 and 7. + * + * @param sz - the new font size for the base. + */ + public void setBaseFontSize(int sz) + { + if (sz <= 7 && sz >= 1) + baseFontSize = sz; + } + + /** + * Sets the base font size from the String. It can either identify + * a specific font size (between 1 and 7) or identify a relative + * font size such as +1 or -2. + * + * @param size - the new font size as a String. + */ + public void setBaseFontSize(String size) + { + size.trim(); + int temp = 0; + try + { + if (size.length() == 2) + { + int i = new Integer(size.substring(1)).intValue(); + if (size.startsWith("+")) + temp = baseFontSize + i; + else if (size.startsWith("-")) + temp = baseFontSize - i; + } + else if (size.length() == 1) + temp = new Integer(size.substring(0)).intValue(); + + if (temp <= 7 && temp >= 1) + baseFontSize = temp; + } + catch (NumberFormatException nfe) + { + // Do nothing here + } + } + + /** + * TODO + * + * @param pt - TODO + * @return TODO + */ + public static int getIndexOfSize(float pt) + { + // FIXME: Not implemented. + return 0; + } + + /** + * Gets the point size, given a size index. + * + * @param index - the size index + * @return the point size. + */ + public float getPointSize(int index) + { + // FIXME: Not implemented. + return 0; + } + + /** + * Given the string of the size, returns the point size value. + * + * @param size - the string representation of the size. + * @return - the point size value. + */ + public float getPointSize(String size) + { + // FIXME: Not implemented. + return 0; + } + + /** + * Converst a color string to a color. If it is not found, null is returned. + * + * @param color - the color string such as "RED" or "#NNNNNN" + * @return the Color, or null if not found. + */ + public Color stringToColor(String color) + { + color = color.toLowerCase(); + if (color.equals("black") || color.equals("#000000")) + return Color.BLACK; + else if (color.equals("aqua") || color.equals("#00FFFF")) + return new Color(127, 255, 212); + else if (color.equals("gray") || color.equals("#808080")) + return Color.GRAY; + else if (color.equals("navy") || color.equals("#000080")) + return new Color(0, 0, 128); + else if (color.equals("silver") || color.equals("#C0C0C0")) + return Color.LIGHT_GRAY; + else if (color.equals("green") || color.equals("#008000")) + return Color.GREEN; + else if (color.equals("olive") || color.equals("#808000")) + return new Color(128, 128, 0); + else if (color.equals("teal") || color.equals("#008080")) + return new Color(0, 128, 128); + else if (color.equals("blue") || color.equals("#0000FF")) + return Color.BLUE; + else if (color.equals("lime") || color.equals("#00FF00")) + return new Color(0, 255, 0); + else if (color.equals("purple") || color.equals("#800080")) + return new Color(128, 0, 128); + else if (color.equals("white") || color.equals("#FFFFFF")) + return Color.WHITE; + else if (color.equals("fuchsia") || color.equals("#FF00FF")) + return Color.MAGENTA; + else if (color.equals("maroon") || color.equals("#800000")) + return new Color(128, 0, 0); + else if (color.equals("Red") || color.equals("#FF0000")) + return Color.RED; + else if (color.equals("Yellow") || color.equals("#FFFF00")) + return Color.YELLOW; + return null; + } + + /** + * This class carries out some of the duties of CSS formatting. This enables views + * to present the CSS formatting while not knowing how the CSS values are cached. + * + * This object is reponsible for the insets of a View and making sure + * the background is maintained according to the CSS attributes. + * + * @author Lillian Angel (langel@redhat.com) + */ + public static class BoxPainter extends Object implements Serializable + { + + /** + * Attribute set for painter + */ + AttributeSet as; + + /** + * Package-private constructor. + * + * @param as - AttributeSet for painter + */ + BoxPainter(AttributeSet as) + { + this.as = as; + } + + /** + * Gets the inset needed on a given side to account for the margin, border + * and padding. + * + * @param size - the size of the box to get the inset for. View.TOP, View.LEFT, + * View.BOTTOM or View.RIGHT. + * @param v - the view making the request. This is used to get the AttributeSet, + * amd may be used to resolve percentage arguments. + * @return the inset + * @throws IllegalArgumentException - for an invalid direction. + */ + public float getInset(int size, View v) + { + // FIXME: Not implemented. + return 0; + } + + /** + * Paints the CSS box according to the attributes given. This should + * paint the border, padding and background. + * + * @param g - the graphics configuration + * @param x - the x coordinate + * @param y - the y coordinate + * @param w - the width of the allocated area + * @param h - the height of the allocated area + * @param v - the view making the request + */ + public void paint(Graphics g, float x, float y, float w, float h, View v) + { + // FIXME: Not implemented. + } + } + + /** + * This class carries out some of the CSS list formatting duties. Implementations + * of this class enable views to present the CSS formatting while not knowing anything + * about how the CSS values are being cached. + * + * @author Lillian Angel (langel@redhat.com) + */ + public static class ListPainter extends Object implements Serializable + { + + /** + * Attribute set for painter + */ + AttributeSet as; + + /** + * Package-private constructor. + * + * @param as - AttributeSet for painter + */ + ListPainter(AttributeSet as) + { + this.as = as; + } + + /** + * Paints the CSS list decoration according to the attributes given. + * + * @param g - the graphics configuration + * @param x - the x coordinate + * @param y - the y coordinate + * @param w - the width of the allocated area + * @param h - the height of the allocated area + * @param v - the view making the request + * @param item - the list item to be painted >=0. + */ + public void paint(Graphics g, float x, float y, float w, float h, View v, + int item) + { + // FIXME: Not implemented. + } + } + + /** + * The parser callback for the CSSParser. + */ + class CssParser implements CSSParser.CSSParserCallback + { + /** + * A vector of all the selectors. + * Each element is an array of all the selector tokens + * in a single rule. + */ + Vector selectors; + + /** A vector of all the selector tokens in a rule. */ + Vector selectorTokens; + + /** Name of the current property. */ + String propertyName; + + /** The set of CSS declarations */ + MutableAttributeSet declaration; + + /** + * True if parsing a declaration, that is the Reader will not + * contain a selector. + */ + boolean parsingDeclaration; + + /** True if the attributes are coming from a linked/imported style. */ + boolean isLink; + + /** The base URL */ + URL base; + + /** The parser */ + CSSParser parser; + + /** + * Constructor + */ + CssParser() + { + selectors = new Vector(); + selectorTokens = new Vector(); + parser = new CSSParser(); + base = StyleSheet.this.base; + declaration = new SimpleAttributeSet(); + } + + /** + * Parses the passed in CSS declaration into an AttributeSet. + * + * @param s - the declaration + * @return the set of attributes containing the property and value. + */ + public AttributeSet parseDeclaration(String s) + { + try + { + return parseDeclaration(new StringReader(s)); + } + catch (IOException e) + { + // Do nothing here. + } + return null; + } + + /** + * Parses the passed in CSS declaration into an AttributeSet. + * + * @param r - the reader + * @return the attribute set + * @throws IOException from the reader + */ + public AttributeSet parseDeclaration(Reader r) throws IOException + { + parse(base, r, true, false); + return declaration; + } + + /** + * Parse the given CSS stream + * + * @param base - the url + * @param r - the reader + * @param parseDec - True if parsing a declaration + * @param isLink - True if parsing a link + */ + public void parse(URL base, Reader r, boolean parseDec, boolean isLink) throws IOException + { + parsingDeclaration = parseDec; + this.isLink = isLink; + this.base = base; + + // flush out all storage + propertyName = null; + selectors.clear(); + selectorTokens.clear(); + declaration.removeAttributes(declaration); + + parser.parse(r, this, parseDec); + } + + /** + * Invoked when a valid @import is encountered, + * will call importStyleSheet if a MalformedURLException + * is not thrown in creating the URL. + * + * @param s - the string after @import + */ + public void handleImport(String s) + { + if (s != null) + { + try + { + if (s.startsWith("url(") && s.endsWith(")")) + s = s.substring(4, s.length() - 1); + if (s.indexOf("\"") >= 0) + s = s.replaceAll("\"",""); + + URL url = new URL(s); + if (url == null && base != null) + url = new URL(base, s); + + importStyleSheet(url); + } + catch (MalformedURLException e) + { + // Do nothing here. + } + } + } + + /** + * A selector has been encountered. + * + * @param s - a selector (e.g. P or UL or even P,) + */ + public void handleSelector(String s) + { + if (s.endsWith(",")) + s = s.substring(0, s.length() - 1); + + selectorTokens.addElement(s); + addSelector(); + } + + /** + * Invoked when the start of a rule is encountered. + */ + public void startRule() + { + addSelector(); + } + + /** + * Invoked when a property name is encountered. + * + * @param s - the property + */ + public void handleProperty(String s) + { + propertyName = s; + } + + /** + * Invoked when a property value is encountered. + * + * @param s - the value + */ + public void handleValue(String s) + { + // call addCSSAttribute + // FIXME: Not implemented + } + + /** + * Invoked when the end of a rule is encountered. + */ + public void endRule() + { + // FIXME: Not implemented + // add rules + propertyName = null; + } + + /** + * Adds the selector to the vector. + */ + private void addSelector() + { + int length = selectorTokens.size(); + if (length > 0) + { + Object[] sel = new Object[length]; + System.arraycopy(selectorTokens.toArray(), 0, sel, 0, length); + selectors.add(sel); + selectorTokens.clear(); + } + } + } +} diff --git a/javax/swing/text/html/default.css b/javax/swing/text/html/default.css new file mode 100644 index 000000000..f2a44f8c8 --- /dev/null +++ b/javax/swing/text/html/default.css @@ -0,0 +1,378 @@ +/* default.css -- + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +nobr { + white-space: nowrap; +} + +ol { + margin-right: 50px; + margin-top: 10px; + margin-left: 50px; + margin-bottom: 10px; + list-style-type: decimal; +} + +u { + text-decoration: underline; +} + +s { + text-decoration: line-through; +} + +p { + margin-top: 15px; +} + +dd p { + margin-left: 0px; + margin-top: 0px; + margin-bottom: 0px; +} + +ol li p { + margin-top: 0px; + margin-bottom: 0px; +} + + +address { + font-style: italic; + color: blue; +} + +i { + font-style: italic; +} + +h6 { + margin-top: 10px; + font-size: xx-small; + font-weight: bold; + margin-bottom: 10px; +} + +h5 { + margin-top: 10px; + font-size: x-small; + font-weight: bold; + margin-bottom: 10px; +} + +h4 { + margin-top: 10px; + font-size: small; + font-weight: bold; + margin-bottom: 10px; +} + +h3 { + margin-top: 10px; + font-size: medium; + font-weight: bold; + margin-bottom: 10px; +} + +dir li p { + margin-top: 0px; + margin-bottom: 0px; +} + +h2 { + margin-top: 10px; + font-size: large; + font-weight: bold; + margin-bottom: 10px; +} + +b { + font-weight: bold; +} + +h1 { + margin-top: 10px; + font-size: x-large; + font-weight: bold; + margin-bottom: 10px; +} + +caption { + text-align: center; + caption-side: top; +} + +a { + text-decoration: underline; + color: blue; +} + +ul li ul li ul li { + margin-left: 0px; + margin-top: 0px; + margin-bottom: 0px; +} + +menu { + margin-right: 40px; + margin-top: 10px; + margin-left: 40px; + margin-bottom: 10px; +} + +menu li p { + margin-top: 0px; + margin-bottom: 0px; +} + +sup { + vertical-align: super; +} + +body { + margin-right: 0px; + margin-left: 0px; + font-family: Serif; + font-size: 14pt; + font-weight: normal; + color: black; +} + +ul li ul li ul { + margin-right: 25px; + margin-left: 25px; + list-style-type: square; +} + +blockquote { + margin-right: 35px; + margin-left: 35px; + margin-top: 5px; + margin-bottom: 5px; +} + +samp { + font-family: Monospaced; + font-size: small; +} + +cite { + font-style: italic; +} + +sub { + vertical-align: sub; +} + +em { + font-style: italic; +} + +ul li p { + margin-top: 0px; + margin-bottom: 0px; +} + +ul li ul li { + margin-right: 0px; + margin-left: 0px; + margin-top: 0px; + margin-bottom: 0px; +} + +var { + font-style: italic; + font-weight: bold; +} + +table { + border-color: Gray; + border-style: outset; +} + +dfn { + font-style: italic; +} + +menu li { + margin-right: 0px; + margin-left: 0px; + margin-top: 0px; + margin-bottom: 0px; +} + +strong { + font-weight: bold; +} + +ul { + margin-right: 50px; + margin-top: 10px; + margin-left: 50px; + margin-bottom: 10px; + list-style-type: disc; +} + +center { + text-align: center; +} + +ul li ul { + margin-right: 25px; + margin-left: 25px; + list-style-type: circle; +} + +kbd { + font-family: Monospaced; + font-size: small; +} + +dir li { + margin-right: 0px; + margin-left: 0px; + margin-top: 0px; + margin-bottom: 0px; +} + +ul li menu { + margin-right: 25px; + margin-left: 25px; + list-style-type: circle; +} + +dt { + margin-top: 0px; + margin-bottom: 0px; +} + +ol li { + margin-right: 0px; + margin-left: 0px; + margin-top: 0px; + margin-bottom: 0px; +} + +li p { + margin-top: 0px; + margin-bottom: 0px; +} + +default { +} + +strike { + text-decoration: line-through; +} + +dl { + margin-left: 0px; + margin-top: 10px; + margin-bottom: 10px; +} + +tt { + font-family: Monospaced; +} + +ul li { + margin-right: 0px; + margin-left: 0px; + margin-top: 0px; + margin-bottom: 0px; +} + +dir { + margin-right: 40px; + margin-top: 10px; + margin-left: 40px; + margin-bottom: 10px; +} + +tr { + text-align: left; +} + +pre p { + margin-top: 0px; +} + +dd { + margin-right: 40px; + margin-top: 0px; + margin-left: 40px; + margin-bottom: 0px; +} + +th { + padding-bottom: 3px; + text-align: center; + padding-top: 3px; + padding-right: 3px; + padding-left: 3px; + font-weight: bold; + border-color: Gray; + border-style: inset; +} + +pre { + margin-top: 5px; + font-family: Monospaced; + margin-bottom: 5px; +} + +td { + padding-bottom: 3px; + padding-top: 3px; + padding-right: 3px; + padding-left: 3px; + border-color: Gray; + border-style: inset; +} + +code { + font-family: Monospaced; + font-size: small; +} + +small { + font-size: x-small; +} + +big { + font-size: x-large; +} diff --git a/javax/swing/tree/DefaultTreeModel.java b/javax/swing/tree/DefaultTreeModel.java index b6205c5db..c9ba7884a 100644 --- a/javax/swing/tree/DefaultTreeModel.java +++ b/javax/swing/tree/DefaultTreeModel.java @@ -148,12 +148,6 @@ public class DefaultTreeModel */ public void setRoot(TreeNode root) { - // Sanity Check - if (root == null) - { - throw new IllegalArgumentException("null root"); - } - // Set new root this.root = root; } diff --git a/javax/swing/tree/DefaultTreeSelectionModel.java b/javax/swing/tree/DefaultTreeSelectionModel.java index b30481425..61418316a 100644 --- a/javax/swing/tree/DefaultTreeSelectionModel.java +++ b/javax/swing/tree/DefaultTreeSelectionModel.java @@ -116,7 +116,7 @@ public class DefaultTreeSelectionModel */ public DefaultTreeSelectionModel() { - setSelectionMode(SINGLE_TREE_SELECTION); + setSelectionMode(DISCONTIGUOUS_TREE_SELECTION); listenerList = new EventListenerList(); } |