diff options
author | Mark Wielaard <mark@gcc.gnu.org> | 2006-01-17 18:09:40 +0000 |
---|---|---|
committer | Mark Wielaard <mark@gcc.gnu.org> | 2006-01-17 18:09:40 +0000 |
commit | 2127637945ea6b763966398130e0770fa993c860 (patch) | |
tree | c976ca91e3ef0bda3b34b37c0195145638d8d08e /libjava/classpath/javax/swing | |
parent | bcb36c3e02e3bd2843aad1b9888513dfb5d6e337 (diff) | |
download | gcc-2127637945ea6b763966398130e0770fa993c860.zip gcc-2127637945ea6b763966398130e0770fa993c860.tar.gz gcc-2127637945ea6b763966398130e0770fa993c860.tar.bz2 |
Imported GNU Classpath 0.20
Imported GNU Classpath 0.20
* Makefile.am (AM_CPPFLAGS): Add classpath/include.
* java/nio/charset/spi/CharsetProvider.java: New override file.
* java/security/Security.java: Likewise.
* sources.am: Regenerated.
* Makefile.in: Likewise.
From-SVN: r109831
Diffstat (limited to 'libjava/classpath/javax/swing')
92 files changed, 12107 insertions, 2608 deletions
diff --git a/libjava/classpath/javax/swing/AbstractAction.java b/libjava/classpath/javax/swing/AbstractAction.java index da65bdd..bd3167e 100644 --- a/libjava/classpath/javax/swing/AbstractAction.java +++ b/libjava/classpath/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/libjava/classpath/javax/swing/BoxLayout.java b/libjava/classpath/javax/swing/BoxLayout.java index ebc0b4c..408dea9 100644 --- a/libjava/classpath/javax/swing/BoxLayout.java +++ b/libjava/classpath/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/libjava/classpath/javax/swing/JComponent.java b/libjava/classpath/javax/swing/JComponent.java index deb08c5..747eba5 100644 --- a/libjava/classpath/javax/swing/JComponent.java +++ b/libjava/classpath/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; @@ -801,14 +799,23 @@ public abstract class JComponent extends Container implements Serializable } /** - * Return all registered listeners of a particular type. - * - * @param listenerType The type of listener to return - * - * @return All listeners in the {@link #listenerList} which - * are of the specified type + * Returns all registered {@link EventListener}s of the given + * <code>listenerType</code>. * + * @param listenerType the class of listeners to filter (<code>null</code> + * not permitted). + * + * @return An array of registered listeners. + * + * @throws ClassCastException if <code>listenerType</code> does not implement + * the {@link EventListener} interface. + * @throws NullPointerException if <code>listenerType</code> is + * <code>null</code>. + * + * @see #getAncestorListeners() * @see #listenerList + * + * @since 1.3 */ public EventListener[] getListeners(Class listenerType) { @@ -1287,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) { @@ -1298,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; } @@ -2530,7 +2532,10 @@ public abstract class JComponent extends Container implements Serializable public void setMaximumSize(Dimension max) { Dimension oldMaximumSize = maximumSize; - maximumSize = new Dimension(max); + if (max != null) + maximumSize = new Dimension(max); + else + maximumSize = null; firePropertyChange("maximumSize", oldMaximumSize, maximumSize); } @@ -2544,7 +2549,10 @@ public abstract class JComponent extends Container implements Serializable public void setMinimumSize(Dimension min) { Dimension oldMinimumSize = minimumSize; - minimumSize = new Dimension(min); + if (min != null) + minimumSize = new Dimension(min); + else + minimumSize = null; firePropertyChange("minimumSize", oldMinimumSize, minimumSize); } @@ -2558,7 +2566,10 @@ public abstract class JComponent extends Container implements Serializable public void setPreferredSize(Dimension pref) { Dimension oldPreferredSize = preferredSize; - preferredSize = new Dimension(pref); + if (pref != null) + preferredSize = new Dimension(pref); + else + preferredSize = null; firePropertyChange("preferredSize", oldPreferredSize, preferredSize); } @@ -2722,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() @@ -3253,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; @@ -3269,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/libjava/classpath/javax/swing/JEditorPane.java b/libjava/classpath/javax/swing/JEditorPane.java index 39f7c1f..3560ffd 100644 --- a/libjava/classpath/javax/swing/JEditorPane.java +++ b/libjava/classpath/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 { @@ -168,6 +171,14 @@ public class JEditorPane extends JTextComponent { /** + * Creates a new JEditorPaneAccessibleHypertextSupport object. + */ + public JEditorPaneAccessibleHypertextSupport() + { + super(); + } + + /** * The accessible representation of a HTML link. * * @author Roman Kennke (kennke@aicas.com) @@ -499,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()); } @@ -512,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; } /** @@ -578,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; } /** @@ -669,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); } /** @@ -694,6 +792,7 @@ public class JEditorPane extends JTextComponent public void replaceSelection(String content) { // TODO: Implement this properly. + super.replaceSelection(content); } /** @@ -741,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); } /** @@ -773,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/libjava/classpath/javax/swing/JFileChooser.java b/libjava/classpath/javax/swing/JFileChooser.java index 1598641..3a9d6a0 100644 --- a/libjava/classpath/javax/swing/JFileChooser.java +++ b/libjava/classpath/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; @@ -407,8 +408,7 @@ public class JFileChooser extends JComponent implements Accessible */ public JFileChooser(String currentDirectoryPath) { - setup(null); - setCurrentDirectory(fsv.createFileObject(currentDirectoryPath)); + this(currentDirectoryPath, null); } /** @@ -424,7 +424,10 @@ public class JFileChooser extends JComponent implements Accessible public JFileChooser(String currentDirectoryPath, FileSystemView fsv) { setup(fsv); - setCurrentDirectory(fsv.createFileObject(currentDirectoryPath)); + File dir = null; + if (currentDirectoryPath != null) + dir = getFileSystemView().createFileObject(currentDirectoryPath); + setCurrentDirectory(dir); } /** @@ -525,7 +528,7 @@ public class JFileChooser extends JComponent implements Accessible */ public void setSelectedFile(File file) { - if (selectedFile != file) + if (selectedFile == null || !selectedFile.equals(file)) { File old = selectedFile; selectedFile = file; @@ -534,10 +537,10 @@ public class JFileChooser extends JComponent implements Accessible } /** - * Returns the selected file or files. + * Returns the selected file or files in an array. If no files are selected, + * an empty array is returned. * - * @return An array of the selected files, or <code>null</code> if there are - * no selected files. + * @return An array of the selected files (possibly empty). */ public File[] getSelectedFiles() { @@ -545,7 +548,7 @@ public class JFileChooser extends JComponent implements Accessible return selectedFiles; if (selectedFile != null) return new File[] { selectedFile }; - return null; + return new File[0]; } /** @@ -557,6 +560,12 @@ public class JFileChooser extends JComponent implements Accessible */ public void setSelectedFiles(File[] selectedFiles) { + if (selectedFiles == null) + selectedFiles = new File[0]; + if (selectedFiles.length > 0) + setSelectedFile(selectedFiles[0]); + else + setSelectedFile(null); if (this.selectedFiles != selectedFiles) { File[] old = this.selectedFiles; @@ -564,8 +573,6 @@ public class JFileChooser extends JComponent implements Accessible firePropertyChange(SELECTED_FILES_CHANGED_PROPERTY, old, selectedFiles); } - if (selectedFiles != null) - setSelectedFile(selectedFiles[0]); } /** @@ -607,8 +614,7 @@ public class JFileChooser extends JComponent implements Accessible */ public void changeToParentDirectory() { - if (fsv.getParentDirectory(currentDir) != null) - setCurrentDirectory(fsv.getParentDirectory(currentDir)); + setCurrentDirectory(fsv.getParentDirectory(currentDir)); } /** @@ -652,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; } @@ -676,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; } @@ -702,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; } @@ -958,14 +967,19 @@ public class JFileChooser extends JComponent implements Accessible * {@link #CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY}) to all registered * listeners. * - * @param filter the filter. + * @param filter the filter (<code>null</code> permitted). */ public void addChoosableFileFilter(FileFilter filter) { - FileFilter[] old = getChoosableFileFilters(); - choosableFilters.add(filter); - FileFilter[] newFilters = getChoosableFileFilters(); - firePropertyChange(CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY, old, newFilters); + if (filter != null) + { + FileFilter[] old = getChoosableFileFilters(); + choosableFilters.add(filter); + FileFilter[] newFilters = getChoosableFileFilters(); + firePropertyChange(CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY, old, + newFilters); + } + setFileFilter(filter); } /** @@ -981,6 +995,8 @@ public class JFileChooser extends JComponent implements Accessible */ public boolean removeChoosableFileFilter(FileFilter f) { + if (f == currentFilter) + setFileFilter(null); FileFilter[] old = getChoosableFileFilters(); if (! choosableFilters.remove(f)) return false; @@ -1037,6 +1053,10 @@ public class JFileChooser extends JComponent implements Accessible if (isAcceptAll != b) { isAcceptAll = b; + if (b) + addChoosableFileFilter(getAcceptAllFileFilter()); + else + removeChoosableFileFilter(getAcceptAllFileFilter()); firePropertyChange(ACCEPT_ALL_FILE_FILTER_USED_CHANGED_PROPERTY, ! isAcceptAll, isAcceptAll); } @@ -1208,15 +1228,17 @@ public class JFileChooser extends JComponent implements Accessible * property name {@link #FILE_FILTER_CHANGED_PROPERTY}) to all registered * listeners. * - * @param filter the filter. + * @param filter the filter (<code>null</code> permitted). */ public void setFileFilter(FileFilter filter) { if (currentFilter != filter) { - FileFilter old = currentFilter; - currentFilter = filter; - firePropertyChange(FILE_FILTER_CHANGED_PROPERTY, old, currentFilter); + if (filter != null && !choosableFilters.contains(filter)) + addChoosableFileFilter(filter); + FileFilter old = currentFilter; + currentFilter = filter; + firePropertyChange(FILE_FILTER_CHANGED_PROPERTY, old, currentFilter); } } @@ -1356,8 +1378,12 @@ public class JFileChooser extends JComponent implements Accessible public boolean accept(File f) { if (f == null) - return false; - return getFileFilter().accept(f); + return true; + FileFilter ff = getFileFilter(); + if (ff != null) + return ff.accept(f); + else + return true; } /** diff --git a/libjava/classpath/javax/swing/JFormattedTextField.java b/libjava/classpath/javax/swing/JFormattedTextField.java index 9890df2..761955d 100644 --- a/libjava/classpath/javax/swing/JFormattedTextField.java +++ b/libjava/classpath/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/libjava/classpath/javax/swing/JInternalFrame.java b/libjava/classpath/javax/swing/JInternalFrame.java index 479294b..948988c 100644 --- a/libjava/classpath/javax/swing/JInternalFrame.java +++ b/libjava/classpath/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/libjava/classpath/javax/swing/JLayeredPane.java b/libjava/classpath/javax/swing/JLayeredPane.java index 346570d..dc8b10d 100644 --- a/libjava/classpath/javax/swing/JLayeredPane.java +++ b/libjava/classpath/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/libjava/classpath/javax/swing/JList.java b/libjava/classpath/javax/swing/JList.java index caa7f31..d0e1090 100644 --- a/libjava/classpath/javax/swing/JList.java +++ b/libjava/classpath/javax/swing/JList.java @@ -1070,14 +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.MULTIPLE_INTERVAL_SELECTION); + setLayout(null); + updateUI(); } @@ -1255,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/libjava/classpath/javax/swing/JMenuItem.java b/libjava/classpath/javax/swing/JMenuItem.java index c87a4dc..b2cfbd1 100644 --- a/libjava/classpath/javax/swing/JMenuItem.java +++ b/libjava/classpath/javax/swing/JMenuItem.java @@ -1,5 +1,5 @@ /* JMenuItem.java -- - Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005,2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -117,6 +117,24 @@ public class JMenuItem extends AbstractButton implements Accessible, super(); super.setAction(action); init(null, null); + if (action != null) + { + String name = (String) action.getValue(Action.NAME); + if (name != null) + setName(name); + + KeyStroke accel = (KeyStroke) action.getValue(Action.ACCELERATOR_KEY); + if (accel != null) + setAccelerator(accel); + + Integer mnemonic = (Integer) action.getValue(Action.MNEMONIC_KEY); + if (mnemonic != null) + setMnemonic(mnemonic.intValue()); + + String command = (String) action.getValue(Action.ACTION_COMMAND_KEY); + if (command != null) + setActionCommand(command); + } } /** @@ -273,8 +291,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/libjava/classpath/javax/swing/JProgressBar.java b/libjava/classpath/javax/swing/JProgressBar.java index 0de9115..abca3e7 100644 --- a/libjava/classpath/javax/swing/JProgressBar.java +++ b/libjava/classpath/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/libjava/classpath/javax/swing/JTabbedPane.java b/libjava/classpath/javax/swing/JTabbedPane.java index 27ba7bb..8a7d4c0 100644 --- a/libjava/classpath/javax/swing/JTabbedPane.java +++ b/libjava/classpath/javax/swing/JTabbedPane.java @@ -497,7 +497,7 @@ public class JTabbedPane extends JComponent implements Serializable, */ public int getMnemonic() { - return (int) mnemonicKey; + return mnemonicKey; } /** @@ -592,9 +592,11 @@ public class JTabbedPane extends JComponent implements Serializable, /** * Creates a new JTabbedPane object using wrap tab layout and the given - * tabPlacement. + * <code>tabPlacement</code>, where <code>tabPlacement</code> can be one + * of the following values: {@link #TOP}, {@link #BOTTOM}, {@link #LEFT} or + * {@link #RIGHT}. * - * @param tabPlacement Where the tabs will be placed. + * @param tabPlacement where the tabs will be placed */ public JTabbedPane(int tabPlacement) { @@ -602,11 +604,14 @@ public class JTabbedPane extends JComponent implements Serializable, } /** - * Creates a new JTabbedPane object with the given tabPlacement and - * tabLayoutPolicy. + * Creates a new JTabbedPane object with the given <code>tabPlacement</code> + * and <code>tabLayoutPolicy</code>. The <code>tabPlacement</code> can be one + * of the following values: {@link #TOP}, {@link #BOTTOM}, {@link #LEFT} or + * {@link #RIGHT}. The <code>tabLayoutPolicy</code> can be either + * {@link #SCROLL_TAB_LAYOUT} or {@link #WRAP_TAB_LAYOUT}. * - * @param tabPlacement Where the tabs will be placed. - * @param tabLayoutPolicy The way tabs will be placed. + * @param tabPlacement where the tabs will be placed + * @param tabLayoutPolicy the way tabs will be placed * * @throws IllegalArgumentException If tabLayoutPolicy or tabPlacement are * not valid. @@ -913,7 +918,7 @@ public class JTabbedPane extends JComponent implements Serializable, if (getSelectedIndex() == -1) setSelectedIndex(0); - layout(); + revalidate(); repaint(); } diff --git a/libjava/classpath/javax/swing/JTable.java b/libjava/classpath/javax/swing/JTable.java index 69a865d..0875306 100644 --- a/libjava/classpath/javax/swing/JTable.java +++ b/libjava/classpath/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 @@ -949,10 +989,13 @@ public class JTable if (ev.getPropertyName().equals("preferredWidth")) { JTableHeader header = getTableHeader(); - TableColumn col = (TableColumn) ev.getSource(); - header.setResizingColumn(col); - doLayout(); - header.setResizingColumn(null); + if (header != null) + { + TableColumn col = (TableColumn) ev.getSource(); + header.setResizingColumn(col); + doLayout(); + header.setResizingColumn(null); + } } } } @@ -1461,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 () @@ -1763,7 +1812,7 @@ public class JTable if ((event.getFirstRow() ==TableModelEvent.HEADER_ROW) && autoCreateColumnsFromModel) - createDefaultColumnsFromModel(); + createDefaultColumnsFromModel(); // If the structure changes, we need to revalidate, since that might // affect the size parameters of the JTable. Otherwise we only need @@ -1796,7 +1845,6 @@ public class JTable { if (point != null) { - int x0 = getLocation().x; int ncols = getColumnCount(); Dimension gap = getIntercellSpacing(); TableColumnModel cols = getColumnModel(); @@ -1826,7 +1874,6 @@ public class JTable { if (point != null) { - int y0 = getLocation().y; int nrows = getRowCount(); int height = getRowHeight(); int y = point.y; @@ -1984,16 +2031,13 @@ public class JTable } } - - public TableCellRenderer getCellRenderer(int row, int column) { TableCellRenderer renderer = columnModel.getColumn(column).getCellRenderer(); - if (renderer == null) - renderer = getDefaultRenderer(dataModel.getColumnClass(column)); - + renderer = getDefaultRenderer(getColumnClass(column)); + return renderer; } @@ -2039,19 +2083,29 @@ public class JTable int row, int column) { - boolean rsa = getRowSelectionAllowed(); - boolean csa = getColumnSelectionAllowed(); - boolean rs = rsa ? getSelectionModel().isSelectedIndex(row) : false; - boolean cs = csa ? columnModel.getSelectionModel().isSelectedIndex(column) : false; - boolean isSelected = ((rsa && csa && rs && cs) - || (rsa && !csa && rs) - || (!rsa && csa && cs)); - + + boolean rowSelAllowed = getRowSelectionAllowed(); + boolean colSelAllowed = getColumnSelectionAllowed(); + boolean isSel = false; + if (rowSelAllowed && colSelAllowed || !rowSelAllowed && !colSelAllowed) + isSel = isCellSelected(row, column); + else + isSel = isRowSelected(row) && getRowSelectionAllowed() + || isColumnSelected(column) && getColumnSelectionAllowed(); + + // Determine the focused cell. The focused cell is the cell at the + // leadSelectionIndices of the row and column selection model. + ListSelectionModel rowSel = getSelectionModel(); + ListSelectionModel colSel = getColumnModel().getSelectionModel(); + boolean hasFocus = hasFocus() && isEnabled() + && rowSel.getLeadSelectionIndex() == row + && colSel.getLeadSelectionIndex() == column; + return renderer.getTableCellRendererComponent(this, dataModel.getValueAt(row, convertColumnIndexToModel(column)), - isSelected, - false, // hasFocus + isSel, + hasFocus, row, column); } @@ -2217,7 +2271,6 @@ public class JTable int lo = lsm.getMinSelectionIndex(); int hi = lsm.getMaxSelectionIndex(); int j = 0; - java.util.ArrayList ls = new java.util.ArrayList(); if (lo != -1 && hi != -1) { switch (lsm.getSelectionMode()) @@ -2973,7 +3026,7 @@ public class JTable public Class getColumnClass(int column) { - return dataModel.getColumnClass(column); + return getModel().getColumnClass(column); } public String getColumnName(int column) @@ -3274,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/libjava/classpath/javax/swing/JTextArea.java b/libjava/classpath/javax/swing/JTextArea.java index 2fa185b..9b50feb 100644 --- a/libjava/classpath/javax/swing/JTextArea.java +++ b/libjava/classpath/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/libjava/classpath/javax/swing/JTextField.java b/libjava/classpath/javax/swing/JTextField.java index 8dc2f25..c490310 100644 --- a/libjava/classpath/javax/swing/JTextField.java +++ b/libjava/classpath/javax/swing/JTextField.java @@ -203,9 +203,22 @@ public class JTextField extends JTextComponent */ protected Document createDefaultModel() { - PlainDocument doc = new PlainDocument(); + return new PlainDocument(); + } + + /** + * Sets the document to be used for this JTextField. + * + * This sets the document property <code>filterNewlines</code> to + * <code>true</code> and then calls the super behaviour to setup a view and + * revalidate the text field. + * + * @param doc the document to set + */ + public void setDocument(Document doc) + { doc.putProperty("filterNewlines", Boolean.TRUE); - return doc; + super.setDocument(doc); } /** diff --git a/libjava/classpath/javax/swing/JTextPane.java b/libjava/classpath/javax/swing/JTextPane.java index a2aebd4..7c95d76 100644 --- a/libjava/classpath/javax/swing/JTextPane.java +++ b/libjava/classpath/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/libjava/classpath/javax/swing/JTree.java b/libjava/classpath/javax/swing/JTree.java index 4422a19..cfcb229 100644 --- a/libjava/classpath/javax/swing/JTree.java +++ b/libjava/classpath/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/libjava/classpath/javax/swing/JViewport.java b/libjava/classpath/javax/swing/JViewport.java index 5f20f0a..debb574 100644 --- a/libjava/classpath/javax/swing/JViewport.java +++ b/libjava/classpath/javax/swing/JViewport.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing; +import gnu.classpath.SystemProperties; + import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; @@ -163,7 +165,13 @@ public class JViewport extends JComponent implements Accessible public static final int BACKINGSTORE_SCROLL_MODE = 2; private static final long serialVersionUID = -6925142919680527970L; - + + /** + * The default scrollmode to be used by all JViewports as determined by + * the system property gnu.javax.swing.JViewport.scrollMode. + */ + private static final int defaultScrollMode; + protected boolean scrollUnderway; protected boolean isViewSizeSet; @@ -243,21 +251,26 @@ public class JViewport extends JComponent implements Accessible */ boolean sizeChanged = true; - public JViewport() + /** + * Initializes the default setting for the scrollMode property. + */ + static { - setOpaque(true); String scrollModeProp = - System.getProperty("gnu.javax.swing.JViewport.scrollMode", + SystemProperties.getProperty("gnu.javax.swing.JViewport.scrollMode", "BLIT"); - int myScrollMode; if (scrollModeProp.equalsIgnoreCase("simple")) - myScrollMode = SIMPLE_SCROLL_MODE; + defaultScrollMode = SIMPLE_SCROLL_MODE; else if (scrollModeProp.equalsIgnoreCase("backingstore")) - myScrollMode = BACKINGSTORE_SCROLL_MODE; + defaultScrollMode = BACKINGSTORE_SCROLL_MODE; else - myScrollMode = BLIT_SCROLL_MODE; - setScrollMode(myScrollMode); + defaultScrollMode = BLIT_SCROLL_MODE; + } + public JViewport() + { + setOpaque(true); + setScrollMode(defaultScrollMode); updateUI(); setLayout(createLayoutManager()); lastPaintPosition = new Point(); @@ -410,8 +423,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) { @@ -772,6 +786,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/libjava/classpath/javax/swing/LookAndFeel.java b/libjava/classpath/javax/swing/LookAndFeel.java index 1a67e84..358a811 100644 --- a/libjava/classpath/javax/swing/LookAndFeel.java +++ b/libjava/classpath/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/libjava/classpath/javax/swing/Popup.java b/libjava/classpath/javax/swing/Popup.java index cbb243e..203ee3c 100644 --- a/libjava/classpath/javax/swing/Popup.java +++ b/libjava/classpath/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/libjava/classpath/javax/swing/RepaintManager.java b/libjava/classpath/javax/swing/RepaintManager.java index b857b12..0be8105 100644 --- a/libjava/classpath/javax/swing/RepaintManager.java +++ b/libjava/classpath/javax/swing/RepaintManager.java @@ -48,6 +48,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; +import java.util.WeakHashMap; /** * <p>The repaint manager holds a set of dirty regions, invalid components, @@ -68,7 +69,7 @@ public class RepaintManager /** * The current repaint managers, indexed by their ThreadGroups. */ - static HashMap currentRepaintManagers; + static WeakHashMap currentRepaintManagers; /** * <p>A helper class which is placed into the system event queue at @@ -286,7 +287,7 @@ public class RepaintManager public static RepaintManager currentManager(Component component) { if (currentRepaintManagers == null) - currentRepaintManagers = new HashMap(); + currentRepaintManagers = new WeakHashMap(); ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); RepaintManager currentManager = (RepaintManager) currentRepaintManagers.get(threadGroup); @@ -330,7 +331,7 @@ public class RepaintManager public static void setCurrentManager(RepaintManager manager) { if (currentRepaintManagers == null) - currentRepaintManagers = new HashMap(); + currentRepaintManagers = new WeakHashMap(); ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); currentRepaintManagers.put(threadGroup, manager); @@ -533,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/libjava/classpath/javax/swing/SwingUtilities.java b/libjava/classpath/javax/swing/SwingUtilities.java index 58b3a78..2d859b7 100644 --- a/libjava/classpath/javax/swing/SwingUtilities.java +++ b/libjava/classpath/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/libjava/classpath/javax/swing/TransferHandler.java b/libjava/classpath/javax/swing/TransferHandler.java index 4828fdb..830feee 100644 --- a/libjava/classpath/javax/swing/TransferHandler.java +++ b/libjava/classpath/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 @@ -53,6 +54,7 @@ public class TransferHandler implements Serializable public TransferAction(String command) { + super(command); this.command = command; } @@ -62,6 +64,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,37 +85,22 @@ 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 */ private static Clipboard getClipboard(JComponent component) { - // Avoid throwing exception if the system clipboard access failed - // in the past. - if (clipboard != null) - return clipboard; - else - { - try - { - SecurityManager sm = System.getSecurityManager(); - if (sm != null) - sm.checkSystemClipboardAccess(); - - // We may access system clipboard. - return component.getToolkit().getSystemClipboard(); - } - catch (Exception e) - { - // We may not access system clipboard. - // Create VM-local clipboard if none exists yet. - clipboard = new Clipboard("Clipboard"); - return clipboard; - } - } + try + { + return component.getToolkit().getSystemClipboard(); + } + catch (SecurityException se) + { + return null; + } } } @@ -125,12 +119,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/libjava/classpath/javax/swing/UIDefaults.java b/libjava/classpath/javax/swing/UIDefaults.java index f6aee1b..00d9700 100644 --- a/libjava/classpath/javax/swing/UIDefaults.java +++ b/libjava/classpath/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 } 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/libjava/classpath/javax/swing/ViewportLayout.java b/libjava/classpath/javax/swing/ViewportLayout.java index 884f7cb..79fd26c 100644 --- a/libjava/classpath/javax/swing/ViewportLayout.java +++ b/libjava/classpath/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) && viewPref.height < portBounds.height + || (view instanceof Scrollable + && ((Scrollable) view).getScrollableTracksViewportHeight()))) + 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) && viewPref.width < portBounds.width + || (view instanceof Scrollable + && ((Scrollable) view).getScrollableTracksViewportWidth()))) + 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/libjava/classpath/javax/swing/event/EventListenerList.java b/libjava/classpath/javax/swing/event/EventListenerList.java index ee3f220..147d68e 100644 --- a/libjava/classpath/javax/swing/event/EventListenerList.java +++ b/libjava/classpath/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/libjava/classpath/javax/swing/plaf/ComponentUI.java b/libjava/classpath/javax/swing/plaf/ComponentUI.java index 6a736f2..be7b68a 100644 --- a/libjava/classpath/javax/swing/plaf/ComponentUI.java +++ b/libjava/classpath/javax/swing/plaf/ComponentUI.java @@ -41,7 +41,6 @@ package javax.swing.plaf; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; -import java.awt.Rectangle; import javax.accessibility.Accessible; import javax.swing.JComponent; @@ -185,12 +184,12 @@ public abstract class ComponentUI public void update(Graphics g, JComponent c) { if (c.isOpaque()) - { - Color oldColor = g.getColor(); - g.setColor(c.getBackground()); - g.fillRect(0, 0, c.getWidth(), c.getHeight()); - g.setColor(oldColor); - } + { + Color oldColor = g.getColor(); + g.setColor(c.getBackground()); + g.fillRect(0, 0, c.getWidth(), c.getHeight()); + g.setColor(oldColor); + } paint(g, c); } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java b/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java index 69d4415..56e4e70 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java @@ -162,8 +162,8 @@ public class BasicArrowButton extends JButton implements SwingConstants super.paint(g); Rectangle bounds = getBounds(); int size = bounds.height / 4; - int x = (bounds.width - size) / 2; - int y = (bounds.height - size) / 2; + int x = bounds.x + (bounds.width - size) / 2; + int y = (bounds.height - size) / 4; ButtonModel m = getModel(); if (m.isArmed()) { diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java index b22aa15..288a8d8 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java @@ -190,10 +190,19 @@ public class BasicComboBoxUI extends ComboBoxUI */ Dimension displaySize; - // FIXME: This fields aren't used anywhere at this moment. - protected Dimension cachedMinimumSize; + // FIXME: This field isn't used anywhere at this moment. protected CellRendererPane currentValuePane; - protected boolean isMinimumSizeDirty; + + /** + * The current minimum size if isMinimumSizeDirty is false. + * Setup by getMinimumSize() and invalidated by the various listeners. + */ + protected Dimension cachedMinimumSize; + + /** + * Indicates whether or not the cachedMinimumSize field is valid or not. + */ + protected boolean isMinimumSizeDirty = true; /** * Creates a new <code>BasicComboBoxUI</code> object. @@ -285,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); @@ -563,6 +571,7 @@ public class BasicComboBoxUI extends ComboBoxUI { arrowButton.setEnabled(comboBox.isEnabled()); arrowButton.setFont(comboBox.getFont()); + arrowButton.setFocusable(false); } /** @@ -615,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(); } /** @@ -668,7 +679,7 @@ public class BasicComboBoxUI extends ComboBoxUI /** * Returns the minimum size for this {@link JComboBox} for this - * look and feel. + * look and feel. Also makes sure cachedMinimimSize is setup correctly. * * @param c The {@link JComponent} to find the minimum size for. * @@ -676,10 +687,15 @@ public class BasicComboBoxUI extends ComboBoxUI */ public Dimension getMinimumSize(JComponent c) { - Dimension d = getDisplaySize(); - int arrowButtonWidth = d.height; - Dimension result = new Dimension(d.width + arrowButtonWidth, d.height); - return result; + if (isMinimumSizeDirty) + { + Dimension d = getDisplaySize(); + int arrowButtonWidth = d.height; + cachedMinimumSize = new Dimension(d.width + arrowButtonWidth, + d.height); + isMinimumSizeDirty = false; + } + return new Dimension(cachedMinimumSize); } /** The value returned by the getMaximumSize() method. */ @@ -1062,6 +1078,9 @@ public class BasicComboBoxUI extends ComboBoxUI */ public void focusGained(FocusEvent e) { + // Lets assume every change invalidates the minimumsize. + isMinimumSizeDirty = true; + hasFocus = true; comboBox.repaint(); } @@ -1074,6 +1093,9 @@ public class BasicComboBoxUI extends ComboBoxUI */ public void focusLost(FocusEvent e) { + // Lets assume every change invalidates the minimumsize. + isMinimumSizeDirty = true; + hasFocus = false; setPopupVisible(comboBox, false); comboBox.repaint(); @@ -1102,6 +1124,9 @@ public class BasicComboBoxUI extends ComboBoxUI */ public void itemStateChanged(ItemEvent e) { + // Lets assume every change invalidates the minimumsize. + isMinimumSizeDirty = true; + if (e.getStateChange() == ItemEvent.SELECTED && comboBox.isEditable()) comboBox.getEditor().setItem(e.getItem()); comboBox.repaint(); @@ -1149,6 +1174,9 @@ public class BasicComboBoxUI extends ComboBoxUI public void contentsChanged(ListDataEvent e) { // if the item is selected or deselected + + // Lets assume every change invalidates the minimumsize. + isMinimumSizeDirty = true; } /** @@ -1158,6 +1186,9 @@ public class BasicComboBoxUI extends ComboBoxUI */ public void intervalAdded(ListDataEvent e) { + // Lets assume every change invalidates the minimumsize. + isMinimumSizeDirty = true; + ComboBoxModel model = comboBox.getModel(); ListCellRenderer renderer = comboBox.getRenderer(); @@ -1179,6 +1210,9 @@ public class BasicComboBoxUI extends ComboBoxUI */ public void intervalRemoved(ListDataEvent e) { + // Lets assume every change invalidates the minimumsize. + isMinimumSizeDirty = true; + // recalculate display size of the JComboBox. displaySize = getDisplaySize(); comboBox.repaint(); @@ -1206,6 +1240,9 @@ public class BasicComboBoxUI extends ComboBoxUI */ public void propertyChange(PropertyChangeEvent e) { + // Lets assume every change invalidates the minimumsize. + isMinimumSizeDirty = true; + if (e.getPropertyName().equals("enabled")) { arrowButton.setEnabled(comboBox.isEnabled()); diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicComboPopup.java b/libjava/classpath/javax/swing/plaf/basic/BasicComboPopup.java index 73979bb..08dab7f 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicComboPopup.java +++ b/libjava/classpath/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/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java index 60179dc..30e3156 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java @@ -37,19 +37,8 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Point; -import java.awt.Polygon; import java.awt.Window; import java.awt.event.ActionEvent; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; @@ -62,23 +51,16 @@ import java.util.Hashtable; import javax.swing.AbstractAction; import javax.swing.Action; -import javax.swing.ButtonGroup; import javax.swing.Icon; import javax.swing.JButton; -import javax.swing.JComboBox; 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.JScrollPane; import javax.swing.JTextField; -import javax.swing.JToggleButton; -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; import javax.swing.event.ListSelectionListener; @@ -87,6 +69,7 @@ import javax.swing.filechooser.FileSystemView; import javax.swing.filechooser.FileView; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.FileChooserUI; +import javax.swing.plaf.metal.MetalIconFactory; /** @@ -144,7 +127,7 @@ public class BasicFileChooserUI extends FileChooserUI */ protected ApproveSelectionAction() { - // Nothing to do here. + super("approveSelection"); } /** @@ -154,17 +137,22 @@ public class BasicFileChooserUI extends FileChooserUI */ public void actionPerformed(ActionEvent e) { - Object obj = new String(parentPath + entry.getText()); + 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(); } @@ -307,7 +295,7 @@ public class BasicFileChooserUI extends FileChooserUI */ protected CancelSelectionAction() { - // Nothing to do here. + super(null); } /** @@ -317,6 +305,8 @@ public class BasicFileChooserUI extends FileChooserUI */ public void actionPerformed(ActionEvent e) { + filechooser.setSelectedFile(null); + filechooser.setSelectedFiles(null); filechooser.cancelSelection(); closeDialog(); } @@ -335,7 +325,7 @@ public class BasicFileChooserUI extends FileChooserUI */ protected ChangeToParentDirectoryAction() { - // Nothing to do here. + super("Go Up"); } /** @@ -358,8 +348,6 @@ public class BasicFileChooserUI extends FileChooserUI */ protected class DoubleClickListener extends MouseAdapter { - /** A timer. */ - private Timer timer = null; /** DOCUMENT ME! */ private Object lastSelected = null; @@ -375,8 +363,6 @@ public class BasicFileChooserUI extends FileChooserUI public DoubleClickListener(JList list) { this.list = list; - timer = new Timer(1000, null); - timer.setRepeats(false); lastSelected = list.getSelectedValue(); setDirectorySelected(false); } @@ -388,14 +374,14 @@ 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 (timer.isRunning() - && list.getSelectedValue().toString().equals(lastSelected.toString())) + if (e.getClickCount() >= 2 && lastSelected != null && + p.toString().equals(lastSelected.toString())) { File f = fsv.createFileObject(lastSelected.toString()); - timer.stop(); if (filechooser.isTraversable(f)) { filechooser.setCurrentDirectory(f); @@ -410,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); @@ -424,8 +421,11 @@ public class BasicFileChooserUI extends FileChooserUI } lastSelected = path; parentPath = path.substring(0, path.lastIndexOf("/") + 1); - entry.setText(path.substring(path.lastIndexOf("/") + 1)); - timer.restart(); + if (f.isFile()) + setFileName(path.substring(path.lastIndexOf("/") + 1)); + else if (filechooser.getFileSelectionMode() == + JFileChooser.DIRECTORIES_ONLY) + setFileName(path); } } @@ -453,7 +453,7 @@ public class BasicFileChooserUI extends FileChooserUI */ protected GoHomeAction() { - // Nothing to do here. + super("Go Home"); } /** @@ -483,7 +483,7 @@ public class BasicFileChooserUI extends FileChooserUI */ protected NewFolderAction() { - // Nothing to do here. + super("New Folder"); } /** @@ -529,7 +529,8 @@ public class BasicFileChooserUI extends FileChooserUI */ public void valueChanged(ListSelectionEvent e) { - Object f = filelist.getSelectedValue(); + JList list = (JList) e.getSource(); + Object f = list.getSelectedValue(); if (f == null) return; File file = filechooser.getFileSystemView().createFileObject(f.toString()); @@ -552,7 +553,7 @@ public class BasicFileChooserUI extends FileChooserUI */ protected UpdateAction() { - // Nothing to do here. + super(null); } /** @@ -576,91 +577,13 @@ public class BasicFileChooserUI extends FileChooserUI protected String cancelButtonToolTipText; /** An icon representing a computer. */ - protected Icon computerIcon = new Icon() - { - public int getIconHeight() - { - return ICON_SIZE; - } - - public int getIconWidth() - { - return ICON_SIZE; - } - - public void paintIcon(Component c, Graphics g, int x, int y) - { - // FIXME: is this not implemented, or is the icon intentionally blank? - } - }; + protected Icon computerIcon; /** An icon for the "details view" button. */ - protected Icon detailsViewIcon = new Icon() - { - public int getIconHeight() - { - return ICON_SIZE; - } - - public int getIconWidth() - { - return ICON_SIZE; - } - - public void paintIcon(Component c, Graphics g, int x, int y) - { - Color saved = g.getColor(); - g.translate(x, y); - - g.setColor(Color.GRAY); - g.drawRect(1, 1, 15, 20); - g.drawLine(17, 6, 23, 6); - g.drawLine(17, 12, 23, 12); - g.drawLine(17, 18, 23, 18); - - g.setColor(saved); - g.translate(-x, -y); - } - }; + protected Icon detailsViewIcon; /** An icon representing a directory. */ - protected Icon directoryIcon = new Icon() - { - public int getIconHeight() - { - return ICON_SIZE; - } - - public int getIconWidth() - { - return ICON_SIZE; - } - - public void paintIcon(Component c, Graphics g, int x, int y) - { - Color saved = g.getColor(); - g.translate(x, y); - - Point ap = new Point(3, 7); - Point bp = new Point(3, 21); - Point cp = new Point(21, 21); - Point dp = new Point(21, 12); - Point ep = new Point(16, 12); - Point fp = new Point(13, 7); - - Polygon dir = new Polygon(new int[] { ap.x, bp.x, cp.x, dp.x, ep.x, fp.x }, - new int[] { ap.y, bp.y, cp.y, dp.y, ep.y, fp.y }, - 6); - - g.setColor(new Color(153, 204, 255)); - g.fillPolygon(dir); - g.setColor(Color.BLACK); - g.drawPolygon(dir); - - g.translate(-x, -y); - g.setColor(saved); - } - }; + protected Icon directoryIcon; /** The localised Mnemonic for the open button. */ protected int directoryOpenButtonMnemonic; @@ -672,82 +595,13 @@ public class BasicFileChooserUI extends FileChooserUI protected String directoryOpenButtonToolTipText; /** An icon representing a file. */ - protected Icon fileIcon = new Icon() - { - public int getIconHeight() - { - return ICON_SIZE; - } - - public int getIconWidth() - { - return ICON_SIZE; - } - - public void paintIcon(Component c, Graphics g, int x, int y) - { - Color saved = g.getColor(); - g.translate(x, y); - - Point a = new Point(5, 4); - Point b = new Point(5, 20); - Point d = new Point(19, 20); - Point e = new Point(19, 7); - Point f = new Point(16, 4); - - Polygon p = new Polygon(new int[] { a.x, b.x, d.x, e.x, f.x, }, - new int[] { a.y, b.y, d.y, e.y, f.y }, 5); - - g.setColor(Color.WHITE); - g.fillPolygon(p); - g.setColor(Color.BLACK); - g.drawPolygon(p); - - g.drawLine(16, 4, 14, 6); - g.drawLine(14, 6, 19, 7); - - g.setColor(saved); - g.translate(-x, -y); - } - }; + protected Icon fileIcon; /** An icon representing a floppy drive. */ - protected Icon floppyDriveIcon = new Icon() - { - public int getIconHeight() - { - return ICON_SIZE; - } - - public int getIconWidth() - { - return ICON_SIZE; - } - - public void paintIcon(Component c, Graphics g, int x, int y) - { - // FIXME: is this not implemented, or is the icon intentionally blank? - } - }; + protected Icon floppyDriveIcon; /** An icon representing a hard drive. */ - protected Icon hardDriveIcon = new Icon() - { - public int getIconHeight() - { - return ICON_SIZE; - } - - public int getIconWidth() - { - return ICON_SIZE; - } - - public void paintIcon(Component c, Graphics g, int x, int y) - { - // FIXME: is this not implemented, or is the icon intentionally blank? - } - }; + protected Icon hardDriveIcon; /** The localised mnemonic for the "help" button. */ protected int helpButtonMnemonic; @@ -759,86 +613,10 @@ public class BasicFileChooserUI extends FileChooserUI protected String helpButtonToolTipText; /** An icon representing the user's home folder. */ - protected Icon homeFolderIcon = new Icon() - { - public int getIconHeight() - { - return ICON_SIZE; - } - - public int getIconWidth() - { - return ICON_SIZE; - } - - public void paintIcon(Component c, Graphics g, int x, int y) - { - Color saved = g.getColor(); - g.translate(x, y); - - Point a = new Point(12, 3); - Point b = new Point(4, 10); - Point d = new Point(20, 10); - - Polygon p = new Polygon(new int[] { a.x, b.x, d.x }, - new int[] { a.y, b.y, d.y }, 3); - - g.setColor(new Color(104, 51, 0)); - g.fillPolygon(p); - g.setColor(Color.BLACK); - g.drawPolygon(p); - - g.setColor(Color.WHITE); - g.fillRect(8, 10, 8, 10); - g.setColor(Color.BLACK); - g.drawRect(8, 10, 8, 10); - - g.setColor(saved); - g.translate(-x, -y); - } - }; + protected Icon homeFolderIcon; /** An icon for the "list view" button. */ - protected Icon listViewIcon = new Icon() - { - public int getIconHeight() - { - return ICON_SIZE; - } - - public int getIconWidth() - { - return ICON_SIZE; - } - - // Not needed. Only simplifies things until we get real icons. - private void paintPartial(Graphics g, int x, int y) - { - Color saved = g.getColor(); - g.translate(x, y); - - g.setColor(Color.GRAY); - g.drawRect(1, 1, 7, 10); - g.drawLine(8, 6, 11, 6); - - g.setColor(saved); - g.translate(-x, -y); - } - - public void paintIcon(Component c, Graphics g, int x, int y) - { - Color saved = g.getColor(); - g.translate(x, y); - - paintPartial(g, 0, 0); - paintPartial(g, 12, 0); - paintPartial(g, 0, 12); - paintPartial(g, 12, 12); - - g.setColor(saved); - g.translate(-x, -y); - } - }; + protected Icon listViewIcon; /** An icon for the "new folder" button. */ protected Icon newFolderIcon = directoryIcon; @@ -871,65 +649,13 @@ public class BasicFileChooserUI extends FileChooserUI protected String updateButtonToolTipText; /** An icon for the "up folder" button. */ - protected Icon upFolderIcon = new Icon() - { - public int getIconHeight() - { - return ICON_SIZE; - } - - public int getIconWidth() - { - return ICON_SIZE; - } - - public void paintIcon(Component comp, Graphics g, int x, int y) - { - Color saved = g.getColor(); - g.translate(x, y); - - Point a = new Point(3, 7); - Point b = new Point(3, 21); - Point c = new Point(21, 21); - Point d = new Point(21, 12); - Point e = new Point(16, 12); - Point f = new Point(13, 7); - - Polygon dir = new Polygon(new int[] { a.x, b.x, c.x, d.x, e.x, f.x }, - new int[] { a.y, b.y, c.y, d.y, e.y, f.y }, 6); - - g.setColor(new Color(153, 204, 255)); - g.fillPolygon(dir); - g.setColor(Color.BLACK); - g.drawPolygon(dir); - - a = new Point(12, 15); - b = new Point(9, 18); - c = new Point(15, 18); - - Polygon arrow = new Polygon(new int[] { a.x, b.x, c.x }, - new int[] { a.y, b.y, c.y }, 3); - - g.fillPolygon(arrow); - - g.drawLine(12, 15, 12, 22); - - g.translate(-x, -y); - g.setColor(saved); - } - }; + protected Icon upFolderIcon; // -- begin private, but package local since used in inner classes -- /** The file chooser component represented by this UI delegate. */ JFileChooser filechooser; - /** The file list. */ - JList filelist; - - /** The combo box used to display/select file filters. */ - JComboBox filters; - /** The model for the directory list. */ BasicDirectoryModel model; @@ -939,32 +665,11 @@ public class BasicFileChooserUI extends FileChooserUI /** The default file view. */ FileView fv = new BasicFileView(); - /** The icon size. */ - static final int ICON_SIZE = 24; - - /** A combo box for display/selection of parent directories. */ - JComboBox parents; - - /** The current file name. */ - String filename; - /** The accept (open/save) button. */ JButton accept; - /** The cancel button. */ - JButton cancel; - - /** The button to move up to the parent directory. */ - JButton upFolderButton; - - /** The button to create a new directory. */ - JButton newFolderButton; - - /** The button to move to the user's home directory. */ - JButton homeFolderButton; - /** An optional accessory panel. */ - JPanel accessoryPanel; + JPanel accessoryPanel = new JPanel(); /** A property change listener. */ PropertyChangeListener propertyChangeListener; @@ -997,47 +702,43 @@ public class BasicFileChooserUI extends FileChooserUI /** Current parent path */ String parentPath; + /** + * The action for the 'approve' button. + * @see #getApproveSelectionAction() + */ + private ApproveSelectionAction approveSelectionAction; + + /** + * The action for the 'cancel' button. + * @see #getCancelSelectionAction() + */ + private CancelSelectionAction cancelSelectionAction; + + /** + * The action for the 'go home' control button. + * @see #getGoHomeAction() + */ + private GoHomeAction goHomeAction; + + /** + * The action for the 'up folder' control button. + * @see #getChangeToParentDirectoryAction() + */ + private ChangeToParentDirectoryAction changeToParentDirectoryAction; + + /** + * The action for the 'new folder' control button. + * @see #getNewFolderAction() + */ + private NewFolderAction newFolderAction; + + /** + * The action for ???. // FIXME: what is this? + * @see #getUpdateAction() + */ + 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. @@ -1056,7 +757,6 @@ public class BasicFileChooserUI extends FileChooserUI */ public BasicFileChooserUI(JFileChooser b) { - this.filechooser = b; } /** @@ -1081,6 +781,7 @@ public class BasicFileChooserUI extends FileChooserUI if (c instanceof JFileChooser) { JFileChooser fc = (JFileChooser) c; + this.filechooser = fc; fc.resetChoosableFileFilters(); createModel(); clearIconCache(); @@ -1130,78 +831,7 @@ public class BasicFileChooserUI extends FileChooserUI if (parentFiles.size() == 0) return; - if (parents.getItemCount() > 0) - parents.removeAllItems(); - for (int i = parentFiles.size() - 1; i >= 0; i--) - parents.addItem(parentFiles.get(i)); - parents.setSelectedIndex(parentFiles.size() - 1); - parents.revalidate(); - parents.repaint(); - } - - /** - * DOCUMENT ME! - * - * @return DOCUMENT ME! - */ - private ItemListener createBoxListener() - { - return new ItemListener() - { - public void itemStateChanged(ItemEvent e) - { - if (parents.getItemCount() - 1 == parents.getSelectedIndex()) - return; - StringBuffer dir = new StringBuffer(); - for (int i = 0; i <= parents.getSelectedIndex(); i++) - { - dir.append(parents.getItemAt(i)); - dir.append(File.separatorChar); - } - filechooser.setCurrentDirectory(filechooser.getFileSystemView() - .createFileObject(dir - .toString())); - } - }; - } - - /** - * DOCUMENT ME! - * - * @return DOCUMENT ME! - */ - private ItemListener createFilterListener() - { - return new ItemListener() - { - public void itemStateChanged(ItemEvent e) - { - int index = filters.getSelectedIndex(); - if (index == -1) - return; - filechooser.setFileFilter(filechooser.getChoosableFileFilters()[index]); - } - }; - } - - void filterEntries() - { - FileFilter[] list = filechooser.getChoosableFileFilters(); - if (filters.getItemCount() > 0) - filters.removeAllItems(); - - int index = -1; - String selected = filechooser.getFileFilter().getDescription(); - for (int i = 0; i < list.length; i++) - { - if (selected.equals(list[i].getDescription())) - index = i; - filters.addItem(list[i].getDescription()); - } - filters.setSelectedIndex(index); - filters.revalidate(); - filters.repaint(); - } + } /** * Creates and install the subcomponents for the file chooser. @@ -1210,121 +840,6 @@ public class BasicFileChooserUI extends FileChooserUI */ public void installComponents(JFileChooser fc) { - JLabel look = new JLabel("Look In:"); - - parents = new JComboBox(); - parents.setRenderer(new BasicComboBoxRenderer()); - boxEntries(); - look.setLabelFor(parents); - JPanel parentsPanel = new JPanel(); - parentsPanel.add(look); - parentsPanel.add(parents); - JPanel buttonPanel = new JPanel(); - - upFolderButton = new JButton(); - upFolderButton.setIcon(upFolderIcon); - buttonPanel.add(upFolderButton); - - homeFolderButton = new JButton(); - homeFolderButton = new JButton(homeFolderIcon); - buttonPanel.add(homeFolderButton); - - newFolderButton = new JButton(); - newFolderButton.setIcon(newFolderIcon); - buttonPanel.add(newFolderButton); - - ButtonGroup toggles = new ButtonGroup(); - JToggleButton listViewButton = new JToggleButton(); - listViewButton.setIcon(listViewIcon); - toggles.add(listViewButton); - buttonPanel.add(listViewButton); - - JToggleButton detailsViewButton = new JToggleButton(); - detailsViewButton.setIcon(detailsViewIcon); - toggles.add(detailsViewButton); - buttonPanel.add(detailsViewButton); - - JPanel topPanel = new JPanel(); - parentsPanel.add(buttonPanel); - topPanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT, 0, 0)); - topPanel.add(parentsPanel); - - accessoryPanel = new JPanel(); - if (filechooser.getAccessory() != null) - accessoryPanel.add(filechooser.getAccessory(), BorderLayout.CENTER); - - filelist = new JList(model); - filelist.setVisibleRowCount(6); - JScrollPane scrollp = new JScrollPane(filelist); - scrollp.setPreferredSize(new Dimension(400, 175)); - filelist.setBackground(Color.WHITE); - - filelist.setLayoutOrientation(JList.VERTICAL_WRAP); - filelist.setCellRenderer(new ListLabelRenderer()); - - GridBagConstraints c = new GridBagConstraints(); - c.gridx = 0; - c.gridy = 0; - c.fill = GridBagConstraints.BOTH; - c.weightx = 1; - c.weighty = 1; - - JPanel centrePanel = new JPanel(); - centrePanel.setLayout(new GridBagLayout()); - centrePanel.add(scrollp, c); - - c.gridx = 1; - centrePanel.add(accessoryPanel, c); - - JLabel fileNameLabel = new JLabel("File Name:"); - JLabel fileTypesLabel = new JLabel("Files of Type:"); - - entry = new JTextField(); - filters = new JComboBox(); - filterEntries(); - - fileNameLabel.setLabelFor(entry); - fileNameLabel.setHorizontalTextPosition(SwingConstants.LEFT); - fileTypesLabel.setLabelFor(filters); - fileTypesLabel.setHorizontalTextPosition(SwingConstants.LEFT); - - closePanel = new JPanel(); - accept = getApproveButton(filechooser); - cancel = new JButton(cancelButtonText); - cancel.setMnemonic(cancelButtonMnemonic); - cancel.setToolTipText(cancelButtonToolTipText); - closePanel.add(accept); - closePanel.add(cancel); - - c.anchor = GridBagConstraints.WEST; - c.weighty = 0; - c.weightx = 0; - c.gridx = 0; - - bottomPanel = new JPanel(); - bottomPanel.setLayout(new GridBagLayout()); - bottomPanel.add(fileNameLabel, c); - - c.gridy = 1; - bottomPanel.add(fileTypesLabel, c); - c.gridx = 1; - c.gridy = 0; - c.weightx = 1; - c.weighty = 1; - bottomPanel.add(entry, c); - - c.gridy = 1; - bottomPanel.add(filters, c); - - c.fill = GridBagConstraints.NONE; - c.gridy = 2; - c.anchor = GridBagConstraints.EAST; - bottomPanel.add(closePanel, c); - - filechooser.setLayout(new BorderLayout()); - filechooser.add(topPanel, BorderLayout.NORTH); - filechooser.add(centrePanel, BorderLayout.CENTER); - filechooser.add(bottomPanel, BorderLayout.SOUTH); } /** @@ -1334,15 +849,6 @@ public class BasicFileChooserUI extends FileChooserUI */ public void uninstallComponents(JFileChooser fc) { - parents = null; - - accept = null; - cancel = null; - upFolderButton = null; - homeFolderButton = null; - newFolderButton = null; - - filelist = null; } /** @@ -1354,17 +860,6 @@ public class BasicFileChooserUI extends FileChooserUI { propertyChangeListener = createPropertyChangeListener(filechooser); filechooser.addPropertyChangeListener(propertyChangeListener); - - //parents.addItemListener(createBoxListener()); - accept.addActionListener(getApproveSelectionAction()); - cancel.addActionListener(getCancelSelectionAction()); - upFolderButton.addActionListener(getChangeToParentDirectoryAction()); - homeFolderButton.addActionListener(getGoHomeAction()); - newFolderButton.addActionListener(getNewFolderAction()); - filters.addItemListener(createFilterListener()); - - filelist.addMouseListener(createDoubleClickListener(filechooser, filelist)); - filelist.addListSelectionListener(createListSelectionListener(filechooser)); } /** @@ -1401,24 +896,42 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * Installs the icons for this UI delegate (NOT YET IMPLEMENTED). + * Installs the icons for this UI delegate. * - * @param fc the file chooser. + * @param fc the file chooser (ignored). */ protected void installIcons(JFileChooser fc) { - // FIXME: Implement. + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + computerIcon = MetalIconFactory.getTreeComputerIcon(); + detailsViewIcon = defaults.getIcon("FileChooser.detailsViewIcon"); + directoryIcon = new MetalIconFactory.TreeFolderIcon(); + fileIcon = new MetalIconFactory.TreeLeafIcon(); + floppyDriveIcon = MetalIconFactory.getTreeFloppyDriveIcon(); + hardDriveIcon = MetalIconFactory.getTreeHardDriveIcon(); + homeFolderIcon = defaults.getIcon("FileChooser.homeFolderIcon"); + listViewIcon = defaults.getIcon("FileChooser.listViewIcon"); + newFolderIcon = defaults.getIcon("FileChooser.newFolderIcon"); + upFolderIcon = defaults.getIcon("FileChooser.upFolderIcon"); } /** - * Uninstalls the icons previously added by this UI delegate (NOT YET - * IMPLEMENTED). + * Uninstalls the icons previously added by this UI delegate. * * @param fc the file chooser. */ protected void uninstallIcons(JFileChooser fc) { - // FIXME: Implement. + computerIcon = null; + detailsViewIcon = null; + directoryIcon = null; + fileIcon = null; + floppyDriveIcon = null; + hardDriveIcon = null; + homeFolderIcon = null; + listViewIcon = null; + newFolderIcon = null; + upFolderIcon = null; } /** @@ -1428,25 +941,36 @@ public class BasicFileChooserUI extends FileChooserUI */ protected void installStrings(JFileChooser fc) { - acceptAllFileFilterText = UIManager.getString("FileChooser.acceptAllFileFilterText"); - cancelButtonMnemonic = UIManager.getInt("FileChooser.cancelButtonMnemonic"); - cancelButtonText = UIManager.getString("FileChooser.cancelButtonText"); - cancelButtonToolTipText = UIManager.getString("FileChooser.cancelButtonToolTipText"); + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + + dirDescText = defaults.getString("FileChooser.directoryDescriptionText"); + fileDescText = defaults.getString("FileChooser.fileDescriptionText"); - dirDescText = UIManager.getString("FileChooser.directoryDescriptionText"); - fileDescText = UIManager.getString("FileChooser.fileDescriptionText"); + acceptAllFileFilterText = defaults.getString("FileChooser.acceptAllFileFilterText"); + cancelButtonText = "Cancel"; + cancelButtonToolTipText = "Abort file chooser dialog"; + cancelButtonMnemonic = new Integer((String) UIManager.get("FileChooser.cancelButtonMnemonic")).intValue(); - helpButtonMnemonic = UIManager.getInt("FileChooser.helpButtonMnemonic"); - helpButtonText = UIManager.getString("FileChooser.helpButtonText"); - helpButtonToolTipText = UIManager.getString("FileChooser.helpButtonToolTipText"); + directoryOpenButtonText = "Open"; + directoryOpenButtonToolTipText = "Open selected directory"; + directoryOpenButtonMnemonic + = new Integer((String) UIManager.get("FileChooser.directoryOpenButtonMnemonic")).intValue(); + + helpButtonText = "Help"; + helpButtonToolTipText = "FileChooser help"; + helpButtonMnemonic = new Integer((String) UIManager.get("FileChooser.helpButtonMnemonic")).intValue(); - openButtonMnemonic = UIManager.getInt("FileChooser.openButtonMnemonic"); - openButtonText = UIManager.getString("FileChooser.openButtonText"); - openButtonToolTipText = UIManager.getString("FileChooser.openButtonToolTipText"); + openButtonText = "Open"; + openButtonToolTipText = "Open selected file"; + openButtonMnemonic = new Integer((String) UIManager.get("FileChooser.openButtonMnemonic")).intValue(); - saveButtonMnemonic = UIManager.getInt("FileChooser.saveButtonMnemonic"); - saveButtonText = UIManager.getString("FileChooser.saveButtonText"); - saveButtonToolTipText = UIManager.getString("FileChooser.saveButtonToolTipText"); + saveButtonText = "Save"; + saveButtonToolTipText = "Save selected file"; + saveButtonMnemonic = new Integer((String) UIManager.get("FileChooser.saveButtonMnemonic")).intValue(); + + updateButtonText = "Update"; + updateButtonToolTipText = "Update directory listing"; + updateButtonMnemonic = new Integer((String) UIManager.get("FileChooser.updateButtonMnemonic")).intValue(); } /** @@ -1457,24 +981,26 @@ public class BasicFileChooserUI extends FileChooserUI protected void uninstallStrings(JFileChooser fc) { acceptAllFileFilterText = null; - cancelButtonMnemonic = 0; + dirDescText = null; + fileDescText = null; + cancelButtonText = null; cancelButtonToolTipText = null; - dirDescText = null; - fileDescText = null; + directoryOpenButtonText = null; + directoryOpenButtonToolTipText = null; - helpButtonMnemonic = 0; helpButtonText = null; helpButtonToolTipText = null; - openButtonMnemonic = 0; openButtonText = null; openButtonToolTipText = null; - saveButtonMnemonic = 0; saveButtonText = null; saveButtonToolTipText = null; + + updateButtonText = null; + updateButtonToolTipText = null; } /** @@ -1509,110 +1035,6 @@ public class BasicFileChooserUI extends FileChooserUI { public void propertyChange(PropertyChangeEvent e) { - // FIXME: Multiple file selection waiting on JList multiple selection - // bug. - if (e.getPropertyName().equals( - JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) - { - if (filechooser.getSelectedFile() == null) - setFileName(null); - else - setFileName(filechooser.getSelectedFile().toString()); - int index = -1; - File file = filechooser.getSelectedFile(); - for (index = 0; index < model.getSize(); index++) - if (((File) model.getElementAt(index)).equals(file)) - break; - if (index == -1) - return; - filelist.setSelectedIndex(index); - filelist.ensureIndexIsVisible(index); - filelist.revalidate(); - filelist.repaint(); - } - else if (e.getPropertyName().equals( - JFileChooser.DIRECTORY_CHANGED_PROPERTY)) - { - filelist.clearSelection(); - filelist.revalidate(); - filelist.repaint(); - setDirectorySelected(false); - setDirectory(filechooser.getCurrentDirectory()); - boxEntries(); - } - else if (e.getPropertyName().equals( - JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY) - || e.getPropertyName().equals( - JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) - filterEntries(); - else if (e.getPropertyName().equals( - JFileChooser.DIALOG_TYPE_CHANGED_PROPERTY) - || e.getPropertyName().equals( - JFileChooser.DIALOG_TITLE_CHANGED_PROPERTY)) - { - Window owner = SwingUtilities.windowForComponent(filechooser); - if (owner instanceof JDialog) - ((JDialog) owner).setTitle(getDialogTitle(filechooser)); - accept.setText(getApproveButtonText(filechooser)); - accept.setToolTipText(getApproveButtonToolTipText(filechooser)); - accept.setMnemonic(getApproveButtonMnemonic(filechooser)); - } - else if (e.getPropertyName().equals( - JFileChooser.APPROVE_BUTTON_TEXT_CHANGED_PROPERTY)) - accept.setText(getApproveButtonText(filechooser)); - else if (e.getPropertyName().equals( - JFileChooser.APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY)) - accept.setToolTipText(getApproveButtonToolTipText(filechooser)); - else if (e.getPropertyName().equals( - JFileChooser.APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY)) - accept.setMnemonic(getApproveButtonMnemonic(filechooser)); - else if (e.getPropertyName().equals( - JFileChooser.CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY)) - { - if (filechooser.getControlButtonsAreShown()) - { - GridBagConstraints c = new GridBagConstraints(); - c.gridy = 1; - bottomPanel.add(filters, c); - - c.fill = GridBagConstraints.BOTH; - c.gridy = 2; - c.anchor = GridBagConstraints.EAST; - bottomPanel.add(closePanel, c); - bottomPanel.revalidate(); - bottomPanel.repaint(); - bottomPanel.doLayout(); - } - else - bottomPanel.remove(closePanel); - } - else if (e.getPropertyName().equals( - JFileChooser.ACCEPT_ALL_FILE_FILTER_USED_CHANGED_PROPERTY)) - { - if (filechooser.isAcceptAllFileFilterUsed()) - filechooser.addChoosableFileFilter(getAcceptAllFileFilter(filechooser)); - else - filechooser.removeChoosableFileFilter(getAcceptAllFileFilter(filechooser)); - } - else if (e.getPropertyName().equals( - JFileChooser.ACCESSORY_CHANGED_PROPERTY)) - { - JComponent old = (JComponent) e.getOldValue(); - if (old != null) - getAccessoryPanel().remove(old); - JComponent newval = (JComponent) e.getNewValue(); - if (newval != null) - getAccessoryPanel().add(newval); - } - if (e.getPropertyName().equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY) - || e.getPropertyName().equals( - JFileChooser.FILE_FILTER_CHANGED_PROPERTY) - || e.getPropertyName().equals( - JFileChooser.FILE_HIDING_CHANGED_PROPERTY)) - rescanCurrentDirectory(filechooser); - - filechooser.revalidate(); - filechooser.repaint(); } }; } @@ -1624,7 +1046,9 @@ public class BasicFileChooserUI extends FileChooserUI */ public String getFileName() { - return filename; + // FIXME: I'm thinking that this method just provides access to the + // text value in the JTextField component...but not sure yet + return null; //filename; } /** @@ -1649,7 +1073,9 @@ public class BasicFileChooserUI extends FileChooserUI */ public void setFileName(String filename) { - this.filename = filename; + // FIXME: it might be the case that this method provides an access + // point for the JTextField (or whatever) a subclass is using... + //this.filename = filename; } /** @@ -1672,7 +1098,6 @@ public class BasicFileChooserUI extends FileChooserUI public void rescanCurrentDirectory(JFileChooser fc) { getModel().validateFileCache(); - filelist.revalidate(); } /** @@ -1708,17 +1133,14 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * Creates and returns an approve (open or save) button for the dialog. + * Returns the approve (open or save) button for the dialog. * * @param fc the file chooser. * * @return The button. */ - public JButton getApproveButton(JFileChooser fc) + protected JButton getApproveButton(JFileChooser fc) { - accept = new JButton(getApproveButtonText(fc)); - accept.setMnemonic(getApproveButtonMnemonic(fc)); - accept.setToolTipText(getApproveButtonToolTipText(fc)); return accept; } @@ -1830,9 +1252,8 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * Returns the file view for the file chooser. This returns either the - * file view that has been explicitly set for the {@link JFileChooser}, or - * a default file view. + * Returns the default file view (NOT the file view from the file chooser, + * if there is one). * * @param fc the file chooser component. * @@ -1856,24 +1277,10 @@ public class BasicFileChooserUI extends FileChooserUI */ public String getDialogTitle(JFileChooser fc) { - String ret = fc.getDialogTitle(); - if (ret != null) - return ret; - switch (fc.getDialogType()) - { - case JFileChooser.OPEN_DIALOG: - ret = openButtonText; - break; - case JFileChooser.SAVE_DIALOG: - ret = saveButtonText; - break; - default: - ret = fc.getApproveButtonText(); - break; - } - if (ret == null) - ret = openButtonText; - return ret; + String result = fc.getDialogTitle(); + if (result == null) + result = getApproveButtonText(fc); + return result; } /** @@ -1906,23 +1313,28 @@ public class BasicFileChooserUI extends FileChooserUI */ public String getApproveButtonText(JFileChooser fc) { - if (fc.getApproveButtonText() != null) - return fc.getApproveButtonText(); - else if (fc.getDialogType() == JFileChooser.SAVE_DIALOG) - return saveButtonText; - else - return openButtonText; + String result = fc.getApproveButtonText(); + if (result == null) + { + if (fc.getDialogType() == JFileChooser.SAVE_DIALOG) + result = saveButtonText; + else + result = openButtonText; + } + return result; } /** * Creates and returns a new action that will be used with the "new folder" * button. * - * @return A new instance of {@link GoHomeAction}. + * @return A new instance of {@link NewFolderAction}. */ public Action getNewFolderAction() { - return new NewFolderAction(); + if (newFolderAction == null) + newFolderAction = new NewFolderAction(); + return newFolderAction; } /** @@ -1933,49 +1345,56 @@ public class BasicFileChooserUI extends FileChooserUI */ public Action getGoHomeAction() { - return new GoHomeAction(); + if (goHomeAction == null) + goHomeAction = new GoHomeAction(); + return goHomeAction; } /** - * Creates and returns a new action that will be used with the "up folder" - * button. + * Returns the action that handles events for the "up folder" control button. * - * @return A new instance of {@link ChangeToParentDirectoryAction}. + * @return An instance of {@link ChangeToParentDirectoryAction}. */ public Action getChangeToParentDirectoryAction() { - return new ChangeToParentDirectoryAction(); + if (changeToParentDirectoryAction == null) + changeToParentDirectoryAction = new ChangeToParentDirectoryAction(); + return changeToParentDirectoryAction; } /** - * Creates and returns a new action that will be used with the "approve" - * button. + * Returns the action that handles events for the "approve" button. * - * @return A new instance of {@link ApproveSelectionAction}. + * @return An instance of {@link ApproveSelectionAction}. */ public Action getApproveSelectionAction() { - return new ApproveSelectionAction(); + if (approveSelectionAction == null) + approveSelectionAction = new ApproveSelectionAction(); + return approveSelectionAction; } /** - * Creates and returns a new action that will be used with the "cancel" - * button. + * Returns the action that handles events for the "cancel" button. * - * @return A new instance of {@link CancelSelectionAction}. + * @return An instance of {@link CancelSelectionAction}. */ public Action getCancelSelectionAction() { - return new CancelSelectionAction(); + if (cancelSelectionAction == null) + cancelSelectionAction = new CancelSelectionAction(); + return cancelSelectionAction; } /** - * Creates and returns a new instance of {@link UpdateAction}. + * Returns the update action (an instance of {@link UpdateAction}). * * @return An action. */ public Action getUpdateAction() { - return new UpdateAction(); + if (updateAction == null) + updateAction = new UpdateAction(); + return updateAction; } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicHTML.java b/libjava/classpath/javax/swing/plaf/basic/BasicHTML.java new file mode 100644 index 0000000..b9891e1 --- /dev/null +++ b/libjava/classpath/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/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java index d9dadda..f9653bd 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java @@ -46,6 +46,7 @@ import java.awt.Dimension; import java.awt.Graphics; import java.awt.Insets; import java.awt.LayoutManager; +import java.awt.LayoutManager2; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ComponentEvent; @@ -1164,9 +1165,6 @@ public class BasicInternalFrameUI extends InternalFrameUI { frame = (JInternalFrame) c; - internalFrameLayout = createLayoutManager(); - frame.setLayout(internalFrameLayout); - ((JComponent) frame.getRootPane().getGlassPane()).setOpaque(false); frame.getRootPane().getGlassPane().setVisible(true); @@ -1192,7 +1190,6 @@ public class BasicInternalFrameUI extends InternalFrameUI uninstallListeners(); uninstallDefaults(); - frame.setLayout(null); ((JComponent) frame.getRootPane().getGlassPane()).setOpaque(true); frame.getRootPane().getGlassPane().setVisible(false); @@ -1204,6 +1201,8 @@ public class BasicInternalFrameUI extends InternalFrameUI */ protected void installDefaults() { + internalFrameLayout = createLayoutManager(); + frame.setLayout(internalFrameLayout); LookAndFeel.installBorder(frame, "InternalFrame.border"); frame.setFrameIcon(UIManager.getIcon("InternalFrame.icon")); // InternalFrames are invisible by default. @@ -1256,6 +1255,8 @@ public class BasicInternalFrameUI extends InternalFrameUI protected void uninstallDefaults() { frame.setBorder(null); + frame.setLayout(null); + internalFrameLayout = null; } /** @@ -1329,7 +1330,13 @@ public class BasicInternalFrameUI extends InternalFrameUI */ public Dimension getPreferredSize(JComponent x) { - return internalFrameLayout.preferredLayoutSize(x); + Dimension pref = null; + LayoutManager layout = frame.getLayout(); + if (frame == x && layout != null) + pref = layout.preferredLayoutSize(frame); + else + pref = new Dimension(100, 100); + return pref; } /** @@ -1341,7 +1348,13 @@ public class BasicInternalFrameUI extends InternalFrameUI */ public Dimension getMinimumSize(JComponent x) { - return internalFrameLayout.minimumLayoutSize(x); + Dimension min = null; + LayoutManager layout = frame.getLayout(); + if (frame == x && layout != null) + min = layout.minimumLayoutSize(frame); + else + min = new Dimension(0, 0); + return min; } /** @@ -1353,7 +1366,13 @@ public class BasicInternalFrameUI extends InternalFrameUI */ public Dimension getMaximumSize(JComponent x) { - return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); + Dimension max = null; + LayoutManager layout = frame.getLayout(); + if (frame == x && layout != null && layout instanceof LayoutManager2) + max = ((LayoutManager2) layout).maximumLayoutSize(frame); + else + max = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); + return max; } /** diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java index c8f677f..fd4cff5 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java @@ -39,7 +39,6 @@ package javax.swing.plaf.basic; import java.awt.Color; import java.awt.Dimension; -import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Insets; @@ -104,7 +103,7 @@ public class BasicLabelUI extends LabelUI implements PropertyChangeListener Rectangle ir = new Rectangle(); Rectangle tr = new Rectangle(); Insets insets = lab.getInsets(); - FontMetrics fm = lab.getToolkit().getFontMetrics(lab.getFont()); + FontMetrics fm = lab.getFontMetrics(lab.getFont()); layoutCL(lab, fm, lab.getText(), lab.getIcon(), vr, ir, tr); Rectangle cr = tr.union(ir); return new Dimension(insets.left + cr.width + insets.right, insets.top @@ -150,17 +149,11 @@ public class BasicLabelUI extends LabelUI implements PropertyChangeListener { JLabel b = (JLabel) c; - Font saved_font = g.getFont(); - Rectangle tr = new Rectangle(); Rectangle ir = new Rectangle(); Rectangle vr = new Rectangle(); - Font f = c.getFont(); - - g.setFont(f); - FontMetrics fm = g.getFontMetrics(f); - + FontMetrics fm = g.getFontMetrics(); vr = SwingUtilities.calculateInnerArea(c, vr); if (vr.width < 0) @@ -182,8 +175,6 @@ public class BasicLabelUI extends LabelUI implements PropertyChangeListener else paintDisabledText(b, g, text, tr.x, tr.y + fm.getAscent()); } - - g.setFont(saved_font); } /** diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java index 2d66645..00d157a 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java +++ b/libjava/classpath/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(); } } @@ -524,7 +506,14 @@ public class BasicListUI extends ListUI */ public void mouseDragged(MouseEvent event) { - // TODO: What should be done here, if anything? + Point click = event.getPoint(); + int index = locationToIndex(list, click); + if (index == -1) + return; + if (!event.isShiftDown() && !event.isControlDown()) + list.setSelectedIndex(index); + + list.ensureIndexIsVisible(list.getLeadSelectionIndex()); } /** @@ -562,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; } } @@ -641,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; @@ -738,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); } @@ -752,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. @@ -804,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) @@ -884,24 +889,12 @@ 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. */ protected void maybeUpdateLayoutState() { - if (updateLayoutStateNeeded != 0) + if (updateLayoutStateNeeded != 0 || !list.isValid()) { updateLayoutState(); updateLayoutStateNeeded = 0; @@ -968,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); } /** @@ -985,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); } @@ -1073,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; } @@ -1148,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) { @@ -1165,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; @@ -1182,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; @@ -1238,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/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java b/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java index 13c78ad..f5217be 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java +++ b/libjava/classpath/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,147 @@ 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 + * + * @since 1.4 + */ + 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/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java index c8754a3..63f0ce2 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java +++ b/libjava/classpath/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/libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java index 6b37d31..005a3b3 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java @@ -774,7 +774,7 @@ public class BasicOptionPaneUI extends OptionPaneUI // it will create a box and burst the string. // otherwise, it will just create a label and re-call // this method with the label o.O - if (msg.toString().length() > maxll) + if (msg.toString().length() > maxll || msg.toString().contains("\n")) { Box tmp = new Box(BoxLayout.Y_AXIS); burstStringInto(tmp, msg.toString(), maxll); @@ -796,17 +796,35 @@ public class BasicOptionPaneUI extends OptionPaneUI */ protected void burstStringInto(Container c, String d, int maxll) { - // FIXME: Verify that this is the correct behaviour. - // One interpretation of the spec is that this method - // should recursively call itself to create (and add) - // JLabels to the container if the length of the String d - // is greater than maxll. - // but in practice, even with a really long string, this is - // all that happens. if (d == null || c == null) return; - JLabel label = new JLabel(d); + + int newlineIndex = d.indexOf('\n'); + String line; + String remainder; + if (newlineIndex >= 0 && newlineIndex < maxll) + { + line = d.substring(0, newlineIndex); + remainder = d.substring(newlineIndex + 1); + } + else + { + line = d.substring(0, maxll); + remainder = d.substring(maxll); + } + JLabel label = new JLabel(line); c.add(label); + + // If there is nothing left to burst, then we can stop. + if (remainder.length() == 0) + return; + + // Recursivly call ourselves to burst the remainder of the string, + if ((remainder.length() > maxll || remainder.contains("\n"))) + burstStringInto(c, remainder, maxll); + else + // Add the remainder to the container and be done. + c.add(new JLabel(remainder)); } /** diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java index 808ed27..71671b7 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java +++ b/libjava/classpath/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/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java index 69ed2be..ff17ff0 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java @@ -376,6 +376,11 @@ public class BasicSplitPaneDivider extends Container dividerSize = getSize(); border.paintBorder(this, g, 0, 0, dividerSize.width, dividerSize.height); } + if (splitPane.isOneTouchExpandable()) + { + ((BasicArrowButton) rightButton).paint(g); + ((BasicArrowButton) leftButton).paint(g); + } } /** @@ -583,7 +588,7 @@ public class BasicSplitPaneDivider extends Container public void mouseReleased(MouseEvent e) { if (isDragging) - dragger.completeDrag(e); + dragger.completeDrag(e); isDragging = false; } @@ -596,7 +601,7 @@ public class BasicSplitPaneDivider extends Container public void mouseDragged(MouseEvent e) { if (dragger != null) - dragger.continueDrag(e); + dragger.continueDrag(e); } /** diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java index ce9ea3e..a8f52ce 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java @@ -208,7 +208,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants incrButton = createIncreaseButton(); decrButton = createDecreaseButton(); } - tabPane.layout(); + tabPane.revalidate(); tabPane.repaint(); } } @@ -241,6 +241,9 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public void calculateLayoutInfo() { + assureRectsCreated(tabPane.getTabCount()); + contentRect = SwingUtilities.calculateInnerArea(tabPane, contentRect); + calculateTabRects(tabPane.getTabPlacement(), tabPane.getTabCount()); if (tabPane.getSelectedIndex() != -1) @@ -286,8 +289,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants componentWidth = Math.max(componentWidth, dims.width); } } - Insets insets = tabPane.getInsets(); - if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { @@ -331,7 +332,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { if (tabCount == 0) return; - assureRectsCreated(tabCount); FontMetrics fm = getFontMetrics(); SwingUtilities.calculateInnerArea(tabPane, calcRect); @@ -944,13 +944,11 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { if (tabCount == 0) return; - assureRectsCreated(tabCount); FontMetrics fm = getFontMetrics(); SwingUtilities.calculateInnerArea(tabPane, calcRect); Insets tabAreaInsets = getTabAreaInsets(tabPlacement); Insets insets = tabPane.getInsets(); - int max = 0; int runs = 1; int start = 0; int top = 0; @@ -959,7 +957,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { int maxHeight = calculateMaxTabHeight(tabPlacement); calcRect.width -= tabAreaInsets.left + tabAreaInsets.right; - max = calcRect.width + tabAreaInsets.left + insets.left; start = tabAreaInsets.left + insets.left; int width = 0; int runWidth = start; @@ -996,7 +993,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants int maxWidth = calculateMaxTabWidth(tabPlacement); calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom; - max = calcRect.height + tabAreaInsets.top; int height = 0; start = tabAreaInsets.top + insets.top; int runHeight = start; @@ -1048,8 +1044,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabCount == 0) return; int tabPlacement = tabPane.getTabPlacement(); - incrButton.hide(); - decrButton.hide(); + incrButton.setVisible(false); + decrButton.setVisible(false); if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { @@ -1068,8 +1064,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants decrDims.width, tabAreaRect.height); tabAreaRect.width -= decrDims.width + incrDims.width; - incrButton.show(); - decrButton.show(); + incrButton.setVisible(true); + decrButton.setVisible(true); } } @@ -1092,8 +1088,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants incrDims.height); tabAreaRect.height -= decrDims.height + incrDims.height; - incrButton.show(); - decrButton.show(); + incrButton.setVisible(true); + decrButton.setVisible(true); } } viewport.setBounds(tabAreaRect.x, tabAreaRect.y, tabAreaRect.width, @@ -1348,6 +1344,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants public BasicTabbedPaneUI() { super(); + rects = new Rectangle[0]; + tabRuns = new int[10]; } /** @@ -1460,7 +1458,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants layoutManager = createLayoutManager(); tabPane.setLayout(layoutManager); - tabPane.layout(); } } @@ -1552,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(); @@ -1884,7 +1881,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected void paintIcon(Graphics g, int tabPlacement, int tabIndex, Icon icon, Rectangle iconRect, boolean isSelected) { - icon.paintIcon(tabPane, g, iconRect.x, iconRect.y); + if (icon != null) + icon.paintIcon(tabPane, g, iconRect.x, iconRect.y); } /** @@ -2110,7 +2108,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected void paintContentBorder(Graphics g, int tabPlacement, int selectedIndex) { - Insets insets = getContentBorderInsets(tabPlacement); int x = contentRect.x; int y = contentRect.y; int w = contentRect.width; @@ -2396,16 +2393,13 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ protected void assureRectsCreated(int tabCount) { - if (rects == null) - rects = new Rectangle[tabCount]; - if (tabCount == rects.length) - return; - else + if (rects.length < tabCount) { - int numToCopy = Math.min(tabCount, rects.length); - Rectangle[] tmp = new Rectangle[tabCount]; - System.arraycopy(rects, 0, tmp, 0, numToCopy); - rects = tmp; + Rectangle[] old = rects; + rects = new Rectangle[tabCount]; + System.arraycopy(old, 0, rects, 0, old.length); + for (int i = old.length; i < rects.length; i++) + rects[i] = new Rectangle(); } } @@ -2763,7 +2757,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ protected FontMetrics getFontMetrics() { - FontMetrics fm = tabPane.getToolkit().getFontMetrics(tabPane.getFont()); + FontMetrics fm = tabPane.getFontMetrics(tabPane.getFont()); return fm; } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java index ec0467a..9c8a5ef 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java @@ -139,6 +139,7 @@ public class BasicTableHeaderUI extends TableHeaderUI public void installUI(JComponent c) { header = (JTableHeader) c; + rendererPane = new CellRendererPane(); installDefaults(); installKeyboardActions(); installListeners(); @@ -194,18 +195,15 @@ public class BasicTableHeaderUI extends TableHeaderUI false, // isSelected false, // isFocused -1, i); + // FIXME: The following settings should be performed in + // rend.getTableCellRendererComponent(). comp.setFont(header.getFont()); comp.setBackground(header.getBackground()); comp.setForeground(header.getForeground()); if (comp instanceof JComponent) ((JComponent)comp).setBorder(cellBorder); - gfx.translate(bounds.x, bounds.y); - gfx.setClip(0, 0, bounds.width, bounds.height); - comp.setSize(bounds.width, bounds.height); - comp.setLocation(0,0); - comp.paint(gfx); - gfx.translate(-bounds.x, -bounds.y); - gfx.setClip(oldClip); + rendererPane.paintComponent(gfx, comp, header, bounds.x, bounds.y, + bounds.width, bounds.height); } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java index 0154439..18b6912 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java @@ -1193,29 +1193,9 @@ public class BasicTableUI extends TableUI TableCellRenderer rend, TableModel data, int rowLead, int colLead) { - boolean rowSelAllowed = table.getRowSelectionAllowed(); - boolean colSelAllowed = table.getColumnSelectionAllowed(); - boolean isSel = false; - if (rowSelAllowed && colSelAllowed || !rowSelAllowed && !colSelAllowed) - isSel = table.isCellSelected(row, col); - else - isSel = table.isRowSelected(row) && table.getRowSelectionAllowed() - || table.isColumnSelected(col) && table.getColumnSelectionAllowed(); - - // Determine the focused cell. The focused cell is the cell at the - // leadSelectionIndices of the row and column selection model. - ListSelectionModel rowSel = table.getSelectionModel(); - ListSelectionModel colSel = table.getColumnModel().getSelectionModel(); - boolean hasFocus = table.hasFocus() && table.isEnabled() - && rowSel.getLeadSelectionIndex() == row - && colSel.getLeadSelectionIndex() == col; - - Component comp = rend.getTableCellRendererComponent(table, - data.getValueAt(row, col), - isSel, hasFocus, row, col); - + Component comp = table.prepareRenderer(rend, row, col); rendererPane.paintComponent(g, comp, table, bounds); - + // FIXME: this is manual painting of the Caret, why doesn't the // JTextField take care of this itself? if (comp instanceof JTextField) @@ -1263,7 +1243,7 @@ public class BasicTableUI extends TableUI width - gap.width + 1, height - gap.height); if (bounds.intersects(clip)) - { + { paintCell(gfx, r, c, bounds, table.getCellRenderer(r, c), table.getModel(), table.getSelectionModel().getLeadSelectionIndex(), @@ -1286,12 +1266,10 @@ public class BasicTableUI extends TableUI x = x0; Color save = gfx.getColor(); gfx.setColor(grid); - boolean paintedLine = false; for (int c = 0; c < ncols && x < xmax; ++c) { x += cols.getColumn(c).getWidth(); gfx.drawLine(x, y0, x, ymax); - paintedLine = true; } gfx.setColor(save); } @@ -1302,12 +1280,10 @@ public class BasicTableUI extends TableUI y = y0; Color save = gfx.getColor(); gfx.setColor(grid); - boolean paintedLine = false; for (int r = 0; r < nrows && y < ymax; ++r) { y += height; gfx.drawLine(x0, y, xmax, y); - paintedLine = true; } gfx.setColor(save); } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java index e8eeece..fc38894 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java +++ b/libjava/classpath/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; @@ -334,9 +340,9 @@ public abstract class BasicTextUI extends TextUI * Returns the document position that is (visually) nearest to the given * document position <code>pos</code> in the given direction <code>d</code>. * - * @param c the text component * @param pos the document position * @param b the bias for <code>pos</code> + * @param a the allocation for the view * @param d the direction, must be either {@link SwingConstants#NORTH}, * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or * {@link SwingConstants#EAST} @@ -351,12 +357,11 @@ public abstract class BasicTextUI extends TextUI * @throws BadLocationException if <code>pos</code> is not a valid offset in * the document model */ - public int getNextVisualPositionFrom(JTextComponent c, int pos, - Position.Bias b, int d, - Position.Bias[] biasRet) + public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a, + int d, Position.Bias[] biasRet) throws BadLocationException { - return view.getNextVisualPositionFrom(c, pos, b, d, biasRet); + return view.getNextVisualPositionFrom(pos, b, a, d, biasRet); } } @@ -616,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(), @@ -637,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; } /** @@ -675,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/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java index 7bf2a4a..261db68 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java @@ -1060,7 +1060,7 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants isDragging = true; if (dragWindow != null) - dragWindow.setOffset(new Point(e.getX(), e.getY())); + dragWindow.setOffset(new Point(cachedBounds.width/2, cachedBounds.height/2)); dragTo(e.getPoint(), origin); } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java index 2d54983..f2ebcfc 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java +++ b/libjava/classpath/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/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxUI.java index a43ee3c..0006b78 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxUI.java @@ -313,4 +313,40 @@ public class MetalComboBoxUI extends BasicComboBoxUI d.height + insetsH + 1); } + /** + * Configures the editor for this combo box. + */ + public void configureEditor() + { + ComboBoxEditor cbe = comboBox.getEditor(); + if (cbe != null) + { + cbe.getEditorComponent().setFont(comboBox.getFont()); + cbe.setItem(comboBox.getSelectedItem()); + cbe.addActionListener(comboBox); + } + } + + /** + * Unconfigures the editor for this combo box. + */ + public void unconfigureEditor() + { + ComboBoxEditor cbe = comboBox.getEditor(); + if (cbe != null) + { + cbe.getEditorComponent().setFont(null); + cbe.setItem(null); + cbe.removeActionListener(comboBox); + } + } + + /** + * Lays out the ComboBox + */ + public void layoutComboBox(Container parent, + MetalComboBoxUI.MetalComboBoxLayoutManager manager) + { + manager.layoutContainer(parent); + } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java index 3a2e1c1..967c40d 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java @@ -38,39 +38,421 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.awt.BorderLayout; import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.Insets; +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; + import java.io.File; -import java.util.List; import javax.swing.AbstractAction; import javax.swing.AbstractListModel; +import javax.swing.ActionMap; +import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; import javax.swing.ComboBoxModel; import javax.swing.DefaultListCellRenderer; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JComboBox; 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.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; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; import javax.swing.filechooser.FileFilter; 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; /** * A UI delegate for the {@link JFileChooser} component. This class is only * partially implemented and is not usable yet. */ -public class MetalFileChooserUI extends BasicFileChooserUI +public class MetalFileChooserUI + extends BasicFileChooserUI { + + /** + * 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 + implements PropertyChangeListener + { + /** + * Default constructor. + */ + public MetalFileChooserPropertyChangeListener() + { + } + + /** + * Handles a property change event. + * + * @param e the event. + */ + public void propertyChange(PropertyChangeEvent e) + { + JFileChooser filechooser = getFileChooser(); + + String n = e.getPropertyName(); + if (n.equals(JFileChooser.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY)) + { + int mode = -1; + if (filechooser.isMultiSelectionEnabled()) + mode = ListSelectionModel.MULTIPLE_INTERVAL_SELECTION; + else + 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 + setFileName(file.getName()); + int index = -1; + index = getModel().indexOf(file); + if (index >= 0) + { + 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)) + { + if (listView) + { + fileList.clearSelection(); + fileList.revalidate(); + fileList.repaint(); + } + else + { + fileTable.clearSelection(); + fileTable.revalidate(); + fileTable.repaint(); + } + + setDirectorySelected(false); + File currentDirectory = filechooser.getCurrentDirectory(); + setDirectory(currentDirectory); + boolean hasParent = (currentDirectory.getParentFile() != null); + getChangeToParentDirectoryAction().setEnabled(hasParent); + } + + else if (n.equals(JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY)) + { + filterModel.propertyChange(e); + } + else if (n.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) + { + filterModel.propertyChange(e); + } + else if (n.equals(JFileChooser.DIALOG_TYPE_CHANGED_PROPERTY) + || n.equals(JFileChooser.DIALOG_TITLE_CHANGED_PROPERTY)) + { + Window owner = SwingUtilities.windowForComponent(filechooser); + if (owner instanceof JDialog) + ((JDialog) owner).setTitle(getDialogTitle(filechooser)); + approveButton.setText(getApproveButtonText(filechooser)); + approveButton.setToolTipText( + getApproveButtonToolTipText(filechooser)); + approveButton.setMnemonic(getApproveButtonMnemonic(filechooser)); + } + + else if (n.equals(JFileChooser.APPROVE_BUTTON_TEXT_CHANGED_PROPERTY)) + approveButton.setText(getApproveButtonText(filechooser)); + + else if (n.equals( + JFileChooser.APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY)) + approveButton.setToolTipText(getApproveButtonToolTipText(filechooser)); + + else if (n.equals(JFileChooser.APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY)) + approveButton.setMnemonic(getApproveButtonMnemonic(filechooser)); + + else if (n.equals( + JFileChooser.CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY)) + { + if (filechooser.getControlButtonsAreShown()) + { + topPanel.add(controls, BorderLayout.EAST); + } + else + topPanel.remove(controls); + topPanel.revalidate(); + topPanel.repaint(); + topPanel.doLayout(); + } + + else if (n.equals( + JFileChooser.ACCEPT_ALL_FILE_FILTER_USED_CHANGED_PROPERTY)) + { + if (filechooser.isAcceptAllFileFilterUsed()) + filechooser.addChoosableFileFilter( + getAcceptAllFileFilter(filechooser)); + else + filechooser.removeChoosableFileFilter( + getAcceptAllFileFilter(filechooser)); + } + + else if (n.equals(JFileChooser.ACCESSORY_CHANGED_PROPERTY)) + { + JComponent old = (JComponent) e.getOldValue(); + if (old != null) + getAccessoryPanel().remove(old); + JComponent newval = (JComponent) e.getNewValue(); + if (newval != null) + getAccessoryPanel().add(newval); + } + + if (n.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY) + || n.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY) + || n.equals(JFileChooser.FILE_HIDING_CHANGED_PROPERTY)) + { + // 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(); + } + }; + /** * A combo box model containing the selected directory and all its parent * directories. */ - protected class DirectoryComboBoxModel extends AbstractListModel + protected class DirectoryComboBoxModel + extends AbstractListModel implements ComboBoxModel { /** Storage for the items in the model. */ @@ -161,7 +543,8 @@ public class MetalFileChooserUI extends BasicFileChooserUI /** * Handles changes to the selection in the directory combo box. */ - protected class DirectoryComboBoxAction extends AbstractAction + protected class DirectoryComboBoxAction + extends AbstractAction { /** * Creates a new action. @@ -184,9 +567,60 @@ public class MetalFileChooserUI extends BasicFileChooserUI } /** + * A renderer for the items in the directory combo box. + */ + class DirectoryComboBoxRenderer + extends DefaultListCellRenderer + { + /** + * Creates a new renderer. + */ + public DirectoryComboBoxRenderer(JFileChooser fc) + { + } + + /** + * Returns a component that can be used to paint the given value within + * the list. + * + * @param list the list. + * @param value the value (a {@link File}). + * @param index the item index. + * @param isSelected is the item selected? + * @param cellHasFocus does the list cell have focus? + * + * @return The list cell renderer. + */ + public Component getListCellRendererComponent(JList list, Object value, + int index, boolean isSelected, boolean cellHasFocus) + { + FileView fileView = getFileView(getFileChooser()); + File file = (File) value; + setIcon(fileView.getIcon(file)); + setText(fileView.getName(file)); + + if (isSelected) + { + setBackground(list.getSelectionBackground()); + setForeground(list.getSelectionForeground()); + } + else + { + setBackground(list.getBackground()); + setForeground(list.getForeground()); + } + + setEnabled(list.isEnabled()); + setFont(list.getFont()); + return this; + } + } + + /** * A renderer for the files and directories in the file chooser. */ - protected class FileRenderer extends DefaultListCellRenderer + protected class FileRenderer + extends DefaultListCellRenderer { /** @@ -213,8 +647,17 @@ public class MetalFileChooserUI extends BasicFileChooserUI { FileView v = getFileView(getFileChooser()); File f = (File) value; - setText(v.getName(f)); - setIcon(v.getIcon(f)); + if (f != null) + { + setText(v.getName(f)); + setIcon(v.getIcon(f)); + } + else + { + setText(""); + setIcon(null); + } + setOpaque(true); if (isSelected) { setBackground(list.getSelectionBackground()); @@ -249,7 +692,7 @@ public class MetalFileChooserUI extends BasicFileChooserUI protected FileFilter[] filters; /** The index of the selected file filter. */ - private int selectedIndex; + private Object selected; /** * Creates a new model. @@ -258,7 +701,7 @@ public class MetalFileChooserUI extends BasicFileChooserUI { filters = new FileFilter[1]; filters[0] = getAcceptAllFileFilter(getFileChooser()); - selectedIndex = 0; + selected = filters[0]; } /** @@ -270,11 +713,11 @@ public class MetalFileChooserUI extends BasicFileChooserUI { if (e.getPropertyName().equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) { - selectedIndex = -1; - FileFilter selected = (FileFilter) e.getNewValue(); - for (int i = 0; i < filters.length; i++) - if (filters[i].equals(selected)) - selectedIndex = i; + JFileChooser fc = getFileChooser(); + FileFilter[] choosableFilters = fc.getChoosableFileFilters(); + filters = choosableFilters; + fireContentsChanged(this, 0, filters.length); + selected = e.getNewValue(); fireContentsChanged(this, -1, -1); } else if (e.getPropertyName().equals( @@ -291,13 +734,15 @@ public class MetalFileChooserUI extends BasicFileChooserUI /** * Sets the selected filter. * - * @param filter the filter. + * @param filter the filter (<code>null</code> ignored). */ public void setSelectedItem(Object filter) { - // change the filter in the file chooser and let the property change - // event trigger the change to the selected item - getFileChooser().setFileFilter((FileFilter) filter); + if (filter != null) + { + selected = filter; + fireContentsChanged(this, -1, -1); + } } /** @@ -307,9 +752,7 @@ public class MetalFileChooserUI extends BasicFileChooserUI */ public Object getSelectedItem() { - if (selectedIndex >= 0) - return filters[selectedIndex]; - return null; + return selected; } /** @@ -339,7 +782,8 @@ public class MetalFileChooserUI extends BasicFileChooserUI /** * A renderer for the items in the file filter combo box. */ - public class FilterComboBoxRenderer extends DefaultListCellRenderer + public class FilterComboBoxRenderer + extends DefaultListCellRenderer { /** * Creates a new renderer. @@ -359,20 +803,488 @@ public class MetalFileChooserUI extends BasicFileChooserUI * @param isSelected is the item selected? * @param cellHasFocus does the list cell have focus? * - * @return A component. + * @return This component as the renderer. */ public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + super.getListCellRendererComponent(list, value, index, isSelected, + cellHasFocus); FileFilter filter = (FileFilter) value; - return super.getListCellRendererComponent(list, filter.getDescription(), - index, isSelected, cellHasFocus); + setText(filter.getDescription()); + return this; + } + } + + /** + * A listener for selection events in the file list. + * + * @see #createListSelectionListener(JFileChooser) + */ + class MetalFileChooserSelectionListener + implements ListSelectionListener + { + /** + * Creates a new <code>SelectionListener</code> object. + */ + protected MetalFileChooserSelectionListener() + { + // Do nothing here. + } + + /** + * Makes changes to different properties when + * a value has changed in the filechooser's selection. + * + * @param e - the list selection event that occured. + */ + public void valueChanged(ListSelectionEvent e) + { + File f = (File) fileList.getSelectedValue(); + if (f == null) + return; + JFileChooser filechooser = getFileChooser(); + if (! filechooser.isTraversable(f)) + filechooser.setSelectedFile(f); + else + filechooser.setSelectedFile(null); + } + } + + /** + * A mouse listener for the {@link JFileChooser}. + * This listener is used for editing filenames. + */ + protected class SingleClickListener + extends MouseAdapter + { + + /** Stores instance of the list */ + JList list; + + /** + * Stores the current file that is being edited. + * It is null if nothing is currently being edited. + */ + File editFile; + + /** The current file chooser. */ + JFileChooser fc; + + /** The last file selected. */ + Object lastSelected; + + /** The textfield used for editing. */ + JTextField editField; + + /** + * Creates a new listener. + * + * @param list the directory/file list. + */ + public SingleClickListener(JList list) + { + this.list = list; + editFile = null; + fc = getFileChooser(); + lastSelected = null; + startEditing = false; + } + + /** + * Receives notification of a mouse click event. + * + * @param e the event. + */ + public void mouseClicked(MouseEvent e) + { + 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 && !startEditing && list.isSelectedIndex(index)) + { + Object tmp = list.getModel().getElementAt(index); + if (lastSelected != null && lastSelected.equals(tmp)) + editFile(index); + lastSelected = tmp; + } + else + completeEditing(); + } + else + completeEditing(); + } + + /** + * Sets up the text editor for the current file. + * + * @param index - + * the current index of the item in the list to be edited. + */ + void editFile(int index) + { + Rectangle bounds = list.getCellBounds(index, index); + list.scrollRectToVisible(bounds); + editFile = (File) list.getModel().getElementAt(index); + if (editFile.canWrite()) + { + startEditing = true; + editField = new JTextField(editFile.getName()); + editField.addActionListener(new EditingActionListener()); + + Icon icon = getFileView(fc).getIcon(editFile); + if (icon != null) + { + int padding = icon.getIconWidth() + 4; + bounds.x += padding; + bounds.width -= padding; + } + editField.setBounds(bounds); + + list.add(editField); + + editField.requestFocus(); + editField.selectAll(); + } + else + completeEditing(); + list.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); + list.remove(editField); + } + 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; + + private JComboBox directoryComboBox; + /** The model for the directory combo box. */ DirectoryComboBoxModel directoryModel; + /** The text for a label describing the file text field. */ + private String fileLabel; + + /** The file name text field. */ + private JTextField fileTextField; + + /** The text for a label describing the filter combo box. */ + private String filterLabel; + + /** + * The top panel (contains the directory combo box and the control buttons). + */ + private JPanel topPanel; + + /** A panel containing the control buttons ('up', 'home' etc.). */ + private JPanel controls; + + /** + * The panel that contains the filename field and the filter combobox. + */ + private JPanel bottomPanel; + + /** + * The panel that contains the 'Open' (or 'Save') and 'Cancel' buttons. + */ + private JPanel buttonPanel; + + private JButton approveButton; + + /** The file list. */ + JList fileList; + + /** The file table. */ + JTable fileTable; + + /** The panel containing the file list. */ + JPanel fileListPanel; + + /** The panel containing the file table. */ + JPanel fileTablePanel; + + /** The filter combo box model. */ + private FilterComboBoxModel filterModel; + + /** 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. @@ -393,9 +1305,439 @@ public class MetalFileChooserUI extends BasicFileChooserUI public MetalFileChooserUI(JFileChooser filechooser) { super(filechooser); + bottomPanel = new JPanel(new GridLayout(3, 2)); + buttonPanel = new JPanel(); + } + + public void installUI(JComponent c) + { + super.installUI(c); + actionMap = createActionMap(); + } + + public void uninstallUI(JComponent c) + { + super.uninstallUI(c); + actionMap = null; + } + + /** + * Installs the sub-components of the file chooser. + * + * @param fc the file chooser component. + */ + public void installComponents(JFileChooser fc) + { + fc.setLayout(new BorderLayout()); + topPanel = new JPanel(new BorderLayout()); + dirLabel = new JLabel(directoryLabel); + topPanel.add(dirLabel, BorderLayout.WEST); + this.controls = new JPanel(); + addControlButtons(); + + JPanel dirPanel = new JPanel(new VerticalMidLayout()); + 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); + + 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); + fileFilterCombo.setRenderer(createFilterComboBoxRenderer()); + + 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 row2 = new JPanel(new BorderLayout()); + row2.add(new JLabel(this.filterLabel), BorderLayout.WEST); + row2.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5)); + row2.add(fileFilterCombo); + bottomPanel.add(row2); + JPanel buttonPanel = new JPanel(new ButtonLayout()); + + 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, BorderLayout.SOUTH); + bottomPanel.setBorder(BorderFactory.createEmptyBorder(0, 8, 8, 8)); + fc.add(bottomPanel, BorderLayout.SOUTH); + + fc.add(getAccessoryPanel(), BorderLayout.EAST); + } + + /** + * Uninstalls the components added by + * {@link #installComponents(JFileChooser)}. + * + * @param fc the file chooser. + */ + public void uninstallComponents(JFileChooser fc) + { + fc.remove(bottomPanel); + bottomPanel = null; + fc.remove(fileListPanel); + fc.remove(fileTablePanel); + fileTablePanel = null; + fileListPanel = null; + fc.remove(topPanel); + topPanel = null; + + directoryModel = null; + fileTextField = null; + directoryComboBox = null; + } + + /** + * Returns the panel that contains the 'Open' (or 'Save') and 'Cancel' + * buttons. + * + * @return The panel. + */ + protected JPanel getButtonPanel() + { + return buttonPanel; + } + + /** + * Creates and returns a new panel that will be used for the controls at + * the bottom of the file chooser. + * + * @return A new panel. + */ + protected JPanel getBottomPanel() + { + if (bottomPanel == null) + bottomPanel = new JPanel(new GridLayout(3, 2)); + return bottomPanel; + } + + /** + * Fetches localised strings for use by the labels and buttons on the + * file chooser. + * + * @param fc the file chooser. + */ + protected void installStrings(JFileChooser fc) + { + super.installStrings(fc); + look = "Look In: "; + save = "Save In: "; + if (fc.getDialogType() == JFileChooser.SAVE_DIALOG) + directoryLabel = save; + else + directoryLabel = look; + + fileLabel = "File Name: "; + filterLabel = "Files of Type: "; + + this.cancelButtonMnemonic = 0; + this.cancelButtonText = "Cancel"; + this.cancelButtonToolTipText = "Abort file chooser dialog"; + + this.directoryOpenButtonMnemonic = 0; + this.directoryOpenButtonText = "Open"; + this.directoryOpenButtonToolTipText = "Open selected directory"; + + this.helpButtonMnemonic = 0; + this.helpButtonText = "Help"; + this.helpButtonToolTipText = "Filechooser help"; + + this.openButtonMnemonic = 0; + this.openButtonText = "Open"; + this.openButtonToolTipText = "Open selected file"; + + this.saveButtonMnemonic = 0; + this.saveButtonText = "Save"; + this.saveButtonToolTipText = "Save selected file"; + + this.updateButtonMnemonic = 0; + this.updateButtonText = "Update"; + this.updateButtonToolTipText = "Update directory listing"; + } + + /** + * Installs the listeners required. + * + * @param fc the file chooser. + */ + protected void installListeners(JFileChooser fc) + { + directoryComboBox.setAction(new DirectoryComboBoxAction()); + 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); + } + + protected void uninstallListeners(JFileChooser fc) + { + 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() + { + if (actionMap == null) + actionMap = createActionMap(); + return actionMap; + } + + /** + * Creates and returns an action map. + * + * @return The action map. + */ + protected ActionMap createActionMap() + { + ActionMap map = new ActionMap(); + map.put("approveSelection", getApproveSelectionAction()); + map.put("cancelSelection", getCancelSelectionAction()); + map.put("Go Up", getChangeToParentDirectoryAction()); + return map; + } + + /** + * Creates a panel containing a list of files. + * + * @param fc the file chooser. + * + * @return A panel. + */ + protected JPanel createList(JFileChooser fc) + { + 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; + } + + /** + * Creates a panel containing a table within a scroll pane. + * + * @param fc the file chooser. + * + * @return The details view. + */ + protected JPanel createDetailsView(JFileChooser fc) + { + 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. + * + * @param fc the file chooser. + * + * @return The listener. + * + * @see #installListeners(JFileChooser) + */ + public ListSelectionListener createListSelectionListener(JFileChooser fc) + { + return new MetalFileChooserSelectionListener(); + } + + /** + * Returns the preferred size for the file chooser component. + * + * @return The preferred size. + */ + public Dimension getPreferredSize(JComponent c) + { + 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. + * + * @return The minimum size. + */ + public Dimension getMinimumSize(JComponent c) + { + Dimension tp = topPanel.getMinimumSize(); + Dimension bp = bottomPanel.getMinimumSize(); + Dimension fl = fileListPanel.getMinimumSize(); + return new Dimension(fl.width, tp.height + bp.height + fl.height); } /** + * Returns the maximum size for the file chooser component. + * + * @return The maximum size. + */ + public Dimension getMaximumSize(JComponent c) + { + return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); + } + + /** + * Creates a property change listener that monitors the {@link JFileChooser} + * for property change events and updates the component display accordingly. + * + * @param fc the file chooser. + * + * @return The property change listener. + * + * @see #installListeners(JFileChooser) + */ + public PropertyChangeListener createPropertyChangeListener(JFileChooser fc) + { + return new MetalFileChooserPropertyChangeListener(); + } + + /** * Creates and returns a new instance of {@link DirectoryComboBoxModel}. * * @return A new instance of {@link DirectoryComboBoxModel}. @@ -407,6 +1749,20 @@ public class MetalFileChooserUI extends BasicFileChooserUI } /** + * Creates a new instance of the renderer used in the directory + * combo box. + * + * @param fc the file chooser. + * + * @return The renderer. + */ + protected DirectoryComboBoxRenderer createDirectoryComboBoxRenderer( + JFileChooser fc) + { + return new DirectoryComboBoxRenderer(fc); + } + + /** * Creates and returns a new instance of {@link FilterComboBoxModel}. * * @return A new instance of {@link FilterComboBoxModel}. @@ -427,4 +1783,297 @@ public class MetalFileChooserUI extends BasicFileChooserUI return new FilterComboBoxRenderer(); } + /** + * Adds the control buttons ('up', 'home' etc.) to the panel. + */ + protected void addControlButtons() + { + JButton upButton = new JButton(getChangeToParentDirectoryAction()); + upButton.setText(null); + upButton.setIcon(this.upFolderIcon); + upButton.setMargin(new Insets(0, 0, 0, 0)); + controls.add(upButton); + + JButton homeButton = new JButton(getGoHomeAction()); + homeButton.setText(null); + homeButton.setIcon(this.homeFolderIcon); + homeButton.setMargin(new Insets(0, 0, 0, 0)); + controls.add(homeButton); + + JButton newFolderButton = new JButton(getNewFolderAction()); + newFolderButton.setText(null); + newFolderButton.setIcon(this.newFolderIcon); + newFolderButton.setMargin(new Insets(0, 0, 0, 0)); + controls.add(newFolderButton); + + JToggleButton listButton = new JToggleButton(this.listViewIcon); + listButton.setMargin(new Insets(0, 0, 0, 0)); + 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)); + detailButton.addActionListener(new DetailViewActionListener()); + detailButton.setSelected(false); + controls.add(detailButton); + + ButtonGroup buttonGroup = new ButtonGroup(); + buttonGroup.add(listButton); + buttonGroup.add(detailButton); + } + + /** + * Removes all the buttons from the control panel. + */ + protected void removeControlButtons() + { + controls.removeAll(); + controls.revalidate(); + controls.repaint(); + } + + /** + * Updates the current directory. + * + * @param the file chooser to update. + */ + public void rescanCurrentDirectory(JFileChooser fc) + { + directoryModel.setSelectedItem(fc.getCurrentDirectory()); + getModel().validateFileCache(); + if (!listView) + updateTable(); + else + createList(fc); + } + + /** + * Returns the file name in the text field. + * + * @return The file name. + */ + public String getFileName() + { + String result = null; + if (fileTextField != null) + result = fileTextField.getText(); + return result; + } + + /** + * Sets the file name in the text field. + * + * @param filename the file name. + */ + public void setFileName(String filename) + { + fileTextField.setText(filename); + } + + /** + * DOCUMENT ME!! + * + * @param e - DOCUMENT ME! + */ + public void valueChanged(ListSelectionEvent e) + { + // FIXME: Not sure what we should be doing here, if anything. + } + + /** + * Returns the approve button. + * + * @return The approve button. + */ + protected JButton getApproveButton(JFileChooser fc) + { + return approveButton; + } + + /** + * A layout manager that is used to arrange the subcomponents of the + * {@link JFileChooser}. + */ + class VerticalMidLayout implements LayoutManager + { + /** + * Performs the layout. + * + * @param parent the container. + */ + public void layoutContainer(Container parent) + { + int count = parent.getComponentCount(); + if (count > 0) + { + Insets insets = parent.getInsets(); + Component c = parent.getComponent(0); + Dimension prefSize = c.getPreferredSize(); + int h = parent.getHeight() - insets.top - insets.bottom; + int adj = Math.max(0, (h - prefSize.height) / 2); + c.setBounds(insets.left, insets.top + adj, parent.getWidth() + - insets.left - insets.right, + (int) Math.min(prefSize.getHeight(), h)); + } + } + + /** + * Returns the minimum layout size. + * + * @param parent the container. + * + * @return The minimum layout size. + */ + public Dimension minimumLayoutSize(Container parent) + { + return preferredLayoutSize(parent); + } + + /** + * Returns the preferred layout size. + * + * @param parent the container. + * + * @return The preferred layout size. + */ + public Dimension preferredLayoutSize(Container parent) + { + if (parent.getComponentCount() > 0) + { + return parent.getComponent(0).getPreferredSize(); + } + else return null; + } + + /** + * This layout manager does not need to track components, so this + * method does nothing. + * + * @param name the name the component is associated with. + * @param component the component. + */ + public void addLayoutComponent(String name, Component component) + { + // do nothing + } + + /** + * This layout manager does not need to track components, so this + * method does nothing. + * + * @param component the component. + */ + public void removeLayoutComponent(Component component) { + // do nothing + } + } + + /** + * A layout manager that is used to arrange buttons for the + * {@link JFileChooser}. + */ + class ButtonLayout implements LayoutManager + { + static final int GAP = 4; + + /** + * Performs the layout. + * + * @param parent the container. + */ + public void layoutContainer(Container parent) + { + int count = parent.getComponentCount(); + if (count > 0) + { + // first find the widest button + int maxW = 0; + for (int i = 0; i < count; i++) + { + Component c = parent.getComponent(i); + Dimension prefSize = c.getPreferredSize(); + maxW = Math.max(prefSize.width, maxW); + } + + // then position the buttons + Insets insets = parent.getInsets(); + int availableH = parent.getHeight() - insets.top - insets.bottom; + int currentX = parent.getWidth() - insets.right; + for (int i = count - 1; i >= 0; i--) + { + Component c = parent.getComponent(i); + Dimension prefSize = c.getPreferredSize(); + int adj = Math.max(0, (availableH - prefSize.height) / 2); + currentX = currentX - prefSize.width; + c.setBounds(currentX, insets.top + adj, prefSize.width, + (int) Math.min(prefSize.getHeight(), availableH)); + currentX = currentX - GAP; + } + } + } + + /** + * Returns the minimum layout size. + * + * @param parent the container. + * + * @return The minimum layout size. + */ + public Dimension minimumLayoutSize(Container parent) + { + return preferredLayoutSize(parent); + } + + /** + * Returns the preferred layout size. + * + * @param parent the container. + * + * @return The preferred layout size. + */ + public Dimension preferredLayoutSize(Container parent) + { + Insets insets = parent.getInsets(); + int maxW = 0; + int maxH = 0; + int count = parent.getComponentCount(); + if (count > 0) + { + for (int i = 0; i < count; i++) + { + Component c = parent.getComponent(i); + Dimension d = c.getPreferredSize(); + maxW = Math.max(d.width, maxW); + maxH = Math.max(d.height, maxH); + } + } + return new Dimension(maxW * count + GAP * (count - 1) + insets.left + + insets.right, maxH + insets.top + insets.bottom); + } + + /** + * This layout manager does not need to track components, so this + * method does nothing. + * + * @param name the name the component is associated with. + * @param component the component. + */ + public void addLayoutComponent(String name, Component component) + { + // do nothing + } + + /** + * This layout manager does not need to track components, so this + * method does nothing. + * + * @param component the component. + */ + public void removeLayoutComponent(Component component) { + // do nothing + } + } + } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java b/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java index bcb86e6..0b644f3 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java @@ -68,6 +68,12 @@ public class MetalIconFactory implements Serializable /** A constant representing "light". */ public static final boolean LIGHT = true; + + /** A shared instance of the MenuArrowIcon. */ + private static Icon menuArrow; + + /** A shared instance of the MenuItemArrowIcon. */ + private static Icon menuItemArrow; /** * An icon displayed for {@link JCheckBoxMenuItem} components. @@ -2467,4 +2473,102 @@ public class MetalIconFactory implements Serializable return treeHardDriveIcon; } + /** + * Returns a new instance of a 4 x 8 icon showing a small black triangle that + * points to the right. This is displayed in menu items that have a + * sub menu. + * + * @return The icon. + */ + public static Icon getMenuArrowIcon() + { + if (menuArrow == null) + menuArrow = new Icon() + { + public int getIconHeight() + { + return 8; + } + + public int getIconWidth() + { + return 4; + } + + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color saved = g.getColor(); + g.setColor(Color.BLACK); + for (int i = 0; i < 4; i++) + g.drawLine(x + i, y + i, x + i, y + 7 - i); + g.setColor(saved); + } + }; + return menuArrow; + } + + /** + * Returns a new instance of a 4 x 8 icon showing a small black triangle that + * points to the right. This is displayed in menu items that have a sub menu. + * + * @return The icon. + */ + public static Icon getMenuItemArrowIcon() + { + if (menuItemArrow == null) + menuItemArrow = new Icon() + { + public int getIconHeight() + { + return 8; + } + + public int getIconWidth() + { + return 4; + } + + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color saved = g.getColor(); + g.setColor(Color.BLACK); + for (int i = 0; i < 4; i++) + g.drawLine(x + i, y + i, x + i, y + 7 - i); + g.setColor(saved); + } + }; + return menuItemArrow; + } + + /** + * Returns a new instance of a 13 x 13 icon showing a small black check mark. + * + * @return The icon. + */ + public static Icon getMenuItemCheckIcon() + { + return new Icon() + { + public int getIconHeight() + { + return 13; + } + + public int getIconWidth() + { + return 13; + } + + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color saved = g.getColor(); + g.setColor(Color.BLACK); + g.drawLine(3 + x, 5 + y, 3 + x, 9 + y); + g.drawLine(4 + x, 5 + y, 4 + x, 9 + y); + g.drawLine(5 + x, 7 + y, 9 + x, 3 + y); + g.drawLine(5 + x, 8 + y, 9 + x, 4 + y); + g.setColor(saved); + } + }; + } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java b/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java index da01937..c60b55c 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java @@ -40,7 +40,6 @@ package javax.swing.plaf.metal; import java.awt.Color; import java.awt.Font; -import java.awt.Insets; import javax.swing.LookAndFeel; import javax.swing.UIDefaults; @@ -55,7 +54,17 @@ import javax.swing.plaf.basic.BasicLookAndFeel; /** * A custom look and feel that is designed to look similar across different - * operating systems. + * operating systems. To install this look and feel, add the following code + * (or something similar) near the start of your application:</p> + * <pre> + * try + * { + * UIManager.setLookAndFeel(new MetalLookAndFeel()); + * } + * catch (UnsupportedLookAndFeelException e) + * { + * e.printStackTrace(); + * }</pre> */ public class MetalLookAndFeel extends BasicLookAndFeel { @@ -72,8 +81,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public MetalLookAndFeel() { - if (theme == null) - createDefaultTheme(); + createDefaultTheme(); } /** @@ -81,7 +89,8 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ protected void createDefaultTheme() { - setCurrentTheme(new DefaultMetalTheme()); + if (theme == null) + setCurrentTheme(new DefaultMetalTheme()); } /** @@ -115,7 +124,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public String getDescription() { - return "Metal look and feel"; + return "The Java(tm) Look and Feel"; } /** @@ -125,7 +134,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public String getID() { - return "MetalLookAndFeel"; + return "Metal"; } /** @@ -135,7 +144,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public String getName() { - return "MetalLookAndFeel"; + return "Metal"; } public UIDefaults getDefaults() @@ -145,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. @@ -159,7 +169,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getAcceleratorForeground() { - return theme.getAcceleratorForeground(); + if (theme != null) + return theme.getAcceleratorForeground(); + return null; } /** @@ -170,7 +182,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getAcceleratorSelectedForeground() { - return theme.getAcceleratorSelectedForeground(); + if (theme != null) + return theme.getAcceleratorSelectedForeground(); + return null; } /** @@ -180,7 +194,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getBlack() { - return theme.getBlack(); + if (theme != null) + return theme.getBlack(); + return null; } /** @@ -190,7 +206,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getControl() { - return theme.getControl(); + if (theme != null) + return theme.getControl(); + return null; } /** @@ -201,7 +219,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getControlDarkShadow() { - return theme.getControlDarkShadow(); + if (theme != null) + return theme.getControlDarkShadow(); + return null; } /** @@ -211,7 +231,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getControlDisabled() { - return theme.getControlDisabled(); + if (theme != null) + return theme.getControlDisabled(); + return null; } /** @@ -222,7 +244,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getControlHighlight() { - return theme.getControlHighlight(); + if (theme != null) + return theme.getControlHighlight(); + return null; } /** @@ -233,7 +257,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getControlInfo() { - return theme.getControlInfo(); + if (theme != null) + return theme.getControlInfo(); + return null; } /** @@ -244,7 +270,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getControlShadow() { - return theme.getControlShadow(); + if (theme != null) + return theme.getControlShadow(); + return null; } /** @@ -254,7 +282,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getControlTextColor() { - return theme.getControlTextColor(); + if (theme != null) + return theme.getControlTextColor(); + return null; } /** @@ -264,7 +294,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static FontUIResource getControlTextFont() { - return theme.getControlTextFont(); + if (theme != null) + return theme.getControlTextFont(); + return null; } /** @@ -275,7 +307,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getDesktopColor() { - return theme.getDesktopColor(); + if (theme != null) + return theme.getDesktopColor(); + return null; } /** @@ -286,7 +320,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getFocusColor() { - return theme.getFocusColor(); + if (theme != null) + return theme.getFocusColor(); + return null; } /** @@ -297,7 +333,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getHighlightedTextColor() { - return theme.getHighlightedTextColor(); + if (theme != null) + return theme.getHighlightedTextColor(); + return null; } /** @@ -308,7 +346,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getInactiveControlTextColor() { - return theme.getInactiveControlTextColor(); + if (theme != null) + return theme.getInactiveControlTextColor(); + return null; } /** @@ -319,7 +359,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getInactiveSystemTextColor() { - return theme.getInactiveSystemTextColor(); + if (theme != null) + return theme.getInactiveSystemTextColor(); + return null; } /** @@ -331,7 +373,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getMenuBackground() { - return theme.getMenuBackground(); + if (theme != null) + return theme.getMenuBackground(); + return null; } /** @@ -344,7 +388,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getMenuDisabledForeground() { - return theme.getMenuDisabledForeground(); + if (theme != null) + return theme.getMenuDisabledForeground(); + return null; } /** @@ -357,7 +403,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getMenuForeground() { - return theme.getMenuForeground(); + if (theme != null) + return theme.getMenuForeground(); + return null; } /** @@ -370,7 +418,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getMenuSelectedBackground() { - return theme.getMenuSelectedBackground(); + if (theme != null) + return theme.getMenuSelectedBackground(); + return null; } /** @@ -383,7 +433,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getMenuSelectedForeground() { - return theme.getMenuSelectedForeground(); + if (theme != null) + return theme.getMenuSelectedForeground(); + return null; } /** @@ -393,7 +445,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static FontUIResource getMenuTextFont() { - return theme.getMenuTextFont(); + if (theme != null) + return theme.getMenuTextFont(); + return null; } /** @@ -403,7 +457,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getPrimaryControl() { - return theme.getPrimaryControl(); + if (theme != null) + return theme.getPrimaryControl(); + return null; } /** @@ -414,7 +470,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getPrimaryControlDarkShadow() { - return theme.getPrimaryControlDarkShadow(); + if (theme != null) + return theme.getPrimaryControlDarkShadow(); + return null; } /** @@ -425,7 +483,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getPrimaryControlHighlight() { - return theme.getPrimaryControlHighlight(); + if (theme != null) + return theme.getPrimaryControlHighlight(); + return null; } /** @@ -436,7 +496,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getPrimaryControlInfo() { - return theme.getPrimaryControlInfo(); + if (theme != null) + return theme.getPrimaryControlInfo(); + return null; } /** @@ -447,7 +509,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getPrimaryControlShadow() { - return theme.getPrimaryControlShadow(); + if (theme != null) + return theme.getPrimaryControlShadow(); + return null; } /** @@ -457,7 +521,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getSeparatorBackground() { - return theme.getSeparatorBackground(); + if (theme != null) + return theme.getSeparatorBackground(); + return null; } /** @@ -467,7 +533,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getSeparatorForeground() { - return theme.getSeparatorForeground(); + if (theme != null) + return theme.getSeparatorForeground(); + return null; } /** @@ -477,7 +545,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static FontUIResource getSubTextFont() { - return theme.getSubTextFont(); + if (theme != null) + return theme.getSubTextFont(); + return null; } /** @@ -487,7 +557,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getSystemTextColor() { - return theme.getSystemTextColor(); + if (theme != null) + return theme.getSystemTextColor(); + return null; } /** @@ -497,7 +569,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static FontUIResource getSystemTextFont() { - return theme.getSystemTextFont(); + if (theme != null) + return theme.getSystemTextFont(); + return null; } /** @@ -507,7 +581,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getTextHighlightColor() { - return theme.getTextHighlightColor(); + if (theme != null) + return theme.getTextHighlightColor(); + return null; } /** @@ -517,7 +593,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getUserTextColor() { - return theme.getUserTextColor(); + if (theme != null) + return theme.getUserTextColor(); + return null; } /** @@ -527,7 +605,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static FontUIResource getUserTextFont() { - return theme.getUserTextFont(); + if (theme != null) + return theme.getUserTextFont(); + return null; } /** @@ -537,7 +617,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getWhite() { - return theme.getWhite(); + if (theme != null) + return theme.getWhite(); + return null; } /** @@ -547,7 +629,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getWindowBackground() { - return theme.getWindowBackground(); + if (theme != null) + return theme.getWindowBackground(); + return null; } /** @@ -557,7 +641,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getWindowTitleBackground() { - return theme.getWindowTitleBackground(); + if (theme != null) + return theme.getWindowTitleBackground(); + return null; } /** @@ -569,7 +655,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static FontUIResource getWindowTitleFont() { - return theme.getWindowTitleFont(); + if (theme != null) + return theme.getWindowTitleFont(); + return null; } /** @@ -579,7 +667,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getWindowTitleForeground() { - return theme.getWindowTitleForeground(); + if (theme != null) + return theme.getWindowTitleForeground(); + return null; } /** @@ -590,7 +680,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getWindowTitleInactiveBackground() { - return theme.getWindowTitleInactiveBackground(); + if (theme != null) + return theme.getWindowTitleInactiveBackground(); + return null; } /** @@ -601,7 +693,9 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public static ColorUIResource getWindowTitleInactiveForeground() { - return theme.getWindowTitleInactiveForeground(); + if (theme != null) + return theme.getWindowTitleInactiveForeground(); + return null; } /** @@ -691,6 +785,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel "CheckBoxUI", "javax.swing.plaf.metal.MetalCheckBoxUI", "ComboBoxUI", "javax.swing.plaf.metal.MetalComboBoxUI", "DesktopIconUI", "javax.swing.plaf.metal.MetalDesktopIconUI", + "FileChooserUI", "javax.swing.plaf.metal.MetalFileChooserUI", "InternalFrameUI", "javax.swing.plaf.metal.MetalInternalFrameUI", "LabelUI", "javax.swing.plaf.metal.MetalLabelUI", "MenuBarUI", "javax.swing.plaf.metal.MetalMenuBarUI", @@ -841,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(), @@ -858,6 +953,19 @@ public class MetalLookAndFeel extends BasicLookAndFeel "FormattedTextField.selectionBackground", getTextHighlightColor(), "FormattedTextField.selectionForeground", getHighlightedTextColor(), + "FileChooser.upFolderIcon", + MetalIconFactory.getFileChooserUpFolderIcon(), + "FileChooser.listViewIcon", + MetalIconFactory.getFileChooserListViewIcon(), + "FileChooser.newFolderIcon", + MetalIconFactory.getFileChooserNewFolderIcon(), + "FileChooser.homeFolderIcon", + 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(), @@ -875,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(), @@ -902,14 +1015,18 @@ public class MetalLookAndFeel extends BasicLookAndFeel "Menu.acceleratorFont", new FontUIResource("Dialog", Font.PLAIN, 10), "Menu.acceleratorForeground", getAcceleratorForeground(), "Menu.acceleratorSelectionForeground", getAcceleratorSelectedForeground(), + "Menu.arrowIcon", MetalIconFactory.getMenuArrowIcon(), "Menu.background", getMenuBackground(), "Menu.border", new MetalBorders.MenuItemBorder(), "Menu.borderPainted", Boolean.TRUE, + "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(), @@ -918,11 +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(), @@ -930,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), @@ -953,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(), @@ -962,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), @@ -997,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(), @@ -1006,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(), @@ -1041,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(), @@ -1048,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(), @@ -1150,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/libjava/classpath/javax/swing/plaf/metal/MetalRadioButtonUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalRadioButtonUI.java index de71fe8..9fb960f 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalRadioButtonUI.java +++ b/libjava/classpath/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/libjava/classpath/javax/swing/plaf/metal/MetalScrollPaneUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalScrollPaneUI.java index d5bf175..ae14af3 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalScrollPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalScrollPaneUI.java @@ -38,7 +38,10 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.beans.PropertyChangeListener; + import javax.swing.JComponent; +import javax.swing.JScrollBar; import javax.swing.JScrollPane; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicScrollPaneUI; @@ -68,4 +71,89 @@ public class MetalScrollPaneUI { return new MetalScrollPaneUI(); } + + /** + * Configures the specified component appropriate for the look and feel. + * This method is invoked when the ComponentUI instance is being installed + * as the UI delegate on the specified component. This method should + * completely configure the component for the look and feel, + * including the following: + * 1. Install any default property values for color, fonts, borders, + * icons, opacity, etc. on the component. Whenever possible, property + * values initialized by the client program should not be overridden. + * 2. Install a LayoutManager on the component if necessary. + * 3. Create/add any required sub-components to the component. + * 4. Create/install event listeners on the component. + * 5. Create/install a PropertyChangeListener on the component in order + * to detect and respond to component property changes appropriately. + * 6. Install keyboard UI (mnemonics, traversal, etc.) on the component. + * 7. Initialize any appropriate instance data. + * + * @param c - the component to install the ui on + */ + public void installUI(JComponent c) + { + super.installUI(c); + JScrollBar hsb = scrollpane.getHorizontalScrollBar(); + hsb.putClientProperty(MetalScrollBarUI.FREE_STANDING_PROP, Boolean.FALSE); + JScrollBar vsb = scrollpane.getVerticalScrollBar(); + vsb.putClientProperty(MetalScrollBarUI.FREE_STANDING_PROP, Boolean.FALSE); + } + + /** + * Reverses configuration which was done on the specified component + * during installUI. This method is invoked when this UIComponent + * instance is being removed as the UI delegate for the specified + * component. This method should undo the configuration performed in + * installUI, being careful to leave the JComponent instance in a + * clean state (no extraneous listeners, look-and-feel-specific property + * objects, etc.). This should include the following: + * 1. Remove any UI-set borders from the component. + * 2. Remove any UI-set layout managers on the component. + * 3. Remove any UI-added sub-components from the component. + * 4. Remove any UI-added event/property listeners from the component. + * 5. Remove any UI-installed keyboard UI from the component. + * 6. Nullify any allocated instance data objects to allow for GC. + * + * @param c - the component to uninstall the ui on + */ + public void uninstallUI(JComponent c) + { + JScrollBar hsb = scrollpane.getHorizontalScrollBar(); + hsb.putClientProperty(MetalScrollBarUI.FREE_STANDING_PROP, null); + JScrollBar vsb = scrollpane.getVerticalScrollBar(); + vsb.putClientProperty(MetalScrollBarUI.FREE_STANDING_PROP, null); + super.uninstallUI(c); + } + + /** + * Installs listeners on scrollPane + * + * @param scrollPane - the component to install the listeners on + */ + public void installListeners(JScrollPane scrollPane) + { + super.installListeners(scrollPane); + } + + /** + * Uninstalls listeners on scrollPane + * + * @param scrollPane - the component to uninstall the listeners on + */ + public void uninstallListeners(JScrollPane scrollPane) + { + super.uninstallListeners(scrollPane); + } + + /** + * TODO + * + * @return TODO + */ + protected PropertyChangeListener createScrollBarSwapListener() + { + // FIXME: Anything else to do here? + return super.createPropertyChangeListener(); + } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java b/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java index 016e095..34a964c 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java @@ -38,9 +38,16 @@ exception statement from your version. */ package javax.swing.plaf.metal; import java.awt.Color; +import java.awt.Component; +import java.awt.Container; import java.awt.Dimension; import java.awt.Graphics; +import java.awt.LayoutManager; +import java.awt.Point; +import javax.swing.JSplitPane; +import javax.swing.SwingConstants; +import javax.swing.plaf.basic.BasicArrowButton; import javax.swing.plaf.basic.BasicSplitPaneDivider; /** @@ -56,7 +63,13 @@ class MetalSplitPaneDivider extends BasicSplitPaneDivider /** The light color in the pattern. */ Color light; + + /** The JSplitPane the divider is on. */ + JSplitPane splitPane; + /** The split pane orientation. */ + int orientation; + /** * Creates a new instance of MetalSplitPaneDivider. * @@ -65,6 +78,9 @@ class MetalSplitPaneDivider extends BasicSplitPaneDivider public MetalSplitPaneDivider(MetalSplitPaneUI ui, Color light, Color dark) { super(ui); + setLayout(new MetalDividerLayout()); + this.splitPane = super.splitPane; + this.orientation = super.orientation; this.light = light; this.dark = dark; } @@ -76,9 +92,127 @@ class MetalSplitPaneDivider extends BasicSplitPaneDivider */ public void paint(Graphics g) { - //super.paint(g); Dimension s = getSize(); MetalUtils.fillMetalPattern(splitPane, g, 2, 2, s.width - 4, s.height - 4, light, dark); + if (splitPane.isOneTouchExpandable()) + { + ((BasicArrowButton) rightButton).paint(g); + ((BasicArrowButton) leftButton).paint(g); + } + } + + /** + * This helper class acts as the Layout Manager for the divider. + */ + public class MetalDividerLayout implements LayoutManager + { + /** The right button. */ + BasicArrowButton rb; + + /** The left button. */ + BasicArrowButton lb; + + /** + * Creates a new DividerLayout object. + */ + public MetalDividerLayout() + { + // Nothing to do here + } + + /** + * This method is called when a Component is added. + * + * @param string The constraints string. + * @param c The Component to add. + */ + public void addLayoutComponent(String string, Component c) + { + // Nothing to do here, constraints are set depending on + // orientation in layoutContainer + } + + /** + * This method is called to lay out the container. + * + * @param c The container to lay out. + */ + public void layoutContainer(Container c) + { + // The only components we care about setting up are the + // one touch buttons. + if (splitPane.isOneTouchExpandable()) + { + if (c.getComponentCount() == 2) + { + Component c1 = c.getComponent(0); + Component c2 = c.getComponent(1); + if ((c1 instanceof BasicArrowButton) + && (c2 instanceof BasicArrowButton)) + { + lb = ((BasicArrowButton) c1); + rb = ((BasicArrowButton) c2); + } + } + if (rb != null && lb != null) + { + Point p = getLocation(); + lb.setSize(lb.getPreferredSize()); + rb.setSize(rb.getPreferredSize()); + lb.setLocation(p.x, p.y); + + if (orientation == JSplitPane.HORIZONTAL_SPLIT) + { + rb.setDirection(SwingConstants.EAST); + lb.setDirection(SwingConstants.WEST); + rb.setLocation(p.x, p.y + lb.getHeight()); + } + else + { + rb.setDirection(SwingConstants.SOUTH); + lb.setDirection(SwingConstants.NORTH); + rb.setLocation(p.x + lb.getWidth(), p.y); + } + } + } + } + + /** + * This method returns the minimum layout size. + * + * @param c The container to calculate for. + * + * @return The minimum layout size. + */ + public Dimension minimumLayoutSize(Container c) + { + return preferredLayoutSize(c); + } + + /** + * This method returns the preferred layout size. + * + * @param c The container to calculate for. + * + * @return The preferred layout size. + */ + public Dimension preferredLayoutSize(Container c) + { + int dividerSize = getDividerSize(); + return new Dimension(dividerSize, dividerSize); + } + + /** + * This method is called when a component is removed. + * + * @param c The component to remove. + */ + public void removeLayoutComponent(Component c) + { + // Nothing to do here. If buttons are removed + // they will not be layed out when layoutContainer is + // called. + } } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java index b1e02c7..c6c46ff 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java @@ -38,11 +38,14 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.awt.Color; import java.awt.Graphics; import java.awt.LayoutManager; +import java.awt.Rectangle; import javax.swing.JComponent; import javax.swing.JTabbedPane; +import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicTabbedPaneUI; @@ -101,6 +104,29 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI } /** + * The minimum tab width. + */ + protected int minTabWidth; + + /** + * The color for the selected tab. + */ + protected Color selectColor; + + /** + * The color for a highlighted selected tab. + */ + protected Color selectHighlight; + + /** + * The background color used for the tab area. + */ + protected Color tabAreaBackground; + + /** The graphics to draw the highlight below the tab. */ + private Graphics hg; + + /** * Constructs a new instance of MetalTabbedPaneUI. */ public MetalTabbedPaneUI() @@ -175,6 +201,16 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI protected void paintTopTabBorder(int tabIndex, Graphics g, int x, int y, int w, int h, int btm, int rght, boolean isSelected) { + int currentRun = getRunForTab(tabPane.getTabCount(), tabIndex); + if (shouldFillGap(currentRun, tabIndex, x, y)) + { + g.translate(x, y); + g.setColor(getColorForGap(currentRun, x, y)); + g.fillRect(1, 0, 5, 3); + g.fillRect(1, 3, 2, 2); + g.translate(-x, -y); + } + if (isSelected) { g.setColor(MetalLookAndFeel.getControlHighlight()); @@ -267,6 +303,16 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI protected void paintBottomTabBorder(int tabIndex, Graphics g, int x, int y, int w, int h, int btm, int rght, boolean isSelected) { + int currentRun = getRunForTab(tabPane.getTabCount(), tabIndex); + if (shouldFillGap(currentRun, tabIndex, x, y)) + { + g.translate(x, y); + g.setColor(getColorForGap(currentRun, x, y)); + g.fillRect(1, h - 5, 3, 5); + g.fillRect(4, h - 2, 2, 2); + g.translate(-x, -y); + } + if (isSelected) { g.setColor(MetalLookAndFeel.getControlHighlight()); @@ -297,9 +343,16 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI int tabIndex, int x, int y, int w, int h, boolean isSelected) { if (isSelected) - g.setColor(MetalLookAndFeel.getControl()); + g.setColor(UIManager.getColor("TabbedPane.selected")); else - g.setColor(MetalLookAndFeel.getControlShadow()); + { + // This is only present in the OceanTheme, so we must check if it + // is actually there + Color background = UIManager.getColor("TabbedPane.unselectedBackground"); + if (background == null) + background = UIManager.getColor("TabbedPane.background"); + g.setColor(background); + } int[] px, py; if (tabPlacement == TOP) { @@ -324,6 +377,8 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI else throw new AssertionError("Unrecognised 'tabPlacement' argument."); g.fillPolygon(px, py, 5); + hg = g; + paintHighlightBelowTab(); } /** @@ -342,5 +397,94 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI // (which is drawn at the very top for tabPlacement == TOP) return run < this.runCount - 1; } + + /** + * Installs the defaults for this UI. This method calls super.installDefaults + * and then loads the Metal specific defaults for TabbedPane. + */ + protected void installDefaults() + { + super.installDefaults(); + selectColor = UIManager.getColor("TabbedPane.selected"); + selectHighlight = UIManager.getColor("TabbedPane.selectHighlight"); + tabAreaBackground = UIManager.getColor("TabbedPane.tabAreaBackground"); + minTabWidth = 0; + } + /** + * Returns the color for the gap. + * + * @param currentRun - The current run to return the color for + * @param x - The x position of the current run + * @param y - The y position of the current run + * + * @return the color for the gap in the current run. + */ + protected Color getColorForGap(int currentRun, int x, int y) + { + int index = tabForCoordinate(tabPane, x, y); + int selected = tabPane.getSelectedIndex(); + if (selected == index) + return selectColor; + return tabAreaBackground; + } + + /** + * Returns true if the gap should be filled in. + * + * @param currentRun - The current run + * @param tabIndex - The current tab + * @param x - The x position of the tab + * @param y - The y position of the tab + * + * @return true if the gap at the current run should be filled + */ + protected boolean shouldFillGap(int currentRun, int tabIndex, int x, int y) + { + // As far as I can tell, the gap is never filled in. + return false; + } + + /** + * Paints the highlight below the tab, if there is one. + */ + protected void paintHighlightBelowTab() + { + int selected = tabPane.getSelectedIndex(); + int tabPlacement = tabPane.getTabPlacement(); + Rectangle bounds = getTabBounds(tabPane, selected); + + hg.setColor(selectColor); + int x = bounds.x; + int y = bounds.y; + int w = bounds.width; + int h = bounds.height; + + if (tabPlacement == TOP) + hg.fillRect(x, y + h - 2, w, 30); + else if (tabPlacement == LEFT) + hg.fillRect(x + w - 1, y, 20, h); + else if (tabPlacement == BOTTOM) + hg.fillRect(x, y - h + 2, w, 30); + else if (tabPlacement == RIGHT) + hg.fillRect(x - 18, y, 20, h); + else + throw new AssertionError("Unrecognised 'tabPlacement' argument."); + hg = null; + } + + /** + * Returns true if we should rotate the tab runs. + * + * @param tabPlacement - The current tab placement. + * @param selectedRun - The selected run. + * + * @return true if the tab runs should be rotated. + */ + protected boolean shouldRotateTabRuns(int tabPlacement, + int selectedRun) + { + // false because tab runs are not rotated in the MetalLookAndFeel + return false; + } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalTextFieldUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalTextFieldUI.java index 6984dae..30738b3 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalTextFieldUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalTextFieldUI.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.beans.PropertyChangeEvent; + import javax.swing.JComponent; import javax.swing.JTextField; import javax.swing.plaf.ComponentUI; @@ -67,4 +69,14 @@ public class MetalTextFieldUI extends BasicTextFieldUI { return new MetalTextFieldUI(); } + + /** + * This method gets called when a bound property is changed on the associated + * JTextComponent. This is a hook which UI implementations may change to + * reflect how the UI displays bound properties of JTextComponent subclasses. + */ + public void propertyChange(PropertyChangeEvent evt) + { + super.propertyChange(evt); + } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalToolBarUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalToolBarUI.java index c5ca913..16e22ac 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalToolBarUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalToolBarUI.java @@ -38,12 +38,16 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.awt.Point; import java.awt.event.ContainerListener; +import java.awt.event.MouseEvent; + import java.beans.PropertyChangeListener; import javax.swing.JComponent; import javax.swing.JToolBar; import javax.swing.border.Border; +import javax.swing.event.MouseInputListener; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicToolBarUI; @@ -158,5 +162,67 @@ public class MetalToolBarUI extends BasicToolBarUI { return MetalBorders.getToolbarButtonBorder(); } - + + /** + * Sets the offset for the window used for dragging the toolbar. + * It is set as long as the window is not null (it has been installed). + */ + protected void setDragOffset(Point p) + { + if (dragWindow != null) + dragWindow.setOffset(p); + } + + /** + * Creates and returns an instance of MetalDockingListener. + * + * @return an instance of MetalDockingListener. + */ + protected MouseInputListener createDockingListener() + { + return new MetalDockingListener(toolBar); + } + + /** + * This is the MouseHandler class that allows the user to drag the JToolBar + * in and out of the parent and dock it if it can. + */ + protected class MetalDockingListener extends BasicToolBarUI.DockingListener + { + /** + * Creates a new DockingListener object. + * + * @param t The JToolBar this DockingListener is being used for. + */ + public MetalDockingListener(JToolBar t) + { + super(t); + } + + /** + * This method is called when the mouse is pressed in the JToolBar. If the + * press doesn't occur in a place where it causes the JToolBar to be + * dragged, it returns. Otherwise, it starts a drag session. + * + * @param e The MouseEvent. + */ + public void mousePressed(MouseEvent e) + { + super.mousePressed(e); + setDragOffset(new Point(e.getX(), e.getY())); + } + + /** + * This method is called when the mouse is dragged. It delegates the drag + * painting to the dragTo method. + * + * @param e The MouseEvent. + */ + public void mouseDragged(MouseEvent e) + { + // Does not do anything differently than dragging + // BasicToolBarUI.DockingListener + super.mouseDragged(e); + } + } } diff --git a/libjava/classpath/javax/swing/plaf/metal/OceanTheme.java b/libjava/classpath/javax/swing/plaf/metal/OceanTheme.java index f1886b1..d1fc4cf 100644 --- a/libjava/classpath/javax/swing/plaf/metal/OceanTheme.java +++ b/libjava/classpath/javax/swing/plaf/metal/OceanTheme.java @@ -238,6 +238,10 @@ public class OceanTheme extends DefaultMetalTheme {new Double(0.3), new Double(0.0), new ColorUIResource(221, 232, 243), new ColorUIResource(Color.WHITE), new ColorUIResource(184, 207, 229)})); + defaults.put("Button.rollover", Boolean.TRUE); + + defaults.put("TabbedPane.selected", new ColorUIResource(200, 221, 242)); + defaults.put("TabbedPane.unselectedBackground", SECONDARY3); } } diff --git a/libjava/classpath/javax/swing/table/DefaultTableCellRenderer.java b/libjava/classpath/javax/swing/table/DefaultTableCellRenderer.java index a187d74..233528c 100644 --- a/libjava/classpath/javax/swing/table/DefaultTableCellRenderer.java +++ b/libjava/classpath/javax/swing/table/DefaultTableCellRenderer.java @@ -181,7 +181,6 @@ public class DefaultTableCellRenderer extends JLabel else setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); - setEnabled(table.isEnabled()); setFont(table.getFont()); // If the current background is equal to the table's background, then we diff --git a/libjava/classpath/javax/swing/text/AbstractDocument.java b/libjava/classpath/javax/swing/text/AbstractDocument.java index a324425..c735388 100644 --- a/libjava/classpath/javax/swing/text/AbstractDocument.java +++ b/libjava/classpath/javax/swing/text/AbstractDocument.java @@ -541,6 +541,9 @@ public abstract class AbstractDocument implements Document, Serializable writeLock(); UndoableEdit undo = content.insertString(offset, text); + if (undo != null) + event.addEdit(undo); + insertUpdate(event, attributes); writeUnlock(); @@ -1326,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; } /** @@ -1809,6 +1819,12 @@ public abstract class AbstractDocument implements Document, Serializable Hashtable changes; /** + * Indicates if this event has been modified or not. This is used to + * determine if this event is thrown. + */ + boolean modified; + + /** * Creates a new <code>DefaultDocumentEvent</code>. * * @param offset the starting offset of the change @@ -1822,6 +1838,7 @@ public abstract class AbstractDocument implements Document, Serializable this.length = length; this.type = type; changes = new Hashtable(); + modified = false; } /** @@ -1836,6 +1853,7 @@ public abstract class AbstractDocument implements Document, Serializable // XXX - Fully qualify ElementChange to work around gcj bug #2499. if (edit instanceof DocumentEvent.ElementChange) { + modified = true; DocumentEvent.ElementChange elEdit = (DocumentEvent.ElementChange) edit; changes.put(elEdit.getElement(), elEdit); @@ -1896,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/libjava/classpath/javax/swing/text/ComponentView.java b/libjava/classpath/javax/swing/text/ComponentView.java index 830dda3..2846f8b 100644 --- a/libjava/classpath/javax/swing/text/ComponentView.java +++ b/libjava/classpath/javax/swing/text/ComponentView.java @@ -264,34 +264,4 @@ public class ComponentView extends View Element el = getElement(); return el.getStartOffset(); } - - /** - * Returns the document position that is (visually) nearest to the given - * document position <code>pos</code> in the given direction <code>d</code>. - * - * @param c the text component - * @param pos the document position - * @param b the bias for <code>pos</code> - * @param d the direction, must be either {@link SwingConstants#NORTH}, - * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or - * {@link SwingConstants#EAST} - * @param biasRet an array of {@link Position.Bias} that can hold at least - * one element, which is filled with the bias of the return position - * on method exit - * - * @return the document position that is (visually) nearest to the given - * document position <code>pos</code> in the given direction - * <code>d</code> - * - * @throws BadLocationException if <code>pos</code> is not a valid offset in - * the document model - */ - public int getNextVisualPositionFrom(JTextComponent c, int pos, - Position.Bias b, int d, - Position.Bias[] biasRet) - throws BadLocationException - { - // FIXME: Implement this method. - throw new AssertionError("Not yet implemented"); - } } diff --git a/libjava/classpath/javax/swing/text/CompositeView.java b/libjava/classpath/javax/swing/text/CompositeView.java index e6c2e4c..cd66452 100644 --- a/libjava/classpath/javax/swing/text/CompositeView.java +++ b/libjava/classpath/javax/swing/text/CompositeView.java @@ -661,34 +661,4 @@ public abstract class CompositeView { return false; } - - /** - * Returns the document position that is (visually) nearest to the given - * document position <code>pos</code> in the given direction <code>d</code>. - * - * @param c the text component - * @param pos the document position - * @param b the bias for <code>pos</code> - * @param d the direction, must be either {@link SwingConstants#NORTH}, - * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or - * {@link SwingConstants#EAST} - * @param biasRet an array of {@link Position.Bias} that can hold at least - * one element, which is filled with the bias of the return position - * on method exit - * - * @return the document position that is (visually) nearest to the given - * document position <code>pos</code> in the given direction - * <code>d</code> - * - * @throws BadLocationException if <code>pos</code> is not a valid offset in - * the document model - */ - public int getNextVisualPositionFrom(JTextComponent c, int pos, - Position.Bias b, int d, - Position.Bias[] biasRet) - throws BadLocationException - { - // TODO: Implement this properly. - throw new AssertionError("Not implemented yet."); - } } diff --git a/libjava/classpath/javax/swing/text/DefaultCaret.java b/libjava/classpath/javax/swing/text/DefaultCaret.java index 3ebeceb..776ef69 100644 --- a/libjava/classpath/javax/swing/text/DefaultCaret.java +++ b/libjava/classpath/javax/swing/text/DefaultCaret.java @@ -1,5 +1,5 @@ /* DefaultCaret.java -- - Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -430,18 +430,45 @@ public class DefaultCaret extends Rectangle */ public void focusGained(FocusEvent event) { - setVisible(true); + setVisible(true); + updateTimerStatus(); } /** * Sets the caret to <code>invisible</code>. - * + * * @param event the <code>FocusEvent</code> */ public void focusLost(FocusEvent event) { if (event.isTemporary() == false) - setVisible(false); + { + setVisible(false); + // Stop the blinker, if running. + if (blinkTimer != null && blinkTimer.isRunning()) + blinkTimer.stop(); + } + } + + /** + * Install (if not present) and start the timer, if the caret must blink. The + * caret does not blink if it is invisible, or the component is disabled or + * not editable. + */ + private void updateTimerStatus() + { + if (textComponent.isEnabled() && textComponent.isEditable()) + { + if (blinkTimer == null) + initBlinkTimer(); + if (!blinkTimer.isRunning()) + blinkTimer.start(); + } + else + { + if (blinkTimer != null) + blinkTimer.stop(); + } } /** @@ -641,8 +668,10 @@ public class DefaultCaret extends Rectangle } catch (BadLocationException e) { - assert false : "Unexpected bad caret location: " + dot; - return; + AssertionError ae; + ae = new AssertionError("Unexpected bad caret location: " + dot); + ae.initCause(e); + throw ae; } if (rect == null) @@ -802,9 +831,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(); @@ -833,13 +865,17 @@ public class DefaultCaret extends Rectangle visible = true; Rectangle area = null; + int dot = getDot(); try { - area = getComponent().modelToView(getDot()); + area = getComponent().modelToView(dot); } - catch (BadLocationException ex) + catch (BadLocationException e) { - assert false : "Unexpected bad caret location: " + getDot(); + AssertionError ae; + ae = new AssertionError("Unexpected bad caret location: " + dot); + ae.initCause(e); + throw ae; } if (area != null) damage(area); @@ -870,26 +906,19 @@ public class DefaultCaret extends Rectangle if (v != visible) { visible = v; - if (visible) - if (textComponent.isEnabled() && textComponent.isEditable()) - { - if (blinkTimer == null) - initBlinkTimer(); - blinkTimer.start(); - } - else - { - if (blinkTimer != null) - blinkTimer.stop(); - } + updateTimerStatus(); Rectangle area = null; + int dot = getDot(); try { - area = getComponent().modelToView(getDot()); + area = getComponent().modelToView(dot); } - catch (BadLocationException ex) + catch (BadLocationException e) { - assert false: "Unexpected bad caret location: " + getDot(); + AssertionError ae; + ae = new AssertionError("Unexpected bad caret location: " + dot); + ae.initCause(e); + throw ae; } if (area != null) damage(area); diff --git a/libjava/classpath/javax/swing/text/DefaultEditorKit.java b/libjava/classpath/javax/swing/text/DefaultEditorKit.java index 3b3fc1f..88094b8 100644 --- a/libjava/classpath/javax/swing/text/DefaultEditorKit.java +++ b/libjava/classpath/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; @@ -112,8 +114,7 @@ public class DefaultEditorKit extends EditorKit */ public void actionPerformed(ActionEvent event) { - // FIXME: Implement me. Tookit.getSystemClipboard should be used - // for that. + getTextComponent(event).copy(); } } @@ -144,8 +145,7 @@ public class DefaultEditorKit extends EditorKit */ public void actionPerformed(ActionEvent event) { - // FIXME: Implement me. Tookit.getSystemClipboard should be used - // for that. + getTextComponent(event).cut(); } } @@ -174,8 +174,7 @@ public class DefaultEditorKit extends EditorKit */ public void actionPerformed(ActionEvent event) { - // FIXME: Implement me. Tookit.getSystemClipboard should be used - // for that. + getTextComponent(event).paste(); } } @@ -216,19 +215,9 @@ public class DefaultEditorKit extends EditorKit return; JTextComponent t = getTextComponent(event); - if (t != null) - { - try - { - t.getDocument().insertString(t.getCaret().getDot(), - event.getActionCommand(), null); - } - catch (BadLocationException be) - { - // FIXME: we're not authorized to throw this.. swallow it? - } - } - } + if (t != null && t.isEnabled() && t.isEditable()) + t.replaceSelection(event.getActionCommand()); + } } /** @@ -309,7 +298,8 @@ public class DefaultEditorKit extends EditorKit */ public void actionPerformed(ActionEvent event) { - // FIXME: Implement this. + JTextComponent t = getTextComponent(event); + t.replaceSelection("\t"); } } @@ -710,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) @@ -911,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/libjava/classpath/javax/swing/text/DefaultFormatter.java b/libjava/classpath/javax/swing/text/DefaultFormatter.java index f9e0f10..493699d 100644 --- a/libjava/classpath/javax/swing/text/DefaultFormatter.java +++ b/libjava/classpath/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/libjava/classpath/javax/swing/text/DefaultFormatterFactory.java b/libjava/classpath/javax/swing/text/DefaultFormatterFactory.java new file mode 100644 index 0000000..84a1676 --- /dev/null +++ b/libjava/classpath/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/libjava/classpath/javax/swing/text/DefaultStyledDocument.java b/libjava/classpath/javax/swing/text/DefaultStyledDocument.java index eb56bb0..46b8225 100644 --- a/libjava/classpath/javax/swing/text/DefaultStyledDocument.java +++ b/libjava/classpath/javax/swing/text/DefaultStyledDocument.java @@ -42,11 +42,13 @@ import java.awt.Color; import java.awt.Font; import java.io.Serializable; import java.util.Enumeration; +import java.util.Stack; 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; @@ -365,7 +367,6 @@ public class DefaultStyledDocument extends AbstractDocument public String toString() { StringBuilder b = new StringBuilder(); - b.append('<'); switch (type) { case StartTagType: @@ -427,6 +428,29 @@ 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 + * incremented when an end tag is inserted. This is evaluated before + * content insertion to go up the element stack. + */ + private int numEndTags; + + /** + * The number of inserted start tags. This is a counter which always gets + * incremented when an end tag is inserted. This is evaluated before + * content insertion to go up the element stack. + */ + private int numStartTags; + + /** + * The current position in the element tree. This is used for bulk inserts + * using ElementSpecs. + */ + private Stack elementStack; /** * Holds fractured elements during insertion of end and start tags. @@ -450,6 +474,7 @@ public class DefaultStyledDocument extends AbstractDocument public ElementBuffer(Element root) { this.root = root; + elementStack = new Stack(); } /** @@ -463,6 +488,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. @@ -493,11 +597,50 @@ public class DefaultStyledDocument extends AbstractDocument { // Split up the element at the start offset if necessary. Element el = getCharacterElement(offset); - split(el, offset); + Element[] res = split(el, offset, 0); + BranchElement par = (BranchElement) el.getParentElement(); + if (res[1] != null) + { + int index = par.getElementIndex(offset); + Element[] removed; + Element[] added; + if (res[0] == null) + { + removed = new Element[0]; + added = new Element[]{ res[1] }; + index++; + } + else + { + removed = new Element[]{ el }; + added = new Element[]{ res[0], res[1] }; + } + par.replace(index, removed.length, added); + addEdit(par, index, removed, added); + } int endOffset = offset + length; el = getCharacterElement(endOffset); - split(el, endOffset); + res = split(el, endOffset, 0); + par = (BranchElement) el.getParentElement(); + if (res[1] != null) + { + int index = par.getElementIndex(offset); + Element[] removed; + Element[] added; + if (res[1] == null) + { + removed = new Element[0]; + added = new Element[]{ res[1] }; + } + else + { + removed = new Element[]{ el }; + added = new Element[]{ res[0], res[1] }; + } + par.replace(index, removed.length, added); + addEdit(par, index, removed, added); + } } /** @@ -505,42 +648,96 @@ public class DefaultStyledDocument extends AbstractDocument * * @param el the Element to possibly split * @param offset the offset at which to possibly split + * @param space the amount of space to create between the splitted parts + * + * @return An array of elements which represent the split result. This + * array has two elements, the two parts of the split. The first + * element might be null, which means that the element which should + * be splitted can remain in place. The second element might also + * be null, which means that the offset is already at an element + * boundary and the element doesn't need to be splitted. + * */ - void split(Element el, int offset) + private Element[] split(Element el, int offset, int space) { - if (el instanceof AbstractElement) + // If we are at an element boundary, then return an empty array. + if ((offset == el.getStartOffset() || offset == el.getEndOffset()) + && space == 0 && el.isLeaf()) + return new Element[2]; + + // If the element is an instance of BranchElement, then we recursivly + // call this method to perform the split. + Element[] res = new Element[2]; + if (el instanceof BranchElement) { - AbstractElement ael = (AbstractElement) el; - int startOffset = ael.getStartOffset(); - int endOffset = ael.getEndOffset(); - int len = endOffset - startOffset; - if (startOffset != offset && endOffset != offset) + int index = el.getElementIndex(offset); + Element child = el.getElement(index); + Element[] result = split(child, offset, space); + Element[] removed; + Element[] added; + Element[] newAdded; + + int count = el.getElementCount(); + if (!(result[1] == null)) { - Element paragraph = ael.getParentElement(); - if (paragraph instanceof BranchElement) + // This is the case when we can keep the first element. + if (result[0] == null) { - BranchElement par = (BranchElement) paragraph; - Element child1 = createLeafElement(par, ael, startOffset, - offset); - Element child2 = createLeafElement(par, ael, offset, - endOffset); - int index = par.getElementIndex(startOffset); - Element[] add = new Element[]{ child1, child2 }; - par.replace(index, 1, add); - documentEvent.addEdit(new ElementEdit(par, index, - new Element[]{ el }, - add)); + removed = new Element[count - index - 1]; + newAdded = new Element[count - index - 1]; + added = new Element[]{}; } + // This is the case when we may not keep the first element. else - throw new AssertionError("paragraph elements are expected to " - + "be instances of " - + "javax.swing.text.AbstractDocument.BranchElement"); + { + removed = new Element[count - index]; + newAdded = new Element[count - index]; + added = new Element[]{result[0]}; + } + newAdded[0] = result[1]; + for (int i = index; i < count; i++) + { + Element el2 = el.getElement(i); + int ind = i - count + removed.length; + removed[ind] = el2; + if (ind != 0) + newAdded[ind] = el2; + } + + ((BranchElement) el).replace(index, removed.length, added); + addEdit(el, index, removed, added); + BranchElement newPar = + (BranchElement) createBranchElement(el.getParentElement(), + el.getAttributes()); + newPar.replace(0, 0, newAdded); + res = new Element[]{ null, newPar }; + } + else + { + removed = new Element[count - index]; + for (int i = index; i < count; ++i) + removed[i - index] = el.getElement(i); + added = new Element[0]; + ((BranchElement) el).replace(index, removed.length, + added); + addEdit(el, index, removed, added); + BranchElement newPar = + (BranchElement) createBranchElement(el.getParentElement(), + el.getAttributes()); + newPar.replace(0, 0, removed); + res = new Element[]{ null, newPar }; } } - else - throw new AssertionError("content elements are expected to be " - + "instances of " - + "javax.swing.text.AbstractDocument.AbstractElement"); + else if (el instanceof LeafElement) + { + BranchElement par = (BranchElement) el.getParentElement(); + Element el1 = createLeafElement(par, el.getAttributes(), + el.getStartOffset(), offset); + Element el2 = createLeafElement(par, el.getAttributes(), + offset + space, el.getEndOffset()); + res = new Element[]{ el1, el2 }; + } + return res; } /** @@ -560,9 +757,18 @@ 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(); + elementStack.push(root); + elementStack.push(root.getElement(root.getElementIndex(offset))); + numEndTags = 0; + numStartTags = 0; insertUpdate(data); } @@ -573,202 +779,348 @@ public class DefaultStyledDocument extends AbstractDocument * {@link #insert}. * * @param data the element specifications for the elements to be inserte - */ + */ protected void insertUpdate(ElementSpec[] data) { + if (data[0].getType() == ElementSpec.EndTagType) + { + // fracture deepest child here + BranchElement paragraph = (BranchElement) elementStack.peek(); + Element curr = paragraph.getParentElement(); + int index = curr.getElementIndex(offset); + while (!curr.isLeaf()) + { + index = curr.getElementIndex(offset); + curr = curr.getElement(index); + } + Element parent = curr.getParentElement(); + Element newEl1 = createLeafElement(parent, + curr.getAttributes(), + curr.getStartOffset(), offset); + Element grandParent = parent.getParentElement(); + BranchElement nextBranch = + (BranchElement) grandParent.getElement + (grandParent.getElementIndex(parent.getEndOffset())); + Element firstLeaf = nextBranch.getElement(0); + while (!firstLeaf.isLeaf()) + { + firstLeaf = firstLeaf.getElement(0); + } + BranchElement parent2 = (BranchElement) firstLeaf.getParentElement(); + Element newEl2 = + createLeafElement(parent2, + firstLeaf.getAttributes(), + offset, firstLeaf.getEndOffset()); + parent2.replace(0, 1, new Element[] { newEl2 }); + + + ((BranchElement) parent). + replace(index, 1, new Element[] { newEl1 }); + } + for (int i = 0; i < data.length; i++) { + BranchElement paragraph = (BranchElement) elementStack.peek(); switch (data[i].getType()) { case ElementSpec.StartTagType: - insertStartTag(data[i]); + switch (data[i].getDirection()) + { + case ElementSpec.JoinFractureDirection: + insertFracture(data[i]); + break; + case ElementSpec.JoinNextDirection: + int index = paragraph.getElementIndex(offset); + elementStack.push(paragraph.getElement(index)); + break; + case ElementSpec.OriginateDirection: + Element current = (Element) elementStack.peek(); + Element newParagraph = + insertParagraph((BranchElement) current, offset); + elementStack.push(newParagraph); + break; + default: + break; + } break; case ElementSpec.EndTagType: - insertEndTag(data[i]); + elementStack.pop(); break; - default: + case ElementSpec.ContentType: insertContentTag(data[i]); break; } } + endEdit(); } /** - * Insert a new paragraph after the paragraph at the current position. - * - * @param tag the element spec that describes the element to be inserted + * Finishes an insertion by possibly evaluating the outstanding start and + * end tags. However, this is only performed if the event has received any + * modifications. + */ + private void endEdit() + { + if (documentEvent.modified) + prepareContentInsertion(); + } + + /** + * Evaluates the number of inserted end tags and performs the corresponding + * structural changes. */ - void insertStartTag(ElementSpec tag) + private void prepareContentInsertion() { - BranchElement root = (BranchElement) getDefaultRootElement(); - int index = root.getElementIndex(offset); - if (index == -1) - index = 0; - - BranchElement newParagraph = - (BranchElement) createBranchElement(root, tag.getAttributes()); - newParagraph.setResolveParent(getStyle(StyleContext.DEFAULT_STYLE)); - - // Add new paragraph into document structure. - Element[] added = new Element[]{newParagraph}; - root.replace(index + 1, 0, added); - ElementEdit edit = new ElementEdit(root, index + 1, new Element[0], - added); - documentEvent.addEdit(edit); - - // Maybe add fractured elements. - if (tag.getDirection() == ElementSpec.JoinFractureDirection) + while (numEndTags > 0) + { + elementStack.pop(); + numEndTags--; + } + + while (numStartTags > 0) { - Element[] newFracture = new Element[fracture.length]; - for (int i = 0; i < fracture.length; i++) + Element current = (Element) elementStack.peek(); + Element newParagraph = + insertParagraph((BranchElement) current, offset); + elementStack.push(newParagraph); + numStartTags--; + } + } + + private Element insertParagraph(BranchElement par, int offset) + { + Element current = par.getElement(par.getElementIndex(offset)); + Element[] res = split(current, offset, 0); + int index = par.getElementIndex(offset); + Element ret; + if (res[1] != null) + { + Element[] removed; + Element[] added; + if (res[0] == null) { - Element oldLeaf = fracture[i]; - Element newLeaf = createLeafElement(newParagraph, - oldLeaf.getAttributes(), - oldLeaf.getStartOffset(), - oldLeaf.getEndOffset()); - newFracture[i] = newLeaf; + removed = new Element[0]; + if (res[1] instanceof BranchElement) + { + added = new Element[]{ res[1] }; + ret = res[1]; + } + else + { + ret = createBranchElement(par, null); + added = new Element[]{ ret, res[1] }; + } + index++; + } + else + { + removed = new Element[]{ current }; + if (res[1] instanceof BranchElement) + { + ret = res[1]; + added = new Element[]{ res[0], res[1] }; + } + else + { + ret = createBranchElement(par, null); + added = new Element[]{ res[0], ret, res[1] }; + } } - newParagraph.replace(0, 0, newFracture); - edit = new ElementEdit(newParagraph, 0, new Element[0], - fracture); - documentEvent.addEdit(edit); - fracture = new Element[0]; + par.replace(index, removed.length, added); + addEdit(par, index, removed, added); } + else + { + ret = createBranchElement(par, null); + Element[] added = new Element[]{ ret }; + par.replace(index, 0, added); + addEdit(par, index, new Element[0], added); + } + return ret; } - + /** - * Inserts an end tag into the document structure. This cuts of the - * current paragraph element, possibly fracturing it's child elements. - * The fractured elements are saved so that they can be joined later - * with a new paragraph element. + * Inserts a fracture into the document structure. + * + * @param tag - the element spec. */ - void insertEndTag(ElementSpec tag) + private void insertFracture(ElementSpec tag) { - BranchElement root = (BranchElement) getDefaultRootElement(); - int parIndex = root.getElementIndex(offset); - BranchElement paragraph = (BranchElement) root.getElement(parIndex); - - int index = paragraph.getElementIndex(offset); - LeafElement content = (LeafElement) paragraph.getElement(index); - // We might have to split the element at offset. - split(content, offset); - index = paragraph.getElementIndex(offset); - - int count = paragraph.getElementCount(); - // Store fractured elements. - fracture = new Element[count - index]; - for (int i = index; i < count; ++i) - fracture[i - index] = paragraph.getElement(i); - - // Delete fractured elements. - paragraph.replace(index, count - index, new Element[0]); - - // Add this action to the document event. - ElementEdit edit = new ElementEdit(paragraph, index, fracture, - new Element[0]); - documentEvent.addEdit(edit); + // This is the parent of the paragraph about to be fractured. We will + // create a new child of this parent. + BranchElement parent = (BranchElement) elementStack.peek(); + int parentIndex = parent.getElementIndex(offset); + + // This is the old paragraph. We must remove all its children that + // occur after offset and move them to a new paragraph. We must + // also recreate its child that occurs at offset to have the proper + // end offset. The remainder of this child will also go in the new + // paragraph. + BranchElement previous = (BranchElement) parent.getElement(parentIndex); + + // This is the new paragraph. + BranchElement newBranch = + (BranchElement) createBranchElement(parent, previous.getAttributes()); + + + // The steps we must take to properly fracture are: + // 1. Recreate the LeafElement at offset to have the correct end offset. + // 2. Create a new LeafElement with the remainder of the LeafElement in + // #1 ==> this is whatever was in that LeafElement to the right of the + // inserted newline. + // 3. Find the paragraph at offset and remove all its children that + // occur _after_ offset. These will be moved to the newly created + // paragraph. + // 4. Move the LeafElement created in #2 and all the LeafElements removed + // in #3 to the newly created paragraph. + // 5. Add the new paragraph to the parent. + int previousIndex = previous.getElementIndex(offset); + int numReplaced = previous.getElementCount() - previousIndex; + Element previousLeaf = previous.getElement(previousIndex); + AttributeSet prevLeafAtts = previous.getAttributes(); + + // This recreates the child at offset to have the proper end offset. + // (Step 1). + Element newPreviousLeaf = + createLeafElement(previous, + prevLeafAtts, previousLeaf.getStartOffset(), + offset); + // This creates the new child, which is the remainder of the old child. + // (Step 2). + + Element firstLeafInNewBranch = + createLeafElement(newBranch, prevLeafAtts, + offset, previousLeaf.getEndOffset()); + + // Now we move the new LeafElement and all the old children that occurred + // after the offset to the new paragraph. (Step 4). + Element[] newLeaves = new Element[numReplaced]; + newLeaves[0] = firstLeafInNewBranch; + for (int i = 1; i < numReplaced; i++) + newLeaves[i] = previous.getElement(previousIndex + i); + newBranch.replace(0, 0, newLeaves); + addEdit(newBranch, 0, null, newLeaves); + + // Now we remove the children after the offset from the previous + // paragraph. (Step 3). + int removeSize = previous.getElementCount() - previousIndex; + Element[] add = new Element[] { newPreviousLeaf }; + Element[] remove = new Element[removeSize]; + for (int j = 0; j < removeSize; j++) + remove[j] = previous.getElement(previousIndex + j); + previous.replace(previousIndex, removeSize, add); + addEdit(previous, previousIndex, remove, add); + + // Finally we add the new paragraph to the parent. (Step 5). + Element[] nb = new Element[] { newBranch }; + int index = parentIndex + 1; + parent.replace(index, 0, nb); + addEdit(parent, index, null, nb); } - + /** * Inserts a content element into the document structure. - * + * * @param tag the element spec */ - void insertContentTag(ElementSpec tag) + private void insertContentTag(ElementSpec tag) { + prepareContentInsertion(); int len = tag.getLength(); int dir = tag.getDirection(); + AttributeSet tagAtts = tag.getAttributes(); if (dir == ElementSpec.JoinPreviousDirection) { - Element prev = getCharacterElement(offset); - BranchElement prevParent = (BranchElement) prev.getParentElement(); - Element join = createLeafElement(prevParent, tag.getAttributes(), - prev.getStartOffset(), - Math.max(prev.getEndOffset(), - offset + len)); - int ind = prevParent.getElementIndex(offset); - if (ind == -1) - ind = 0; - Element[] add = new Element[]{join}; - prevParent.replace(ind, 1, add); - - // Add this action to the document event. - ElementEdit edit = new ElementEdit(prevParent, ind, - new Element[]{prev}, add); - documentEvent.addEdit(edit); + // The mauve tests to this class show that a JoinPrevious insertion + // does not add any edits to the document event. To me this means + // that nothing is done here. The previous element naturally should + // expand so that it covers the new characters. } else if (dir == ElementSpec.JoinNextDirection) { - Element next = getCharacterElement(offset + len); - BranchElement nextParent = (BranchElement) next.getParentElement(); - Element join = createLeafElement(nextParent, tag.getAttributes(), - offset, - next.getEndOffset()); - int ind = nextParent.getElementIndex(offset + len); - if (ind == -1) - ind = 0; - Element[] add = new Element[]{join}; - nextParent.replace(ind, 1, add); - - // Add this action to the document event. - ElementEdit edit = new ElementEdit(nextParent, ind, - new Element[]{next}, add); - documentEvent.addEdit(edit); + // FIXME: + // Have to handle JoinNext differently depending on whether + // or not it comes after a fracture. If comes after a fracture, + // the insertFracture method takes care of everything and nothing + // needs to be done here. Otherwise, we need to adjust the + // Element structure. For now, I check if the elementStack's + // top Element is the immediate parent of the LeafElement at + // offset - if so, we did not come immediately after a + // fracture. This seems awkward and should probably be improved. + // We may be doing too much in insertFracture because we are + // adjusting the offsets, the correct thing to do may be to + // create a new branch element and push it on to element stack + // and then this method here can be more general. + + BranchElement paragraph = (BranchElement) elementStack.peek(); + int index = paragraph.getElementIndex(offset); + Element target = paragraph.getElement(index); + if (target.isLeaf() && paragraph.getElementCount() > (index + 1)) + { + Element next = paragraph.getElement(index + 1); + Element newEl1 = createLeafElement(paragraph, + target.getAttributes(), + target.getStartOffset(), + offset); + Element newEl2 = createLeafElement(paragraph, + next.getAttributes(), offset, + next.getEndOffset()); + Element[] add = new Element[] { newEl1, newEl2 }; + paragraph.replace (index, 2, add); + addEdit(paragraph, index, new Element[] { target, next }, add); + } } - else + else if (dir == ElementSpec.OriginateDirection) { - BranchElement par = (BranchElement) getParagraphElement(offset); - - int ind = par.getElementIndex(offset); - - // Make room for the element. - // Cut previous element. - Element prev = par.getElement(ind); - if (prev != null && prev.getStartOffset() < offset) + BranchElement paragraph = (BranchElement) elementStack.peek(); + int index = paragraph.getElementIndex(offset); + Element current = paragraph.getElement(index); + + Element[] added; + Element[] removed = new Element[] {current}; + Element[] splitRes = split(current, offset, length); + if (splitRes[0] == null) { - Element cutPrev = createLeafElement(par, prev.getAttributes(), - prev.getStartOffset(), - offset); - Element[] remove = new Element[]{prev}; - Element[] add = new Element[]{cutPrev}; - if (prev.getEndOffset() > offset + len) - { - Element rem = createLeafElement(par, prev.getAttributes(), - offset + len, - prev.getEndOffset()); - add = new Element[]{cutPrev, rem}; - } - - par.replace(ind, 1, add); - documentEvent.addEdit(new ElementEdit(par, ind, remove, add)); - ind++; + added = new Element[2]; + added[0] = createLeafElement(paragraph, tagAtts, + offset, endOffset); + added[1] = splitRes[1]; + removed = new Element[0]; + index++; } - // ind now points to the next element. - - // Cut next element if necessary. - Element next = par.getElement(ind); - if (next != null && next.getStartOffset() < offset + len) + 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, tagAtts, offset, + endOffset); + added[1] = splitRes[1]; + } + else if (current.getEndOffset() == endOffset) { - Element cutNext = createLeafElement(par, next.getAttributes(), - offset + len, - next.getEndOffset()); - Element[] remove = new Element[]{next}; - Element[] add = new Element[]{cutNext}; - par.replace(ind, 1, add); - documentEvent.addEdit(new ElementEdit(par, ind, remove, - add)); + // 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, tagAtts, offset, + endOffset); } - - // Insert new element. - Element newEl = createLeafElement(par, tag.getAttributes(), - offset, offset + len); - Element[] added = new Element[]{newEl}; - par.replace(ind, 0, added); - // Add this action to the document event. - ElementEdit edit = new ElementEdit(par, ind, new Element[0], - added); - documentEvent.addEdit(edit); + 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, tagAtts, offset, + endOffset); + added[2] = splitRes[1]; + } + paragraph.replace(index, removed.length, added); + addEdit(paragraph, index, removed, added); } offset += len; } @@ -800,6 +1152,79 @@ public class DefaultStyledDocument extends AbstractDocument result.replace(0, 0, children); return result; } + + /** + * Adds an ElementChange for a given element modification to the document + * event. If there already is an ElementChange registered for this element, + * this method tries to merge the ElementChanges together. However, this + * is only possible if the indices of the new and old ElementChange are + * equal. + * + * @param e the element + * @param i the index of the change + * @param removed the removed elements, or <code>null</code> + * @param added the added elements, or <code>null</code> + */ + private void addEdit(Element e, int i, Element[] removed, Element[] added) + { + // Perform sanity check first. + DocumentEvent.ElementChange ec = documentEvent.getChange(e); + + // Merge the existing stuff with the new stuff. + Element[] oldAdded = ec == null ? null: ec.getChildrenAdded(); + Element[] newAdded; + if (oldAdded != null && added != null) + { + if (ec.getIndex() <= i) + { + int index = i - ec.getIndex(); + // Merge adds together. + newAdded = new Element[oldAdded.length + added.length]; + System.arraycopy(oldAdded, 0, newAdded, 0, index); + System.arraycopy(added, 0, newAdded, index, added.length); + System.arraycopy(oldAdded, index, newAdded, index + added.length, + oldAdded.length - index); + i = ec.getIndex(); + } + else + throw new AssertionError("Not yet implemented case."); + } + else if (added != null) + newAdded = added; + else if (oldAdded != null) + newAdded = oldAdded; + else + newAdded = new Element[0]; + + Element[] oldRemoved = ec == null ? null: ec.getChildrenRemoved(); + Element[] newRemoved; + if (oldRemoved != null && removed != null) + { + if (ec.getIndex() <= i) + { + int index = i - ec.getIndex(); + // Merge removes together. + newRemoved = new Element[oldRemoved.length + removed.length]; + System.arraycopy(oldAdded, 0, newRemoved, 0, index); + System.arraycopy(removed, 0, newRemoved, index, removed.length); + System.arraycopy(oldRemoved, index, newRemoved, + index + removed.length, + oldRemoved.length - index); + i = ec.getIndex(); + } + else + throw new AssertionError("Not yet implemented case."); + } + else if (removed != null) + newRemoved = removed; + else if (oldRemoved != null) + newRemoved = oldRemoved; + else + newRemoved = new Element[0]; + + // Replace the existing edit for the element with the merged. + documentEvent.addEdit(new ElementEdit(e, i, newRemoved, newAdded)); + } } /** @@ -824,7 +1249,7 @@ public class DefaultStyledDocument extends AbstractDocument */ public String getName() { - return "section"; + return SectionElementName; } } @@ -945,9 +1370,7 @@ public class DefaultStyledDocument extends AbstractDocument // Use createBranchElement() and createLeafElement instead. SectionElement section = new SectionElement(); - BranchElement paragraph = - (BranchElement) createBranchElement(section, null); - paragraph.setResolveParent(getStyle(StyleContext.DEFAULT_STYLE)); + BranchElement paragraph = new BranchElement(section, null); tmp = new Element[1]; tmp[0] = paragraph; section.replace(0, 0, tmp); @@ -1043,7 +1466,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; } /** @@ -1112,50 +1539,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); } /** @@ -1167,14 +1598,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(); + } } /** @@ -1190,15 +1643,47 @@ public class DefaultStyledDocument extends AbstractDocument AttributeSet attributes, boolean replace) { - int index = offset; - while (index < offset + length) + try + { + // 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 { - AbstractElement par = (AbstractElement) getParagraphElement(index); - AttributeContext ctx = getAttributeContext(); - if (replace) - par.removeAttributes(par); - par.addAttributes(attributes); - index = par.getElementCount(); + writeUnlock(); } } @@ -1212,9 +1697,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 { @@ -1229,66 +1719,141 @@ 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) + { + if (prevCharWasNewline) + finalStartTag.setDirection(finalStartDirection); + else if (prevParagraph.getEndOffset() != endOffset) + { + try + { + String last = getText(endOffset - 1, 1); + if (!last.equals("\n")) + finalStartTag.setDirection(ElementSpec.JoinFractureDirection); + } + catch (BadLocationException ble) + { + // This shouldn't happen. + AssertionError ae = new AssertionError(); + ae.initCause(ble); + throw ae; + } + } + else + { + // 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); + } + } + + // If we are at the last index, then check if we could probably be + // joined with the next element. + // This means: + // - we must be a ContentTag + // - if there is a next Element, we must have the same attributes + // - if there is no next Element, but one will be created, + // we must have the same attributes as the higher-level run. + ElementSpec last = (ElementSpec) specs.lastElement(); + if (last.getType() == ElementSpec.ContentType) { - 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) + Element currentRun = + prevParagraph.getElement(prevParagraph.getElementIndex(offset)); + if (currentRun.getEndOffset() == endOffset) { - if (prev.getAttributes().isEqual(attr)) - spec.setDirection(ElementSpec.JoinPreviousDirection); + if (endOffset < getLength() && next.getAttributes().isEqual(attr) + && last.getType() == ElementSpec.ContentType) + last.setDirection(ElementSpec.JoinNextDirection); + } + else + { + if (finalStartTag != null + && finalStartTag.getDirection() == + ElementSpec.JoinFractureDirection + && currentRun.getAttributes().isEqual(attr)) + { + last.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 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()]); @@ -1296,6 +1861,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 @@ -1316,6 +1922,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. * @@ -1325,36 +1960,53 @@ 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++) + if (data == null || data.length == 0) + return; + 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); + + // Finally we must update the document structure and fire the insert + // update event. + buffer.insert(offset, length, data, ev); + fireInsertUpdate(ev); + fireUndoableEditUpdate(new UndoableEditEvent(this, 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); - fireInsertUpdate(ev); - writeUnlock(); } /** @@ -1382,4 +2034,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/libjava/classpath/javax/swing/text/FlowView.java b/libjava/classpath/javax/swing/text/FlowView.java index 765f515..6d4b9cd 100644 --- a/libjava/classpath/javax/swing/text/FlowView.java +++ b/libjava/classpath/javax/swing/text/FlowView.java @@ -396,37 +396,6 @@ public abstract class FlowView extends BoxView throw new AssertionError("This method must not be called in " + "LogicalView."); } - - /** - * Returns the document position that is (visually) nearest to the given - * document position <code>pos</code> in the given direction <code>d</code>. - * - * @param c the text component - * @param pos the document position - * @param b the bias for <code>pos</code> - * @param d the direction, must be either {@link SwingConstants#NORTH}, - * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or - * {@link SwingConstants#EAST} - * @param biasRet an array of {@link Position.Bias} that can hold at least - * one element, which is filled with the bias of the return position - * on method exit - * - * @return the document position that is (visually) nearest to the given - * document position <code>pos</code> in the given direction - * <code>d</code> - * - * @throws BadLocationException if <code>pos</code> is not a valid offset in - * the document model - */ - public int getNextVisualPositionFrom(JTextComponent c, int pos, - Position.Bias b, int d, - Position.Bias[] biasRet) - throws BadLocationException - { - assert false : "getNextVisualPositionFrom() must not be called in " - + "LogicalView"; - return 0; - } } /** diff --git a/libjava/classpath/javax/swing/text/GapContent.java b/libjava/classpath/javax/swing/text/GapContent.java index 4c65de0..80dcfa5 100644 --- a/libjava/classpath/javax/swing/text/GapContent.java +++ b/libjava/classpath/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>. */ @@ -100,15 +101,15 @@ public class GapContent public int compareTo(Object o) { if (o instanceof Integer) - { - int otherMark = ((Integer) o).intValue(); - return mark - otherMark; - } + { + int otherMark = ((Integer) o).intValue(); + return mark - otherMark; + } else - { - GapContentPosition other = (GapContentPosition) o; - return mark - other.mark; - } + { + GapContentPosition other = (GapContentPosition) o; + return mark - other.mark; + } } /** @@ -122,7 +123,6 @@ public class GapContent assert mark <= gapStart || mark >= gapEnd : "mark: " + mark + ", gapStart: " + gapStart + ", gapEnd: " + gapEnd; - if (mark <= gapStart) return mark; else @@ -130,11 +130,11 @@ public class GapContent } } - class UndoInsertString extends AbstractUndoableEdit + class InsertUndo extends AbstractUndoableEdit { public int where, length; String text; - public UndoInsertString(int start, int len) + public InsertUndo(int start, int len) { where = start; length = len; @@ -316,7 +316,7 @@ public class GapContent replace(where, 0, str.toCharArray(), strLen); - return new UndoInsertString(where, strLen); + return new InsertUndo(where, strLen); } /** @@ -449,7 +449,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 +584,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. @@ -644,6 +645,13 @@ public class GapContent new GapContentPosition(offset)); if (index1 < 0) index1 = -(index1 + 1); + + // Search the first index with the specified offset. The binarySearch does + // not necessarily find the first one. + while (index1 > 0 + && ((GapContentPosition) positions.get(index1 - 1)).mark == offset) + index1--; + for (ListIterator i = positions.listIterator(index1); i.hasNext();) { GapContentPosition p = (GapContentPosition) i.next(); @@ -672,6 +680,13 @@ public class GapContent new GapContentPosition(offset)); if (index1 < 0) index1 = -(index1 + 1); + + // Search the first index with the specified offset. The binarySearch does + // not necessarily find the first one. + while (index1 > 0 + && ((GapContentPosition) positions.get(index1 - 1)).mark == offset) + index1--; + for (ListIterator i = positions.listIterator(index1); i.hasNext();) { GapContentPosition p = (GapContentPosition) i.next(); @@ -700,6 +715,12 @@ public class GapContent new GapContentPosition(offset)); if (index1 < 0) index1 = -(index1 + 1); + + // Search the first index with the specified offset. The binarySearch does + // not necessarily find the first one. + while (index1 > 0 + && ((GapContentPosition) positions.get(index1 - 1)).mark == offset) + index1--; for (ListIterator i = positions.listIterator(index1); i.hasNext();) { GapContentPosition p = (GapContentPosition) i.next(); diff --git a/libjava/classpath/javax/swing/text/GlyphView.java b/libjava/classpath/javax/swing/text/GlyphView.java index d3dd75e..47deb50 100644 --- a/libjava/classpath/javax/swing/text/GlyphView.java +++ b/libjava/classpath/javax/swing/text/GlyphView.java @@ -1057,34 +1057,4 @@ public class GlyphView extends View implements TabableView, Cloneable return painter.getNextVisualPositionFrom(this, pos, bias, a, direction, biasRet); } - - /** - * Returns the document position that is (visually) nearest to the given - * document position <code>pos</code> in the given direction <code>d</code>. - * - * @param c the text component - * @param pos the document position - * @param b the bias for <code>pos</code> - * @param d the direction, must be either {@link SwingConstants#NORTH}, - * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or - * {@link SwingConstants#EAST} - * @param biasRet an array of {@link Position.Bias} that can hold at least - * one element, which is filled with the bias of the return position - * on method exit - * - * @return the document position that is (visually) nearest to the given - * document position <code>pos</code> in the given direction - * <code>d</code> - * - * @throws BadLocationException if <code>pos</code> is not a valid offset in - * the document model - */ - public int getNextVisualPositionFrom(JTextComponent c, int pos, - Position.Bias b, int d, - Position.Bias[] biasRet) - throws BadLocationException - { - // TODO: Implement this properly. - throw new AssertionError("Not implemented yet."); - } } diff --git a/libjava/classpath/javax/swing/text/IconView.java b/libjava/classpath/javax/swing/text/IconView.java index 86c27dd..af2581a 100644 --- a/libjava/classpath/javax/swing/text/IconView.java +++ b/libjava/classpath/javax/swing/text/IconView.java @@ -158,34 +158,4 @@ public class IconView return el.getStartOffset(); } - /** - * Returns the document position that is (visually) nearest to the given - * document position <code>pos</code> in the given direction <code>d</code>. - * - * @param c the text component - * @param pos the document position - * @param b the bias for <code>pos</code> - * @param d the direction, must be either {@link SwingConstants#NORTH}, - * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or - * {@link SwingConstants#EAST} - * @param biasRet an array of {@link Position.Bias} that can hold at least - * one element, which is filled with the bias of the return position - * on method exit - * - * @return the document position that is (visually) nearest to the given - * document position <code>pos</code> in the given direction - * <code>d</code> - * - * @throws BadLocationException if <code>pos</code> is not a valid offset in - * the document model - */ - public int getNextVisualPositionFrom(JTextComponent c, int pos, - Position.Bias b, int d, - Position.Bias[] biasRet) - throws BadLocationException - { - // TODO: Implement this properly. - throw new AssertionError("Not implemented yet."); - } - } diff --git a/libjava/classpath/javax/swing/text/InternationalFormatter.java b/libjava/classpath/javax/swing/text/InternationalFormatter.java index 86300a7..ba3cffa 100644 --- a/libjava/classpath/javax/swing/text/InternationalFormatter.java +++ b/libjava/classpath/javax/swing/text/InternationalFormatter.java @@ -78,6 +78,8 @@ public class InternationalFormatter minimum = null; maximum = null; format = null; + setCommitsOnValidEdit(false); + setOverwriteMode(false); } /** @@ -226,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/libjava/classpath/javax/swing/text/JTextComponent.java b/libjava/classpath/javax/swing/text/JTextComponent.java index 83966bb..afa1f24 100644 --- a/libjava/classpath/javax/swing/text/JTextComponent.java +++ b/libjava/classpath/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; } } @@ -864,7 +870,7 @@ public abstract class JTextComponent extends JComponent Hashtable acts = new Hashtable(actions.length); for (int i = 0; i < actions.length; ++i) acts.put(actions[i].getValue(Action.NAME), actions[i]); - for (int i = 0; i < bindings.length; ++i) + for (int i = 0; i < bindings.length; ++i) if (acts.containsKey(bindings[i].actionName)) map.addActionForKeyStroke(bindings[i].key, (Action) acts.get(bindings[i].actionName)); } @@ -906,33 +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("typed \u007f"), - DefaultEditorKit.deleteNextCharAction) - }, - getActions()); } public void setDocument(Document newDoc) diff --git a/libjava/classpath/javax/swing/text/MaskFormatter.java b/libjava/classpath/javax/swing/text/MaskFormatter.java new file mode 100644 index 0000000..d12b9ea --- /dev/null +++ b/libjava/classpath/javax/swing/text/MaskFormatter.java @@ -0,0 +1,583 @@ +/* MaskFormatter.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; + +import java.text.ParseException; + +import javax.swing.JFormattedTextField; + +/** + * @author Anthony Balkissoon abalkiss at redhat dot com + * + */ +public class MaskFormatter extends DefaultFormatter +{ + // The declaration of the valid mask characters + private static final char NUM_CHAR = '#'; + private static final char ESCAPE_CHAR = '\''; + private static final char UPPERCASE_CHAR = 'U'; + private static final char LOWERCASE_CHAR = 'L'; + private static final char ALPHANUM_CHAR = 'A'; + private static final char LETTER_CHAR = '?'; + private static final char ANYTHING_CHAR = '*'; + private static final char HEX_CHAR = 'H'; + + /** The mask for this MaskFormatter **/ + private String mask; + + /** + * A String made up of the characters that are not valid for input for + * this MaskFormatter. + */ + private String invalidChars; + + /** + * A String made up of the characters that are valid for input for + * this MaskFormatter. + */ + private String validChars; + + /** A String used in place of missing chracters if the value does not + * completely fill in the spaces in the mask. + */ + private String placeHolder; + + /** A character used in place of missing characters if the value does + * not completely fill in the spaces in the mask. + */ + private char placeHolderChar = ' '; + + /** + * Whether or not stringToValue should return literal characters in the mask. + */ + private boolean valueContainsLiteralCharacters = true; + + /** A String used for easy access to valid HEX characters **/ + private static String hexString = "0123456789abcdefABCDEF"; + + /** An int to hold the length of the mask, accounting for escaped characters **/ + int maskLength = 0; + + public MaskFormatter () + { + // Override super's default behaviour, in MaskFormatter the default + // is not to allow invalid values + setAllowsInvalid(false); + } + + /** + * Creates a MaskFormatter with the specified mask. + * @specnote doesn't actually throw a ParseException although it + * is declared to do so + * @param mask + * @throws java.text.ParseException + */ + public MaskFormatter (String mask) throws java.text.ParseException + { + // Override super's default behaviour, in MaskFormatter the default + // is not to allow invalid values + setAllowsInvalid(false); + setMask (mask); + } + + /** + * Returns the mask used in this MaskFormatter. + * @return the mask used in this MaskFormatter. + */ + public String getMask() + { + return mask; + } + + /** + * Returns a String containing the characters that are not valid for input + * for this MaskFormatter. + * @return a String containing the invalid characters. + */ + public String getInvalidCharacters() + { + return invalidChars; + } + + /** + * Sets characters that are not valid for input. If + * <code>invalidCharacters</code> is non-null then no characters contained + * in it will be allowed to be input. + * + * @param invalidCharacters the String specifying invalid characters. + */ + public void setInvalidCharacters (String invalidCharacters) + { + this.invalidChars = invalidCharacters; + } + + /** + * Returns a String containing the characters that are valid for input + * for this MaskFormatter. + * @return a String containing the valid characters. + */ + public String getValidCharacters() + { + return validChars; + } + + /** + * Sets characters that are valid for input. If + * <code>validCharacters</code> is non-null then no characters that are + * not contained in it will be allowed to be input. + * + * @param validCharacters the String specifying valid characters. + */ + public void setValidCharacters (String validCharacters) + { + this.validChars = validCharacters; + } + + /** + * Returns the place holder String that is used in place of missing + * characters when the value doesn't completely fill in the spaces + * in the mask. + * @return the place holder String. + */ + public String getPlaceholder() + { + return placeHolder; + } + + /** + * Sets the string to use if the value does not completely fill in the mask. + * If this is null, the place holder character will be used instead. + * @param placeholder the String to use if the value doesn't completely + * fill in the mask. + */ + public void setPlaceholder (String placeholder) + { + this.placeHolder = placeholder; + } + + /** + * Returns the character used in place of missing characters when the + * value doesn't completely fill the mask. + * @return the place holder character + */ + public char getPlaceholderCharacter() + { + return placeHolderChar; + } + + /** + * Sets the char to use if the value does not completely fill in the mask. + * This is only used if the place holder String has not been set or does + * not completely fill in the mask. + * @param placeholder the char to use if the value doesn't completely + * fill in the mask. + */ + public void setPlaceholderCharacter (char placeholder) + { + this.placeHolderChar = placeholder; + } + + /** + * Returns true if stringToValue should return the literal + * characters in the mask. + * @return true if stringToValue should return the literal + * characters in the mask + */ + public boolean getValueContainsLiteralCharacters() + { + return valueContainsLiteralCharacters; + } + + /** + * Determines whether stringToValue will return literal characters or not. + * @param containsLiteralChars if true, stringToValue will return the + * literal characters in the mask, otherwise it will not. + */ + public void setValueContainsLiteralCharacters (boolean containsLiteralChars) + { + this.valueContainsLiteralCharacters = containsLiteralChars; + } + + /** + * Sets the mask for this MaskFormatter. + * @specnote doesn't actually throw a ParseException even though it is + * declared to do so + * @param mask the new mask for this MaskFormatter + * @throws ParseException if <code>mask</code> is not valid. + */ + public void setMask (String mask) throws ParseException + { + this.mask = mask; + + // Update the cached maskLength. + int end = mask.length() - 1; + maskLength = 0; + for (int i = 0; i <= end; i++) + { + // Handle escape characters properly - they don't add to the maskLength + // but 2 escape characters in a row is really one escape character and + // one literal single quote, so that does add 1 to the maskLength. + if (mask.charAt(i) == '\'') + { + // Escape characters at the end of the mask don't do anything. + if (i != end) + maskLength++; + i++; + } + else + maskLength++; + } + } + + /** + * Installs this MaskFormatter on the JFormattedTextField. + * Invokes valueToString to convert the current value from the + * JFormattedTextField to a String, then installs the Actions from + * getActions, the DocumentFilter from getDocumentFilter, and the + * NavigationFilter from getNavigationFilter. + * + * If valueToString throws a ParseException, this method sets the text + * to an empty String and marks the JFormattedTextField as invalid. + */ + public void install (JFormattedTextField ftf) + { + super.install(ftf); + if (ftf != null) + { + try + { + valueToString(ftf.getValue()); + } + catch (ParseException pe) + { + // Set the text to an empty String and mark the JFormattedTextField + // as invalid. + ftf.setText(""); + setEditValid(false); + } + } + } + + /** + * Parses the text using the mask, valid characters, and invalid characters + * to determine the appropriate Object to return. This strips the literal + * characters if necessary and invokes super.stringToValue. If the paramter + * is invalid for the current mask and valid/invalid character sets this + * method will throw a ParseException. + * + * @param value the String to parse + * @throws ParseException if value doesn't match the mask and valid/invalid + * character sets + */ + public Object stringToValue (String value) throws ParseException + { + int vLength = value.length(); + + // For value to be a valid it must be the same length as the mask + // note this doesn't take into account symbols that occupy more than + // one character, this is something we may possibly need to fix. + if (maskLength != vLength) + throw new ParseException ("stringToValue passed invalid value", vLength); + + // Check if the value is valid according to the mask and valid/invalid + // sets. + try + { + convertValue(value, false); + } + catch (ParseException pe) + { + throw new ParseException("stringToValue passed invalid value", + pe.getErrorOffset()); + } + + if (!getValueContainsLiteralCharacters()) + value = stripLiterals(value); + return super.stringToValue(value); + } + + /** + * Strips the literal characters from the given String. + * @param value the String to strip + * @return the stripped String + */ + String stripLiterals(String value) + { + StringBuffer result = new StringBuffer(); + for (int i = 0; i < value.length(); i++) + { + // Only append the characters that don't correspond to literal + // characters in the mask. + switch (mask.charAt(i)) + { + case NUM_CHAR: + case UPPERCASE_CHAR: + case LOWERCASE_CHAR: + case ALPHANUM_CHAR: + case LETTER_CHAR: + case HEX_CHAR: + case ANYTHING_CHAR: + result.append(value.charAt(i)); + break; + default: + } + } + return result.toString(); + } + + /** + * Returns a String representation of the Object value based on the mask. + * + * @param value the value to convert + * @throws ParseException if value is invalid for this mask and valid/invalid + * character sets + */ + public String valueToString (Object value) throws ParseException + { + String result = super.valueToString(value); + int rLength = result.length(); + + // If value is longer than the mask, truncate it. Note we may need to + // account for symbols that are more than one character long. + if (rLength > maskLength) + result = result.substring(0, maskLength); + + // Verify the validity and convert to upper/lowercase as needed. + result = convertValue(result, true); + if (rLength < maskLength) + return pad(result, rLength); + return result; + } + + /** + * This method takes in a String and runs it through the mask to make + * sure that it is valid. If <code>convert</code> is true, it also + * converts letters to upper/lowercase as required by the mask. + * @param value the String to convert + * @param convert true if we should convert letters to upper/lowercase + * @return the converted String + * @throws ParseException if the given String isn't valid for the mask + */ + String convertValue(String value, boolean convert) throws ParseException + { + StringBuffer result = new StringBuffer(value); + char markChar; + char resultChar; + boolean literal; + + // this boolean is specifically to avoid calling the isCharValid method + // when neither invalidChars or validChars has been set + boolean checkCharSets = (invalidChars != null || validChars != null); + + for (int i = 0, j = 0; i < value.length(); i++, j++) + { + literal = false; + resultChar = result.charAt(i); + // This switch block on the mask character checks that the character + // within <code>value</code> at that point is valid according to the + // mask and also converts to upper/lowercase as needed. + switch (mask.charAt(j)) + { + case NUM_CHAR: + if (!Character.isDigit(resultChar)) + throw new ParseException("Number expected", i); + break; + case UPPERCASE_CHAR: + if (!Character.isLetter(resultChar)) + throw new ParseException("Letter expected", i); + if (convert) + result.setCharAt(i, Character.toUpperCase(resultChar)); + break; + case LOWERCASE_CHAR: + if (!Character.isLetter(resultChar)) + throw new ParseException("Letter expected", i); + if (convert) + result.setCharAt(i, Character.toLowerCase(resultChar)); + break; + case ALPHANUM_CHAR: + if (!Character.isLetterOrDigit(resultChar)) + throw new ParseException("Letter or number expected", i); + break; + case LETTER_CHAR: + if (!Character.isLetter(resultChar)) + throw new ParseException("Letter expected", i); + break; + case HEX_CHAR: + if (hexString.indexOf(resultChar) == -1) + throw new ParseException("Hexadecimal character expected", i); + break; + case ANYTHING_CHAR: + break; + case ESCAPE_CHAR: + // Escape character, check the next character to make sure that + // the literals match + j++; + literal = true; + if (resultChar != mask.charAt(j)) + throw new ParseException ("Invalid character: "+resultChar, i); + break; + default: + literal = true; + if (!getValueContainsLiteralCharacters() && convert) + throw new ParseException ("Invalid character: "+resultChar, i); + else if (resultChar != mask.charAt(j)) + throw new ParseException ("Invalid character: "+resultChar, i); + } + // If necessary, check if the character is valid. + if (!literal && checkCharSets && !isCharValid(resultChar)) + throw new ParseException("invalid character: "+resultChar, i); + + } + return result.toString(); + } + + /** + * Convenience method used by many other methods to check if a character is + * valid according to the mask, the validChars, and the invalidChars. To + * be valid a character must: + * 1. be allowed by the mask + * 2. be present in any non-null validChars String + * 3. not be present in any non-null invalidChars String + * @param testChar the character to test + * @return true if the character is valid + */ + boolean isCharValid(char testChar) + { + char lower = Character.toLowerCase(testChar); + char upper = Character.toUpperCase(testChar); + // If validChars isn't null, the character must appear in it. + if (validChars != null) + if (validChars.indexOf(lower) == -1 && validChars.indexOf(upper) == -1) + return false; + // If invalidChars isn't null, the character must not appear in it. + if (invalidChars != null) + if (invalidChars.indexOf(lower) != -1 + || invalidChars.indexOf(upper) != -1) + return false; + return true; + } + + /** + * Pads the value with literals, the placeholder String and/or placeholder + * character as appropriate. + * @param value the value to pad + * @param currLength the current length of the value + * @return the padded String + */ + String pad (String value, int currLength) + { + StringBuffer result = new StringBuffer(value); + int index = currLength; + while (result.length() < maskLength) + { + // The character used to pad may be a literal, a character from the + // place holder string, or the place holder character. getPadCharAt + // will find the proper one for us. + result.append (getPadCharAt(index)); + index++; + } + return result.toString(); + } + + /** + * Returns the character with which to pad the value at the given index + * position. If the mask has a literal at this position, this is returned + * otherwise if the place holder string is initialized and is longer than + * <code>i</code> characters then the character at position <code>i</code> + * from this String is returned. Else, the place holder character is + * returned. + * @param i the index at which we want to pad the value + * @return the character with which we should pad the value + */ + char getPadCharAt(int i) + { + boolean escaped = false; + int target = i; + char maskChar; + int holderLength = placeHolder == null ? -1 : placeHolder.length(); + // We must iterate through the mask from the beginning, because the given + // index doesn't account for escaped characters. For example, with the + // mask "1A'A''A1" index 2 refers to the literalized A, not to the + // single quotation. + for (int n = 0; n < mask.length(); n++) + { + maskChar = mask.charAt(n); + if (maskChar == ESCAPE_CHAR && !escaped) + { + target++; + escaped = true; + } + else if (escaped == true) + { + // Check if target == n which means we've come to the character + // we want to return and since it is a literal (because escaped + // is true), we return it. + if (target == n) + return maskChar; + escaped = false; + } + if (target == n) + { + // We've come to the character we want to return. It wasn't + // escaped so if it isn't a literal we should return either + // the character from place holder string or the place holder + // character, depending on whether or not the place holder + // string is long enough. + switch (maskChar) + { + case NUM_CHAR: + case UPPERCASE_CHAR: + case LOWERCASE_CHAR: + case ALPHANUM_CHAR: + case LETTER_CHAR: + case HEX_CHAR: + case ANYTHING_CHAR: + if (holderLength > i) + return placeHolder.charAt(i); + else + return placeHolderChar; + default: + return maskChar; + } + } + } + // This shouldn't happen + throw new AssertionError("MaskFormatter.getMaskCharAt failed"); + } +} diff --git a/libjava/classpath/javax/swing/text/NumberFormatter.java b/libjava/classpath/javax/swing/text/NumberFormatter.java new file mode 100644 index 0000000..a858ff4 --- /dev/null +++ b/libjava/classpath/javax/swing/text/NumberFormatter.java @@ -0,0 +1,86 @@ +/* NumberFormatter.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; + +import java.text.Format; +import java.text.NumberFormat; + +/** + * <code>NumberFormatter</code> is an {@link InternationalFormatter} + * that implements value to string and string to value conversion via + * an instance of {@link NumberFormat}. + * + * @author Anthony Balkissoon abalkiss at redhat dot com + * @since 1.4 + */ +public class NumberFormatter extends InternationalFormatter +{ + + /** + * Creates a NumberFormatter with the default NumberFormat from + * NumberFormat.getNumberInstance(). + */ + public NumberFormatter () + { + this (NumberFormat.getNumberInstance()); + } + + /** + * Creates a NumberFormatter with the specified NumberFormat. + * @param format the NumberFormat to use for this NumberFormatter. + */ + public NumberFormatter (NumberFormat format) + { + super(format); + setFormat(format); + } + + /** + * Sets the NumberFormat that this NumberFormatter will use to determine + * legal values for editing and displaying. + * + * @param format the Format to use to determine legal values. + */ + public void setFormat (Format format) + { + // TODO: This should be different from the super implementation + // but I don't yet know how. + super.setFormat(format); + } +} diff --git a/libjava/classpath/javax/swing/text/PasswordView.java b/libjava/classpath/javax/swing/text/PasswordView.java index c3aa66c..e54331c5 100644 --- a/libjava/classpath/javax/swing/text/PasswordView.java +++ b/libjava/classpath/javax/swing/text/PasswordView.java @@ -41,6 +41,7 @@ package javax.swing.text; import java.awt.Color; import java.awt.FontMetrics; import java.awt.Graphics; +import java.awt.Rectangle; import java.awt.Shape; import javax.swing.JPasswordField; @@ -211,7 +212,10 @@ public class PasswordView /** * Provides a mapping from the document model coordinate space to the * coordinate space of the view mapped to it. - * + * + * This method is overridden to provide a correct mapping with respect to the + * echo char and not to the real content. + * * @param pos - the position to convert >= 0 * @param a - the allocated region to render into * @param b - typesafe enumeration to indicate bias to a position in the model. @@ -222,7 +226,35 @@ public class PasswordView public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException { - return super.modelToView(pos, a, b); + Shape newAlloc = adjustAllocation(a); + + // Ensure metrics are up-to-date. + updateMetrics(); + + // Get rectangle of the line containing position. + int lineIndex = getElement().getElementIndex(pos); + Rectangle rect = lineToRect(newAlloc, lineIndex); + + // Get the rectangle for position. + Element line = getElement().getElement(lineIndex); + int lineStart = line.getStartOffset(); + Segment segment = getLineBuffer(); + segment.array = new char[pos - lineStart]; + char echoChar = getEchoChar(); + for (int i = 0; i < segment.array.length; ++i) + segment.array[i] = echoChar; + segment.offset = 0; + segment.count = segment.array.length; + + int xoffset = Utilities.getTabbedTextWidth(segment, metrics, rect.x, + this, lineStart); + + // Calc the real rectangle. + rect.x += xoffset; + rect.width = 1; + rect.height = metrics.getHeight(); + + return rect; } /** @@ -239,6 +271,8 @@ public class PasswordView */ public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias) { + // FIXME: This only provides a view->model mapping for the real text + // content and does not respect the echo char. return super.viewToModel(fx, fy, a, bias); } } diff --git a/libjava/classpath/javax/swing/text/PlainDocument.java b/libjava/classpath/javax/swing/text/PlainDocument.java index 0c00a06..2200cae 100644 --- a/libjava/classpath/javax/swing/text/PlainDocument.java +++ b/libjava/classpath/javax/swing/text/PlainDocument.java @@ -113,6 +113,46 @@ public class PlainDocument extends AbstractDocument int elementIndex = rootElement.getElementIndex(offset); Element firstElement = rootElement.getElement(elementIndex); + // If we're inserting immediately after a newline we have to fix the + // Element structure. + if (offset > 0) + { + try + { + String s = getText(offset - 1, 1); + if (s.equals("\n")) + { + int newEl2EndOffset = end; + boolean replaceNext = false; + if (rootElement.getElementCount() > elementIndex + 1) + { + replaceNext = true; + newEl2EndOffset = + rootElement.getElement(elementIndex + 1).getEndOffset(); + } + Element newEl1 = + createLeafElement(rootElement, firstElement.getAttributes(), + firstElement.getStartOffset(), offset); + Element newEl2 = + createLeafElement (rootElement, firstElement.getAttributes(), + offset, newEl2EndOffset); + if (replaceNext) + rootElement.replace(elementIndex, 2, new Element[] { newEl1, newEl2 }); + else + rootElement.replace(elementIndex, 1, new Element[] { newEl1, newEl2 }); + firstElement = newEl2; + elementIndex ++; + } + } + catch (BadLocationException ble) + { + // This shouldn't happen. + AssertionError ae = new AssertionError(); + ae.initCause(ble); + throw ae; + } + } + // added and removed are Element arrays used to add an ElementEdit // to the DocumentEvent if there were entire lines added or removed. Element[] removed = new Element[1]; diff --git a/libjava/classpath/javax/swing/text/PlainView.java b/libjava/classpath/javax/swing/text/PlainView.java index 9f5ee8ad..a318ee7 100644 --- a/libjava/classpath/javax/swing/text/PlainView.java +++ b/libjava/classpath/javax/swing/text/PlainView.java @@ -185,7 +185,6 @@ public class PlainView extends View implements TabExpander JTextComponent textComponent = (JTextComponent) getContainer(); - g.setFont(textComponent.getFont()); selectedColor = textComponent.getSelectedTextColor(); unselectedColor = textComponent.getForeground(); disabledColor = textComponent.getDisabledTextColor(); @@ -513,7 +512,8 @@ public class PlainView extends View implements TabExpander else { Rectangle repaintRec = rec0.union(rec1); - host.repaint(); + host.repaint(repaintRec.x, repaintRec.y, repaintRec.width, + repaintRec.height); } } @@ -530,35 +530,5 @@ public class PlainView extends View implements TabExpander lineBuffer = new Segment(); return lineBuffer; } - - /** - * Returns the document position that is (visually) nearest to the given - * document position <code>pos</code> in the given direction <code>d</code>. - * - * @param c the text component - * @param pos the document position - * @param b the bias for <code>pos</code> - * @param d the direction, must be either {@link SwingConstants#NORTH}, - * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or - * {@link SwingConstants#EAST} - * @param biasRet an array of {@link Position.Bias} that can hold at least - * one element, which is filled with the bias of the return position - * on method exit - * - * @return the document position that is (visually) nearest to the given - * document position <code>pos</code> in the given direction - * <code>d</code> - * - * @throws BadLocationException if <code>pos</code> is not a valid offset in - * the document model - */ - public int getNextVisualPositionFrom(JTextComponent c, int pos, - Position.Bias b, int d, - Position.Bias[] biasRet) - throws BadLocationException - { - // TODO: Implement this properly. - throw new AssertionError("Not implemented yet."); - } } diff --git a/libjava/classpath/javax/swing/text/StyleContext.java b/libjava/classpath/javax/swing/text/StyleContext.java index 6c4e2994..dabc0ba 100644 --- a/libjava/classpath/javax/swing/text/StyleContext.java +++ b/libjava/classpath/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/libjava/classpath/javax/swing/text/StyledEditorKit.java b/libjava/classpath/javax/swing/text/StyledEditorKit.java index e71f992..c4eef44 100644 --- a/libjava/classpath/javax/swing/text/StyledEditorKit.java +++ b/libjava/classpath/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/libjava/classpath/javax/swing/text/TableView.java b/libjava/classpath/javax/swing/text/TableView.java new file mode 100644 index 0000000..d3113b8 --- /dev/null +++ b/libjava/classpath/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/libjava/classpath/javax/swing/text/Utilities.java b/libjava/classpath/javax/swing/text/Utilities.java index 7830b2f..1adc8ff 100644 --- a/libjava/classpath/javax/swing/text/Utilities.java +++ b/libjava/classpath/javax/swing/text/Utilities.java @@ -45,6 +45,7 @@ import java.awt.Rectangle; import java.text.BreakIterator; import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; /** * A set of utilities to deal with text. This is used by several other classes @@ -573,10 +574,11 @@ public class Utilities View rootView = c.getUI().getRootView(c); Rectangle r = c.modelToView(offset); int offs = c.viewToModel(new Point(x, r.y)); - int pos = rootView.getNextVisualPositionFrom(c, offs, - Position.Bias.Forward, - SwingConstants.NORTH, - new Position.Bias[1]); + int pos = rootView.getNextVisualPositionFrom(offs, + Position.Bias.Forward, + SwingUtilities.calculateInnerArea(c, null), + SwingConstants.NORTH, + new Position.Bias[1]); return pos; } @@ -599,10 +601,11 @@ public class Utilities View rootView = c.getUI().getRootView(c); Rectangle r = c.modelToView(offset); int offs = c.viewToModel(new Point(x, r.y)); - int pos = rootView.getNextVisualPositionFrom(c, offs, - Position.Bias.Forward, - SwingConstants.SOUTH, - new Position.Bias[1]); + int pos = rootView.getNextVisualPositionFrom(offs, + Position.Bias.Forward, + SwingUtilities.calculateInnerArea(c, null), + SwingConstants.SOUTH, + new Position.Bias[1]); return pos; } } diff --git a/libjava/classpath/javax/swing/text/View.java b/libjava/classpath/javax/swing/text/View.java index daab347..b835842 100644 --- a/libjava/classpath/javax/swing/text/View.java +++ b/libjava/classpath/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(); } /** @@ -599,9 +601,9 @@ public abstract class View implements SwingConstants * Returns the document position that is (visually) nearest to the given * document position <code>pos</code> in the given direction <code>d</code>. * - * @param c the text component * @param pos the document position * @param b the bias for <code>pos</code> + * @param a the allocation for this view * @param d the direction, must be either {@link SwingConstants#NORTH}, * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or * {@link SwingConstants#EAST} @@ -615,9 +617,31 @@ public abstract class View implements SwingConstants * * @throws BadLocationException if <code>pos</code> is not a valid offset in * the document model + * @throws IllegalArgumentException if <code>d</code> is not a valid direction */ - public abstract int getNextVisualPositionFrom(JTextComponent c, int pos, - Position.Bias b, int d, - Position.Bias[] biasRet) - throws BadLocationException; + public int getNextVisualPositionFrom(int pos, Position.Bias b, + Shape a, int d, + Position.Bias[] biasRet) + throws BadLocationException + { + int ret = pos; + switch (d) + { + case WEST: + ret = pos - 1; + break; + case EAST: + ret = pos + 1; + break; + case NORTH: + // TODO: Implement this + break; + case SOUTH: + // TODO: Implement this + break; + default: + throw new IllegalArgumentException("Illegal value for d"); + } + return ret; + } } diff --git a/libjava/classpath/javax/swing/text/WrappedPlainView.java b/libjava/classpath/javax/swing/text/WrappedPlainView.java index b03399d..baba343 100644 --- a/libjava/classpath/javax/swing/text/WrappedPlainView.java +++ b/libjava/classpath/javax/swing/text/WrappedPlainView.java @@ -620,36 +620,6 @@ public class WrappedPlainView extends BoxView implements TabExpander currLineStart = currLineEnd; } } - - /** - * Returns the document position that is (visually) nearest to the given - * document position <code>pos</code> in the given direction <code>d</code>. - * - * @param c the text component - * @param pos the document position - * @param b the bias for <code>pos</code> - * @param d the direction, must be either {@link SwingConstants#NORTH}, - * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or - * {@link SwingConstants#EAST} - * @param biasRet an array of {@link Position.Bias} that can hold at least - * one element, which is filled with the bias of the return position - * on method exit - * - * @return the document position that is (visually) nearest to the given - * document position <code>pos</code> in the given direction - * <code>d</code> - * - * @throws BadLocationException if <code>pos</code> is not a valid offset - * in the document model - */ - public int getNextVisualPositionFrom(JTextComponent c, int pos, - Position.Bias b, int d, - Position.Bias[] biasRet) - throws BadLocationException - { - // TODO: Implement this properly. - throw new AssertionError("Not implemented yet."); - } /** * This method is called from insertUpdate and removeUpdate. diff --git a/libjava/classpath/javax/swing/text/html/BlockView.java b/libjava/classpath/javax/swing/text/html/BlockView.java new file mode 100644 index 0000000..6274e7b --- /dev/null +++ b/libjava/classpath/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/libjava/classpath/javax/swing/text/html/CSS.java b/libjava/classpath/javax/swing/text/html/CSS.java index 029ad26..c248e75 100644 --- a/libjava/classpath/javax/swing/text/html/CSS.java +++ b/libjava/classpath/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/libjava/classpath/javax/swing/text/html/CSSParser.java b/libjava/classpath/javax/swing/text/html/CSSParser.java new file mode 100644 index 0000000..0bf76eb --- /dev/null +++ b/libjava/classpath/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/libjava/classpath/javax/swing/text/html/HTMLDocument.java b/libjava/classpath/javax/swing/text/html/HTMLDocument.java index d048a04..5b2452b 100644 --- a/libjava/classpath/javax/swing/text/html/HTMLDocument.java +++ b/libjava/classpath/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/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java b/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java index 5189c77..1ef9768 100644 --- a/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java +++ b/libjava/classpath/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,297 @@ 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; } -}
\ No newline at end of file +} diff --git a/libjava/classpath/javax/swing/text/html/StyleSheet.java b/libjava/classpath/javax/swing/text/html/StyleSheet.java new file mode 100644 index 0000000..2466a28 --- /dev/null +++ b/libjava/classpath/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/libjava/classpath/javax/swing/text/html/default.css b/libjava/classpath/javax/swing/text/html/default.css new file mode 100644 index 0000000..f2a44f8 --- /dev/null +++ b/libjava/classpath/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/libjava/classpath/javax/swing/tree/DefaultTreeModel.java b/libjava/classpath/javax/swing/tree/DefaultTreeModel.java index 5cf8098..818f548 100644 --- a/libjava/classpath/javax/swing/tree/DefaultTreeModel.java +++ b/libjava/classpath/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/libjava/classpath/javax/swing/tree/DefaultTreeSelectionModel.java b/libjava/classpath/javax/swing/tree/DefaultTreeSelectionModel.java index 75b76a9..de27dad 100644 --- a/libjava/classpath/javax/swing/tree/DefaultTreeSelectionModel.java +++ b/libjava/classpath/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(); } |