summaryrefslogtreecommitdiff
path: root/javax
diff options
context:
space:
mode:
Diffstat (limited to 'javax')
-rw-r--r--javax/crypto/CipherOutputStream.java180
-rw-r--r--javax/swing/AbstractButton.java137
-rw-r--r--javax/swing/DefaultComboBoxModel.java30
-rw-r--r--javax/swing/JComponent.java120
-rw-r--r--javax/swing/JDialog.java55
-rw-r--r--javax/swing/JEditorPane.java33
-rw-r--r--javax/swing/JFrame.java51
-rw-r--r--javax/swing/JLabel.java68
-rw-r--r--javax/swing/JMenuItem.java82
-rw-r--r--javax/swing/JScrollPane.java8
-rw-r--r--javax/swing/JSlider.java71
-rw-r--r--javax/swing/JSplitPane.java34
-rw-r--r--javax/swing/JTree.java21
-rw-r--r--javax/swing/JViewport.java31
-rw-r--r--javax/swing/JWindow.java5
-rw-r--r--javax/swing/RepaintManager.java36
-rw-r--r--javax/swing/ToolTipManager.java77
-rw-r--r--javax/swing/TransferHandler.java357
-rw-r--r--javax/swing/filechooser/FileSystemView.java3
-rw-r--r--javax/swing/plaf/basic/BasicLookAndFeel.java7
-rw-r--r--javax/swing/plaf/basic/BasicMenuItemUI.java34
-rw-r--r--javax/swing/plaf/basic/BasicRadioButtonUI.java106
-rw-r--r--javax/swing/plaf/basic/BasicScrollBarUI.java78
-rw-r--r--javax/swing/plaf/basic/BasicScrollPaneUI.java108
-rw-r--r--javax/swing/plaf/basic/BasicSliderUI.java566
-rw-r--r--javax/swing/plaf/basic/BasicSplitPaneUI.java52
-rw-r--r--javax/swing/plaf/basic/BasicTabbedPaneUI.java107
-rw-r--r--javax/swing/plaf/metal/MetalRadioButtonUI.java2
-rw-r--r--javax/swing/plaf/metal/MetalSliderUI.java27
-rw-r--r--javax/swing/text/FlowView.java258
-rw-r--r--javax/swing/text/GlyphView.java37
-rw-r--r--javax/swing/text/ParagraphView.java32
-rw-r--r--javax/swing/text/html/BRView.java5
-rw-r--r--javax/swing/tree/AbstractLayoutCache.java25
-rw-r--r--javax/swing/tree/DefaultTreeSelectionModel.java778
-rw-r--r--javax/swing/tree/VariableHeightLayoutCache.java188
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;
+ }
}