diff options
Diffstat (limited to 'javax')
36 files changed, 2240 insertions, 1569 deletions
diff --git a/javax/crypto/CipherOutputStream.java b/javax/crypto/CipherOutputStream.java index adeb6e5ed..5d1e57a16 100644 --- a/javax/crypto/CipherOutputStream.java +++ b/javax/crypto/CipherOutputStream.java @@ -45,59 +45,25 @@ import java.io.OutputStream; /** * A filtered output stream that transforms data written to it with a * {@link Cipher} before sending it to the underlying output stream. - * + * * @author Casey Marshall (csm@gnu.org) */ public class CipherOutputStream extends FilterOutputStream { - - // Fields. - // ------------------------------------------------------------------------ - /** The underlying cipher. */ private Cipher cipher; - private byte[][] inBuffer; - - private int inLength; - - private byte[] outBuffer; - - private static final int FIRST_TIME = 0; - private static final int SECOND_TIME = 1; - private static final int SEASONED = 2; - private int state; - - /** True if the cipher is a stream cipher (blockSize == 1) */ - private boolean isStream; - - // Constructors. - // ------------------------------------------------------------------------ - /** - * Create a new cipher output stream. The cipher argument must have - * already been initialized. - * - * @param out The sink for transformed data. + * Create a new cipher output stream. The cipher argument must have already + * been initialized. + * + * @param out The sink for transformed data. * @param cipher The cipher to transform data with. */ public CipherOutputStream(OutputStream out, Cipher cipher) { super(out); - if (cipher != null) - { - this.cipher = cipher; - if (!(isStream = cipher.getBlockSize() == 1)) - { - inBuffer = new byte[2][]; - inBuffer[0] = new byte[cipher.getBlockSize()]; - inBuffer[1] = new byte[cipher.getBlockSize()]; - inLength = 0; - state = FIRST_TIME; - } - } - else - this.cipher = new NullCipher(); + this.cipher = (cipher != null) ? cipher : new NullCipher(); } /** @@ -110,52 +76,36 @@ public class CipherOutputStream extends FilterOutputStream super(out); } - // Instance methods. - // ------------------------------------------------------------------------ - /** * Close this output stream, and the sink output stream. - * - * <p>This method will first invoke the {@link Cipher#doFinal()} - * method of the underlying {@link Cipher}, and writes the output of - * that method to the sink output stream. - * - * @throws java.io.IOException If an I/O error occurs, or if an error - * is caused by finalizing the transformation. + * <p> + * This method will first invoke the {@link Cipher#doFinal()} method of the + * underlying {@link Cipher}, and writes the output of that method to the + * sink output stream. + * + * @throws IOException If an I/O error occurs, or if an error is caused by + * finalizing the transformation. */ public void close() throws IOException { try { - int len; - if (state != FIRST_TIME) - { - len = cipher.update(inBuffer[0], 0, inBuffer[0].length, outBuffer); - out.write(outBuffer, 0, len); - } - len = cipher.doFinal(inBuffer[0], 0, inLength, outBuffer); - out.write(outBuffer, 0, len); - } - catch (javax.crypto.IllegalBlockSizeException ibse) - { - throw new IOException(ibse.toString()); + out.write(cipher.doFinal()); + out.flush(); + out.close(); } - catch (javax.crypto.BadPaddingException bpe) + catch (Exception cause) { - throw new IOException(bpe.toString()); + IOException ioex = new IOException(String.valueOf(cause)); + ioex.initCause(cause); + throw ioex; } - catch (ShortBufferException sbe) - { - throw new IOException(sbe.toString()); - } - out.flush(); - out.close(); } /** * Flush any pending output. * - * @throws java.io.IOException If an I/O error occurs. + * @throws IOException If an I/O error occurs. */ public void flush() throws IOException { @@ -164,40 +114,22 @@ public class CipherOutputStream extends FilterOutputStream /** * Write a single byte to the output stream. - * + * * @param b The next byte. - * @throws java.io.IOException If an I/O error occurs, or if the - * underlying cipher is not in the correct state to transform - * data. + * @throws IOException If an I/O error occurs, or if the underlying cipher is + * not in the correct state to transform data. */ public void write(int b) throws IOException { - if (isStream) - { - byte[] buf = new byte[] { (byte) b }; - try - { - cipher.update(buf, 0, 1, buf, 0); - } - catch (ShortBufferException sbe) - { - throw new IOException(sbe.toString()); - } - out.write(buf); - return; - } - inBuffer[1][inLength++] = (byte) b; - if (inLength == inBuffer[1].length) - process(); + write(new byte[] { (byte) b }, 0, 1); } /** * Write a byte array to the output stream. - * + * * @param buf The next bytes. - * @throws java.io.IOException If an I/O error occurs, or if the - * underlying cipher is not in the correct state to transform - * data. + * @throws IOException If an I/O error occurs, or if the underlying cipher is + * not in the correct state to transform data. */ public void write(byte[] buf) throws IOException { @@ -206,63 +138,15 @@ public class CipherOutputStream extends FilterOutputStream /** * Write a portion of a byte array to the output stream. - * + * * @param buf The next bytes. * @param off The offset in the byte array to start. * @param len The number of bytes to write. - * @throws java.io.IOException If an I/O error occurs, or if the - * underlying cipher is not in the correct state to transform - * data. + * @throws IOException If an I/O error occurs, or if the underlying cipher is + * not in the correct state to transform data. */ public void write(byte[] buf, int off, int len) throws IOException { - if (isStream) - { - out.write(cipher.update(buf, off, len)); - return; - } - int count = 0; - while (count < len) - { - int l = Math.min(inBuffer[1].length - inLength, len - count); - System.arraycopy(buf, off+count, inBuffer[1], inLength, l); - count += l; - inLength += l; - if (inLength == inBuffer[1].length) - process(); - } - } - - // Own method. - // ------------------------------------------------------------------------- - - private void process() throws IOException - { - if (state == SECOND_TIME) - { - state = SEASONED; - } - else - { - byte[] temp = inBuffer[0]; - inBuffer[0] = inBuffer[1]; - inBuffer[1] = temp; - } - if (state == FIRST_TIME) - { - inLength = 0; - state = SECOND_TIME; - return; - } - try - { - cipher.update(inBuffer[0], 0, inBuffer[0].length, outBuffer); - } - catch (ShortBufferException sbe) - { - throw new IOException(sbe.toString()); - } - out.write(outBuffer); - inLength = 0; + out.write(cipher.update(buf, off, len)); } } diff --git a/javax/swing/AbstractButton.java b/javax/swing/AbstractButton.java index c2c894c06..cb0f458b8 100644 --- a/javax/swing/AbstractButton.java +++ b/javax/swing/AbstractButton.java @@ -37,8 +37,6 @@ exception statement from your version. */ package javax.swing; -import gnu.classpath.NotImplementedException; - import java.awt.Component; import java.awt.Graphics; import java.awt.Image; @@ -74,7 +72,10 @@ import javax.swing.plaf.ButtonUI; import javax.swing.plaf.basic.BasicHTML; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.Element; import javax.swing.text.Position; +import javax.swing.text.StyledDocument; import javax.swing.text.View; @@ -804,22 +805,127 @@ public abstract class AbstractButton extends JComponent return -1; } - public String getAtIndex(int value0, int value1) - throws NotImplementedException + /** + * Returns the character, word or sentence at the specified index. The + * <code>part</code> parameter determines what is returned, the character, + * word or sentence after the index. + * + * @param part one of {@link AccessibleText#CHARACTER}, + * {@link AccessibleText#WORD} or + * {@link AccessibleText#SENTENCE}, specifying what is returned + * @param index the index + * + * @return the character, word or sentence after <code>index</code> + */ + public String getAtIndex(int part, int index) { - return null; // TODO + String result = ""; + int startIndex = -1; + int endIndex = -1; + switch(part) + { + case AccessibleText.CHARACTER: + result = String.valueOf(text.charAt(index)); + break; + case AccessibleText.WORD: + startIndex = text.lastIndexOf(' ', index); + endIndex = text.indexOf(' ', startIndex + 1); + if (endIndex == -1) + endIndex = startIndex + 1; + result = text.substring(startIndex + 1, endIndex); + break; + case AccessibleText.SENTENCE: + default: + startIndex = text.lastIndexOf('.', index); + endIndex = text.indexOf('.', startIndex + 1); + if (endIndex == -1) + endIndex = startIndex + 1; + result = text.substring(startIndex + 1, endIndex); + break; + } + return result; } - public String getAfterIndex(int value0, int value1) - throws NotImplementedException + /** + * Returns the character, word or sentence after the specified index. The + * <code>part</code> parameter determines what is returned, the character, + * word or sentence after the index. + * + * @param part one of {@link AccessibleText#CHARACTER}, + * {@link AccessibleText#WORD} or + * {@link AccessibleText#SENTENCE}, specifying what is returned + * @param index the index + * + * @return the character, word or sentence after <code>index</code> + */ + public String getAfterIndex(int part, int index) { - return null; // TODO + String result = ""; + int startIndex = -1; + int endIndex = -1; + switch(part) + { + case AccessibleText.CHARACTER: + result = String.valueOf(text.charAt(index + 1)); + break; + case AccessibleText.WORD: + startIndex = text.indexOf(' ', index); + endIndex = text.indexOf(' ', startIndex + 1); + if (endIndex == -1) + endIndex = startIndex + 1; + result = text.substring(startIndex + 1, endIndex); + break; + case AccessibleText.SENTENCE: + default: + startIndex = text.indexOf('.', index); + endIndex = text.indexOf('.', startIndex + 1); + if (endIndex == -1) + endIndex = startIndex + 1; + result = text.substring(startIndex + 1, endIndex); + break; + } + return result; } - public String getBeforeIndex(int value0, int value1) - throws NotImplementedException + /** + * Returns the character, word or sentence before the specified index. The + * <code>part</code> parameter determines what is returned, the character, + * word or sentence before the index. + * + * @param part one of {@link AccessibleText#CHARACTER}, + * {@link AccessibleText#WORD} or + * {@link AccessibleText#SENTENCE}, specifying what is returned + * @param index the index + * + * @return the character, word or sentence before <code>index</code> + */ + public String getBeforeIndex(int part, int index) { - return null; // TODO + String result = ""; + int startIndex = -1; + int endIndex = -1; + switch(part) + { + case AccessibleText.CHARACTER: + result = String.valueOf(text.charAt(index - 1)); + break; + case AccessibleText.WORD: + endIndex = text.lastIndexOf(' ', index); + if (endIndex == -1) + endIndex = 0; + startIndex = text.lastIndexOf(' ', endIndex - 1); + result = text.substring(startIndex + 1, endIndex); + break; + case AccessibleText.SENTENCE: + default: + endIndex = text.lastIndexOf('.', index); + if (endIndex == -1) + endIndex = 0; + startIndex = text.lastIndexOf('.', endIndex - 1); + result = text.substring(startIndex + 1, endIndex); + break; + } + return result; } /** @@ -837,7 +943,14 @@ public abstract class AbstractButton extends JComponent View view = (View) getClientProperty(BasicHTML.propertyKey); if (view != null) { - + Document doc = view.getDocument(); + if (doc instanceof StyledDocument) + { + StyledDocument sDoc = (StyledDocument) doc; + Element charEl = sDoc.getCharacterElement(i); + if (charEl != null) + atts = charEl.getAttributes(); + } } return atts; } diff --git a/javax/swing/DefaultComboBoxModel.java b/javax/swing/DefaultComboBoxModel.java index 34af3db87..9b5bdb60d 100644 --- a/javax/swing/DefaultComboBoxModel.java +++ b/javax/swing/DefaultComboBoxModel.java @@ -224,18 +224,26 @@ public class DefaultComboBoxModel extends AbstractListModel */ public void setSelectedItem(Object object) { - if (selectedItem == null) - { - if (object == null) - return; - } - else - { - if (selectedItem.equals(object)) - return; - } + // No item is selected and object is null, so no change required. + if (selectedItem == null && object == null) + return; + + // object is already selected so no change required. + if (selectedItem != null && selectedItem.equals(object)) + return; + + // Simply return if object is not in the list. + if (object != null && getIndexOf(object) == -1) + return; + + // Here we know that object is either an item in the list or null. + + // Handle the three change cases: selectedItem is null, object is + // non-null; selectedItem is non-null, object is null; + // selectedItem is non-null, object is non-null and they're not + // equal. selectedItem = object; - fireContentsChanged(this, -1, -1); + fireContentsChanged(this, -1, -1); } /** diff --git a/javax/swing/JComponent.java b/javax/swing/JComponent.java index 0b4631f6c..3b8f1f68c 100644 --- a/javax/swing/JComponent.java +++ b/javax/swing/JComponent.java @@ -541,14 +541,6 @@ public abstract class JComponent extends Container implements Serializable */ Border border; - /** - * The text to show in the tooltip associated with this component. - * - * @see #setToolTipText - * @see #getToolTipText() - */ - String toolTipText; - /** * The popup menu for the component. * @@ -1439,14 +1431,12 @@ public abstract class JComponent extends Container implements Serializable { JToolTip toolTip = new JToolTip(); toolTip.setComponent(this); - toolTip.setTipText(toolTipText); - return toolTip; } /** - * Return the location at which the {@link #toolTipText} property should be - * displayed, when triggered by a particular mouse event. + * Return the location at which the <code>toolTipText</code> property should + * be displayed, when triggered by a particular mouse event. * * @param event The event the tooltip is being presented in response to * @@ -1459,53 +1449,56 @@ public abstract class JComponent extends Container implements Serializable } /** - * Set the value of the {@link #toolTipText} property. + * Set the tooltip text for this component. If a non-<code>null</code> + * value is set, this component is registered in the + * <code>ToolTipManager</code> in order to turn on tooltips for this + * component. If a <code>null</code> value is set, tooltips are turne off + * for this component. * - * @param text The new property value + * @param text the tooltip text for this component * * @see #getToolTipText() + * @see #getToolTipText(MouseEvent) */ public void setToolTipText(String text) { + String old = getToolTipText(); + putClientProperty(TOOL_TIP_TEXT_KEY, text); + ToolTipManager ttm = ToolTipManager.sharedInstance(); if (text == null) - { - ToolTipManager.sharedInstance().unregisterComponent(this); - toolTipText = null; - return; - } - - // XXX: The tip text doesn't get updated unless you set it to null - // and then to something not-null. This is consistent with the behaviour - // of Sun's ToolTipManager. - - String oldText = toolTipText; - toolTipText = text; - - if (oldText == null) - ToolTipManager.sharedInstance().registerComponent(this); + ttm.unregisterComponent(this); + else if (old == null) + ttm.registerComponent(this); } /** - * Get the value of the {@link #toolTipText} property. + * Returns the current tooltip text for this component, or <code>null</code> + * if none has been set. * - * @return The current property value + * @return the current tooltip text for this component, or <code>null</code> + * if none has been set * * @see #setToolTipText + * @see #getToolTipText(MouseEvent) */ public String getToolTipText() { - return toolTipText; + return (String) getClientProperty(TOOL_TIP_TEXT_KEY); } /** - * Get the value of the {@link #toolTipText} property, in response to a - * particular mouse event. + * Returns the tooltip text for this component for a particular mouse + * event. This can be used to support context sensitive tooltips that can + * change with the mouse location. By default this returns the static + * tooltip text returned by {@link #getToolTipText()}. * - * @param event The mouse event which triggered the tooltip + * @param event the mouse event which triggered the tooltip * - * @return The current property value + * @return the tooltip text for this component for a particular mouse + * event * * @see #setToolTipText + * @see #getToolTipText() */ public String getToolTipText(MouseEvent event) { @@ -2188,6 +2181,10 @@ public abstract class JComponent extends Container implements Serializable components.add(c); if (! onTop && jc != null && ! jc.isOptimizedDrawingEnabled()) { + // Indicates whether we reset the paint root to be the current + // component. + boolean updatePaintRoot = false; + // Check obscured state of the child. // Generally, we have 3 cases here: // 1. Not obscured. No need to paint from the parent. @@ -2195,23 +2192,32 @@ public abstract class JComponent extends Container implements Serializable // 3. Completely obscured. No need to paint anything. if (c != this) { - int count = c.getComponentCount(); - int i = 0; - for (; i < count && c.getComponent(i) != child; i++); + if (jc.isPaintRoot()) + updatePaintRoot = true; + else + { + int count = c.getComponentCount(); + int i = 0; + for (; i < count && c.getComponent(i) != child; i++); - if (jc.isCompletelyObscured(i, paintX, paintY, paintW, paintH)) - return; // No need to paint anything. - else if (jc.isPartiallyObscured(i, paintX, paintY, paintW, + if (jc.isCompletelyObscured(i, paintX, paintY, paintW, paintH)) - { - // Paint from parent. - paintRoot = jc; - pIndex = pCount; - offsX = 0; - offsY = 0; - haveBuffer = false; + return; // No need to paint anything. + else if (jc.isPartiallyObscured(i, paintX, paintY, paintW, + paintH)) + updatePaintRoot = true; + } } + if (updatePaintRoot) + { + // Paint from parent. + paintRoot = jc; + pIndex = pCount; + offsX = 0; + offsY = 0; + haveBuffer = false; + } } pCount++; // Check if component is double buffered. @@ -2257,8 +2263,7 @@ public abstract class JComponent extends Container implements Serializable // Actually trigger painting. if (haveBuffer) - paintRoot.paintDoubleBuffered(paintX, paintY, paintW, - paintH); + paintRoot.paintDoubleBuffered(paintX, paintY, paintW, paintH); else { Graphics g = paintRoot.getGraphics(); @@ -2303,6 +2308,19 @@ public abstract class JComponent extends Container implements Serializable } /** + * This returns true when a component needs to force itself as a paint + * origin. This is used for example in JViewport to make sure that it + * gets to update its backbuffer. + * + * @return true when a component needs to force itself as a paint + * origin + */ + boolean isPaintRoot() + { + return false; + } + + /** * Performs double buffered repainting. */ private void paintDoubleBuffered(int x, int y, int w, int h) diff --git a/javax/swing/JDialog.java b/javax/swing/JDialog.java index 08dada2fd..495c9c791 100644 --- a/javax/swing/JDialog.java +++ b/javax/swing/JDialog.java @@ -38,6 +38,7 @@ exception statement from your version. */ package javax.swing; +import java.awt.AWTEvent; import java.awt.Component; import java.awt.Container; import java.awt.Dialog; @@ -97,7 +98,7 @@ public class JDialog extends Dialog implements Accessible, WindowConstants, protected boolean rootPaneCheckingEnabled = false; /** The default action taken when closed. */ - private int close_action = HIDE_ON_CLOSE; + private int closeAction = HIDE_ON_CLOSE; /** Whether JDialogs are decorated by the Look and Feel. */ private static boolean decorated; @@ -245,6 +246,10 @@ public class JDialog extends Dialog implements Accessible, WindowConstants, */ protected void dialogInit() { + // We need to explicitly enable events here so that our processKeyEvent() + // and processWindowEvent() gets called. + enableEvents(AWTEvent.WINDOW_EVENT_MASK); + // FIXME: Do a check on GraphicsEnvironment.isHeadless() setLocale(JComponent.getDefaultLocale()); getRootPane(); // Will do set/create. @@ -507,37 +512,23 @@ public class JDialog extends Dialog implements Accessible, WindowConstants, */ protected void processWindowEvent(WindowEvent e) { - // System.out.println("PROCESS_WIN_EV-1: " + e); super.processWindowEvent(e); - // System.out.println("PROCESS_WIN_EV-2: " + e); - switch (e.getID()) + if (e.getID() == WindowEvent.WINDOW_CLOSING) { - case WindowEvent.WINDOW_CLOSING: - { - switch (getDefaultCloseOperation()) - { - case DISPOSE_ON_CLOSE: - { - dispose(); - break; - } - case HIDE_ON_CLOSE: - { - setVisible(false); - break; - } - case DO_NOTHING_ON_CLOSE: - break; - } - break; - } - case WindowEvent.WINDOW_CLOSED: - case WindowEvent.WINDOW_OPENED: - case WindowEvent.WINDOW_ICONIFIED: - case WindowEvent.WINDOW_DEICONIFIED: - case WindowEvent.WINDOW_ACTIVATED: - case WindowEvent.WINDOW_DEACTIVATED: - break; + switch (closeAction) + { + case EXIT_ON_CLOSE: + System.exit(0); + break; + case DISPOSE_ON_CLOSE: + dispose(); + break; + case HIDE_ON_CLOSE: + setVisible(false); + break; + case DO_NOTHING_ON_CLOSE: + break; + } } } @@ -554,7 +545,7 @@ public class JDialog extends Dialog implements Accessible, WindowConstants, must return the invalid code, and the behaviour defaults to DO_NOTHING_ON_CLOSE. processWindowEvent above handles this */ - close_action = operation; + closeAction = operation; } /** @@ -565,7 +556,7 @@ public class JDialog extends Dialog implements Accessible, WindowConstants, */ public int getDefaultCloseOperation() { - return close_action; + return closeAction; } /** diff --git a/javax/swing/JEditorPane.java b/javax/swing/JEditorPane.java index a5efa07df..06844355a 100644 --- a/javax/swing/JEditorPane.java +++ b/javax/swing/JEditorPane.java @@ -47,6 +47,7 @@ import java.io.Reader; import java.io.StringReader; import java.net.MalformedURLException; import java.net.URL; +import java.net.URLConnection; import java.util.HashMap; import javax.accessibility.AccessibleContext; @@ -508,7 +509,6 @@ public class JEditorPane extends JTextComponent private static final long serialVersionUID = 3140472492599046285L; - private URL page; private EditorKit editorKit; boolean focus_root; @@ -762,13 +762,19 @@ public class JEditorPane extends JTextComponent public URL getPage() { - return page; + return (URL) getDocument().getProperty(Document.StreamDescriptionProperty); } protected InputStream getStream(URL page) throws IOException { - return page.openStream(); + URLConnection conn = page.openConnection(); + // Try to detect the content type of the stream data. + String type = conn.getContentType(); + if (type != null) + setContentType(type); + InputStream stream = conn.getInputStream(); + return stream; } public String getText() @@ -799,10 +805,12 @@ public class JEditorPane extends JTextComponent EditorKit kit = getEditorKit(); if (kit instanceof HTMLEditorKit && desc instanceof HTMLDocument) { - Document doc = (Document) desc; + HTMLDocument doc = (HTMLDocument) desc; + setDocument(doc); try { - kit.read(in, doc, 0); + InputStreamReader reader = new InputStreamReader(in); + kit.read(reader, doc, 0); } catch (BadLocationException ex) { @@ -921,15 +929,16 @@ public class JEditorPane extends JTextComponent if (page == null) throw new IOException("invalid url"); - try - { - this.page = page; - getEditorKit().read(page.openStream(), getDocument(), 0); - } - catch (BadLocationException e) + URL old = getPage();; + InputStream in = getStream(page); + if (editorKit != null) { - // Ignored. '0' is always a valid offset. + Document doc = editorKit.createDefaultDocument(); + doc.putProperty(Document.StreamDescriptionProperty, page); + read(in, doc); + setDocument(doc); } + firePropertyChange("page", old, page); } /** diff --git a/javax/swing/JFrame.java b/javax/swing/JFrame.java index 1371525dd..0ae23f101 100644 --- a/javax/swing/JFrame.java +++ b/javax/swing/JFrame.java @@ -157,6 +157,10 @@ public class JFrame extends Frame protected void frameInit() { + // We need to explicitly enable events here so that our processKeyEvent() + // and processWindowEvent() gets called. + enableEvents(AWTEvent.WINDOW_EVENT_MASK | AWTEvent.KEY_EVENT_MASK); + super.setLayout(new BorderLayout()); setBackground(UIManager.getDefaults().getColor("control")); enableEvents(AWTEvent.WINDOW_EVENT_MASK); @@ -351,39 +355,22 @@ public class JFrame extends Frame protected void processWindowEvent(WindowEvent e) { super.processWindowEvent(e); - switch (e.getID()) + if (e.getID() == WindowEvent.WINDOW_CLOSING) { - case WindowEvent.WINDOW_CLOSING: - { - switch (closeAction) - { - case EXIT_ON_CLOSE: - { - System.exit(0); - break; - } - case DISPOSE_ON_CLOSE: - { - dispose(); - break; - } - case HIDE_ON_CLOSE: - { - setVisible(false); - break; - } - case DO_NOTHING_ON_CLOSE: - break; - } - break; - } - case WindowEvent.WINDOW_CLOSED: - case WindowEvent.WINDOW_OPENED: - case WindowEvent.WINDOW_ICONIFIED: - case WindowEvent.WINDOW_DEICONIFIED: - case WindowEvent.WINDOW_ACTIVATED: - case WindowEvent.WINDOW_DEACTIVATED: - break; + switch (closeAction) + { + case EXIT_ON_CLOSE: + System.exit(0); + break; + case DISPOSE_ON_CLOSE: + dispose(); + break; + case HIDE_ON_CLOSE: + setVisible(false); + break; + case DO_NOTHING_ON_CLOSE: + break; + } } } diff --git a/javax/swing/JLabel.java b/javax/swing/JLabel.java index fcf0fd7cb..3e0f28ed7 100644 --- a/javax/swing/JLabel.java +++ b/javax/swing/JLabel.java @@ -38,13 +38,14 @@ exception statement from your version. */ package javax.swing; -import gnu.classpath.NotImplementedException; - import java.awt.Component; import java.awt.Font; +import java.awt.FontMetrics; import java.awt.Image; +import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; +import java.awt.Shape; import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; @@ -54,8 +55,12 @@ import javax.accessibility.AccessibleExtendedComponent; import javax.accessibility.AccessibleRole; import javax.accessibility.AccessibleText; import javax.swing.plaf.LabelUI; +import javax.swing.plaf.basic.BasicHTML; import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.Position; import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.View; /** * A component that displays a static text message and/or an icon. @@ -303,10 +308,52 @@ public class JLabel extends JComponent implements Accessible, SwingConstants * @return the bounding box of the character at the specified index */ public Rectangle getCharacterBounds(int index) - throws NotImplementedException { - // FIXME: Implement this correctly. - return new Rectangle(); + Rectangle bounds = null; + View view = (View) getClientProperty(BasicHTML.propertyKey); + if (view != null) + { + Rectangle textR = getTextRectangle(); + try + { + Shape s = view.modelToView(index, textR, Position.Bias.Forward); + bounds = s.getBounds(); + } + catch (BadLocationException ex) + { + // Can't return something reasonable in this case. + } + } + return bounds; + } + + /** + * Returns the rectangle inside the JLabel, in which the actual text is + * rendered. This method has been adopted from the Mauve testcase + * gnu.testlet.javax.swing.JLabel.AccessibleJLabel.getCharacterBounds. + * + * @return the rectangle inside the JLabel, in which the actual text is + * rendered + */ + private Rectangle getTextRectangle() + { + JLabel l = JLabel.this; + Rectangle textR = new Rectangle(); + Rectangle iconR = new Rectangle(); + Insets i = l.getInsets(); + int w = l.getWidth(); + int h = l.getHeight(); + Rectangle viewR = new Rectangle(i.left, i.top, w - i.left - i.right, + h - i.top - i.bottom); + FontMetrics fm = l.getFontMetrics(l.getFont()); + SwingUtilities.layoutCompoundLabel(l, fm, l.getText(), l.getIcon(), + l.getVerticalAlignment(), + l.getHorizontalAlignment(), + l.getVerticalTextPosition(), + l.getHorizontalTextPosition(), + viewR, iconR, textR, + l.getIconTextGap()); + return textR; } /** @@ -319,10 +366,15 @@ public class JLabel extends JComponent implements Accessible, SwingConstants * point */ public int getIndexAtPoint(Point point) - throws NotImplementedException { - // FIXME: Implement this correctly. - return 0; + int index = -1; + View view = (View) getClientProperty(BasicHTML.propertyKey); + if (view != null) + { + Rectangle r = getTextRectangle(); + index = view.viewToModel(point.x, point.y, r, new Position.Bias[0]); + } + return index; } } diff --git a/javax/swing/JMenuItem.java b/javax/swing/JMenuItem.java index 4231e4fa6..ffdccdcef 100644 --- a/javax/swing/JMenuItem.java +++ b/javax/swing/JMenuItem.java @@ -39,7 +39,6 @@ exception statement from your version. */ package javax.swing; import java.awt.Component; -import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; @@ -77,6 +76,11 @@ public class JMenuItem extends AbstractButton implements Accessible, private KeyStroke accelerator; /** + * Indicates if we are currently dragging the mouse. + */ + private boolean isDragging; + + /** * Creates a new JMenuItem object. */ public JMenuItem() @@ -319,71 +323,21 @@ public class JMenuItem extends AbstractButton implements Accessible, /** * Process mouse events forwarded from MenuSelectionManager. * - * @param event event forwarded from MenuSelectionManager + * @param ev event forwarded from MenuSelectionManager * @param path path to the menu element from which event was generated * @param manager MenuSelectionManager for the current menu hierarchy */ - public void processMouseEvent(MouseEvent event, MenuElement[] path, + public void processMouseEvent(MouseEvent ev, MenuElement[] path, MenuSelectionManager manager) { - // Fire MenuDragMouseEvents if mouse is being dragged. - boolean dragged - = (event.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) != 0; - if (dragged) - processMenuDragMouseEvent(createMenuDragMouseEvent(event, path, manager)); - - switch (event.getID()) - { - case MouseEvent.MOUSE_CLICKED: - break; - case MouseEvent.MOUSE_ENTERED: - if (isRolloverEnabled()) - model.setRollover(true); - break; - case MouseEvent.MOUSE_EXITED: - if (isRolloverEnabled()) - model.setRollover(false); - - // for JMenu last element on the path is its popupMenu. - // JMenu shouldn't me disarmed. - if (! (path[path.length - 1] instanceof JPopupMenu) && ! dragged) - setArmed(false); - break; - case MouseEvent.MOUSE_PRESSED: - if ((event.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) != 0) - { - model.setArmed(true); - model.setPressed(true); - } - break; - case MouseEvent.MOUSE_RELEASED: - break; - case MouseEvent.MOUSE_MOVED: - break; - case MouseEvent.MOUSE_DRAGGED: - break; - } - } - - /** - * Creates MenuDragMouseEvent. - * - * @param event MouseEvent that occured while mouse was pressed. - * @param path Path the the menu element where the dragging event was - * originated - * @param manager MenuSelectionManager for the current menu hierarchy. - * - * @return new MenuDragMouseEvent - */ - private MenuDragMouseEvent createMenuDragMouseEvent(MouseEvent event, - MenuElement[] path, - MenuSelectionManager manager) - { - return new MenuDragMouseEvent((Component) event.getSource(), - event.getID(), event.getWhen(), - event.getModifiers(), event.getX(), - event.getY(), event.getClickCount(), - event.isPopupTrigger(), path, manager); + MenuDragMouseEvent e = new MenuDragMouseEvent(ev.getComponent(), + ev.getID(), ev.getWhen(), + ev.getModifiers(), ev.getX(), + ev.getY(), + ev.getClickCount(), + ev.isPopupTrigger(), path, + manager); + processMenuDragMouseEvent(e); } /** @@ -419,16 +373,20 @@ public class JMenuItem extends AbstractButton implements Accessible, switch (event.getID()) { case MouseEvent.MOUSE_ENTERED: + isDragging = false; fireMenuDragMouseEntered(event); break; case MouseEvent.MOUSE_EXITED: + isDragging = false; fireMenuDragMouseExited(event); break; case MouseEvent.MOUSE_DRAGGED: + isDragging = true; fireMenuDragMouseDragged(event); break; case MouseEvent.MOUSE_RELEASED: - fireMenuDragMouseReleased(event); + if (isDragging) + fireMenuDragMouseReleased(event); break; } } diff --git a/javax/swing/JScrollPane.java b/javax/swing/JScrollPane.java index 45df1d919..f6d37c7b4 100644 --- a/javax/swing/JScrollPane.java +++ b/javax/swing/JScrollPane.java @@ -161,9 +161,10 @@ public class JScrollPane extends JComponent protected int verticalScrollBarPolicy; protected JViewport viewport; - - Border viewportBorder; - boolean wheelScrollingEnabled; + + private Border viewportBorder; + + private boolean wheelScrollingEnabled; public JViewport getColumnHeader() { @@ -595,6 +596,7 @@ public class JScrollPane extends JComponent */ public JScrollPane(Component view, int vsbPolicy, int hsbPolicy) { + wheelScrollingEnabled = true; setVerticalScrollBarPolicy(vsbPolicy); setVerticalScrollBar(createVerticalScrollBar()); setHorizontalScrollBarPolicy(hsbPolicy); diff --git a/javax/swing/JSlider.java b/javax/swing/JSlider.java index 8a06d4f01..91eec4751 100644 --- a/javax/swing/JSlider.java +++ b/javax/swing/JSlider.java @@ -38,7 +38,6 @@ exception statement from your version. */ package javax.swing; -import java.awt.Dimension; import java.awt.MenuContainer; import java.awt.image.ImageObserver; import java.beans.PropertyChangeEvent; @@ -56,6 +55,7 @@ import javax.accessibility.AccessibleValue; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.plaf.SliderUI; +import javax.swing.plaf.UIResource; /** * A visual component that allows selection of a value within a @@ -112,6 +112,22 @@ public class JSlider extends JComponent implements SwingConstants, Accessible, ImageObserver, MenuContainer, Serializable { + + /** + * A little testing shows that the reference implementation creates + * labels from a class named LabelUIResource. + */ + private class LabelUIResource + extends JLabel + implements UIResource + { + LabelUIResource(String text, int align) + { + super(text, align); + setName("Slider.label"); + } + } + private static final long serialVersionUID = -1441275936141218479L; /** @@ -425,6 +441,7 @@ public class JSlider extends JComponent implements SwingConstants, Accessible, */ public void updateUI() { + updateLabelUIs(); setUI((SliderUI) UIManager.getUI(this)); } @@ -721,6 +738,7 @@ public class JSlider extends JComponent implements SwingConstants, Accessible, int oldOrientation = this.orientation; this.orientation = orientation; firePropertyChange("orientation", oldOrientation, this.orientation); + revalidate(); } } @@ -751,7 +769,10 @@ public class JSlider extends JComponent implements SwingConstants, Accessible, { Dictionary oldTable = labelTable; labelTable = table; + updateLabelUIs(); firePropertyChange("labelTable", oldTable, labelTable); + revalidate(); + repaint(); } } @@ -761,12 +782,18 @@ public class JSlider extends JComponent implements SwingConstants, Accessible, */ protected void updateLabelUIs() { - if (labelTable == null) - return; - for (Enumeration list = labelTable.elements(); list.hasMoreElements();) + if (labelTable != null) { - JLabel label = (JLabel) list.nextElement(); - label.updateUI(); + for (Enumeration list = labelTable.elements(); list.hasMoreElements();) + { + Object o = (JLabel) list.nextElement(); + if (o instanceof JComponent) + { + JComponent jc = (JComponent) o; + jc.updateUI(); + jc.setSize(jc.getPreferredSize()); + } + } } } @@ -810,23 +837,11 @@ public class JSlider extends JComponent implements SwingConstants, Accessible, if (start < getMinimum() || start > getMaximum()) throw new IllegalArgumentException("The 'start' value is out of range."); Hashtable table = new Hashtable(); - JLabel label; - Dimension dim; - - int max = sliderModel.getMaximum(); - + int max = getMaximum(); for (int i = start; i <= max; i += increment) { - label = new JLabel(String.valueOf(i)); - label.setVerticalAlignment(CENTER); - label.setHorizontalAlignment(CENTER); - - // Make sure these labels have the width and height - // they want. - dim = label.getPreferredSize(); - label.setBounds(label.getX(), label.getY(), - (int) dim.getWidth(), - (int) dim.getHeight()); + LabelUIResource label = new LabelUIResource(String.valueOf(i), + JLabel.CENTER); table.put(new Integer(i), label); } return table; @@ -867,6 +882,7 @@ public class JSlider extends JComponent implements SwingConstants, Accessible, boolean oldInverted = isInverted; isInverted = inverted; firePropertyChange("inverted", oldInverted, isInverted); + repaint(); } } @@ -898,7 +914,11 @@ public class JSlider extends JComponent implements SwingConstants, Accessible, { int oldSpacing = majorTickSpacing; majorTickSpacing = spacing; + if (labelTable == null && majorTickSpacing > 0 && getPaintLabels()) + setLabelTable(createStandardLabels(majorTickSpacing)); firePropertyChange("majorTickSpacing", oldSpacing, majorTickSpacing); + if (getPaintTicks()) + repaint(); } } @@ -932,6 +952,8 @@ public class JSlider extends JComponent implements SwingConstants, Accessible, int oldSpacing = minorTickSpacing; minorTickSpacing = spacing; firePropertyChange("minorTickSpacing", oldSpacing, minorTickSpacing); + if (getPaintTicks()) + repaint(); } } @@ -1001,6 +1023,8 @@ public class JSlider extends JComponent implements SwingConstants, Accessible, boolean oldPaintTicks = paintTicks; paintTicks = paint; firePropertyChange("paintTicks", oldPaintTicks, paintTicks); + revalidate(); + repaint(); } } @@ -1031,6 +1055,7 @@ public class JSlider extends JComponent implements SwingConstants, Accessible, { paintTrack = paint; firePropertyChange("paintTrack", !paint, paint); + repaint(); } } @@ -1062,8 +1087,10 @@ public class JSlider extends JComponent implements SwingConstants, Accessible, { paintLabels = paint; if (paint && majorTickSpacing > 0 && labelTable == null) - labelTable = createStandardLabels(majorTickSpacing); + setLabelTable(createStandardLabels(majorTickSpacing)); firePropertyChange("paintLabels", !paint, paint); + revalidate(); + repaint(); } } diff --git a/javax/swing/JSplitPane.java b/javax/swing/JSplitPane.java index 5b77f5176..fcdc1c041 100644 --- a/javax/swing/JSplitPane.java +++ b/javax/swing/JSplitPane.java @@ -247,6 +247,11 @@ public class JSplitPane extends JComponent implements Accessible /** The component on the right or bottom. */ protected Component rightComponent; + /** + * The divider location. + */ + private int dividerLocation; + /** Determines how extra space should be allocated. */ private transient double resizeWeight; @@ -288,7 +293,7 @@ public class JSplitPane extends JComponent implements Accessible continuousLayout = newContinuousLayout; setLeftComponent(newLeftComponent); setRightComponent(newRightComponent); - + dividerLocation = -1; updateUI(); } @@ -355,10 +360,6 @@ public class JSplitPane extends JComponent implements Accessible */ protected void addImpl(Component comp, Object constraints, int index) { - int left = 0; - int right = 1; - int div = 2; - int place; if (constraints == null) { if (leftComponent == null) @@ -431,10 +432,7 @@ public class JSplitPane extends JComponent implements Accessible */ public int getDividerLocation() { - if (ui != null) - return ((SplitPaneUI) ui).getDividerLocation(this); - else - return -1; + return dividerLocation; } /** @@ -722,17 +720,13 @@ public class JSplitPane extends JComponent implements Accessible */ public void setDividerLocation(int location) { - if (ui != null && location != getDividerLocation()) - { - int oldLocation = getDividerLocation(); - if (location < 0) - ((SplitPaneUI) ui).resetToPreferredSizes(this); - else - ((SplitPaneUI) ui).setDividerLocation(this, location); - - firePropertyChange(DIVIDER_LOCATION_PROPERTY, oldLocation, - getDividerLocation()); - } + int oldLocation = dividerLocation; + dividerLocation = location; + SplitPaneUI ui = getUI(); + if (ui != null) + ui.setDividerLocation(this, location); + firePropertyChange(DIVIDER_LOCATION_PROPERTY, oldLocation, + location); } /** diff --git a/javax/swing/JTree.java b/javax/swing/JTree.java index 7d093e3d0..c6f08b49c 100644 --- a/javax/swing/JTree.java +++ b/javax/swing/JTree.java @@ -1279,13 +1279,6 @@ public class JTree extends JComponent implements Scrollable, Accessible TreeSelectionEvent rewritten = (TreeSelectionEvent) ev.cloneWithSource(JTree.this); fireValueChanged(rewritten); - - // Only repaint the changed nodes. - TreePath[] changed = ev.getPaths(); - for (int i = 0; i < changed.length; i++) - { - repaint(getPathBounds(changed[i])); - } } } @@ -1698,7 +1691,7 @@ public class JTree extends JComponent implements Scrollable, Accessible if (direction < 0) delta = Math.max(0, visibleRect.y - b.y); else - delta = b.y + b.height - visibleRect.height; + delta = b.y + b.height - visibleRect.y; } else { @@ -2433,9 +2426,19 @@ public class JTree extends JComponent implements Scrollable, Accessible return selectionModel.isPathSelected(path); } + /** + * Returns <code>true</code> when the specified row is selected, + * <code>false</code> otherwise. This call is delegated to the + * {@link TreeSelectionModel#isRowSelected(int)} method. + * + * @param row the row to check + * + * @return <code>true</code> when the specified row is selected, + * <code>false</code> otherwise + */ public boolean isRowSelected(int row) { - return selectionModel.isPathSelected(getPathForRow(row)); + return selectionModel.isRowSelected(row); } public boolean isSelectionEmpty() diff --git a/javax/swing/JViewport.java b/javax/swing/JViewport.java index 9fd14e80a..d90da1d15 100644 --- a/javax/swing/JViewport.java +++ b/javax/swing/JViewport.java @@ -837,10 +837,13 @@ public class JViewport extends JComponent implements Accessible if (canBlit) { // Copy the part that remains visible during scrolling. - g2.copyArea(cachedBlitFrom.x, cachedBlitFrom.y, - cachedBlitSize.width, cachedBlitSize.height, - cachedBlitTo.x - cachedBlitFrom.x, - cachedBlitTo.y - cachedBlitFrom.y); + if (cachedBlitSize.width > 0 && cachedBlitSize.height > 0) + { + g2.copyArea(cachedBlitFrom.x, cachedBlitFrom.y, + cachedBlitSize.width, cachedBlitSize.height, + cachedBlitTo.x - cachedBlitFrom.x, + cachedBlitTo.y - cachedBlitFrom.y); + } // Now paint the part that becomes newly visible. g2.setClip(cachedBlitPaint.x, cachedBlitPaint.y, cachedBlitPaint.width, cachedBlitPaint.height); @@ -888,10 +891,13 @@ public class JViewport extends JComponent implements Accessible if (canBlit && isPaintRoot) { // Copy the part that remains visible during scrolling. - g.copyArea(cachedBlitFrom.x, cachedBlitFrom.y, - cachedBlitSize.width, cachedBlitSize.height, - cachedBlitTo.x - cachedBlitFrom.x, - cachedBlitTo.y - cachedBlitFrom.y); + if (cachedBlitSize.width > 0 && cachedBlitSize.width > 0) + { + g.copyArea(cachedBlitFrom.x, cachedBlitFrom.y, + cachedBlitSize.width, cachedBlitSize.height, + cachedBlitTo.x - cachedBlitFrom.x, + cachedBlitTo.y - cachedBlitFrom.y); + } // Now paint the part that becomes newly visible. Shape oldClip = g.getClip(); g.clipRect(cachedBlitPaint.x, cachedBlitPaint.y, @@ -926,4 +932,13 @@ public class JViewport extends JComponent implements Accessible super.paintImmediately2(x, y, w, h); isPaintRoot = false; } + + /** + * Returns true when the JViewport is using a backbuffer, so that we + * can update our backbuffer correctly. + */ + boolean isPaintRoot() + { + return scrollMode == BACKINGSTORE_SCROLL_MODE; + } } diff --git a/javax/swing/JWindow.java b/javax/swing/JWindow.java index 19d830ed1..b36b8cf2a 100644 --- a/javax/swing/JWindow.java +++ b/javax/swing/JWindow.java @@ -38,6 +38,7 @@ exception statement from your version. */ package javax.swing; +import java.awt.AWTEvent; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Container; @@ -158,6 +159,10 @@ public class JWindow extends Window implements Accessible, RootPaneContainer protected void windowInit() { + // We need to explicitly enable events here so that our processKeyEvent() + // and processWindowEvent() gets called. + enableEvents(AWTEvent.KEY_EVENT_MASK); + super.setLayout(new BorderLayout(1, 1)); getRootPane(); // will do set/create // Now we're done init stage, adds and layouts go to content pane. diff --git a/javax/swing/RepaintManager.java b/javax/swing/RepaintManager.java index fa374a34f..afed7ec8e 100644 --- a/javax/swing/RepaintManager.java +++ b/javax/swing/RepaintManager.java @@ -69,6 +69,7 @@ import java.util.WeakHashMap; * <p>See <a * href="http://java.sun.com/products/jfc/tsc/articles/painting/index.html">this * document</a> for more details.</p> + * document</a> for more details.</p> * * @author Roman Kennke (kennke@aicas.com) * @author Graydon Hoare (graydon@redhat.com) @@ -96,9 +97,21 @@ public class RepaintManager * @param source the source * @param runnable the runnable to execute */ - public RepaintWorkerEvent(Object source, Runnable runnable) + public RepaintWorkerEvent(Object source, Runnable runnable, + Object notifier, boolean catchEx) + { + super(source, runnable, notifier, catchEx); + } + + /** + * An application that I met implements its own event dispatching and + * calls dispatch() via reflection, and only checks declared methods, + * that is, it expects this method to be in the event's class, not + * in a superclass. So I put this in here... sigh. + */ + public void dispatch() { - super(source, runnable); + super.dispatch(); } } @@ -419,15 +432,16 @@ public class RepaintManager if (! rectCache.isEmpty()) { - if (dirtyComponents.containsKey(component)) - { - SwingUtilities.computeUnion(rectCache.x, rectCache.y, - rectCache.width, rectCache.height, - (Rectangle) dirtyComponents.get(component)); - } - else + synchronized (dirtyComponents) { - synchronized (dirtyComponents) + Rectangle dirtyRect = (Rectangle)dirtyComponents.get(component); + if (dirtyRect != null) + { + SwingUtilities.computeUnion(rectCache.x, rectCache.y, + rectCache.width, rectCache.height, + dirtyRect); + } + else { dirtyComponents.put(component, rectCache.getBounds()); } @@ -838,7 +852,7 @@ public class RepaintManager { Toolkit tk = Toolkit.getDefaultToolkit(); EventQueue evQueue = tk.getSystemEventQueue(); - InvocationEvent ev = new RepaintWorkerEvent(this, runnable); + InvocationEvent ev = new RepaintWorkerEvent(evQueue, runnable, null, false); evQueue.postEvent(ev); } } diff --git a/javax/swing/ToolTipManager.java b/javax/swing/ToolTipManager.java index 963ccf881..152fc0343 100644 --- a/javax/swing/ToolTipManager.java +++ b/javax/swing/ToolTipManager.java @@ -163,16 +163,21 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener private static ToolTipManager shared; /** The current component the tooltip is being displayed for. */ - private static Component currentComponent; + private JComponent currentComponent; /** The current tooltip. */ - private static JToolTip currentTip; + private JToolTip currentTip; + + /** + * The tooltip text. + */ + private String toolTipText; /** The last known position of the mouse cursor. */ - private static Point currentPoint; - + private Point currentPoint; + /** */ - private static Popup popup; + private Popup popup; /** * Creates a new ToolTipManager and sets up the timers. @@ -364,8 +369,8 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener return; currentPoint = event.getPoint(); - currentComponent = (Component) event.getSource(); - + currentComponent = (JComponent) event.getSource(); + toolTipText = currentComponent.getToolTipText(event); if (exitTimer.isRunning()) { exitTimer.stop(); @@ -443,8 +448,52 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener public void mouseMoved(MouseEvent event) { currentPoint = event.getPoint(); - if (enterTimer.isRunning()) - enterTimer.restart(); + if (currentTip != null && currentTip.isShowing()) + checkTipUpdate(event); + else + { + if (enterTimer.isRunning()) + enterTimer.restart(); + } + } + + /** + * Checks if the tooltip's text or location changes when the mouse is moved + * over the component. + */ + private void checkTipUpdate(MouseEvent ev) + { + JComponent comp = (JComponent) ev.getSource(); + String newText = comp.getToolTipText(ev); + String oldText = toolTipText; + if (newText != null) + { + if (((newText != null && newText.equals(oldText)) || newText == null)) + { + // No change at all. Restart timers. + if (popup == null) + enterTimer.restart(); + else + insideTimer.restart(); + } + else + { + // Update the tooltip. + toolTipText = newText; + hideTip(); + showTip(); + exitTimer.stop(); + } + } + else + { + // Hide tooltip. + currentTip = null; + currentPoint = null; + hideTip(); + enterTimer.stop(); + exitTimer.stop(); + } } /** @@ -461,9 +510,9 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener return; } - if (currentTip == null || currentTip.getComponent() != currentComponent - && currentComponent instanceof JComponent) - currentTip = ((JComponent) currentComponent).createToolTip(); + if (currentTip == null || currentTip.getComponent() != currentComponent) + currentTip = currentComponent.createToolTip(); + currentTip.setTipText(toolTipText); Point p = currentPoint; Point cP = currentComponent.getLocationOnScreen(); @@ -531,8 +580,8 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener private Component getContentPaneDeepestComponent(MouseEvent e) { Component source = (Component) e.getSource(); - Container parent = (Container) SwingUtilities.getAncestorOfClass(JRootPane.class, - currentComponent); + Container parent = SwingUtilities.getAncestorOfClass(JRootPane.class, + currentComponent); if (parent == null) return null; parent = ((JRootPane) parent).getContentPane(); diff --git a/javax/swing/TransferHandler.java b/javax/swing/TransferHandler.java index 40a36b27d..d594a8244 100644 --- a/javax/swing/TransferHandler.java +++ b/javax/swing/TransferHandler.java @@ -44,12 +44,117 @@ import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.event.ActionEvent; import java.awt.event.InputEvent; +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.io.IOException; import java.io.Serializable; +import java.lang.reflect.Method; public class TransferHandler implements Serializable { + + /** + * An implementation of {@link Transferable} that can be used to export + * data from a component's property. + */ + private static class PropertyTransferable + implements Transferable + { + /** + * The component from which we export. + */ + private JComponent component; + + /** + * The property descriptor of the property that we handle. + */ + private PropertyDescriptor property; + + /** + * Creates a new PropertyTransferable. + * + * @param c the component from which we export + * @param prop the property from which we export + */ + PropertyTransferable(JComponent c, PropertyDescriptor prop) + { + component = c; + property = prop; + } + + /** + * Returns the data flavors supported by the Transferable. + * + * @return the data flavors supported by the Transferable + */ + public DataFlavor[] getTransferDataFlavors() + { + DataFlavor[] flavors; + Class propClass = property.getPropertyType(); + String mime = DataFlavor.javaJVMLocalObjectMimeType + "; class=" + + propClass.getName(); + try + { + DataFlavor flavor = new DataFlavor(mime); + flavors = new DataFlavor[]{ flavor }; + } + catch (ClassNotFoundException ex) + { + flavors = new DataFlavor[0]; + } + return flavors; + } + + /** + * Returns <code>true</code> when the specified data flavor is supported, + * <code>false</code> otherwise. + * + * @return <code>true</code> when the specified data flavor is supported, + * <code>false</code> otherwise + */ + public boolean isDataFlavorSupported(DataFlavor flavor) + { + Class propClass = property.getPropertyType(); + return flavor.getPrimaryType().equals("application") + && flavor.getSubType().equals("x-java-jvm-local-objectref") + && propClass.isAssignableFrom(flavor.getRepresentationClass()); + } + + /** + * Returns the actual transfer data. + * + * @param flavor the data flavor + * + * @return the actual transfer data + */ + public Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException, IOException + { + if (isDataFlavorSupported(flavor)) + { + Method getter = property.getReadMethod(); + Object o; + try + { + o = getter.invoke(component, null); + return o; + } + catch (Exception ex) + { + throw new IOException("Property read failed: " + + property.getName()); + } + } + else + throw new UnsupportedFlavorException(flavor); + } + } + static class TransferAction extends AbstractAction { private String command; @@ -123,7 +228,13 @@ public class TransferHandler implements Serializable private int sourceActions; private Icon visualRepresentation; - + + /** + * The name of the property into/from which this TransferHandler + * imports/exports. + */ + private String propertyName; + public static Action getCopyAction() { return copyAction; @@ -146,19 +257,78 @@ public class TransferHandler implements Serializable public TransferHandler(String property) { + propertyName = property; this.sourceActions = property != null ? COPY : NONE; } + /** + * Returns <code>true</code> if the data in this TransferHandler can be + * imported into the specified component. This will be the case when: + * <ul> + * <li>The component has a readable and writable property with the property + * name specified in the TransferHandler constructor.</li> + * <li>There is a dataflavor with a mime type of + * <code>application/x-java-jvm-local-object-ref</code>.</li> + * <li>The dataflavor's representation class matches the class of the + * property in the component.</li> + * </li> + * + * @param c the component to check + * @param flavors the possible data flavors + * + * @return <code>true</code> if the data in this TransferHandler can be + * imported into the specified component, <code>false</code> + * otherwise + */ public boolean canImport(JComponent c, DataFlavor[] flavors) - throws NotImplementedException { - return false; + PropertyDescriptor propDesc = getPropertyDescriptor(c); + boolean canImport = false; + if (propDesc != null) + { + // Check if the property is writable. The readable check is already + // done in getPropertyDescriptor(). + Method writer = propDesc.getWriteMethod(); + if (writer != null) + { + Class[] params = writer.getParameterTypes(); + if (params.length == 1) + { + // Number of parameters ok, now check mime type and + // representation class. + DataFlavor flavor = getPropertyDataFlavor(params[0], flavors); + if (flavor != null) + canImport = true; + } + } + } + return canImport; } + /** + * Creates a {@link Transferable} that can be used to export data + * from the specified component. + * + * This method returns <code>null</code> when the specified component + * doesn't have a readable property that matches the property name + * specified in the <code>TransferHandler</code> constructor. + * + * @param c the component to create a transferable for + * + * @return a {@link Transferable} that can be used to export data + * from the specified component, or null if the component doesn't + * have a readable property like the transfer handler + */ protected Transferable createTransferable(JComponent c) - throws NotImplementedException { - return null; + Transferable transferable = null; + if (propertyName != null) + { + PropertyDescriptor prop = getPropertyDescriptor(c); + if (prop != null) + transferable = new PropertyTransferable(c, prop); + } + return transferable; } public void exportAsDrag(JComponent c, InputEvent e, int action) @@ -167,16 +337,64 @@ public class TransferHandler implements Serializable // TODO: Implement this properly } - protected void exportDone(JComponent c, Transferable data, int action) - throws NotImplementedException + /** + * This method is invoked after data has been exported. + * Subclasses should implement this method to remove the data that has been + * transferred when the action was <code>MOVE</code>. + * + * The default implementation does nothing because MOVE is not supported. + * + * @param c the source component + * @param data the data that has been transferred or <code>null</code> + * when the action is NONE + * @param action the action that has been performed + */ + protected void exportDone(JComponent c, Transferable data, int action) { - // TODO: Implement this properly + // Nothing to do in the default implementation. } + /** + * Exports the property of the component <code>c</code> that was + * specified for this TransferHandler to the clipboard, performing + * the specified action. + * + * This will check if the action is allowed by calling + * {@link #getSourceActions(JComponent)}. If the action is not allowed, + * then no export is performed. + * + * In either case the method {@link #exportDone} will be called with + * the action that has been performed, or {@link #NONE} if the action + * was not allowed or could otherwise not be completed. + * Any IllegalStateException that is thrown by the Clipboard due to + * beeing unavailable will be propagated through this method. + * + * @param c the component from which to export + * @param clip the clipboard to which the data will be exported + * @param action the action to perform + * + * @throws IllegalStateException when the clipboard is not available + */ public void exportToClipboard(JComponent c, Clipboard clip, int action) - throws NotImplementedException + throws IllegalStateException { - // TODO: Implement this properly + action &= getSourceActions(c); + Transferable transferable = createTransferable(c); + if (transferable != null && action != NONE) + { + try + { + clip.setContents(transferable, null); + exportDone(c, transferable, action); + } + catch (IllegalStateException ex) + { + exportDone(c, transferable, NONE); + throw ex; + } + } + else + exportDone(c, null, NONE); } public int getSourceActions(JComponent c) @@ -189,9 +407,124 @@ public class TransferHandler implements Serializable return visualRepresentation; } + /** + * Imports the transfer data represented by <code>t</code> into the specified + * component <code>c</code> by setting the property of this TransferHandler + * on that component. If this succeeds, this method returns + * <code>true</code>, otherwise <code>false</code>. + * + * + * @param c the component to import into + * @param t the transfer data to import + * + * @return <code>true</code> if the transfer succeeds, <code>false</code> + * otherwise + */ public boolean importData(JComponent c, Transferable t) - throws NotImplementedException { - return false; + boolean ok = false; + PropertyDescriptor prop = getPropertyDescriptor(c); + if (prop != null) + { + Method writer = prop.getWriteMethod(); + if (writer != null) + { + Class[] params = writer.getParameterTypes(); + if (params.length == 1) + { + DataFlavor flavor = getPropertyDataFlavor(params[0], + t.getTransferDataFlavors()); + if (flavor != null) + { + try + { + Object value = t.getTransferData(flavor); + writer.invoke(c, new Object[]{ value }); + ok = true; + } + catch (Exception ex) + { + // If anything goes wrong here, do nothing and return + // false; + } + } + } + } + } + return ok; + } + + /** + * Returns the property descriptor for the property of this TransferHandler + * in the specified component, or <code>null</code> if no such property + * exists in the component. This method only returns properties that are + * at least readable (that is, it has a public no-arg getter method). + * + * @param c the component to check + * + * @return the property descriptor for the property of this TransferHandler + * in the specified component, or <code>null</code> if no such + * property exists in the component + */ + private PropertyDescriptor getPropertyDescriptor(JComponent c) + { + PropertyDescriptor prop = null; + if (propertyName != null) + { + Class clazz = c.getClass(); + BeanInfo beanInfo; + try + { + beanInfo = Introspector.getBeanInfo(clazz); + } + catch (IntrospectionException ex) + { + beanInfo = null; + } + if (beanInfo != null) + { + PropertyDescriptor[] props = beanInfo.getPropertyDescriptors(); + for (int i = 0; i < props.length && prop == null; i++) + { + PropertyDescriptor desc = props[i]; + if (desc.getName().equals(propertyName)) + { + Method reader = desc.getReadMethod(); + if (reader != null) + { + Class[] params = reader.getParameterTypes(); + if (params == null || params.length == 0) + prop = desc; + } + } + } + } + } + return prop; + } + + /** + * Searches <code>flavors</code> to find a suitable data flavor that + * has the mime type application/x-java-jvm-local-objectref and a + * representation class that is the same as the specified <code>clazz</code>. + * When no such data flavor is found, this returns <code>null</code>. + * + * @param clazz the representation class required for the data flavor + * @param flavors the possible data flavors + * + * @return the suitable data flavor or null if none is found + */ + private DataFlavor getPropertyDataFlavor(Class clazz, DataFlavor[] flavors) + { + DataFlavor found = null; + for (int i = 0; i < flavors.length && found == null; i++) + { + DataFlavor flavor = flavors[i]; + if (flavor.getPrimaryType().equals("application") + && flavor.getSubType().equals("x-java-jvm-local-objectref") + && clazz.isAssignableFrom(flavor.getRepresentationClass())) + found = flavor; + } + return found; } } diff --git a/javax/swing/filechooser/FileSystemView.java b/javax/swing/filechooser/FileSystemView.java index 26ca4860c..41d865a96 100644 --- a/javax/swing/filechooser/FileSystemView.java +++ b/javax/swing/filechooser/FileSystemView.java @@ -37,8 +37,6 @@ exception statement from your version. */ package javax.swing.filechooser; -import gnu.classpath.NotImplementedException; - import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -171,7 +169,6 @@ public abstract class FileSystemView * @return A default {@link FileSystemView} appropriate for the platform. */ public static FileSystemView getFileSystemView() - throws NotImplementedException { if (defaultFileSystemView == null) { diff --git a/javax/swing/plaf/basic/BasicLookAndFeel.java b/javax/swing/plaf/basic/BasicLookAndFeel.java index 76d67b002..154309454 100644 --- a/javax/swing/plaf/basic/BasicLookAndFeel.java +++ b/javax/swing/plaf/basic/BasicLookAndFeel.java @@ -1062,8 +1062,7 @@ public abstract class BasicLookAndFeel extends LookAndFeel "ProgressBar.repaintInterval", new Integer(50), "ProgressBar.cycleTime", new Integer(3000), "RadioButton.background", new ColorUIResource(light), - "RadioButton.border", new BorderUIResource.CompoundBorderUIResource(null, - null), + "RadioButton.border", BasicBorders.getRadioButtonBorder(), "RadioButton.darkShadow", new ColorUIResource(shadow), "RadioButton.focusInputMap", new UIDefaults.LazyInputMap(new Object[] { KeyStroke.getKeyStroke("SPACE"), "pressed", @@ -1183,6 +1182,10 @@ public abstract class BasicLookAndFeel extends LookAndFeel "Slider.thumbHeight", new Integer(20), "Slider.thumbWidth", new Integer(11), "Slider.tickHeight", new Integer(12), + "Slider.horizontalSize", new Dimension(200, 21), + "Slider.verticalSize", new Dimension(21, 200), + "Slider.minimumHorizontalSize", new Dimension(36, 21), + "Slider.minimumVerticalSize", new Dimension(21, 36), "Spinner.background", new ColorUIResource(light), "Spinner.foreground", new ColorUIResource(light), "Spinner.arrowButtonSize", new DimensionUIResource(16, 5), diff --git a/javax/swing/plaf/basic/BasicMenuItemUI.java b/javax/swing/plaf/basic/BasicMenuItemUI.java index 87dce2ef4..5fafb4108 100644 --- a/javax/swing/plaf/basic/BasicMenuItemUI.java +++ b/javax/swing/plaf/basic/BasicMenuItemUI.java @@ -364,7 +364,7 @@ public class BasicMenuItemUI extends MenuItemUI */ protected void doClick(MenuSelectionManager msm) { - menuItem.doClick(); + menuItem.doClick(0); msm.clearSelectedPath(); } @@ -1058,15 +1058,14 @@ public class BasicMenuItemUI extends MenuItemUI */ public void mouseReleased(MouseEvent e) { - Rectangle size = menuItem.getBounds(); MenuSelectionManager manager = MenuSelectionManager.defaultManager(); - if (e.getX() > 0 && e.getX() < size.width && e.getY() > 0 - && e.getY() < size.height) + int x = e.getX(); + int y = e.getY(); + if (x > 0 && x < menuItem.getWidth() && y > 0 + && y < menuItem.getHeight()) { - manager.clearSelectedPath(); - menuItem.doClick(); + doClick(manager); } - else manager.processMouseEvent(e); } @@ -1085,7 +1084,7 @@ public class BasicMenuItemUI extends MenuItemUI */ public void menuDragMouseDragged(MenuDragMouseEvent e) { - MenuSelectionManager manager = MenuSelectionManager.defaultManager(); + MenuSelectionManager manager = e.getMenuSelectionManager(); manager.setSelectedPath(e.getPath()); } @@ -1098,7 +1097,7 @@ public class BasicMenuItemUI extends MenuItemUI */ public void menuDragMouseEntered(MenuDragMouseEvent e) { - MenuSelectionManager manager = MenuSelectionManager.defaultManager(); + MenuSelectionManager manager = e.getMenuSelectionManager(); manager.setSelectedPath(e.getPath()); } @@ -1110,7 +1109,7 @@ public class BasicMenuItemUI extends MenuItemUI */ public void menuDragMouseExited(MenuDragMouseEvent e) { - // TODO: What should be done here, if anything? + // Nothing to do here yet. } /** @@ -1122,13 +1121,14 @@ public class BasicMenuItemUI extends MenuItemUI */ public void menuDragMouseReleased(MenuDragMouseEvent e) { - MenuElement[] path = e.getPath(); - - if (path[path.length - 1] instanceof JMenuItem) - ((JMenuItem) path[path.length - 1]).doClick(); - - MenuSelectionManager manager = MenuSelectionManager.defaultManager(); - manager.clearSelectedPath(); + MenuSelectionManager manager = e.getMenuSelectionManager(); + int x = e.getX(); + int y = e.getY(); + if (x >= 0 && x < menuItem.getWidth() && y >= 0 + && y < menuItem.getHeight()) + doClick(manager); + else + manager.clearSelectedPath(); } } diff --git a/javax/swing/plaf/basic/BasicRadioButtonUI.java b/javax/swing/plaf/basic/BasicRadioButtonUI.java index fb84cf443..bfb9e98db 100644 --- a/javax/swing/plaf/basic/BasicRadioButtonUI.java +++ b/javax/swing/plaf/basic/BasicRadioButtonUI.java @@ -52,6 +52,7 @@ import javax.swing.JComponent; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; +import javax.swing.text.View; /** * The BasicLookAndFeel UI implementation for @@ -129,52 +130,92 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI public void paint(Graphics g, JComponent c) { AbstractButton b = (AbstractButton) c; - + Dimension size = c.getSize(); Insets i = b.getInsets(); - Rectangle tr = textR; textR.x = 0; textR.y = 0; textR.width = 0; textR.height = 0; - Rectangle ir = iconR; iconR.x = 0; iconR.y = 0; iconR.width = 0; iconR.height = 0; - Rectangle vr = viewR; viewR.x = i.left; viewR.y = i.right; - viewR.width = b.getWidth() - i.left - i.right; - viewR.height = b.getHeight() - i.top - i.bottom; + viewR.width = size.width - i.left - i.right; + viewR.height = size.height - i.top - i.bottom; Font f = c.getFont(); g.setFont(f); ButtonModel m = b.getModel(); - // FIXME: Do a filtering on any customized icon if the following property - // is set. - boolean enabled = b.isEnabled(); - - Icon currentIcon = b.getIcon(); - if (currentIcon == null) - { - currentIcon = getDefaultIcon(); - } - + // This is the icon that we use for layout. + Icon icon = b.getIcon(); + if (icon == null) + icon = getDefaultIcon(); + + // Do the layout. String text = SwingUtilities.layoutCompoundLabel(c, g.getFontMetrics(f), - b.getText(), currentIcon, + b.getText(), icon, b.getVerticalAlignment(), b.getHorizontalAlignment(), b.getVerticalTextPosition(), b.getHorizontalTextPosition(), - vr, ir, tr, b.getIconTextGap() + defaultTextShiftOffset); + viewR, iconR, textR, b.getIconTextGap()); - currentIcon.paintIcon(c, g, ir.x, ir.y); - + // Figure out the correct icon. + icon = b.getIcon(); + if (icon == null) + icon = getDefaultIcon(); + else + { + if (! m.isEnabled()) + { + if (m.isSelected()) + icon = b.getDisabledSelectedIcon(); + else + icon = b.getDisabledIcon(); + } + else if (m.isArmed() && m.isPressed()) + { + icon = b.getPressedIcon(); + if (icon == null) + icon = b.getSelectedIcon(); + } + else if (m.isSelected()) + { + if (b.isRolloverEnabled() && m.isRollover()) + { + icon = b.getRolloverSelectedIcon(); + if (icon == null) + icon = b.getSelectedIcon(); + } + else + icon = b.getSelectedIcon(); + } + else if (b.isRolloverEnabled() && m.isRollover()) + icon = b.getRolloverIcon(); + if (icon == null) + icon = b.getIcon(); + } + // .. and paint it. + icon.paintIcon(c, g, iconR.x, iconR.y); + + // Paint text and focus indicator. if (text != null) - paintText(g, b, tr, text); - if (b.hasFocus() && b.isFocusPainted() && m.isEnabled()) - paintFocus(g, tr, c.getSize()); + { + // Maybe render HTML in the radio button. + View v = (View) c.getClientProperty(BasicHTML.propertyKey); + if (v != null) + v.paint(g, textR); + else + paintText(g, b, textR, text); + + // Paint focus indicator if necessary. + if (b.hasFocus() && b.isFocusPainted() + && textR.width > 0 && textR.height > 0) + paintFocus(g, textR, size); + } } public Dimension getPreferredSize(JComponent c) @@ -207,17 +248,14 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI viewR.width = Short.MAX_VALUE; viewR.height = Short.MAX_VALUE; - SwingUtilities.layoutCompoundLabel( - b, // for the component orientation - b.getFontMetrics(b.getFont()), - b.getText(), - i, - b.getVerticalAlignment(), - b.getHorizontalAlignment(), - b.getVerticalTextPosition(), - b.getHorizontalTextPosition(), - viewR, iconR, textR, - defaultTextIconGap + defaultTextShiftOffset); + SwingUtilities.layoutCompoundLabel(b, // for the component orientation + b.getFontMetrics(b.getFont()), + text, i, b.getVerticalAlignment(), + b.getHorizontalAlignment(), + b.getVerticalTextPosition(), + b.getHorizontalTextPosition(), + viewR, iconR, textR, + text == null ? 0 : b.getIconTextGap()); Rectangle r = SwingUtilities.computeUnion(textR.x, textR.y, textR.width, textR.height, iconR); diff --git a/javax/swing/plaf/basic/BasicScrollBarUI.java b/javax/swing/plaf/basic/BasicScrollBarUI.java index 5205724b5..400ede03c 100644 --- a/javax/swing/plaf/basic/BasicScrollBarUI.java +++ b/javax/swing/plaf/basic/BasicScrollBarUI.java @@ -1225,12 +1225,36 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ protected void scrollByBlock(int direction) { + scrollByBlock(scrollbar, direction); + } + + /** + * Scrolls the specified <code>scrollBar</code> by one block (according + * to the scrollable protocol) in the specified <code>direction</code>. + * + * This method is here statically to support wheel scrolling from the + * BasicScrollPaneUI without code duplication. + * + * @param scrollBar the scrollbar to scroll + * @param direction the scroll direction + */ + static final void scrollByBlock(JScrollBar scrollBar, int direction) + { + int delta; if (direction > 0) - scrollbar.setValue(scrollbar.getValue() - + scrollbar.getBlockIncrement(direction)); + delta = scrollBar.getBlockIncrement(direction); else - scrollbar.setValue(scrollbar.getValue() - - scrollbar.getBlockIncrement(direction)); + delta = - scrollBar.getBlockIncrement(direction); + int oldValue = scrollBar.getValue(); + int newValue = oldValue + delta; + + // Overflow check. + if (delta > 0 && newValue < oldValue) + newValue = scrollBar.getMaximum(); + else if (delta < 0 && newValue > oldValue) + newValue = scrollBar.getMinimum(); + + scrollBar.setValue(newValue); } /** @@ -1240,12 +1264,46 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ protected void scrollByUnit(int direction) { - if (direction > 0) - scrollbar.setValue(scrollbar.getValue() - + scrollbar.getUnitIncrement(direction)); - else - scrollbar.setValue(scrollbar.getValue() - - scrollbar.getUnitIncrement(direction)); + scrollByUnits(scrollbar, direction, 1); + } + + /** + * Scrolls the specified <code>scrollbac/code> by <code>units</code> units + * in the specified <code>direction</code>. + * + * This method is here statically to support wheel scrolling from the + * BasicScrollPaneUI without code duplication. + * + * @param scrollBar the scrollbar to scroll + * @param direction the direction + * @param units the number of units to scroll + */ + static final void scrollByUnits(JScrollBar scrollBar, int direction, + int units) + { + // Do this inside a loop so that we don't clash with the scrollable + // interface, which can return different units at times. For instance, + // a Scrollable could return a unit of 2 pixels only to adjust the + // visibility of an item. If we would simply multiply this by units, + // then we would only get 6 pixels, which is complete crap. + for (int i = 0; i < units; i++) + { + int delta; + if (direction > 0) + delta = scrollBar.getUnitIncrement(direction); + else + delta = - scrollBar.getUnitIncrement(direction); + int oldValue = scrollBar.getValue(); + int newValue = oldValue + delta; + + // Overflow check. + if (delta > 0 && newValue < oldValue) + newValue = scrollBar.getMaximum(); + else if (delta < 0 && newValue > oldValue) + newValue = scrollBar.getMinimum(); + + scrollBar.setValue(newValue); + } } /** diff --git a/javax/swing/plaf/basic/BasicScrollPaneUI.java b/javax/swing/plaf/basic/BasicScrollPaneUI.java index 236eac073..a71942840 100644 --- a/javax/swing/plaf/basic/BasicScrollPaneUI.java +++ b/javax/swing/plaf/basic/BasicScrollPaneUI.java @@ -38,7 +38,6 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Point; @@ -62,8 +61,6 @@ import javax.swing.JViewport; import javax.swing.LookAndFeel; import javax.swing.ScrollPaneConstants; import javax.swing.ScrollPaneLayout; -import javax.swing.Scrollable; -import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.border.Border; @@ -229,103 +226,24 @@ public class BasicScrollPaneUI extends ScrollPaneUI */ public void mouseWheelMoved(MouseWheelEvent e) { - if (scrollpane.getViewport().getComponentCount() == 0) - return; - - Component target = scrollpane.getViewport().getComponent(0); - JScrollBar bar = scrollpane.getVerticalScrollBar(); - Scrollable scrollable = (target instanceof Scrollable) ? (Scrollable) target - : null; - - boolean tracksHeight = scrollable != null - && scrollable.getScrollableTracksViewportHeight(); - int wheel = e.getWheelRotation() * ROWS_PER_WHEEL_CLICK; - int delta; - - // If possible, scroll vertically. - if (bar != null && ! tracksHeight) - { - if (scrollable != null) - { - bounds(target); - delta = scrollable.getScrollableUnitIncrement( - rect, SwingConstants.VERTICAL, wheel); - } - else - { - // Scroll non scrollables. - delta = wheel * SCROLL_NON_SCROLLABLES; - } - scroll(bar, wheel > 0 ? delta : -delta); - } - // If not, try to scroll horizontally - else + if (scrollpane.isWheelScrollingEnabled() && e.getScrollAmount() != 0) { - bar = scrollpane.getHorizontalScrollBar(); - boolean tracksWidth = scrollable != null - && scrollable.getScrollableTracksViewportWidth(); - - if (bar != null && ! tracksWidth) + // Try to scroll vertically first. + JScrollBar scrollBar = scrollpane.getVerticalScrollBar(); + if (scrollBar == null || ! scrollBar.isVisible()) + scrollBar = scrollpane.getHorizontalScrollBar(); + if (scrollBar != null && scrollBar.isVisible()) { - if (scrollable != null) - { - bounds(target); - delta = scrollable.getScrollableUnitIncrement( - rect, SwingConstants.HORIZONTAL, wheel); - } - else - { - // Scroll non scrollables. - delta = wheel * SCROLL_NON_SCROLLABLES; - } - scroll(bar, delta); + int direction = e.getWheelRotation() < 0 ? -1 : 1; + int scrollType = e.getScrollType(); + if (scrollType == MouseWheelEvent.WHEEL_UNIT_SCROLL) + BasicScrollBarUI.scrollByUnits(scrollBar, direction, + e.getScrollAmount()); + else if (scrollType == MouseWheelEvent.WHEEL_BLOCK_SCROLL) + BasicScrollBarUI.scrollByBlock(scrollBar, direction); } } } - - /** - * Place the component bounds into rect. The x and y values - * need to be reversed. - * - * @param target the target being scrolled - */ - final void bounds(Component target) - { - // Viewport bounds, translated by the scroll bar positions. - target.getParent().getBounds(rect); - rect.x = getValue(scrollpane.getHorizontalScrollBar()); - rect.y = getValue(scrollpane.getVerticalScrollBar()); - } - - /** - * Get the scroll bar value or 0 if there is no such scroll bar. - * - * @param bar the scroll bar (<code>null</code> permitted). - * - * @return The scroll bar value, or 0. - */ - final int getValue(JScrollBar bar) - { - return bar != null ? bar.getValue() : 0; - } - - /** - * Scroll the given distance. - * - * @param bar the scrollbar to scroll - * @param delta the distance - */ - final void scroll(JScrollBar bar, int delta) - { - int y = bar.getValue() + delta; - - if (y < bar.getMinimum()) - y = bar.getMinimum(); - if (y > bar.getMaximum()) - y = bar.getMaximum(); - - bar.setValue(y); - } } /** diff --git a/javax/swing/plaf/basic/BasicSliderUI.java b/javax/swing/plaf/basic/BasicSliderUI.java index 3811eebdf..474a42256 100644 --- a/javax/swing/plaf/basic/BasicSliderUI.java +++ b/javax/swing/plaf/basic/BasicSliderUI.java @@ -40,7 +40,6 @@ package javax.swing.plaf.basic; import java.awt.Color; import java.awt.Component; -import java.awt.ComponentOrientation; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Insets; @@ -65,7 +64,6 @@ import javax.swing.ActionMap; import javax.swing.BoundedRangeModel; import javax.swing.InputMap; import javax.swing.JComponent; -import javax.swing.JLabel; import javax.swing.JSlider; import javax.swing.LookAndFeel; import javax.swing.SwingUtilities; @@ -185,8 +183,6 @@ public class BasicSliderUI extends SliderUI public void componentResized(ComponentEvent e) { calculateGeometry(); - - slider.revalidate(); slider.repaint(); } } @@ -209,7 +205,6 @@ public class BasicSliderUI extends SliderUI public void focusGained(FocusEvent e) { slider.repaint(); - hasFocus = true; } /** @@ -221,7 +216,6 @@ public class BasicSliderUI extends SliderUI public void focusLost(FocusEvent e) { slider.repaint(); - hasFocus = false; } } @@ -240,25 +234,27 @@ public class BasicSliderUI extends SliderUI public void propertyChange(PropertyChangeEvent e) { // Check for orientation changes. - if (e.getPropertyName().equals("orientation")) - recalculateIfOrientationChanged(); + String prop = e.getPropertyName(); + if (prop.equals("orientation") + || prop.equals("inverted") + || prop.equals("labelTable") + || prop.equals("majorTickSpacing") + || prop.equals("minorTickSpacing") + || prop.equals("paintTicks") + || prop.equals("paintTrack") + || prop.equals("paintLabels")) + { + calculateGeometry(); + slider.repaint(); + } else if (e.getPropertyName().equals("model")) { BoundedRangeModel oldModel = (BoundedRangeModel) e.getOldValue(); oldModel.removeChangeListener(changeListener); slider.getModel().addChangeListener(changeListener); calculateThumbLocation(); + slider.repaint(); } - else if (e.getPropertyName().equals("paintTicks")) - calculateGeometry(); - - // elif the componentOrientation changes (this is a bound property, - // just undocumented) we change leftToRightCache. In Sun's - // implementation, the LTR cache changes on a repaint. This is strange - // since there is no need to do so. We could events here and - // update the cache. - // elif the border/insets change, we recalculateInsets. - slider.repaint(); } } @@ -466,6 +462,7 @@ public class BasicSliderUI extends SliderUI if (scrollTimer != null) scrollTimer.stop(); } + slider.repaint(); } /** @@ -592,10 +589,7 @@ public class BasicSliderUI extends SliderUI /** The focus color. */ private transient Color focusColor; - - /** True if the slider has focus. */ - private transient boolean hasFocus; - + /** True if the user is dragging the slider. */ boolean dragging; @@ -935,36 +929,10 @@ public class BasicSliderUI extends SliderUI */ public Dimension getPreferredHorizontalSize() { - Insets insets = slider.getInsets(); - - // The width should cover all the labels (which are usually the - // deciding factor of the width) - int width = getWidthOfWidestLabel() * (slider.getLabelTable() == null ? 0 - : slider.getLabelTable().size()); - - // If there are not enough labels. - // This number is pretty much arbitrary, but it looks nice. - if (width < 200) - width = 200; - - // We can only draw inside of the focusRectangle, so we have to - // pad it with insets. - width += insets.left + insets.right + focusInsets.left + focusInsets.right; - - // Height is determined by the thumb, the ticks and the labels. - int height = getThumbSize().height; - - if (slider.getPaintTicks() && slider.getMajorTickSpacing() > 0 - || slider.getMinorTickSpacing() > 0) - height += getTickLength(); - - if (slider.getPaintLabels()) - height += getHeightOfTallestLabel(); - - height += insets.top + insets.bottom + focusInsets.top - + focusInsets.bottom; - - return new Dimension(width, height); + Dimension dim = UIManager.getDimension("Slider.horizontalSize"); + if (dim == null) // Just to be sure we mirror the default. + dim = new Dimension(200, 21); + return dim; } /** @@ -975,30 +943,10 @@ public class BasicSliderUI extends SliderUI */ public Dimension getPreferredVerticalSize() { - Insets insets = slider.getInsets(); - - int height = getHeightOfTallestLabel() * (slider.getLabelTable() == null - ? 0 : slider.getLabelTable() - .size()); - - if (height < 200) - height = 200; - - height += insets.top + insets.bottom + focusInsets.top - + focusInsets.bottom; - - int width = getThumbSize().width; - - if (slider.getPaintTicks() && slider.getMajorTickSpacing() > 0 - || slider.getMinorTickSpacing() > 0) - width += getTickLength(); - - if (slider.getPaintLabels()) - width += getWidthOfWidestLabel(); - - width += insets.left + insets.right + focusInsets.left + focusInsets.right; - - return new Dimension(width, height); + Dimension dim = UIManager.getDimension("Slider.verticalSize"); + if (dim == null) // Just to be sure we mirror the default. + dim = new Dimension(21, 200); + return dim; } /** @@ -1009,21 +957,10 @@ public class BasicSliderUI extends SliderUI */ public Dimension getMinimumHorizontalSize() { - Insets insets = slider.getInsets(); - // Height is determined by the thumb, the ticks and the labels. - int height = getThumbSize().height; - - if (slider.getPaintTicks() && slider.getMajorTickSpacing() > 0 - || slider.getMinorTickSpacing() > 0) - height += getTickLength(); - - if (slider.getPaintLabels()) - height += getHeightOfTallestLabel(); - - height += insets.top + insets.bottom + focusInsets.top - + focusInsets.bottom; - - return new Dimension(36, height); + Dimension dim = UIManager.getDimension("Slider.minimumHorizontalSize"); + if (dim == null) // Just to be sure we mirror the default. + dim = new Dimension(36, 21); + return dim; } /** @@ -1034,19 +971,10 @@ public class BasicSliderUI extends SliderUI */ public Dimension getMinimumVerticalSize() { - Insets insets = slider.getInsets(); - int width = getThumbSize().width; - - if (slider.getPaintTicks() && slider.getMajorTickSpacing() > 0 - || slider.getMinorTickSpacing() > 0) - width += getTickLength(); - - if (slider.getPaintLabels()) - width += getWidthOfWidestLabel(); - - width += insets.left + insets.right + focusInsets.left + focusInsets.right; - - return new Dimension(width, 36); + Dimension dim = UIManager.getDimension("Slider.minimumVerticalSize"); + if (dim == null) // Just to be sure we mirror the default. + dim = new Dimension(21, 36); + return dim; } /** @@ -1060,10 +988,25 @@ public class BasicSliderUI extends SliderUI */ public Dimension getPreferredSize(JComponent c) { + recalculateIfInsetsChanged(); + Dimension dim; if (slider.getOrientation() == JSlider.HORIZONTAL) - return getPreferredHorizontalSize(); + { + // Create copy here to protect the UIManager value. + dim = new Dimension(getPreferredHorizontalSize()); + dim.height = insetCache.top + insetCache.bottom; + dim.height += focusInsets.top + focusInsets.bottom; + dim.height += trackRect.height + tickRect.height + labelRect.height; + } else - return getPreferredVerticalSize(); + { + // Create copy here to protect the UIManager value. + dim = new Dimension(getPreferredVerticalSize()); + dim.width = insetCache.left + insetCache.right; + dim.width += focusInsets.left + focusInsets.right; + dim.width += trackRect.width + tickRect.width + labelRect.width; + } + return dim; } /** @@ -1077,10 +1020,25 @@ public class BasicSliderUI extends SliderUI */ public Dimension getMinimumSize(JComponent c) { + recalculateIfInsetsChanged(); + Dimension dim; if (slider.getOrientation() == JSlider.HORIZONTAL) - return getMinimumHorizontalSize(); + { + // Create copy here to protect the UIManager value. + dim = new Dimension(getMinimumHorizontalSize()); + dim.height = insetCache.top + insetCache.bottom; + dim.height += focusInsets.top + focusInsets.bottom; + dim.height += trackRect.height + tickRect.height + labelRect.height; + } else - return getMinimumVerticalSize(); + { + // Create copy here to protect the UIManager value. + dim = new Dimension(getMinimumVerticalSize()); + dim.width = insetCache.left + insetCache.right; + dim.width += focusInsets.left + focusInsets.right; + dim.width += trackRect.width + tickRect.width + labelRect.width; + } + return dim; } /** @@ -1093,40 +1051,12 @@ public class BasicSliderUI extends SliderUI */ public Dimension getMaximumSize(JComponent c) { - Insets insets = slider.getInsets(); + Dimension dim = getPreferredSize(c); if (slider.getOrientation() == JSlider.HORIZONTAL) - { - // Height is determined by the thumb, the ticks and the labels. - int height = getThumbSize().height; - - if (slider.getPaintTicks() && slider.getMajorTickSpacing() > 0 - || slider.getMinorTickSpacing() > 0) - height += getTickLength(); - - if (slider.getPaintLabels()) - height += getHeightOfTallestLabel(); - - height += insets.top + insets.bottom + focusInsets.top - + focusInsets.bottom; - - return new Dimension(32767, height); - } + dim.width = Short.MAX_VALUE; else - { - int width = getThumbSize().width; - - if (slider.getPaintTicks() && slider.getMajorTickSpacing() > 0 - || slider.getMinorTickSpacing() > 0) - width += getTickLength(); - - if (slider.getPaintLabels()) - width += getWidthOfWidestLabel(); - - width += insets.left + insets.right + focusInsets.left - + focusInsets.right; - - return new Dimension(width, 32767); - } + dim.height = Short.MAX_VALUE; + return dim; } /** @@ -1151,12 +1081,10 @@ public class BasicSliderUI extends SliderUI */ protected void calculateFocusRect() { - insetCache = slider.getInsets(); - focusRect = SwingUtilities.calculateInnerArea(slider, focusRect); - if (focusRect.width < 0) - focusRect.width = 0; - if (focusRect.height < 0) - focusRect.height = 0; + focusRect.x = insetCache.left; + focusRect.y = insetCache.top; + focusRect.width = slider.getWidth() - insetCache.left - insetCache.right; + focusRect.height = slider.getHeight() - insetCache.top - insetCache.bottom; } /** @@ -1181,13 +1109,8 @@ public class BasicSliderUI extends SliderUI contentRect.y = focusRect.y + focusInsets.top; contentRect.width = focusRect.width - focusInsets.left - focusInsets.right; - contentRect.height = focusRect.height - focusInsets.top - - focusInsets.bottom; - - if (contentRect.width < 0) - contentRect.width = 0; - if (contentRect.height < 0) - contentRect.height = 0; + contentRect.height = focusRect.height - focusInsets.top + - focusInsets.bottom; } /** @@ -1258,26 +1181,24 @@ public class BasicSliderUI extends SliderUI { if (slider.getOrientation() == JSlider.HORIZONTAL) { - trackRect.x = contentRect.x + trackBuffer; - int h = getThumbSize().height; - if (slider.getPaintTicks() && (slider.getMajorTickSpacing() > 0 - || slider.getMinorTickSpacing() > 0)) - h += getTickLength(); + int center = thumbRect.height; + if (slider.getPaintTicks()) + center += getTickLength(); if (slider.getPaintLabels()) - h += getHeightOfTallestLabel(); - trackRect.y = contentRect.y + (contentRect.height - h) / 2 - 1; + center += getHeightOfTallestLabel(); + trackRect.x = contentRect.x + trackBuffer; + trackRect.y = contentRect.y + (contentRect.height - center - 1) / 2; trackRect.width = contentRect.width - 2 * trackBuffer; trackRect.height = thumbRect.height; } else { - int w = getThumbSize().width; - if (slider.getPaintTicks() && (slider.getMajorTickSpacing() > 0 - || slider.getMinorTickSpacing() > 0)) - w += getTickLength(); + int center = thumbRect.width; + if (slider.getPaintTicks()) + center += getTickLength(); if (slider.getPaintLabels()) - w += getWidthOfWidestLabel(); - trackRect.x = contentRect.x + (contentRect.width - w) / 2 - 1; + center += getWidthOfWidestLabel(); + trackRect.x = contentRect.x + (contentRect.width - center - 1) / 2; trackRect.y = contentRect.y + trackBuffer; trackRect.width = thumbRect.width; trackRect.height = contentRect.height - 2 * trackBuffer; @@ -1310,28 +1231,28 @@ public class BasicSliderUI extends SliderUI tickRect.x = trackRect.x; tickRect.y = trackRect.y + trackRect.height; tickRect.width = trackRect.width; - tickRect.height = slider.getPaintTicks() ? getTickLength() : 0; + tickRect.height = getTickLength(); // this makes our Mauve tests pass...can't explain it! if (!slider.getPaintTicks()) - tickRect.y--; - - if (tickRect.y + tickRect.height > contentRect.y + contentRect.height) - tickRect.height = contentRect.y + contentRect.height - tickRect.y; + { + tickRect.y--; + tickRect.height = 0; + } } else { tickRect.x = trackRect.x + trackRect.width; tickRect.y = trackRect.y; - tickRect.width = slider.getPaintTicks() ? getTickLength() : 0; + tickRect.width = getTickLength(); tickRect.height = trackRect.height; // this makes our Mauve tests pass...can't explain it! if (!slider.getPaintTicks()) - tickRect.x--; - - if (tickRect.x + tickRect.width > contentRect.x + contentRect.width) - tickRect.width = contentRect.x + contentRect.width - tickRect.x; + { + tickRect.x--; + tickRect.width = 0; + } } } @@ -1345,33 +1266,35 @@ public class BasicSliderUI extends SliderUI { if (slider.getPaintLabels()) { - labelRect.x = contentRect.x; - labelRect.y = tickRect.y + tickRect.height - 1; - labelRect.width = contentRect.width; + labelRect.x = tickRect.x - trackBuffer; + labelRect.y = tickRect.y + tickRect.height; + labelRect.width = tickRect.width + trackBuffer * 2; + labelRect.height = getHeightOfTallestLabel(); } else { - labelRect.x = trackRect.x; + labelRect.x = tickRect.x; labelRect.y = tickRect.y + tickRect.height; - labelRect.width = trackRect.width; + labelRect.width = tickRect.width; + labelRect.height = 0; } - labelRect.height = getHeightOfTallestLabel(); } else { if (slider.getPaintLabels()) { - labelRect.x = tickRect.x + tickRect.width - 1; - labelRect.y = contentRect.y; - labelRect.height = contentRect.height; + labelRect.x = tickRect.x + tickRect.width; + labelRect.y = tickRect.y - trackBuffer; + labelRect.width = getWidthOfWidestLabel(); + labelRect.height = tickRect.height + trackBuffer * 2; } else { labelRect.x = tickRect.x + tickRect.width; - labelRect.y = trackRect.y; - labelRect.height = trackRect.height; + labelRect.y = tickRect.y; + labelRect.width = 0; + labelRect.height = tickRect.height; } - labelRect.width = getWidthOfWidestLabel(); } } @@ -1384,22 +1307,15 @@ public class BasicSliderUI extends SliderUI protected int getWidthOfWidestLabel() { int widest = 0; - Component label; - - if (slider.getLabelTable() == null) - return 0; - - Dimension pref; - for (Enumeration list = slider.getLabelTable().elements(); - list.hasMoreElements();) + Dictionary table = slider.getLabelTable(); + if (table != null) { - Object comp = list.nextElement(); - if (! (comp instanceof Component)) - continue; - label = (Component) comp; - pref = label.getPreferredSize(); - if (pref != null && pref.width > widest) - widest = pref.width; + for (Enumeration list = slider.getLabelTable().elements(); + list.hasMoreElements();) + { + Component label = (Component) list.nextElement(); + widest = Math.max(label.getPreferredSize().width, widest); + } } return widest; } @@ -1576,23 +1492,18 @@ public class BasicSliderUI extends SliderUI */ public void paint(Graphics g, JComponent c) { - // FIXME: Move this to propertyChangeEvent handler, when we get those. - leftToRightCache = slider.getComponentOrientation() - != ComponentOrientation.RIGHT_TO_LEFT; - // FIXME: This next line is only here because the above line is here. - calculateGeometry(); - - if (slider.getPaintTrack()) + recalculateIfInsetsChanged(); + recalculateIfOrientationChanged(); + if (slider.getPaintTrack() && hitClip(g, trackRect)) paintTrack(g); - if (slider.getPaintTicks()) + if (slider.getPaintTicks() && hitClip(g, tickRect)) paintTicks(g); - if (slider.getPaintLabels()) + if (slider.getPaintLabels() && hitClip(g, labelRect)) paintLabels(g); - - paintThumb(g); - - if (hasFocus) + if (slider.hasFocus() && hitClip(g, focusRect)) paintFocus(g); + if (hitClip(g, thumbRect)) + paintThumb(g); } /** @@ -1601,18 +1512,12 @@ public class BasicSliderUI extends SliderUI */ protected void recalculateIfInsetsChanged() { - // Examining a test program shows that either Sun calls private - // methods that we don't know about, or these don't do anything. - calculateFocusRect(); - - calculateContentRect(); - calculateThumbSize(); - calculateTrackBuffer(); - calculateTrackRect(); - calculateThumbLocation(); - - calculateTickRect(); - calculateLabelRect(); + Insets insets = slider.getInsets(); + if (! insets.equals(insetCache)) + { + insetCache = insets; + calculateGeometry(); + } } /** @@ -1863,45 +1768,30 @@ public class BasicSliderUI extends SliderUI */ public void paintLabels(Graphics g) { - if (slider.getLabelTable() != null) + Dictionary table = slider.getLabelTable(); + if (table != null) { - Dictionary table = slider.getLabelTable(); - Integer tmpKey; - Object key; - Object element; - Component label; - if (slider.getOrientation() == JSlider.HORIZONTAL) - { - for (Enumeration list = table.keys(); list.hasMoreElements();) - { - key = list.nextElement(); - if (! (key instanceof Integer)) - continue; - tmpKey = (Integer) key; - element = table.get(tmpKey); - // We won't paint them if they're not - // JLabels so continue anyway - if (! (element instanceof JLabel)) - continue; - label = (Component) element; - paintHorizontalLabel(g, tmpKey.intValue(), label); - } - } - else + int min = slider.getMinimum(); + int max = slider.getMaximum(); + for (Enumeration list = table.keys(); list.hasMoreElements();) { - for (Enumeration list = table.keys(); list.hasMoreElements();) + Integer key = (Integer) list.nextElement(); + int value = key.intValue(); + if (value >= min && value <= max) { - key = list.nextElement(); - if (! (key instanceof Integer)) - continue; - tmpKey = (Integer) key; - element = table.get(tmpKey); - // We won't paint them if they're not - // JLabels so continue anyway - if (! (element instanceof JLabel)) - continue; - label = (Component) element; - paintVerticalLabel(g, tmpKey.intValue(), label); + Component label = (Component) table.get(key); + if (slider.getOrientation() == JSlider.HORIZONTAL) + { + g.translate(0, labelRect.y); + paintHorizontalLabel(g, value, label); + g.translate(0, -labelRect.y); + } + else + { + g.translate(labelRect.x, 0); + paintVerticalLabel(g, value, label); + g.translate(-labelRect.x, 0); + } } } } @@ -1920,51 +1810,11 @@ public class BasicSliderUI extends SliderUI */ protected void paintHorizontalLabel(Graphics g, int value, Component label) { - // This relies on clipping working properly or we'll end up - // painting all over the place. If our preferred size is ignored, then - // the labels may not fit inside the slider's bounds. Rather than mucking - // with font sizes and possible icon sizes, we'll set the bounds for - // the label and let it get clipped. - Dimension dim = label.getPreferredSize(); - int w = (int) dim.getWidth(); - int h = (int) dim.getHeight(); - - int max = slider.getMaximum(); - int min = slider.getMinimum(); - - if (value > max || value < min) - return; - - // value - // | - // ------------ - // | | - // | | - // | | - // The label must move w/2 to the right to fit directly under the value. - int xpos = xPositionForValue(value) - w / 2; - int ypos = labelRect.y; - - // We want to center the label around the xPositionForValue - // So we use xpos - w / 2. However, if value is min and the label - // is large, we run the risk of going out of bounds. So we bring it back - // to 0 if it becomes negative. - if (xpos < 0) - xpos = 0; - - // If the label + starting x position is greater than - // the x space in the label rectangle, we reset it to the largest - // amount possible in the rectangle. This means ugliness. - if (xpos + w > labelRect.x + labelRect.width) - w = labelRect.x + labelRect.width - xpos; - - // If the label is too tall. We reset it to the height of the label - // rectangle. - if (h > labelRect.height) - h = labelRect.height; - - label.setBounds(xpos, ypos, w, h); - SwingUtilities.paintComponent(g, label, null, label.getBounds()); + int center = xPositionForValue(value); + int left = center - label.getPreferredSize().width / 2; + g.translate(left, 0); + label.paint(g); + g.translate(-left, 0); } /** @@ -1980,30 +1830,11 @@ public class BasicSliderUI extends SliderUI */ protected void paintVerticalLabel(Graphics g, int value, Component label) { - Dimension dim = label.getPreferredSize(); - int w = (int) dim.getWidth(); - int h = (int) dim.getHeight(); - - int max = slider.getMaximum(); - int min = slider.getMinimum(); - - if (value > max || value < min) - return; - - int xpos = labelRect.x; - int ypos = yPositionForValue(value) - h / 2; - - if (ypos < 0) - ypos = 0; - - if (ypos + h > labelRect.y + labelRect.height) - h = labelRect.y + labelRect.height - ypos; - - if (w > labelRect.width) - w = labelRect.width; - - label.setBounds(xpos, ypos, w, h); - SwingUtilities.paintComponent(g, label, null, label.getBounds()); + int center = yPositionForValue(value); + int top = center - label.getPreferredSize().height / 2; + g.translate(0, top); + label.paint(g); + g.translate(0, -top); } /** @@ -2118,8 +1949,11 @@ public class BasicSliderUI extends SliderUI */ public void setThumbLocation(int x, int y) { - thumbRect.x = x; - thumbRect.y = y; + Rectangle union = new Rectangle(thumbRect); + thumbRect.setLocation(x, y); + SwingUtilities.computeUnion(thumbRect.x, thumbRect.y, thumbRect.width, + thumbRect.height, union); + slider.repaint(union); } /** @@ -2197,21 +2031,21 @@ public class BasicSliderUI extends SliderUI */ protected int xPositionForValue(int value) { - double min = slider.getMinimum(); - if (value < min) - value = (int) min; - double max = slider.getMaximum(); - if (value > max) - value = (int) max; - double len = trackRect.width; - if ((max - min) <= 0.0) - return 0; - int xPos = (int) ((value - min) / (max - min) * len + 0.5); - - if (drawInverted()) - return trackRect.x + Math.max(trackRect.width - xPos - 1, 0); + int min = slider.getMinimum(); + int max = slider.getMaximum(); + int len = trackRect.width; + double range = max - min; + double pixPerVal = len / range; + int left = trackRect.x; + int right = left + trackRect.width - 1; + int xpos; + if (! drawInverted()) + xpos = left + (int) Math.round(pixPerVal * ((double) value - min)); else - return trackRect.x + Math.min(xPos, trackRect.width - 1); + xpos = right - (int) Math.round(pixPerVal * ((double) value - min)); + xpos = Math.max(left, xpos); + xpos = Math.min(right, xpos); + return xpos; } /** @@ -2225,22 +2059,21 @@ public class BasicSliderUI extends SliderUI */ protected int yPositionForValue(int value) { - double min = slider.getMinimum(); - if (value < min) - value = (int) min; - double max = slider.getMaximum(); - if (value > max) - value = (int) max; + int min = slider.getMinimum(); + int max = slider.getMaximum(); int len = trackRect.height; - if ((max - min) <= 0.0) - return 0; - - int yPos = (int) ((value - min) / (max - min) * len + 0.5); - + double range = max - min; + double pixPerVal = len / range; + int top = trackRect.y; + int bottom = top + trackRect.height - 1; + int ypos; if (! drawInverted()) - return trackRect.y + trackRect.height - Math.max(yPos, 1); + ypos = top + (int) Math.round(pixPerVal * ((double) max - value)); else - return trackRect.y + Math.min(yPos, trackRect.height - 1); + ypos = top + (int) Math.round(pixPerVal * ((double) value - min)); + ypos = Math.max(top, ypos); + ypos = Math.min(bottom, ypos); + return ypos; } /** @@ -2494,4 +2327,13 @@ public class BasicSliderUI extends SliderUI ); return map; } + + /** + * Small utility method to save me from typing the hell out of myself in + * paint(). + */ + private boolean hitClip(Graphics g, Rectangle r) + { + return g.hitClip(r.x, r.y, r.width, r.height); + } } diff --git a/javax/swing/plaf/basic/BasicSplitPaneUI.java b/javax/swing/plaf/basic/BasicSplitPaneUI.java index 6ef4c08ce..b7cc42548 100644 --- a/javax/swing/plaf/basic/BasicSplitPaneUI.java +++ b/javax/swing/plaf/basic/BasicSplitPaneUI.java @@ -320,8 +320,17 @@ public class BasicSplitPaneUI extends SplitPaneUI Dimension dims = split.getSize(); int loc = getInitialLocation(insets); int available = getAvailableSize(dims, insets); - sizes[0] = getDividerLocation(split) - loc; + sizes[0] = split.getDividerLocation(); sizes[1] = available - sizes[0] - sizes[2]; + + // According to a Mauve test we only honour the minimum + // size of the components, when the dividerLocation hasn't + // been excplicitly set. + if (! dividerLocationSet) + { + sizes[0] = Math.max(sizes[0], minimumSizeOfComponent(0)); + sizes[1] = Math.max(sizes[1], minimumSizeOfComponent(1)); + } // The size of the divider won't change. // Layout component#1. @@ -363,7 +372,6 @@ public class BasicSplitPaneUI extends SplitPaneUI Dimension dim = new Dimension(); if (target instanceof JSplitPane) { - JSplitPane split = (JSplitPane) target; int primary = 0; int secondary = 0; for (int i = 0; i < components.length; i++) @@ -401,7 +409,6 @@ public class BasicSplitPaneUI extends SplitPaneUI Dimension dim = new Dimension(); if (target instanceof JSplitPane) { - JSplitPane split = (JSplitPane) target; int primary = 0; int secondary = 0; for (int i = 0; i < components.length; i++) @@ -460,8 +467,6 @@ public class BasicSplitPaneUI extends SplitPaneUI { for (int i = 0; i < components.length; i++) resetSizeAt(i); - setDividerLocation(splitPane, - getInitialLocation(splitPane.getInsets()) + sizes[0]); } /** @@ -857,7 +862,13 @@ public class BasicSplitPaneUI extends SplitPaneUI /** The JSplitPane that this UI draws. */ protected JSplitPane splitPane; - private int dividerLocation; + /** + * True, when setDividerLocation() has been called at least + * once on the JSplitPane, false otherwise. + * + * This is package private to avoid a synthetic accessor method. + */ + boolean dividerLocationSet; /** * Creates a new BasicSplitPaneUI object. @@ -889,6 +900,7 @@ public class BasicSplitPaneUI extends SplitPaneUI if (c instanceof JSplitPane) { splitPane = (JSplitPane) c; + dividerLocationSet = false; installDefaults(); installListeners(); installKeyboardActions(); @@ -906,6 +918,7 @@ public class BasicSplitPaneUI extends SplitPaneUI uninstallListeners(); uninstallDefaults(); + dividerLocationSet = false; splitPane = null; } @@ -1054,8 +1067,10 @@ public class BasicSplitPaneUI extends SplitPaneUI new AbstractAction("negativeIncrement") { public void actionPerformed(ActionEvent event) { - setDividerLocation(splitPane, Math.max(dividerLocation - - KEYBOARD_DIVIDER_MOVE_OFFSET, 0)); + int oldLoc = splitPane.getDividerLocation(); + int newLoc = + Math.max(oldLoc - KEYBOARD_DIVIDER_MOVE_OFFSET, 0); + splitPane.setDividerLocation(newLoc); } } ); @@ -1063,8 +1078,10 @@ public class BasicSplitPaneUI extends SplitPaneUI new AbstractAction("positiveIncrement") { public void actionPerformed(ActionEvent event) { - setDividerLocation(splitPane, dividerLocation - + KEYBOARD_DIVIDER_MOVE_OFFSET); + int oldLoc = splitPane.getDividerLocation(); + int newLoc = + Math.max(oldLoc + KEYBOARD_DIVIDER_MOVE_OFFSET, 0); + splitPane.setDividerLocation(newLoc); } } ); @@ -1354,7 +1371,7 @@ public class BasicSplitPaneUI extends SplitPaneUI */ public void setDividerLocation(JSplitPane jc, int location) { - dividerLocation = location; + dividerLocationSet = true; splitPane.revalidate(); splitPane.repaint(); } @@ -1368,7 +1385,12 @@ public class BasicSplitPaneUI extends SplitPaneUI */ public int getDividerLocation(JSplitPane jc) { - return dividerLocation; + int loc; + if (jc.getOrientation() == JSplitPane.HORIZONTAL_SPLIT) + loc = divider.getX(); + else + loc = divider.getY(); + return loc; } /** @@ -1441,7 +1463,7 @@ public class BasicSplitPaneUI extends SplitPaneUI */ public Dimension getPreferredSize(JComponent jc) { - return layoutManager.preferredLayoutSize((Container) jc); + return layoutManager.preferredLayoutSize(jc); } /** @@ -1453,7 +1475,7 @@ public class BasicSplitPaneUI extends SplitPaneUI */ public Dimension getMinimumSize(JComponent jc) { - return layoutManager.minimumLayoutSize((Container) jc); + return layoutManager.minimumLayoutSize(jc); } /** @@ -1465,7 +1487,7 @@ public class BasicSplitPaneUI extends SplitPaneUI */ public Dimension getMaximumSize(JComponent jc) { - return layoutManager.maximumLayoutSize((Container) jc); + return layoutManager.maximumLayoutSize(jc); } /** diff --git a/javax/swing/plaf/basic/BasicTabbedPaneUI.java b/javax/swing/plaf/basic/BasicTabbedPaneUI.java index 21dcf0d29..0d1fa1eed 100644 --- a/javax/swing/plaf/basic/BasicTabbedPaneUI.java +++ b/javax/swing/plaf/basic/BasicTabbedPaneUI.java @@ -957,82 +957,51 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected void normalizeTabRuns(int tabPlacement, int tabCount, int start, int max) { - if (tabPlacement == SwingUtilities.TOP - || tabPlacement == SwingUtilities.BOTTOM) + boolean horizontal = tabPlacement == TOP || tabPlacement == BOTTOM; + int currentRun = runCount - 1; + double weight = 1.25; + for (boolean adjust = true; adjust == true;) { - // We should only do this for runCount - 1, cause we can - // only shift that many times between runs. - for (int i = 1; i < runCount; i++) + int last = lastTabInRun(tabCount, currentRun); + int prevLast = lastTabInRun(tabCount, currentRun - 1); + int end; + int prevLength; + if (horizontal) { - Rectangle currRun = rects[lastTabInRun(tabCount, i)]; - Rectangle nextRun = rects[lastTabInRun(tabCount, - getNextTabRun(i))]; - int spaceInCurr = currRun.x + currRun.width; - int spaceInNext = nextRun.x + nextRun.width; - - int diffNow = spaceInCurr - spaceInNext; - int diffLater = (spaceInCurr - currRun.width) - - (spaceInNext + currRun.width); - - while (Math.abs(diffLater) < Math.abs(diffNow) - && spaceInNext + currRun.width < max) - { - tabRuns[i]--; - spaceInNext += currRun.width; - spaceInCurr -= currRun.width; - currRun = rects[lastTabInRun(tabCount, i)]; - diffNow = spaceInCurr - spaceInNext; - diffLater = (spaceInCurr - currRun.width) - - (spaceInNext + currRun.width); - } - - // Fixes the bounds of all tabs in the current - // run. - int first = tabRuns[i]; - int last = lastTabInRun(tabCount, i); - int currX = start; - for (int j = first; j <= last; j++) - { - rects[j].x = currX; - currX += rects[j].width; - } + end = rects[last].x + rects[last].width; + prevLength = (int) (maxTabWidth * weight); } - } - else - { - for (int i = 1; i < runCount; i++) + else { - Rectangle currRun = rects[lastTabInRun(tabCount, i)]; - Rectangle nextRun = rects[lastTabInRun(tabCount, - getNextTabRun(i))]; - int spaceInCurr = currRun.y + currRun.height; - int spaceInNext = nextRun.y + nextRun.height; - - int diffNow = spaceInCurr - spaceInNext; - int diffLater = (spaceInCurr - currRun.height) - - (spaceInNext + currRun.height); - while (Math.abs(diffLater) < Math.abs(diffNow) - && spaceInNext + currRun.height < max) - { - tabRuns[i]--; - spaceInNext += currRun.height; - spaceInCurr -= currRun.height; - currRun = rects[lastTabInRun(tabCount, i)]; - diffNow = spaceInCurr - spaceInNext; - diffLater = (spaceInCurr - currRun.height) - - (spaceInNext + currRun.height); - } - - // Fixes the bounds of tabs in the current run. - int first = tabRuns[i]; - int last = lastTabInRun(tabCount, i); - int currY = start; - for (int j = first; j <= last; j++) + end = rects[last].y + rects[last].height; + prevLength = (int) (maxTabWidth * weight * 2); + } + if (max - end > prevLength) + { + tabRuns[currentRun] = prevLast; + if (horizontal) + rects[prevLast].x = start; + else + rects[prevLast].y = start; + for (int i = prevLast + 1; i <= last; i++) { - rects[j].y = currY; - currY += rects[j].height; + if (horizontal) + rects[i].x = rects[i - 1].x + rects[i - 1].width; + else + rects[i].y = rects[i - 1].y + rects[i - 1].height; } } + else if (currentRun == runCount - 1) + adjust = false; + if (currentRun - 1 > 0) + currentRun -= 1; + else + { + // Check again, but with higher ratio to avoid + // clogging up the last run. + currentRun = runCount - 1; + weight += 0.25; + } } } diff --git a/javax/swing/plaf/metal/MetalRadioButtonUI.java b/javax/swing/plaf/metal/MetalRadioButtonUI.java index 046e4942e..57f5bbe3e 100644 --- a/javax/swing/plaf/metal/MetalRadioButtonUI.java +++ b/javax/swing/plaf/metal/MetalRadioButtonUI.java @@ -177,7 +177,7 @@ public class MetalRadioButtonUI protected void paintFocus(Graphics g, Rectangle t, Dimension d) { g.setColor(focusColor); - g.drawRect(t.x - 1, t.y - 1, t.width + 2, t.height + 2); + g.drawRect(t.x - 1, t.y - 1, t.width + 1, t.height + 1); } } diff --git a/javax/swing/plaf/metal/MetalSliderUI.java b/javax/swing/plaf/metal/MetalSliderUI.java index 0f824418c..b3e8707c9 100644 --- a/javax/swing/plaf/metal/MetalSliderUI.java +++ b/javax/swing/plaf/metal/MetalSliderUI.java @@ -352,7 +352,10 @@ public class MetalSliderUI extends BasicSliderUI */ public int getTickLength() { - return tickLength + TICK_BUFFER; + int len = tickLength + TICK_BUFFER + 1; + if (slider.getOrientation() == JSlider.VERTICAL) + len += 2; + return len; } /** @@ -406,9 +409,9 @@ public class MetalSliderUI extends BasicSliderUI // Note the incoming 'g' has a translation in place to get us to the // start of the tick rect already... if (slider.isEnabled()) - g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + g.setColor(slider.getForeground()); else - g.setColor(MetalLookAndFeel.getControlDisabled()); + g.setColor(MetalLookAndFeel.getControlShadow()); g.drawLine(x, TICK_BUFFER, x, TICK_BUFFER + tickLength / 2); } @@ -425,10 +428,10 @@ public class MetalSliderUI extends BasicSliderUI // Note the incoming 'g' has a translation in place to get us to the // start of the tick rect already... if (slider.isEnabled()) - g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + g.setColor(slider.getForeground()); else - g.setColor(MetalLookAndFeel.getControlDisabled()); - g.drawLine(x, TICK_BUFFER, x, TICK_BUFFER + tickLength); + g.setColor(MetalLookAndFeel.getControlShadow()); + g.drawLine(x, TICK_BUFFER, x, TICK_BUFFER + tickLength - 1); } /** @@ -444,10 +447,10 @@ public class MetalSliderUI extends BasicSliderUI // Note the incoming 'g' has a translation in place to get us to the // start of the tick rect already... if (slider.isEnabled()) - g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + g.setColor(slider.getForeground()); else - g.setColor(MetalLookAndFeel.getControlDisabled()); - g.drawLine(TICK_BUFFER - 1, y, TICK_BUFFER - 1 + tickLength / 2, y); + g.setColor(MetalLookAndFeel.getControlShadow()); + g.drawLine(TICK_BUFFER, y, TICK_BUFFER + tickLength / 2, y); } /** @@ -463,10 +466,10 @@ public class MetalSliderUI extends BasicSliderUI // Note the incoming 'g' has a translation in place to get us to the // start of the tick rect already... if (slider.isEnabled()) - g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + g.setColor(slider.getForeground()); else - g.setColor(MetalLookAndFeel.getControlDisabled()); - g.drawLine(TICK_BUFFER - 1, y, TICK_BUFFER - 1 + tickLength, y); + g.setColor(MetalLookAndFeel.getControlShadow()); + g.drawLine(TICK_BUFFER, y, TICK_BUFFER + tickLength, y); } } diff --git a/javax/swing/text/FlowView.java b/javax/swing/text/FlowView.java index 085b0ac45..c4625fc62 100644 --- a/javax/swing/text/FlowView.java +++ b/javax/swing/text/FlowView.java @@ -38,6 +38,7 @@ exception statement from your version. */ package javax.swing.text; +import java.awt.Component; import java.awt.Rectangle; import java.awt.Shape; @@ -85,7 +86,17 @@ public abstract class FlowView extends BoxView */ public void insertUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) { - // The default implementation does nothing. + if (alloc != null) + { + fv.layoutChanged(X_AXIS); + fv.layoutChanged(Y_AXIS); + } + else + { + Component host = fv.getContainer(); + if (host != null) + host.repaint(); + } } /** @@ -101,7 +112,17 @@ public abstract class FlowView extends BoxView */ public void removeUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) { - // The default implementation does nothing. + if (alloc != null) + { + fv.layoutChanged(X_AXIS); + fv.layoutChanged(Y_AXIS); + } + else + { + Component host = fv.getContainer(); + if (host != null) + host.repaint(); + } } /** @@ -117,7 +138,17 @@ public abstract class FlowView extends BoxView */ public void changedUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) { - // The default implementation does nothing. + if (alloc != null) + { + fv.layoutChanged(X_AXIS); + fv.layoutChanged(Y_AXIS); + } + else + { + Component host = fv.getContainer(); + if (host != null) + host.repaint(); + } } /** @@ -143,18 +174,35 @@ public abstract class FlowView extends BoxView */ public void layout(FlowView fv) { - fv.removeAll(); - Element el = fv.getElement(); + int start = fv.getStartOffset(); + int end = fv.getEndOffset(); + + // Preserve the views from the logical view from beeing removed. + View lv = getLogicalView(fv); + int viewCount = lv.getViewCount(); + for (int i = 0; i < viewCount; i++) + { + View v = lv.getView(i); + v.setParent(lv); + } - int rowStart = el.getStartOffset(); - int end = el.getEndOffset(); - int rowIndex = 0; - while (rowStart >= 0 && rowStart < end) + // Then remove all views from the flow view. + fv.removeAll(); + + for (int rowIndex = 0; start < end; rowIndex++) { View row = fv.createRow(); fv.append(row); - rowStart = layoutRow(fv, rowIndex, rowStart); - rowIndex++; + int next = layoutRow(fv, rowIndex, start); + if (row.getViewCount() == 0) + { + row.append(createView(fv, start, Integer.MAX_VALUE, rowIndex)); + next = row.getEndOffset(); + } + if (start < next) + start = next; + else + assert false: "May not happen"; } } @@ -179,46 +227,69 @@ public abstract class FlowView extends BoxView int axis = fv.getFlowAxis(); int span = fv.getFlowSpan(rowIndex); int x = fv.getFlowStart(rowIndex); - int offset = pos; - View logicalView = getLogicalView(fv); - // Special case when span == 0. We need to layout the row as if it had - // a span of Integer.MAX_VALUE. - if (span == 0) - span = Integer.MAX_VALUE; - - Row: while (span > 0) + int end = fv.getEndOffset(); + + // Needed for adjusting indentation in adjustRow(). + int preX = x; + int availableSpan = span; + + TabExpander tabExp = fv instanceof TabExpander ? (TabExpander) fv : null; + + boolean forcedBreak = false; + while (pos < end && span >= 0) { - if (logicalView.getViewIndex(offset, Position.Bias.Forward) == - 1) - break; - View view = createView(fv, offset, span, rowIndex); - if (view == null) + View view = createView(fv, pos, span, rowIndex); + if (view == null + || (span == 0 && view.getPreferredSpan(axis) > 0)) break; - int viewSpan = (int) view.getPreferredSpan(axis); - int breakWeight = view.getBreakWeight(axis, x, span); - - row.append(view); - offset += (view.getEndOffset() - view.getStartOffset()); - x += viewSpan; - span -= viewSpan; + int viewSpan; + if (axis == X_AXIS && view instanceof TabableView) + viewSpan = (int) ((TabableView) view).getTabbedSpan(x, tabExp); + else + viewSpan = (int) view.getPreferredSpan(axis); // Break if the line if the view does not fit in this row or the // line just must be broken. - if (span < 0 || breakWeight >= View.ForcedBreakWeight) + int breakWeight = view.getBreakWeight(axis, pos, span); + if (breakWeight >= ForcedBreakWeight) { - int flowStart = fv.getFlowStart(axis); - int flowSpan = fv.getFlowSpan(axis); - adjustRow(fv, rowIndex, flowSpan, flowStart); int rowViewCount = row.getViewCount(); if (rowViewCount > 0) - offset = row.getView(rowViewCount - 1).getEndOffset(); - else - offset = - 1; - break Row; + { + view = view.breakView(axis, pos, x, span); + if (view != null) + { + if (axis == X_AXIS && view instanceof TabableView) + viewSpan = + (int) ((TabableView) view).getTabbedSpan(x, tabExp); + else + viewSpan = (int) view.getPreferredSpan(axis); + } + else + viewSpan = 0; + } + forcedBreak = true; + } + span -= viewSpan; + x += viewSpan; + if (view != null) + { + row.append(view); + pos = view.getEndOffset(); } + if (forcedBreak) + break; } - return offset != pos ? offset : - 1; + if (span < 0) + adjustRow(fv, rowIndex, availableSpan, preX); + else if (row.getViewCount() == 0) + { + View view = createView(fv, pos, Integer.MAX_VALUE, rowIndex); + row.append(view); + } + return row.getEndOffset(); } /** @@ -248,13 +319,9 @@ public abstract class FlowView extends BoxView View logicalView = getLogicalView(fv); // FIXME: Handle the bias thing correctly. int index = logicalView.getViewIndex(startOffset, Position.Bias.Forward); - View retVal = null; - if (index >= 0) - { - retVal = logicalView.getView(index); - if (retVal.getStartOffset() != startOffset) - retVal = retVal.createFragment(startOffset, retVal.getEndOffset()); - } + View retVal = logicalView.getView(index); + if (retVal.getStartOffset() != startOffset) + retVal = retVal.createFragment(startOffset, retVal.getEndOffset()); return retVal; } @@ -279,37 +346,82 @@ public abstract class FlowView extends BoxView View row = fv.getView(rowIndex); int count = row.getViewCount(); int breakIndex = -1; - int maxBreakWeight = View.BadBreakWeight; - int breakX = x; - int breakSpan = desiredSpan; - int currentX = x; - int currentSpan = desiredSpan; + int breakWeight = BadBreakWeight; + int breakSpan = 0; + int currentSpan = 0; for (int i = 0; i < count; ++i) { View view = row.getView(i); - int weight = view.getBreakWeight(axis, currentX, currentSpan); - if (weight >= maxBreakWeight) + int spanLeft = desiredSpan - currentSpan; + int weight = view.getBreakWeight(axis, x + currentSpan, spanLeft); + if (weight >= breakWeight && weight > BadBreakWeight) { breakIndex = i; - breakX = currentX; breakSpan = currentSpan; - maxBreakWeight = weight; + breakWeight = weight; + if (weight >= ForcedBreakWeight) + // Don't search further. + break; } - int size = (int) view.getPreferredSpan(axis); - currentX += size; - currentSpan -= size; + currentSpan += view.getPreferredSpan(axis); } // If there is a potential break location found, break the row at // this location. - if (breakIndex > -1) + if (breakIndex >= 0) { + int spanLeft = desiredSpan - breakSpan; View toBeBroken = row.getView(breakIndex); View brokenView = toBeBroken.breakView(axis, toBeBroken.getStartOffset(), - breakX, breakSpan); + x + breakSpan, spanLeft); + View lv = getLogicalView(fv); + for (int i = breakIndex; i < count; i++) + { + View tmp = row.getView(i); + if (contains(lv, tmp)) + tmp.setParent(lv); + else if (tmp.getViewCount() > 0) + reparent(tmp, lv); + } row.replace(breakIndex, count - breakIndex, - new View[]{brokenView}); + new View[]{ brokenView }); + } + + } + + /** + * Helper method to determine if one view contains another as child. + */ + private boolean contains(View view, View child) + { + boolean ret = false; + int n = view.getViewCount(); + for (int i = 0; i < n && ret == false; i++) + { + if (view.getView(i) == child) + ret = true; + } + return ret; + } + + /** + * Helper method that reparents the <code>view</code> and all of its + * decendents to the <code>parent</code> (the logical view). + * + * @param view the view to reparent + * @param parent the new parent + */ + private void reparent(View view, View parent) + { + int n = view.getViewCount(); + for (int i = 0; i < n; i++) + { + View tmp = view.getView(i); + if (contains(parent, tmp)) + tmp.setParent(parent); + else + reparent(tmp, parent); } } } @@ -367,11 +479,6 @@ public abstract class FlowView extends BoxView protected FlowStrategy strategy; /** - * Indicates if the flow should be rebuild during the next layout. - */ - private boolean layoutDirty; - - /** * Creates a new <code>FlowView</code> for the given * <code>Element</code> and <code>axis</code>. * @@ -384,7 +491,6 @@ public abstract class FlowView extends BoxView { super(element, axis); strategy = sharedStrategy; - layoutDirty = true; } /** @@ -493,15 +599,20 @@ public abstract class FlowView extends BoxView } } - if (layoutDirty) + if (! isLayoutValid(flowAxis)) { + int axis = getAxis(); + int oldSpan = axis == X_AXIS ? getWidth() : getHeight(); strategy.layout(this); - layoutDirty = false; + int newSpan = (int) getPreferredSpan(axis); + if (oldSpan != newSpan) + { + View parent = getParent(); + if (parent != null) + parent.preferenceChanged(this, axis == X_AXIS, axis == Y_AXIS); + } } - if (getPreferredSpan(getAxis()) != height) - preferenceChanged(this, false, true); - super.layout(width, height); } @@ -520,7 +631,6 @@ public abstract class FlowView extends BoxView // be updated accordingly. layoutPool.insertUpdate(changes, a, vf); strategy.insertUpdate(this, changes, getInsideAllocation(a)); - layoutDirty = true; } /** @@ -536,7 +646,6 @@ public abstract class FlowView extends BoxView { layoutPool.removeUpdate(changes, a, vf); strategy.removeUpdate(this, changes, getInsideAllocation(a)); - layoutDirty = true; } /** @@ -552,7 +661,6 @@ public abstract class FlowView extends BoxView { layoutPool.changedUpdate(changes, a, vf); strategy.changedUpdate(this, changes, getInsideAllocation(a)); - layoutDirty = true; } /** diff --git a/javax/swing/text/GlyphView.java b/javax/swing/text/GlyphView.java index 385f50bf6..e177d927d 100644 --- a/javax/swing/text/GlyphView.java +++ b/javax/swing/text/GlyphView.java @@ -467,12 +467,12 @@ public class GlyphView extends View implements TabableView, Cloneable /** * The start offset within the document for this view. */ - private int startOffset; + private int offset; /** * The end offset within the document for this view. */ - private int endOffset; + private int length; /** * Creates a new <code>GlyphView</code> for the given <code>Element</code>. @@ -482,8 +482,8 @@ public class GlyphView extends View implements TabableView, Cloneable public GlyphView(Element element) { super(element); - startOffset = -1; - endOffset = -1; + offset = 0; + length = 0; } /** @@ -699,10 +699,11 @@ public class GlyphView extends View implements TabableView, Cloneable */ public int getStartOffset() { - int start = startOffset; - if (start < 0) - start = super.getStartOffset(); - return start; + Element el = getElement(); + int offs = el.getStartOffset(); + if (length > 0) + offs += offset; + return offs; } /** @@ -714,10 +715,13 @@ public class GlyphView extends View implements TabableView, Cloneable */ public int getEndOffset() { - int end = endOffset; - if (end < 0) - end = super.getEndOffset(); - return end; + Element el = getElement(); + int offs; + if (length > 0) + offs = el.getStartOffset() + offset + length; + else + offs = el.getEndOffset(); + return offs; } /** @@ -1029,11 +1033,12 @@ public class GlyphView extends View implements TabableView, Cloneable */ public View createFragment(int p0, int p1) { + checkPainter(); + Element el = getElement(); GlyphView fragment = (GlyphView) clone(); - if (p0 != getStartOffset()) - fragment.startOffset = p0; - if (p1 != getEndOffset()) - fragment.endOffset = p1; + fragment.offset = p0 - el.getStartOffset(); + fragment.length = p1 - p0; + fragment.glyphPainter = glyphPainter.getPainter(fragment, p0, p1); return fragment; } diff --git a/javax/swing/text/ParagraphView.java b/javax/swing/text/ParagraphView.java index b0b4b246e..6a13b2a3e 100644 --- a/javax/swing/text/ParagraphView.java +++ b/javax/swing/text/ParagraphView.java @@ -161,6 +161,38 @@ public class ParagraphView extends FlowView implements TabExpander { // Do nothing here. The children are added while layouting. } + + /** + * Overridden to determine the minimum start offset of the row's children. + */ + public int getStartOffset() + { + // Determine minimum start offset of the children. + int offset = Integer.MAX_VALUE; + int n = getViewCount(); + for (int i = 0; i < n; i++) + { + View v = getView(i); + offset = Math.min(offset, v.getStartOffset()); + } + return offset; + } + + /** + * Overridden to determine the maximum end offset of the row's children. + */ + public int getEndOffset() + { + // Determine minimum start offset of the children. + int offset = 0; + int n = getViewCount(); + for (int i = 0; i < n; i++) + { + View v = getView(i); + offset = Math.max(offset, v.getEndOffset()); + } + return offset; + } } /** diff --git a/javax/swing/text/html/BRView.java b/javax/swing/text/html/BRView.java index 5521fed8e..7d0d5164d 100644 --- a/javax/swing/text/html/BRView.java +++ b/javax/swing/text/html/BRView.java @@ -44,8 +44,7 @@ import javax.swing.text.Element; * Handled the HTML BR tag. */ class BRView - extends NullView - + extends InlineView { /** * Creates the new BR view. @@ -66,6 +65,6 @@ class BRView if (axis == X_AXIS) return ForcedBreakWeight; else - return BadBreakWeight; + return super.getBreakWeight(axis, pos, len); } } diff --git a/javax/swing/tree/AbstractLayoutCache.java b/javax/swing/tree/AbstractLayoutCache.java index 772c0c96c..4a6899fbe 100644 --- a/javax/swing/tree/AbstractLayoutCache.java +++ b/javax/swing/tree/AbstractLayoutCache.java @@ -149,9 +149,11 @@ public abstract class AbstractLayoutCache protected Rectangle getNodeDimensions(Object value, int row, int depth, boolean expanded, Rectangle bounds) { - if (nodeDimensions == null) - throw new InternalError("The NodeDimensions are not set"); - return nodeDimensions.getNodeDimensions(value, row, depth, expanded, bounds); + Rectangle d = null; + if (nodeDimensions != null) + d = nodeDimensions.getNodeDimensions(value, row, depth, expanded, + bounds); + return d; } /** @@ -224,7 +226,12 @@ public abstract class AbstractLayoutCache */ public void setSelectionModel(TreeSelectionModel model) { + if (treeSelectionModel != null) + treeSelectionModel.setRowMapper(null); treeSelectionModel = model; + if (treeSelectionModel != null) + treeSelectionModel.setRowMapper(this); + } /** @@ -425,9 +432,13 @@ public abstract class AbstractLayoutCache */ public int[] getRowsForPaths(TreePath[] paths) { - int[] rows = new int[paths.length]; - for (int i = 0; i < rows.length; i++) - rows[i] = getRowForPath(paths[i]); + int[] rows = null; + if (paths != null) + { + rows = new int[paths.length]; + for (int i = 0; i < rows.length; i++) + rows[i] = getRowForPath(paths[i]); + } return rows; } @@ -440,6 +451,6 @@ public abstract class AbstractLayoutCache */ protected boolean isFixedRowHeight() { - return false; + return rowHeight > 0; } } diff --git a/javax/swing/tree/DefaultTreeSelectionModel.java b/javax/swing/tree/DefaultTreeSelectionModel.java index db16a1a60..3d9c67728 100644 --- a/javax/swing/tree/DefaultTreeSelectionModel.java +++ b/javax/swing/tree/DefaultTreeSelectionModel.java @@ -44,6 +44,7 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Arrays; +import java.util.BitSet; import java.util.EventListener; import java.util.HashSet; import java.util.Iterator; @@ -67,7 +68,39 @@ import javax.swing.event.TreeSelectionListener; public class DefaultTreeSelectionModel implements Cloneable, Serializable, TreeSelectionModel { - + + /** + * According to the API docs, the method + * {@link DefaultTreeSelectionModel#notifyPathChange} should + * expect instances of a class PathPlaceHolder in the Vector parameter. + * This seems to be a non-public class, so I can only make guesses about the + * use of it. + */ + private static class PathPlaceHolder + { + /** + * The path that we wrap. + */ + TreePath path; + + /** + * Indicates if the path is new or already in the selection. + */ + boolean isNew; + + /** + * Creates a new instance. + * + * @param p the path to wrap + * @param n if the path is new or already in the selection + */ + PathPlaceHolder(TreePath p, boolean n) + { + path = p; + isNew = n; + } + } + /** * Use serialVersionUID for interoperability. */ @@ -124,12 +157,36 @@ public class DefaultTreeSelectionModel protected int leadRow = -1; /** + * A supporting datastructure that is used in addSelectionPaths() and + * removeSelectionPaths(). It contains currently selected paths. + * + * @see #addSelectionPaths(TreePath[]) + * @see #removeSelectionPaths(TreePath[]) + * @see #setSelectionPaths(TreePath[]) + */ + private transient HashSet selectedPaths; + + /** + * A supporting datastructure that is used in addSelectionPaths() and + * removeSelectionPaths(). It contains the paths that are added or removed. + * + * @see #addSelectionPaths(TreePath[]) + * @see #removeSelectionPaths(TreePath[]) + * @see #setSelectionPaths(TreePath[]) + */ + private transient HashSet tmpPaths; + + /** * Constructs a new DefaultTreeSelectionModel. */ public DefaultTreeSelectionModel() { setSelectionMode(DISCONTIGUOUS_TREE_SELECTION); + listSelectionModel = new DefaultListSelectionModel(); listenerList = new EventListenerList(); + leadIndex = -1; + tmpPaths = new HashSet(); + selectedPaths = new HashSet(); } /** @@ -144,12 +201,14 @@ public class DefaultTreeSelectionModel { DefaultTreeSelectionModel cloned = (DefaultTreeSelectionModel) super.clone(); - - // Clone the selection and the list selection model. + cloned.changeSupport = null; cloned.selection = (TreePath[]) selection.clone(); - if (listSelectionModel != null) - cloned.listSelectionModel - = (DefaultListSelectionModel) listSelectionModel.clone(); + cloned.listenerList = new EventListenerList(); + cloned.listSelectionModel = + (DefaultListSelectionModel) listSelectionModel.clone(); + cloned.selectedPaths = new HashSet(); + cloned.tmpPaths = new HashSet(); + return cloned; } @@ -209,6 +268,7 @@ public class DefaultTreeSelectionModel public void setRowMapper(RowMapper mapper) { rowMapper = mapper; + resetRowSelection(); } /** @@ -236,8 +296,18 @@ public class DefaultTreeSelectionModel */ public void setSelectionMode(int mode) { + int oldMode = selectionMode; selectionMode = mode; - insureRowContinuity(); + // Make sure we have a valid selection mode. + if (selectionMode != SINGLE_TREE_SELECTION + && selectionMode != CONTIGUOUS_TREE_SELECTION + && selectionMode != DISCONTIGUOUS_TREE_SELECTION) + selectionMode = DISCONTIGUOUS_TREE_SELECTION; + + // Fire property change event. + if (oldMode != selectionMode && changeSupport != null) + changeSupport.firePropertyChange(SELECTION_MODE_PROPERTY, oldMode, + selectionMode); } /** @@ -262,32 +332,10 @@ public class DefaultTreeSelectionModel */ public void setSelectionPath(TreePath path) { - // The most frequently only one cell in the tree is selected. - TreePath[] ose = selection; - selection = new TreePath[] { path }; - TreePath oldLead = leadPath; - leadIndex = 0; - leadRow = getRow(path); - leadPath = path; - - TreeSelectionEvent event; - - if (ose != null && ose.length > 0) - { - // The first item in the path list is the selected path. - // The remaining items are unselected pathes. - TreePath[] changed = new TreePath[ose.length + 1]; - boolean[] news = new boolean[changed.length]; - news[0] = true; - changed[0] = path; - System.arraycopy(ose, 0, changed, 1, ose.length); - event = new TreeSelectionEvent(this, changed, news, oldLead, path); - } - else - { - event = new TreeSelectionEvent(this, path, true, oldLead, path); - } - fireValueChanged(event); + TreePath[] paths = null; + if (path != null) + paths = new TreePath[]{ path }; + setSelectionPaths(paths); } /** @@ -307,7 +355,7 @@ public class DefaultTreeSelectionModel AbstractLayoutCache ama = (AbstractLayoutCache) mapper; return ama.getRowForPath(path); } - else + else if (mapper != null) { // Generic non optimized implementation. int[] rows = mapper.getRowsForPaths(new TreePath[] { path }); @@ -316,6 +364,7 @@ public class DefaultTreeSelectionModel else return rows[0]; } + return -1; } /** @@ -327,10 +376,90 @@ public class DefaultTreeSelectionModel */ public void setSelectionPaths(TreePath[] paths) { - // Must be called, as defined in JDK API 1.4. - insureUniqueness(); - clearSelection(); - addSelectionPaths(paths); + int oldLength = 0; + if (selection != null) + oldLength = selection.length; + int newLength = 0; + if (paths != null) + newLength = paths.length; + if (newLength > 0 || oldLength > 0) + { + // For SINGLE_TREE_SELECTION and for CONTIGUOUS_TREE_SELECTION with + // a non-contiguous path, we only allow the first path element. + if ((selectionMode == SINGLE_TREE_SELECTION && newLength > 1) + || (selectionMode == CONTIGUOUS_TREE_SELECTION && newLength > 0 + && ! arePathsContiguous(paths))) + { + paths = new TreePath[] { paths[0] }; + newLength = 1; + } + // Find new paths. + Vector changedPaths = null; + tmpPaths.clear(); + int validPaths = 0; + TreePath oldLeadPath = leadPath; + for (int i = 0; i < newLength; i++) + { + if (paths[i] != null && ! tmpPaths.contains(paths[i])) + { + validPaths++; + tmpPaths.add(paths[i]); + if (! selectedPaths.contains(paths[i])) + { + if (changedPaths == null) + changedPaths = new Vector(); + changedPaths.add(new PathPlaceHolder(paths[i], true)); + } + leadPath = paths[i]; + } + } + // Put together the new selection. + TreePath[] newSelection = null; + if (validPaths != 0) + { + if (validPaths != newLength) + { + // Some of the paths are already selected, put together + // the new selection carefully. + newSelection = new TreePath[validPaths]; + Iterator newPaths = tmpPaths.iterator(); + validPaths = 0; + for (int i = 0; newPaths.hasNext(); i++) + newSelection[i] = (TreePath) newPaths.next(); + } + else + { + newSelection = new TreePath[paths.length]; + System.arraycopy(paths, 0, newSelection, 0, paths.length); + } + } + + // Find paths that have been selected, but are no more. + for (int i = 0; i < oldLength; i++) + { + if (selection[i] != null && ! tmpPaths.contains(selection[i])) + { + if (changedPaths == null) + changedPaths = new Vector(); + changedPaths.add(new PathPlaceHolder(selection[i], false)); + } + } + + // Perform changes and notification. + selection = newSelection; + HashSet tmp = selectedPaths; + selectedPaths = tmpPaths; + tmpPaths = tmp; + tmpPaths.clear(); + + // Not necessary, but required according to the specs and to tests. + if (selection != null) + insureUniqueness(); + updateLeadIndex(); + resetRowSelection(); + if (changedPaths != null && changedPaths.size() > 0) + notifyPathChange(changedPaths, oldLeadPath); + } } /** @@ -345,29 +474,10 @@ public class DefaultTreeSelectionModel */ public void addSelectionPath(TreePath path) { - if (! isPathSelected(path)) + if (path != null) { - if (selectionMode == SINGLE_TREE_SELECTION || isSelectionEmpty() - || ! canPathBeAdded(path)) - setSelectionPath(path); - else - { - TreePath[] temp = new TreePath[selection.length + 1]; - System.arraycopy(selection, 0, temp, 0, selection.length); - temp[temp.length - 1] = path; - selection = new TreePath[temp.length]; - System.arraycopy(temp, 0, selection, 0, temp.length); - } - } - - if (path != leadPath) - { - TreePath oldLead = leadPath; - leadPath = path; - leadRow = getRow(path); - leadIndex = selection.length - 1; - fireValueChanged(new TreeSelectionEvent(this, path, true, oldLead, - leadPath)); + TreePath[] add = new TreePath[]{ path }; + addSelectionPaths(add); } } @@ -380,37 +490,76 @@ public class DefaultTreeSelectionModel */ public void addSelectionPaths(TreePath[] paths) { - // Must be called, as defined in JDK API 1.4. - insureUniqueness(); - - if (paths != null) + int length = paths != null ? paths.length : 0; + if (length > 0) { - TreePath v0 = null; - for (int i = 0; i < paths.length; i++) + if (selectionMode == SINGLE_TREE_SELECTION) + setSelectionPaths(paths); + else if (selectionMode == CONTIGUOUS_TREE_SELECTION + && ! canPathsBeAdded(paths)) + { + if (arePathsContiguous(paths)) + setSelectionPaths(paths); + else + setSelectionPaths(new TreePath[] { paths[0] }); + } + else { - v0 = paths[i]; - if (! isPathSelected(v0)) + Vector changedPaths = null; + tmpPaths.clear(); + int validPaths = 0; + TreePath oldLeadPath = leadPath; + int oldPaths = 0; + if (selection != null) + oldPaths = selection.length; + int i; + for (i = 0; i < length; i++) { - if (isSelectionEmpty()) - setSelectionPath(v0); - else + if (paths[i] != null) { - TreePath[] temp = new TreePath[selection.length + 1]; - System.arraycopy(selection, 0, temp, 0, selection.length); - temp[temp.length - 1] = v0; - selection = new TreePath[temp.length]; - System.arraycopy(temp, 0, selection, 0, temp.length); + if (! selectedPaths.contains(paths[i])) + { + validPaths++; + if (changedPaths == null) + changedPaths = new Vector(); + changedPaths.add(new PathPlaceHolder(paths[i], true)); + selectedPaths.add(paths[i]); + tmpPaths.add(paths[i]); + } + leadPath = paths[i]; } - TreePath oldLead = leadPath; - leadPath = paths[paths.length - 1]; - leadRow = getRow(leadPath); - leadIndex = selection.length - 1; - - fireValueChanged(new TreeSelectionEvent(this, v0, true, - oldLead, leadPath)); } + if (validPaths > 0) + { + TreePath[] newSelection = new TreePath[oldPaths + validPaths]; + if (oldPaths > 0) + System.arraycopy(selection, 0, newSelection, 0, oldPaths); + if (validPaths != paths.length) + { + // Some of the paths are already selected, put together + // the new selection carefully. + Iterator newPaths = tmpPaths.iterator(); + i = oldPaths; + while (newPaths.hasNext()) + { + newSelection[i] = (TreePath) newPaths.next(); + i++; + } + } + else + System.arraycopy(paths, 0, newSelection, oldPaths, + validPaths); + selection = newSelection; + insureUniqueness(); + updateLeadIndex(); + resetRowSelection(); + if (changedPaths != null && changedPaths.size() > 0) + notifyPathChange(changedPaths, oldLeadPath); + } + else + leadPath = oldLeadPath; + tmpPaths.clear(); } - insureRowContinuity(); } } @@ -422,36 +571,8 @@ public class DefaultTreeSelectionModel */ public void removeSelectionPath(TreePath path) { - if (isSelectionEmpty()) - return; - - int index = - 1; - if (isPathSelected(path)) - { - for (int i = 0; i < selection.length; i++) - { - if (selection[i].equals(path)) - { - index = i; - break; - } - } - TreePath[] temp = new TreePath[selection.length - 1]; - System.arraycopy(selection, 0, temp, 0, index); - System.arraycopy(selection, index + 1, temp, index, selection.length - - index - 1); - selection = new TreePath[temp.length]; - System.arraycopy(temp, 0, selection, 0, temp.length); - - // If the removed path was the lead path, set the lead path to null. - TreePath oldLead = leadPath; - if (path != null && leadPath != null && path.equals(leadPath)) - leadPath = null; - - fireValueChanged(new TreeSelectionEvent(this, path, false, oldLead, - leadPath)); - insureRowContinuity(); - } + if (path != null) + removeSelectionPaths(new TreePath[]{ path }); } /** @@ -462,40 +583,54 @@ public class DefaultTreeSelectionModel */ public void removeSelectionPaths(TreePath[] paths) { - if (isSelectionEmpty()) - return; - if (paths != null) + if (paths != null && selection != null && paths.length > 0) { - int index = - 1; - TreePath v0 = null; - TreePath oldLead = leadPath; - for (int i = 0; i < paths.length; i++) + if (! canPathsBeRemoved(paths)) + clearSelection(); + else { - v0 = paths[i]; - if (isPathSelected(v0)) + Vector pathsToRemove = null; + for (int i = paths.length - 1; i >= 0; i--) { - for (int x = 0; x < selection.length; x++) + if (paths[i] != null && selectedPaths.contains(paths[i])) { - if (selection[i].equals(v0)) - { - index = x; - break; - } - if (leadPath != null && leadPath.equals(v0)) + if (pathsToRemove == null) + pathsToRemove = new Vector(); + selectedPaths.remove(paths[i]); + pathsToRemove.add(new PathPlaceHolder(paths[i], + false)); + } + } + if (pathsToRemove != null) + { + int numRemove = pathsToRemove.size(); + TreePath oldLead = leadPath; + if (numRemove == selection.length) + selection = null; + else + { + selection = new TreePath[selection.length - numRemove]; + Iterator keep = selectedPaths.iterator(); + for (int valid = 0; keep.hasNext(); valid++) + selection[valid] = (TreePath) keep.next(); + } + // Update lead path. + if (leadPath != null && ! selectedPaths.contains(leadPath)) + { + if (selection != null) + leadPath = selection[selection.length - 1]; + else leadPath = null; } - TreePath[] temp = new TreePath[selection.length - 1]; - System.arraycopy(selection, 0, temp, 0, index); - System.arraycopy(selection, index + 1, temp, index, - selection.length - index - 1); - selection = new TreePath[temp.length]; - System.arraycopy(temp, 0, selection, 0, temp.length); - - fireValueChanged(new TreeSelectionEvent(this, v0, false, - oldLead, leadPath)); + else if (selection != null) + leadPath = selection[selection.length - 1]; + else + leadPath = null; + updateLeadIndex(); + resetRowSelection(); + notifyPathChange(pathsToRemove, oldLead); } } - insureRowContinuity(); } } @@ -572,19 +707,22 @@ public class DefaultTreeSelectionModel */ public void clearSelection() { - if (! isSelectionEmpty()) + if (selection != null) { - TreeSelectionEvent event = new TreeSelectionEvent( - this, selection, new boolean[selection.length], leadPath, null); + int selectionLength = selection.length; + boolean[] news = new boolean[selectionLength]; + Arrays.fill(news, false); + TreeSelectionEvent event = new TreeSelectionEvent(this, selection, + news, leadPath, + null); leadPath = null; + leadIndex = 0; + leadRow = 0; + selectedPaths.clear(); selection = null; + resetRowSelection(); fireValueChanged(event); } - else - { - leadPath = null; - selection = null; - } } /** @@ -650,10 +788,43 @@ public class DefaultTreeSelectionModel */ public int[] getSelectionRows() { - if (rowMapper == null) - return null; - else - return rowMapper.getRowsForPaths(selection); + int[] rows = null; + if (rowMapper != null && selection != null) + { + rows = rowMapper.getRowsForPaths(selection); + if (rows != null) + { + // Find invisible rows. + int invisible = 0; + for (int i = rows.length - 1; i >= 0; i--) + { + if (rows[i] == -1) + invisible++; + + } + // Clean up invisible rows. + if (invisible > 0) + { + if (invisible == rows.length) + rows = null; + else + { + int[] newRows = new int[rows.length - invisible]; + int visCount = 0; + for (int i = rows.length - 1; i >= 0; i--) + { + if (rows[i] != -1) + { + newRows[visCount] = rows[i]; + visCount++; + } + } + rows = newRows; + } + } + } + } + return rows; } /** @@ -663,16 +834,7 @@ public class DefaultTreeSelectionModel */ public int getMinSelectionRow() { - if ((rowMapper == null) || (selection == null) || (selection.length == 0)) - return - 1; - else - { - int[] rows = rowMapper.getRowsForPaths(selection); - int minRow = Integer.MAX_VALUE; - for (int index = 0; index < rows.length; index++) - minRow = Math.min(minRow, rows[index]); - return minRow; - } + return listSelectionModel.getMinSelectionIndex(); } /** @@ -682,16 +844,7 @@ public class DefaultTreeSelectionModel */ public int getMaxSelectionRow() { - if ((rowMapper == null) || (selection == null) || (selection.length == 0)) - return - 1; - else - { - int[] rows = rowMapper.getRowsForPaths(selection); - int maxRow = - 1; - for (int index = 0; index < rows.length; index++) - maxRow = Math.max(maxRow, rows[index]); - return maxRow; - } + return listSelectionModel.getMaxSelectionIndex(); } /** @@ -706,29 +859,7 @@ public class DefaultTreeSelectionModel */ public boolean isRowSelected(int row) { - // Return false if nothing is selected. - if (isSelectionEmpty()) - return false; - - RowMapper mapper = getRowMapper(); - - if (mapper instanceof AbstractLayoutCache) - { - // The absolute majority of cases, unless the TreeUI is very - // seriously rewritten - AbstractLayoutCache ama = (AbstractLayoutCache) mapper; - TreePath path = ama.getPathForRow(row); - return isPathSelected(path); - } - else - { - // Generic non optimized implementation. - int[] rows = mapper.getRowsForPaths(selection); - for (int i = 0; i < rows.length; i++) - if (rows[i] == row) - return true; - return false; - } + return listSelectionModel.isSelectedIndex(row); } /** @@ -736,7 +867,32 @@ public class DefaultTreeSelectionModel */ public void resetRowSelection() { - // Nothing to do here. + listSelectionModel.clearSelection(); + if (selection != null && rowMapper != null) + { + int[] rows = rowMapper.getRowsForPaths(selection); + // Update list selection model. + for (int i = 0; i < rows.length; i++) + { + int row = rows[i]; + if (row != -1) + listSelectionModel.addSelectionInterval(row, row); + } + // Update lead selection. + if (leadIndex != -1 && rows != null) + leadRow = rows[leadIndex]; + else if (leadPath != null) + { + TreePath[] tmp = new TreePath[]{ leadPath }; + rows = rowMapper.getRowsForPaths(tmp); + leadRow = rows != null ? rows[0] : -1; + } + else + leadRow = -1; + insureRowContinuity(); + } + else + leadRow = -1; } /** @@ -766,6 +922,8 @@ public class DefaultTreeSelectionModel */ public void addPropertyChangeListener(PropertyChangeListener listener) { + if (changeSupport == null) + changeSupport = new SwingPropertyChangeSupport(this); changeSupport.addPropertyChangeListener(listener); } @@ -776,7 +934,8 @@ public class DefaultTreeSelectionModel */ public void removePropertyChangeListener(PropertyChangeListener listener) { - changeSupport.removePropertyChangeListener(listener); + if (changeSupport != null) + changeSupport.removePropertyChangeListener(listener); } /** @@ -787,7 +946,12 @@ public class DefaultTreeSelectionModel */ public PropertyChangeListener[] getPropertyChangeListeners() { - return changeSupport.getPropertyChangeListeners(); + PropertyChangeListener[] listeners = null; + if (changeSupport != null) + listeners = changeSupport.getPropertyChangeListeners(); + else + listeners = new PropertyChangeListener[0]; + return listeners; } /** @@ -801,67 +965,41 @@ public class DefaultTreeSelectionModel */ protected void insureRowContinuity() { - if (selection == null || selection.length < 2) - return; - else if (selectionMode == CONTIGUOUS_TREE_SELECTION) + if (selectionMode == CONTIGUOUS_TREE_SELECTION && selection != null + && rowMapper != null) { - if (rowMapper == null) - // This is the best we can do without the row mapper: - selectOne(); - else + int min = listSelectionModel.getMinSelectionIndex(); + if (min != -1) { - int[] rows = rowMapper.getRowsForPaths(selection); - Arrays.sort(rows); - int i; - for (i = 1; i < rows.length; i++) - { - if (rows[i - 1] != rows[i] - 1) - // Break if no longer continuous. - break; - } - - if (i < rows.length) + int max = listSelectionModel.getMaxSelectionIndex(); + for (int i = min; i <= max; i++) { - TreePath[] ns = new TreePath[i]; - for (int j = 0; j < ns.length; j++) - ns[i] = getPath(j); - setSelectionPaths(ns); + if (! listSelectionModel.isSelectedIndex(i)) + { + if (i == min) + clearSelection(); + else + { + TreePath[] newSelection = new TreePath[i - min]; + int[] rows = rowMapper.getRowsForPaths(selection); + for (int j = 0; j < rows.length; j++) + { + if (rows[j] < i) + newSelection[rows[j] - min] = selection[j]; + } + setSelectionPaths(newSelection); + break; + } + } } } } - else if (selectionMode == SINGLE_TREE_SELECTION) - selectOne(); + else if (selectionMode == SINGLE_TREE_SELECTION && selection != null + && selection.length > 1) + setSelectionPath(selection[0]); } /** - * Keep only one (normally last or leading) path in the selection. - */ - private void selectOne() - { - if (leadIndex > 0 && leadIndex < selection.length) - setSelectionPath(selection[leadIndex]); - else - setSelectionPath(selection[selection.length - 1]); - } - - /** - * Get path for the given row that must be in the current selection. - */ - private TreePath getPath(int row) - { - if (rowMapper instanceof AbstractLayoutCache) - return ((AbstractLayoutCache) rowMapper).getPathForRow(row); - else - { - int[] rows = rowMapper.getRowsForPaths(selection); - for (int i = 0; i < rows.length; i++) - if (rows[i] == row) - return selection[i]; - } - throw new InternalError(row + " not in selection"); - } - - /** * Returns <code>true</code> if the paths are contiguous (take subsequent * rows in the diplayed tree view. The method returns <code>true</code> if * we have no RowMapper assigned. @@ -875,16 +1013,36 @@ public class DefaultTreeSelectionModel if (rowMapper == null || paths.length < 2) return true; - int[] rows = rowMapper.getRowsForPaths(paths); - - // The patches may not be sorted. - Arrays.sort(rows); - - for (int i = 1; i < rows.length; i++) + int length = paths.length; + TreePath[] tmp = new TreePath[1]; + tmp[0] = paths[0]; + int min = rowMapper.getRowsForPaths(tmp)[0]; + BitSet selected = new BitSet(); + int valid = 0; + for (int i = 0; i < length; i++) { - if (rows[i - 1] != rows[i] - 1) - return false; + if (paths[i] != null) + { + tmp[0] = paths[i]; + int[] rows = rowMapper.getRowsForPaths(tmp); + if (rows == null) + return false; // No row mapping yet, can't be selected. + int row = rows[0]; + if (row == -1 || row < (min - length) || row > (min + length)) + return false; // Not contiguous. + min = Math.min(min, row); + if (! selected.get(row)) + { + selected.set(row); + valid++; + } + + } } + int max = valid + min; + for (int i = min; i < max; i++) + if (! selected.get(i)) + return false; // Not contiguous. return true; } @@ -904,34 +1062,51 @@ public class DefaultTreeSelectionModel */ protected boolean canPathsBeAdded(TreePath[] paths) { - if (rowMapper == null || isSelectionEmpty() - || selectionMode == DISCONTIGUOUS_TREE_SELECTION) + if (paths == null || paths.length == 0 || rowMapper == null + || selection == null || selectionMode == DISCONTIGUOUS_TREE_SELECTION) return true; - - TreePath [] all = new TreePath[paths.length + selection.length]; - System.arraycopy(paths, 0, all, 0, paths.length); - System.arraycopy(selection, 0, all, paths.length, selection.length); - return arePathsContiguous(all); + BitSet selected = new BitSet(); + int min = listSelectionModel.getMinSelectionIndex(); + int max = listSelectionModel.getMaxSelectionIndex(); + TreePath[] tmp = new TreePath[1]; + if (min != -1) + { + // Set the bitmask of selected elements. + for (int i = min; i <= max; i++) + selected.set(i); + } + else + { + tmp[0] = paths[0]; + min = rowMapper.getRowsForPaths(tmp)[0]; + max = min; + } + // Mark new paths as selected. + for (int i = paths.length - 1; i >= 0; i--) + { + if (paths[i] != null) + { + tmp[0] = paths[i]; + int[] rows = rowMapper.getRowsForPaths(tmp); + if (rows == null) + return false; // Now row mapping yet, can't be selected. + int row = rows[0]; + if (row == -1) + return false; // Now row mapping yet, can't be selected. + min = Math.min(min, row); + max = Math.max(max, row); + selected.set(row); + } + } + // Now look if the new selection would be contiguous. + for (int i = min; i <= max; i++) + if (! selected.get(i)) + return false; + return true; } /** - * Checks if the single path can be added to selection. - */ - private boolean canPathBeAdded(TreePath path) - { - if (rowMapper == null || isSelectionEmpty() - || selectionMode == DISCONTIGUOUS_TREE_SELECTION) - return true; - - TreePath[] all = new TreePath[selection.length + 1]; - System.arraycopy(selection, 0, all, 0, selection.length); - all[all.length - 1] = path; - - return arePathsContiguous(all); - } - - /** * Checks if the paths can be removed without breaking the continuity of the * selection according to selectionMode. * @@ -966,20 +1141,23 @@ public class DefaultTreeSelectionModel * method will call listeners if invoked, but it is not called from the * implementation of this class. * - * @param vPathes the vector of the changed patches + * @param vPaths the vector of the changed patches * @param oldLeadSelection the old selection index */ - protected void notifyPathChange(Vector vPathes, TreePath oldLeadSelection) + protected void notifyPathChange(Vector vPaths, TreePath oldLeadSelection) { - TreePath[] pathes = new TreePath[vPathes.size()]; - for (int i = 0; i < pathes.length; i++) - pathes[i] = (TreePath) vPathes.get(i); - boolean[] news = new boolean[pathes.length]; - for (int i = 0; i < news.length; i++) - news[i] = isPathSelected(pathes[i]); + int numChangedPaths = vPaths.size(); + boolean[] news = new boolean[numChangedPaths]; + TreePath[] paths = new TreePath[numChangedPaths]; + for (int i = 0; i < numChangedPaths; i++) + { + PathPlaceHolder p = (PathPlaceHolder) vPaths.get(i); + news[i] = p.isNew; + paths[i] = p.path; + } - TreeSelectionEvent event = new TreeSelectionEvent(this, pathes, news, + TreeSelectionEvent event = new TreeSelectionEvent(this, paths, news, oldLeadSelection, leadPath); fireValueChanged(event); @@ -991,22 +1169,20 @@ public class DefaultTreeSelectionModel */ protected void updateLeadIndex() { - if (isSelectionEmpty()) + leadIndex = -1; + if (leadPath != null) { - leadRow = leadIndex = - 1; - } - else - { - leadRow = getRow(leadPath); - for (int i = 0; i < selection.length; i++) + leadRow = -1; + if (selection == null) + leadPath = null; + else { - if (selection[i].equals(leadPath)) + for (int i = selection.length - 1; i >= 0 && leadIndex == -1; i--) { - leadIndex = i; - break; + if (selection[i] == leadPath) + leadIndex = i; } } - leadIndex = leadRow; } } diff --git a/javax/swing/tree/VariableHeightLayoutCache.java b/javax/swing/tree/VariableHeightLayoutCache.java index b256f1fe9..8c70c13af 100644 --- a/javax/swing/tree/VariableHeightLayoutCache.java +++ b/javax/swing/tree/VariableHeightLayoutCache.java @@ -40,6 +40,7 @@ package javax.swing.tree; import gnu.javax.swing.tree.GnuPath; import java.awt.Rectangle; +import java.util.ArrayList; import java.util.Enumeration; import java.util.HashSet; import java.util.Hashtable; @@ -60,8 +61,11 @@ import javax.swing.event.TreeModelEvent; * @author Audrius Meskauskas */ public class VariableHeightLayoutCache - extends AbstractLayoutCache + extends AbstractLayoutCache { + + private static final Rectangle RECT_CACHE = new Rectangle(); + /** * The cached node record. */ @@ -73,8 +77,8 @@ public class VariableHeightLayoutCache depth = aDepth; parent = aParent; node = aNode; - - isExpanded = expanded.contains(aNode); + isExpanded = expanded.contains(aNode); + bounds = new Rectangle(0, -1, 0, 0); } /** @@ -102,7 +106,7 @@ public class VariableHeightLayoutCache * Using this field saves one hashtable access operation. */ final boolean isExpanded; - + /** * The cached bounds of the tree row. */ @@ -160,11 +164,6 @@ public class VariableHeightLayoutCache */ Rectangle getBounds() { - // This method may be called in the context when the tree rectangle is - // not known. To work around this, it is assumed near infinitely large. - if (bounds == null) - bounds = getNodeDimensions(node, row, depth, isExpanded, - new Rectangle()); return bounds; } } @@ -182,7 +181,7 @@ public class VariableHeightLayoutCache /** * Maps row numbers to nodes. */ - Hashtable row2node = new Hashtable(); + ArrayList row2node = new ArrayList(); /** * If true, the row map must be recomputed before using. @@ -236,45 +235,54 @@ public class VariableHeightLayoutCache return; Object root = treeModel.getRoot(); - - if (rootVisible) - { - countRows(root, null, 0); - } - else - { - int sc = treeModel.getChildCount(root); - for (int i = 0; i < sc; i++) - { - Object child = treeModel.getChild(root, i); - countRows(child, root, 0); - } - } + countRows(root, null, 0, 0); dirty = false; } /** * Recursively counts all rows in the tree. */ - private final void countRows(Object node, Object parent, int depth) + private final int countRows(Object node, Object parent, int depth, int y) { - Integer n = new Integer(row2node.size()); - row2node.put(n, node); - - NodeRecord nr = new NodeRecord(n.intValue(), depth, node, parent); + boolean visible = node != treeModel.getRoot() || rootVisible; + int row = row2node.size(); + if (visible) + { + row2node.add(node); + } + NodeRecord nr = new NodeRecord(row, depth, node, parent); + NodeDimensions d = getNodeDimensions(); + Rectangle r = RECT_CACHE; + if (d != null) + r = d.getNodeDimensions(node, row, depth, nr.isExpanded, r); + else + r.setBounds(0, 0, 0, 0); + + if (! visible) + r.y = -1; + else + r.y = Math.max(0, y); + + if (isFixedRowHeight()) + r.height = getRowHeight(); + + nr.bounds.setBounds(r); nodes.put(node, nr); - - // For expanded nodes + + if (visible) + y += r.height; + + int sc = treeModel.getChildCount(node); + int deeper = depth + 1; if (expanded.contains(node)) { - int sc = treeModel.getChildCount(node); - int deeper = depth + 1; for (int i = 0; i < sc; i++) { Object child = treeModel.getChild(node, i); - countRows(child, node, deeper); + y = countRows(child, node, deeper, y); } } + return y; } /** @@ -309,10 +317,14 @@ public class VariableHeightLayoutCache public void setExpandedState(TreePath path, boolean isExpanded) { if (isExpanded) - expanded.add(path.getLastPathComponent()); + { + int length = path.getPathCount(); + for (int i = 0; i < length; i++) + expanded.add(path.getPathComponent(i)); + } else expanded.remove(path.getLastPathComponent()); - + dirty = true; } @@ -340,30 +352,20 @@ public class VariableHeightLayoutCache if (dirty) update(); - // The RI allows null arguments for rect, in which case a new Rectangle - // is created. - if (rect == null) - rect = new Rectangle(); - Object last = path.getLastPathComponent(); + Rectangle result = null; NodeRecord r = (NodeRecord) nodes.get(last); - if (r == null) - // This node is not visible. - { - rect.x = rect.y = rect.width = rect.height = 0; - } - else + if (r != null) { - if (r.bounds == null) - { - Rectangle dim = getNodeDimensions(last, r.row, r.depth, - r.isExpanded, rect); - r.bounds = dim; - } - - rect.setRect(r.bounds); + // The RI allows null arguments for rect, in which case a new Rectangle + // is created. + result = rect; + if (result == null) + result = new Rectangle(r.bounds); + else + result.setBounds(r.bounds); } - return rect; + return result; } /** @@ -376,14 +378,17 @@ public class VariableHeightLayoutCache { if (dirty) update(); - Object last = row2node.get(new Integer(row)); - if (last == null) - return null; - else + + TreePath path = null; + // Search row in the nodes map. TODO: This is inefficient, optimize this. + Enumeration nodesEnum = nodes.elements(); + while (nodesEnum.hasMoreElements() && path == null) { - NodeRecord r = (NodeRecord) nodes.get(last); - return r.getPath(); + NodeRecord record = (NodeRecord) nodesEnum.nextElement(); + if (record.row == row) + path = record.getPath(); } + return path; } /** @@ -396,7 +401,9 @@ public class VariableHeightLayoutCache { if (path == null) return -1; - if (dirty) update(); + + if (dirty) + update(); NodeRecord r = (NodeRecord) nodes.get(path.getLastPathComponent()); if (r == null) @@ -474,7 +481,7 @@ public class VariableHeightLayoutCache */ public int getVisibleChildCount(TreePath path) { - if (isExpanded(path)) + if (! isExpanded(path) || treeModel == null) return 0; else return treeModel.getChildCount(path.getLastPathComponent()); @@ -499,7 +506,7 @@ public class VariableHeightLayoutCache { node = parentPath.getPathComponent(i); nr = (NodeRecord) nodes.get(node); - if (nr.row >= 0) + if (nr != null && nr.row >= 0) p.add(node); } return p.elements(); @@ -564,15 +571,11 @@ public class VariableHeightLayoutCache public void setModel(TreeModel newModel) { treeModel = newModel; - // We need to clear the table and update the layout, - // so that we don't end up with wrong data in the tables. - expanded.clear(); - update(); + dirty = true; if (treeModel != null) { // The root node is expanded by default. expanded.add(treeModel.getRoot()); - dirty = true; } } @@ -596,15 +599,14 @@ public class VariableHeightLayoutCache { if (dirty) update(); - totalHeight = 0; - Enumeration en = nodes.elements(); - while (en.hasMoreElements()) + int height = 0; + int rowCount = getRowCount(); + if (rowCount > 0) { - NodeRecord nr = (NodeRecord) en.nextElement(); - Rectangle r = nr.getBounds(); - totalHeight += r.height; + NodeRecord last = (NodeRecord) nodes.get(row2node.get(rowCount - 1)); + height = last.bounds.y + last.bounds.height; } - return totalHeight; + return height; } /** @@ -620,10 +622,36 @@ public class VariableHeightLayoutCache while (en.hasMoreElements()) { NodeRecord nr = (NodeRecord) en.nextElement(); - Rectangle r = nr.getBounds(); - if (r.x + r.width > maximalWidth) - maximalWidth = r.x + r.width; + if (nr != null) + { + Rectangle r = nr.getBounds(); + int width = r.x + r.width; + if (width > maximalWidth) + maximalWidth = width; + } } return maximalWidth; } + + /** + * Sets the node dimensions and invalidates the cached layout. + * + * @param dim the dimensions to set + */ + public void setNodeDimensions(NodeDimensions dim) + { + super.setNodeDimensions(dim); + dirty = true; + } + + /** + * Sets the row height and marks the layout as invalid. + * + * @param height the row height to set + */ + public void setRowHeight(int height) + { + super.setRowHeight(height); + dirty = true; + } } |