summaryrefslogtreecommitdiff
path: root/libjava/classpath/javax/swing
diff options
context:
space:
mode:
authortromey <tromey@138bc75d-0d04-0410-961f-82ee72b054a4>2007-01-09 19:58:05 +0000
committertromey <tromey@138bc75d-0d04-0410-961f-82ee72b054a4>2007-01-09 19:58:05 +0000
commit65bf3316cf384588453604be6b4f0ed3751a8b0f (patch)
tree996a5f57d4a68c53473382e45cb22f574cb3e4db /libjava/classpath/javax/swing
parent8fc56618a84446beccd45b80381cdfe0e94050df (diff)
downloadgcc-65bf3316cf384588453604be6b4f0ed3751a8b0f.tar.gz
Merged gcj-eclipse branch to trunk.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@120621 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libjava/classpath/javax/swing')
-rw-r--r--libjava/classpath/javax/swing/AbstractButton.java229
-rw-r--r--libjava/classpath/javax/swing/AbstractListModel.java2
-rw-r--r--libjava/classpath/javax/swing/AbstractSpinnerModel.java12
-rw-r--r--libjava/classpath/javax/swing/ButtonGroup.java8
-rw-r--r--libjava/classpath/javax/swing/DefaultBoundedRangeModel.java2
-rw-r--r--libjava/classpath/javax/swing/DefaultButtonModel.java4
-rw-r--r--libjava/classpath/javax/swing/DefaultComboBoxModel.java32
-rw-r--r--libjava/classpath/javax/swing/DefaultListModel.java2
-rw-r--r--libjava/classpath/javax/swing/DefaultListSelectionModel.java2
-rw-r--r--libjava/classpath/javax/swing/DefaultSingleSelectionModel.java2
-rw-r--r--libjava/classpath/javax/swing/JButton.java2
-rw-r--r--libjava/classpath/javax/swing/JComboBox.java2
-rw-r--r--libjava/classpath/javax/swing/JComponent.java803
-rw-r--r--libjava/classpath/javax/swing/JDialog.java55
-rw-r--r--libjava/classpath/javax/swing/JEditorPane.java336
-rw-r--r--libjava/classpath/javax/swing/JFrame.java51
-rw-r--r--libjava/classpath/javax/swing/JLabel.java103
-rw-r--r--libjava/classpath/javax/swing/JLayeredPane.java2
-rw-r--r--libjava/classpath/javax/swing/JList.java235
-rw-r--r--libjava/classpath/javax/swing/JMenu.java263
-rw-r--r--libjava/classpath/javax/swing/JMenuBar.java33
-rw-r--r--libjava/classpath/javax/swing/JMenuItem.java107
-rw-r--r--libjava/classpath/javax/swing/JPopupMenu.java23
-rw-r--r--libjava/classpath/javax/swing/JRootPane.java12
-rw-r--r--libjava/classpath/javax/swing/JScrollBar.java113
-rw-r--r--libjava/classpath/javax/swing/JScrollPane.java8
-rw-r--r--libjava/classpath/javax/swing/JSlider.java71
-rw-r--r--libjava/classpath/javax/swing/JSplitPane.java34
-rw-r--r--libjava/classpath/javax/swing/JTabbedPane.java31
-rw-r--r--libjava/classpath/javax/swing/JTable.java120
-rw-r--r--libjava/classpath/javax/swing/JTextField.java3
-rw-r--r--libjava/classpath/javax/swing/JTextPane.java19
-rw-r--r--libjava/classpath/javax/swing/JToggleButton.java5
-rw-r--r--libjava/classpath/javax/swing/JToolTip.java14
-rw-r--r--libjava/classpath/javax/swing/JTree.java173
-rw-r--r--libjava/classpath/javax/swing/JViewport.java133
-rw-r--r--libjava/classpath/javax/swing/JWindow.java5
-rw-r--r--libjava/classpath/javax/swing/LookAndFeel.java2
-rw-r--r--libjava/classpath/javax/swing/Popup.java2
-rw-r--r--libjava/classpath/javax/swing/RepaintManager.java299
-rw-r--r--libjava/classpath/javax/swing/ScrollPaneLayout.java38
-rw-r--r--libjava/classpath/javax/swing/SizeSequence.java8
-rw-r--r--libjava/classpath/javax/swing/SortingFocusTraversalPolicy.java6
-rw-r--r--libjava/classpath/javax/swing/SpinnerListModel.java6
-rw-r--r--libjava/classpath/javax/swing/SwingUtilities.java274
-rw-r--r--libjava/classpath/javax/swing/Timer.java2
-rw-r--r--libjava/classpath/javax/swing/ToolTipManager.java77
-rw-r--r--libjava/classpath/javax/swing/TransferHandler.java357
-rw-r--r--libjava/classpath/javax/swing/UIDefaults.java8
-rw-r--r--libjava/classpath/javax/swing/UIManager.java251
-rw-r--r--libjava/classpath/javax/swing/border/CompoundBorder.java27
-rw-r--r--libjava/classpath/javax/swing/event/EventListenerList.java60
-rw-r--r--libjava/classpath/javax/swing/filechooser/FileSystemView.java9
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java136
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java330
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicDirectoryModel.java6
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java10
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java55
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicHTML.java26
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java10
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java64
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java57
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicListUI.java2
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java24
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java144
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java207
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java160
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java79
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java237
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java566
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java412
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java372
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java417
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java8
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java9
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java538
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java6
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicToolTipUI.java123
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java529
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/DefaultMetalTheme.java100
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalBorders.java16
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalButtonUI.java72
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxIcon.java11
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java119
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java50
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalMenuBarUI.java9
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalRadioButtonUI.java2
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalScrollBarUI.java15
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalSliderUI.java27
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java281
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java2
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalToolTipUI.java70
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalTreeUI.java122
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/OceanTheme.java2
-rw-r--r--libjava/classpath/javax/swing/plaf/synth/SynthLookAndFeel.java4
-rw-r--r--libjava/classpath/javax/swing/table/AbstractTableModel.java4
-rw-r--r--libjava/classpath/javax/swing/table/DefaultTableColumnModel.java8
-rw-r--r--libjava/classpath/javax/swing/table/DefaultTableModel.java2
-rw-r--r--libjava/classpath/javax/swing/table/TableColumnModel.java2
-rw-r--r--libjava/classpath/javax/swing/table/TableModel.java2
-rw-r--r--libjava/classpath/javax/swing/text/AbstractDocument.java913
-rw-r--r--libjava/classpath/javax/swing/text/AttributeSet.java2
-rw-r--r--libjava/classpath/javax/swing/text/BoxView.java484
-rw-r--r--libjava/classpath/javax/swing/text/ComponentView.java336
-rw-r--r--libjava/classpath/javax/swing/text/CompositeView.java195
-rw-r--r--libjava/classpath/javax/swing/text/DefaultCaret.java15
-rw-r--r--libjava/classpath/javax/swing/text/DefaultEditorKit.java365
-rw-r--r--libjava/classpath/javax/swing/text/DefaultFormatter.java6
-rw-r--r--libjava/classpath/javax/swing/text/DefaultHighlighter.java389
-rw-r--r--libjava/classpath/javax/swing/text/DefaultStyledDocument.java2036
-rw-r--r--libjava/classpath/javax/swing/text/ElementIterator.java203
-rw-r--r--libjava/classpath/javax/swing/text/FieldView.java4
-rw-r--r--libjava/classpath/javax/swing/text/FlowView.java419
-rw-r--r--libjava/classpath/javax/swing/text/GapContent.java723
-rw-r--r--libjava/classpath/javax/swing/text/GlyphView.java620
-rw-r--r--libjava/classpath/javax/swing/text/InternationalFormatter.java2
-rw-r--r--libjava/classpath/javax/swing/text/JTextComponent.java278
-rw-r--r--libjava/classpath/javax/swing/text/LabelView.java78
-rw-r--r--libjava/classpath/javax/swing/text/MaskFormatter.java377
-rw-r--r--libjava/classpath/javax/swing/text/MutableAttributeSet.java2
-rw-r--r--libjava/classpath/javax/swing/text/ParagraphView.java107
-rw-r--r--libjava/classpath/javax/swing/text/PlainView.java150
-rw-r--r--libjava/classpath/javax/swing/text/Position.java4
-rw-r--r--libjava/classpath/javax/swing/text/SimpleAttributeSet.java18
-rw-r--r--libjava/classpath/javax/swing/text/StringContent.java304
-rw-r--r--libjava/classpath/javax/swing/text/StyleConstants.java11
-rw-r--r--libjava/classpath/javax/swing/text/StyleContext.java600
-rw-r--r--libjava/classpath/javax/swing/text/StyledEditorKit.java46
-rw-r--r--libjava/classpath/javax/swing/text/TextAction.java51
-rw-r--r--libjava/classpath/javax/swing/text/Utilities.java189
-rw-r--r--libjava/classpath/javax/swing/text/View.java104
-rw-r--r--libjava/classpath/javax/swing/text/WrappedPlainView.java199
-rw-r--r--libjava/classpath/javax/swing/text/ZoneView.java442
-rw-r--r--libjava/classpath/javax/swing/text/html/BRView.java5
-rw-r--r--libjava/classpath/javax/swing/text/html/BlockView.java482
-rw-r--r--libjava/classpath/javax/swing/text/html/CSS.java274
-rw-r--r--libjava/classpath/javax/swing/text/html/CSSBorder.java421
-rw-r--r--libjava/classpath/javax/swing/text/html/FormSubmitEvent.java123
-rw-r--r--libjava/classpath/javax/swing/text/html/FormView.java649
-rw-r--r--libjava/classpath/javax/swing/text/html/FrameSetView.java274
-rw-r--r--libjava/classpath/javax/swing/text/html/FrameView.java233
-rw-r--r--libjava/classpath/javax/swing/text/html/HTML.java22
-rw-r--r--libjava/classpath/javax/swing/text/html/HTMLDocument.java931
-rw-r--r--libjava/classpath/javax/swing/text/html/HTMLEditorKit.java578
-rw-r--r--libjava/classpath/javax/swing/text/html/HTMLWriter.java1084
-rw-r--r--libjava/classpath/javax/swing/text/html/ImageView.java403
-rw-r--r--libjava/classpath/javax/swing/text/html/InlineView.java151
-rw-r--r--libjava/classpath/javax/swing/text/html/ListView.java3
-rw-r--r--libjava/classpath/javax/swing/text/html/MultiAttributeSet.java213
-rw-r--r--libjava/classpath/javax/swing/text/html/MultiStyle.java136
-rw-r--r--libjava/classpath/javax/swing/text/html/Option.java12
-rw-r--r--libjava/classpath/javax/swing/text/html/ParagraphView.java131
-rw-r--r--libjava/classpath/javax/swing/text/html/ResetableModel.java50
-rw-r--r--libjava/classpath/javax/swing/text/html/ResetablePlainDocument.java82
-rw-r--r--libjava/classpath/javax/swing/text/html/ResetableToggleButtonModel.java71
-rw-r--r--libjava/classpath/javax/swing/text/html/SelectComboBoxModel.java84
-rw-r--r--libjava/classpath/javax/swing/text/html/SelectListModel.java106
-rw-r--r--libjava/classpath/javax/swing/text/html/StyleSheet.java1124
-rw-r--r--libjava/classpath/javax/swing/text/html/TableView.java942
-rw-r--r--libjava/classpath/javax/swing/text/html/ViewAttributeSet.java163
-rw-r--r--libjava/classpath/javax/swing/text/html/parser/AttributeList.java6
-rw-r--r--libjava/classpath/javax/swing/text/html/parser/ContentModel.java6
-rw-r--r--libjava/classpath/javax/swing/text/html/parser/DTD.java17
-rw-r--r--libjava/classpath/javax/swing/text/html/parser/DocumentParser.java4
-rw-r--r--libjava/classpath/javax/swing/text/html/parser/ParserDelegator.java4
-rw-r--r--libjava/classpath/javax/swing/tree/AbstractLayoutCache.java27
-rw-r--r--libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java12
-rw-r--r--libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java322
-rw-r--r--libjava/classpath/javax/swing/tree/DefaultTreeCellRenderer.java108
-rw-r--r--libjava/classpath/javax/swing/tree/DefaultTreeModel.java29
-rw-r--r--libjava/classpath/javax/swing/tree/DefaultTreeSelectionModel.java780
-rw-r--r--libjava/classpath/javax/swing/tree/FixedHeightLayoutCache.java2
-rw-r--r--libjava/classpath/javax/swing/tree/VariableHeightLayoutCache.java190
-rw-r--r--libjava/classpath/javax/swing/undo/CompoundEdit.java16
-rw-r--r--libjava/classpath/javax/swing/undo/StateEdit.java4
-rw-r--r--libjava/classpath/javax/swing/undo/StateEditable.java4
-rw-r--r--libjava/classpath/javax/swing/undo/UndoableEditSupport.java5
177 files changed, 21503 insertions, 8376 deletions
diff --git a/libjava/classpath/javax/swing/AbstractButton.java b/libjava/classpath/javax/swing/AbstractButton.java
index 63f827a1ae0..cb0f458b89f 100644
--- a/libjava/classpath/javax/swing/AbstractButton.java
+++ b/libjava/classpath/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;
@@ -187,9 +188,32 @@ public abstract class AbstractButton extends JComponent
*/
public void stateChanged(ChangeEvent ev)
{
- AbstractButton.this.fireStateChanged();
+ getEventHandler().stateChanged(ev);
+ }
+ }
+
+ /**
+ * The combined event handler for ActionEvent, ChangeEvent and
+ * ItemEvent. This combines ButtonChangeListener, ActionListener
+ */
+ private class EventHandler
+ implements ActionListener, ChangeListener, ItemListener
+ {
+ public void actionPerformed(ActionEvent ev)
+ {
+ fireActionPerformed(ev);
+ }
+
+ public void stateChanged(ChangeEvent ev)
+ {
+ fireStateChanged();
repaint();
}
+
+ public void itemStateChanged(ItemEvent ev)
+ {
+ fireItemStateChanged(ev);
+ }
}
/** The icon displayed by default. */
@@ -264,16 +288,29 @@ public abstract class AbstractButton extends JComponent
*/
int mnemonicIndex;
- /** Listener the button uses to receive ActionEvents from its model. */
+ /**
+ * Listener the button uses to receive ActionEvents from its model.
+ */
protected ActionListener actionListener;
- /** Listener the button uses to receive ItemEvents from its model. */
+ /**
+ * Listener the button uses to receive ItemEvents from its model.
+ */
protected ItemListener itemListener;
- /** Listener the button uses to receive ChangeEvents from its model. */
+ /**
+ * Listener the button uses to receive ChangeEvents from its model.
+ */
protected ChangeListener changeListener;
/**
+ * The event handler for ActionEvent, ItemEvent and ChangeEvent.
+ * This replaces the above three handlers and combines them
+ * into one for efficiency.
+ */
+ private EventHandler eventHandler;
+
+ /**
* The time in milliseconds in which clicks get coalesced into a single
* <code>ActionEvent</code>.
*/
@@ -768,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;
}
/**
@@ -801,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;
}
@@ -855,10 +1004,6 @@ public abstract class AbstractButton extends JComponent
*/
public AbstractButton()
{
- actionListener = createActionListener();
- changeListener = createChangeListener();
- itemListener = createItemListener();
-
horizontalAlignment = CENTER;
horizontalTextPosition = TRAILING;
verticalAlignment = CENTER;
@@ -872,7 +1017,10 @@ public abstract class AbstractButton extends JComponent
setDisplayedMnemonicIndex(-1);
setOpaque(true);
text = "";
- updateUI();
+ // testing on JRE1.5 shows that the iconTextGap default value is
+ // hard-coded here and the 'Button.iconTextGap' setting in the
+ // UI defaults is ignored, at least by the MetalLookAndFeel
+ iconTextGap = 4;
}
/**
@@ -900,15 +1048,21 @@ public abstract class AbstractButton extends JComponent
if (model != null)
{
model.removeActionListener(actionListener);
+ actionListener = null;
model.removeChangeListener(changeListener);
+ changeListener = null;
model.removeItemListener(itemListener);
+ itemListener = null;
}
ButtonModel old = model;
model = newModel;
if (model != null)
{
+ actionListener = createActionListener();
model.addActionListener(actionListener);
+ changeListener = createChangeListener();
model.addChangeListener(changeListener);
+ itemListener = createItemListener();
model.addItemListener(itemListener);
}
firePropertyChange(MODEL_CHANGED_PROPERTY, old, model);
@@ -927,6 +1081,8 @@ public abstract class AbstractButton extends JComponent
if (icon != null)
default_icon = icon;
+
+ updateUI();
}
/**
@@ -1923,13 +2079,7 @@ public abstract class AbstractButton extends JComponent
*/
protected ActionListener createActionListener()
{
- return new ActionListener()
- {
- public void actionPerformed(ActionEvent e)
- {
- AbstractButton.this.fireActionPerformed(e);
- }
- };
+ return getEventHandler();
}
/**
@@ -1995,7 +2145,7 @@ public abstract class AbstractButton extends JComponent
*/
protected ChangeListener createChangeListener()
{
- return new ButtonChangeListener();
+ return getEventHandler();
}
/**
@@ -2021,13 +2171,7 @@ public abstract class AbstractButton extends JComponent
*/
protected ItemListener createItemListener()
{
- return new ItemListener()
- {
- public void itemStateChanged(ItemEvent e)
- {
- AbstractButton.this.fireItemStateChanged(e);
- }
- };
+ return getEventHandler();
}
/**
@@ -2490,4 +2634,17 @@ public abstract class AbstractButton extends JComponent
super.setUIProperty(propertyName, value);
}
}
+
+ /**
+ * Returns the combined event handler. The instance is created if
+ * necessary.
+ *
+ * @return the combined event handler
+ */
+ EventHandler getEventHandler()
+ {
+ if (eventHandler == null)
+ eventHandler = new EventHandler();
+ return eventHandler;
+ }
}
diff --git a/libjava/classpath/javax/swing/AbstractListModel.java b/libjava/classpath/javax/swing/AbstractListModel.java
index 4b89689ddda..7d4b2bb2a87 100644
--- a/libjava/classpath/javax/swing/AbstractListModel.java
+++ b/libjava/classpath/javax/swing/AbstractListModel.java
@@ -164,7 +164,7 @@ public abstract class AbstractListModel implements ListModel, Serializable
*
* @return The set of listeners of the specified type
*/
- public EventListener[] getListeners(Class listenerType)
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
{
return listenerList.getListeners(listenerType);
}
diff --git a/libjava/classpath/javax/swing/AbstractSpinnerModel.java b/libjava/classpath/javax/swing/AbstractSpinnerModel.java
index 089e2048da2..d247a33136a 100644
--- a/libjava/classpath/javax/swing/AbstractSpinnerModel.java
+++ b/libjava/classpath/javax/swing/AbstractSpinnerModel.java
@@ -1,5 +1,5 @@
/* AbstractSpinnerModel.java --
- Copyright (C) 2004 Free Software Foundation, Inc.
+ Copyright (C) 2004, 2006, Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -47,6 +47,8 @@ import javax.swing.event.EventListenerList;
/**
* Provides standard implementations for some of the methods in
* {@link SpinnerModel}.
+ *
+ * @since 1.4
*
* @author Ka-Hing Cheung
*/
@@ -54,6 +56,7 @@ public abstract class AbstractSpinnerModel implements SpinnerModel
{
private ChangeEvent changeEvent = new ChangeEvent(this);
+ /** Stores the listeners registered with the model. */
protected EventListenerList listenerList = new EventListenerList();
/**
@@ -65,9 +68,10 @@ public abstract class AbstractSpinnerModel implements SpinnerModel
}
/**
- * Adds a <code>ChangeListener</code>.
+ * Registers a <code>ChangeListener</code> with the model so that it will
+ * receive {@link ChangeEvent} notifications when the model changes.
*
- * @param listener the listener to add
+ * @param listener the listener to add (<code>null</code> is ignored).
*/
public void addChangeListener(ChangeListener listener)
{
@@ -80,7 +84,7 @@ public abstract class AbstractSpinnerModel implements SpinnerModel
* @param c the type of listener
* @return the listeners that are of the specific type
*/
- public EventListener[] getListeners(Class c)
+ public <T extends EventListener> T[] getListeners(Class<T> c)
{
return listenerList.getListeners(c);
}
diff --git a/libjava/classpath/javax/swing/ButtonGroup.java b/libjava/classpath/javax/swing/ButtonGroup.java
index efa36b5f641..6a474f98d28 100644
--- a/libjava/classpath/javax/swing/ButtonGroup.java
+++ b/libjava/classpath/javax/swing/ButtonGroup.java
@@ -68,7 +68,7 @@ public class ButtonGroup implements Serializable
private static final long serialVersionUID = 4259076101881721375L;
/** Stores references to the buttons added to this button group. */
- protected Vector buttons = new Vector();
+ protected Vector<AbstractButton> buttons = new Vector<AbstractButton>();
/** The currently selected button model. */
ButtonModel sel;
@@ -129,7 +129,7 @@ public class ButtonGroup implements Serializable
*
* @return <code>Enumeration</code> over all added buttons
*/
- public Enumeration getElements()
+ public Enumeration<AbstractButton> getElements()
{
return buttons.elements();
}
@@ -183,6 +183,10 @@ public class ButtonGroup implements Serializable
if (old != null)
old.setSelected(false);
+
+ if (m != null)
+ sel.setSelected(true);
+
AbstractButton button = findButton(old);
if (button != null)
button.repaint();
diff --git a/libjava/classpath/javax/swing/DefaultBoundedRangeModel.java b/libjava/classpath/javax/swing/DefaultBoundedRangeModel.java
index efca148f449..786e4ee9204 100644
--- a/libjava/classpath/javax/swing/DefaultBoundedRangeModel.java
+++ b/libjava/classpath/javax/swing/DefaultBoundedRangeModel.java
@@ -424,7 +424,7 @@ public class DefaultBoundedRangeModel
*
* @since 1.3
*/
- public EventListener[] getListeners(Class listenerType)
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
{
return listenerList.getListeners(listenerType);
}
diff --git a/libjava/classpath/javax/swing/DefaultButtonModel.java b/libjava/classpath/javax/swing/DefaultButtonModel.java
index 020c904a4e9..c0eaea239b5 100644
--- a/libjava/classpath/javax/swing/DefaultButtonModel.java
+++ b/libjava/classpath/javax/swing/DefaultButtonModel.java
@@ -166,7 +166,7 @@ public class DefaultButtonModel implements ButtonModel, Serializable
*
* @return array of listeners
*/
- public EventListener[] getListeners(Class listenerType)
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
{
return listenerList.getListeners(listenerType);
}
@@ -425,7 +425,7 @@ public class DefaultButtonModel implements ButtonModel, Serializable
public void setRollover(boolean r)
{
// if this call does not represent a CHANGE in state, then return
- if ((r && isRollover()) || (!r && !isRollover()))
+ if (r == isRollover())
return;
// cannot set ROLLOVER property unless button is enabled
diff --git a/libjava/classpath/javax/swing/DefaultComboBoxModel.java b/libjava/classpath/javax/swing/DefaultComboBoxModel.java
index ef785f34dec..9b5bdb60d18 100644
--- a/libjava/classpath/javax/swing/DefaultComboBoxModel.java
+++ b/libjava/classpath/javax/swing/DefaultComboBoxModel.java
@@ -104,7 +104,7 @@ public class DefaultComboBoxModel extends AbstractListModel
*
* @throws NullPointerException if <code>vector</code> is <code>null</code>.
*/
- public DefaultComboBoxModel(Vector vector)
+ public DefaultComboBoxModel(Vector<?> vector)
{
this.list = vector;
if (getSize() > 0)
@@ -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/libjava/classpath/javax/swing/DefaultListModel.java b/libjava/classpath/javax/swing/DefaultListModel.java
index 2d02874a7e4..674864cce3a 100644
--- a/libjava/classpath/javax/swing/DefaultListModel.java
+++ b/libjava/classpath/javax/swing/DefaultListModel.java
@@ -309,7 +309,7 @@ public class DefaultListModel extends AbstractListModel
*
* @return A new enumeration which iterates over the list
*/
- public Enumeration elements()
+ public Enumeration<?> elements()
{
return elements.elements();
}
diff --git a/libjava/classpath/javax/swing/DefaultListSelectionModel.java b/libjava/classpath/javax/swing/DefaultListSelectionModel.java
index 482ce2cc224..d1e2da85fe8 100644
--- a/libjava/classpath/javax/swing/DefaultListSelectionModel.java
+++ b/libjava/classpath/javax/swing/DefaultListSelectionModel.java
@@ -815,7 +815,7 @@ public class DefaultListSelectionModel implements Cloneable,
* @see #getListSelectionListeners
* @since 1.3
*/
- public EventListener[] getListeners(Class listenerType)
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
{
return listenerList.getListeners(listenerType);
}
diff --git a/libjava/classpath/javax/swing/DefaultSingleSelectionModel.java b/libjava/classpath/javax/swing/DefaultSingleSelectionModel.java
index 1c6f473fdd3..3f79f0deed5 100644
--- a/libjava/classpath/javax/swing/DefaultSingleSelectionModel.java
+++ b/libjava/classpath/javax/swing/DefaultSingleSelectionModel.java
@@ -174,7 +174,7 @@ public class DefaultSingleSelectionModel
*
* @since 1.3
*/
- public EventListener[] getListeners(Class listenerClass)
+ public <T extends EventListener> T[] getListeners(Class<T> listenerClass)
{
return listenerList.getListeners(listenerClass);
}
diff --git a/libjava/classpath/javax/swing/JButton.java b/libjava/classpath/javax/swing/JButton.java
index 787adb87cf1..878cfa68397 100644
--- a/libjava/classpath/javax/swing/JButton.java
+++ b/libjava/classpath/javax/swing/JButton.java
@@ -132,8 +132,8 @@ public class JButton extends AbstractButton
public JButton(String text, Icon icon)
{
super();
- init(text, icon);
setModel(new DefaultButtonModel());
+ init(text, icon);
defaultCapable = true;
}
diff --git a/libjava/classpath/javax/swing/JComboBox.java b/libjava/classpath/javax/swing/JComboBox.java
index c75a94bdc36..fa6941cf977 100644
--- a/libjava/classpath/javax/swing/JComboBox.java
+++ b/libjava/classpath/javax/swing/JComboBox.java
@@ -196,7 +196,7 @@ public class JComboBox extends JComponent implements ItemSelectable,
*
* @param itemVector vector containing list of items for this JComboBox.
*/
- public JComboBox(Vector itemVector)
+ public JComboBox(Vector<?> itemVector)
{
this(new DefaultComboBoxModel(itemVector));
diff --git a/libjava/classpath/javax/swing/JComponent.java b/libjava/classpath/javax/swing/JComponent.java
index fa83502946d..5ec5079223e 100644
--- a/libjava/classpath/javax/swing/JComponent.java
+++ b/libjava/classpath/javax/swing/JComponent.java
@@ -69,6 +69,7 @@ import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.beans.VetoableChangeSupport;
import java.io.Serializable;
+import java.util.ArrayList;
import java.util.EventListener;
import java.util.Hashtable;
import java.util.Locale;
@@ -503,27 +504,6 @@ public abstract class JComponent extends Container implements Serializable
}
}
- /**
- * An explicit value for the component's preferred size; if not set by a
- * user, this is calculated on the fly by delegating to the {@link
- * ComponentUI#getPreferredSize} method on the {@link #ui} property.
- */
- Dimension preferredSize;
-
- /**
- * An explicit value for the component's minimum size; if not set by a
- * user, this is calculated on the fly by delegating to the {@link
- * ComponentUI#getMinimumSize} method on the {@link #ui} property.
- */
- Dimension minimumSize;
-
- /**
- * An explicit value for the component's maximum size; if not set by a
- * user, this is calculated on the fly by delegating to the {@link
- * ComponentUI#getMaximumSize} method on the {@link #ui} property.
- */
- Dimension maximumSize;
-
/**
* A value between 0.0 and 1.0 indicating the preferred horizontal
* alignment of the component, relative to its siblings. The values
@@ -561,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.
*
@@ -687,7 +659,7 @@ public abstract class JComponent extends Container implements Serializable
* Indicates whether we are calling paintDoubleBuffered() from
* paintImmadiately (RepaintManager) or from paint() (AWT refresh).
*/
- static private boolean isRepainting = false;
+ static boolean isRepainting = false;
/**
* Listeners for events other than {@link PropertyChangeEvent} are
@@ -783,6 +755,13 @@ public abstract class JComponent extends Container implements Serializable
*/
public static final int WHEN_IN_FOCUSED_WINDOW = 2;
+
+ /**
+ * Used to optimize painting. This is set in paintImmediately2() to specify
+ * the exact component path to be painted by paintChildren.
+ */
+ Component paintChild;
+
/**
* Indicates if the opaque property has been set by a client program or by
* the UI.
@@ -868,7 +847,12 @@ public abstract class JComponent extends Container implements Serializable
t.put(key, value);
else
t.remove(key);
- firePropertyChange(key.toString(), old, value);
+
+ // When both old and new value are null, no event is fired. This is
+ // different from what firePropertyChange() normally does, so we add this
+ // check here.
+ if (old != null || value != null)
+ firePropertyChange(key.toString(), old, value);
}
/**
@@ -943,12 +927,12 @@ public abstract class JComponent extends Container implements Serializable
*
* @since 1.3
*/
- public EventListener[] getListeners(Class listenerType)
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
{
if (listenerType == PropertyChangeListener.class)
- return getPropertyChangeListeners();
+ return (T[]) getPropertyChangeListeners();
else if (listenerType == VetoableChangeListener.class)
- return getVetoableChangeListeners();
+ return (T[]) getVetoableChangeListeners();
else
return listenerList.getListeners(listenerType);
}
@@ -1270,37 +1254,38 @@ public abstract class JComponent extends Container implements Serializable
}
/**
- * Get the component's maximum size. If the {@link #maximumSize} property
- * has been explicitly set, it is returned. If the {@link #maximumSize}
+ * Get the component's maximum size. If the <code>maximumSize</code> property
+ * has been explicitly set, it is returned. If the <code>maximumSize</code>
* property has not been set but the {@link #ui} property has been, the
* result of {@link ComponentUI#getMaximumSize} is returned. If neither
* property has been set, the result of {@link Container#getMaximumSize}
* is returned.
*
- * @return The maximum size of the component
+ * @return the maximum size of the component
*
- * @see #maximumSize
- * @see #setMaximumSize
+ * @see Component#setMaximumSize
+ * @see Component#getMaximumSize()
+ * @see Component#isMaximumSizeSet()
+ * @see ComponentUI#getMaximumSize(JComponent)
*/
public Dimension getMaximumSize()
{
- if (maximumSize != null)
- return maximumSize;
-
- if (ui != null)
+ Dimension size = null;
+ if (isMaximumSizeSet())
+ size = super.getMaximumSize();
+ else
{
- Dimension s = ui.getMaximumSize(this);
- if (s != null)
- return s;
+ if (ui != null)
+ size = ui.getMaximumSize(this);
+ if (size == null)
+ size = super.getMaximumSize();
}
-
- Dimension p = super.getMaximumSize();
- return p;
+ return size;
}
/**
- * Get the component's minimum size. If the {@link #minimumSize} property
- * has been explicitly set, it is returned. If the {@link #minimumSize}
+ * Get the component's minimum size. If the <code>minimumSize</code> property
+ * has been explicitly set, it is returned. If the <code>minimumSize</code>
* property has not been set but the {@link #ui} property has been, the
* result of {@link ComponentUI#getMinimumSize} is returned. If neither
* property has been set, the result of {@link Container#getMinimumSize}
@@ -1308,97 +1293,57 @@ public abstract class JComponent extends Container implements Serializable
*
* @return The minimum size of the component
*
- * @see #minimumSize
- * @see #setMinimumSize
+ * @see Component#setMinimumSize
+ * @see Component#getMinimumSize()
+ * @see Component#isMinimumSizeSet()
+ * @see ComponentUI#getMinimumSize(JComponent)
*/
public Dimension getMinimumSize()
{
- if (minimumSize != null)
- return minimumSize;
-
- if (ui != null)
+ Dimension size = null;
+ if (isMinimumSizeSet())
+ size = super.getMinimumSize();
+ else
{
- Dimension s = ui.getMinimumSize(this);
- if (s != null)
- return s;
+ if (ui != null)
+ size = ui.getMinimumSize(this);
+ if (size == null)
+ size = super.getMinimumSize();
}
-
- Dimension p = super.getMinimumSize();
- return p;
+ return size;
}
/**
- * Get the component's preferred size. If the {@link #preferredSize}
- * property has been explicitly set, it is returned. If the {@link
- * #preferredSize} property has not been set but the {@link #ui} property
- * has been, the result of {@link ComponentUI#getPreferredSize} is
+ * Get the component's preferred size. If the <code>preferredSize</code>
+ * property has been explicitly set, it is returned. If the
+ * <code>preferredSize</code> property has not been set but the {@link #ui}
+ * property has been, the result of {@link ComponentUI#getPreferredSize} is
* returned. If neither property has been set, the result of {@link
* Container#getPreferredSize} is returned.
*
* @return The preferred size of the component
*
- * @see #preferredSize
- * @see #setPreferredSize
+ * @see Component#setPreferredSize
+ * @see Component#getPreferredSize()
+ * @see Component#isPreferredSizeSet()
+ * @see ComponentUI#getPreferredSize(JComponent)
*/
public Dimension getPreferredSize()
{
- Dimension prefSize = null;
- if (preferredSize != null)
- prefSize = new Dimension(preferredSize);
-
- else if (ui != null)
+ Dimension size = null;
+ if (isPreferredSizeSet())
+ size = super.getPreferredSize();
+ else
{
- Dimension s = ui.getPreferredSize(this);
- if (s != null)
- prefSize = s;
+ if (ui != null)
+ size = ui.getPreferredSize(this);
+ if (size == null)
+ size = super.getPreferredSize();
}
-
- if (prefSize == null)
- prefSize = super.getPreferredSize();
-
- return prefSize;
+ return size;
}
/**
- * Checks if a maximum size was explicitely set on the component.
- *
- * @return <code>true</code> if a maximum size was set,
- * <code>false</code> otherwise
- *
- * @since 1.3
- */
- public boolean isMaximumSizeSet()
- {
- return maximumSize != null;
- }
-
- /**
- * Checks if a minimum size was explicitely set on the component.
- *
- * @return <code>true</code> if a minimum size was set,
- * <code>false</code> otherwise
- *
- * @since 1.3
- */
- public boolean isMinimumSizeSet()
- {
- return minimumSize != null;
- }
-
- /**
- * Checks if a preferred size was explicitely set on the component.
- *
- * @return <code>true</code> if a preferred size was set,
- * <code>false</code> otherwise
- *
- * @since 1.3
- */
- public boolean isPreferredSizeSet()
- {
- return preferredSize != null;
- }
-
- /**
* Return the value of the <code>nextFocusableComponent</code> property.
*
* @return The current value of the property, or <code>null</code>
@@ -1491,14 +1436,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
*
@@ -1511,53 +1454,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)
{
@@ -1850,7 +1796,7 @@ public abstract class JComponent extends Container implements Serializable
&& rm.isDoubleBufferingEnabled())
{
Rectangle clip = g.getClipBounds();
- paintDoubleBuffered(clip);
+ paintDoubleBuffered(clip.x, clip.y, clip.width, clip.height);
}
else
{
@@ -1865,8 +1811,22 @@ public abstract class JComponent extends Container implements Serializable
dragBuffer = null;
}
- if (g.getClip() == null)
- g.setClip(0, 0, getWidth(), getHeight());
+ Rectangle clip = g.getClipBounds();
+ int clipX, clipY, clipW, clipH;
+ if (clip == null)
+ {
+ clipX = 0;
+ clipY = 0;
+ clipW = getWidth();
+ clipH = getHeight();
+ }
+ else
+ {
+ clipX = clip.x;
+ clipY = clip.y;
+ clipW = clip.width;
+ clipH = clip.height;
+ }
if (dragBuffer != null && dragBufferInitialized)
{
g.drawImage(dragBuffer, 0, 0, this);
@@ -1874,14 +1834,51 @@ public abstract class JComponent extends Container implements Serializable
else
{
Graphics g2 = getComponentGraphics(g);
- paintComponent(g2);
- paintBorder(g2);
+ if (! isOccupiedByChild(clipX, clipY, clipW, clipH))
+ {
+ paintComponent(g2);
+ paintBorder(g2);
+ }
paintChildren(g2);
}
}
}
/**
+ * Determines if a region of this component is completely occupied by
+ * an opaque child component, in which case we don't need to bother
+ * painting this component at all.
+ *
+ * @param x the area, x coordinate
+ * @param y the area, y coordinate
+ * @param w the area, width
+ * @param h the area, height
+ *
+ * @return <code>true</code> if the specified area is completely covered
+ * by a child component, <code>false</code> otherwise
+ */
+ private boolean isOccupiedByChild(int x, int y, int w, int h)
+ {
+ boolean occupied = false;
+ int count = getComponentCount();
+ for (int i = 0; i < count; i++)
+ {
+ Component child = getComponent(i);
+ int cx = child.getX();
+ int cy = child.getY();
+ int cw = child.getWidth();
+ int ch = child.getHeight();
+ if (child.isVisible() && x >= cx && x + w <= cx + cw && y >= cy
+ && y + h <= cy + ch)
+ {
+ occupied = child.isOpaque();
+ break;
+ }
+ }
+ return occupied;
+ }
+
+ /**
* Initializes the drag buffer by creating a new image and painting this
* component into it.
*/
@@ -1940,7 +1937,14 @@ public abstract class JComponent extends Container implements Serializable
// Need to lock the tree to avoid problems with AWT and concurrency.
synchronized (getTreeLock())
{
- for (int i = getComponentCount() - 1; i >= 0; i--)
+ // Fast forward to the child to paint, if set by
+ // paintImmediately2()
+ int i = getComponentCount() - 1;
+ if (paintChild != null && paintChild.isOpaque())
+ {
+ for (; i >= 0 && getComponent(i) != paintChild; i--);
+ }
+ for (; i >= 0; i--)
{
Component child = getComponent(i);
if (child != null && child.isLightweight()
@@ -1958,7 +1962,8 @@ public abstract class JComponent extends Container implements Serializable
Rectangle clip = g.getClipBounds(); // A copy.
SwingUtilities.computeIntersection(cx, cy, cw, ch,
clip);
- if (isCompletelyObscured(i, clip))
+ if (isCompletelyObscured(i, clip.x, clip.y,
+ clip.width, clip.height))
continue; // Continues the for-loop.
}
Graphics cg = g.create(cx, cy, cw, ch);
@@ -1984,12 +1989,15 @@ public abstract class JComponent extends Container implements Serializable
* of its siblings.
*
* @param index the index of the child component
- * @param rect the region to check
+ * @param x the region to check, x coordinate
+ * @param y the region to check, y coordinate
+ * @param w the region to check, width
+ * @param h the region to check, height
*
* @return <code>true</code> if the region is completely obscured by a
* sibling, <code>false</code> otherwise
*/
- private boolean isCompletelyObscured(int index, Rectangle rect)
+ private boolean isCompletelyObscured(int index, int x, int y, int w, int h)
{
boolean obscured = false;
for (int i = index - 1; i >= 0 && obscured == false; i--)
@@ -1998,10 +2006,10 @@ public abstract class JComponent extends Container implements Serializable
if (sib.isVisible())
{
Rectangle sibRect = sib.getBounds(rectCache);
- if (sib.isOpaque() && rect.x >= sibRect.x
- && (rect.x + rect.width) <= (sibRect.x + sibRect.width)
- && rect.y >= sibRect.y
- && (rect.y + rect.height) <= (sibRect.y + sibRect.height))
+ if (sib.isOpaque() && x >= sibRect.x
+ && (x + w) <= (sibRect.x + sibRect.width)
+ && y >= sibRect.y
+ && (y + h) <= (sibRect.y + sibRect.height))
{
obscured = true;
}
@@ -2011,6 +2019,39 @@ public abstract class JComponent extends Container implements Serializable
}
/**
+ * Checks if a component/rectangle is partially obscured by one of its
+ * siblings.
+ * Note that this doesn't check for completely obscured, this is
+ * done by isCompletelyObscured() and should probably also be checked.
+ *
+ * @param i the component index from which to start searching
+ * @param x the x coordinate of the rectangle to check
+ * @param y the y coordinate of the rectangle to check
+ * @param w the width of the rectangle to check
+ * @param h the height of the rectangle to check
+ *
+ * @return <code>true</code> if the rectangle is partially obscured
+ */
+ private boolean isPartiallyObscured(int i, int x, int y, int w, int h)
+ {
+ boolean obscured = false;
+ for (int j = i - 1; j >= 0 && ! obscured; j--)
+ {
+ Component sibl = getComponent(j);
+ if (sibl.isVisible())
+ {
+ Rectangle rect = sibl.getBounds(rectCache);
+ if (!(x + w <= rect.x)
+ || (y + h <= rect.y)
+ || (x >= rect.x + rect.width)
+ || (y >= rect.y + rect.height))
+ obscured = true;
+ }
+ }
+ return obscured;
+ }
+
+ /**
* Paint the component's body. This usually means calling {@link
* ComponentUI#update} on the {@link #ui} property of the component, if
* it is non-<code>null</code>. You may override this if you wish to
@@ -2050,7 +2091,26 @@ public abstract class JComponent extends Container implements Serializable
*/
public void paintImmediately(int x, int y, int w, int h)
{
- paintImmediately(new Rectangle(x, y, w, h));
+ // Find opaque parent and call paintImmediately2() on it.
+ if (isShowing())
+ {
+ Component c = this;
+ Component p;
+ while (c != null && ! c.isOpaque())
+ {
+ p = c.getParent();
+ if (p != null)
+ {
+ x += c.getX();
+ y += c.getY();
+ c = p;
+ }
+ }
+ if (c instanceof JComponent)
+ ((JComponent) c).paintImmediately2(x, y, w, h);
+ else
+ c.repaint(x, y, w, h);
+ }
}
/**
@@ -2073,60 +2133,202 @@ public abstract class JComponent extends Container implements Serializable
*/
public void paintImmediately(Rectangle r)
{
- // Try to find a root pane for this component.
- //Component root = findPaintRoot(r);
- Component root = findPaintRoot(r);
- // If no paint root is found, then this component is completely overlapped
- // by another component and we don't need repainting.
- if (root == null|| !root.isShowing())
- return;
- SwingUtilities.convertRectangleToAncestor(this, r, root);
- if (root instanceof JComponent)
- ((JComponent) root).paintImmediately2(r);
- else
- root.repaint(r.x, r.y, r.width, r.height);
+ paintImmediately(r.x, r.y, r.width, r.height);
}
/**
* Performs the actual work of paintImmediatly on the repaint root.
*
- * @param r the area to be repainted
+ * @param x the area to be repainted, X coordinate
+ * @param y the area to be repainted, Y coordinate
*/
- void paintImmediately2(Rectangle r)
+ void paintImmediately2(int x, int y, int w, int h)
{
- isRepainting = true;
+ // Optimization for components that are always painted on top.
+ boolean onTop = onTop() && isOpaque();
+
+ // Fetch the RepaintManager.
RepaintManager rm = RepaintManager.currentManager(this);
- if (rm.isDoubleBufferingEnabled() && isPaintingDoubleBuffered())
- paintDoubleBuffered(r);
- else
- paintSimple(r);
- isRepainting = false;
+
+ // The painting clip;
+ int paintX = x;
+ int paintY = y;
+ int paintW = w;
+ int paintH = h;
+
+ // If we should paint buffered or not.
+ boolean haveBuffer = false;
+
+ // The component that is finally triggered for painting.
+ JComponent paintRoot = this;
+
+ // Stores the component and all its parents. This will be used to limit
+ // the actually painted components in paintChildren by setting
+ // the field paintChild.
+ int pIndex = -1;
+ int pCount = 0;
+ ArrayList components = new ArrayList();
+
+ // Offset to subtract from the paintRoot rectangle when painting.
+ int offsX = 0;
+ int offsY = 0;
+
+ // The current component and its child.
+ Component child;
+ Container c;
+
+ // Find appropriate paint root.
+ for (c = this, child = null;
+ c != null && ! (c instanceof Window) && ! (c instanceof Applet);
+ child = c, c = c.getParent())
+ {
+ JComponent jc = c instanceof JComponent ? (JComponent) c : null;
+ 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.
+ // 2. Partially obscured. Paint from the parent.
+ // 3. Completely obscured. No need to paint anything.
+ if (c != this)
+ {
+ 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,
+ 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.
+ if (rm.isDoubleBufferingEnabled() && jc != null
+ && jc.isDoubleBuffered())
+ {
+ haveBuffer = true;
+ }
+
+ // Clip the paint region with the parent.
+ if (! onTop)
+ {
+ paintX = Math.max(0, paintX);
+ paintY = Math.max(0, paintY);
+ paintW = Math.min(c.getWidth(), paintW + paintX) - paintX;
+ paintH = Math.min(c.getHeight(), paintH + paintY) - paintY;
+ int dx = c.getX();
+ int dy = c.getY();
+ paintX += dx;
+ paintY += dy;
+ offsX += dx;
+ offsY += dy;
+ }
+ }
+ if (c != null && c.getPeer() != null && paintW > 0 && paintH > 0)
+ {
+ isRepainting = true;
+ paintX -= offsX;
+ paintY -= offsY;
+
+ // Set the painting path so that paintChildren paints only what we
+ // want.
+ if (paintRoot != this)
+ {
+ for (int i = pIndex; i > 0; i--)
+ {
+ Component paintParent = (Component) components.get(i);
+ if (paintParent instanceof JComponent)
+ ((JComponent) paintParent).paintChild =
+ (Component) components.get(i - 1);
+ }
+ }
+
+ // Actually trigger painting.
+ if (haveBuffer)
+ paintRoot.paintDoubleBuffered(paintX, paintY, paintW, paintH);
+ else
+ {
+ Graphics g = paintRoot.getGraphics();
+ try
+ {
+ g.setClip(paintX, paintY, paintW, paintH);
+ paintRoot.paint(g);
+ }
+ finally
+ {
+ g.dispose();
+ }
+ }
+
+ // Reset the painting path.
+ if (paintRoot != this)
+ {
+ for (int i = pIndex; i > 0; i--)
+ {
+ Component paintParent = (Component) components.get(i);
+ if (paintParent instanceof JComponent)
+ ((JComponent) paintParent).paintChild = null;
+ }
+ }
+
+ isRepainting = false;
+ }
}
/**
- * Returns true if we must paint double buffered, that is, when this
- * component or any of it's ancestors are double buffered.
+ * Returns <code>true</code> if the component is guaranteed to be painted
+ * on top of others. This returns false by default and is overridden by
+ * components like JMenuItem, JPopupMenu and JToolTip to return true for
+ * added efficiency.
*
- * @return true if we must paint double buffered, that is, when this
- * component or any of it's ancestors are double buffered
+ * @return <code>true</code> if the component is guaranteed to be painted
+ * on top of others
*/
- private boolean isPaintingDoubleBuffered()
+ boolean onTop()
{
- boolean doubleBuffered = isDoubleBuffered();
- Component parent = getParent();
- while (! doubleBuffered && parent != null)
- {
- doubleBuffered = parent instanceof JComponent
- && ((JComponent) parent).isDoubleBuffered();
- parent = parent.getParent();
- }
- return doubleBuffered;
+ return false;
+ }
+
+ /**
+ * 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(Rectangle r)
+ private void paintDoubleBuffered(int x, int y, int w, int h)
{
RepaintManager rm = RepaintManager.currentManager(this);
@@ -2143,7 +2345,7 @@ public abstract class JComponent extends Container implements Serializable
//Rectangle targetClip = SwingUtilities.convertRectangle(this, r, root);
Graphics g2 = buffer.getGraphics();
clipAndTranslateGraphics(root, this, g2);
- g2.clipRect(r.x, r.y, r.width, r.height);
+ g2.clipRect(x, y, w, h);
g2 = getComponentGraphics(g2);
paintingDoubleBuffered = true;
try
@@ -2164,7 +2366,7 @@ public abstract class JComponent extends Container implements Serializable
}
// Paint the buffer contents on screen.
- rm.commitBuffer(this, r);
+ rm.commitBuffer(this, x, y, w, h);
}
/**
@@ -2577,7 +2779,7 @@ public abstract class JComponent extends Container implements Serializable
KeyEvent e,
int condition,
boolean pressed)
- {
+ {
if (isEnabled())
{
Action act = null;
@@ -2591,7 +2793,7 @@ public abstract class JComponent extends Container implements Serializable
if (cmd instanceof ActionListenerProxy)
act = (Action) cmd;
else
- act = (Action) getActionMap().get(cmd);
+ act = getActionMap().get(cmd);
}
}
if (act != null && act.isEnabled())
@@ -2741,9 +2943,25 @@ public abstract class JComponent extends Container implements Serializable
*/
public void scrollRectToVisible(Rectangle r)
{
- Component p = getParent();
- if (p instanceof JComponent)
- ((JComponent) p).scrollRectToVisible(r);
+ // Search nearest JComponent.
+ int xOffs = getX();
+ int yOffs = getY();
+ Component p;
+ for (p = getParent(); p != null && ! (p instanceof JComponent);
+ p = p.getParent())
+ {
+ xOffs += p.getX();
+ yOffs += p.getY();
+ }
+ if (p != null)
+ {
+ r.x += xOffs;
+ r.y += yOffs;
+ JComponent jParent = (JComponent) p;
+ jParent.scrollRectToVisible(r);
+ r.x -= xOffs;
+ r.y -= yOffs;
+ }
}
/**
@@ -2862,57 +3080,6 @@ public abstract class JComponent extends Container implements Serializable
}
/**
- * Set the value of the {@link #maximumSize} property. The passed value is
- * copied, the later direct changes on the argument have no effect on the
- * property value.
- *
- * @param max The new value of the property
- */
- public void setMaximumSize(Dimension max)
- {
- Dimension oldMaximumSize = maximumSize;
- if (max != null)
- maximumSize = new Dimension(max);
- else
- maximumSize = null;
- firePropertyChange("maximumSize", oldMaximumSize, maximumSize);
- }
-
- /**
- * Set the value of the {@link #minimumSize} property. The passed value is
- * copied, the later direct changes on the argument have no effect on the
- * property value.
- *
- * @param min The new value of the property
- */
- public void setMinimumSize(Dimension min)
- {
- Dimension oldMinimumSize = minimumSize;
- if (min != null)
- minimumSize = new Dimension(min);
- else
- minimumSize = null;
- firePropertyChange("minimumSize", oldMinimumSize, minimumSize);
- }
-
- /**
- * Set the value of the {@link #preferredSize} property. The passed value is
- * copied, the later direct changes on the argument have no effect on the
- * property value.
- *
- * @param pref The new value of the property
- */
- public void setPreferredSize(Dimension pref)
- {
- Dimension oldPreferredSize = preferredSize;
- if (pref != null)
- preferredSize = new Dimension(pref);
- else
- preferredSize = null;
- firePropertyChange("preferredSize", oldPreferredSize, preferredSize);
- }
-
- /**
* Set the specified component to be the next component in the
* focus cycle, overriding the {@link FocusTraversalPolicy} for
* this component.
@@ -3571,130 +3738,6 @@ public abstract class JComponent extends Container implements Serializable
jc.fireAncestorEvent(ancestor, id);
}
}
-
- /**
- * Finds a suitable paint root for painting this component. This method first
- * checks if this component is overlapped using
- * {@link #findOverlapFreeParent(Rectangle)}. The returned paint root is then
- * feeded to {@link #findOpaqueParent(Component)} to find the nearest opaque
- * component for this paint root. If no paint is necessary, then we return
- * <code>null</code>.
- *
- * @param c the clip of this component
- *
- * @return the paint root or <code>null</code> if no painting is necessary
- */
- private Component findPaintRoot(Rectangle c)
- {
- Component p = findOverlapFreeParent(c);
- if (p == null)
- return null;
- Component root = findOpaqueParent(p);
- return root;
- }
-
- /**
- * Scans the containment hierarchy upwards for components that overlap the
- * this component in the specified clip. This method returns
- * <code>this</code>, if no component overlaps this component. It returns
- * <code>null</code> if another component completely covers this component
- * in the specified clip (no repaint necessary). If another component partly
- * overlaps this component in the specified clip, then the parent of this
- * component is returned (this is the component that must be used as repaint
- * root). For efficient lookup, the method
- * {@link #isOptimizedDrawingEnabled()} is used.
- *
- * @param clip the clip of this component
- *
- * @return the paint root, or <code>null</code> if no paint is necessary
- */
- private Component findOverlapFreeParent(Rectangle clip)
- {
- Rectangle currentClip = clip;
- Component found = this;
- Container parent = this;
-
- while (parent != null && !(parent instanceof Window))
- {
- Container newParent = parent.getParent();
- if (newParent == null || newParent instanceof Window)
- break;
- // If the parent is optimizedDrawingEnabled, then its children are
- // tiled and cannot have an overlapping child. Go directly to next
- // parent.
- if ((newParent instanceof JComponent
- && ((JComponent) newParent).isOptimizedDrawingEnabled()))
-
- {
- parent = newParent;
- continue;
- }
-
- // If the parent is not optimizedDrawingEnabled, we must check if the
- // parent or some neighbor overlaps the current clip.
-
- // This is the current clip converted to the parent's coordinate
- // system. TODO: We can do this more efficiently by succesively
- // cumulating the parent-child translations.
- Rectangle target = SwingUtilities.convertRectangle(found,
- currentClip,
- newParent);
-
- // We have an overlap if either:
- // - The new parent itself doesn't completely cover the clip
- // (this can be the case with viewports).
- // - If some higher-level (than the current) children of the new parent
- // intersect the target rectangle.
- Rectangle parentRect = SwingUtilities.getLocalBounds(newParent);
- boolean haveOverlap =
- ! SwingUtilities.isRectangleContainingRectangle(parentRect, target);
- if (! haveOverlap)
- {
- Component child;
- for (int i = 0; (child = newParent.getComponent(i)) != parent && !haveOverlap; i++)
- {
- Rectangle childRect = child.getBounds();
- haveOverlap = target.intersects(childRect);
- }
- }
- if (haveOverlap)
- {
- found = newParent;
- currentClip = target;
- }
- parent = newParent;
- }
- //System.err.println("overlapfree parent: " + found);
- return found;
- }
-
- /**
- * Finds the nearest component to <code>c</code> (upwards in the containment
- * hierarchy), that is opaque. If <code>c</code> itself is opaque,
- * this returns <code>c</code> itself.
- *
- * @param c the start component for the search
- * @return the nearest component to <code>c</code> (upwards in the containment
- * hierarchy), that is opaque; If <code>c</code> itself is opaque,
- * this returns <code>c</code> itself
- */
- private Component findOpaqueParent(Component c)
- {
- Component found = c;
- while (true)
- {
- if ((found instanceof JComponent) && ((JComponent) found).isOpaque())
- break;
- else if (!(found instanceof JComponent) && !found.isLightweight())
- break;
- Container p = found.getParent();
- if (p == null)
- break;
- else
- found = p;
- }
- return found;
- }
/**
* This is the method that gets called when the WHEN_IN_FOCUSED_WINDOW map
diff --git a/libjava/classpath/javax/swing/JDialog.java b/libjava/classpath/javax/swing/JDialog.java
index 08dada2fd81..495c9c791d7 100644
--- a/libjava/classpath/javax/swing/JDialog.java
+++ b/libjava/classpath/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/libjava/classpath/javax/swing/JEditorPane.java b/libjava/classpath/javax/swing/JEditorPane.java
index 4ae3c5a1c6b..38b0761f00f 100644
--- a/libjava/classpath/javax/swing/JEditorPane.java
+++ b/libjava/classpath/javax/swing/JEditorPane.java
@@ -40,6 +40,8 @@ package javax.swing;
import java.awt.Container;
import java.awt.Dimension;
+import java.io.BufferedInputStream;
+import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -47,6 +49,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;
@@ -56,6 +59,8 @@ import javax.accessibility.AccessibleStateSet;
import javax.accessibility.AccessibleText;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
+import javax.swing.plaf.TextUI;
+import javax.swing.text.AbstractDocument;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultEditorKit;
import javax.swing.text.Document;
@@ -482,6 +487,34 @@ public class JEditorPane extends JTextComponent
}
/**
+ * Used to store a mapping for content-type to editor kit class.
+ */
+ private static class EditorKitMapping
+ {
+ /**
+ * The classname of the editor kit.
+ */
+ String className;
+
+ /**
+ * The classloader with which the kit is to be loaded.
+ */
+ ClassLoader classLoader;
+
+ /**
+ * Creates a new EditorKitMapping object.
+ *
+ * @param cn the classname
+ * @param cl the classloader
+ */
+ EditorKitMapping(String cn, ClassLoader cl)
+ {
+ className = cn;
+ classLoader = cl;
+ }
+ }
+
+ /**
* An EditorKit used for plain text. This is the default editor kit for
* JEditorPanes.
*
@@ -505,19 +538,159 @@ public class JEditorPane extends JTextComponent
}
}
+ /**
+ * A special stream that can be cancelled.
+ */
+ private class PageStream
+ extends FilterInputStream
+ {
+ /**
+ * True when the stream has been cancelled, false otherwise.
+ */
+ private boolean cancelled;
+
+ protected PageStream(InputStream in)
+ {
+ super(in);
+ cancelled = false;
+ }
+
+ private void checkCancelled()
+ throws IOException
+ {
+ if (cancelled)
+ throw new IOException("Stream has been cancelled");
+ }
+
+ void cancel()
+ {
+ cancelled = true;
+ }
+
+ public int read()
+ throws IOException
+ {
+ checkCancelled();
+ return super.read();
+ }
+
+ public int read(byte[] b, int off, int len)
+ throws IOException
+ {
+ checkCancelled();
+ return super.read(b, off, len);
+ }
+
+ public long skip(long n)
+ throws IOException
+ {
+ checkCancelled();
+ return super.skip(n);
+ }
+
+ public int available()
+ throws IOException
+ {
+ checkCancelled();
+ return super.available();
+ }
+
+ public void reset()
+ throws IOException
+ {
+ checkCancelled();
+ super.reset();
+ }
+ }
+
+ /**
+ * The thread that loads documents asynchronously.
+ */
+ private class PageLoader
+ implements Runnable
+ {
+ private Document doc;
+ private PageStream in;
+ private URL old;
+ URL page;
+ PageLoader(Document doc, InputStream in, URL old, URL page)
+ {
+ this.doc = doc;
+ this.in = new PageStream(in);
+ this.old = old;
+ this.page = page;
+ }
+
+ public void run()
+ {
+ try
+ {
+ read(in, doc);
+ }
+ catch (IOException ex)
+ {
+ UIManager.getLookAndFeel().provideErrorFeedback(JEditorPane.this);
+ }
+ finally
+ {
+ if (SwingUtilities.isEventDispatchThread())
+ firePropertyChange("page", old, page);
+ else
+ {
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ firePropertyChange("page", old, page);
+ }
+ });
+ }
+ }
+ }
+
+ void cancel()
+ {
+ in.cancel();
+ }
+ }
+
private static final long serialVersionUID = 3140472492599046285L;
- private URL page;
private EditorKit editorKit;
boolean focus_root;
+ /**
+ * Maps content-types to editor kit instances.
+ */
+ static HashMap editorKits;
+
// A mapping between content types and registered EditorKit types
static HashMap registerMap;
-
+
+ static
+ {
+ registerMap = new HashMap();
+ editorKits = new HashMap();
+ registerEditorKitForContentType("application/rtf",
+ "javax.swing.text.rtf.RTFEditorKit");
+ registerEditorKitForContentType("text/plain",
+ "javax.swing.JEditorPane$PlainEditorKit");
+ registerEditorKitForContentType("text/html",
+ "javax.swing.text.html.HTMLEditorKit");
+ registerEditorKitForContentType("text/rtf",
+ "javax.swing.text.rtf.RTFEditorKit");
+
+ }
+
// A mapping between content types and used EditorKits
HashMap editorMap;
+ /**
+ * The currently loading stream, if any.
+ */
+ private PageLoader loader;
+
public JEditorPane()
{
init();
@@ -550,15 +723,6 @@ public class JEditorPane extends JTextComponent
void init()
{
editorMap = new HashMap();
- registerMap = new HashMap();
- registerEditorKitForContentType("application/rtf",
- "javax.swing.text.rtf.RTFEditorKit");
- registerEditorKitForContentType("text/plain",
- "javax.swing.JEditorPane$PlainEditorKit");
- registerEditorKitForContentType("text/html",
- "javax.swing.text.html.HTMLEditorKit");
- registerEditorKitForContentType("text/rtf",
- "javax.swing.text.rtf.RTFEditorKit");
}
protected EditorKit createDefaultEditorKit()
@@ -578,20 +742,28 @@ public class JEditorPane extends JTextComponent
*/
public static EditorKit createEditorKitForContentType(String type)
{
- // TODO: Have to handle the case where a ClassLoader was specified
- // when the EditorKit was registered
- EditorKit e = null;
- String className = (String) registerMap.get(type);
- if (className != null)
+ // Try cached instance.
+ EditorKit e = (EditorKit) editorKits.get(type);
+ if (e == null)
{
- try
- {
- e = (EditorKit) Class.forName(className).newInstance();
- }
- catch (Exception e2)
- {
- // TODO: Not sure what to do here.
- }
+ EditorKitMapping m = (EditorKitMapping) registerMap.get(type);
+ if (m != null)
+ {
+ String className = m.className;
+ ClassLoader loader = m.classLoader;
+ try
+ {
+ e = (EditorKit) loader.loadClass(className).newInstance();
+ }
+ catch (Exception e2)
+ {
+ // The reference implementation returns null when class is not
+ // loadable or instantiatable.
+ }
+ }
+ // Cache this for later retrieval.
+ if (e != null)
+ editorKits.put(type, e);
}
return e;
}
@@ -652,7 +824,9 @@ public class JEditorPane extends JTextComponent
*/
public static String getEditorKitClassNameForContentType(String type)
{
- return (String) registerMap.get(type);
+ EditorKitMapping m = (EditorKitMapping) registerMap.get(type);
+ String kitName = m != null ? m.className : null;
+ return kitName;
}
/**
@@ -675,10 +849,14 @@ public class JEditorPane extends JTextComponent
EditorKit e = (EditorKit) editorMap.get(type);
// Then check to see if we can create one.
if (e == null)
- e = createEditorKitForContentType(type);
+ {
+ e = createEditorKitForContentType(type);
+ if (e != null)
+ setEditorKitForContentType(type, e);
+ }
// Otherwise default to PlainEditorKit.
if (e == null)
- e = new PlainEditorKit();
+ e = createDefaultEditorKit();
return e;
}
@@ -695,10 +873,28 @@ public class JEditorPane extends JTextComponent
public Dimension getPreferredSize()
{
Dimension pref = super.getPreferredSize();
- if (getScrollableTracksViewportWidth())
- pref.width = getUI().getMinimumSize(this).width;
- if (getScrollableTracksViewportHeight())
- pref.height = getUI().getMinimumSize(this).height;
+ Container parent = getParent();
+ if (parent instanceof JViewport)
+ {
+ JViewport vp = (JViewport) getParent();
+ TextUI ui = getUI();
+ Dimension min = null;
+ if (! getScrollableTracksViewportWidth())
+ {
+ min = ui.getMinimumSize(this);
+ int vpWidth = vp.getWidth();
+ if (vpWidth != 0 && vpWidth < min.width)
+ pref.width = min.width;
+ }
+ if (! getScrollableTracksViewportHeight())
+ {
+ if (min == null)
+ min = ui.getMinimumSize(this);
+ int vpHeight = vp.getHeight();
+ if (vpHeight != 0 && vpHeight < min.height)
+ pref.height = min.height;
+ }
+ }
return pref;
}
@@ -716,8 +912,11 @@ public class JEditorPane extends JTextComponent
// Tests show that this returns true when the parent is a JViewport
// and has a height > minimum UI height.
Container parent = getParent();
+ int height = parent.getHeight();
+ TextUI ui = getUI();
return parent instanceof JViewport
- && parent.getHeight() > getUI().getMinimumSize(this).height;
+ && height >= ui.getMinimumSize(this).height
+ && height <= ui.getMaximumSize(this).height;
}
/**
@@ -740,13 +939,19 @@ public class JEditorPane extends JTextComponent
public URL getPage()
{
- return page;
+ return loader != null ? loader.page : null;
}
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 new BufferedInputStream(stream);
}
public String getText()
@@ -777,10 +982,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)
{
@@ -805,7 +1012,8 @@ public class JEditorPane extends JTextComponent
public static void registerEditorKitForContentType(String type,
String classname)
{
- registerMap.put(type, classname);
+ registerEditorKitForContentType(type, classname,
+ Thread.currentThread().getContextClassLoader());
}
/**
@@ -815,7 +1023,7 @@ public class JEditorPane extends JTextComponent
String classname,
ClassLoader loader)
{
- // TODO: Implement this properly.
+ registerMap.put(type, new EditorKitMapping(classname, loader));
}
/**
@@ -839,6 +1047,13 @@ public class JEditorPane extends JTextComponent
public final void setContentType(String type)
{
+ // Strip off content type parameters.
+ int paramIndex = type.indexOf(';');
+ if (paramIndex > -1)
+ {
+ // TODO: Handle character encoding.
+ type = type.substring(0, paramIndex).trim();
+ }
if (editorKit != null
&& editorKit.getContentType().equals(type))
return;
@@ -899,14 +1114,45 @@ public class JEditorPane extends JTextComponent
if (page == null)
throw new IOException("invalid url");
- try
+ URL old = getPage();
+ // Only reload if the URL doesn't point to the same file.
+ // This is not the same as equals because there might be different
+ // URLs on the same file with different anchors.
+ if (old == null || ! old.sameFile(page))
{
- this.page = page;
- getEditorKit().read(page.openStream(), getDocument(), 0);
- }
- catch (BadLocationException e)
- {
- // Ignored. '0' is always a valid offset.
+ InputStream in = getStream(page);
+ if (editorKit != null)
+ {
+ Document doc = editorKit.createDefaultDocument();
+ doc.putProperty(Document.StreamDescriptionProperty, page);
+
+ if (loader != null)
+ loader.cancel();
+ loader = new PageLoader(doc, in, old, page);
+
+ int prio = -1;
+ if (doc instanceof AbstractDocument)
+ {
+ AbstractDocument aDoc = (AbstractDocument) doc;
+ prio = aDoc.getAsynchronousLoadPriority();
+ }
+ if (prio >= 0)
+ {
+ // Load asynchronously.
+ setDocument(doc);
+ Thread loadThread = new Thread(loader,
+ "JEditorPane.PageLoader");
+ loadThread.setDaemon(true);
+ loadThread.setPriority(prio);
+ loadThread.start();
+ }
+ else
+ {
+ // Load synchronously.
+ loader.run();
+ setDocument(doc);
+ }
+ }
}
}
diff --git a/libjava/classpath/javax/swing/JFrame.java b/libjava/classpath/javax/swing/JFrame.java
index 1371525ddf2..0ae23f101fa 100644
--- a/libjava/classpath/javax/swing/JFrame.java
+++ b/libjava/classpath/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/libjava/classpath/javax/swing/JLabel.java b/libjava/classpath/javax/swing/JLabel.java
index fcf0fd7cb13..721287b21df 100644
--- a/libjava/classpath/javax/swing/JLabel.java
+++ b/libjava/classpath/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;
}
}
@@ -379,11 +431,11 @@ public class JLabel extends JComponent implements Accessible, SwingConstants
* Creates a new vertically and horizontally centered
* JLabel object with no text and the given icon.
*
- * @param image The icon to use with the label.
+ * @param image The icon to use with the label, <code>null</code> permitted.
*/
public JLabel(Icon image)
{
- this("", image, CENTER);
+ this(null, image, CENTER);
}
/**
@@ -391,19 +443,21 @@ public class JLabel extends JComponent implements Accessible, SwingConstants
* given icon and horizontal alignment. By default, the text is TRAILING
* the image.
*
- * @param image The icon to use with the label.
- * @param horizontalAlignment The horizontal alignment of the label.
+ * @param image The icon to use with the label, <code>null</code> premitted.
+ * @param horizontalAlignment The horizontal alignment of the label, must be
+ * either <code>CENTER</code>, <code>LEFT</code>, <code>RIGHT</code>,
+ * <code>LEADING</code> or <code>TRAILING</code>.
*/
public JLabel(Icon image, int horizontalAlignment)
{
- this("", image, horizontalAlignment);
+ this(null, image, horizontalAlignment);
}
/**
* Creates a new horizontally leading and vertically centered JLabel
* object with no icon and the given text.
*
- * @param text The text to use with the label.
+ * @param text The text to use with the label, <code>null</code> permitted.
*/
public JLabel(String text)
{
@@ -414,8 +468,10 @@ public class JLabel extends JComponent implements Accessible, SwingConstants
* Creates a new vertically centered JLabel object with no icon and the
* given text and horizontal alignment.
*
- * @param text The text to use with the label.
- * @param horizontalAlignment The horizontal alignment of the label.
+ * @param text The text to use with the label, <code>null</code> permitted.
+ * @param horizontalAlignment The horizontal alignment of the label, must be
+ * either <code>CENTER</code>, <code>LEFT</code>, <code>RIGHT</code>,
+ * <code>LEADING</code> or <code>TRAILING</code>.
*/
public JLabel(String text, int horizontalAlignment)
{
@@ -426,12 +482,21 @@ public class JLabel extends JComponent implements Accessible, SwingConstants
* Creates a new vertically centered JLabel object with the given text,
* icon, and horizontal alignment.
*
- * @param text The text to use with the label.
- * @param icon The icon to use with the label.
- * @param horizontalAlignment The horizontal alignment of the label.
+ * @param text The text to use with the label, <code>null</code> permitted.
+ * @param icon The icon to use with the label, <code>null</code> premitted.
+ * @param horizontalAlignment The horizontal alignment of the label, must be
+ * either <code>CENTER</code>, <code>LEFT</code>, <code>RIGHT</code>,
+ * <code>LEADING</code> or <code>TRAILING</code>.
*/
public JLabel(String text, Icon icon, int horizontalAlignment)
{
+ if (horizontalAlignment != SwingConstants.LEFT
+ && horizontalAlignment != SwingConstants.RIGHT
+ && horizontalAlignment != SwingConstants.CENTER
+ && horizontalAlignment != SwingConstants.LEADING
+ && horizontalAlignment != SwingConstants.TRAILING)
+ throw new IllegalArgumentException();
+
this.text = text;
this.icon = icon;
this.horizontalAlignment = horizontalAlignment;
diff --git a/libjava/classpath/javax/swing/JLayeredPane.java b/libjava/classpath/javax/swing/JLayeredPane.java
index ca913e97fed..d981788ec7d 100644
--- a/libjava/classpath/javax/swing/JLayeredPane.java
+++ b/libjava/classpath/javax/swing/JLayeredPane.java
@@ -406,7 +406,7 @@ public class JLayeredPane extends JComponent implements Accessible
* Return a hashtable mapping child components of this container to
* Integer objects representing the component's layer assignments.
*/
- protected Hashtable getComponentToLayer()
+ protected Hashtable<Component, Integer> getComponentToLayer()
{
return componentToLayer;
}
diff --git a/libjava/classpath/javax/swing/JList.java b/libjava/classpath/javax/swing/JList.java
index 6a98770eeda..ff1b239217a 100644
--- a/libjava/classpath/javax/swing/JList.java
+++ b/libjava/classpath/javax/swing/JList.java
@@ -1041,7 +1041,7 @@ public class JList extends JComponent implements Accessible, Scrollable
*
* @param items the initial list items.
*/
- public JList(Vector items)
+ public JList(Vector<?> items)
{
init(createListModel(items));
}
@@ -1643,9 +1643,20 @@ public class JList extends JComponent implements Accessible, Scrollable
* @param listData The object array to build a new list model on
* @see #setModel
*/
- public void setListData(Vector listData)
+ public void setListData(final Vector<?> listData)
{
- setModel(createListModel(listData));
+ setModel(new AbstractListModel()
+ {
+ public int getSize()
+ {
+ return listData.size();
+ }
+
+ public Object getElementAt(int i)
+ {
+ return listData.elementAt(i);
+ }
+ });
}
/**
@@ -1935,72 +1946,74 @@ public class JList extends JComponent implements Accessible, Scrollable
public int getScrollableUnitIncrement(Rectangle visibleRect,
int orientation, int direction)
{
- ListUI lui = this.getUI();
+ int unit = -1;
if (orientation == SwingConstants.VERTICAL)
{
- if (direction > 0)
+ int row = getFirstVisibleIndex();
+ if (row == -1)
+ unit = 0;
+ else if (direction > 0)
+ {
+ // Scrolling down.
+ Rectangle bounds = getCellBounds(row, row);
+ if (bounds != null)
+ unit = bounds.height - (visibleRect.y - bounds.y);
+ else
+ unit = 0;
+ }
+ else
{
- // Scrolling down
- Point bottomLeft = new Point(visibleRect.x,
- visibleRect.y + visibleRect.height);
- int curIdx = lui.locationToIndex(this, bottomLeft);
- Rectangle curBounds = lui.getCellBounds(this, curIdx, curIdx);
- if (curBounds.y + curBounds.height == bottomLeft.y)
+ // Scrolling up.
+ Rectangle bounds = getCellBounds(row, row);
+ // First row.
+ if (row == 0 && bounds.y == visibleRect.y)
+ unit = 0; // No need to scroll.
+ else if (bounds.y == visibleRect.y)
{
- // we are at the exact bottom of the current cell, so we
- // are being asked to scroll to the end of the next one
- if (curIdx + 1 < model.getSize())
- {
- // there *is* a next item in the list
- Rectangle nxtBounds = lui.getCellBounds(this, curIdx + 1, curIdx + 1);
- return nxtBounds.height;
- }
+ // Scroll to previous row.
+ Point loc = bounds.getLocation();
+ loc.y--;
+ int prev = locationToIndex(loc);
+ Rectangle prevR = getCellBounds(prev, prev);
+ if (prevR == null || prevR.y >= bounds.y)
+ unit = 0; // For multicolumn lists.
else
- {
- // no next item, no advance possible
- return 0;
- }
+ unit = prevR.height;
}
else
- {
- // we are part way through an existing cell, so we are being
- // asked to scroll to the bottom of it
- return (curBounds.y + curBounds.height) - bottomLeft.y;
- }
+ unit = visibleRect.y - bounds.y;
}
- else
+ }
+ else if (orientation == SwingConstants.HORIZONTAL && getLayoutOrientation() != VERTICAL)
+ {
+ // Horizontal scrolling.
+ int i = locationToIndex(visibleRect.getLocation());
+ if (i != -1)
{
- // scrolling up
- Point topLeft = new Point(visibleRect.x, visibleRect.y);
- int curIdx = lui.locationToIndex(this, topLeft);
- Rectangle curBounds = lui.getCellBounds(this, curIdx, curIdx);
- if (curBounds.y == topLeft.y)
+ Rectangle b = getCellBounds(i, i);
+ if (b != null)
{
- // we are at the exact top of the current cell, so we
- // are being asked to scroll to the top of the previous one
- if (curIdx > 0)
+ if (b.x != visibleRect.x)
{
- // there *is* a previous item in the list
- Rectangle nxtBounds = lui.getCellBounds(this, curIdx - 1, curIdx - 1);
- return -nxtBounds.height;
+ if (direction < 0)
+ unit = Math.abs(b.x - visibleRect.x);
+ else
+ unit = b.width + b.x - visibleRect.x;
}
else
- {
- // no previous item, no advance possible
- return 0;
- }
+ unit = b.width;
}
- else
- {
- // we are part way through an existing cell, so we are being
- // asked to scroll to the top of it
- return curBounds.y - topLeft.y;
- }
}
}
- // FIXME: handle horizontal scrolling (also wrapping?)
- return 1;
+ if (unit == -1)
+ {
+ // This fallback seems to be used by the RI for the degenerate cases
+ // not covered above.
+ Font f = getFont();
+ unit = f != null ? f.getSize() : 1;
+ }
+ return unit;
}
/**
@@ -2029,10 +2042,120 @@ public class JList extends JComponent implements Accessible, Scrollable
public int getScrollableBlockIncrement(Rectangle visibleRect,
int orientation, int direction)
{
- if (orientation == VERTICAL)
- return visibleRect.height * direction;
- else
- return visibleRect.width * direction;
+ int block = -1;
+ if (orientation == SwingConstants.VERTICAL)
+ {
+ // Default block scroll. Special cases are handled below for
+ // better usability.
+ block = visibleRect.height;
+ if (direction > 0)
+ {
+ // Scroll down.
+ // Scroll so that after scrolling the last line aligns with
+ // the lower boundary of the visible area.
+ Point p = new Point(visibleRect.x,
+ visibleRect.y + visibleRect.height - 1);
+ int last = locationToIndex(p);
+ if (last != -1)
+ {
+ Rectangle lastR = getCellBounds(last, last);
+ if (lastR != null)
+ {
+ block = lastR.y - visibleRect.y;
+ if (block == 0&& last < getModel().getSize() - 1)
+ block = lastR.height;
+ }
+ }
+ }
+ else
+ {
+ // Scroll up.
+ // Scroll so that after scrolling the first line aligns with
+ // the upper boundary of the visible area.
+ Point p = new Point(visibleRect.x,
+ visibleRect.y - visibleRect.height);
+ int newFirst = locationToIndex(p);
+ if (newFirst != -1)
+ {
+ int first = getFirstVisibleIndex();
+ if (first == -1)
+ first = locationToIndex(visibleRect.getLocation());
+ Rectangle newFirstR = getCellBounds(newFirst, newFirst);
+ Rectangle firstR = getCellBounds(first, first);
+ if (newFirstR != null && firstR != null)
+ {
+ // Search first item that would left the current first
+ // item visible when scrolled to.
+ while (newFirstR.y + visibleRect.height
+ < firstR.y + firstR.height
+ && newFirstR.y < firstR.y)
+ {
+ newFirst++;
+ newFirstR = getCellBounds(newFirst, newFirst);
+ }
+ block = visibleRect.y - newFirstR.y;
+ if (block <= 0 && newFirstR.y > 0)
+ {
+ newFirst--;
+ newFirstR = getCellBounds(newFirst, newFirst);
+ if (newFirstR != null)
+ block = visibleRect.y - newFirstR.y;
+ }
+ }
+ }
+ }
+ }
+ else if (orientation == SwingConstants.HORIZONTAL
+ && getLayoutOrientation() != VERTICAL)
+ {
+ // Default block increment. Special cases are handled below for
+ // better usability.
+ block = visibleRect.width;
+ if (direction > 0)
+ {
+ // Scroll right.
+ Point p = new Point(visibleRect.x + visibleRect.width + 1,
+ visibleRect.y);
+ int last = locationToIndex(p);
+ if (last != -1)
+ {
+ Rectangle lastR = getCellBounds(last, last);
+ if (lastR != null)
+ {
+ block = lastR.x - visibleRect.x;
+ if (block < 0)
+ block += lastR.width;
+ else if (block == 0 && last < getModel().getSize() - 1)
+ block = lastR.width;
+ }
+ }
+ }
+ else
+ {
+ // Scroll left.
+ Point p = new Point(visibleRect.x - visibleRect.width,
+ visibleRect.y);
+ int first = locationToIndex(p);
+ if (first != -1)
+ {
+ Rectangle firstR = getCellBounds(first, first);
+ if (firstR != null)
+ {
+ if (firstR.x < visibleRect.x - visibleRect.width)
+ {
+ if (firstR.x + firstR.width > visibleRect.x)
+ block = visibleRect.x - firstR.x;
+ else
+ block = visibleRect.x - firstR.x - firstR.width;
+ }
+ else
+ block = visibleRect.x - firstR.x;
+ }
+ }
+ }
+ }
+
+ return block;
}
/**
diff --git a/libjava/classpath/javax/swing/JMenu.java b/libjava/classpath/javax/swing/JMenu.java
index 0840509f906..7e627f1181d 100644
--- a/libjava/classpath/javax/swing/JMenu.java
+++ b/libjava/classpath/javax/swing/JMenu.java
@@ -39,8 +39,14 @@ exception statement from your version. */
package javax.swing;
import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
+import java.awt.Insets;
import java.awt.Point;
-import java.awt.PopupMenu;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
@@ -54,6 +60,8 @@ import javax.accessibility.Accessible;
import javax.accessibility.AccessibleContext;
import javax.accessibility.AccessibleRole;
import javax.accessibility.AccessibleSelection;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
import javax.swing.plaf.MenuItemUI;
@@ -72,6 +80,36 @@ import javax.swing.plaf.MenuItemUI;
*/
public class JMenu extends JMenuItem implements Accessible, MenuElement
{
+ /**
+ * Receives notifications when the JMenu's ButtonModel is changed and
+ * fires menuSelected or menuDeselected events when appropriate.
+ */
+ private class MenuChangeListener
+ implements ChangeListener
+ {
+ /**
+ * Indicates the last selected state.
+ */
+ private boolean selected;
+
+ /**
+ * Receives notification when the JMenu's ButtonModel changes.
+ */
+ public void stateChanged(ChangeEvent ev)
+ {
+ ButtonModel m = (ButtonModel) ev.getSource();
+ boolean s = m.isSelected();
+ if (s != selected)
+ {
+ if (s)
+ fireMenuSelected();
+ else
+ fireMenuDeselected();
+ selected = s;
+ }
+ }
+ }
+
private static final long serialVersionUID = 4227225638931828014L;
/** A Popup menu associated with this menu, which pops up when menu is selected */
@@ -88,18 +126,26 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement
/* PopupListener */
protected WinListener popupListener;
- /** Location at which popup menu associated with this menu will be
- displayed */
+ /**
+ * Location at which popup menu associated with this menu will be
+ * displayed
+ */
private Point menuLocation;
/**
+ * The ChangeListener for the ButtonModel.
+ *
+ * @see MenuChangeListener
+ */
+ private ChangeListener menuChangeListener;
+
+ /**
* Creates a new JMenu object.
*/
public JMenu()
{
super();
setOpaque(false);
- setDelay(200);
}
/**
@@ -113,7 +159,6 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement
popupMenu = new JPopupMenu();
popupMenu.setInvoker(this);
setOpaque(false);
- setDelay(200);
}
/**
@@ -129,7 +174,6 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement
popupMenu = new JPopupMenu();
popupMenu.setInvoker(this);
setOpaque(false);
- setDelay(200);
}
/**
@@ -143,7 +187,6 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement
{
// FIXME: tearoff not implemented
this(text);
- setDelay(200);
}
/**
@@ -193,7 +236,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement
*/
public JMenuItem add(String text)
{
- return getPopupMenu().add(text);
+ return add(new JMenuItem(text));
}
/**
@@ -205,7 +248,10 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement
*/
public JMenuItem add(Action action)
{
- return getPopupMenu().add(action);
+ JMenuItem i = createActionComponent(action);
+ i.setAction(action);
+ add(i);
+ return i;
}
/**
@@ -328,7 +374,18 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement
*/
public void setModel(ButtonModel model)
{
+ ButtonModel oldModel = getModel();
+ if (oldModel != null && menuChangeListener != null)
+ oldModel.removeChangeListener(menuChangeListener);
+
super.setModel(model);
+
+ if (model != null)
+ {
+ if (menuChangeListener == null)
+ menuChangeListener = new MenuChangeListener();
+ model.addChangeListener(menuChangeListener);
+ }
}
/**
@@ -342,63 +399,6 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement
}
/**
- * A helper method to handle setSelected calls from both mouse events and
- * direct calls to setSelected. Direct calls shouldn't expand the popup
- * menu and should select the JMenu even if it is disabled. Mouse events
- * only select the JMenu if it is enabled and should expand the popup menu
- * associated with this JMenu.
- * @param selected whether or not the JMenu was selected
- * @param menuEnabled whether or not selecting the menu is "enabled". This
- * is always true for direct calls, and is set to isEnabled() for mouse
- * based calls.
- * @param showMenu whether or not to show the popup menu
- */
- private void setSelectedHelper(boolean selected, boolean menuEnabled, boolean showMenu)
- {
- // If menu is selected and enabled, activates the menu and
- // displays associated popup.
- if (selected && menuEnabled)
- {
- super.setArmed(true);
- super.setSelected(true);
-
- // FIXME: The popup menu should be shown on the screen after certain
- // number of seconds pass. The 'delay' property of this menu indicates
- // this amount of seconds. 'delay' property is 0 by default.
- if (isShowing())
- {
- fireMenuSelected();
-
- int x = 0;
- int y = 0;
- if (showMenu)
- if (menuLocation == null)
- {
- // Calculate correct position of the popup. Note that location of the popup
- // passed to show() should be relative to the popup's invoker
- if (isTopLevelMenu())
- y = this.getHeight();
- else
- x = this.getWidth();
- getPopupMenu().show(this, x, y);
- }
- else
- {
- getPopupMenu().show(this, menuLocation.x, menuLocation.y);
- }
- }
- }
-
- else
- {
- super.setSelected(false);
- super.setArmed(false);
- fireMenuDeselected();
- getPopupMenu().setVisible(false);
- }
- }
-
- /**
* Changes this menu selected state if selected is true and false otherwise
* This method fires menuEvents to menu's registered listeners.
*
@@ -406,7 +406,9 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement
*/
public void setSelected(boolean selected)
{
- setSelectedHelper(selected, true, false);
+ ButtonModel m = getModel();
+ if (selected != m.isSelected())
+ m.setSelected(selected);
}
/**
@@ -427,23 +429,126 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement
*/
public void setPopupMenuVisible(boolean popup)
{
- if (getModel().isEnabled())
- getPopupMenu().setVisible(popup);
+ if (popup != isPopupMenuVisible() && (isEnabled() || ! popup))
+ {
+ if (popup && isShowing())
+ {
+ // Set location as determined by getPopupLocation().
+ Point loc = menuLocation == null ? getPopupMenuOrigin()
+ : menuLocation;
+ getPopupMenu().show(this, loc.x, loc.y);
+ }
+ else
+ getPopupMenu().setVisible(false);
+ }
}
/**
- * Returns origin point of the popup menu
+ * Returns origin point of the popup menu. This takes the screen bounds
+ * into account and places the popup where it fits best.
*
- * @return Point containing
+ * @return the origin of the popup menu
*/
protected Point getPopupMenuOrigin()
{
- // if menu in the menu bar
+ // The menu's screen location and size.
+ Point screenLoc = getLocationOnScreen();
+ Dimension size = getSize();
+
+ // Determine the popup's size.
+ JPopupMenu popup = getPopupMenu();
+ Dimension popupSize = popup.getSize();
+ if (popupSize.width == 0 || popupSize.height == 0)
+ popupSize = popup.getPreferredSize();
+
+ // Determine screen bounds.
+ Toolkit tk = Toolkit.getDefaultToolkit();
+ Rectangle screenBounds = new Rectangle(tk.getScreenSize());
+ GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+ GraphicsDevice gd = ge.getDefaultScreenDevice();
+ GraphicsConfiguration gc = gd.getDefaultConfiguration();
+ Insets screenInsets = tk.getScreenInsets(gc);
+ screenBounds.x -= screenInsets.left;
+ screenBounds.width -= screenInsets.left + screenInsets.right;
+ screenBounds.y -= screenInsets.top;
+ screenBounds.height -= screenInsets.top + screenInsets.bottom;
+ screenLoc.x -= screenInsets.left;
+ screenLoc.y -= screenInsets.top;
+
+ Point point = new Point();
if (isTopLevelMenu())
- return new Point(0, this.getHeight());
-
- // if submenu
- return new Point(this.getWidth(), 0);
+ {
+ // If menu in the menu bar.
+ int xOffset = UIManager.getInt("Menu.menuPopupOffsetX");
+ int yOffset = UIManager.getInt("Menu.menuPopupOffsetY");
+ // Determine X location.
+ if (getComponentOrientation().isLeftToRight())
+ {
+ // Prefer popup to the right.
+ point.x = xOffset;
+ // Check if it fits, otherwise place popup wherever it fits.
+ if (screenLoc.x + point.x + popupSize.width
+ > screenBounds.width + screenBounds.width
+ && screenBounds.width - size.width
+ < 2 * (screenLoc.x - screenBounds.x))
+ // Popup to the right if there's not enough room.
+ point.x = size.width - xOffset - popupSize.width;
+ }
+ else
+ {
+ // Prefer popup to the left.
+ point.x = size.width - xOffset - popupSize.width;
+ if (screenLoc.x + point.x < screenBounds.x
+ && screenBounds.width - size.width
+ > 2 * (screenLoc.x - screenBounds.x))
+ // Popup to the left if there's not enough room.
+ point.x = xOffset;
+ }
+ // Determine Y location. Prefer popping down.
+ point.y = size.height + yOffset;
+ if (screenLoc.y + point.y + popupSize.height >= screenBounds.height
+ && screenBounds.height - size.height
+ < 2 * (screenLoc.y - screenBounds.y))
+ // Position above if there's not enough room below.
+ point.y = - yOffset - popupSize.height;
+ }
+ else
+ {
+ // If submenu.
+ int xOffset = UIManager.getInt("Menu.submenuPopupOffsetX");
+ int yOffset = UIManager.getInt("Menu.submenuPopupOffsetY");
+ // Determine X location.
+ if (getComponentOrientation().isLeftToRight())
+ {
+ // Prefer popup to the right.
+ point.x = size.width + xOffset;
+ if (screenLoc.x + point.x + popupSize.width
+ >= screenBounds.x + screenBounds.width
+ && screenBounds.width - size.width
+ < 2 * (screenLoc.x - screenBounds.x))
+ // Position to the left if there's not enough room on the right.
+ point.x = - xOffset - popupSize.width;
+ }
+ else
+ {
+ // Prefer popup on the left side.
+ point.x = - xOffset - popupSize.width;
+ if (screenLoc.x + point.x < screenBounds.x
+ && screenBounds.width - size.width
+ > 2 * (screenLoc.x - screenBounds.x))
+ // Popup to the right if there's not enough room.
+ point.x = size.width + xOffset;
+ }
+ // Determine Y location. Prefer popping down.
+ point.y = yOffset;
+ if (screenLoc.y + point.y + popupSize.height
+ >= screenBounds.y + screenBounds.height
+ && screenBounds.height - size.height
+ < 2 * (screenLoc.y - screenBounds.y))
+ // Pop up if there's not enough room below.
+ point.y = size.height - yOffset - popupSize.height;
+ }
+ return point;
}
/**
@@ -483,6 +588,8 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement
public void setMenuLocation(int x, int y)
{
menuLocation = new Point(x, y);
+ if (popupMenu != null)
+ popupMenu.setLocation(x, y);
}
/**
@@ -600,7 +707,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement
if (getPopupMenu() == null || getMenuComponentCount() == 0)
return null;
- return (Component) popupMenu.getComponentAtIndex(index);
+ return popupMenu.getComponentAtIndex(index);
}
/**
@@ -748,7 +855,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement
{
// if this menu selection is true, then activate this menu and
// display popup associated with this menu
- setSelectedHelper(changed, isEnabled(), true);
+ setSelected(changed);
}
/**
diff --git a/libjava/classpath/javax/swing/JMenuBar.java b/libjava/classpath/javax/swing/JMenuBar.java
index 35f129377f1..73b409e42db 100644
--- a/libjava/classpath/javax/swing/JMenuBar.java
+++ b/libjava/classpath/javax/swing/JMenuBar.java
@@ -298,19 +298,25 @@ public class JMenuBar extends JComponent implements Accessible, MenuElement
}
/**
- * DOCUMENT ME!
+ * This method is not implemented and will throw an {@link Error} if called.
*
- * @return DOCUMENT ME!
+ * @return This method never returns anything, it throws an exception.
*/
public JMenu getHelpMenu()
{
- return null;
+ // the following error matches the behaviour of the reference
+ // implementation...
+ throw new Error("getHelpMenu() is not implemented");
}
/**
- * Returns margin betweeen menu bar's border and its menues
+ * Returns the margin between the menu bar's border and its menus. If the
+ * margin is <code>null</code>, this method returns
+ * <code>new Insets(0, 0, 0, 0)</code>.
*
- * @return margin between menu bar's border and its menues
+ * @return The margin (never <code>null</code>).
+ *
+ * @see #setMargin(Insets)
*/
public Insets getMargin()
{
@@ -617,21 +623,20 @@ public class JMenuBar extends JComponent implements Accessible, MenuElement
}
/**
- * Sets the menu bar's "margin" bound property, which represents
- * distance between the menubar's border and its menus.
- * icon. When marging property is modified, PropertyChangeEvent will
- * be fired to menuBar's PropertyChangeListener's.
- *
- * @param m distance between the menubar's border and its menus.
+ * Sets the margin between the menu bar's border and its menus (this is a
+ * bound property with the name 'margin').
*
+ * @param m the margin (<code>null</code> permitted).
+ *
+ * @see #getMargin()
*/
public void setMargin(Insets m)
{
if (m != margin)
{
- Insets oldMargin = margin;
- margin = m;
- firePropertyChange("margin", oldMargin, margin);
+ Insets oldMargin = margin;
+ margin = m;
+ firePropertyChange("margin", oldMargin, margin);
}
}
diff --git a/libjava/classpath/javax/swing/JMenuItem.java b/libjava/classpath/javax/swing/JMenuItem.java
index f7f93bf00d7..ffdccdcef1a 100644
--- a/libjava/classpath/javax/swing/JMenuItem.java
+++ b/libjava/classpath/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,12 +76,16 @@ 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()
{
- super();
- init(null, null);
+ this(null, null);
}
/**
@@ -94,8 +97,7 @@ public class JMenuItem extends AbstractButton implements Accessible,
{
// FIXME: The requestedFocusEnabled property should
// be set to false, when only icon is set for menu item.
- super();
- init(null, icon);
+ this(null, icon);
}
/**
@@ -117,6 +119,7 @@ public class JMenuItem extends AbstractButton implements Accessible,
{
super();
super.setAction(action);
+ setModel(new DefaultButtonModel());
init(null, null);
if (action != null)
{
@@ -148,6 +151,7 @@ public class JMenuItem extends AbstractButton implements Accessible,
public JMenuItem(String text, Icon icon)
{
super();
+ setModel(new DefaultButtonModel());
init(text, icon);
}
@@ -174,7 +178,6 @@ public class JMenuItem extends AbstractButton implements Accessible,
protected void init(String text, Icon icon)
{
super.init(text, icon);
- setModel(new DefaultButtonModel());
// Initializes properties for this menu item, that are different
// from Abstract button properties.
@@ -320,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);
}
/**
@@ -420,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;
}
}
@@ -833,4 +790,20 @@ public class JMenuItem extends AbstractButton implements Accessible,
return AccessibleRole.MENU_ITEM;
}
}
+
+ /**
+ * Returns <code>true</code> if the component is guaranteed to be painted
+ * on top of others. This returns false by default and is overridden by
+ * components like JMenuItem, JPopupMenu and JToolTip to return true for
+ * added efficiency.
+ *
+ * @return <code>true</code> if the component is guaranteed to be painted
+ * on top of others
+ */
+ boolean onTop()
+ {
+ return SwingUtilities.getAncestorOfClass(JInternalFrame.class, this)
+ == null;
+ }
+
}
diff --git a/libjava/classpath/javax/swing/JPopupMenu.java b/libjava/classpath/javax/swing/JPopupMenu.java
index d46015afdf3..1ae8adad02a 100644
--- a/libjava/classpath/javax/swing/JPopupMenu.java
+++ b/libjava/classpath/javax/swing/JPopupMenu.java
@@ -820,7 +820,14 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement
*/
public void menuSelectionChanged(boolean changed)
{
- if (! changed)
+ if (invoker instanceof JMenu)
+ {
+ // We need to special case this since the JMenu calculates the
+ // position etc of the popup.
+ JMenu menu = (JMenu) invoker;
+ menu.setPopupMenuVisible(changed);
+ }
+ else if (! changed)
setVisible(false);
}
@@ -895,6 +902,20 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement
}
}
+ /**
+ * Returns <code>true</code> if the component is guaranteed to be painted
+ * on top of others. This returns false by default and is overridden by
+ * components like JMenuItem, JPopupMenu and JToolTip to return true for
+ * added efficiency.
+ *
+ * @return <code>true</code> if the component is guaranteed to be painted
+ * on top of others
+ */
+ boolean onTop()
+ {
+ return true;
+ }
+
protected class AccessibleJPopupMenu extends AccessibleJComponent
{
private static final long serialVersionUID = 7423261328879849768L;
diff --git a/libjava/classpath/javax/swing/JRootPane.java b/libjava/classpath/javax/swing/JRootPane.java
index a2cd9c7a000..10fdf10c0ec 100644
--- a/libjava/classpath/javax/swing/JRootPane.java
+++ b/libjava/classpath/javax/swing/JRootPane.java
@@ -505,15 +505,21 @@ public class JRootPane extends JComponent implements Accessible
}
/**
- * DOCUMENT ME!
+ * Set the layered pane for the root pane.
*
- * @param f DOCUMENT ME!
+ * @param f The JLayeredPane to be used.
+ *
+ * @throws IllegalComponentStateException if JLayeredPane
+ * parameter is null.
*/
public void setLayeredPane(JLayeredPane f)
{
+ if (f == null)
+ throw new IllegalComponentStateException();
+
if (layeredPane != null)
remove(layeredPane);
-
+
layeredPane = f;
add(f, -1);
}
diff --git a/libjava/classpath/javax/swing/JScrollBar.java b/libjava/classpath/javax/swing/JScrollBar.java
index bf0803ab5a1..1f21aa13ff5 100644
--- a/libjava/classpath/javax/swing/JScrollBar.java
+++ b/libjava/classpath/javax/swing/JScrollBar.java
@@ -50,6 +50,8 @@ import javax.accessibility.AccessibleRole;
import javax.accessibility.AccessibleState;
import javax.accessibility.AccessibleStateSet;
import javax.accessibility.AccessibleValue;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
import javax.swing.plaf.ScrollBarUI;
/**
@@ -172,6 +174,28 @@ public class JScrollBar extends JComponent implements Adjustable, Accessible
}
}
+ /**
+ * Listens for changes on the model and fires them to interested
+ * listeners on the JScrollBar, after re-sourcing them.
+ */
+ private class ScrollBarChangeListener
+ implements ChangeListener
+ {
+
+ public void stateChanged(ChangeEvent event)
+ {
+ Object o = event.getSource();
+ if (o instanceof BoundedRangeModel)
+ {
+ BoundedRangeModel m = (BoundedRangeModel) o;
+ fireAdjustmentValueChanged(AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED,
+ AdjustmentEvent.TRACK, m.getValue(),
+ m.getValueIsAdjusting());
+ }
+ }
+
+ }
+
private static final long serialVersionUID = -8195169869225066566L;
/** How much the thumb moves when moving in a block. */
@@ -186,6 +210,12 @@ public class JScrollBar extends JComponent implements Adjustable, Accessible
/** How much the thumb moves when moving in a unit. */
protected int unitIncrement = 1;
+ /**
+ * This ChangeListener forwards events fired from the model and re-sources
+ * them to originate from this JScrollBar.
+ */
+ private ChangeListener sbChangeListener;
+
/**
* Creates a new horizontal JScrollBar object with a minimum
* of 0, a maxmium of 100, a value of 0 and an extent of 10.
@@ -220,6 +250,8 @@ public class JScrollBar extends JComponent implements Adjustable, Accessible
public JScrollBar(int orientation, int value, int extent, int min, int max)
{
model = new DefaultBoundedRangeModel(value, extent, min, max);
+ sbChangeListener = new ScrollBarChangeListener();
+ model.addChangeListener(sbChangeListener);
if (orientation != SwingConstants.HORIZONTAL
&& orientation != SwingConstants.VERTICAL)
throw new IllegalArgumentException(orientation
@@ -319,12 +351,13 @@ public class JScrollBar extends JComponent implements Adjustable, Accessible
*/
public void setModel(BoundedRangeModel newModel)
{
- if (model != newModel)
- {
- BoundedRangeModel oldModel = model;
- model = newModel;
- firePropertyChange("model", oldModel, model);
- }
+ BoundedRangeModel oldModel = model;
+ if (oldModel != null)
+ oldModel.removeChangeListener(sbChangeListener);
+ model = newModel;
+ if (model != null)
+ model.addChangeListener(sbChangeListener);
+ firePropertyChange("model", oldModel, model);
}
/**
@@ -424,12 +457,7 @@ public class JScrollBar extends JComponent implements Adjustable, Accessible
*/
public void setValue(int value)
{
- if (isEnabled() && value != getValue())
- {
- model.setValue(value);
- fireAdjustmentValueChanged(AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED,
- AdjustmentEvent.TRACK, value);
- }
+ model.setValue(value);
}
/**
@@ -451,12 +479,7 @@ public class JScrollBar extends JComponent implements Adjustable, Accessible
*/
public void setVisibleAmount(int extent)
{
- if (extent != getVisibleAmount())
- {
- model.setExtent(extent);
- fireAdjustmentValueChanged(AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED,
- AdjustmentEvent.TRACK, extent);
- }
+ model.setExtent(extent);
}
/**
@@ -476,12 +499,7 @@ public class JScrollBar extends JComponent implements Adjustable, Accessible
*/
public void setMinimum(int minimum)
{
- if (minimum != getMinimum())
- {
- model.setMinimum(minimum);
- fireAdjustmentValueChanged(AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED,
- AdjustmentEvent.TRACK, minimum);
- }
+ model.setMinimum(minimum);
}
/**
@@ -501,12 +519,7 @@ public class JScrollBar extends JComponent implements Adjustable, Accessible
*/
public void setMaximum(int maximum)
{
- if (maximum != getMaximum())
- {
- model.setMaximum(maximum);
- fireAdjustmentValueChanged(AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED,
- AdjustmentEvent.TRACK, maximum);
- }
+ model.setMaximum(maximum);
}
/**
@@ -540,17 +553,8 @@ public class JScrollBar extends JComponent implements Adjustable, Accessible
*/
public void setValues(int newValue, int newExtent, int newMin, int newMax)
{
- if (!isEnabled())
- newValue = model.getValue();
- // It seems to be that on any change the value is fired.
- if (newValue != getValue() || newExtent != getVisibleAmount() ||
- newMin != getMinimum() || newMax != getMaximum())
- {
- model.setRangeProperties(newValue, newExtent, newMin, newMax,
- model.getValueIsAdjusting());
- fireAdjustmentValueChanged(AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED,
- AdjustmentEvent.TRACK, newValue);
- }
+ model.setRangeProperties(newValue, newExtent, newMin, newMax,
+ model.getValueIsAdjusting());
}
/**
@@ -596,15 +600,30 @@ public class JScrollBar extends JComponent implements Adjustable, Accessible
*/
protected void fireAdjustmentValueChanged(int id, int type, int value)
{
+ fireAdjustmentValueChanged(id, type, value, getValueIsAdjusting());
+ }
+
+ /**
+ * Helper method for firing adjustment events that can have their
+ * isAdjusting field modified.
+ *
+ * This is package private to avoid an accessor method.
+ *
+ * @param id the ID of the event
+ * @param type the type of the event
+ * @param value the value
+ * @param isAdjusting if the scrollbar is adjusting or not
+ */
+ void fireAdjustmentValueChanged(int id, int type, int value,
+ boolean isAdjusting)
+ {
Object[] adjustmentListeners = listenerList.getListenerList();
- AdjustmentEvent adjustmentEvent = new AdjustmentEvent(this,
- AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED,
- AdjustmentEvent.TRACK,
- value);
+ AdjustmentEvent adjustmentEvent = new AdjustmentEvent(this, id, type,
+ value, isAdjusting);
for (int i = adjustmentListeners.length - 2; i >= 0; i -= 2)
{
- if (adjustmentListeners[i] == AdjustmentListener.class)
- ((AdjustmentListener) adjustmentListeners[i + 1]).adjustmentValueChanged(adjustmentEvent);
+ if (adjustmentListeners[i] == AdjustmentListener.class)
+ ((AdjustmentListener) adjustmentListeners[i + 1]).adjustmentValueChanged(adjustmentEvent);
}
}
diff --git a/libjava/classpath/javax/swing/JScrollPane.java b/libjava/classpath/javax/swing/JScrollPane.java
index 45df1d9190e..f6d37c7b47d 100644
--- a/libjava/classpath/javax/swing/JScrollPane.java
+++ b/libjava/classpath/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/libjava/classpath/javax/swing/JSlider.java b/libjava/classpath/javax/swing/JSlider.java
index 8a06d4f0116..948a9629b4c 100644
--- a/libjava/classpath/javax/swing/JSlider.java
+++ b/libjava/classpath/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 = 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/libjava/classpath/javax/swing/JSplitPane.java b/libjava/classpath/javax/swing/JSplitPane.java
index 5b77f5176ae..fcdc1c04128 100644
--- a/libjava/classpath/javax/swing/JSplitPane.java
+++ b/libjava/classpath/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/libjava/classpath/javax/swing/JTabbedPane.java b/libjava/classpath/javax/swing/JTabbedPane.java
index 5c8d0474852..c7244bf2b71 100644
--- a/libjava/classpath/javax/swing/JTabbedPane.java
+++ b/libjava/classpath/javax/swing/JTabbedPane.java
@@ -760,11 +760,7 @@ public class JTabbedPane extends JComponent implements Serializable,
this.tabPlacement = tabPlacement;
layoutPolicy = tabLayoutPolicy;
- changeEvent = new ChangeEvent(this);
- changeListener = createChangeListener();
-
- model = new DefaultSingleSelectionModel();
- model.addChangeListener(changeListener);
+ setModel(new DefaultSingleSelectionModel());
updateUI();
}
@@ -877,16 +873,24 @@ public class JTabbedPane extends JComponent implements Serializable,
/**
* This method changes the model property of the JTabbedPane.
*
- * @param model The new model to use with the JTabbedPane.
+ * @param m The new model to use with the JTabbedPane.
*/
- public void setModel(SingleSelectionModel model)
+ public void setModel(SingleSelectionModel m)
{
- if (model != this.model)
+ if (m != model)
{
SingleSelectionModel oldModel = this.model;
- this.model.removeChangeListener(changeListener);
- this.model = model;
- this.model.addChangeListener(changeListener);
+ if (oldModel != null && changeListener != null)
+ oldModel.removeChangeListener(changeListener);
+
+ model = m;
+
+ if (model != null)
+ {
+ if (changeListener == null)
+ changeListener = createChangeListener();
+ model.addChangeListener(changeListener);
+ }
firePropertyChange("model", oldModel, this.model);
}
}
@@ -1050,7 +1054,10 @@ public class JTabbedPane extends JComponent implements Serializable,
}
if (getSelectedIndex() == -1)
- setSelectedIndex(0);
+ {
+ setSelectedIndex(0);
+ fireStateChanged();
+ }
revalidate();
repaint();
diff --git a/libjava/classpath/javax/swing/JTable.java b/libjava/classpath/javax/swing/JTable.java
index 5c7bff5d019..42563e6a2ca 100644
--- a/libjava/classpath/javax/swing/JTable.java
+++ b/libjava/classpath/javax/swing/JTable.java
@@ -2635,6 +2635,7 @@ public class JTable
setModel(dm == null ? createDefaultDataModel() : dm);
setAutoCreateColumnsFromModel(autoCreate);
initializeLocalVars();
+
// The following four lines properly set the lead selection indices.
// After this, the UI will handle the lead selection indices.
// FIXME: this should probably not be necessary, if the UI is installed
@@ -2642,11 +2643,13 @@ public class JTable
// own, but certain variables need to be set before the UI can be installed
// so we must get the correct order for all the method calls in this
// constructor.
- selectionModel.setAnchorSelectionIndex(0);
- selectionModel.setLeadSelectionIndex(0);
- columnModel.getSelectionModel().setAnchorSelectionIndex(0);
- columnModel.getSelectionModel().setLeadSelectionIndex(0);
- updateUI();
+ // These four lines are not needed. A Mauve test that shows this is
+ // gnu.testlet.javax.swing.JTable.constructors(linesNotNeeded).
+ // selectionModel.setAnchorSelectionIndex(-1);
+ // selectionModel.setLeadSelectionIndex(-1);
+ // columnModel.getSelectionModel().setAnchorSelectionIndex(-1);
+ // columnModel.getSelectionModel().setLeadSelectionIndex(-1);
+ updateUI();
}
/**
@@ -2675,10 +2678,12 @@ public class JTable
setRowHeight(16);
this.rowMargin = 1;
this.rowSelectionAllowed = true;
+
// this.accessibleContext = new AccessibleJTable();
this.cellEditor = null;
+
// COMPAT: Both Sun and IBM have drag enabled
- this.dragEnabled = true;
+ this.dragEnabled = false;
this.preferredViewportSize = new Dimension(450,400);
this.showHorizontalLines = true;
this.showVerticalLines = true;
@@ -3267,7 +3272,7 @@ public class JTable
cellRect.x += cMargin / 2;
cellRect.width -= cMargin;
}
- }
+ }
return cellRect;
}
@@ -3303,10 +3308,21 @@ public class JTable
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction)
{
- if (orientation == SwingConstants.VERTICAL)
- return visibleRect.height * direction;
+ int block;
+ if (orientation == SwingConstants.HORIZONTAL)
+ {
+ block = visibleRect.width;
+ }
else
- return visibleRect.width * direction;
+ {
+ int rowHeight = getRowHeight();
+ if (rowHeight > 0)
+ block = Math.max(rowHeight, // Little hack for useful rounding.
+ (visibleRect.height / rowHeight) * rowHeight);
+ else
+ block = visibleRect.height;
+ }
+ return block;
}
/**
@@ -3345,24 +3361,40 @@ public class JTable
* The values greater than one means that more mouse wheel or similar
* events were generated, and hence it is better to scroll the longer
* distance.
- * @author Audrius Meskauskas (audriusa@bioinformatics.org)
+ *
+ * @author Roman Kennke (kennke@aicas.com)
*/
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
int direction)
{
- int h = (rowHeight + rowMargin);
- int delta = h * direction;
-
- // Round so that the top would start from the row boundary
- if (orientation == SwingConstants.VERTICAL)
+ int unit;
+ if (orientation == SwingConstants.HORIZONTAL)
+ unit = 100;
+ else
{
- // Completely expose the top row
- int near = ((visibleRect.y + delta + h / 2) / h) * h;
- int diff = visibleRect.y + delta - near;
- delta -= diff;
+ unit = getRowHeight();
+ // The following adjustment doesn't work for variable height rows.
+ // It fully exposes partially visible rows in the scrolling direction.
+ if (rowHeights == null)
+ {
+ if (direction > 0)
+ {
+ // Scroll down.
+ // How much pixles are exposed from the last item?
+ int exposed = (visibleRect.y + visibleRect.height) % unit;
+ if (exposed > 0 && exposed < unit - 1)
+ unit = unit - exposed - 1;
+ }
+ else
+ {
+ // Scroll up.
+ int exposed = visibleRect.y % unit;
+ if (exposed > 0 && exposed < unit)
+ unit = exposed;
+ }
+ }
}
- return delta;
- // TODO when scrollng horizontally, scroll into the column boundary.
+ return unit;
}
/**
@@ -3397,7 +3429,7 @@ public class JTable
*
* @return the editor, suitable for editing this data type
*/
- public TableCellEditor getDefaultEditor(Class columnClass)
+ public TableCellEditor getDefaultEditor(Class<?> columnClass)
{
if (defaultEditorsByColumnClass.containsKey(columnClass))
return (TableCellEditor) defaultEditorsByColumnClass.get(columnClass);
@@ -3409,7 +3441,7 @@ public class JTable
return r;
}
}
-
+
/**
* Get the cell renderer for rendering the given cell.
*
@@ -3419,7 +3451,9 @@ public class JTable
*/
public TableCellRenderer getCellRenderer(int row, int column)
{
- TableCellRenderer renderer = columnModel.getColumn(column).getCellRenderer();
+ TableCellRenderer renderer = null;
+ if (columnModel.getColumnCount() > 0)
+ renderer = columnModel.getColumn(column).getCellRenderer();
if (renderer == null)
{
int mcolumn = convertColumnIndexToModel(column);
@@ -3427,7 +3461,7 @@ public class JTable
}
return renderer;
}
-
+
/**
* Set default renderer for rendering the given data type.
*
@@ -3435,11 +3469,11 @@ public class JTable
* rendered.
* @param rend the renderer that will rend this data type
*/
- public void setDefaultRenderer(Class columnClass, TableCellRenderer rend)
+ public void setDefaultRenderer(Class<?> columnClass, TableCellRenderer rend)
{
defaultRenderersByColumnClass.put(columnClass, rend);
}
-
+
/**
* Get the default renderer for rendering the given data type.
*
@@ -3447,7 +3481,7 @@ public class JTable
*
* @return the appropriate defauld renderer for rendering that data type.
*/
- public TableCellRenderer getDefaultRenderer(Class columnClass)
+ public TableCellRenderer getDefaultRenderer(Class<?> columnClass)
{
if (defaultRenderersByColumnClass.containsKey(columnClass))
return (TableCellRenderer) defaultRenderersByColumnClass.get(columnClass);
@@ -3536,7 +3570,7 @@ public class JTable
return renderer.getTableCellRendererComponent(this,
dataModel.getValueAt(row,
- convertColumnIndexToModel(column)),
+ convertColumnIndexToModel(column)),
isSel,
hasFocus,
row, column);
@@ -4414,7 +4448,7 @@ public class JTable
{
TableColumn resizingColumn = null;
- int ncols = getColumnCount();
+ int ncols = columnModel.getColumnCount();
if (ncols < 1)
return;
@@ -4423,7 +4457,7 @@ public class JTable
if (tableHeader != null)
resizingColumn = tableHeader.getResizingColumn();
-
+
for (int i = 0; i < ncols; ++i)
{
TableColumn col = columnModel.getColumn(i);
@@ -4432,7 +4466,7 @@ public class JTable
if (resizingColumn == col)
rCol = i;
}
-
+
int spill = getWidth() - prefSum;
if (resizingColumn != null)
@@ -4498,9 +4532,11 @@ public class JTable
}
else
{
- TableColumn [] cols = new TableColumn[ncols];
+ TableColumn[] cols = new TableColumn[ncols];
+
for (int i = 0; i < ncols; ++i)
cols[i] = columnModel.getColumn(i);
+
distributeSpill(cols, spill);
}
@@ -4588,7 +4624,7 @@ public class JTable
{
setUI((TableUI) UIManager.getUI(this));
}
-
+
/**
* Get the class (datatype) of the column. The cells are rendered and edited
* differently, depending from they data type.
@@ -4598,7 +4634,7 @@ public class JTable
* @return the class, defining data type of that column (String.class for
* String, Boolean.class for boolean and so on).
*/
- public Class getColumnClass(int column)
+ public Class<?> getColumnClass(int column)
{
return getModel().getColumnClass(convertColumnIndexToModel(column));
}
@@ -4619,7 +4655,7 @@ public class JTable
int modelColumn = columnModel.getColumn(column).getModelIndex();
return dataModel.getColumnName(modelColumn);
}
-
+
/**
* Get the column, currently being edited
*
@@ -4629,7 +4665,7 @@ public class JTable
{
return editingColumn;
}
-
+
/**
* Set the column, currently being edited
*
@@ -4649,7 +4685,7 @@ public class JTable
{
return editingRow;
}
-
+
/**
* Set the row currently being edited.
*
@@ -4680,7 +4716,7 @@ public class JTable
{
return editorComp != null;
}
-
+
/**
* Set the default editor for the given column class (column data type).
* By default, String is handled by text field and Boolean is handled by
@@ -4691,7 +4727,7 @@ public class JTable
*
* @see TableModel#getColumnClass(int)
*/
- public void setDefaultEditor(Class columnClass, TableCellEditor editor)
+ public void setDefaultEditor(Class<?> columnClass, TableCellEditor editor)
{
if (editor != null)
defaultEditorsByColumnClass.put(columnClass, editor);
@@ -4713,7 +4749,7 @@ public class JTable
if ((index0 < 0 || index0 > (getRowCount()-1)
|| index1 < 0 || index1 > (getRowCount()-1)))
throw new IllegalArgumentException("Row index out of range.");
-
+
getSelectionModel().addSelectionInterval(index0, index1);
}
diff --git a/libjava/classpath/javax/swing/JTextField.java b/libjava/classpath/javax/swing/JTextField.java
index 367503b739f..ace358f8922 100644
--- a/libjava/classpath/javax/swing/JTextField.java
+++ b/libjava/classpath/javax/swing/JTextField.java
@@ -270,7 +270,8 @@ public class JTextField extends JTextComponent
*/
protected void fireActionPerformed()
{
- ActionEvent event = new ActionEvent(this, 0, getText());
+ ActionEvent event = new ActionEvent(this, 0,
+ actionCommand == null ? getText() : actionCommand);
ActionListener[] listeners = getActionListeners();
for (int index = 0; index < listeners.length; ++index)
diff --git a/libjava/classpath/javax/swing/JTextPane.java b/libjava/classpath/javax/swing/JTextPane.java
index c0a5f80cfc8..05968fc8c20 100644
--- a/libjava/classpath/javax/swing/JTextPane.java
+++ b/libjava/classpath/javax/swing/JTextPane.java
@@ -214,20 +214,11 @@ public class JTextPane
*/
public void insertIcon(Icon icon)
{
- SimpleAttributeSet atts = new SimpleAttributeSet();
- atts.addAttribute(StyleConstants.IconAttribute, icon);
- atts.addAttribute(StyleConstants.NameAttribute,
- StyleConstants.IconElementName);
- try
- {
- getDocument().insertString(getCaret().getDot(), " ", atts);
- }
- catch (BadLocationException ex)
- {
- AssertionError err = new AssertionError("Unexpected bad location");
- err.initCause(ex);
- throw err;
- }
+ MutableAttributeSet inputAtts = getInputAttributes();
+ inputAtts.removeAttributes(inputAtts);
+ StyleConstants.setIcon(inputAtts, icon);
+ replaceSelection(" ");
+ inputAtts.removeAttributes(inputAtts);
}
/**
diff --git a/libjava/classpath/javax/swing/JToggleButton.java b/libjava/classpath/javax/swing/JToggleButton.java
index 1769c5ee08a..60c44896019 100644
--- a/libjava/classpath/javax/swing/JToggleButton.java
+++ b/libjava/classpath/javax/swing/JToggleButton.java
@@ -1,5 +1,5 @@
/* JToggleButton.java --
- Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -291,9 +291,8 @@ public class JToggleButton extends AbstractButton implements Accessible
public JToggleButton (String text, Icon icon, boolean selected)
{
super();
+ setModel(new ToggleButtonModel());
init(text, icon);
-
- setModel(new ToggleButtonModel());
model.setSelected(selected);
setAlignmentX(LEFT_ALIGNMENT);
}
diff --git a/libjava/classpath/javax/swing/JToolTip.java b/libjava/classpath/javax/swing/JToolTip.java
index 836c122c6bf..3153894da81 100644
--- a/libjava/classpath/javax/swing/JToolTip.java
+++ b/libjava/classpath/javax/swing/JToolTip.java
@@ -225,4 +225,18 @@ public class JToolTip extends JComponent implements Accessible
{
setUI((ToolTipUI) UIManager.getUI(this));
}
+
+ /**
+ * Returns <code>true</code> if the component is guaranteed to be painted
+ * on top of others. This returns false by default and is overridden by
+ * components like JMenuItem, JPopupMenu and JToolTip to return true for
+ * added efficiency.
+ *
+ * @return <code>true</code> if the component is guaranteed to be painted
+ * on top of others
+ */
+ boolean onTop()
+ {
+ return true;
+ }
}
diff --git a/libjava/classpath/javax/swing/JTree.java b/libjava/classpath/javax/swing/JTree.java
index fa898c5a940..332ec74247d 100644
--- a/libjava/classpath/javax/swing/JTree.java
+++ b/libjava/classpath/javax/swing/JTree.java
@@ -1230,8 +1230,32 @@ public class JTree extends JComponent implements Scrollable, Accessible
*/
public void treeNodesRemoved(TreeModelEvent ev)
{
- // TODO: The API docs suggest that this method should do something
- // but I cannot really see what has to be done here ...
+ if (ev != null)
+ {
+ TreePath parent = ev.getTreePath();
+ Object[] children = ev.getChildren();
+ TreeSelectionModel sm = getSelectionModel();
+ if (children != null)
+ {
+ TreePath path;
+ Vector toRemove = new Vector();
+ // Collect items that we must remove.
+ for (int i = children.length - 1; i >= 0; i--)
+ {
+ path = parent.pathByAddingChild(children[i]);
+ if (nodeStates.containsKey(path))
+ toRemove.add(path);
+ // Clear selection while we are at it.
+ if (sm != null)
+ removeDescendantSelectedPaths(path, true);
+ }
+ if (toRemove.size() > 0)
+ removeDescendantToggledPaths(toRemove.elements());
+ TreeModel model = getModel();
+ if (model == null || model.isLeaf(parent.getLastPathComponent()))
+ nodeStates.remove(parent);
+ }
+ }
}
/**
@@ -1243,9 +1267,38 @@ public class JTree extends JComponent implements Scrollable, Accessible
*/
public void treeStructureChanged(TreeModelEvent ev)
{
- // Set state of new path.
- TreePath path = ev.getTreePath();
- setExpandedState(path, isExpanded(path));
+ if (ev != null)
+ {
+ TreePath parent = ev.getTreePath();
+ if (parent != null)
+ {
+ if (parent.getPathCount() == 1)
+ {
+ // We have a new root, clear everything.
+ clearToggledPaths();
+ Object root = treeModel.getRoot();
+ if (root != null && treeModel.isLeaf(root))
+ nodeStates.put(parent, Boolean.TRUE);
+ }
+ else if (nodeStates.containsKey(parent))
+ {
+ Vector toRemove = new Vector();
+ boolean expanded = isExpanded(parent);
+ toRemove.add(parent);
+ removeDescendantToggledPaths(toRemove.elements());
+ if (expanded)
+ {
+ TreeModel model = getModel();
+ if (model != null
+ || model.isLeaf(parent.getLastPathComponent()))
+ collapsePath(parent);
+ else
+ nodeStates.put(parent, Boolean.TRUE);
+ }
+ }
+ removeDescendantSelectedPaths(parent, false);
+ }
+ }
}
}
@@ -1279,13 +1332,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]));
- }
}
}
@@ -1406,8 +1452,10 @@ public class JTree extends JComponent implements Scrollable, Accessible
* This contains the state of all nodes in the tree. Al/ entries map the
* TreePath of a note to to its state. Valid states are EXPANDED and
* COLLAPSED. Nodes not in this Hashtable are assumed state COLLAPSED.
+ *
+ * This is package private to avoid accessor methods.
*/
- private Hashtable nodeStates = new Hashtable();
+ Hashtable nodeStates = new Hashtable();
protected transient TreeCellEditor cellEditor;
@@ -1486,7 +1534,7 @@ public class JTree extends JComponent implements Scrollable, Accessible
*
* @param value the initial nodes in the tree
*/
- public JTree(Hashtable value)
+ public JTree(Hashtable<?, ?> value)
{
this(createTreeModel(value));
}
@@ -1509,8 +1557,7 @@ public class JTree extends JComponent implements Scrollable, Accessible
public JTree(TreeModel model)
{
setRootVisible(true);
- setSelectionModel(new EmptySelectionModel());
- selectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
+ setSelectionModel( new DefaultTreeSelectionModel() );
// The root node appears expanded by default.
nodeStates = new Hashtable();
@@ -1554,7 +1601,7 @@ public class JTree extends JComponent implements Scrollable, Accessible
*
* @param value the initial nodes in the tree
*/
- public JTree(Vector value)
+ public JTree(Vector<?> value)
{
this(createTreeModel(value));
}
@@ -1685,29 +1732,52 @@ public class JTree extends JComponent implements Scrollable, Accessible
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
int direction)
{
- int delta;
+ int delta = 0;
// Round so that the top would start from the row boundary
if (orientation == SwingConstants.VERTICAL)
{
- // One pixel down, otherwise picks another row too high.
- int row = getClosestRowForLocation(visibleRect.x, visibleRect.y + 1);
- row = row + direction;
- if (row < 0)
- row = 0;
-
- Rectangle newTop = getRowBounds(row);
- delta = newTop.y - visibleRect.y;
+ int row = getClosestRowForLocation(0, visibleRect.y);
+ if (row != -1)
+ {
+ Rectangle b = getRowBounds(row);
+ if (b.y != visibleRect.y)
+ {
+ if (direction < 0)
+ delta = Math.max(0, visibleRect.y - b.y);
+ else
+ delta = b.y + b.height - visibleRect.y;
+ }
+ else
+ {
+ if (direction < 0)
+ {
+ if (row != 0)
+ {
+ b = getRowBounds(row - 1);
+ delta = b.height;
+ }
+ }
+ else
+ delta = b.height;
+ }
+ }
}
else
- delta = direction * rowHeight == 0 ? 20 : rowHeight;
+ // The RI always returns 4 for HORIZONTAL scrolling.
+ delta = 4;
return delta;
}
public int getScrollableBlockIncrement(Rectangle visibleRect,
int orientation, int direction)
{
- return getScrollableUnitIncrement(visibleRect, orientation, direction);
+ int block;
+ if (orientation == SwingConstants.VERTICAL)
+ block = visibleRect.height;
+ else
+ block = visibleRect.width;
+ return block;
}
public boolean getScrollableTracksViewportHeight()
@@ -2050,14 +2120,16 @@ public class JTree extends JComponent implements Scrollable, Accessible
if (selectionModel == model)
return;
+ if( model == null )
+ model = EmptySelectionModel.sharedInstance();
+
if (selectionModel != null)
selectionModel.removeTreeSelectionListener(selectionRedirector);
TreeSelectionModel oldValue = selectionModel;
selectionModel = model;
- if (selectionModel != null)
- selectionModel.addTreeSelectionListener(selectionRedirector);
+ selectionModel.addTreeSelectionListener(selectionRedirector);
firePropertyChange(SELECTION_MODEL_PROPERTY, oldValue, model);
revalidate();
@@ -2184,20 +2256,35 @@ public class JTree extends JComponent implements Scrollable, Accessible
public void setSelectionPath(TreePath path)
{
+ clearSelectionPathStates();
selectionModel.setSelectionPath(path);
}
public void setSelectionPaths(TreePath[] paths)
{
+ clearSelectionPathStates();
selectionModel.setSelectionPaths(paths);
}
+
+ /**
+ * This method, and all calls to it, should be removed once the
+ * DefaultTreeModel fires events properly. Maintenance of the nodeStates
+ * table should really be done in the TreeModelHandler.
+ */
+ private void clearSelectionPathStates()
+ {
+ TreePath[] oldPaths = selectionModel.getSelectionPaths();
+ if (oldPaths != null)
+ for (int i = 0; i < oldPaths.length; i++)
+ nodeStates.remove(oldPaths[i]);
+ }
public void setSelectionRow(int row)
{
TreePath path = getPathForRow(row);
if (path != null)
- selectionModel.setSelectionPath(path);
+ setSelectionPath(path);
}
public void setSelectionRows(int[] rows)
@@ -2272,11 +2359,13 @@ public class JTree extends JComponent implements Scrollable, Accessible
public void removeSelectionPath(TreePath path)
{
+ clearSelectionPathStates();
selectionModel.removeSelectionPath(path);
}
public void removeSelectionPaths(TreePath[] paths)
{
+ clearSelectionPathStates();
selectionModel.removeSelectionPaths(paths);
}
@@ -2285,7 +2374,7 @@ public class JTree extends JComponent implements Scrollable, Accessible
TreePath path = getPathForRow(row);
if (path != null)
- selectionModel.removeSelectionPath(path);
+ removeSelectionPath(path);
}
public void removeSelectionRows(int[] rows)
@@ -2331,7 +2420,7 @@ public class JTree extends JComponent implements Scrollable, Accessible
if (selectionModel != null)
{
TreePath oldValue = selectionModel.getLeadSelectionPath();
- if (path.equals(oldValue))
+ if (path == oldValue || path != null && path.equals(oldValue))
return;
// Repaint the previous and current rows with the lead selection path.
@@ -2409,9 +2498,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()
@@ -2725,7 +2824,7 @@ public class JTree extends JComponent implements Scrollable, Accessible
nodeStates.clear();
}
- protected Enumeration getDescendantToggledPaths(TreePath parent)
+ protected Enumeration<TreePath> getDescendantToggledPaths(TreePath parent)
{
if (parent == null)
return null;
@@ -2874,7 +2973,7 @@ public class JTree extends JComponent implements Scrollable, Accessible
*
* @return An Enumeration containing TreePath objects
*/
- public Enumeration getExpandedDescendants(TreePath path)
+ public Enumeration<TreePath> getExpandedDescendants(TreePath path)
{
Enumeration paths = nodeStates.keys();
Vector relevantPaths = new Vector();
@@ -3002,7 +3101,7 @@ public class JTree extends JComponent implements Scrollable, Accessible
* @param toRemove - Enumeration of TreePaths that need to be removed from
* cache of toggled tree paths.
*/
- protected void removeDescendantToggledPaths(Enumeration toRemove)
+ protected void removeDescendantToggledPaths(Enumeration<TreePath> toRemove)
{
while (toRemove.hasMoreElements())
{
diff --git a/libjava/classpath/javax/swing/JViewport.java b/libjava/classpath/javax/swing/JViewport.java
index 7cf393996c3..d90da1d157b 100644
--- a/libjava/classpath/javax/swing/JViewport.java
+++ b/libjava/classpath/javax/swing/JViewport.java
@@ -157,6 +157,9 @@ public class JViewport extends JComponent implements Accessible
*/
public void componentResized(ComponentEvent ev)
{
+ // Fire state change, because resizing the view means changing the
+ // extentSize.
+ fireStateChanged();
revalidate();
}
}
@@ -198,22 +201,6 @@ public class JViewport extends JComponent implements Accessible
int scrollMode;
- /**
- * The width and height of the Viewport's area in terms of view
- * coordinates. Typically this will be the same as the width and height
- * of the viewport's bounds, unless the viewport transforms units of
- * width and height, which it may do, for example if it magnifies or
- * rotates its view.
- *
- * @see #toViewCoordinates(Dimension)
- */
- Dimension extentSize;
-
- /**
- * The width and height of the view in its own coordinate space.
- */
- Dimension viewSize;
-
/**
* The ViewListener instance.
*/
@@ -265,8 +252,7 @@ public class JViewport extends JComponent implements Accessible
static
{
String scrollModeProp =
- SystemProperties.getProperty("gnu.javax.swing.JViewport.scrollMode",
- "BLIT");
+ SystemProperties.getProperty("gnu.swing.scrollmode", "BACKINGSTORE");
if (scrollModeProp.equalsIgnoreCase("simple"))
defaultScrollMode = SIMPLE_SCROLL_MODE;
else if (scrollModeProp.equalsIgnoreCase("backingstore"))
@@ -290,10 +276,7 @@ public class JViewport extends JComponent implements Accessible
public Dimension getExtentSize()
{
- if (extentSize == null)
- return toViewCoordinates(getSize());
- else
- return extentSize;
+ return getSize();
}
public Dimension toViewCoordinates(Dimension size)
@@ -310,8 +293,12 @@ public class JViewport extends JComponent implements Accessible
public void setExtentSize(Dimension newSize)
{
- extentSize = newSize;
- fireStateChanged();
+ Dimension oldExtent = getExtentSize();
+ if (! newSize.equals(oldExtent))
+ {
+ setSize(newSize);
+ fireStateChanged();
+ }
}
/**
@@ -321,32 +308,34 @@ public class JViewport extends JComponent implements Accessible
*/
public Dimension getViewSize()
{
- if (isViewSizeSet)
- return viewSize;
- else
+ Dimension size;
+ Component view = getView();
+ if (view != null)
{
- Component view = getView();
- if (view != null)
- return view.getPreferredSize();
- else
- return new Dimension();
+ if (isViewSizeSet)
+ size = view.getSize();
+ else
+ size = view.getPreferredSize();
}
+ else
+ size = new Dimension(0, 0);
+ return size;
}
public void setViewSize(Dimension newSize)
{
- viewSize = newSize;
Component view = getView();
if (view != null)
{
- if (newSize != view.getSize())
+ if (! newSize.equals(view.getSize()))
{
- view.setSize(viewSize);
+ scrollUnderway = false;
+ view.setSize(newSize);
+ isViewSizeSet = true;
fireStateChanged();
}
}
- isViewSizeSet = true;
}
/**
@@ -371,23 +360,18 @@ public class JViewport extends JComponent implements Accessible
public void setViewPosition(Point p)
{
- if (getViewPosition().equals(p))
- return;
Component view = getView();
- if (view != null)
+ if (view != null && ! p.equals(getViewPosition()))
{
- Point q = new Point(-p.x, -p.y);
- view.setLocation(q);
- isViewSizeSet = false;
+ scrollUnderway = true;
+ view.setLocation(-p.x, -p.y);
fireStateChanged();
}
- repaint();
}
public Rectangle getViewRect()
{
- return new Rectangle(getViewPosition(),
- getExtentSize());
+ return new Rectangle(getViewPosition(), getExtentSize());
}
/**
@@ -495,7 +479,6 @@ public class JViewport extends JComponent implements Accessible
if (view == null)
return;
- Point pos = getViewPosition();
Rectangle viewBounds = view.getBounds();
Rectangle portBounds = getBounds();
@@ -643,19 +626,11 @@ public class JViewport extends JComponent implements Accessible
*/
public void repaint(long tm, int x, int y, int w, int h)
{
-// Component parent = getParent();
-// if (parent != null)
-// parent.repaint(tm, x + getX(), y + getY(), w, h);
-// else
-// super.repaint(tm, x, y, w, h);
-
- // The specs suggest to implement something like the above. This however
- // breaks blit painting, because the parent (most likely a JScrollPane)
- // clears the background of the offscreen area of the JViewport, thus
- // destroying the pieces that we want to clip. So we simply call super here
- // instead.
- super.repaint(tm, x, y, w, h);
-
+ Component parent = getParent();
+ if (parent != null)
+ parent.repaint(tm, x + getX(), y + getY(), w, h);
+ else
+ super.repaint(tm, x, y, w, h);
}
protected void addImpl(Component comp, Object constraints, int index)
@@ -862,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);
@@ -913,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,
@@ -940,12 +921,24 @@ public class JViewport extends JComponent implements Accessible
/**
* Overridden from JComponent to set the {@link #isPaintRoot} flag.
*
- * @param r the rectangle to paint
+ * @param x the rectangle to paint, X coordinate
+ * @param y the rectangle to paint, Y coordinate
+ * @param w the rectangle to paint, width
+ * @param h the rectangle to paint, height
*/
- void paintImmediately2(Rectangle r)
+ void paintImmediately2(int x, int y, int w, int h)
{
isPaintRoot = true;
- super.paintImmediately2(r);
+ 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/libjava/classpath/javax/swing/JWindow.java b/libjava/classpath/javax/swing/JWindow.java
index 19d830ed1f7..b36b8cf2a60 100644
--- a/libjava/classpath/javax/swing/JWindow.java
+++ b/libjava/classpath/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/libjava/classpath/javax/swing/LookAndFeel.java b/libjava/classpath/javax/swing/LookAndFeel.java
index be543439636..d995bc9e981 100644
--- a/libjava/classpath/javax/swing/LookAndFeel.java
+++ b/libjava/classpath/javax/swing/LookAndFeel.java
@@ -284,7 +284,7 @@ public abstract class LookAndFeel
* @return A {@link UIDefaults.LazyValue} that serves up an
* {@link IconUIResource}.
*/
- public static Object makeIcon(Class baseClass, String gifFile)
+ public static Object makeIcon(Class<?> baseClass, String gifFile)
{
final URL file = baseClass.getResource(gifFile);
return new UIDefaults.LazyValue()
diff --git a/libjava/classpath/javax/swing/Popup.java b/libjava/classpath/javax/swing/Popup.java
index 308cd662d8d..5074d64186c 100644
--- a/libjava/classpath/javax/swing/Popup.java
+++ b/libjava/classpath/javax/swing/Popup.java
@@ -284,7 +284,7 @@ public class Popup
panel.setSize(contents.getSize());
Point layeredPaneLoc = layeredPane.getLocationOnScreen();
panel.setLocation(x - layeredPaneLoc.x, y - layeredPaneLoc.y);
- layeredPane.add(panel, JLayeredPane.POPUP_LAYER);
+ layeredPane.add(panel, JLayeredPane.POPUP_LAYER, 0);
panel.repaint();
}
diff --git a/libjava/classpath/javax/swing/RepaintManager.java b/libjava/classpath/javax/swing/RepaintManager.java
index 80f0a3481cd..773371489d9 100644
--- a/libjava/classpath/javax/swing/RepaintManager.java
+++ b/libjava/classpath/javax/swing/RepaintManager.java
@@ -38,21 +38,29 @@ exception statement from your version. */
package javax.swing;
+import gnu.classpath.SystemProperties;
+import gnu.java.awt.LowPriorityEvent;
+
+import java.applet.Applet;
import java.awt.Component;
import java.awt.Dimension;
+import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
+import java.awt.Toolkit;
import java.awt.Window;
+import java.awt.event.InvocationEvent;
import java.awt.image.VolatileImage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
-import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
+import javax.swing.text.JTextComponent;
+
/**
* <p>The repaint manager holds a set of dirty regions, invalid components,
* and a double buffer surface. The dirty regions and invalid components
@@ -64,6 +72,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)
@@ -72,6 +81,44 @@ import java.util.WeakHashMap;
public class RepaintManager
{
/**
+ * An InvocationEvent subclass that implements LowPriorityEvent. This is used
+ * to defer the execution of RepaintManager requests as long as possible on
+ * the event queue. This way we make sure that all available input is
+ * processed before getting active with the RepaintManager. This allows
+ * for better optimization (more validate and repaint requests can be
+ * coalesced) and thus has a positive effect on performance for GUI
+ * applications under heavy load.
+ */
+ private static class RepaintWorkerEvent
+ extends InvocationEvent
+ implements LowPriorityEvent
+ {
+
+ /**
+ * Creates a new RepaintManager event.
+ *
+ * @param source the source
+ * @param runnable the runnable to execute
+ */
+ 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.dispatch();
+ }
+ }
+
+ /**
* The current repaint managers, indexed by their ThreadGroups.
*/
static WeakHashMap currentRepaintManagers;
@@ -197,19 +244,6 @@ public class RepaintManager
private WeakHashMap offscreenBuffers;
/**
- * Indicates if the RepaintManager is currently repainting an area.
- */
- private boolean repaintUnderway;
-
- /**
- * This holds buffer commit requests when the RepaintManager is working.
- * This maps Component objects (the top level components) to Rectangle
- * objects (the area of the corresponding buffer that must be blitted on
- * the component).
- */
- private HashMap commitRequests;
-
- /**
* The maximum width and height to allocate as a double buffer. Requests
* beyond this size are ignored.
*
@@ -230,10 +264,10 @@ public class RepaintManager
invalidComponents = new ArrayList();
repaintWorker = new RepaintWorker();
doubleBufferMaximumSize = new Dimension(2000,2000);
- doubleBufferingEnabled = true;
+ doubleBufferingEnabled =
+ SystemProperties.getProperty("gnu.swing.doublebuffering", "true")
+ .equals("true");
offscreenBuffers = new WeakHashMap();
- repaintUnderway = false;
- commitRequests = new HashMap();
}
/**
@@ -355,7 +389,7 @@ public class RepaintManager
if (! repaintWorker.isLive())
{
repaintWorker.setLive(true);
- SwingUtilities.invokeLater(repaintWorker);
+ invokeLater(repaintWorker);
}
}
@@ -397,23 +431,21 @@ public class RepaintManager
{
if (w <= 0 || h <= 0 || !component.isShowing())
return;
-
- Component parent = component.getParent();
-
component.computeVisibleRect(rectCache);
SwingUtilities.computeIntersection(x, y, w, h, rectCache);
if (! rectCache.isEmpty())
{
- if (dirtyComponents.containsKey(component))
+ synchronized (dirtyComponents)
{
- SwingUtilities.computeUnion(rectCache.x, rectCache.y,
- rectCache.width, rectCache.height,
- (Rectangle) dirtyComponents.get(component));
- }
- else
- {
- 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());
}
@@ -422,7 +454,7 @@ public class RepaintManager
if (! repaintWorker.isLive())
{
repaintWorker.setLive(true);
- SwingUtilities.invokeLater(repaintWorker);
+ invokeLater(repaintWorker);
}
}
}
@@ -536,7 +568,7 @@ public class RepaintManager
*/
public void paintDirtyRegions()
{
- // Short cicuit if there is nothing to paint.
+ // Short circuit if there is nothing to paint.
if (dirtyComponents.size() == 0)
return;
@@ -557,7 +589,6 @@ public class RepaintManager
compileRepaintRoots(dirtyComponentsWork, dirty, repaintRoots);
}
- repaintUnderway = true;
for (Iterator i = repaintRoots.iterator(); i.hasNext();)
{
JComponent comp = (JComponent) i.next();
@@ -567,13 +598,11 @@ public class RepaintManager
comp.paintImmediately(damaged);
}
dirtyComponentsWork.clear();
- repaintUnderway = false;
- commitRemainingBuffers();
}
-
+
/**
* Compiles a list of components that really get repainted. This is called
- * once for each component in the dirtyComponents HashMap, each time with
+ * once for each component in the dirtyRegions HashMap, each time with
* another <code>dirty</code> parameter. This searches up the component
* hierarchy of <code>dirty</code> to find the highest parent that is also
* marked dirty and merges the dirty regions.
@@ -588,6 +617,29 @@ public class RepaintManager
Component current = dirty;
Component root = dirty;
+ // This will contain the dirty region in the root coordinate system,
+ // possibly clipped by ancestor's bounds.
+ Rectangle originalDirtyRect = (Rectangle) dirtyRegions.get(dirty);
+ rectCache.setBounds(originalDirtyRect);
+
+ // The bounds of the current component.
+ int x = dirty.getX();
+ int y = dirty.getY();
+ int w = dirty.getWidth();
+ int h = dirty.getHeight();
+
+ // Do nothing if dirty region is clipped away by the component's bounds.
+ rectCache = SwingUtilities.computeIntersection(0, 0, w, h, rectCache);
+ if (rectCache.isEmpty())
+ return;
+
+ // The cumulated offsets.
+ int dx = 0;
+ int dy = 0;
+ // The actual offset for the found root.
+ int rootDx = 0;
+ int rootDy = 0;
+
// Search the highest component that is also marked dirty.
Component parent;
while (true)
@@ -597,10 +649,29 @@ public class RepaintManager
break;
current = parent;
+ // Update the offset.
+ dx += x;
+ dy += y;
+ rectCache.x += x;
+ rectCache.y += y;
+
+ x = current.getX();
+ y = current.getY();
+ w = current.getWidth();
+ h = current.getHeight();
+ rectCache = SwingUtilities.computeIntersection(0, 0, w, h, rectCache);
+
+ // Don't paint if the dirty regions is clipped away by any of
+ // its ancestors.
+ if (rectCache.isEmpty())
+ return;
+
// We can skip to the next up when this parent is not dirty.
if (dirtyRegions.containsKey(parent))
{
root = current;
+ rootDx = dx;
+ rootDy = dy;
}
}
@@ -608,15 +679,16 @@ public class RepaintManager
// the are different.
if (root != dirty)
{
- Rectangle dirtyRect = (Rectangle) dirtyRegions.get(dirty);
- dirtyRect = SwingUtilities.convertRectangle(dirty, dirtyRect, root);
- Rectangle rootRect = (Rectangle) dirtyRegions.get(root);
- SwingUtilities.computeUnion(dirtyRect.x, dirtyRect.y, dirtyRect.width,
- dirtyRect.height, rootRect);
+ rectCache.x += rootDx - dx;
+ rectCache.y += rootDy - dy;
+ Rectangle dirtyRect = (Rectangle) dirtyRegions.get(root);
+ SwingUtilities.computeUnion(rectCache.x, rectCache.y, rectCache.width,
+ rectCache.height, dirtyRect);
}
// Adds the root to the roots set.
- roots.add(root);
+ if (! roots.contains(root))
+ roots.add(root);
}
/**
@@ -643,128 +715,43 @@ public class RepaintManager
width = Math.min(doubleBufferMaximumSize.width, width);
int height = Math.max(proposedHeight, root.getHeight());
height = Math.min(doubleBufferMaximumSize.height, height);
- buffer = root.createImage(width, height);
+ buffer = component.createImage(width, height);
offscreenBuffers.put(root, buffer);
}
return buffer;
}
/**
- * Blits the back buffer of the specified root component to the screen. If
- * the RepaintManager is currently working on a paint request, the commit
- * requests are queued up and committed at once when the paint request is
- * done (by {@link #commitRemainingBuffers}). This is package private because
- * it must get called by JComponent.
+ * Blits the back buffer of the specified root component to the screen.
+ * This is package private because it must get called by JComponent.
*
* @param comp the component to be painted
- * @param area the area to paint on screen, in comp coordinates
- */
- void commitBuffer(Component comp, Rectangle area)
- {
- // Determine the component that we finally paint the buffer upon.
- // We need to paint on the nearest heavyweight component, so that Swing
- // hierarchies inside (non-window) heavyweights get painted correctly.
- // Otherwise we would end up blitting the backbuffer behind the heavyweight
- // which is wrong.
- Component root = getHeavyweightParent(comp);
- // FIXME: Optimize this.
- Rectangle rootRect = SwingUtilities.convertRectangle(comp, area, root);
-
- // We synchronize on dirtyComponents here because that is what
- // paintDirtyRegions also synchronizes on while painting.
- synchronized (dirtyComponents)
- {
- // If the RepaintManager is not currently painting, then directly
- // blit the requested buffer on the screen.
- if (true || ! repaintUnderway)
- {
- blitBuffer(root, rootRect);
- }
-
- // Otherwise queue this request up, until all the RepaintManager work
- // is done.
- else
- {
- if (commitRequests.containsKey(root))
- SwingUtilities.computeUnion(rootRect.x, rootRect.y,
- rootRect.width, rootRect.height,
- (Rectangle) commitRequests.get(root));
- else
- commitRequests.put(root, rootRect);
- }
- }
- }
-
- /**
- * Copies the buffer to the screen. Note that the root component here is
- * not necessarily the component with which the offscreen buffer is
- * associated. The offscreen buffers are always allocated for the toplevel
- * windows. However, painted is performed on lower-level heavyweight
- * components too, if they contain Swing components.
- *
- * @param root the heavyweight component to blit upon
- * @param rootRect the rectangle in the root component's coordinate space
+ * @param x the area to paint on screen, in comp coordinates
+ * @param y the area to paint on screen, in comp coordinates
+ * @param w the area to paint on screen, in comp coordinates
+ * @param h the area to paint on screen, in comp coordinates
*/
- private void blitBuffer(Component root, Rectangle rootRect)
+ void commitBuffer(Component comp, int x, int y, int w, int h)
{
- if (! root.isShowing())
- return;
-
- // Find the Window from which we use the backbuffer.
- Component bufferRoot = root;
- Rectangle bufferRect = rootRect.getBounds();
- if (!(bufferRoot instanceof Window))
+ Component root = comp;
+ while (root != null
+ && ! (root instanceof Window || root instanceof Applet))
{
- bufferRoot = SwingUtilities.getWindowAncestor(bufferRoot);
- SwingUtilities.convertRectangleToAncestor(root, rootRect, bufferRoot);
+ x += root.getX();
+ y += root.getY();
+ root = root.getParent();
}
- Graphics g = root.getGraphics();
- Image buffer = (Image) offscreenBuffers.get(bufferRoot);
-
- // Make sure we have a sane clip at this point.
- g.clipRect(rootRect.x, rootRect.y, rootRect.width, rootRect.height);
- g.drawImage(buffer, rootRect.x - bufferRect.x, rootRect.y - bufferRect.y,
- root);
- g.dispose();
-
- }
-
- /**
- * Finds and returns the nearest heavyweight parent for the specified
- * component. If the component isn't contained inside a heavyweight parent,
- * this returns null.
- *
- * @param comp the component
- *
- * @return the nearest heavyweight parent for the specified component or
- * null if the component has no heavyweight ancestor
- */
- private Component getHeavyweightParent(Component comp)
- {
- while (comp != null && comp.isLightweight())
- comp = comp.getParent();
- return comp;
- }
-
- /**
- * Commits the queued up back buffers to screen all at once.
- */
- private void commitRemainingBuffers()
- {
- // We synchronize on dirtyComponents here because that is what
- // paintDirtyRegions also synchronizes on while painting.
- synchronized (dirtyComponents)
+ if (root != null)
{
- Set entrySet = commitRequests.entrySet();
- Iterator i = entrySet.iterator();
- while (i.hasNext())
+ Graphics g = root.getGraphics();
+ Image buffer = (Image) offscreenBuffers.get(root);
+ if (buffer != null)
{
- Map.Entry entry = (Map.Entry) i.next();
- Component root = (Component) entry.getKey();
- Rectangle area = (Rectangle) entry.getValue();
- blitBuffer(root, area);
- i.remove();
+ // Make sure we have a sane clip at this point.
+ g.clipRect(x, y, w, h);
+ g.drawImage(buffer, 0, 0, root);
+ g.dispose();
}
}
}
@@ -858,4 +845,18 @@ public class RepaintManager
{
return "RepaintManager";
}
+
+ /**
+ * Sends an RepaintManagerEvent to the event queue with the specified
+ * runnable. This is similar to SwingUtilities.invokeLater(), only that the
+ * event is a low priority event in order to defer the execution a little
+ * more.
+ */
+ private void invokeLater(Runnable runnable)
+ {
+ Toolkit tk = Toolkit.getDefaultToolkit();
+ EventQueue evQueue = tk.getSystemEventQueue();
+ InvocationEvent ev = new RepaintWorkerEvent(evQueue, runnable, null, false);
+ evQueue.postEvent(ev);
+ }
}
diff --git a/libjava/classpath/javax/swing/ScrollPaneLayout.java b/libjava/classpath/javax/swing/ScrollPaneLayout.java
index 8ce8fd86f7a..2a16f26eae9 100644
--- a/libjava/classpath/javax/swing/ScrollPaneLayout.java
+++ b/libjava/classpath/javax/swing/ScrollPaneLayout.java
@@ -46,6 +46,8 @@ import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.io.Serializable;
+import javax.swing.border.Border;
+
/**
* ScrollPaneLayout
* @author Andrew Selkirk
@@ -277,6 +279,16 @@ public class ScrollPaneLayout
width += rowHead.getPreferredSize().width;
if (colHead != null && colHead.isVisible())
height += colHead.getPreferredSize().height;
+
+ // Add insets of viewportBorder if present.
+ Border vpBorder = sc.getViewportBorder();
+ if (vpBorder != null)
+ {
+ Insets i = vpBorder.getBorderInsets(sc);
+ width += i.left + i.right;
+ height += i.top + i.bottom;
+ }
+
Insets i = sc.getInsets();
return new Dimension(width + i.left + i.right,
height + i.left + i.right);
@@ -300,6 +312,15 @@ public class ScrollPaneLayout
!= JScrollPane.HORIZONTAL_SCROLLBAR_NEVER)
height += sc.getHorizontalScrollBar().getMinimumSize().height;
+ // Add insets of viewportBorder if present.
+ Border vpBorder = sc.getViewportBorder();
+ if (vpBorder != null)
+ {
+ i = vpBorder.getBorderInsets(sc);
+ width += i.left + i.right;
+ height += i.top + i.bottom;
+ }
+
return new Dimension(width, height);
}
@@ -342,6 +363,15 @@ public class ScrollPaneLayout
int y1 = 0, y2 = 0, y3 = 0, y4 = 0;
Rectangle scrollPaneBounds = SwingUtilities.calculateInnerArea(sc, null);
+ // If there is a viewportBorder, remove its insets from the available
+ // space.
+ Border vpBorder = sc.getViewportBorder();
+ Insets vpi;
+ if (vpBorder != null)
+ vpi = vpBorder.getBorderInsets(sc);
+ else
+ vpi = new Insets(0, 0, 0, 0);
+
x1 = scrollPaneBounds.x;
y1 = scrollPaneBounds.y;
x4 = scrollPaneBounds.x + scrollPaneBounds.width;
@@ -404,7 +434,9 @@ public class ScrollPaneLayout
// now set the layout
if (viewport != null)
- viewport.setBounds(new Rectangle(x2, y2, x3 - x2, y3 - y2));
+ viewport.setBounds(new Rectangle(x2 + vpi.left, y2 + vpi.top,
+ x3 - x2 - vpi.left - vpi.right,
+ y3 - y2 - vpi.top - vpi.bottom));
if (colHead != null)
colHead.setBounds(new Rectangle(x2, y1, x3 - x2, y2 - y1));
@@ -415,7 +447,7 @@ public class ScrollPaneLayout
if (showVsb)
{
vsb.setVisible(true);
- vsb.setBounds(new Rectangle(x3, y2, x4 - x3, y3 - y2));
+ vsb.setBounds(new Rectangle(x3, y2, x4 - x3, y3 - y2 ));
}
else if (vsb != null)
vsb.setVisible(false);
@@ -423,7 +455,7 @@ public class ScrollPaneLayout
if (showHsb)
{
hsb.setVisible(true);
- hsb.setBounds(new Rectangle(x2, y3, x3 - x2, y4 - y3));
+ hsb.setBounds(new Rectangle(x2 , y3, x3 - x2, y4 - y3));
}
else if (hsb != null)
hsb.setVisible(false);
diff --git a/libjava/classpath/javax/swing/SizeSequence.java b/libjava/classpath/javax/swing/SizeSequence.java
index a5f34710c76..cb6c8bc25df 100644
--- a/libjava/classpath/javax/swing/SizeSequence.java
+++ b/libjava/classpath/javax/swing/SizeSequence.java
@@ -129,14 +129,18 @@ public class SizeSequence
}
/**
- * Returns the size of the specified element.
+ * Returns the size of the specified element, or 0 if the element index is
+ * outside the defined range.
*
* @param index the element index.
*
- * @return The size of the specified element.
+ * @return The size of the specified element, or 0 if the element index is
+ * outside the defined range.
*/
public int getSize(int index)
{
+ if (index < 0 || index >= sizes.length)
+ return 0;
return sizes[index];
}
diff --git a/libjava/classpath/javax/swing/SortingFocusTraversalPolicy.java b/libjava/classpath/javax/swing/SortingFocusTraversalPolicy.java
index 96ef3832955..d14ee1d30a4 100644
--- a/libjava/classpath/javax/swing/SortingFocusTraversalPolicy.java
+++ b/libjava/classpath/javax/swing/SortingFocusTraversalPolicy.java
@@ -91,7 +91,7 @@ public class SortingFocusTraversalPolicy
*
* @param comparator the comparator to set
*/
- public SortingFocusTraversalPolicy(Comparator comparator)
+ public SortingFocusTraversalPolicy(Comparator<? super Component> comparator)
{
this.comparator = comparator;
}
@@ -119,7 +119,7 @@ public class SortingFocusTraversalPolicy
*
* @see #setComparator
*/
- protected Comparator getComparator()
+ protected Comparator<? super Component> getComparator()
{
return comparator;
}
@@ -131,7 +131,7 @@ public class SortingFocusTraversalPolicy
*
* @see #getComparator
*/
- protected void setComparator(Comparator comparator)
+ protected void setComparator(Comparator<? super Component> comparator)
{
this.comparator = comparator;
}
diff --git a/libjava/classpath/javax/swing/SpinnerListModel.java b/libjava/classpath/javax/swing/SpinnerListModel.java
index d8e2f22d585..52ac360e924 100644
--- a/libjava/classpath/javax/swing/SpinnerListModel.java
+++ b/libjava/classpath/javax/swing/SpinnerListModel.java
@@ -118,7 +118,7 @@ public class SpinnerListModel extends AbstractSpinnerModel
* @see SpinnerListModel#getNextValue()
* @see SpinnerListModel#getValue()
*/
- public SpinnerListModel(List list)
+ public SpinnerListModel(List<?> list)
{
// Retain a reference to the valid list.
setList(list);
@@ -163,7 +163,7 @@ public class SpinnerListModel extends AbstractSpinnerModel
*
* @return The backing list.
*/
- public List getList()
+ public List<?> getList()
{
return list;
}
@@ -239,7 +239,7 @@ public class SpinnerListModel extends AbstractSpinnerModel
*
* @see ChangeEvent
*/
- public void setList(List list)
+ public void setList(List<?> list)
{
// Check for null or zero size list.
if (list == null || list.size() == 0)
diff --git a/libjava/classpath/javax/swing/SwingUtilities.java b/libjava/classpath/javax/swing/SwingUtilities.java
index ccd37d03a55..6ff0b3346df 100644
--- a/libjava/classpath/javax/swing/SwingUtilities.java
+++ b/libjava/classpath/javax/swing/SwingUtilities.java
@@ -40,7 +40,6 @@ package javax.swing;
import java.applet.Applet;
import java.awt.Component;
-import java.awt.ComponentOrientation;
import java.awt.Container;
import java.awt.FontMetrics;
import java.awt.Frame;
@@ -61,6 +60,8 @@ import javax.accessibility.Accessible;
import javax.accessibility.AccessibleStateSet;
import javax.swing.plaf.ActionMapUIResource;
import javax.swing.plaf.InputMapUIResource;
+import javax.swing.plaf.basic.BasicHTML;
+import javax.swing.text.View;
/**
* A number of static utility functions which are
@@ -324,7 +325,7 @@ public class SwingUtilities
* @see #getAncestorOfClass
* @see #windowForComponent
*/
- public static Container getAncestorOfClass(Class c, Component comp)
+ public static Container getAncestorOfClass(Class<?> c, Component comp)
{
while (comp != null && (! c.isInstance(comp)))
comp = comp.getParent();
@@ -719,44 +720,41 @@ public class SwingUtilities
// Fix up the orientation-based horizontal positions.
- if (horizontalTextPosition == LEADING)
- {
- if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
- horizontalTextPosition = RIGHT;
- else
- horizontalTextPosition = LEFT;
- }
- else if (horizontalTextPosition == TRAILING)
+ boolean ltr = true;
+ if (c != null && ! c.getComponentOrientation().isLeftToRight())
+ ltr = false;
+
+ switch (horizontalTextPosition)
{
- if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
- horizontalTextPosition = LEFT;
- else
- horizontalTextPosition = RIGHT;
+ case LEADING:
+ horizontalTextPosition = ltr ? LEFT : RIGHT;
+ break;
+ case TRAILING:
+ horizontalTextPosition = ltr ? RIGHT : LEFT;
+ break;
+ default:
+ // Nothing to do in the other cases.
}
// Fix up the orientation-based alignments.
-
- if (horizontalAlignment == LEADING)
- {
- if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
- horizontalAlignment = RIGHT;
- else
- horizontalAlignment = LEFT;
- }
- else if (horizontalAlignment == TRAILING)
+ switch (horizontalAlignment)
{
- if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
- horizontalAlignment = LEFT;
- else
- horizontalAlignment = RIGHT;
+ case LEADING:
+ horizontalAlignment = ltr ? LEFT : RIGHT;
+ break;
+ case TRAILING:
+ horizontalAlignment = ltr ? RIGHT : LEFT;
+ break;
+ default:
+ // Nothing to do in the other cases.
}
-
- return layoutCompoundLabel(fm, text, icon,
- verticalAlignment,
- horizontalAlignment,
- verticalTextPosition,
- horizontalTextPosition,
- viewR, iconR, textR, textIconGap);
+
+ return layoutCompoundLabelImpl(c, fm, text, icon,
+ verticalAlignment,
+ horizontalAlignment,
+ verticalTextPosition,
+ horizontalTextPosition,
+ viewR, iconR, textR, textIconGap);
}
/**
@@ -829,6 +827,82 @@ public class SwingUtilities
Rectangle textR,
int textIconGap)
{
+ return layoutCompoundLabelImpl(null, fm, text, icon, verticalAlignment,
+ horizontalAlignment, verticalTextPosition,
+ horizontalTextPosition, viewR, iconR, textR,
+ textIconGap);
+ }
+
+ /**
+ * <p>Layout a "compound label" consisting of a text string and an icon
+ * which is to be placed near the rendered text. Once the text and icon
+ * are laid out, the text rectangle and icon rectangle parameters are
+ * altered to store the calculated positions.</p>
+ *
+ * <p>The size of the text is calculated from the provided font metrics
+ * object. This object should be the metrics of the font you intend to
+ * paint the label with.</p>
+ *
+ * <p>The position values control where the text is placed relative to
+ * the icon. The horizontal position value should be one of the constants
+ * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>. The
+ * vertical position value should be one fo the constants
+ * <code>TOP</code>, <code>BOTTOM</code> or <code>CENTER</code>.</p>
+ *
+ * <p>The text-icon gap value controls the number of pixels between the
+ * icon and the text.</p>
+ *
+ * <p>The alignment values control where the text and icon are placed, as
+ * a combined unit, within the view rectangle. The horizontal alignment
+ * value should be one of the constants <code>LEFT</code>, <code>RIGHT</code> or
+ * <code>CENTER</code>. The vertical alignment valus should be one of the
+ * constants <code>TOP</code>, <code>BOTTOM</code> or
+ * <code>CENTER</code>.</p>
+ *
+ * <p>If the text and icon are equal to or larger than the view
+ * rectangle, the horizontal and vertical alignment values have no
+ * affect.</p>
+ *
+ * <p>Note that this method does <em>not</em> know how to deal with
+ * horizontal alignments or positions given as <code>LEADING</code> or
+ * <code>TRAILING</code> values. Use the other overloaded variant of this
+ * method if you wish to use such values.
+ *
+ * @param fm The font metrics used to measure the text
+ * @param text The text to place in the compound label
+ * @param icon The icon to place next to the text
+ * @param verticalAlignment The vertical alignment of the label relative
+ * to its component
+ * @param horizontalAlignment The horizontal alignment of the label
+ * relative to its component
+ * @param verticalTextPosition The vertical position of the label's text
+ * relative to its icon
+ * @param horizontalTextPosition The horizontal position of the label's
+ * text relative to its icon
+ * @param viewR The view rectangle, specifying the area which layout is
+ * constrained to
+ * @param iconR A rectangle which is modified to hold the laid-out
+ * position of the icon
+ * @param textR A rectangle which is modified to hold the laid-out
+ * position of the text
+ * @param textIconGap The distance between text and icon
+ *
+ * @return The string of characters, possibly truncated with an elipsis,
+ * which is laid out in this label
+ */
+ private static String layoutCompoundLabelImpl(JComponent c,
+ FontMetrics fm,
+ String text,
+ Icon icon,
+ int verticalAlignment,
+ int horizontalAlignment,
+ int verticalTextPosition,
+ int horizontalTextPosition,
+ Rectangle viewR,
+ Rectangle iconR,
+ Rectangle textR,
+ int textIconGap)
+ {
// Work out basic height and width.
@@ -843,94 +917,108 @@ public class SwingUtilities
iconR.width = icon.getIconWidth();
iconR.height = icon.getIconHeight();
}
+
if (text == null || text.equals(""))
{
textIconGap = 0;
textR.width = 0;
textR.height = 0;
+ text = "";
}
else
{
- int fromIndex = 0;
- textR.width = fm.stringWidth(text);
- textR.height = fm.getHeight();
- while (text.indexOf('\n', fromIndex) != -1)
+ int availableWidth = viewR.width;
+ if (horizontalTextPosition != CENTER)
+ availableWidth -= iconR.width + textIconGap;
+ View html = c == null ? null
+ : (View) c.getClientProperty(BasicHTML.propertyKey);
+ if (html != null)
+ {
+ textR.width = (int) html.getPreferredSpan(View.X_AXIS);
+ textR.width = Math.min(availableWidth, textR.width);
+ textR.height = (int) html.getPreferredSpan(View.Y_AXIS);
+ }
+ else
{
- textR.height += fm.getHeight();
- fromIndex = text.indexOf('\n', fromIndex) + 1;
+ int fromIndex = 0;
+ textR.width = fm.stringWidth(text);
+ textR.height = fm.getHeight();
+ if (textR.width > availableWidth)
+ {
+ text = clipString(c, fm, text, availableWidth);
+ textR.width = fm.stringWidth(text);
+ }
}
}
- // Work out the position of text and icon, assuming the top-left coord
+ // Work out the position of text, assuming the top-left coord
// starts at (0,0). We will fix that up momentarily, after these
// "position" decisions are made and we look at alignment.
- switch (horizontalTextPosition)
+ switch (verticalTextPosition)
{
- case LEFT:
- textR.x = 0;
- iconR.x = textR.width + textIconGap;
+ case TOP:
+ textR.y = horizontalTextPosition == CENTER ?
+ - textR.height - textIconGap : 0;
break;
- case RIGHT:
- iconR.x = 0;
- textR.x = iconR.width + textIconGap;
+ case BOTTOM:
+ textR.y = horizontalTextPosition == CENTER ?
+ iconR.height + textIconGap : iconR.height - textR.height;
break;
case CENTER:
- int centerLine = Math.max(textR.width, iconR.width) / 2;
- textR.x = centerLine - textR.width/2;
- iconR.x = centerLine - iconR.width/2;
+ textR.y = iconR.height / 2 - textR.height / 2;
break;
}
- switch (verticalTextPosition)
+ switch (horizontalTextPosition)
{
- case TOP:
- textR.y = 0;
- iconR.y = (horizontalTextPosition == CENTER
- ? textR.height + textIconGap : 0);
+ case LEFT:
+ textR.x = -(textR.width + textIconGap);
break;
- case BOTTOM:
- iconR.y = 0;
- textR.y = (horizontalTextPosition == CENTER
- ? iconR.height + textIconGap
- : Math.max(iconR.height - textR.height, 0));
+ case RIGHT:
+ textR.x = iconR.width + textIconGap;
break;
case CENTER:
- int centerLine = Math.max(textR.height, iconR.height) / 2;
- textR.y = centerLine - textR.height/2;
- iconR.y = centerLine - iconR.height/2;
+ textR.x = iconR.width / 2 - textR.width / 2;
break;
}
+
// The two rectangles are laid out correctly now, but only assuming
// that their upper left corner is at (0,0). If we have any alignment other
// than TOP and LEFT, we need to adjust them.
- Rectangle u = textR.union(iconR);
- int horizontalAdjustment = viewR.x;
- int verticalAdjustment = viewR.y;
+ // These coordinates specify the rectangle that contains both the
+ // icon and text. Move it so that it fullfills the alignment properties.
+ int lx = Math.min(iconR.x, textR.x);
+ int lw = Math.max(iconR.x + iconR.width, textR.x + textR.width) - lx;
+ int ly = Math.min(iconR.y, textR.y);
+ int lh = Math.max(iconR.y + iconR.height, textR.y + textR.height) - ly;
+ int horizontalAdjustment = 0;
+ int verticalAdjustment = 0;
switch (verticalAlignment)
{
case TOP:
+ verticalAdjustment = viewR.y - ly;
break;
case BOTTOM:
- verticalAdjustment += (viewR.height - u.height);
+ verticalAdjustment = viewR.y + viewR.height - ly - lh;
break;
case CENTER:
- verticalAdjustment += ((viewR.height/2) - (u.height/2));
+ verticalAdjustment = viewR.y + viewR.height / 2 - ly - lh / 2;
break;
}
switch (horizontalAlignment)
{
case LEFT:
+ horizontalAdjustment = viewR.x - lx;
break;
case RIGHT:
- horizontalAdjustment += (viewR.width - u.width);
+ horizontalAdjustment = viewR.x + viewR.width - lx - lw;
break;
case CENTER:
- horizontalAdjustment += ((viewR.width/2) - (u.width/2));
+ horizontalAdjustment = (viewR.x + (viewR.width / 2)) - (lx + (lw / 2));
break;
}
-
iconR.x += horizontalAdjustment;
iconR.y += verticalAdjustment;
@@ -940,6 +1028,48 @@ public class SwingUtilities
return text;
}
+ /**
+ * The method clips the specified string so that it fits into the
+ * available width. It is only called when the text really doesn't fit,
+ * so we don't need to check that again.
+ *
+ * @param c the component
+ * @param fm the font metrics
+ * @param text the text
+ * @param availableWidth the available width
+ *
+ * @return the clipped string
+ */
+ private static String clipString(JComponent c, FontMetrics fm, String text,
+ int availableWidth)
+ {
+ String dots = "...";
+ int dotsWidth = fm.stringWidth(dots);
+ char[] string = text.toCharArray();
+ int endIndex = string.length;
+ while (fm.charsWidth(string, 0, endIndex) + dotsWidth > availableWidth
+ && endIndex > 0)
+ endIndex--;
+ String clipped;
+ if (string.length >= endIndex + 3)
+ {
+ string[endIndex] = '.';
+ string[endIndex + 1] = '.';
+ string[endIndex + 2] = '.';
+ clipped = new String(string, 0, endIndex + 3);
+ }
+ else
+ {
+ char[] clippedChars = new char[string.length + 3];
+ System.arraycopy(string, 0, clippedChars, 0, string.length);
+ clippedChars[endIndex] = '.';
+ clippedChars[endIndex + 1] = '.';
+ clippedChars[endIndex + 2] = '.';
+ clipped = new String(clippedChars, 0, endIndex + 3);
+ }
+ return clipped;
+ }
+
/**
* Calls {@link java.awt.EventQueue#invokeLater} with the
* specified {@link Runnable}.
diff --git a/libjava/classpath/javax/swing/Timer.java b/libjava/classpath/javax/swing/Timer.java
index acd22624947..acd1eb49359 100644
--- a/libjava/classpath/javax/swing/Timer.java
+++ b/libjava/classpath/javax/swing/Timer.java
@@ -228,7 +228,7 @@ public class Timer
* fired by this timer
* @since 1.3
*/
- public EventListener[] getListeners(Class listenerType)
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
{
return listenerList.getListeners(listenerType);
}
diff --git a/libjava/classpath/javax/swing/ToolTipManager.java b/libjava/classpath/javax/swing/ToolTipManager.java
index 963ccf88117..152fc03430f 100644
--- a/libjava/classpath/javax/swing/ToolTipManager.java
+++ b/libjava/classpath/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/libjava/classpath/javax/swing/TransferHandler.java b/libjava/classpath/javax/swing/TransferHandler.java
index 40a36b27d24..d594a8244bb 100644
--- a/libjava/classpath/javax/swing/TransferHandler.java
+++ b/libjava/classpath/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/libjava/classpath/javax/swing/UIDefaults.java b/libjava/classpath/javax/swing/UIDefaults.java
index bf5242f6552..9766cb05cec 100644
--- a/libjava/classpath/javax/swing/UIDefaults.java
+++ b/libjava/classpath/javax/swing/UIDefaults.java
@@ -63,7 +63,7 @@ import javax.swing.plaf.InputMapUIResource;
*
* @author Ronald Veldema (rveldema@cs.vu.nl)
*/
-public class UIDefaults extends Hashtable
+public class UIDefaults extends Hashtable<Object, Object>
{
/** Our ResourceBundles. */
@@ -672,7 +672,7 @@ public class UIDefaults extends Hashtable
*
* @return the UI class for <code>id</code>
*/
- public Class getUIClass(String id, ClassLoader loader)
+ public Class<? extends ComponentUI> getUIClass(String id, ClassLoader loader)
{
String className = (String) get(id);
if (className == null)
@@ -681,7 +681,7 @@ public class UIDefaults extends Hashtable
{
if (loader == null)
loader = ClassLoader.getSystemClassLoader();
- return loader.loadClass (className);
+ return (Class<? extends ComponentUI>) loader.loadClass (className);
}
catch (Exception e)
{
@@ -698,7 +698,7 @@ public class UIDefaults extends Hashtable
*
* @return the UI class for <code>id</code>
*/
- public Class getUIClass(String id)
+ public Class<? extends ComponentUI> getUIClass(String id)
{
return getUIClass (id, null);
}
diff --git a/libjava/classpath/javax/swing/UIManager.java b/libjava/classpath/javax/swing/UIManager.java
index 77be44afcbb..3b1b3f72b38 100644
--- a/libjava/classpath/javax/swing/UIManager.java
+++ b/libjava/classpath/javax/swing/UIManager.java
@@ -154,8 +154,16 @@ public class UIManager implements Serializable
UIDefaults fallback;
+ /**
+ * Creates a new <code>MultiplexUIDefaults</code> instance with
+ * <code>d</code> as the fallback defaults.
+ *
+ * @param d the fallback defaults (<code>null</code> not permitted).
+ */
MultiplexUIDefaults(UIDefaults d)
{
+ if (d == null)
+ throw new NullPointerException();
fallback = d;
}
@@ -400,6 +408,8 @@ public class UIManager implements Serializable
* @param key the key.
*
* @return The object.
+ *
+ * @since 1.4
*/
public static Object get(Object key, Locale locale)
{
@@ -407,61 +417,120 @@ public class UIManager implements Serializable
}
/**
- * Returns a boolean value from the defaults table,
- * <code>false</code> if key is not present.
+ * Returns a boolean value from the defaults table. If there is no value
+ * for the specified key, or the value is not an instance of {@link Boolean},
+ * this method returns <code>false</code>.
+ *
+ * @param key the key (<code>null</code> not permitted).
*
+ * @return The boolean value associated with the specified key.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
+ *
* @since 1.4
*/
public static boolean getBoolean(Object key)
{
- Boolean value = (Boolean) get(key);
- return value != null ? value.booleanValue() : false;
+ Object value = get(key);
+ if (value instanceof Boolean)
+ return ((Boolean) value).booleanValue();
+ return false;
}
/**
- * Returns a boolean value from the defaults table,
- * <code>false</code> if key is not present.
+ * Returns a boolean value from the defaults table. If there is no value
+ * for the specified key, or the value is not an instance of {@link Boolean},
+ * this method returns <code>false</code>.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ * @param locale the locale.
*
+ * @return The boolean value associated with the specified key.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
+ *
* @since 1.4
*/
public static boolean getBoolean(Object key, Locale locale)
{
- Boolean value = (Boolean) get(key, locale);
- return value != null ? value.booleanValue() : false;
+ Object value = get(key, locale);
+ if (value instanceof Boolean)
+ return ((Boolean) value).booleanValue();
+ return false;
}
/**
* Returns a border from the defaults table.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ *
+ * @return The border associated with the given key, or <code>null</code>.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
*/
public static Border getBorder(Object key)
{
- return (Border) get(key);
+ Object value = get(key);
+ if (value instanceof Border)
+ return (Border) value;
+ return null;
}
/**
- * Returns a border from the defaults table.
+ * Returns a border from the defaults table.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ * @param locale the locale.
+ *
+ * @return The border associated with the given key, or <code>null</code>.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
*
* @since 1.4
*/
public static Border getBorder(Object key, Locale locale)
{
- return (Border) get(key, locale);
+ Object value = get(key, locale);
+ if (value instanceof Border)
+ return (Border) value;
+ return null;
}
/**
* Returns a drawing color from the defaults table.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ *
+ * @return The color associated with the given key, or <code>null</code>.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
*/
public static Color getColor(Object key)
{
- return (Color) get(key);
+ Object value = get(key);
+ if (value instanceof Color)
+ return (Color) value;
+ return null;
}
/**
* Returns a drawing color from the defaults table.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ * @param locale the locale.
+ *
+ * @return The color associated with the given key, or <code>null</code>.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
+ *
+ * @since 1.4
*/
public static Color getColor(Object key, Locale locale)
{
- return (Color) get(key);
+ Object value = get(key, locale);
+ if (value instanceof Color)
+ return (Color) value;
+ return null;
}
/**
@@ -483,24 +552,44 @@ public class UIManager implements Serializable
public static UIDefaults getDefaults()
{
if (currentUIDefaults == null)
- currentUIDefaults = new MultiplexUIDefaults(null);
+ currentUIDefaults = new MultiplexUIDefaults(new UIDefaults());
return currentUIDefaults;
}
/**
* Returns a dimension from the defaults table.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ *
+ * @return The color associated with the given key, or <code>null</code>.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
*/
public static Dimension getDimension(Object key)
{
- return (Dimension) get(key);
+ Object value = get(key);
+ if (value instanceof Dimension)
+ return (Dimension) value;
+ return null;
}
/**
* Returns a dimension from the defaults table.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ * @param locale the locale.
+ *
+ * @return The color associated with the given key, or <code>null</code>.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
+ * @since 1.4
*/
public static Dimension getDimension(Object key, Locale locale)
{
- return (Dimension) get(key, locale);
+ Object value = get(key, locale);
+ if (value instanceof Dimension)
+ return (Dimension) value;
+ return null;
}
/**
@@ -510,10 +599,17 @@ public class UIManager implements Serializable
* @param key an Object that specifies the font. Typically,
* this is a String such as
* <code>TitledBorder.font</code>.
+ *
+ * @return The font associated with the given key, or <code>null</code>.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
*/
public static Font getFont(Object key)
{
- return (Font) get(key);
+ Object value = get(key);
+ if (value instanceof Font)
+ return (Font) value;
+ return null;
}
/**
@@ -523,30 +619,66 @@ public class UIManager implements Serializable
* @param key an Object that specifies the font. Typically,
* this is a String such as
* <code>TitledBorder.font</code>.
+ * @param locale the locale.
+ *
+ * @return The font associated with the given key, or <code>null</code>.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
+ *
+ * @since 1.4
*/
public static Font getFont(Object key, Locale locale)
{
- return (Font) get(key, locale);
+ Object value = get(key, locale);
+ if (value instanceof Font)
+ return (Font) value;
+ return null;
}
/**
- * Returns an Icon from the defaults table.
+ * Returns an icon from the defaults table.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ *
+ * @return The icon associated with the given key, or <code>null</code>.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
*/
public static Icon getIcon(Object key)
{
- return (Icon) get(key);
+ Object value = get(key);
+ if (value instanceof Icon)
+ return (Icon) value;
+ return null;
}
/**
- * Returns an Icon from the defaults table.
+ * Returns an icon from the defaults table.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ * @param locale the locale.
+ *
+ * @return The icon associated with the given key, or <code>null</code>.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
+ * @since 1.4
*/
public static Icon getIcon(Object key, Locale locale)
{
- return (Icon) get(key, locale);
+ Object value = get(key, locale);
+ if (value instanceof Icon)
+ return (Icon) value;
+ return null;
}
/**
* Returns an Insets object from the defaults table.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ *
+ * @return The insets associated with the given key, or <code>null</code>.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
*/
public static Insets getInsets(Object key)
{
@@ -559,6 +691,14 @@ public class UIManager implements Serializable
/**
* Returns an Insets object from the defaults table.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ * @param locale the locale.
+ *
+ * @return The insets associated with the given key, or <code>null</code>.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
+ * @since 1.4
*/
public static Insets getInsets(Object key, Locale locale)
{
@@ -580,20 +720,41 @@ public class UIManager implements Serializable
return installed;
}
+ /**
+ * Returns the integer value of the {@link Integer} associated with the
+ * given key. If there is no value, or the value is not an instance of
+ * {@link Integer}, this method returns 0.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ *
+ * @return The integer value associated with the given key, or 0.
+ */
public static int getInt(Object key)
{
- Integer x = (Integer) get(key);
- if (x == null)
- return 0;
- return x.intValue();
+ Object x = get(key);
+ if (x instanceof Integer)
+ return ((Integer) x).intValue();
+ return 0;
}
+ /**
+ * Returns the integer value of the {@link Integer} associated with the
+ * given key. If there is no value, or the value is not an instance of
+ * {@link Integer}, this method returns 0.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ * @param locale the locale.
+ *
+ * @return The integer value associated with the given key, or 0.
+ *
+ * @since 1.4
+ */
public static int getInt(Object key, Locale locale)
{
- Integer x = (Integer) get(key, locale);
- if (x == null)
- return 0;
- return x.intValue();
+ Object x = get(key, locale);
+ if (x instanceof Integer)
+ return ((Integer) x).intValue();
+ return 0;
}
/**
@@ -620,19 +781,38 @@ public class UIManager implements Serializable
}
/**
- * Returns a string from the defaults table.
+ * Returns the {@link String} associated with the given key. If the value
+ * is not a {@link String}, this method returns <code>null</code>.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ *
+ * @return The string associated with the given key, or <code>null</code>.
*/
public static String getString(Object key)
{
- return (String) get(key);
+ Object s = get(key);
+ if (s instanceof String)
+ return (String) s;
+ return null;
}
/**
- * Returns a string from the defaults table.
+ * Returns the {@link String} associated with the given key. If the value
+ * is not a {@link String}, this method returns <code>null</code>.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ * @param locale the locale.
+ *
+ * @return The string associated with the given key, or <code>null</code>.
+ *
+ * @since 1.4
*/
public static String getString(Object key, Locale locale)
{
- return (String) get(key, locale);
+ Object s = get(key, locale);
+ if (s instanceof String)
+ return (String) s;
+ return null;
}
/**
@@ -686,6 +866,9 @@ public class UIManager implements Serializable
/**
* Stores an object in the defaults table.
+ *
+ * @param key the key.
+ * @param value the value.
*/
public static Object put(Object key, Object value)
{
diff --git a/libjava/classpath/javax/swing/border/CompoundBorder.java b/libjava/classpath/javax/swing/border/CompoundBorder.java
index 2ee639cf9a3..ba2e745aab5 100644
--- a/libjava/classpath/javax/swing/border/CompoundBorder.java
+++ b/libjava/classpath/javax/swing/border/CompoundBorder.java
@@ -115,15 +115,24 @@ public class CompoundBorder extends AbstractBorder
*/
public boolean isBorderOpaque()
{
- // While it would be safe to assume true for the opacity of
- // a null border, this behavior would not be according to
- // the API specification. Also, it is pathological to have
- // null borders anyway.
- if ((insideBorder == null) || (outsideBorder == null))
- return false;
-
- return insideBorder.isBorderOpaque()
- && outsideBorder.isBorderOpaque();
+ // Although the API specification states that this method
+ // returns true if both the inside and outside borders are non-null
+ // and opaque, and false otherwise, a mauve test shows that if both
+ // the inside or outside borders are null, then true is returned.
+ if ((insideBorder == null) && (outsideBorder == null))
+ return true;
+
+ // A mauve test shows that if the inside border has a null value,
+ // then true is returned if the outside border is opaque; if the
+ // outside border has a null value, then true is returned if the
+ // inside border is opaque; else, true is returned if both the
+ // inside and outside borders are opaque.
+ if (insideBorder == null)
+ return outsideBorder.isBorderOpaque();
+ else if (outsideBorder == null)
+ return insideBorder.isBorderOpaque();
+ else
+ return insideBorder.isBorderOpaque() && outsideBorder.isBorderOpaque();
}
/**
diff --git a/libjava/classpath/javax/swing/event/EventListenerList.java b/libjava/classpath/javax/swing/event/EventListenerList.java
index bde8b3c7e4f..1568039f0ff 100644
--- a/libjava/classpath/javax/swing/event/EventListenerList.java
+++ b/libjava/classpath/javax/swing/event/EventListenerList.java
@@ -37,6 +37,9 @@ exception statement from your version. */
package javax.swing.event;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.EventListener;
@@ -136,7 +139,7 @@ public class EventListenerList
*
* @throws NullPointerException if <code>t</code> is <code>null</code>.
*/
- public void add(Class t, EventListener listener)
+ public <T extends EventListener> void add(Class<T> t, T listener)
{
int oldLength;
Object[] newList;
@@ -175,7 +178,7 @@ public class EventListenerList
* <code>t</code>. Thus, subclasses of <code>t</code> will not be
* counted.
*/
- public int getListenerCount(Class t)
+ public int getListenerCount(Class<?> t)
{
int result = 0;
for (int i = 0; i < listenerList.length; i += 2)
@@ -224,7 +227,7 @@ public class EventListenerList
*
* @since 1.3
*/
- public EventListener[] getListeners(Class c)
+ public <T extends EventListener> T[] getListeners(Class<T> c)
{
int count, f;
EventListener[] result;
@@ -236,7 +239,7 @@ public class EventListenerList
if (listenerList[i] == c)
result[f++] = (EventListener) listenerList[i + 1];
- return result;
+ return (T[]) result;
}
@@ -253,7 +256,7 @@ public class EventListenerList
*
* @throws NullPointerException if <code>t</code> is <code>null</code>.
*/
- public void remove(Class t, EventListener listener)
+ public <T extends EventListener> void remove(Class<T> t, T listener)
{
Object[] oldList, newList;
int oldLength;
@@ -304,4 +307,51 @@ public class EventListenerList
}
return buf.toString();
}
+
+ /**
+ * Serializes an instance to an ObjectOutputStream.
+ *
+ * @param out the stream to serialize to
+ *
+ * @throws IOException if something goes wrong
+ */
+ private void writeObject(ObjectOutputStream out)
+ throws IOException
+ {
+ out.defaultWriteObject();
+ for (int i = 0; i < listenerList.length; i += 2)
+ {
+ Class cl = (Class) listenerList[i];
+ EventListener l = (EventListener) listenerList[i + 1];
+ if (l != null && l instanceof Serializable)
+ {
+ out.writeObject(cl.getName());
+ out.writeObject(l);
+ }
+ }
+ // Write end marker.
+ out.writeObject(null);
+ }
+
+ /**
+ * Deserializes an instance from an ObjectInputStream.
+ *
+ * @param in the input stream
+ *
+ * @throws ClassNotFoundException if a serialized class can't be found
+ * @throws IOException if something goes wrong
+ */
+ private <T extends EventListener> void readObject(ObjectInputStream in)
+ throws ClassNotFoundException, IOException
+ {
+ listenerList = NO_LISTENERS;
+ in.defaultReadObject();
+ Object type;
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ while ((type = in.readObject()) != null)
+ {
+ EventListener l = (EventListener) in.readObject();
+ add(((Class<T>) Class.forName((String) type, true, cl)), (T) l);
+ }
+ }
}
diff --git a/libjava/classpath/javax/swing/filechooser/FileSystemView.java b/libjava/classpath/javax/swing/filechooser/FileSystemView.java
index 84b80dd402c..41d865a964b 100644
--- a/libjava/classpath/javax/swing/filechooser/FileSystemView.java
+++ b/libjava/classpath/javax/swing/filechooser/FileSystemView.java
@@ -172,13 +172,8 @@ public abstract class FileSystemView
{
if (defaultFileSystemView == null)
{
- if (File.separator.equals("/"))
- defaultFileSystemView = new UnixFileSystemView();
- // FIXME: need to implement additional views
- // else if (File.Separator.equals("\"))
- // return new Win32FileSystemView();
- // else
- // return new GenericFileSystemView();
+ // FIXME: We need to support other file systems too.
+ defaultFileSystemView = new UnixFileSystemView();
}
return defaultFileSystemView;
}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java b/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java
index 84895821518..c99de2c708c 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java
@@ -54,15 +54,79 @@ import java.beans.PropertyChangeListener;
import javax.swing.AbstractAction;
import javax.swing.AbstractButton;
+import javax.swing.Action;
+import javax.swing.ActionMap;
import javax.swing.ButtonModel;
+import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ButtonUI;
-public class BasicButtonListener implements MouseListener, MouseMotionListener,
- FocusListener, ChangeListener, PropertyChangeListener
+public class BasicButtonListener
+ implements MouseListener, MouseMotionListener, FocusListener, ChangeListener,
+ PropertyChangeListener
{
+ /**
+ * Implements the keyboard action for Swing buttons.
+ */
+ private class ButtonAction
+ extends AbstractAction
+ {
+ /**
+ * The key for pressed action.
+ */
+ static final String PRESSED = "pressed";
+
+ /**
+ * The key for released action.
+ */
+ static final String RELEASED = "released";
+
+ /**
+ * Performs the action.
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ Object cmd = getValue("__command__");
+ AbstractButton b = (AbstractButton) event.getSource();
+ ButtonModel m = b.getModel();
+ if (PRESSED.equals(cmd))
+ {
+ m.setArmed(true);
+ m.setPressed(true);
+ if (! b.isFocusOwner())
+ b.requestFocus();
+ }
+ else if (RELEASED.equals(cmd))
+ {
+ m.setPressed(false);
+ m.setArmed(false);
+ }
+ }
+
+ /**
+ * Indicates if this action is enabled.
+ *
+ * @param source the source of the action
+ *
+ * @return <code>true</code> when enabled, <code>false</code> otherwise
+ */
+ public boolean isEnabled(Object source)
+ {
+ boolean enabled = true;
+ if (source instanceof AbstractButton)
+ {
+ AbstractButton b = (AbstractButton) source;
+ enabled = b.isEnabled();
+ }
+ return enabled;
+ }
+ }
+
public BasicButtonListener(AbstractButton b)
{
// Do nothing here.
@@ -73,12 +137,12 @@ public class BasicButtonListener implements MouseListener, MouseMotionListener,
// Store the TextLayout for this in a client property for speed-up
// painting of the label.
String property = e.getPropertyName();
+ AbstractButton b = (AbstractButton) e.getSource();
if ((property.equals(AbstractButton.TEXT_CHANGED_PROPERTY)
|| property.equals("font"))
&& SystemProperties.getProperty("gnu.javax.swing.noGraphics2D")
== null)
{
- AbstractButton b = (AbstractButton) e.getSource();
String text = b.getText();
if (text == null)
text = "";
@@ -86,12 +150,25 @@ public class BasicButtonListener implements MouseListener, MouseMotionListener,
false, false);
TextLayout layout = new TextLayout(text, b.getFont(), frc);
b.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, layout);
+
+ // Update HTML renderer.
+ BasicHTML.updateRenderer(b, b.getText());
+ }
+ else if (property.equals(AbstractButton.CONTENT_AREA_FILLED_CHANGED_PROPERTY))
+ {
+ checkOpacity(b);
}
}
-
+
+ /**
+ * Checks the <code>contentAreaFilled</code> property and updates the
+ * opaque property of the button.
+ *
+ * @param b the button to check
+ */
protected void checkOpacity(AbstractButton b)
{
- // TODO: What should be done here?
+ b.setOpaque(b.isContentAreaFilled());
}
public void focusGained(FocusEvent e)
@@ -116,6 +193,26 @@ public class BasicButtonListener implements MouseListener, MouseMotionListener,
public void installKeyboardActions(JComponent c)
{
+ ButtonUI ui = ((AbstractButton) c).getUI();
+ if (ui instanceof BasicButtonUI)
+ {
+ // Install InputMap.
+ BasicButtonUI basicUI = (BasicButtonUI) ui;
+ String prefix = basicUI.getPropertyPrefix();
+ InputMap focusInputMap =
+ (InputMap) UIManager.get(prefix + "focusInputMap");
+ SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_FOCUSED,
+ focusInputMap);
+
+ ActionMap am = (ActionMap) UIManager.get(prefix + "actionMap");
+ if (am == null)
+ {
+ am = createDefaultActionMap();
+ UIManager.put(prefix + "actionMap", am);
+ }
+ SwingUtilities.replaceUIActionMap(c, am);
+ }
+
c.getActionMap().put("pressed",
new AbstractAction()
{
@@ -142,31 +239,46 @@ public class BasicButtonListener implements MouseListener, MouseMotionListener,
}
});
}
-
+
+ /**
+ * Creates and returns the default action map for Swing buttons.
+ *
+ * @return the default action map for Swing buttons
+ */
+ private ActionMap createDefaultActionMap()
+ {
+ Action action = new ButtonAction();
+ ActionMapUIResource am = new ActionMapUIResource();
+ am.put(ButtonAction.PRESSED, action);
+ am.put(ButtonAction.RELEASED, action);
+ return am;
+ }
+
public void uninstallKeyboardActions(JComponent c)
{
- c.getActionMap().put("pressed", null);
- c.getActionMap().put("released", null);
+ SwingUtilities.replaceUIActionMap(c, null);
+ SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_FOCUSED, null);
}
public void stateChanged(ChangeEvent e)
{
- // TODO: What should be done here, if anything?
+ // Need to repaint when the button state changes.
+ ((AbstractButton) e.getSource()).repaint();
}
public void mouseMoved(MouseEvent e)
{
- // TODO: What should be done here, if anything?
+ // Nothing to do here.
}
public void mouseDragged(MouseEvent e)
{
- // TODO: What should be done here, if anything?
+ // Nothing to do here.
}
public void mouseClicked(MouseEvent e)
{
- // TODO: What should be done here, if anything?
+ // Nothing to do here.
}
/**
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java
index d531133ba26..9f685bb7bfd 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java
@@ -42,12 +42,14 @@ import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
+import java.awt.Insets;
import java.awt.Rectangle;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
import javax.swing.AbstractButton;
import javax.swing.ButtonModel;
import javax.swing.Icon;
-import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.LookAndFeel;
@@ -56,6 +58,7 @@ import javax.swing.UIManager;
import javax.swing.plaf.ButtonUI;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
+import javax.swing.text.View;
/**
* A UI delegate for the {@link JButton} component.
@@ -63,6 +66,39 @@ import javax.swing.plaf.UIResource;
public class BasicButtonUI extends ButtonUI
{
/**
+ * Cached rectangle for layouting the label. Used in paint() and
+ * BasicGraphicsUtils.getPreferredButtonSize().
+ */
+ static Rectangle viewR = new Rectangle();
+
+ /**
+ * Cached rectangle for layouting the label. Used in paint() and
+ * BasicGraphicsUtils.getPreferredButtonSize().
+ */
+ static Rectangle iconR = new Rectangle();
+
+ /**
+ * Cached rectangle for layouting the label. Used in paint() and
+ * BasicGraphicsUtils.getPreferredButtonSize().
+ */
+ static Rectangle textR = new Rectangle();
+
+ /**
+ * Cached Insets instance, used in paint().
+ */
+ static Insets cachedInsets;
+
+ /**
+ * The shared button UI.
+ */
+ private static BasicButtonUI sharedUI;
+
+ /**
+ * The shared BasicButtonListener.
+ */
+ private static BasicButtonListener sharedListener;
+
+ /**
* A constant used to pad out elements in the button's layout and
* preferred size calculations.
*/
@@ -86,7 +122,9 @@ public class BasicButtonUI extends ButtonUI
*/
public static ComponentUI createUI(final JComponent c)
{
- return new BasicButtonUI();
+ if (sharedUI == null)
+ sharedUI = new BasicButtonUI();
+ return sharedUI;
}
/**
@@ -153,14 +191,29 @@ public class BasicButtonUI extends ButtonUI
protected void installDefaults(AbstractButton b)
{
String prefix = getPropertyPrefix();
+ // Install colors and font.
LookAndFeel.installColorsAndFont(b, prefix + "background",
prefix + "foreground", prefix + "font");
+ // Install border.
LookAndFeel.installBorder(b, prefix + "border");
+
+ // Install margin property.
if (b.getMargin() == null || b.getMargin() instanceof UIResource)
b.setMargin(UIManager.getInsets(prefix + "margin"));
- b.setIconTextGap(UIManager.getInt(prefix + "textIconGap"));
- b.setInputMap(JComponent.WHEN_FOCUSED,
- (InputMap) UIManager.get(prefix + "focusInputMap"));
+
+ // Install rollover property.
+ Object rollover = UIManager.get(prefix + "rollover");
+ if (rollover != null)
+ LookAndFeel.installProperty(b, "rolloverEnabled", rollover);
+
+ // Fetch default textShiftOffset.
+ defaultTextShiftOffset = UIManager.getInt(prefix + "textShiftOffset");
+
+ // Make button opaque if needed.
+ if (b.isContentAreaFilled())
+ LookAndFeel.installProperty(b, "opaque", Boolean.TRUE);
+ else
+ LookAndFeel.installProperty(b, "opaque", Boolean.FALSE);
}
/**
@@ -170,21 +223,10 @@ public class BasicButtonUI extends ButtonUI
*/
protected void uninstallDefaults(AbstractButton b)
{
- if (b.getFont() instanceof UIResource)
- b.setFont(null);
- if (b.getForeground() instanceof UIResource)
- b.setForeground(null);
- if (b.getBackground() instanceof UIResource)
- b.setBackground(null);
- if (b.getBorder() instanceof UIResource)
- b.setBorder(null);
- b.setIconTextGap(defaultTextIconGap);
- if (b.getMargin() instanceof UIResource)
- b.setMargin(null);
+ // The other properties aren't uninstallable.
+ LookAndFeel.uninstallBorder(b);
}
- protected BasicButtonListener listener;
-
/**
* Creates and returns a new instance of {@link BasicButtonListener}. This
* method provides a hook to make it easy for subclasses to install a
@@ -196,7 +238,13 @@ public class BasicButtonUI extends ButtonUI
*/
protected BasicButtonListener createButtonListener(AbstractButton b)
{
- return new BasicButtonListener(b);
+ // Note: The RI always returns a new instance here. However,
+ // the BasicButtonListener class is perfectly suitable to be shared
+ // between multiple buttons, so we return a shared instance here
+ // for efficiency.
+ if (sharedListener == null)
+ sharedListener = new BasicButtonListener(b);
+ return sharedListener;
}
/**
@@ -206,12 +254,19 @@ public class BasicButtonUI extends ButtonUI
*/
protected void installListeners(AbstractButton b)
{
- listener = createButtonListener(b);
- b.addChangeListener(listener);
- b.addPropertyChangeListener(listener);
- b.addFocusListener(listener);
- b.addMouseListener(listener);
- b.addMouseMotionListener(listener);
+ BasicButtonListener listener = createButtonListener(b);
+ if (listener != null)
+ {
+ b.addChangeListener(listener);
+ b.addPropertyChangeListener(listener);
+ b.addFocusListener(listener);
+ b.addMouseListener(listener);
+ b.addMouseMotionListener(listener);
+ }
+ // Fire synthetic property change event to let the listener update
+ // the TextLayout cache.
+ listener.propertyChange(new PropertyChangeEvent(b, "font", null,
+ b.getFont()));
}
/**
@@ -221,21 +276,29 @@ public class BasicButtonUI extends ButtonUI
*/
protected void uninstallListeners(AbstractButton b)
{
- b.removeChangeListener(listener);
- b.removePropertyChangeListener(listener);
- b.removeFocusListener(listener);
- b.removeMouseListener(listener);
- b.removeMouseMotionListener(listener);
+ BasicButtonListener listener = getButtonListener(b);
+ if (listener != null)
+ {
+ b.removeChangeListener(listener);
+ b.removePropertyChangeListener(listener);
+ b.removeFocusListener(listener);
+ b.removeMouseListener(listener);
+ b.removeMouseMotionListener(listener);
+ }
}
protected void installKeyboardActions(AbstractButton b)
{
- listener.installKeyboardActions(b);
+ BasicButtonListener listener = getButtonListener(b);
+ if (listener != null)
+ listener.installKeyboardActions(b);
}
protected void uninstallKeyboardActions(AbstractButton b)
{
- listener.uninstallKeyboardActions(b);
+ BasicButtonListener listener = getButtonListener(b);
+ if (listener != null)
+ listener.uninstallKeyboardActions(b);
}
/**
@@ -253,9 +316,75 @@ public class BasicButtonUI extends ButtonUI
{
AbstractButton b = (AbstractButton) c;
installDefaults(b);
+ // It is important to install the listeners before installing
+ // the keyboard actions, because the keyboard actions
+ // are actually installed on the listener instance.
installListeners(b);
installKeyboardActions(b);
+ BasicHTML.updateRenderer(b, b.getText());
+ }
+ }
+
+ /**
+ * Uninstalls the UI from the component.
+ *
+ * @param c the component from which to uninstall the UI
+ */
+ public void uninstallUI(JComponent c)
+ {
+ if (c instanceof AbstractButton)
+ {
+ AbstractButton b = (AbstractButton) c;
+ uninstallKeyboardActions(b);
+ uninstallListeners(b);
+ uninstallDefaults(b);
+ BasicHTML.updateRenderer(b, "");
+ b.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, null);
+ }
+ }
+
+ /**
+ * Calculates the minimum size for the specified component.
+ *
+ * @param c the component for which to compute the minimum size
+ *
+ * @return the minimum size for the specified component
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension size = getPreferredSize(c);
+ // When the HTML view has a minimum width different from the preferred
+ // width, then substract this here accordingly. The height is not
+ // affected by that.
+ View html = (View) c.getClientProperty(BasicHTML.propertyKey);
+ if (html != null)
+ {
+ size.width -= html.getPreferredSpan(View.X_AXIS)
+ - html.getPreferredSpan(View.X_AXIS);
}
+ return size;
+ }
+
+ /**
+ * Calculates the maximum size for the specified component.
+ *
+ * @param c the component for which to compute the maximum size
+ *
+ * @return the maximum size for the specified component
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension size = getPreferredSize(c);
+ // When the HTML view has a maximum width different from the preferred
+ // width, then add this here accordingly. The height is not
+ // affected by that.
+ View html = (View) c.getClientProperty(BasicHTML.propertyKey);
+ if (html != null)
+ {
+ size.width += html.getMaximumSpan(View.X_AXIS)
+ - html.getPreferredSpan(View.X_AXIS);
+ }
+ return size;
}
/**
@@ -269,8 +398,8 @@ public class BasicButtonUI extends ButtonUI
public Dimension getPreferredSize(JComponent c)
{
AbstractButton b = (AbstractButton) c;
- Dimension d = BasicGraphicsUtils.getPreferredButtonSize(b,
- defaultTextIconGap + defaultTextShiftOffset);
+ Dimension d = BasicGraphicsUtils.getPreferredButtonSize(b,
+ b.getIconTextGap());
return d;
}
@@ -315,38 +444,50 @@ public class BasicButtonUI extends ButtonUI
{
AbstractButton b = (AbstractButton) c;
- Rectangle tr = new Rectangle();
- Rectangle ir = new Rectangle();
- Rectangle vr = new Rectangle();
+ Insets i = c.getInsets(cachedInsets);
+ viewR.x = i.left;
+ viewR.y = i.top;
+ viewR.width = c.getWidth() - i.left - i.right;
+ viewR.height = c.getHeight() - i.top - i.bottom;
+ textR.x = 0;
+ textR.y = 0;
+ textR.width = 0;
+ textR.height = 0;
+ iconR.x = 0;
+ iconR.y = 0;
+ iconR.width = 0;
+ iconR.height = 0;
Font f = c.getFont();
-
g.setFont(f);
+ Icon icon = b.getIcon();
+ String text = b.getText();
+ text = SwingUtilities.layoutCompoundLabel(c, g.getFontMetrics(f),
+ text, icon,
+ b.getVerticalAlignment(),
+ b.getHorizontalAlignment(),
+ b.getVerticalTextPosition(),
+ b.getHorizontalTextPosition(),
+ viewR, iconR, textR,
+ text == null ? 0
+ : b.getIconTextGap());
- if (b.isBorderPainted())
- SwingUtilities.calculateInnerArea(b, vr);
- else
- vr = SwingUtilities.getLocalBounds(b);
- String text = SwingUtilities.layoutCompoundLabel(c, g.getFontMetrics(f),
- b.getText(),
- currentIcon(b),
- b.getVerticalAlignment(),
- b.getHorizontalAlignment(),
- b.getVerticalTextPosition(),
- b.getHorizontalTextPosition(),
- vr, ir, tr,
- b.getIconTextGap()
- + defaultTextShiftOffset);
-
- if ((b.getModel().isArmed() && b.getModel().isPressed())
- || b.isSelected())
+ ButtonModel model = b.getModel();
+ if (model.isArmed() && model.isPressed())
paintButtonPressed(g, b);
-
- paintIcon(g, c, ir);
+
+ if (icon != null)
+ paintIcon(g, c, iconR);
if (text != null)
- paintText(g, b, tr, text);
+ {
+ View html = (View) b.getClientProperty(BasicHTML.propertyKey);
+ if (html != null)
+ html.paint(g, textR);
+ else
+ paintText(g, b, textR, text);
+ }
if (b.isFocusOwner() && b.isFocusPainted())
- paintFocus(g, b, vr, tr, ir);
+ paintFocus(g, b, viewR, textR, iconR);
}
/**
@@ -386,7 +527,16 @@ public class BasicButtonUI extends ButtonUI
Icon i = currentIcon(b);
if (i != null)
- i.paintIcon(c, g, iconRect.x, iconRect.y);
+ {
+ ButtonModel m = b.getModel();
+ if (m.isPressed() && m.isArmed())
+ {
+ int offs = getTextShiftOffset();
+ i.paintIcon(c, g, iconRect.x + offs, iconRect.y + offs);
+ }
+ else
+ i.paintIcon(c, g, iconRect.x, iconRect.y);
+ }
}
/**
@@ -419,22 +569,7 @@ public class BasicButtonUI extends ButtonUI
protected void paintText(Graphics g, JComponent c, Rectangle textRect,
String text)
{
- paintText(g, (AbstractButton) c, textRect, text);
- }
-
- /**
- * Paints the "text" property of an {@link AbstractButton}.
- *
- * @param g The graphics context to paint with
- * @param b The button to paint the state of
- * @param textRect The area in which to paint the text
- * @param text The text to paint
- *
- * @since 1.4
- */
- protected void paintText(Graphics g, AbstractButton b, Rectangle textRect,
- String text)
- {
+ AbstractButton b = (AbstractButton) c;
Font f = b.getFont();
g.setFont(f);
FontMetrics fm = g.getFontMetrics(f);
@@ -454,5 +589,48 @@ public class BasicButtonUI extends ButtonUI
BasicGraphicsUtils.drawString(b, g, text, -1, textRect.x,
textRect.y + fm.getAscent());
}
+ }
+
+ /**
+ * Paints the "text" property of an {@link AbstractButton}.
+ *
+ * @param g The graphics context to paint with
+ * @param b The button to paint the state of
+ * @param textRect The area in which to paint the text
+ * @param text The text to paint
+ *
+ * @since 1.4
+ */
+ protected void paintText(Graphics g, AbstractButton b, Rectangle textRect,
+ String text)
+ {
+ paintText(g, (JComponent) b, textRect, text);
}
+
+ /**
+ * A helper method that finds the BasicButtonListener for the specified
+ * button. This is there because this UI class is stateless and
+ * shared for all buttons, and thus can't store the listener
+ * as instance field. (We store our shared instance in sharedListener,
+ * however, subclasses may override createButtonListener() and we would
+ * be lost in this case).
+ *
+ * @param b the button
+ *
+ * @return the UI event listener
+ */
+ private BasicButtonListener getButtonListener(AbstractButton b)
+ {
+ // The listener gets installed as PropertyChangeListener,
+ // so look for it in the list of property change listeners.
+ PropertyChangeListener[] listeners = b.getPropertyChangeListeners();
+ BasicButtonListener l = null;
+ for (int i = 0; listeners != null && l == null && i < listeners.length;
+ i++)
+ {
+ if (listeners[i] instanceof BasicButtonListener)
+ l = (BasicButtonListener) listeners[i];
+ }
+ return l;
+ }
}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicDirectoryModel.java b/libjava/classpath/javax/swing/plaf/basic/BasicDirectoryModel.java
index ed916cb5f1a..de82bd47bb6 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicDirectoryModel.java
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicDirectoryModel.java
@@ -381,7 +381,7 @@ public class BasicDirectoryModel extends AbstractListModel
*
* @return a Vector
*/
- public Vector getDirectories()
+ public Vector<File> getDirectories()
{
// Synchronize this with the UpdateSwingRequest for the case when
// contents is modified.
@@ -418,7 +418,7 @@ public class BasicDirectoryModel extends AbstractListModel
*
* @return a Vector
*/
- public Vector getFiles()
+ public Vector<File> getFiles()
{
synchronized (contents)
{
@@ -562,7 +562,7 @@ public class BasicDirectoryModel extends AbstractListModel
*
* @param v The Vector to sort.
*/
- protected void sort(Vector v)
+ protected void sort(Vector<? extends File> v)
{
Collections.sort(v, comparator);
}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java
index dc1c051225c..e1f8e4b28ba 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java
@@ -183,7 +183,7 @@ public class BasicFileChooserUI extends FileChooserUI
protected class BasicFileView extends FileView
{
/** Storage for cached icons. */
- protected Hashtable iconCache = new Hashtable();
+ protected Hashtable<File, Icon> iconCache = new Hashtable<File, Icon>();
/**
* Creates a new instance.
@@ -444,10 +444,10 @@ public class BasicFileChooserUI extends FileChooserUI
setDirectory(null);
}
lastSelected = path;
- parentPath = path.substring(0, path.lastIndexOf("/") + 1);
+ parentPath = f.getParent();
if (f.isFile())
- setFileName(path.substring(path.lastIndexOf("/") + 1));
+ setFileName(f.getName());
else if (filechooser.getFileSelectionMode() !=
JFileChooser.FILES_ONLY)
setFileName(path);
@@ -827,9 +827,9 @@ public class BasicFileChooserUI extends FileChooserUI
installComponents(fc);
installListeners(fc);
- Object path = filechooser.getCurrentDirectory();
+ File path = filechooser.getCurrentDirectory();
if (path != null)
- parentPath = path.toString().substring(path.toString().lastIndexOf("/"));
+ parentPath = path.getParent();
}
}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java b/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java
index 1e84be93282..4c270682d88 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java
@@ -748,7 +748,6 @@ public class BasicGraphicsUtils
}
}
-
/**
* Determines the preferred width and height of an AbstractButton,
* given the gap between the button&#x2019;s text and icon.
@@ -769,24 +768,31 @@ public class BasicGraphicsUtils
public static Dimension getPreferredButtonSize(AbstractButton b,
int textIconGap)
{
- Rectangle contentRect;
- Rectangle viewRect;
- Rectangle iconRect = new Rectangle();
- Rectangle textRect = new Rectangle();
- Insets insets = b.getInsets();
-
- viewRect = new Rectangle();
-
- /* java.awt.Toolkit.getFontMetrics is deprecated. However, it
- * seems not obvious how to get to the correct FontMetrics object
- * otherwise. The real problem probably is that the method
- * javax.swing.SwingUtilities.layoutCompundLabel should take a
- * LineMetrics, not a FontMetrics argument. But fixing this that
- * would change the public API.
- */
+ // These cached rectangles are use here and in BasicButtonUI.paint(),
+ // so these two methods must never be executed concurrently. Maybe
+ // we must use other Rectangle instances here. OTOH, Swing is
+ // designed to be not thread safe, and every layout and paint operation
+ // should be performed from the EventDispatchThread, so it _should_ be
+ // OK to do this optimization.
+ Rectangle viewRect = BasicButtonUI.viewR;
+ viewRect.x = 0;
+ viewRect.y = 0;
+ viewRect.width = Short.MAX_VALUE;
+ viewRect.height = Short.MAX_VALUE;
+ Rectangle iconRect = BasicButtonUI.iconR;
+ iconRect.x = 0;
+ iconRect.y = 0;
+ iconRect.width = 0;
+ iconRect.height = 0;
+ Rectangle textRect = BasicButtonUI.textR;
+ textRect.x = 0;
+ textRect.y = 0;
+ textRect.width = 0;
+ textRect.height = 0;
+
SwingUtilities.layoutCompoundLabel(
b, // for the component orientation
- b.getToolkit().getFontMetrics(b.getFont()), // see comment above
+ b.getFontMetrics(b.getFont()), // see comment above
b.getText(),
b.getIcon(),
b.getVerticalAlignment(),
@@ -804,13 +810,12 @@ public class BasicGraphicsUtils
* +------------------------+ +------------------------+
*/
- contentRect = textRect.union(iconRect);
-
- return new Dimension(insets.left
- + contentRect.width
- + insets.right + b.getHorizontalAlignment(),
- insets.top
- + contentRect.height
- + insets.bottom);
+ Rectangle contentRect =
+ SwingUtilities.computeUnion(textRect.x, textRect.y, textRect.width,
+ textRect.height, iconRect);
+
+ Insets insets = b.getInsets();
+ return new Dimension(insets.left + contentRect.width + insets.right,
+ insets.top + contentRect.height + insets.bottom);
}
}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicHTML.java b/libjava/classpath/javax/swing/plaf/basic/BasicHTML.java
index 98c9cb277f4..6e26d5355a2 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicHTML.java
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicHTML.java
@@ -48,6 +48,7 @@ import java.io.StringReader;
import javax.swing.JComponent;
import javax.swing.SwingConstants;
import javax.swing.event.DocumentEvent;
+import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.EditorKit;
@@ -107,6 +108,7 @@ public class BasicHTML
editorKit = kit;
document = doc;
setView(view);
+ setSize(view.getPreferredSpan(X_AXIS), view.getPreferredSpan(Y_AXIS));
}
/**
@@ -151,6 +153,14 @@ public class BasicHTML
}
/**
+ * Overridden to forward to real view.
+ */
+ public void setSize(float w, float h)
+ {
+ view.setSize(w, h);
+ }
+
+ /**
* Returns the real root view, regardless of the index.
*
* @param index not used here
@@ -346,6 +356,22 @@ public class BasicHTML
{
return document;
}
+
+ /**
+ * Overridden to return null, as a RootView has no attributes on its own.
+ */
+ public AttributeSet getAttributes()
+ {
+ return null;
+ }
+
+ /**
+ * Overridden to provide an element for the view.
+ */
+ public Element getElement()
+ {
+ return view.getElement();
+ }
}
/**
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java
index 11980f6ca2e..ea8b4603691 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java
@@ -177,9 +177,15 @@ public class BasicInternalFrameTitlePane extends JComponent
try
{
if (frame.isMaximizable() && ! frame.isMaximum())
- frame.setMaximum(true);
+ {
+ frame.setMaximum(true);
+ maxButton.setIcon(minIcon);
+ }
else if (frame.isMaximum())
- frame.setMaximum(false);
+ {
+ frame.setMaximum(false);
+ maxButton.setIcon(maxIcon);
+ }
}
catch (PropertyVetoException pve)
{
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java
index 8f2181336cb..87c5268c8c7 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java
@@ -459,18 +459,12 @@ public class BasicInternalFrameUI extends InternalFrameUI
{
if (frame.isMaximum())
{
- JDesktopPane pane = (JDesktopPane) e.getSource();
- Insets insets = pane.getInsets();
- Rectangle bounds = pane.getBounds();
-
- frame.setBounds(bounds.x + insets.left, bounds.y + insets.top,
- bounds.width - insets.left - insets.right,
- bounds.height - insets.top - insets.bottom);
- frame.revalidate();
- frame.repaint();
+ Container parent = frame.getParent();
+ Insets i = parent.getInsets();
+ int width = parent.getWidth() - i.left - i.right;
+ int height = parent.getHeight() - i.top - i.bottom;
+ frame.setBounds(0, 0, width, height);
}
-
- // Sun also resizes the icons. but it doesn't seem to do anything.
}
/**
@@ -949,17 +943,25 @@ public class BasicInternalFrameUI extends InternalFrameUI
{
if (evt.getNewValue() == Boolean.TRUE)
{
+ Container parent = frame.getParent();
+ if (parent != null)
+ parent.removeComponentListener(componentListener);
closeFrame(frame);
}
}
- /*
- * FIXME: need to add ancestor properties to JComponents. else if
- * (evt.getPropertyName().equals(JComponent.ANCESTOR_PROPERTY)) { if
- * (desktopPane != null)
- * desktopPane.removeComponentListener(componentListener); desktopPane =
- * frame.getDesktopPane(); if (desktopPane != null)
- * desktopPane.addComponentListener(componentListener); }
- */
+ else if (property.equals("ancestor"))
+ {
+ Container newParent = (Container) evt.getNewValue();
+ Container oldParent = (Container) evt.getOldValue();
+ if (newParent != null)
+ {
+ newParent.addComponentListener(componentListener);
+ }
+ else if (oldParent != null)
+ {
+ oldParent.removeComponentListener(componentListener);
+ }
+ }
}
}
@@ -1258,6 +1260,12 @@ public class BasicInternalFrameUI extends InternalFrameUI
frame.addPropertyChangeListener(propertyChangeListener);
frame.getRootPane().getGlassPane().addMouseListener(glassPaneDispatcher);
frame.getRootPane().getGlassPane().addMouseMotionListener(glassPaneDispatcher);
+
+ Container parent = frame.getParent();
+ if (parent != null)
+ {
+ parent.addComponentListener(componentListener);
+ }
}
/**
@@ -1286,8 +1294,13 @@ public class BasicInternalFrameUI extends InternalFrameUI
*/
protected void uninstallListeners()
{
- if (desktopPane != null)
- desktopPane.removeComponentListener(componentListener);
+
+ Container parent = frame.getParent();
+ if (parent != null)
+ {
+ parent.removeComponentListener(componentListener);
+ }
+ componentListener = null;
frame.getRootPane().getGlassPane().removeMouseMotionListener(glassPaneDispatcher);
frame.getRootPane().getGlassPane().removeMouseListener(glassPaneDispatcher);
@@ -1298,7 +1311,7 @@ public class BasicInternalFrameUI extends InternalFrameUI
frame.removeMouseListener(borderListener);
propertyChangeListener = null;
- componentListener = null;
+
borderListener = null;
internalFrameListener = null;
glassPaneDispatcher = null;
@@ -1581,6 +1594,13 @@ public class BasicInternalFrameUI extends InternalFrameUI
{
replacePane(northPane, c);
northPane = c;
+ // the following is needed to make internal frames draggable when using
+ // the JGoodies PlasticLookAndFeel, because it overrides the
+ // createNorthPane() method and doesn't assign anything to the titlePane
+ // field. It is possible there is another way to make this work, but
+ // I didn't find it...
+ if (c instanceof BasicInternalFrameTitlePane)
+ titlePane = (BasicInternalFrameTitlePane) c;
}
/**
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java
index 304e13ad735..1ec020b1c0b 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java
@@ -119,13 +119,37 @@ public class BasicLabelUI extends LabelUI implements PropertyChangeListener
{
JLabel lab = (JLabel) c;
Insets insets = lab.getInsets();
- FontMetrics fm = lab.getFontMetrics(lab.getFont());
- layoutCL(lab, fm, lab.getText(), lab.getIcon(), vr, ir, tr);
- Rectangle cr = SwingUtilities.computeUnion(tr.x, tr.y, tr.width, tr.height,
- ir);
- return new Dimension(insets.left + cr.width + insets.right, insets.top
- + cr.height + insets.bottom);
-
+ int insetsX = insets.left + insets.right;
+ int insetsY = insets.top + insets.bottom;
+ Icon icon = lab.getIcon();
+ String text = lab.getText();
+ Dimension ret;
+ if (icon == null && text == null)
+ ret = new Dimension(insetsX, insetsY);
+ else if (icon != null && text == null)
+ ret = new Dimension(icon.getIconWidth() + insetsX,
+ icon.getIconHeight() + insetsY);
+ else
+ {
+ FontMetrics fm = lab.getFontMetrics(lab.getFont());
+ ir.x = 0;
+ ir.y = 0;
+ ir.width = 0;
+ ir.height = 0;
+ tr.x = 0;
+ tr.y = 0;
+ tr.width = 0;
+ tr.height = 0;
+ vr.x = 0;
+ vr.y = 0;
+ vr.width = Short.MAX_VALUE;
+ vr.height = Short.MAX_VALUE;
+ layoutCL(lab, fm, text, icon, vr, ir, tr);
+ Rectangle cr = SwingUtilities.computeUnion(tr.x, tr.y, tr.width,
+ tr.height, ir);
+ ret = new Dimension(cr.width + insetsX, cr.height + insetsY);
+ }
+ return ret;
}
/**
@@ -166,13 +190,20 @@ public class BasicLabelUI extends LabelUI implements PropertyChangeListener
{
JLabel b = (JLabel) c;
FontMetrics fm = g.getFontMetrics();
- vr = SwingUtilities.calculateInnerArea(c, vr);
-
- if (vr.width < 0)
- vr.width = 0;
- if (vr.height < 0)
- vr.height = 0;
+ Insets i = c.getInsets();
+ vr.x = i.left;
+ vr.y = i.right;
+ vr.width = c.getWidth() - i.left + i.right;
+ vr.height = c.getHeight() - i.top + i.bottom;
+ ir.x = 0;
+ ir.y = 0;
+ ir.width = 0;
+ ir.height = 0;
+ tr.x = 0;
+ tr.y = 0;
+ tr.width = 0;
+ tr.height = 0;
Icon icon = (b.isEnabled()) ? b.getIcon() : b.getDisabledIcon();
String text = layoutCL(b, fm, b.getText(), icon, vr, ir, tr);
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java
index 493fc0578e3..befc227364a 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java
@@ -921,7 +921,7 @@ public class BasicListUI extends ListUI
*/
protected void maybeUpdateLayoutState()
{
- if (updateLayoutStateNeeded != 0)
+ if (updateLayoutStateNeeded != 0 || !list.isValid())
{
updateLayoutState();
updateLayoutStateNeeded = 0;
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java b/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java
index c056a2403f9..15430945468 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java
+++ b/libjava/classpath/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),
@@ -1218,10 +1221,10 @@ public abstract class BasicLookAndFeel extends LookAndFeel
"ctrl UP", "requestFocus",
"ctrl KP_UP", "requestFocus"
}),
- "TabbedPane.background", new ColorUIResource(light),
+ "TabbedPane.background", new ColorUIResource(192, 192, 192),
"TabbedPane.contentBorderInsets", new InsetsUIResource(2, 2, 3, 3),
- "TabbedPane.darkShadow", new ColorUIResource(shadow),
- "TabbedPane.focus", new ColorUIResource(darkShadow),
+ "TabbedPane.darkShadow", new ColorUIResource(Color.black),
+ "TabbedPane.focus", new ColorUIResource(Color.black),
"TabbedPane.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
KeyStroke.getKeyStroke("ctrl DOWN"), "requestFocusForVisibleComponent",
KeyStroke.getKeyStroke("KP_UP"), "navigateUp",
@@ -1235,17 +1238,16 @@ public abstract class BasicLookAndFeel extends LookAndFeel
KeyStroke.getKeyStroke("DOWN"), "navigateDown"
}),
"TabbedPane.font", new FontUIResource("Dialog", Font.PLAIN, 12),
- "TabbedPane.foreground", new ColorUIResource(darkShadow),
- "TabbedPane.highlight", new ColorUIResource(highLight),
- "TabbedPane.light", new ColorUIResource(highLight),
+ "TabbedPane.foreground", new ColorUIResource(Color.black),
+ "TabbedPane.highlight", new ColorUIResource(Color.white),
+ "TabbedPane.light", new ColorUIResource(192, 192, 192),
"TabbedPane.selectedTabPadInsets", new InsetsUIResource(2, 2, 2, 1),
- "TabbedPane.shadow", new ColorUIResource(shadow),
- "TabbedPane.tabbedPaneContentBorderInsets", new InsetsUIResource(3, 2, 1, 2),
- "TabbedPane.tabbedPaneTabPadInsets", new InsetsUIResource(1, 1, 1, 1),
+ "TabbedPane.shadow", new ColorUIResource(128, 128, 128),
"TabbedPane.tabsOpaque", Boolean.TRUE,
"TabbedPane.tabAreaInsets", new InsetsUIResource(3, 2, 0, 2),
"TabbedPane.tabInsets", new InsetsUIResource(0, 4, 1, 4),
"TabbedPane.tabRunOverlay", new Integer(2),
+ "TabbedPane.tabsOverlapBorder", Boolean.FALSE,
"TabbedPane.textIconGap", new Integer(4),
"Table.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] {
"ctrl DOWN", "selectNextRowChangeLead",
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java
index bbc08535cdc..5fafb4108b2 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java
@@ -180,17 +180,9 @@ public class BasicMenuItemUI extends MenuItemUI
private ItemListener itemListener;
/**
- * Number of spaces between accelerator and menu item's label.
+ * A PropertyChangeListener to make UI updates after property changes.
*/
- private int defaultAcceleratorLabelGap = 10;
-
- /**
- * The gap between different menus on the MenuBar.
- */
- private int MenuGap = 10;
-
- /** A PropertyChangeListener to make UI updates after property changes **/
- PropertyChangeHandler propertyChangeListener;
+ private PropertyChangeHandler propertyChangeListener;
/**
* The view rectangle used for layout of the menu item.
@@ -262,7 +254,6 @@ public class BasicMenuItemUI extends MenuItemUI
|| property.equals("font"))
&& SystemProperties.getProperty("gnu.javax.swing.noGraphics2D")
== null)
-
{
AbstractButton b = (AbstractButton) e.getSource();
String text = b.getText();
@@ -373,7 +364,7 @@ public class BasicMenuItemUI extends MenuItemUI
*/
protected void doClick(MenuSelectionManager msm)
{
- menuItem.doClick();
+ menuItem.doClick(0);
msm.clearSelectedPath();
}
@@ -411,14 +402,10 @@ public class BasicMenuItemUI extends MenuItemUI
{
ArrayList path = new ArrayList();
- // Path to menu should also include its popup menu.
- if (menuItem instanceof JMenu)
- path.add(((JMenu) menuItem).getPopupMenu());
-
Component c = menuItem;
while (c instanceof MenuElement)
{
- path.add(0, (MenuElement) c);
+ path.add(0, c);
if (c instanceof JPopupMenu)
c = ((JPopupMenu) c).getInvoker();
@@ -453,6 +440,7 @@ public class BasicMenuItemUI extends MenuItemUI
// Layout the menu item. The result gets stored in the rectangle
// fields of this class.
+ resetRectangles(null);
layoutMenuItem(m, accelText);
// The union of the text and icon areas is the label area.
@@ -606,6 +594,11 @@ public class BasicMenuItemUI extends MenuItemUI
menuItem.addMenuKeyListener(menuKeyListener);
menuItem.addItemListener(itemListener);
menuItem.addPropertyChangeListener(propertyChangeListener);
+ // Fire synthetic property change event to let the listener update
+ // the TextLayout cache.
+ propertyChangeListener.propertyChange(new PropertyChangeEvent(menuItem,
+ "font", null,
+ menuItem.getFont()));
}
/**
@@ -704,6 +697,8 @@ public class BasicMenuItemUI extends MenuItemUI
// Layout menu item. The result gets stored in the rectangle fields
// of this class.
+ resetRectangles(m);
+
layoutMenuItem(m, accelText);
// Paint the background.
@@ -936,6 +931,7 @@ public class BasicMenuItemUI extends MenuItemUI
uninstallListeners();
uninstallDefaults();
uninstallComponents(menuItem);
+ c.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, null);
menuItem = null;
}
@@ -953,47 +949,6 @@ public class BasicMenuItemUI extends MenuItemUI
}
/**
- * Return text representation of the specified accelerator
- *
- * @param accelerator
- * Accelerator for which to return string representation
- * @return $String$ Text representation of the given accelerator
- */
- private String getAcceleratorText(KeyStroke accelerator)
- {
- // convert keystroke into string format
- String modifiersText = "";
- int modifiers = accelerator.getModifiers();
- char keyChar = accelerator.getKeyChar();
- int keyCode = accelerator.getKeyCode();
-
- if (modifiers != 0)
- modifiersText = KeyEvent.getKeyModifiersText(modifiers)
- + acceleratorDelimiter;
-
- if (keyCode == KeyEvent.VK_UNDEFINED)
- return modifiersText + keyChar;
- else
- return modifiersText + KeyEvent.getKeyText(keyCode);
- }
-
- /**
- * Calculates and return rectange in which accelerator should be displayed
- *
- * @param accelerator
- * accelerator for which to return the display rectangle
- * @param fm
- * The font metrics used to measure the text
- * @return $Rectangle$ reactangle which will be used to display accelerator
- */
- private Rectangle getAcceleratorRect(KeyStroke accelerator, FontMetrics fm)
- {
- int width = fm.stringWidth(getAcceleratorText(accelerator));
- int height = fm.getHeight();
- return new Rectangle(0, 0, width, height);
- }
-
- /**
* This class handles mouse events occuring inside the menu item. Most of the
* events are forwarded for processing to MenuSelectionManager of the current
* menu hierarchy.
@@ -1103,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);
}
@@ -1130,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());
}
@@ -1143,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());
}
@@ -1155,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.
}
/**
@@ -1167,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();
}
}
@@ -1275,32 +1230,41 @@ public class BasicMenuItemUI extends MenuItemUI
}
/**
- * A helper method that lays out the menu item. The layout is stored
- * in the fields of this class.
+ * Resets the cached layout rectangles. If <code>i</code> is not null, then
+ * the view rectangle is set to the inner area of the component, otherwise
+ * it is set to (0, 0, Short.MAX_VALUE, Short.MAX_VALUE), this is needed
+ * for layouting.
*
- * @param m the menu item to layout
- * @param accelText the accelerator text
+ * @param i the component for which to initialize the rectangles
*/
- private void layoutMenuItem(JMenuItem m, String accelText)
+ private void resetRectangles(JMenuItem i)
{
- int width = m.getWidth();
- int height = m.getHeight();
-
// Reset rectangles.
iconRect.setBounds(0, 0, 0, 0);
textRect.setBounds(0, 0, 0, 0);
accelRect.setBounds(0, 0, 0, 0);
checkIconRect.setBounds(0, 0, 0, 0);
arrowIconRect.setBounds(0, 0, 0, 0);
- viewRect.setBounds(0, 0, width, height);
-
- // Substract insets to the view rect.
- Insets insets = m.getInsets();
- viewRect.x += insets.left;
- viewRect.y += insets.top;
- viewRect.width -= insets.left + insets.right;
- viewRect.height -= insets.top + insets.bottom;
+ if (i == null)
+ viewRect.setBounds(0, 0, Short.MAX_VALUE, Short.MAX_VALUE);
+ else
+ {
+ Insets insets = i.getInsets();
+ viewRect.setBounds(insets.left, insets.top,
+ i.getWidth() - insets.left - insets.right,
+ i.getHeight() - insets.top - insets.bottom);
+ }
+ }
+ /**
+ * A helper method that lays out the menu item. The layout is stored
+ * in the fields of this class.
+ *
+ * @param m the menu item to layout
+ * @param accelText the accelerator text
+ */
+ private void layoutMenuItem(JMenuItem m, String accelText)
+ {
// Fetch the fonts.
Font font = m.getFont();
FontMetrics fm = m.getFontMetrics(font);
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java
index 7d8784fd15a..355e0435ec8 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java
@@ -41,16 +41,22 @@ package javax.swing.plaf.basic;
import gnu.classpath.NotImplementedException;
import java.awt.Component;
+import java.awt.Container;
import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeListener;
+import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JPopupMenu;
import javax.swing.LookAndFeel;
+import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
+import javax.swing.Timer;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
@@ -69,6 +75,32 @@ import javax.swing.plaf.ComponentUI;
*/
public class BasicMenuUI extends BasicMenuItemUI
{
+ /**
+ * Selects a menu. This is used to delay menu selection.
+ */
+ class SelectMenuAction
+ extends AbstractAction
+ {
+ /**
+ * Performs the action.
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ JMenu menu = (JMenu) menuItem;
+ MenuSelectionManager defaultManager =
+ MenuSelectionManager.defaultManager();
+ MenuElement path[] = defaultManager.getSelectedPath();
+ if(path.length > 0 && path[path.length - 1] == menu)
+ {
+ MenuElement newPath[] = new MenuElement[path.length + 1];
+ System.arraycopy(path, 0, newPath, 0, path.length);
+ newPath[path.length] = menu.getPopupMenu();
+ defaultManager.setSelectedPath(newPath);
+ }
+ }
+
+ }
+
protected ChangeListener changeListener;
/* MenuListener listens to MenuEvents fired by JMenu */
@@ -201,6 +233,7 @@ public class BasicMenuUI extends BasicMenuItemUI
*/
protected void installDefaults()
{
+
LookAndFeel.installBorder(menuItem, "Menu.border");
LookAndFeel.installColorsAndFont(menuItem, "Menu.background",
"Menu.foreground", "Menu.font");
@@ -212,6 +245,7 @@ public class BasicMenuUI extends BasicMenuItemUI
selectionForeground = UIManager.getColor("Menu.selectionForeground");
arrowIcon = UIManager.getIcon("Menu.arrowIcon");
oldBorderPainted = UIManager.getBoolean("Menu.borderPainted");
+ ((JMenu) menuItem).setDelay(200);
}
/**
@@ -234,9 +268,10 @@ public class BasicMenuUI extends BasicMenuItemUI
}
protected void setupPostTimer(JMenu menu)
- throws NotImplementedException
{
- // TODO: Implement this properly.
+ Timer timer = new Timer(menu.getDelay(), new SelectMenuAction());
+ timer.setRepeats(false);
+ timer.start();
}
/**
@@ -285,8 +320,7 @@ public class BasicMenuUI extends BasicMenuItemUI
{
public void mouseClicked(MouseEvent e)
{
- MenuSelectionManager manager = MenuSelectionManager.defaultManager();
- manager.processMouseEvent(e);
+ // Nothing to do here.
}
public void mouseDragged(MouseEvent e)
@@ -313,29 +347,46 @@ public class BasicMenuUI extends BasicMenuItemUI
public void mouseEntered(MouseEvent e)
{
- /* When mouse enters menu item, it should be considered selected
-
- if (i) if this menu is a submenu in some other menu
- (ii) or if this menu is in a menu bar and some other menu in a
- menu bar was just selected and has its popup menu visible.
- (If nothing was selected, menu should be pressed before
- it will be selected)
- */
JMenu menu = (JMenu) menuItem;
-
- // NOTE: the following if used to require !menu.isArmed but I could find
- // no reason for this and it was preventing some JDK-compatible behaviour.
- // Specifically, if a menu is selected but its popup menu not visible,
- // and then another menu is selected whose popup menu IS visible, when
- // the mouse is moved over the first menu, its popup menu should become
- // visible.
-
- if (! menu.isTopLevelMenu() || popupVisible())
+ if (menu.isEnabled())
{
- // set new selection and forward this event to MenuSelectionManager
- MenuSelectionManager manager = MenuSelectionManager.defaultManager();
- manager.setSelectedPath(getPath());
- manager.processMouseEvent(e);
+ MenuSelectionManager manager =
+ MenuSelectionManager.defaultManager();
+ MenuElement[] selectedPath = manager.getSelectedPath();
+ if (! menu.isTopLevelMenu())
+ {
+ // Open the menu immediately or delayed, depending on the
+ // delay value.
+ if(! (selectedPath.length > 0
+ && selectedPath[selectedPath.length - 1] == menu.getPopupMenu()))
+ {
+ if(menu.getDelay() == 0)
+ {
+ MenuElement[] path = getPath();
+ MenuElement[] newPath = new MenuElement[path.length + 1];
+ System.arraycopy(path, 0, newPath, 0, path.length);
+ newPath[path.length] = menu.getPopupMenu();
+ manager.setSelectedPath(newPath);
+ }
+ else
+ {
+ manager.setSelectedPath(getPath());
+ setupPostTimer(menu);
+ }
+ }
+ }
+ else
+ {
+ if(selectedPath.length > 0
+ && selectedPath[0] == menu.getParent())
+ {
+ MenuElement[] newPath = new MenuElement[3];
+ newPath[0] = (MenuElement) menu.getParent();
+ newPath[1] = menu;
+ newPath[2] = menu.getPopupMenu();
+ manager.setSelectedPath(newPath);
+ }
+ }
}
}
@@ -354,29 +405,48 @@ public class BasicMenuUI extends BasicMenuItemUI
{
MenuSelectionManager manager = MenuSelectionManager.defaultManager();
JMenu menu = (JMenu) menuItem;
- manager.processMouseEvent(e);
-
- // Menu should be displayed when the menu is pressed only if
- // it is top-level menu
- if (menu.isTopLevelMenu())
+ if (menu.isEnabled())
{
- if (menu.getPopupMenu().isVisible())
- // If menu is visible and menu button was pressed..
- // then need to cancel the menu
- manager.clearSelectedPath();
- else
- {
- // Display the menu
- int x = 0;
- int y = menu.getHeight();
-
- manager.setSelectedPath(getPath());
-
- JMenuBar mb = (JMenuBar) menu.getParent();
-
- // set selectedIndex of the selectionModel of a menuBar
- mb.getSelectionModel().setSelectedIndex(mb.getComponentIndex(menu));
- }
+ // Open up the menu immediately if it's a toplevel menu.
+ // But not yet the popup, which might be opened delayed, see below.
+ if (menu.isTopLevelMenu())
+ {
+ if (menu.isSelected())
+ manager.clearSelectedPath();
+ else
+ {
+ Container cnt = menu.getParent();
+ if (cnt != null && cnt instanceof JMenuBar)
+ {
+ MenuElement[] me = new MenuElement[2];
+ me[0] = (MenuElement) cnt;
+ me[1] = menu;
+ manager.setSelectedPath(me);
+ }
+ }
+ }
+
+ // Open the menu's popup. Either do that immediately if delay == 0,
+ // or delayed when delay > 0.
+ MenuElement[] selectedPath = manager.getSelectedPath();
+ if (selectedPath.length > 0
+ && selectedPath[selectedPath.length - 1] != menu.getPopupMenu())
+ {
+ if(menu.isTopLevelMenu() || menu.getDelay() == 0)
+ {
+ MenuElement[] newPath =
+ new MenuElement[selectedPath.length + 1];
+ System.arraycopy(selectedPath, 0, newPath, 0,
+ selectedPath.length);
+ newPath[selectedPath.length] = menu.getPopupMenu();
+ manager.setSelectedPath(newPath);
+ }
+ else
+ {
+ setupPostTimer(menu);
+ }
+ }
+
}
}
@@ -493,8 +563,44 @@ public class BasicMenuUI extends BasicMenuItemUI
*/
public void menuDragMouseDragged(MenuDragMouseEvent e)
{
- MenuSelectionManager manager = MenuSelectionManager.defaultManager();
- manager.setSelectedPath(e.getPath());
+ if (menuItem.isEnabled())
+ {
+ MenuSelectionManager manager = e.getMenuSelectionManager();
+ MenuElement path[] = e.getPath();
+
+ Point p = e.getPoint();
+ if(p.x >= 0 && p.x < menuItem.getWidth()
+ && p.y >= 0 && p.y < menuItem.getHeight())
+ {
+ JMenu menu = (JMenu) menuItem;
+ MenuElement[] selectedPath = manager.getSelectedPath();
+ if(! (selectedPath.length > 0
+ && selectedPath[selectedPath.length-1]
+ == menu.getPopupMenu()))
+ {
+ if(menu.isTopLevelMenu() || menu.getDelay() == 0
+ || e.getID() == MouseEvent.MOUSE_DRAGGED)
+ {
+ MenuElement[] newPath = new MenuElement[path.length + 1];
+ System.arraycopy(path, 0, newPath, 0, path.length);
+ newPath[path.length] = menu.getPopupMenu();
+ manager.setSelectedPath(newPath);
+ }
+ else
+ {
+ manager.setSelectedPath(path);
+ setupPostTimer(menu);
+ }
+ }
+ }
+ else if (e.getID() == MouseEvent.MOUSE_RELEASED)
+ {
+ Component comp = manager.componentForPoint(e.getComponent(),
+ e.getPoint());
+ if (comp == null)
+ manager.clearSelectedPath();
+ }
+ }
}
/**
@@ -505,8 +611,7 @@ public class BasicMenuUI extends BasicMenuItemUI
*/
public void menuDragMouseEntered(MenuDragMouseEvent e)
{
- MenuSelectionManager manager = MenuSelectionManager.defaultManager();
- manager.setSelectedPath(e.getPath());
+ // Nothing to do here.
}
/**
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java
index aed4d69d6d5..bfb9e98dbc9 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java
+++ b/libjava/classpath/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
@@ -81,7 +82,7 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI
*/
public BasicRadioButtonUI()
{
- icon = getDefaultIcon();
+ // nothing to do
}
/**
@@ -93,6 +94,7 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI
protected void installDefaults(AbstractButton b)
{
super.installDefaults(b);
+ icon = UIManager.getIcon(getPropertyPrefix() + "icon");
}
/**
@@ -116,7 +118,7 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI
*/
public Icon getDefaultIcon()
{
- return UIManager.getIcon(getPropertyPrefix() + "icon");
+ return icon;
}
/**
@@ -128,40 +130,92 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI
public void paint(Graphics g, JComponent c)
{
AbstractButton b = (AbstractButton) c;
-
- Rectangle tr = new Rectangle();
- Rectangle ir = new Rectangle();
- Rectangle vr = new Rectangle();
+ Dimension size = c.getSize();
+ Insets i = b.getInsets();
+ textR.x = 0;
+ textR.y = 0;
+ textR.width = 0;
+ textR.height = 0;
+ iconR.x = 0;
+ iconR.y = 0;
+ iconR.width = 0;
+ iconR.height = 0;
+ viewR.x = i.left;
+ viewR.y = i.right;
+ 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();
- }
-
- SwingUtilities.calculateInnerArea(b, vr);
+ // 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);
-
- currentIcon.paintIcon(c, g, ir.x, ir.y);
-
+ viewR, iconR, textR, b.getIconTextGap());
+
+ // 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)
@@ -174,38 +228,40 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI
// The other icon properties are ignored.
AbstractButton b = (AbstractButton) c;
- Rectangle contentRect;
- Rectangle viewRect;
- Rectangle iconRect = new Rectangle();
- Rectangle textRect = new Rectangle();
Insets insets = b.getInsets();
-
+
+ String text = b.getText();
Icon i = b.getIcon();
if (i == null)
i = getDefaultIcon();
- viewRect = new Rectangle();
-
- SwingUtilities.layoutCompoundLabel(
- b, // for the component orientation
- b.getFontMetrics(b.getFont()),
- b.getText(),
- i,
- b.getVerticalAlignment(),
- b.getHorizontalAlignment(),
- b.getVerticalTextPosition(),
- b.getHorizontalTextPosition(),
- viewRect, iconRect, textRect,
- defaultTextIconGap + defaultTextShiftOffset);
-
- contentRect = textRect.union(iconRect);
-
- return new Dimension(insets.left
- + contentRect.width
- + insets.right + b.getHorizontalAlignment(),
- insets.top
- + contentRect.height
- + insets.bottom);
+ textR.x = 0;
+ textR.y = 0;
+ textR.width = 0;
+ textR.height = 0;
+ iconR.x = 0;
+ iconR.y = 0;
+ iconR.width = 0;
+ iconR.height = 0;
+ viewR.x = 0;
+ viewR.y = 0;
+ viewR.width = Short.MAX_VALUE;
+ viewR.height = Short.MAX_VALUE;
+
+ 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);
+
+ return new Dimension(insets.left + r.width + insets.right,
+ insets.top + r.height + insets.bottom);
}
/**
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java
index 78e5168fc80..400ede03ce9 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java
@@ -760,10 +760,7 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager,
scrollbar.setOpaque(true);
scrollbar.setLayout(this);
- thumbColor = UIManager.getColor("ScrollBar.thumb");
- thumbDarkShadowColor = UIManager.getColor("ScrollBar.thumbDarkShadow");
- thumbHighlightColor = UIManager.getColor("ScrollBar.thumbHighlight");
- thumbLightShadowColor = UIManager.getColor("ScrollBar.thumbShadow");
+ configureScrollBarColors();
maximumThumbSize = UIManager.getDimension("ScrollBar.maximumThumbSize");
minimumThumbSize = UIManager.getDimension("ScrollBar.minimumThumbSize");
@@ -1228,8 +1225,36 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager,
*/
protected void scrollByBlock(int direction)
{
- scrollbar.setValue(scrollbar.getValue()
- + scrollbar.getBlockIncrement(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)
+ delta = scrollBar.getBlockIncrement(direction);
+ else
+ 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);
}
/**
@@ -1239,8 +1264,46 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager,
*/
protected void scrollByUnit(int direction)
{
- 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/libjava/classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java
index a0616a8c1cf..a7194284050 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java
@@ -38,9 +38,6 @@ exception statement from your version. */
package javax.swing.plaf.basic;
-import gnu.classpath.NotImplementedException;
-
-import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
@@ -54,7 +51,6 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.AbstractAction;
-import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
@@ -65,15 +61,15 @@ 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;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.ActionMapUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.ScrollPaneUI;
+import javax.swing.plaf.UIResource;
/**
* A UI delegate for the {@link JScrollPane} component.
@@ -102,19 +98,8 @@ public class BasicScrollPaneUI extends ScrollPaneUI
JScrollBar hsb = scrollpane.getHorizontalScrollBar();
JViewport vp = scrollpane.getViewport();
Point viewPosition = vp.getViewPosition();
- int xpos = hsb.getValue();
-
- if (xpos != viewPosition.x)
- {
- viewPosition.x = xpos;
- vp.setViewPosition(viewPosition);
- }
-
- viewPosition.y = 0;
- JViewport columnHeader = scrollpane.getColumnHeader();
- if (columnHeader != null
- && !columnHeader.getViewPosition().equals(viewPosition))
- columnHeader.setViewPosition(viewPosition);
+ viewPosition.x = hsb.getValue();
+ vp.setViewPosition(viewPosition);
}
}
@@ -139,18 +124,8 @@ public class BasicScrollPaneUI extends ScrollPaneUI
JScrollBar vsb = scrollpane.getVerticalScrollBar();
JViewport vp = scrollpane.getViewport();
Point viewPosition = vp.getViewPosition();
- int ypos = vsb.getValue();
- if (ypos != viewPosition.y)
- {
- viewPosition.y = ypos;
- vp.setViewPosition(viewPosition);
- }
-
- viewPosition.x = 0;
- JViewport rowHeader = scrollpane.getRowHeader();
- if (rowHeader != null
- && !rowHeader.getViewPosition().equals(viewPosition))
- rowHeader.setViewPosition(viewPosition);
+ viewPosition.y = vsb.getValue();
+ vp.setViewPosition(viewPosition);
}
}
@@ -174,9 +149,6 @@ public class BasicScrollPaneUI extends ScrollPaneUI
*/
public void stateChanged(ChangeEvent event)
{
- JViewport vp = scrollpane.getViewport();
- JScrollBar hsb = scrollpane.getHorizontalScrollBar();
- JScrollBar vsb = scrollpane.getVerticalScrollBar();
syncScrollPaneWithViewport();
}
@@ -254,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 (scrollpane.isWheelScrollingEnabled() && e.getScrollAmount() != 0)
{
- if (scrollable != null)
+ // Try to scroll vertically first.
+ JScrollBar scrollBar = scrollpane.getVerticalScrollBar();
+ if (scrollBar == null || ! scrollBar.isVisible())
+ scrollBar = scrollpane.getHorizontalScrollBar();
+ if (scrollBar != null && scrollBar.isVisible())
{
- bounds(target);
- delta = scrollable.getScrollableUnitIncrement(
- rect, SwingConstants.VERTICAL, wheel);
- }
- else
- {
- // Scroll non scrollables.
- delta = wheel * SCROLL_NON_SCROLLABLES;
- }
- scroll(bar, delta);
- }
- // If not, try to scroll horizontally
- else
- {
- bar = scrollpane.getHorizontalScrollBar();
- boolean tracksWidth = scrollable != null
- && scrollable.getScrollableTracksViewportWidth();
-
- if (bar != null && ! tracksWidth)
- {
- 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);
- }
}
/**
@@ -436,16 +329,24 @@ public class BasicScrollPaneUI extends ScrollPaneUI
"ScrollPane.foreground",
"ScrollPane.font");
LookAndFeel.installBorder(p, "ScrollPane.border");
+
+ // Install Viewport border.
+ Border vpBorder = p.getViewportBorder();
+ if (vpBorder == null || vpBorder instanceof UIResource)
+ {
+ vpBorder = UIManager.getBorder("ScrollPane.viewportBorder");
+ p.setViewportBorder(vpBorder);
+ }
+
p.setOpaque(true);
}
protected void uninstallDefaults(JScrollPane p)
{
- p.setForeground(null);
- p.setBackground(null);
- p.setFont(null);
- p.setBorder(null);
- scrollpane = null;
+ LookAndFeel.uninstallBorder(p);
+ Border vpBorder = p.getViewportBorder();
+ if (vpBorder != null && vpBorder instanceof UIResource)
+ p.setViewportBorder(null);
}
public void installUI(final JComponent c)
@@ -770,9 +671,8 @@ public class BasicScrollPaneUI extends ScrollPaneUI
public void uninstallUI(final JComponent c)
{
- super.uninstallUI(c);
- this.uninstallDefaults((JScrollPane) c);
- uninstallListeners((JScrollPane) c);
+ uninstallDefaults((JScrollPane) c);
+ uninstallListeners(c);
installKeyboardActions((JScrollPane) c);
}
@@ -808,29 +708,65 @@ public class BasicScrollPaneUI extends ScrollPaneUI
}
public void paint(Graphics g, JComponent c)
- {
- // do nothing; the normal painting-of-children algorithm, along with
- // ScrollPaneLayout, does all the relevant work.
+ {
+ Border vpBorder = scrollpane.getViewportBorder();
+ if (vpBorder != null)
+ {
+ Rectangle r = scrollpane.getViewportBorderBounds();
+ vpBorder.paintBorder(scrollpane, g, r.x, r.y, r.width, r.height);
+ }
}
/**
- * Synchronizes the scrollbars with the viewport's extents.
+ * Synchronizes the scrollbar and header settings positions and extent
+ * with the viewport's view position and extent.
*/
protected void syncScrollPaneWithViewport()
{
JViewport vp = scrollpane.getViewport();
- // Update the horizontal scrollbar.
- JScrollBar hsb = scrollpane.getHorizontalScrollBar();
- hsb.setMaximum(vp.getViewSize().width);
- hsb.setValue(vp.getViewPosition().x);
- hsb.setVisibleAmount(vp.getExtentSize().width);
-
- // Update the vertical scrollbar.
- JScrollBar vsb = scrollpane.getVerticalScrollBar();
- vsb.setMaximum(vp.getViewSize().height);
- vsb.setValue(vp.getViewPosition().y);
- vsb.setVisibleAmount(vp.getExtentSize().height);
+ if (vp != null)
+ {
+ Dimension extentSize = vp.getExtentSize();
+ Point viewPos = vp.getViewPosition();
+ Dimension viewSize = vp.getViewSize();
+
+ // Update the vertical scrollbar.
+ JScrollBar vsb = scrollpane.getVerticalScrollBar();
+ if (vsb != null)
+ {
+ int extent = extentSize.height;
+ int max = viewSize.height;
+ int val = Math.max(0, Math.min(viewPos.y, max - extent));
+ vsb.setValues(val, extent, 0, max);
+ }
+
+ // Update the horizontal scrollbar.
+ JScrollBar hsb = scrollpane.getHorizontalScrollBar();
+ if (hsb != null)
+ {
+ int extent = extentSize.width;
+ int max = viewSize.width;
+ int val = Math.max(0, Math.min(viewPos.x, max - extent));
+ hsb.setValues(val, extent, 0, max);
+ }
+
+ // Update the row header.
+ JViewport rowHeader = scrollpane.getRowHeader();
+ if (rowHeader != null)
+ {
+ Point p = new Point(0, viewPos.y);
+ rowHeader.setViewPosition(p);
+ }
+
+ // Update the column header.
+ JViewport colHeader = scrollpane.getColumnHeader();
+ if (colHeader != null)
+ {
+ Point p = new Point(viewPos.x, 0);
+ colHeader.setViewPosition(p);
+ }
+ }
}
/**
@@ -863,7 +799,8 @@ public class BasicScrollPaneUI extends ScrollPaneUI
*/
protected void updateScrollBarDisplayPolicy(PropertyChangeEvent ev)
{
- // TODO: Find out what should be done here. Or is this only a hook?
+ scrollpane.revalidate();
+ scrollpane.repaint();
}
/**
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java
index 3811eebdfd6..474a4225640 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java
+++ b/libjava/classpath/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/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java
index 06d32984efb..95468caa972 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java
@@ -38,12 +38,15 @@ exception statement from your version. */
package javax.swing.plaf.basic;
+import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.LayoutManager;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
@@ -52,7 +55,7 @@ import java.beans.PropertyChangeListener;
import javax.swing.JButton;
import javax.swing.JSplitPane;
-import javax.swing.SwingConstants;
+import javax.swing.UIManager;
import javax.swing.border.Border;
/**
@@ -72,6 +75,207 @@ public class BasicSplitPaneDivider extends Container
implements PropertyChangeListener
{
/**
+ * The buttons used as one touch buttons.
+ */
+ private class BasicOneTouchButton
+ extends JButton
+ {
+ /**
+ * Denotes a left button.
+ */
+ static final int LEFT = 0;
+
+ /**
+ * Denotes a right button.
+ */
+ static final int RIGHT = 1;
+
+ /**
+ * The x points for the arrow.
+ */
+ private int[] xpoints;
+
+ /**
+ * The y points for the arrow.
+ */
+ private int[] ypoints;
+
+ /**
+ * Either LEFT or RIGHT.
+ */
+ private int direction;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param dir either LEFT or RIGHT
+ */
+ BasicOneTouchButton(int dir)
+ {
+ direction = dir;
+ xpoints = new int[3];
+ ypoints = new int[3];
+ }
+
+ /**
+ * Never allow borders.
+ */
+ public void setBorder(Border b)
+ {
+ }
+
+ /**
+ * Never allow focus traversal.
+ */
+ public boolean isFocusTraversable()
+ {
+ return false;
+ }
+
+ /**
+ * Paints the one touch button.
+ */
+ public void paint(Graphics g)
+ {
+ if (splitPane != null)
+ {
+ // Fill background.
+ g.setColor(splitPane.getBackground());
+ g.fillRect(0, 0, getWidth(), getHeight());
+
+ // Draw arrow.
+ int size;
+ if (direction == LEFT)
+ {
+ if (orientation == JSplitPane.VERTICAL_SPLIT)
+ {
+ size = Math.min(getHeight(), ONE_TOUCH_SIZE);
+ xpoints[0] = 0;
+ xpoints[1] = size / 2;
+ xpoints[2] = size;
+ ypoints[0] = size;
+ ypoints[1] = 0;
+ ypoints[2] = size;
+ }
+ else
+ {
+ size = Math.min(getWidth(), ONE_TOUCH_SIZE);
+ xpoints[0] = size;
+ xpoints[1] = 0;
+ xpoints[2] = size;
+ ypoints[0] = 0;
+ ypoints[1] = size / 2;
+ ypoints[2] = size;
+ }
+ }
+ else
+ {
+ if (orientation == JSplitPane.VERTICAL_SPLIT)
+ {
+ size = Math.min(getHeight(), ONE_TOUCH_SIZE);
+ xpoints[0] = 0;
+ xpoints[1] = size / 2;
+ xpoints[2] = size;
+ ypoints[0] = 0;
+ ypoints[1] = size;
+ ypoints[2] = 0;
+ }
+ else
+ {
+ size = Math.min(getWidth(), ONE_TOUCH_SIZE);
+ xpoints[0] = 0;
+ xpoints[1] = size;
+ xpoints[2] = 0;
+ ypoints[0] = 0;
+ ypoints[1] = size / 2;
+ ypoints[2] = size;
+ }
+ }
+ g.setColor(Color.BLACK);
+ g.fillPolygon(xpoints, ypoints, 3);
+ }
+ }
+ }
+
+ /**
+ * Listens for actions on the one touch buttons.
+ */
+ private class OneTouchAction
+ implements ActionListener
+ {
+
+ public void actionPerformed(ActionEvent ev)
+ {
+ Insets insets = splitPane.getInsets();
+ int lastLoc = splitPane.getLastDividerLocation();
+ int currentLoc = splitPaneUI.getDividerLocation(splitPane);
+ int newLoc;
+
+ if (ev.getSource() == leftButton)
+ {
+ if (orientation == JSplitPane.VERTICAL_SPLIT)
+ {
+ if (currentLoc
+ >= splitPane.getHeight() - insets.bottom - getHeight())
+ {
+ newLoc = Math.min(splitPane.getMaximumDividerLocation(),
+ lastLoc);
+ }
+ else
+ {
+ newLoc = insets.top;
+ }
+ }
+ else
+ {
+ if (currentLoc
+ >= splitPane.getWidth() - insets.right - getWidth())
+ {
+ newLoc = Math.min(splitPane.getMaximumDividerLocation(),
+ lastLoc);
+ }
+ else
+ {
+ newLoc = insets.left;
+ }
+ }
+ }
+ else
+ {
+ if (orientation == JSplitPane.VERTICAL_SPLIT)
+ {
+ if (currentLoc == insets.top)
+ {
+ newLoc = Math.min(splitPane.getMaximumDividerLocation(),
+ lastLoc);
+ }
+ else
+ {
+ newLoc = splitPane.getHeight() - insets.top - getHeight();
+ }
+ }
+ else
+ {
+ if (currentLoc == insets.left)
+ {
+ newLoc = Math.min(splitPane.getMaximumDividerLocation(),
+ lastLoc);
+ }
+ else
+ {
+ newLoc = splitPane.getWidth() - insets.left - getWidth();
+ }
+ }
+ }
+ if (currentLoc != newLoc)
+ {
+ splitPane.setDividerLocation(newLoc);
+ splitPane.setLastDividerLocation(currentLoc);
+ }
+ }
+ }
+
+ /**
* Determined using the <code>serialver</code> tool of Apple/Sun JDK 1.3.1
* on MacOS X 10.1.5.
*/
@@ -161,6 +365,14 @@ public class BasicSplitPaneDivider extends Container
transient int currentDividerLocation = 1;
/**
+ * Indicates if the ont touch buttons are laid out centered or at the
+ * top/left.
+ *
+ * Package private to avoid accessor method.
+ */
+ boolean centerOneTouchButtons;
+
+ /**
* Constructs a new divider.
*
* @param ui the UI delegate of the enclosing <code>JSplitPane</code>.
@@ -170,6 +382,8 @@ public class BasicSplitPaneDivider extends Container
setLayout(new DividerLayout());
setBasicSplitPaneUI(ui);
setDividerSize(splitPane.getDividerSize());
+ centerOneTouchButtons =
+ UIManager.getBoolean("SplitPane.centerOneTouchButtons");
}
/**
@@ -202,7 +416,8 @@ public class BasicSplitPaneDivider extends Container
addMouseMotionListener(mouseHandler);
hiddenDivider = splitPaneUI.getNonContinuousLayoutDivider();
orientation = splitPane.getOrientation();
- oneTouchExpandableChanged();
+ if (splitPane.isOneTouchExpandable())
+ oneTouchExpandableChanged();
}
}
@@ -293,7 +508,12 @@ public class BasicSplitPaneDivider extends Container
*/
public Dimension getPreferredSize()
{
- return getLayout().preferredLayoutSize(this);
+ Dimension d;
+ if (orientation == JSplitPane.HORIZONTAL_SPLIT)
+ d = new Dimension(getDividerSize(), 1);
+ else
+ d = new Dimension(1, getDividerSize());
+ return d;
}
/**
@@ -320,11 +540,9 @@ public class BasicSplitPaneDivider extends Container
else if (e.getPropertyName().equals(JSplitPane.ORIENTATION_PROPERTY))
{
orientation = splitPane.getOrientation();
- if (splitPane.isOneTouchExpandable())
- {
- layout();
- repaint();
- }
+ invalidate();
+ if (splitPane != null)
+ splitPane.revalidate();
}
else if (e.getPropertyName().equals(JSplitPane.DIVIDER_SIZE_PROPERTY))
dividerSize = splitPane.getDividerSize();
@@ -345,11 +563,6 @@ public class BasicSplitPaneDivider extends Container
dividerSize = getSize();
border.paintBorder(this, g, 0, 0, dividerSize.width, dividerSize.height);
}
- if (splitPane.isOneTouchExpandable())
- {
- ((BasicArrowButton) rightButton).paint(g);
- ((BasicArrowButton) leftButton).paint(g);
- }
}
/**
@@ -361,31 +574,23 @@ public class BasicSplitPaneDivider extends Container
if (splitPane.isOneTouchExpandable())
{
leftButton = createLeftOneTouchButton();
- rightButton = createRightOneTouchButton();
- add(leftButton);
- add(rightButton);
+ if (leftButton != null)
+ leftButton.addActionListener(new OneTouchAction());
- leftButton.addMouseListener(mouseHandler);
- rightButton.addMouseListener(mouseHandler);
+ rightButton = createRightOneTouchButton();
+ if (rightButton != null)
+ rightButton.addActionListener(new OneTouchAction());
- // Set it to 1.
- currentDividerLocation = 1;
- }
- else
- {
+ // Only add them when both are non-null.
if (leftButton != null && rightButton != null)
- {
- leftButton.removeMouseListener(mouseHandler);
- rightButton.removeMouseListener(mouseHandler);
-
- remove(leftButton);
- remove(rightButton);
- leftButton = null;
- rightButton = null;
+ {
+ add(leftButton);
+ add(rightButton);
}
}
- layout();
- repaint();
+ invalidate();
+ if (splitPane != null)
+ splitPane.revalidate();
}
/**
@@ -396,12 +601,9 @@ public class BasicSplitPaneDivider extends Container
*/
protected JButton createLeftOneTouchButton()
{
- int dir = SwingConstants.WEST;
- if (orientation == JSplitPane.VERTICAL_SPLIT)
- dir = SwingConstants.NORTH;
- JButton button = new BasicArrowButton(dir);
- button.setBorder(null);
-
+ JButton button = new BasicOneTouchButton(BasicOneTouchButton.LEFT);
+ button.setMinimumSize(new Dimension(ONE_TOUCH_SIZE, ONE_TOUCH_SIZE));
+ button.setRequestFocusEnabled(false);
return button;
}
@@ -413,11 +615,9 @@ public class BasicSplitPaneDivider extends Container
*/
protected JButton createRightOneTouchButton()
{
- int dir = SwingConstants.EAST;
- if (orientation == JSplitPane.VERTICAL_SPLIT)
- dir = SwingConstants.SOUTH;
- JButton button = new BasicArrowButton(dir);
- button.setBorder(null);
+ JButton button = new BasicOneTouchButton(BasicOneTouchButton.RIGHT);
+ button.setMinimumSize(new Dimension(ONE_TOUCH_SIZE, ONE_TOUCH_SIZE));
+ button.setRequestFocusEnabled(false);
return button;
}
@@ -521,25 +721,6 @@ public class BasicSplitPaneDivider extends Container
*/
public void mousePressed(MouseEvent e)
{
- if (splitPane.isOneTouchExpandable())
- {
- if (e.getSource() == leftButton)
- {
- currentDividerLocation--;
- if (currentDividerLocation < 0)
- currentDividerLocation = 0;
- moveDividerTo(currentDividerLocation);
- return;
- }
- else if (e.getSource() == rightButton)
- {
- currentDividerLocation++;
- if (currentDividerLocation > 2)
- currentDividerLocation = 2;
- moveDividerTo(currentDividerLocation);
- return;
- }
- }
isDragging = true;
currentDividerLocation = 1;
if (orientation == JSplitPane.HORIZONTAL_SPLIT)
@@ -797,10 +978,64 @@ public class BasicSplitPaneDivider extends Container
*/
public void layoutContainer(Container c)
{
- if (splitPane.isOneTouchExpandable())
+ if (leftButton != null && rightButton != null
+ && c == BasicSplitPaneDivider.this)
{
- changeButtonOrientation();
- positionButtons();
+ if (splitPane.isOneTouchExpandable())
+ {
+ Insets insets = getInsets();
+ if (orientation == JSplitPane.HORIZONTAL_SPLIT)
+ {
+ int size = getWidth() - insets.left - insets.right;
+ size = Math.max(size, 0);
+ size = Math.min(size, ONE_TOUCH_SIZE);
+ int x, y;
+ if (centerOneTouchButtons)
+ {
+ y = insets.top;
+ x = (getWidth() - size) / 2;
+ }
+ else
+ {
+ x = insets.left;
+ y = 0;
+ }
+
+ leftButton.setBounds(x, y + ONE_TOUCH_OFFSET, size,
+ size * 2);
+ rightButton.setBounds(x, y + ONE_TOUCH_OFFSET
+ + ONE_TOUCH_SIZE * 2, size, size * 2);
+ }
+ else
+ {
+ int size = getHeight() - insets.top - insets.bottom;
+ size = Math.max(size, 0);
+ size = Math.min(size, ONE_TOUCH_SIZE);
+ int x, y;
+ if (centerOneTouchButtons)
+ {
+ x = insets.left;
+ y = (getHeight() - size) / 2;
+ }
+ else
+ {
+ x = 0;
+ y = insets.top;
+ }
+ leftButton.setBounds(x + ONE_TOUCH_OFFSET, y, size * 2,
+ size);
+ rightButton.setBounds(x + ONE_TOUCH_OFFSET
+ + ONE_TOUCH_SIZE * 2, y, size * 2,
+ size);
+ }
+ }
+ else
+ {
+ // The JDK sets this bounds for disabled one touch buttons, so
+ // do we.
+ leftButton.setBounds(-5, -5, 1, 1);
+ rightButton.setBounds(-5, -5, 1, 1);
+ }
}
}
@@ -838,50 +1073,5 @@ public class BasicSplitPaneDivider extends Container
// Do nothing.
}
- /**
- * This method changes the button orientation when the orientation of the
- * SplitPane changes.
- */
- private void changeButtonOrientation()
- {
- if (orientation == JSplitPane.HORIZONTAL_SPLIT)
- {
- ((BasicArrowButton) rightButton).setDirection(SwingConstants.EAST);
- ((BasicArrowButton) leftButton).setDirection(SwingConstants.WEST);
- }
- else
- {
- ((BasicArrowButton) rightButton).setDirection(SwingConstants.SOUTH);
- ((BasicArrowButton) leftButton).setDirection(SwingConstants.NORTH);
- }
- }
-
- /**
- * This method sizes and positions the buttons.
- */
- private void positionButtons()
- {
- int w = 0;
- int h = 0;
- if (orientation == JSplitPane.HORIZONTAL_SPLIT)
- {
- rightButton.setLocation(ONE_TOUCH_OFFSET, ONE_TOUCH_OFFSET);
- leftButton.setLocation(ONE_TOUCH_OFFSET,
- ONE_TOUCH_OFFSET + 2 * ONE_TOUCH_SIZE);
- w = dividerSize - 2 * ONE_TOUCH_OFFSET;
- h = 2 * ONE_TOUCH_SIZE;
- }
- else
- {
- leftButton.setLocation(ONE_TOUCH_OFFSET, ONE_TOUCH_OFFSET);
- rightButton.setLocation(ONE_TOUCH_OFFSET + 2 * ONE_TOUCH_SIZE,
- ONE_TOUCH_OFFSET);
- h = dividerSize - 2 * ONE_TOUCH_OFFSET;
- w = 2 * ONE_TOUCH_SIZE;
- }
- Dimension dims = new Dimension(w, h);
- leftButton.setSize(dims);
- rightButton.setSize(dims);
- }
}
}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java
index 2d595597424..b7cc425482d 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java
@@ -63,6 +63,7 @@ import javax.swing.JSlider;
import javax.swing.JSplitPane;
import javax.swing.KeyStroke;
import javax.swing.LookAndFeel;
+import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.ActionMapUIResource;
@@ -107,13 +108,34 @@ public class BasicSplitPaneUI extends SplitPaneUI
protected int[] sizes = new int[3];
/**
+ * This is used to determine if we are vertical or horizontal layout.
+ * In the JDK, the BasicVerticalLayoutManager seems to have no more
+ * methods implemented (as of JDK5), so we keep this state here.
+ */
+ private int axis;
+
+ /**
* Creates a new instance. This is package private because the reference
* implementation has no public constructor either. Still, we need to
* call it from BasicVerticalLayoutManager.
*/
BasicHorizontalLayoutManager()
{
- // Nothing to do here.
+ this(SwingConstants.HORIZONTAL);
+ }
+
+ /**
+ * Creates a new instance for a specified axis. This is provided for
+ * compatibility, since the BasicVerticalLayoutManager seems to have
+ * no more implementation in the RI, according to the specs. So
+ * we handle all the axis specific stuff here.
+ *
+ * @param a the axis, either SwingConstants#HORIZONTAL,
+ * or SwingConstants#VERTICAL
+ */
+ BasicHorizontalLayoutManager(int a)
+ {
+ axis = a;
}
/**
@@ -167,7 +189,12 @@ public class BasicSplitPaneUI extends SplitPaneUI
*/
protected int getAvailableSize(Dimension containerSize, Insets insets)
{
- return containerSize.width - insets.left - insets.right;
+ int size;
+ if (axis == SwingConstants.HORIZONTAL)
+ size = containerSize.width - insets.left - insets.right;
+ else
+ size = containerSize.height - insets.top - insets.bottom;
+ return size;
}
/**
@@ -180,9 +207,15 @@ public class BasicSplitPaneUI extends SplitPaneUI
*/
protected int getInitialLocation(Insets insets)
{
+ int loc = 0;
if (insets != null)
- return insets.left;
- return 0;
+ {
+ if (axis == SwingConstants.HORIZONTAL)
+ loc = insets.left;
+ else
+ loc = insets.top;
+ }
+ return loc;
}
/**
@@ -195,7 +228,7 @@ public class BasicSplitPaneUI extends SplitPaneUI
*/
public float getLayoutAlignmentX(Container target)
{
- return target.getAlignmentX();
+ return 0.0f;
}
/**
@@ -208,7 +241,7 @@ public class BasicSplitPaneUI extends SplitPaneUI
*/
public float getLayoutAlignmentY(Container target)
{
- return target.getAlignmentY();
+ return 0.0f;
}
/**
@@ -220,10 +253,19 @@ public class BasicSplitPaneUI extends SplitPaneUI
*/
protected int getPreferredSizeOfComponent(Component c)
{
+ int size = 0;
Dimension dims = c.getPreferredSize();
- if (dims != null)
- return dims.width;
- return 0;
+ if (axis == SwingConstants.HORIZONTAL)
+ {
+ if (dims != null)
+ size = dims.width;
+ }
+ else
+ {
+ if (dims != null)
+ size = dims.height;
+ }
+ return size;
}
/**
@@ -235,7 +277,12 @@ public class BasicSplitPaneUI extends SplitPaneUI
*/
protected int getSizeOfComponent(Component c)
{
- return c.getWidth();
+ int size;
+ if (axis == SwingConstants.HORIZONTAL)
+ size = c.getHeight();
+ else
+ size = c.getWidth();
+ return size;
}
/**
@@ -273,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.
@@ -313,27 +369,30 @@ public class BasicSplitPaneUI extends SplitPaneUI
*/
public Dimension minimumLayoutSize(Container target)
{
+ Dimension dim = new Dimension();
if (target instanceof JSplitPane)
{
- JSplitPane split = (JSplitPane) target;
- Insets insets = target.getInsets();
-
- int height = 0;
- int width = 0;
+ int primary = 0;
+ int secondary = 0;
for (int i = 0; i < components.length; i++)
{
- if (components[i] == null)
- continue;
- Dimension dims = components[i].getMinimumSize();
- if (dims != null)
+ if (components[i] != null)
{
- width += dims.width;
- height = Math.max(height, dims.height);
+ Dimension dims = components[i].getMinimumSize();
+ primary += axis == SwingConstants.HORIZONTAL ? dims.width
+ : dims.height;
+ int sec = axis == SwingConstants.HORIZONTAL ? dims.height
+ : dims.width;
+ secondary = Math.max(sec, secondary);
}
}
- return new Dimension(width, height);
+ int width = axis == SwingConstants.HORIZONTAL ? primary : secondary;
+ int height = axis == SwingConstants.VERTICAL ? secondary : primary;
+
+ Insets i = splitPane.getInsets();
+ dim.setSize(width + i.left + i.right, height + i.top + i.bottom);
}
- return null;
+ return dim;
}
/**
@@ -347,28 +406,30 @@ public class BasicSplitPaneUI extends SplitPaneUI
*/
public Dimension preferredLayoutSize(Container target)
{
+ Dimension dim = new Dimension();
if (target instanceof JSplitPane)
{
- JSplitPane split = (JSplitPane) target;
- Insets insets = target.getInsets();
-
- int height = 0;
- int width = 0;
+ int primary = 0;
+ int secondary = 0;
for (int i = 0; i < components.length; i++)
{
- if (components[i] == null)
- continue;
- Dimension dims = components[i].getPreferredSize();
- if (dims != null)
+ if (components[i] != null)
{
- width += dims.width;
- if (!(components[i] instanceof BasicSplitPaneDivider))
- height = Math.max(height, dims.height);
+ Dimension dims = components[i].getPreferredSize();
+ primary += axis == SwingConstants.HORIZONTAL ? dims.width
+ : dims.height;
+ int sec = axis == SwingConstants.HORIZONTAL ? dims.height
+ : dims.width;
+ secondary = Math.max(sec, secondary);
}
}
- return new Dimension(width, height);
+ int width = axis == SwingConstants.HORIZONTAL ? primary : secondary;
+ int height = axis == SwingConstants.VERTICAL ? secondary : primary;
+
+ Insets i = splitPane.getInsets();
+ dim.setSize(width + i.left + i.right, height + i.top + i.bottom);
}
- return null;
+ return dim;
}
/**
@@ -406,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]);
}
/**
@@ -425,11 +484,23 @@ public class BasicSplitPaneUI extends SplitPaneUI
protected void setComponentToSize(Component c, int size, int location,
Insets insets, Dimension containerSize)
{
- int w = size;
- int h = containerSize.height - insets.top - insets.bottom;
- int x = location;
- int y = insets.top;
- c.setBounds(x, y, w, h);
+ if (insets != null)
+ {
+ if (axis == SwingConstants.HORIZONTAL)
+ c.setBounds(location, insets.top, size,
+ containerSize.height - insets.top - insets.bottom);
+ else
+ c.setBounds(insets.left, location,
+ containerSize.width - insets.left - insets.right,
+ size);
+ }
+ else
+ {
+ if (axis == SwingConstants.HORIZONTAL)
+ c.setBounds(location, 0, size, containerSize.height);
+ else
+ c.setBounds(0, location, containerSize.width, size);
+ }
}
/**
@@ -462,7 +533,6 @@ public class BasicSplitPaneUI extends SplitPaneUI
resetSizeAt(1);
}
components[2] = divider;
- resetSizeAt(2);
}
/**
@@ -485,10 +555,13 @@ public class BasicSplitPaneUI extends SplitPaneUI
int minimumSizeOfComponent(int index)
{
Dimension dims = components[index].getMinimumSize();
+ int size = 0;
if (dims != null)
- return dims.width;
- else
- return 0;
+ if (axis == SwingConstants.HORIZONTAL)
+ size = dims.width;
+ else
+ size = dims.height;
+ return size;
}
} //end BasicHorizontalLayoutManager
@@ -504,163 +577,11 @@ public class BasicSplitPaneUI extends SplitPaneUI
extends BasicHorizontalLayoutManager
{
/**
- * This method returns the height of the container minus the top and
- * bottom inset.
- *
- * @param containerSize The size of the container.
- * @param insets The insets of the container.
- *
- * @return The height minus top and bottom inset.
- */
- protected int getAvailableSize(Dimension containerSize, Insets insets)
- {
- return containerSize.height - insets.top - insets.bottom;
- }
-
- /**
- * This method returns the top inset.
- *
- * @param insets The Insets to use.
- *
- * @return The top inset.
- */
- protected int getInitialLocation(Insets insets)
- {
- return insets.top;
- }
-
- /**
- * This method returns the preferred height of the component.
- *
- * @param c The component to measure.
- *
- * @return The preferred height of the component.
- */
- protected int getPreferredSizeOfComponent(Component c)
- {
- Dimension dims = c.getPreferredSize();
- if (dims != null)
- return dims.height;
- return 0;
- }
-
- /**
- * This method returns the current height of the component.
- *
- * @param c The component to measure.
- *
- * @return The current height of the component.
- */
- protected int getSizeOfComponent(Component c)
- {
- return c.getHeight();
- }
-
- /**
- * This method returns the minimum layout size. The minimum height is the
- * sum of all the components' minimum heights. The minimum width is the
- * maximum of all the components' minimum widths.
- *
- * @param container The container to measure.
- *
- * @return The minimum size.
+ * Creates a new instance.
*/
- public Dimension minimumLayoutSize(Container container)
+ public BasicVerticalLayoutManager()
{
- if (container instanceof JSplitPane)
- {
- JSplitPane split = (JSplitPane) container;
- Insets insets = container.getInsets();
-
- int height = 0;
- int width = 0;
- for (int i = 0; i < components.length; i++)
- {
- if (components[i] == null)
- continue;
- Dimension dims = components[i].getMinimumSize();
- if (dims != null)
- {
- height += dims.height;
- width = Math.max(width, dims.width);
- }
- }
- return new Dimension(width, height);
- }
- return null;
- }
-
- /**
- * This method returns the preferred layout size. The preferred height is
- * the sum of all the components' preferred heights. The preferred width
- * is the maximum of all the components' preferred widths.
- *
- * @param container The container to measure.
- *
- * @return The preferred size.
- */
- public Dimension preferredLayoutSize(Container container)
- {
- if (container instanceof JSplitPane)
- {
- JSplitPane split = (JSplitPane) container;
- Insets insets = container.getInsets();
-
- int height = 0;
- int width = 0;
- for (int i = 0; i < components.length; i++)
- {
- if (components[i] == null)
- continue;
- Dimension dims = components[i].getPreferredSize();
- if (dims != null)
- {
- height += dims.height;
- width = Math.max(width, dims.width);
- }
- }
- return new Dimension(width, height);
- }
- return null;
- }
-
- /**
- * This method sets the bounds of the given component. The y coordinate is
- * the location given. The x coordinate is the left inset. The height is
- * the size given. The width is the container size minus the left and
- * right inset.
- *
- * @param c The component to set bounds for.
- * @param size The height.
- * @param location The y coordinate.
- * @param insets The insets to use.
- * @param containerSize The container's size.
- */
- protected void setComponentToSize(Component c, int size, int location,
- Insets insets, Dimension containerSize)
- {
- int y = location;
- int x = insets.left;
- int h = size;
- int w = containerSize.width - insets.left - insets.right;
- c.setBounds(x, y, w, h);
- }
-
- /**
- * This method returns the minimum height of the component at the given
- * index.
- *
- * @param index The index of the component to check.
- *
- * @return The minimum height of the given component.
- */
- int minimumSizeOfComponent(int index)
- {
- Dimension dims = components[index].getMinimumSize();
- if (dims != null)
- return dims.height;
- else
- return 0;
+ super(SwingConstants.VERTICAL);
}
}
@@ -941,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.
@@ -973,6 +900,7 @@ public class BasicSplitPaneUI extends SplitPaneUI
if (c instanceof JSplitPane)
{
splitPane = (JSplitPane) c;
+ dividerLocationSet = false;
installDefaults();
installListeners();
installKeyboardActions();
@@ -990,6 +918,7 @@ public class BasicSplitPaneUI extends SplitPaneUI
uninstallListeners();
uninstallDefaults();
+ dividerLocationSet = false;
splitPane = null;
}
@@ -1007,8 +936,10 @@ public class BasicSplitPaneUI extends SplitPaneUI
nonContinuousLayoutDivider = createDefaultNonContinuousLayoutDivider();
splitPane.add(divider, JSplitPane.DIVIDER);
- // There is no need to add the nonContinuousLayoutDivider
- splitPane.setDividerSize(UIManager.getInt("SplitPane.dividerSize"));
+ // There is no need to add the nonContinuousLayoutDivider.
+ dividerSize = UIManager.getInt("SplitPane.dividerSize");
+ splitPane.setDividerSize(dividerSize);
+ divider.setDividerSize(dividerSize);
splitPane.setOpaque(true);
}
@@ -1136,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);
}
}
);
@@ -1145,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);
}
}
);
@@ -1436,7 +1371,7 @@ public class BasicSplitPaneUI extends SplitPaneUI
*/
public void setDividerLocation(JSplitPane jc, int location)
{
- dividerLocation = location;
+ dividerLocationSet = true;
splitPane.revalidate();
splitPane.repaint();
}
@@ -1450,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;
}
/**
@@ -1523,7 +1463,7 @@ public class BasicSplitPaneUI extends SplitPaneUI
*/
public Dimension getPreferredSize(JComponent jc)
{
- return layoutManager.preferredLayoutSize((Container) jc);
+ return layoutManager.preferredLayoutSize(jc);
}
/**
@@ -1535,7 +1475,7 @@ public class BasicSplitPaneUI extends SplitPaneUI
*/
public Dimension getMinimumSize(JComponent jc)
{
- return layoutManager.minimumLayoutSize((Container) jc);
+ return layoutManager.minimumLayoutSize(jc);
}
/**
@@ -1547,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/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java
index 11f25167d21..0d1fa1eed9b 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java
@@ -76,7 +76,6 @@ import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.ActionMapUIResource;
import javax.swing.plaf.ComponentUI;
-import javax.swing.plaf.PanelUI;
import javax.swing.plaf.TabbedPaneUI;
import javax.swing.plaf.UIResource;
import javax.swing.text.View;
@@ -252,7 +251,16 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
{
public void mouseReleased(MouseEvent e)
{
- // Nothing to do here.
+ Object s = e.getSource();
+
+ // Event may originate from the viewport in
+ // SCROLL_TAB_LAYOUT mode. It is redisptached
+ // through the tabbed pane then.
+ if (tabPane != e.getSource())
+ {
+ redispatchEvent(e);
+ e.setSource(s);
+ }
}
/**
@@ -264,6 +272,16 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
public void mousePressed(MouseEvent e)
{
Object s = e.getSource();
+
+ // Event may originate from the viewport in
+ // SCROLL_TAB_LAYOUT mode. It is redisptached
+ // through the tabbed pane then.
+ if (tabPane != e.getSource())
+ {
+ redispatchEvent(e);
+ e.setSource(s);
+ }
+
int placement = tabPane.getTabPlacement();
if (s == incrButton)
@@ -298,47 +316,61 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
if(!decrButton.isEnabled())
return;
- // The scroll location may be zero but the offset
- // greater than zero because of an adjustement to
- // make a partially visible tab completely visible.
- if (currentScrollLocation > 0)
- currentScrollLocation--;
-
- // Set the offset back to 0 and recompute it.
- currentScrollOffset = 0;
-
- switch (placement)
- {
- case JTabbedPane.TOP:
- case JTabbedPane.BOTTOM:
- // Take the tab area inset into account.
- if (currentScrollLocation > 0)
- currentScrollOffset = getTabAreaInsets(placement).left;
- // Recompute scroll offset.
- for (int i = 0; i < currentScrollLocation; i++)
- currentScrollOffset += rects[i].width;
- break;
- default:
- // Take the tab area inset into account.
- if (currentScrollLocation > 0)
- currentScrollOffset = getTabAreaInsets(placement).top;
+ // The scroll location may be zero but the offset
+ // greater than zero because of an adjustement to
+ // make a partially visible tab completely visible.
+ if (currentScrollLocation > 0)
+ currentScrollLocation--;
- for (int i = 0; i < currentScrollLocation; i++)
- currentScrollOffset += rects[i].height;
- }
+ // Set the offset back to 0 and recompute it.
+ currentScrollOffset = 0;
+
+ switch (placement)
+ {
+ case JTabbedPane.TOP:
+ case JTabbedPane.BOTTOM:
+ // Take the tab area inset into account.
+ if (currentScrollLocation > 0)
+ currentScrollOffset = getTabAreaInsets(placement).left;
+ // Recompute scroll offset.
+ for (int i = 0; i < currentScrollLocation; i++)
+ currentScrollOffset += rects[i].width;
+ break;
+ default:
+ // Take the tab area inset into account.
+ if (currentScrollLocation > 0)
+ currentScrollOffset = getTabAreaInsets(placement).top;
+
+ for (int i = 0; i < currentScrollLocation; i++)
+ currentScrollOffset += rects[i].height;
+ }
- updateViewPosition();
- updateButtons();
+ updateViewPosition();
+ updateButtons();
- tabPane.repaint();
- } else if (tabPane.isEnabled())
+ tabPane.repaint();
+ }
+ else if (tabPane.isEnabled())
{
int index = tabForCoordinate(tabPane, e.getX(), e.getY());
+ if (!tabPane.isEnabledAt(index))
+ return;
+
if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT
&& s == panel)
+ {
scrollTab(index, placement);
+
+ tabPane.setSelectedIndex(index);
+ tabPane.repaint();
+ }
+ else
+ {
+ tabPane.setSelectedIndex(index);
+ tabPane.revalidate();
+ tabPane.repaint();
+ }
- tabPane.setSelectedIndex(index);
}
}
@@ -347,11 +379,22 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
* Receives notification when the mouse pointer has entered the tabbed
* pane.
*
- * @param ev the mouse event
+ * @param e the mouse event
*/
- public void mouseEntered(MouseEvent ev)
+ public void mouseEntered(MouseEvent e)
{
- int tabIndex = tabForCoordinate(tabPane, ev.getX(), ev.getY());
+ Object s = e.getSource();
+
+ // Event may originate from the viewport in
+ // SCROLL_TAB_LAYOUT mode. It is redisptached
+ // through the tabbed pane then.
+ if (tabPane != e.getSource())
+ {
+ redispatchEvent(e);
+ e.setSource(s);
+ }
+
+ int tabIndex = tabForCoordinate(tabPane, e.getX(), e.getY());
setRolloverTab(tabIndex);
}
@@ -359,10 +402,21 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
* Receives notification when the mouse pointer has exited the tabbed
* pane.
*
- * @param ev the mouse event
+ * @param e the mouse event
*/
- public void mouseExited(MouseEvent ev)
+ public void mouseExited(MouseEvent e)
{
+ Object s = e.getSource();
+
+ // Event may originate from the viewport in
+ // SCROLL_TAB_LAYOUT mode. It is redisptached
+ // through the tabbed pane then.
+ if (tabPane != e.getSource())
+ {
+ redispatchEvent(e);
+ e.setSource(s);
+ }
+
setRolloverTab(-1);
}
@@ -374,9 +428,37 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
*/
public void mouseMoved(MouseEvent ev)
{
+ Object s = ev.getSource();
+
+ if (tabPane != ev.getSource())
+ {
+ ev.setSource(tabPane);
+ tabPane.dispatchEvent(ev);
+
+ ev.setSource(s);
+ }
+
int tabIndex = tabForCoordinate(tabPane, ev.getX(), ev.getY());
setRolloverTab(tabIndex);
}
+
+ /** Modifies the mouse event to originate from
+ * the tabbed pane and redispatches it.
+ *
+ * @param me
+ */
+ void redispatchEvent(MouseEvent me)
+ {
+ me.setSource(tabPane);
+ Point viewPos = viewport.getViewPosition();
+ viewPos.x -= viewport.getX();
+ viewPos.y -= viewport.getY();
+ me.translatePoint(-viewPos.x, -viewPos.y);
+ tabPane.dispatchEvent(me);
+
+ me.translatePoint(viewPos.x, viewPos.y);
+ }
+
}
/**
@@ -396,20 +478,56 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
*/
public void propertyChange(PropertyChangeEvent e)
{
- if (e.getPropertyName().equals("tabLayoutPolicy"))
+ out:
{
- currentScrollLocation = currentScrollOffset = 0;
-
- layoutManager = createLayoutManager();
-
- tabPane.setLayout(layoutManager);
- }
- else if (e.getPropertyName().equals("tabPlacement")
- && tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
- {
- incrButton = createIncreaseButton();
- decrButton = createDecreaseButton();
+ if (e.getPropertyName().equals("tabLayoutPolicy"))
+ {
+ currentScrollLocation = currentScrollOffset = 0;
+
+ layoutManager = createLayoutManager();
+
+ tabPane.setLayout(layoutManager);
+ }
+ else if (e.getPropertyName().equals("tabPlacement")
+ && tabPane.getTabLayoutPolicy()
+ == JTabbedPane.SCROLL_TAB_LAYOUT)
+ {
+ incrButton = createIncreaseButton();
+ decrButton = createDecreaseButton();
+
+ // If the tab placement value was changed of a tabbed pane
+ // in SCROLL_TAB_LAYOUT mode we investigate the change to
+ // implement the following behavior which was observed in
+ // the RI:
+ // The scrolling offset will be reset if we change to
+ // a direction which is orthogonal to the current
+ // direction and stays the same if it is parallel.
+
+ int oldPlacement = ((Integer) e.getOldValue()).intValue();
+ int newPlacement = ((Integer) e.getNewValue()).intValue();
+ switch (newPlacement)
+ {
+ case JTabbedPane.TOP:
+ case JTabbedPane.BOTTOM:
+ if (oldPlacement == JTabbedPane.TOP
+ || oldPlacement == JTabbedPane.BOTTOM)
+ break out;
+
+ currentScrollOffset = getTabAreaInsets(newPlacement).left;
+ break;
+ default:
+ if (oldPlacement == JTabbedPane.LEFT
+ || oldPlacement == JTabbedPane.RIGHT)
+ break out;
+
+ currentScrollOffset = getTabAreaInsets(newPlacement).top;
+ }
+
+ updateViewPosition();
+ updateButtons();
+ }
}
+
tabPane.revalidate();
tabPane.repaint();
}
@@ -784,6 +902,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
default:
tabAreaHeight = calculateTabAreaHeight(tabPlacement, runCount,
maxTabHeight);
+
compX = insets.left + contentBorderInsets.left;
compY = tabAreaHeight + insets.top + contentBorderInsets.top;
}
@@ -838,83 +957,51 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
protected void normalizeTabRuns(int tabPlacement, int tabCount, int start,
int max)
{
- Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
- 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;
+ }
}
}
@@ -1325,7 +1412,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
{
super.layoutContainer(pane);
int tabCount = tabPane.getTabCount();
- Point p = null;
if (tabCount == 0)
return;
int tabPlacement = tabPane.getTabPlacement();
@@ -1512,7 +1598,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
*/
public void updateUI()
{
- setUI((PanelUI) new ScrollingPanelUI());
+ setUI(new ScrollingPanelUI());
}
}
@@ -1892,15 +1978,19 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
final void updateViewPosition()
{
Point p = viewport.getViewPosition();
-
+
+ // The unneeded coordinate must be set to zero
+ // in order to correctly handle placement changes.
switch (tabPane.getTabPlacement())
{
case JTabbedPane.LEFT:
case JTabbedPane.RIGHT:
+ p.x = 0;
p.y = currentScrollOffset;
break;
default:
p.x = currentScrollOffset;
+ p.y = 0;
}
viewport.setViewPosition(p);
@@ -2331,7 +2421,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
paintTabBorder(g, tabPlacement, tabIndex, rect.x, rect.y, rect.width,
rect.height, isSelected);
-
// Layout label.
FontMetrics fm = getFontMetrics();
Icon icon = getIconForTab(tabIndex);
@@ -2369,7 +2458,17 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
Rectangle tabRect, Rectangle iconRect,
Rectangle textRect, boolean isSelected)
{
- SwingUtilities.layoutCompoundLabel(metrics, title, icon,
+ // Reset the icon and text rectangles, as the result is not specified
+ // when the locations are not (0,0).
+ textRect.x = 0;
+ textRect.y = 0;
+ textRect.width = 0;
+ textRect.height = 0;
+ iconRect.x = 0;
+ iconRect.y = 0;
+ iconRect.width = 0;
+ iconRect.height = 0;
+ SwingUtilities.layoutCompoundLabel(tabPane, metrics, title, icon,
SwingConstants.CENTER,
SwingConstants.CENTER,
SwingConstants.CENTER,
@@ -2764,7 +2863,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
int width = tabPane.getWidth();
int height = tabPane.getHeight();
Insets insets = tabPane.getInsets();
- Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
// Calculate coordinates of content area.
int x = insets.left;
@@ -2869,8 +2967,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
int endgap = rects[selectedIndex].y + rects[selectedIndex].height
- currentScrollOffset;
- int diff = 0;
-
if (tabPlacement == SwingConstants.LEFT && startgap >= 0)
{
g.drawLine(x, y, x, startgap);
@@ -2957,8 +3053,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
int endgap = rects[selectedIndex].y + rects[selectedIndex].height
- currentScrollOffset;
- int diff = 0;
-
if (tabPlacement == SwingConstants.RIGHT && startgap >= 0)
{
g.setColor(shadow);
@@ -2988,8 +3082,13 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
}
/**
- * This method returns the tab bounds for the given index.
- *
+ * <p>This method returns the bounds of a tab for the given index
+ * and shifts it by the current scrolling offset if the tabbed
+ * pane is in scrolling tab layout mode.</p>
+ *
+ * <p>Subclassses should retrievs a tab's bounds by this method
+ * if they want to find out whether the tab is currently visible.</p>
+ *
* @param pane The JTabbedPane.
* @param i The index to look for.
*
@@ -3000,6 +3099,26 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
// Need to re-layout container if tab does not exist.
if (i >= rects.length)
layoutManager.layoutContainer(pane);
+
+ // Properly shift coordinates if scrolling has taken
+ // place.
+ if (pane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
+ {
+ Rectangle r = new Rectangle(rects[i]);
+
+ switch(pane.getTabPlacement())
+ {
+ case SwingConstants.TOP:
+ case SwingConstants.BOTTOM:
+ r.x -= currentScrollOffset;
+ break;
+ default:
+ r.y -= currentScrollOffset;
+ }
+
+ return r;
+ }
+
return rects[i];
}
@@ -3048,7 +3167,10 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
}
/**
- * This method returns the tab bounds in the given rectangle.
+ * <p>This method returns the tab bounds in the given rectangle.</p>
+ *
+ * <p>The returned rectangle will be shifted by the current scroll
+ * offset if the tabbed pane is in scrolling tab layout mode.</p>.
*
* @param tabIndex The index to get bounds for.
* @param dest The rectangle to store bounds in.
@@ -3324,21 +3446,20 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
Icon icon = getIconForTab(tabIndex);
Insets insets = getTabInsets(tabPlacement, tabIndex);
- int width = 0;
+ int width = insets.bottom + insets.right + 3;
if (icon != null)
{
- Rectangle vr = new Rectangle();
- Rectangle ir = new Rectangle();
- Rectangle tr = new Rectangle();
- layoutLabel(tabPlacement, getFontMetrics(), tabIndex,
- tabPane.getTitleAt(tabIndex), icon, vr, ir, tr,
- tabIndex == tabPane.getSelectedIndex());
- width = tr.union(ir).width;
+ width += icon.getIconWidth() + textIconGap;
}
- else
- width = metrics.stringWidth(tabPane.getTitleAt(tabIndex));
- width += insets.left + insets.right;
+ View v = getTextViewForTab(tabIndex);
+ if (v != null)
+ width += v.getPreferredSpan(View.X_AXIS);
+ else
+ {
+ String label = tabPane.getTitleAt(tabIndex);
+ width += metrics.stringWidth(label);
+ }
return width;
}
@@ -3377,7 +3498,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
{
Insets insets = getTabAreaInsets(tabPlacement);
int tabAreaHeight = horizRunCount * maxTabHeight
- - (horizRunCount - 1) * tabRunOverlay;
+ - (horizRunCount - 1)
+ * getTabRunOverlay(tabPlacement);
tabAreaHeight += insets.top + insets.bottom;
@@ -3399,7 +3521,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
{
Insets insets = getTabAreaInsets(tabPlacement);
int tabAreaWidth = vertRunCount * maxTabWidth
- - (vertRunCount - 1) * tabRunOverlay;
+ - (vertRunCount - 1)
+ * getTabRunOverlay(tabPlacement);
tabAreaWidth += insets.left + insets.right;
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java
index abe7cab43b3..8a8eeb837fe 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java
@@ -38,8 +38,6 @@ exception statement from your version. */
package javax.swing.plaf.basic;
-import gnu.classpath.NotImplementedException;
-
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
@@ -415,9 +413,8 @@ public class BasicTableHeaderUI extends TableHeaderUI
}
protected void installKeyboardActions()
- throws NotImplementedException
{
- // TODO: Implement this properly.
+ // AFAICS, the RI does nothing here.
}
/**
@@ -448,9 +445,8 @@ public class BasicTableHeaderUI extends TableHeaderUI
}
protected void uninstallKeyboardActions()
- throws NotImplementedException
{
- // TODO: Implement this properly.
+ // AFAICS, the RI does nothing here.
}
/**
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java
index 15be4d57e62..a672173c725 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java
@@ -443,11 +443,14 @@ public class BasicTableUI extends TableUI
public Dimension getPreferredSize(JComponent comp)
{
int prefTotalColumnWidth = 0;
- for (int i = 0; i < table.getColumnCount(); i++)
+ TableColumnModel tcm = table.getColumnModel();
+
+ for (int i = 0; i < tcm.getColumnCount(); i++)
{
- TableColumn col = table.getColumnModel().getColumn(i);
+ TableColumn col = tcm.getColumn(i);
prefTotalColumnWidth += col.getPreferredWidth();
}
+
return new Dimension(prefTotalColumnWidth, getHeight());
}
@@ -455,7 +458,7 @@ public class BasicTableUI extends TableUI
* Returns the table height. This helper method is used by
* {@link #getMinimumSize(JComponent)}, {@link #getPreferredSize(JComponent)}
* and {@link #getMaximumSize(JComponent)} to determine the table height.
- *
+ *
* @return the table height
*/
private int getHeight()
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java
index 8e9c8c949f3..e152a3034d5 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java
@@ -38,6 +38,8 @@ exception statement from your version. */
package javax.swing.plaf.basic;
+import gnu.classpath.SystemProperties;
+
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
@@ -71,6 +73,7 @@ import javax.swing.plaf.InputMapUIResource;
import javax.swing.plaf.TextUI;
import javax.swing.plaf.UIResource;
import javax.swing.text.AbstractDocument;
+import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Caret;
import javax.swing.text.DefaultCaret;
@@ -120,6 +123,140 @@ public abstract class BasicTextUI extends TextUI
}
}
+ private static class FocusHandler
+ implements FocusListener
+ {
+ public void focusGained(FocusEvent e)
+ {
+ // Nothing to do here.
+ }
+ public void focusLost(FocusEvent e)
+ {
+ JTextComponent textComponent = (JTextComponent) e.getComponent();
+ // Integrates Swing text components with the system clipboard:
+ // The idea is that if one wants to copy text around X11-style
+ // (select text and middle-click in the target component) the focus
+ // will move to the new component which gives the old focus owner the
+ // possibility to paste its selection into the clipboard.
+ if (!e.isTemporary()
+ && textComponent.getSelectionStart()
+ != textComponent.getSelectionEnd())
+ {
+ SecurityManager sm = System.getSecurityManager();
+ try
+ {
+ if (sm != null)
+ sm.checkSystemClipboardAccess();
+
+ Clipboard cb = Toolkit.getDefaultToolkit().getSystemSelection();
+ if (cb != null)
+ {
+ StringSelection selection = new StringSelection(
+ textComponent.getSelectedText());
+ cb.setContents(selection, selection);
+ }
+ }
+ catch (SecurityException se)
+ {
+ // Not allowed to access the clipboard: Ignore and
+ // do not access it.
+ }
+ catch (HeadlessException he)
+ {
+ // There is no AWT: Ignore and do not access the
+ // clipboard.
+ }
+ catch (IllegalStateException ise)
+ {
+ // Clipboard is currently unavaible.
+ }
+ }
+ }
+ }
+
+ /**
+ * This FocusListener triggers repaints on focus shift.
+ */
+ private static FocusListener focusListener;
+
+ /**
+ * Receives notifications when properties of the text component change.
+ */
+ private class Handler
+ implements PropertyChangeListener, DocumentListener
+ {
+ /**
+ * Notifies when a property of the text component changes.
+ *
+ * @param event the PropertyChangeEvent describing the change
+ */
+ public void propertyChange(PropertyChangeEvent event)
+ {
+ if (event.getPropertyName().equals("document"))
+ {
+ // Document changed.
+ Object oldValue = event.getOldValue();
+ if (oldValue != null)
+ {
+ Document oldDoc = (Document) oldValue;
+ oldDoc.removeDocumentListener(handler);
+ }
+ Object newValue = event.getNewValue();
+ if (newValue != null)
+ {
+ Document newDoc = (Document) newValue;
+ newDoc.addDocumentListener(handler);
+ }
+ modelChanged();
+ }
+
+ BasicTextUI.this.propertyChange(event);
+ }
+
+ /**
+ * Notification about a document change event.
+ *
+ * @param ev the DocumentEvent describing the change
+ */
+ public void changedUpdate(DocumentEvent ev)
+ {
+ // Updates are forwarded to the View even if 'getVisibleEditorRect'
+ // method returns null. This means the View classes have to be
+ // aware of that possibility.
+ rootView.changedUpdate(ev, getVisibleEditorRect(),
+ rootView.getViewFactory());
+ }
+
+ /**
+ * Notification about a document insert event.
+ *
+ * @param ev the DocumentEvent describing the insertion
+ */
+ public void insertUpdate(DocumentEvent ev)
+ {
+ // Updates are forwarded to the View even if 'getVisibleEditorRect'
+ // method returns null. This means the View classes have to be
+ // aware of that possibility.
+ rootView.insertUpdate(ev, getVisibleEditorRect(),
+ rootView.getViewFactory());
+ }
+
+ /**
+ * Notification about a document removal event.
+ *
+ * @param ev the DocumentEvent describing the removal
+ */
+ public void removeUpdate(DocumentEvent ev)
+ {
+ // Updates are forwarded to the View even if 'getVisibleEditorRect'
+ // method returns null. This means the View classes have to be
+ // aware of that possibility.
+ rootView.removeUpdate(ev, getVisibleEditorRect(),
+ rootView.getViewFactory());
+ }
+
+ }
+
/**
* This view forms the root of the View hierarchy. However, it delegates
* most calls to another View which is the real root of the hierarchy.
@@ -226,19 +363,14 @@ public abstract class BasicTextUI extends TextUI
}
/**
- * Returns the preferred span along the specified <code>axis</code>.
- * This is delegated to the real root view.
- *
- * @param axis the axis for which the preferred span is queried
- *
- * @return the preferred span along the axis
+ * Sets the size of the renderer. This is synchronized because that
+ * potentially triggers layout and we don't want more than one thread
+ * playing with the layout information.
*/
- public float getPreferredSpan(int axis)
+ public synchronized void setSize(float w, float h)
{
if (view != null)
- return view.getPreferredSpan(axis);
-
- return Integer.MAX_VALUE;
+ view.setSize(w, h);
}
/**
@@ -251,8 +383,8 @@ public abstract class BasicTextUI extends TextUI
{
if (view != null)
{
- Rectangle b = s.getBounds();
- view.setSize(b.width, b.height);
+ Rectangle b = s instanceof Rectangle ? (Rectangle) s : s.getBounds();
+ setSize(b.width, b.height);
view.paint(g, s);
}
}
@@ -312,7 +444,8 @@ public abstract class BasicTextUI extends TextUI
*/
public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
{
- view.insertUpdate(ev, shape, vf);
+ if (view != null)
+ view.insertUpdate(ev, shape, vf);
}
/**
@@ -325,7 +458,8 @@ public abstract class BasicTextUI extends TextUI
*/
public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
{
- view.removeUpdate(ev, shape, vf);
+ if (view != null)
+ view.removeUpdate(ev, shape, vf);
}
/**
@@ -338,7 +472,8 @@ public abstract class BasicTextUI extends TextUI
*/
public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
{
- view.changedUpdate(ev, shape, vf);
+ if (view != null)
+ view.changedUpdate(ev, shape, vf);
}
/**
@@ -400,116 +535,73 @@ public abstract class BasicTextUI extends TextUI
{
return textComponent.getDocument();
}
- }
- /**
- * Receives notifications when properties of the text component change.
- */
- private class PropertyChangeHandler implements PropertyChangeListener
- {
/**
- * Notifies when a property of the text component changes.
- *
- * @param event the PropertyChangeEvent describing the change
+ * Returns the attributes, which is null for the RootView.
*/
- public void propertyChange(PropertyChangeEvent event)
+ public AttributeSet getAttributes()
{
- if (event.getPropertyName().equals("document"))
- {
- // Document changed.
- Object oldValue = event.getOldValue();
- if (oldValue != null)
- {
- Document oldDoc = (Document) oldValue;
- oldDoc.removeDocumentListener(documentHandler);
- }
- Object newValue = event.getNewValue();
- if (newValue != null)
- {
- Document newDoc = (Document) newValue;
- newDoc.addDocumentListener(documentHandler);
- }
- modelChanged();
- }
-
- BasicTextUI.this.propertyChange(event);
+ return null;
}
- }
- /**
- * Listens for changes on the underlying model and forwards notifications
- * to the View. This also updates the caret position of the text component.
- *
- * TODO: Maybe this should somehow be handled through EditorKits
- */
- class DocumentHandler implements DocumentListener
- {
/**
- * Notification about a document change event.
- *
- * @param ev the DocumentEvent describing the change
+ * Overridden to forward to the view.
*/
- public void changedUpdate(DocumentEvent ev)
+ public float getPreferredSpan(int axis)
{
- // Updates are forwarded to the View even if 'getVisibleEditorRect'
- // method returns null. This means the View classes have to be
- // aware of that possibility.
- rootView.changedUpdate(ev, getVisibleEditorRect(),
- rootView.getViewFactory());
+ // The RI returns 10 in the degenerate case.
+ float span = 10;
+ if (view != null)
+ span = view.getPreferredSpan(axis);
+ return span;
}
/**
- * Notification about a document insert event.
- *
- * @param ev the DocumentEvent describing the insertion
+ * Overridden to forward to the real view.
*/
- public void insertUpdate(DocumentEvent ev)
+ public float getMinimumSpan(int axis)
{
- // Updates are forwarded to the View even if 'getVisibleEditorRect'
- // method returns null. This means the View classes have to be
- // aware of that possibility.
- rootView.insertUpdate(ev, getVisibleEditorRect(),
- rootView.getViewFactory());
+ // The RI returns 10 in the degenerate case.
+ float span = 10;
+ if (view != null)
+ span = view.getMinimumSpan(axis);
+ return span;
}
/**
- * Notification about a document removal event.
- *
- * @param ev the DocumentEvent describing the removal
+ * Overridden to return Integer.MAX_VALUE.
*/
- public void removeUpdate(DocumentEvent ev)
+ public float getMaximumSpan(int axis)
{
- // Updates are forwarded to the View even if 'getVisibleEditorRect'
- // method returns null. This means the View classes have to be
- // aware of that possibility.
- rootView.removeUpdate(ev, getVisibleEditorRect(),
- rootView.getViewFactory());
+ // The RI returns Integer.MAX_VALUE here, regardless of the real view's
+ // maximum size.
+ return Integer.MAX_VALUE;
}
}
/**
* The EditorKit used by this TextUI.
*/
- // FIXME: should probably be non-static.
- static EditorKit kit = new DefaultEditorKit();
+ private static EditorKit kit;
/**
- * The root view.
+ * The combined event handler for text components.
+ *
+ * This is package private to avoid accessor methods.
*/
- RootView rootView = new RootView();
+ Handler handler;
/**
- * The text component that we handle.
+ * The root view.
+ *
+ * This is package private to avoid accessor methods.
*/
- JTextComponent textComponent;
+ RootView rootView;
/**
- * Receives notification when the model changes.
+ * The text component that we handle.
*/
- private PropertyChangeHandler updateHandler = new PropertyChangeHandler();
-
- /** The DocumentEvent handler. */
- DocumentHandler documentHandler = new DocumentHandler();
+ JTextComponent textComponent;
/**
* Creates a new <code>BasicTextUI</code> instance.
@@ -558,17 +650,31 @@ public abstract class BasicTextUI extends TextUI
public void installUI(final JComponent c)
{
textComponent = (JTextComponent) c;
+
+ if (rootView == null)
+ rootView = new RootView();
+
installDefaults();
- textComponent.addPropertyChangeListener(updateHandler);
+ installFixedDefaults();
+
+ // These listeners must be installed outside of installListeners(),
+ // because overriding installListeners() doesn't prevent installing
+ // these in the RI, but overriding isntallUI() does.
+ if (handler == null)
+ handler = new Handler();
+ textComponent.addPropertyChangeListener(handler);
Document doc = textComponent.getDocument();
if (doc == null)
{
+ // The Handler takes care of installing the necessary listeners
+ // on the document here.
doc = getEditorKit(textComponent).createDefaultDocument();
textComponent.setDocument(doc);
}
else
{
- doc.addDocumentListener(documentHandler);
+ // Must install the document listener.
+ doc.addDocumentListener(handler);
modelChanged();
}
@@ -586,7 +692,6 @@ public abstract class BasicTextUI extends TextUI
LookAndFeel.installColorsAndFont(textComponent, prefix + ".background",
prefix + ".foreground", prefix + ".font");
LookAndFeel.installBorder(textComponent, prefix + ".border");
- textComponent.setMargin(UIManager.getInsets(prefix + ".margin"));
// Some additional text component only properties.
Color color = textComponent.getCaretColor();
@@ -600,7 +705,7 @@ public abstract class BasicTextUI extends TextUI
color = textComponent.getDisabledTextColor();
if (color == null || color instanceof UIResource)
{
- color = UIManager.getColor(prefix + ".inactiveBackground");
+ color = UIManager.getColor(prefix + ".inactiveForeground");
textComponent.setDisabledTextColor(color);
}
color = textComponent.getSelectedTextColor();
@@ -623,6 +728,15 @@ public abstract class BasicTextUI extends TextUI
textComponent.setMargin(margin);
}
+ }
+
+ /**
+ * Installs defaults that can't be overridden by overriding
+ * installDefaults().
+ */
+ private void installFixedDefaults()
+ {
+ String prefix = getPropertyPrefix();
Caret caret = textComponent.getCaret();
if (caret == null || caret instanceof UIResource)
{
@@ -638,64 +752,18 @@ public abstract class BasicTextUI extends TextUI
}
/**
- * This FocusListener triggers repaints on focus shift.
- */
- private FocusListener focuslistener = new FocusListener() {
- public void focusGained(FocusEvent e)
- {
- textComponent.repaint();
- }
- public void focusLost(FocusEvent e)
- {
- textComponent.repaint();
-
- // Integrates Swing text components with the system clipboard:
- // The idea is that if one wants to copy text around X11-style
- // (select text and middle-click in the target component) the focus
- // will move to the new component which gives the old focus owner the
- // possibility to paste its selection into the clipboard.
- if (!e.isTemporary()
- && textComponent.getSelectionStart()
- != textComponent.getSelectionEnd())
- {
- SecurityManager sm = System.getSecurityManager();
- try
- {
- if (sm != null)
- sm.checkSystemClipboardAccess();
-
- Clipboard cb = Toolkit.getDefaultToolkit().getSystemSelection();
- if (cb != null)
- {
- StringSelection selection = new StringSelection(
- textComponent.getSelectedText());
- cb.setContents(selection, selection);
- }
- }
- catch (SecurityException se)
- {
- // Not allowed to access the clipboard: Ignore and
- // do not access it.
- }
- catch (HeadlessException he)
- {
- // There is no AWT: Ignore and do not access the
- // clipboard.
- }
- catch (IllegalStateException ise)
- {
- // Clipboard is currently unavaible.
- }
- }
- }
- };
-
- /**
* Install all listeners on the text component.
*/
protected void installListeners()
{
- textComponent.addFocusListener(focuslistener);
+ //
+ if (SystemProperties.getProperty("gnu.swing.text.no-xlike-clipboard")
+ == null)
+ {
+ if (focusListener == null)
+ focusListener = new FocusHandler();
+ textComponent.addFocusListener(focusListener);
+ }
}
/**
@@ -834,10 +902,12 @@ public abstract class BasicTextUI extends TextUI
*/
public void uninstallUI(final JComponent component)
{
- super.uninstallUI(component);
+ textComponent.removePropertyChangeListener(handler);
+ textComponent.getDocument().removeDocumentListener(handler);
rootView.setView(null);
uninstallDefaults();
+ uninstallFixedDefaults();
uninstallListeners();
uninstallKeyboardActions();
@@ -850,7 +920,29 @@ public abstract class BasicTextUI extends TextUI
*/
protected void uninstallDefaults()
{
- // Do nothing here.
+ if (textComponent.getCaretColor() instanceof UIResource)
+ textComponent.setCaretColor(null);
+ if (textComponent.getSelectionColor() instanceof UIResource)
+ textComponent.setSelectionColor(null);
+ if (textComponent.getDisabledTextColor() instanceof UIResource)
+ textComponent.setDisabledTextColor(null);
+ if (textComponent.getSelectedTextColor() instanceof UIResource)
+ textComponent.setSelectedTextColor(null);
+ LookAndFeel.uninstallBorder(textComponent);
+ if (textComponent.getMargin() instanceof UIResource)
+ textComponent.setMargin(null);
+ }
+
+ /**
+ * Uninstalls additional fixed defaults that were installed
+ * by installFixedDefaults().
+ */
+ private void uninstallFixedDefaults()
+ {
+ if (textComponent.getCaret() instanceof UIResource)
+ textComponent.setCaret(null);
+ if (textComponent.getHighlighter() instanceof UIResource)
+ textComponent.setHighlighter(null);
}
/**
@@ -859,7 +951,10 @@ public abstract class BasicTextUI extends TextUI
*/
protected void uninstallListeners()
{
- textComponent.removeFocusListener(focuslistener);
+ // Don't nullify the focusListener field, as it is static and shared
+ // between components.
+ if (focusListener != null)
+ textComponent.removeFocusListener(focusListener);
}
/**
@@ -891,14 +986,38 @@ public abstract class BasicTextUI extends TextUI
*/
public Dimension getPreferredSize(JComponent c)
{
- View v = getRootView(textComponent);
-
- float w = v.getPreferredSpan(View.X_AXIS);
- float h = v.getPreferredSpan(View.Y_AXIS);
-
+ Dimension d = c.getSize();
Insets i = c.getInsets();
- return new Dimension((int) w + i.left + i.right,
+ // We need to lock here, since we require the view hierarchy to _not_
+ // change in between.
+ float w;
+ float h;
+ Document doc = textComponent.getDocument();
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ if (d.width > (i.left + i.right) && d.height > (i.top + i.bottom))
+ {
+ rootView.setSize(d.width - i.left - i.right,
+ d.height - i.top - i.bottom);
+ }
+ else
+ {
+ // Not laid out yet. Force some pseudo size.
+ rootView.setSize(Integer.MAX_VALUE, Integer.MAX_VALUE);
+ }
+ w = rootView.getPreferredSpan(View.X_AXIS);
+ h = rootView.getPreferredSpan(View.Y_AXIS);
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ Dimension size = new Dimension((int) w + i.left + i.right,
(int) h + i.top + i.bottom);
+ return size;
}
/**
@@ -912,8 +1031,27 @@ public abstract class BasicTextUI extends TextUI
*/
public Dimension getMaximumSize(JComponent c)
{
- // Sun's implementation returns Integer.MAX_VALUE here, so do we.
- return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
+ Dimension d = new Dimension();
+ Insets i = c.getInsets();
+ Document doc = textComponent.getDocument();
+ // We need to lock here, since we require the view hierarchy to _not_
+ // change in between.
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ // Check for overflow here.
+ d.width = (int) Math.min((long) rootView.getMaximumSpan(View.X_AXIS)
+ + i.left + i.right, Integer.MAX_VALUE);
+ d.height = (int) Math.min((long) rootView.getMaximumSpan(View.Y_AXIS)
+ + i.top + i.bottom, Integer.MAX_VALUE);
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ return d;
}
/**
@@ -924,8 +1062,26 @@ public abstract class BasicTextUI extends TextUI
*/
public Dimension getMinimumSize(JComponent c)
{
+ Dimension d = new Dimension();
+ Document doc = textComponent.getDocument();
+ // We need to lock here, since we require the view hierarchy to _not_
+ // change in between.
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ d.width = (int) rootView.getMinimumSpan(View.X_AXIS);
+ d.height = (int) rootView.getMinimumSpan(View.Y_AXIS);
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
Insets i = c.getInsets();
- return new Dimension(i.left + i.right, i.top + i.bottom);
+ d.width += i.left + i.right;
+ d.height += i.top + i.bottom;
+ return d;
}
/**
@@ -946,7 +1102,6 @@ public abstract class BasicTextUI extends TextUI
AbstractDocument aDoc = (AbstractDocument) doc;
aDoc.readLock();
}
-
paintSafely(g);
}
finally
@@ -996,7 +1151,6 @@ public abstract class BasicTextUI extends TextUI
g.setColor(oldColor);
}
-
rootView.paint(g, getVisibleEditorRect());
if (caret != null && textComponent.hasFocus())
@@ -1104,6 +1258,8 @@ public abstract class BasicTextUI extends TextUI
*/
public EditorKit getEditorKit(JTextComponent t)
{
+ if (kit == null)
+ kit = new DefaultEditorKit();
return kit;
}
@@ -1126,12 +1282,26 @@ public abstract class BasicTextUI extends TextUI
Position.Bias[] biasRet)
throws BadLocationException
{
- // A comment in the spec of NavigationFilter.getNextVisualPositionFrom()
- // suggests that this method should be implemented by forwarding the call
- // the root view.
- return rootView.getNextVisualPositionFrom(pos, b,
- getVisibleEditorRect(),
- direction, biasRet);
+ int offset = -1;
+ Document doc = textComponent.getDocument();
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ Rectangle alloc = getVisibleEditorRect();
+ if (alloc != null)
+ {
+ rootView.setSize(alloc.width, alloc.height);
+ offset = rootView.getNextVisualPositionFrom(pos, b, alloc,
+ direction, biasRet);
+ }
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ return offset;
}
/**
@@ -1224,7 +1394,7 @@ public abstract class BasicTextUI extends TextUI
*/
public int viewToModel(JTextComponent t, Point pt)
{
- return viewToModel(t, pt, null);
+ return viewToModel(t, pt, new Position.Bias[1]);
}
/**
@@ -1241,7 +1411,25 @@ public abstract class BasicTextUI extends TextUI
*/
public int viewToModel(JTextComponent t, Point pt, Position.Bias[] biasReturn)
{
- return rootView.viewToModel(pt.x, pt.y, getVisibleEditorRect(), biasReturn);
+ int offset = -1;
+ Document doc = textComponent.getDocument();
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ Rectangle alloc = getVisibleEditorRect();
+ if (alloc != null)
+ {
+ rootView.setSize(alloc.width, alloc.height);
+ offset = rootView.viewToModel(pt.x, pt.y, alloc, biasReturn);
+ }
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ return offset;
}
/**
@@ -1273,6 +1461,11 @@ public abstract class BasicTextUI extends TextUI
}
/**
+ * A cached Insets instance to be reused below.
+ */
+ private Insets cachedInsets;
+
+ /**
* Returns the allocation to give the root view.
*
* @return the allocation to give the root view
@@ -1290,7 +1483,7 @@ public abstract class BasicTextUI extends TextUI
if (width <= 0 || height <= 0)
return null;
- Insets insets = textComponent.getInsets();
+ Insets insets = textComponent.getInsets(cachedInsets);
return new Rectangle(insets.left, insets.top,
width - insets.left - insets.right,
height - insets.top - insets.bottom);
@@ -1341,4 +1534,5 @@ public abstract class BasicTextUI extends TextUI
{
// The default implementation does nothing.
}
+
}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java
index 1c36b408d5a..7be69ec2576 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java
@@ -898,7 +898,8 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants
b.setRolloverEnabled(false);
// Save old border in hashtable.
- borders.put(b, b.getBorder());
+ if (b.getBorder() != null)
+ borders.put(b, b.getBorder());
b.setBorder(nonRolloverBorder);
}
@@ -932,7 +933,8 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants
b.setRolloverEnabled(false);
// Save old border in hashtable.
- borders.put(b, b.getBorder());
+ if (b.getBorder() != null)
+ borders.put(b, b.getBorder());
b.setBorder(rolloverBorder);
}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicToolTipUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicToolTipUI.java
index 5cec2e33365..94e7bc322f6 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicToolTipUI.java
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicToolTipUI.java
@@ -40,19 +40,20 @@ package javax.swing.plaf.basic;
import java.awt.Color;
import java.awt.Dimension;
+import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
-import java.awt.Toolkit;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
import javax.swing.JComponent;
import javax.swing.JToolTip;
import javax.swing.LookAndFeel;
-import javax.swing.SwingConstants;
-import javax.swing.SwingUtilities;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.ToolTipUI;
+import javax.swing.text.View;
/**
* This is the Basic Look and Feel UI class for JToolTip.
@@ -60,6 +61,28 @@ import javax.swing.plaf.ToolTipUI;
public class BasicToolTipUI extends ToolTipUI
{
+ /**
+ * Receives notification when a property of the JToolTip changes.
+ * This updates the HTML renderer if appropriate.
+ */
+ private class PropertyChangeHandler
+ implements PropertyChangeListener
+ {
+
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ String prop = e.getPropertyName();
+ if (prop.equals("tiptext") || prop.equals("font")
+ || prop.equals("foreground"))
+ {
+ JToolTip tip = (JToolTip) e.getSource();
+ String text = tip.getTipText();
+ BasicHTML.updateRenderer(tip, text);
+ }
+ }
+
+ }
+
/** The shared instance of BasicToolTipUI used for all ToolTips. */
private static BasicToolTipUI shared;
@@ -67,6 +90,11 @@ public class BasicToolTipUI extends ToolTipUI
private String text;
/**
+ * Handles property changes.
+ */
+ private PropertyChangeListener propertyChangeHandler;
+
+ /**
* Creates a new BasicToolTipUI object.
*/
public BasicToolTipUI()
@@ -98,7 +126,12 @@ public class BasicToolTipUI extends ToolTipUI
*/
public Dimension getMaximumSize(JComponent c)
{
- return getPreferredSize(c);
+ Dimension d = getPreferredSize(c);
+ View view = (View) c.getClientProperty(BasicHTML.propertyKey);
+ if (view != null)
+ d.width += view.getMaximumSpan(View.X_AXIS)
+ - view.getPreferredSpan(View.X_AXIS);
+ return d;
}
/**
@@ -110,7 +143,12 @@ public class BasicToolTipUI extends ToolTipUI
*/
public Dimension getMinimumSize(JComponent c)
{
- return getPreferredSize(c);
+ Dimension d = getPreferredSize(c);
+ View view = (View) c.getClientProperty(BasicHTML.propertyKey);
+ if (view != null)
+ d.width -= view.getPreferredSpan(View.X_AXIS)
+ - view.getMinimumSpan(View.X_AXIS);
+ return d;
}
/**
@@ -123,22 +161,25 @@ public class BasicToolTipUI extends ToolTipUI
public Dimension getPreferredSize(JComponent c)
{
JToolTip tip = (JToolTip) c;
- FontMetrics fm;
- Toolkit g = tip.getToolkit();
- text = tip.getTipText();
-
- Rectangle vr = new Rectangle();
- Rectangle ir = new Rectangle();
- Rectangle tr = new Rectangle();
- Insets insets = tip.getInsets();
- fm = g.getFontMetrics(tip.getFont());
- SwingUtilities.layoutCompoundLabel(tip, fm, text, null,
- SwingConstants.CENTER,
- SwingConstants.CENTER,
- SwingConstants.CENTER,
- SwingConstants.CENTER, vr, ir, tr, 0);
- return new Dimension(insets.left + tr.width + insets.right,
- insets.top + tr.height + insets.bottom);
+ String str = tip.getTipText();
+ FontMetrics fm = c.getFontMetrics(c.getFont());
+ Insets i = c.getInsets();
+ Dimension d = new Dimension(i.left + i.right, i.top + i.bottom);
+ if (str != null && ! str.equals(""))
+ {
+ View view = (View) c.getClientProperty(BasicHTML.propertyKey);
+ if (view != null)
+ {
+ d.width += (int) view.getPreferredSpan(View.X_AXIS);
+ d.height += (int) view.getPreferredSpan(View.Y_AXIS);
+ }
+ else
+ {
+ d.width += fm.stringWidth(str) + 6;
+ d.height += fm.getHeight();
+ }
+ }
+ return d;
}
/**
@@ -160,7 +201,8 @@ public class BasicToolTipUI extends ToolTipUI
*/
protected void installListeners(JComponent c)
{
- // TODO: Implement this properly.
+ propertyChangeHandler = new PropertyChangeHandler();
+ c.addPropertyChangeListener(propertyChangeHandler);
}
/**
@@ -172,6 +214,7 @@ public class BasicToolTipUI extends ToolTipUI
{
c.setOpaque(true);
installDefaults(c);
+ BasicHTML.updateRenderer(c, ((JToolTip) c).getTipText());
installListeners(c);
}
@@ -186,26 +229,25 @@ public class BasicToolTipUI extends ToolTipUI
JToolTip tip = (JToolTip) c;
String text = tip.getTipText();
- Toolkit t = tip.getToolkit();
- if (text == null)
- return;
-
- Rectangle vr = new Rectangle();
- vr = SwingUtilities.calculateInnerArea(tip, vr);
- Rectangle ir = new Rectangle();
- Rectangle tr = new Rectangle();
- FontMetrics fm = t.getFontMetrics(tip.getFont());
+ Font font = c.getFont();
+ FontMetrics fm = c.getFontMetrics(font);
int ascent = fm.getAscent();
- SwingUtilities.layoutCompoundLabel(tip, fm, text, null,
- SwingConstants.CENTER,
- SwingConstants.CENTER,
- SwingConstants.CENTER,
- SwingConstants.CENTER, vr, ir, tr, 0);
+ Insets i = c.getInsets();
+ Dimension size = c.getSize();
+ Rectangle paintR = new Rectangle(i.left, i.top,
+ size.width - i.left - i.right,
+ size.height - i.top - i.bottom);
Color saved = g.getColor();
+ Font oldFont = g.getFont();
g.setColor(Color.BLACK);
- g.drawString(text, vr.x, vr.y + ascent);
+ View view = (View) c.getClientProperty(BasicHTML.propertyKey);
+ if (view != null)
+ view.paint(g, paintR);
+ else
+ g.drawString(text, paintR.x + 3, paintR.y + ascent);
+ g.setFont(oldFont);
g.setColor(saved);
}
@@ -229,7 +271,11 @@ public class BasicToolTipUI extends ToolTipUI
*/
protected void uninstallListeners(JComponent c)
{
- // TODO: Implement this properly.
+ if (propertyChangeHandler != null)
+ {
+ c.removePropertyChangeListener(propertyChangeHandler);
+ propertyChangeHandler = null;
+ }
}
/**
@@ -240,6 +286,7 @@ public class BasicToolTipUI extends ToolTipUI
public void uninstallUI(JComponent c)
{
uninstallDefaults(c);
+ BasicHTML.updateRenderer(c, "");
uninstallListeners(c);
}
}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java
index 9a193986ac5..5b0ffce09b6 100644
--- a/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java
@@ -38,17 +38,16 @@
package javax.swing.plaf.basic;
-import gnu.classpath.NotImplementedException;
import gnu.javax.swing.tree.GnuPath;
import java.awt.Color;
import java.awt.Component;
+import java.awt.Container;
import java.awt.Dimension;
-import java.awt.Font;
-import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Label;
+import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@@ -196,7 +195,7 @@ public class BasicTreeUI
protected AbstractLayoutCache treeState;
/** Used for minimizing the drawing of vertical lines. */
- protected Hashtable drawingCache;
+ protected Hashtable<TreePath, Boolean> drawingCache;
/**
* True if doing optimizations for a largeModel. Subclasses that don't support
@@ -275,13 +274,6 @@ public class BasicTreeUI
TreeModelListener treeModelListener;
/**
- * This timer fires the editing action after about 1200 ms if not reset during
- * that time. It handles the editing start with the single mouse click (and
- * not the double mouse click) on the selected tree node.
- */
- Timer startEditTimer;
-
- /**
* The zero size icon, used for expand controls, if they are not visible.
*/
static Icon nullIcon;
@@ -428,6 +420,7 @@ public class BasicTreeUI
{
if (largeModel != this.largeModel)
{
+ completeEditing();
tree.removeComponentListener(componentListener);
this.largeModel = largeModel;
tree.addComponentListener(componentListener);
@@ -451,6 +444,7 @@ public class BasicTreeUI
*/
protected void setRowHeight(int rowHeight)
{
+ completeEditing();
if (rowHeight == 0)
rowHeight = getMaxHeight(tree);
treeState.setRowHeight(rowHeight);
@@ -544,6 +538,7 @@ public class BasicTreeUI
*/
protected void setRootVisible(boolean newValue)
{
+ completeEditing();
tree.setRootVisible(newValue);
}
@@ -590,8 +585,7 @@ public class BasicTreeUI
*/
protected void setCellEditor(TreeCellEditor editor)
{
- cellEditor = editor;
- createdCellEditor = true;
+ updateCellEditor();
}
/**
@@ -611,7 +605,7 @@ public class BasicTreeUI
*/
protected void setEditable(boolean newValue)
{
- tree.setEditable(newValue);
+ updateCellEditor();
}
/**
@@ -632,6 +626,7 @@ public class BasicTreeUI
*/
protected void setSelectionModel(TreeSelectionModel newLSM)
{
+ completeEditing();
if (newLSM != null)
{
treeSelectionModel = newLSM;
@@ -787,12 +782,13 @@ public class BasicTreeUI
*/
public boolean stopEditing(JTree tree)
{
- if (isEditing(tree))
+ boolean ret = false;
+ if (editingComponent != null && cellEditor.stopCellEditing())
{
completeEditing(false, false, true);
- finish();
+ ret = true;
}
- return ! isEditing(tree);
+ return ret;
}
/**
@@ -805,8 +801,8 @@ public class BasicTreeUI
// There is no need to send the cancel message to the editor,
// as the cancellation event itself arrives from it. This would
// only be necessary when cancelling the editing programatically.
- completeEditing(false, false, false);
- finish();
+ if (editingComponent != null)
+ completeEditing(false, true, false);
}
/**
@@ -818,7 +814,9 @@ public class BasicTreeUI
*/
public void startEditingAtPath(JTree tree, TreePath path)
{
- startEditing(path, null);
+ tree.scrollPathToVisible(path);
+ if (path != null && tree.isVisible(path))
+ startEditing(path, null);
}
/**
@@ -842,6 +840,7 @@ public class BasicTreeUI
preferredSize = new Dimension();
largeModel = tree.isLargeModel();
preferredSize = new Dimension();
+ stopEditingInCompleteEditing = true;
setModel(tree.getModel());
}
@@ -1136,6 +1135,7 @@ public class BasicTreeUI
*/
protected void updateExpandedDescendants(TreePath path)
{
+ completeEditing();
Enumeration expanded = tree.getExpandedDescendants(path);
while (expanded.hasMoreElements())
treeState.setExpandedState((TreePath) expanded.nextElement(), true);
@@ -1167,9 +1167,33 @@ public class BasicTreeUI
*/
protected void updateCellEditor()
{
- if (tree.isEditable() && cellEditor == null)
- setCellEditor(createDefaultCellEditor());
- createdCellEditor = true;
+ completeEditing();
+ TreeCellEditor newEd = null;
+ if (tree != null && tree.isEditable())
+ {
+ newEd = tree.getCellEditor();
+ if (newEd == null)
+ {
+ newEd = createDefaultCellEditor();
+ if (newEd != null)
+ {
+ tree.setCellEditor(newEd);
+ createdCellEditor = true;
+ }
+ }
+ }
+ // Update listeners.
+ if (newEd != cellEditor)
+ {
+ if (cellEditor != null && cellEditorListener != null)
+ cellEditor.removeCellEditorListener(cellEditorListener);
+ cellEditor = newEd;
+ if (cellEditorListener == null)
+ cellEditorListener = createCellEditorListener();
+ if (cellEditor != null && cellEditorListener != null)
+ cellEditor.addCellEditorListener(cellEditorListener);
+ createdCellEditor = false;
+ }
}
/**
@@ -1563,12 +1587,15 @@ public class BasicTreeUI
for (int i = startIndex; i <= endIndex; i++, k++)
{
path[k] = treeState.getPathForRow(i);
- isLeaf[k] = treeModel.isLeaf(path[k].getLastPathComponent());
- isExpanded[k] = tree.isExpanded(path[k]);
- bounds[k] = getPathBounds(tree, path[k]);
-
- paintHorizontalPartOfLeg(g, clip, insets, bounds[k], path[k], i,
- isExpanded[k], false, isLeaf[k]);
+ if (path[k] != null)
+ {
+ isLeaf[k] = treeModel.isLeaf(path[k].getLastPathComponent());
+ isExpanded[k] = tree.isExpanded(path[k]);
+ bounds[k] = getPathBounds(tree, path[k]);
+
+ paintHorizontalPartOfLeg(g, clip, insets, bounds[k], path[k],
+ i, isExpanded[k], false, isLeaf[k]);
+ }
if (isLastChild(path[k]))
paintVerticalPartOfLeg(g, clip, insets, path[k]);
}
@@ -1576,8 +1603,9 @@ public class BasicTreeUI
k = 0;
for (int i = startIndex; i <= endIndex; i++, k++)
{
- paintRow(g, clip, insets, bounds[k], path[k], i, isExpanded[k],
- false, isLeaf[k]);
+ if (path[k] != null)
+ paintRow(g, clip, insets, bounds[k], path[k], i, isExpanded[k],
+ false, isLeaf[k]);
}
}
}
@@ -1587,7 +1615,9 @@ public class BasicTreeUI
*/
private boolean isLastChild(TreePath path)
{
- if (path instanceof GnuPath)
+ if (path == null)
+ return false;
+ else if (path instanceof GnuPath)
{
// Except the seldom case when the layout cache is changed, this
// optimized code will be executed.
@@ -1719,6 +1749,10 @@ public class BasicTreeUI
*/
protected void completeEditing()
{
+ if (tree.getInvokesStopCellEditing() && stopEditingInCompleteEditing
+ && editingComponent != null)
+ cellEditor.stopCellEditing();
+
completeEditing(false, true, false);
}
@@ -1736,28 +1770,35 @@ public class BasicTreeUI
boolean messageTree)
{
// Make no attempt to complete the non existing editing session.
- if (!isEditing(tree))
- return;
-
- if (messageStop)
+ if (stopEditingInCompleteEditing && editingComponent != null)
{
- getCellEditor().stopCellEditing();
- stopEditingInCompleteEditing = true;
- }
-
- if (messageCancel)
- {
- getCellEditor().cancelCellEditing();
- stopEditingInCompleteEditing = true;
- }
+ Component comp = editingComponent;
+ TreePath p = editingPath;
+ editingComponent = null;
+ editingPath = null;
+ if (messageStop)
+ cellEditor.stopCellEditing();
+ else if (messageCancel)
+ cellEditor.cancelCellEditing();
+
+ tree.remove(comp);
+
+ if (editorHasDifferentSize)
+ {
+ treeState.invalidatePathBounds(p);
+ updateSize();
+ }
+ else
+ {
+ // Need to refresh the tree.
+ Rectangle b = getPathBounds(tree, p);
+ tree.repaint(0, b.y, tree.getWidth(), b.height);
+ }
- if (messageTree)
- {
- TreeCellEditor editor = getCellEditor();
- if (editor != null)
+ if (messageTree)
{
- Object value = editor.getCellEditorValue();
- treeModel.valueForPathChanged(tree.getLeadSelectionPath(), value);
+ Object value = cellEditor.getCellEditorValue();
+ treeModel.valueForPathChanged(p, value);
}
}
}
@@ -1772,47 +1813,105 @@ public class BasicTreeUI
*/
protected boolean startEditing(TreePath path, MouseEvent event)
{
- updateCellEditor();
- TreeCellEditor ed = getCellEditor();
+ // Maybe cancel editing.
+ if (isEditing(tree) && tree.getInvokesStopCellEditing()
+ && ! stopEditing(tree))
+ return false;
- if (ed != null && (event == EDIT || ed.shouldSelectCell(event))
- && ed.isCellEditable(event))
+ completeEditing();
+ TreeCellEditor ed = cellEditor;
+ if (ed != null && tree.isPathEditable(path))
{
- Rectangle bounds = getPathBounds(tree, path);
-
- // Extend the right boundary till the tree width.
- bounds.width = tree.getWidth() - bounds.x;
-
- editingPath = path;
- editingRow = tree.getRowForPath(editingPath);
-
- Object value = editingPath.getLastPathComponent();
-
- stopEditingInCompleteEditing = false;
- boolean expanded = tree.isExpanded(editingPath);
- isEditing = true;
- editingComponent = ed.getTreeCellEditorComponent(tree, value, true,
- expanded,
- isLeaf(editingRow),
- editingRow);
-
- // Remove all previous components (if still present). Only one
- // container with the editing component inside is allowed in the tree.
- tree.removeAll();
-
- // The editing component must be added to its container. We add the
- // container, not the editing component itself.
- Component container = editingComponent.getParent();
- container.setBounds(bounds);
- tree.add(container);
- editingComponent.requestFocus();
+ if (ed.isCellEditable(event))
+ {
+ editingRow = getRowForPath(tree, path);
+ Object value = path.getLastPathComponent();
+ boolean isSelected = tree.isPathSelected(path);
+ boolean isExpanded = tree.isExpanded(editingPath);
+ boolean isLeaf = treeModel.isLeaf(value);
+ editingComponent = ed.getTreeCellEditorComponent(tree, value,
+ isSelected,
+ isExpanded,
+ isLeaf,
+ editingRow);
+
+ Rectangle bounds = getPathBounds(tree, path);
+
+ Dimension size = editingComponent.getPreferredSize();
+ int rowHeight = getRowHeight();
+ if (size.height != bounds.height && rowHeight > 0)
+ size.height = rowHeight;
+
+ if (size.width != bounds.width || size.height != bounds.height)
+ {
+ editorHasDifferentSize = true;
+ treeState.invalidatePathBounds(path);
+ updateSize();
+ }
+ else
+ editorHasDifferentSize = false;
+
+ // The editing component must be added to its container. We add the
+ // container, not the editing component itself.
+ tree.add(editingComponent);
+ editingComponent.setBounds(bounds.x, bounds.y, size.width,
+ size.height);
+ editingComponent.validate();
+ editingPath = path;
+
+ if (ed.shouldSelectCell(event))
+ {
+ stopEditingInCompleteEditing = false;
+ tree.setSelectionRow(editingRow);
+ stopEditingInCompleteEditing = true;
+ }
+
+ editorRequestFocus(editingComponent);
+ // Register MouseInputHandler to redispatch initial mouse events
+ // correctly.
+ if (event instanceof MouseEvent)
+ {
+ Point p = SwingUtilities.convertPoint(tree, event.getX(), event.getY(),
+ editingComponent);
+ Component active =
+ SwingUtilities.getDeepestComponentAt(editingComponent, p.x, p.y);
+ if (active != null)
+ {
+ MouseInputHandler ih = new MouseInputHandler(tree, active, event);
+
+ }
+ }
- return true;
+ return true;
+ }
+ else
+ editingComponent = null;
}
return false;
}
/**
+ * Requests focus on the editor. The method is necessary since the
+ * DefaultTreeCellEditor returns a container that contains the
+ * actual editor, and we want to request focus on the editor, not the
+ * container.
+ */
+ private void editorRequestFocus(Component c)
+ {
+ if (c instanceof Container)
+ {
+ // TODO: Maybe do something more reasonable here, like queriying the
+ // FocusTraversalPolicy.
+ Container cont = (Container) c;
+ if (cont.getComponentCount() > 0)
+ cont.getComponent(0).requestFocus();
+ }
+ else if (c.isFocusable())
+ c.requestFocus();
+
+ }
+
+ /**
* If the <code>mouseX</code> and <code>mouseY</code> are in the expand or
* collapse region of the row, this will toggle the row.
*
@@ -2180,7 +2279,7 @@ public class BasicTreeUI
*/
public void editingStopped(ChangeEvent e)
{
- stopEditing(tree);
+ completeEditing(false, false, true);
}
/**
@@ -2191,7 +2290,7 @@ public class BasicTreeUI
*/
public void editingCanceled(ChangeEvent e)
{
- cancelEditing(tree);
+ completeEditing(false, false, false);
}
} // CellEditorHandler
@@ -2347,9 +2446,15 @@ public class BasicTreeUI
* events.
*/
public class MouseHandler
- extends MouseAdapter
- implements MouseMotionListener
+ extends MouseAdapter
+ implements MouseMotionListener
{
+
+ /**
+ * If the cell has been selected on mouse press.
+ */
+ private boolean selectedOnPress;
+
/**
* Constructor
*/
@@ -2365,76 +2470,15 @@ public class BasicTreeUI
*/
public void mousePressed(MouseEvent e)
{
- // Any mouse click cancels the previous waiting edit action, initiated
- // by the single click on the selected node.
- if (startEditTimer != null)
+ if (! e.isConsumed())
{
- startEditTimer.stop();
- startEditTimer = null;
+ handleEvent(e);
+ selectedOnPress = true;
}
-
- if (tree != null && tree.isEnabled())
+ else
{
- // Always end the current editing session if clicked on the
- // tree and outside the bounds of the editing component.
- if (isEditing(tree))
- if (!stopEditing(tree))
- // Return if we have failed to cancel the editing session.
- return;
-
- int x = e.getX();
- int y = e.getY();
- TreePath path = getClosestPathForLocation(tree, x, y);
-
- if (path != null)
- {
- Rectangle bounds = getPathBounds(tree, path);
- if (SwingUtilities.isLeftMouseButton(e))
- checkForClickInExpandControl(path, x, y);
-
- if (x > bounds.x && x <= (bounds.x + bounds.width))
- {
- TreePath currentLead = tree.getLeadSelectionPath();
- if (currentLead != null && currentLead.equals(path)
- && e.getClickCount() == 1 && tree.isEditable())
- {
- // Schedule the editing session.
- final TreePath editPath = path;
-
- // The code below handles the required click-pause-click
- // functionality which must be present in the tree UI.
- // If the next click comes after the
- // time longer than the double click interval AND
- // the same node stays focused for the WAIT_TILL_EDITING
- // duration, the timer starts the editing session.
- if (startEditTimer != null)
- startEditTimer.stop();
-
- startEditTimer = new Timer(WAIT_TILL_EDITING,
- new ActionListener()
- {
- public void actionPerformed(ActionEvent e)
- {
- startEditing(editPath, EDIT);
- }
- });
-
- startEditTimer.setRepeats(false);
- startEditTimer.start();
- }
- else
- {
- if (e.getClickCount() == 2)
- toggleExpandState(path);
- else
- selectPathForEvent(path, e);
- }
- }
- }
+ selectedOnPress = false;
}
-
- // We need to request the focus.
- tree.requestFocusInWindow();
}
/**
@@ -2446,9 +2490,8 @@ public class BasicTreeUI
* @param e is the mouse event that occured
*/
public void mouseDragged(MouseEvent e)
- throws NotImplementedException
{
- // TODO: What should be done here, if anything?
+ // Nothing to do here.
}
/**
@@ -2458,9 +2501,8 @@ public class BasicTreeUI
* @param e the mouse event that occured
*/
public void mouseMoved(MouseEvent e)
- throws NotImplementedException
{
- // TODO: What should be done here, if anything?
+ // Nothing to do here.
}
/**
@@ -2469,9 +2511,46 @@ public class BasicTreeUI
* @param e is the mouse event that occured
*/
public void mouseReleased(MouseEvent e)
- throws NotImplementedException
{
- // TODO: What should be done here, if anything?
+ if (! e.isConsumed() && ! selectedOnPress)
+ handleEvent(e);
+ }
+
+ /**
+ * Handles press and release events.
+ *
+ * @param e the mouse event
+ */
+ private void handleEvent(MouseEvent e)
+ {
+ if (tree != null && tree.isEnabled())
+ {
+ // Maybe stop editing.
+ if (isEditing(tree) && tree.getInvokesStopCellEditing()
+ && ! stopEditing(tree))
+ return;
+
+ // Explicitly request focus.
+ tree.requestFocusInWindow();
+
+ int x = e.getX();
+ int y = e.getY();
+ TreePath path = getClosestPathForLocation(tree, x, y);
+ if (path != null)
+ {
+ Rectangle b = getPathBounds(tree, path);
+ if (y <= b.y + b.height)
+ {
+ if (SwingUtilities.isLeftMouseButton(e))
+ checkForClickInExpandControl(path, x, y);
+ if (x > b.x && x <= b.x + b.width)
+ {
+ if (! startEditing(path, e))
+ selectPathForEvent(path, e);
+ }
+ }
+ }
+ }
}
}
@@ -2501,6 +2580,9 @@ public class BasicTreeUI
{
this.source = source;
this.destination = destination;
+ source.addMouseListener(this);
+ source.addMouseMotionListener(this);
+ dispatch(e);
}
/**
@@ -2510,9 +2592,8 @@ public class BasicTreeUI
* @param e mouse event that occured
*/
public void mouseClicked(MouseEvent e)
- throws NotImplementedException
{
- // TODO: What should be done here, if anything?
+ dispatch(e);
}
/**
@@ -2521,9 +2602,8 @@ public class BasicTreeUI
* @param e mouse event that occured
*/
public void mousePressed(MouseEvent e)
- throws NotImplementedException
{
- // TODO: What should be done here, if anything?
+ // Nothing to do here.
}
/**
@@ -2532,9 +2612,9 @@ public class BasicTreeUI
* @param e mouse event that occured
*/
public void mouseReleased(MouseEvent e)
- throws NotImplementedException
{
- // TODO: What should be done here, if anything?
+ dispatch(e);
+ removeFromSource();
}
/**
@@ -2543,9 +2623,9 @@ public class BasicTreeUI
* @param e mouse event that occured
*/
public void mouseEntered(MouseEvent e)
- throws NotImplementedException
{
- // TODO: What should be done here, if anything?
+ if (! SwingUtilities.isLeftMouseButton(e))
+ removeFromSource();
}
/**
@@ -2554,9 +2634,9 @@ public class BasicTreeUI
* @param e mouse event that occured
*/
public void mouseExited(MouseEvent e)
- throws NotImplementedException
{
- // TODO: What should be done here, if anything?
+ if (! SwingUtilities.isLeftMouseButton(e))
+ removeFromSource();
}
/**
@@ -2568,9 +2648,8 @@ public class BasicTreeUI
* @param e mouse event that occured
*/
public void mouseDragged(MouseEvent e)
- throws NotImplementedException
{
- // TODO: What should be done here, if anything?
+ dispatch(e);
}
/**
@@ -2580,18 +2659,37 @@ public class BasicTreeUI
* @param e mouse event that occured
*/
public void mouseMoved(MouseEvent e)
- throws NotImplementedException
{
- // TODO: What should be done here, if anything?
+ removeFromSource();
}
/**
* Removes event from the source
*/
protected void removeFromSource()
- throws NotImplementedException
{
- // TODO: Implement this properly.
+ if (source != null)
+ {
+ source.removeMouseListener(this);
+ source.removeMouseMotionListener(this);
+ }
+ source = null;
+ destination = null;
+ }
+
+ /**
+ * Redispatches mouse events to the destination.
+ *
+ * @param e the mouse event to redispatch
+ */
+ private void dispatch(MouseEvent e)
+ {
+ if (destination != null)
+ {
+ MouseEvent e2 = SwingUtilities.convertMouseEvent(source, e,
+ destination);
+ destination.dispatchEvent(e2);
+ }
}
}
@@ -2627,24 +2725,42 @@ public class BasicTreeUI
public Rectangle getNodeDimensions(Object cell, int row, int depth,
boolean expanded, Rectangle size)
{
- if (size == null || cell == null)
- return null;
-
- String s = cell.toString();
- Font f = tree.getFont();
- FontMetrics fm = tree.getToolkit().getFontMetrics(f);
-
- if (s != null)
+ Dimension prefSize;
+ if (editingComponent != null && editingRow == row)
+ {
+ // Editing, ask editor for preferred size.
+ prefSize = editingComponent.getPreferredSize();
+ int rowHeight = getRowHeight();
+ if (rowHeight > 0 && rowHeight != prefSize.height)
+ prefSize.height = rowHeight;
+ }
+ else
+ {
+ // Not editing, ask renderer for preferred size.
+ Component rend =
+ currentCellRenderer.getTreeCellRendererComponent(tree, cell,
+ tree.isRowSelected(row),
+ expanded,
+ treeModel.isLeaf(cell),
+ row, false);
+ // Make sure the layout is valid.
+ rendererPane.add(rend);
+ rend.validate();
+ prefSize = rend.getPreferredSize();
+ }
+ if (size != null)
{
- TreePath path = treeState.getPathForRow(row);
size.x = getRowX(row, depth);
- size.width = SwingUtilities.computeStringWidth(fm, s);
- size.width = size.width + getCurrentControlIcon(path).getIconWidth()
- + gap + getNodeIcon(path).getIconWidth();
- size.height = getMaxHeight(tree);
- size.y = size.height * row;
+ // FIXME: This should be handled by the layout cache.
+ size.y = prefSize.height * row;
+ size.width = prefSize.width;
+ size.height = prefSize.height;
}
-
+ else
+ // FIXME: The y should be handled by the layout cache.
+ size = new Rectangle(getRowX(row, depth), prefSize.height * row, prefSize.width,
+ prefSize.height);
+
return size;
}
@@ -2706,6 +2822,9 @@ public class BasicTreeUI
if (treeState != null)
treeState.invalidateSizes();
}
+ else if (property.equals(JTree.EDITABLE_PROPERTY))
+ setEditable(((Boolean) event.getNewValue()).booleanValue());
+
}
}
@@ -2714,7 +2833,7 @@ public class BasicTreeUI
* properties of the model change.
*/
public class SelectionModelPropertyChangeHandler
- implements PropertyChangeListener
+ implements PropertyChangeListener
{
/**
@@ -2732,9 +2851,8 @@ public class BasicTreeUI
* the property that has changed.
*/
public void propertyChange(PropertyChangeEvent event)
- throws NotImplementedException
{
- // TODO: What should be done here, if anything?
+ treeSelectionModel.resetRowSelection();
}
}
@@ -2804,6 +2922,7 @@ public class BasicTreeUI
*/
public void treeCollapsed(TreeExpansionEvent event)
{
+ completeEditing();
validCachedPreferredSize = false;
treeState.setExpandedState(event.getPath(), false);
// The maximal cell height may change
@@ -3269,8 +3388,7 @@ public class BasicTreeUI
*/
public void valueChanged(TreeSelectionEvent event)
{
- if (tree.isEditing())
- tree.cancelEditing();
+ completeEditing();
TreePath op = event.getOldLeadSelectionPath();
TreePath np = event.getNewLeadSelectionPath();
@@ -3808,25 +3926,6 @@ public class BasicTreeUI
}
/**
- * Finish the editing session.
- */
- void finish()
- {
- treeState.invalidatePathBounds(treeState.getPathForRow(editingRow));
- editingPath = null;
- editingRow = - 1;
- stopEditingInCompleteEditing = false;
- isEditing = false;
- Rectangle bounds = editingComponent.getParent().getBounds();
- tree.removeAll();
- validCachedPreferredSize = false;
- // Repaint the region, where was the editing component.
- tree.repaint(bounds);
- editingComponent = null;
- tree.requestFocus();
- }
-
- /**
* Returns the amount to indent the given row
*
* @return amount to indent the given row.
diff --git a/libjava/classpath/javax/swing/plaf/metal/DefaultMetalTheme.java b/libjava/classpath/javax/swing/plaf/metal/DefaultMetalTheme.java
index 673aec1e418..672676fa081 100644
--- a/libjava/classpath/javax/swing/plaf/metal/DefaultMetalTheme.java
+++ b/libjava/classpath/javax/swing/plaf/metal/DefaultMetalTheme.java
@@ -38,8 +38,11 @@ exception statement from your version. */
package javax.swing.plaf.metal;
+import gnu.classpath.SystemProperties;
+
import java.awt.Font;
+import javax.swing.UIManager;
import javax.swing.plaf.ColorUIResource;
import javax.swing.plaf.FontUIResource;
@@ -63,10 +66,6 @@ public class DefaultMetalTheme extends MetalTheme
private static final ColorUIResource SECONDARY3 =
new ColorUIResource(204, 204, 204);
- private static final FontUIResource CONTROL_TEXT_FONT =
- new FontUIResource("Dialog", Font.BOLD, 12);
- private static final FontUIResource MENU_TEXT_FONT =
- new FontUIResource("Dialog", Font.BOLD, 12);
private static final FontUIResource SUB_TEXT_FONT =
new FontUIResource("Dialog", Font.PLAIN, 10);
private static final FontUIResource SYSTEM_TEXT_FONT =
@@ -77,6 +76,40 @@ public class DefaultMetalTheme extends MetalTheme
new FontUIResource("Dialog", Font.BOLD, 12);
/**
+ * The control text font for swing.boldMetal=false.
+ */
+ private static final FontUIResource PLAIN_CONTROL_TEXT_FONT =
+ new FontUIResource("Dialog", Font.PLAIN, 12);
+
+ /**
+ * The standard control text font.
+ */
+ private static final FontUIResource BOLD_CONTROL_TEXT_FONT =
+ new FontUIResource("Dialog", Font.BOLD, 12);
+
+ /**
+ * The menu text font for swing.boldMetal=false.
+ */
+ private static final FontUIResource PLAIN_MENU_TEXT_FONT =
+ new FontUIResource("Dialog", Font.PLAIN, 12);
+
+ /**
+ * The menu control text font.
+ */
+ private static final FontUIResource BOLD_MENU_TEXT_FONT =
+ new FontUIResource("Dialog", Font.BOLD, 12);
+
+ /**
+ * Indicates the control text font.
+ */
+ static final int CONTROL_TEXT_FONT = 1;
+
+ /**
+ * Indicates the menu text font.
+ */
+ static final int MENU_TEXT_FONT = 2;
+
+ /**
* Creates a new instance of this theme.
*/
public DefaultMetalTheme()
@@ -156,23 +189,28 @@ public class DefaultMetalTheme extends MetalTheme
/**
* Returns the font used for text on controls. In this case, the font is
- * <code>FontUIResource("Dialog", Font.BOLD, 12)</code>.
+ * <code>FontUIResource("Dialog", Font.BOLD, 12)</code>, unless the
+ * <code>swing.boldMetal</code> UI default is set to {@link Boolean#FALSE}
+ * in which case it is <code>FontUIResource("Dialog", Font.PLAIN, 12)</code>.
*
* @return The font.
*/
public FontUIResource getControlTextFont()
{
- return CONTROL_TEXT_FONT;
+ return getFont(CONTROL_TEXT_FONT);
}
+
/**
* Returns the font used for text in menus. In this case, the font is
- * <code>FontUIResource("Dialog", Font.BOLD, 12)</code>.
+ * <code>FontUIResource("Dialog", Font.BOLD, 12)</code>, unless the
+ * <code>swing.boldMetal</code> UI default is set to {@link Boolean#FALSE}
+ * in which case it is <code>FontUIResource("Dialog", Font.PLAIN, 12)</code>.
*
* @return The font used for text in menus.
*/
public FontUIResource getMenuTextFont()
{
- return MENU_TEXT_FONT;
+ return getFont(MENU_TEXT_FONT);
}
/**
@@ -218,4 +256,50 @@ public class DefaultMetalTheme extends MetalTheme
{
return WINDOW_TITLE_FONT;
}
+
+ /**
+ * Returns the appropriate font. The font type to return is identified
+ * by the specified id.
+ *
+ * @param id the font type to return
+ *
+ * @return the correct font
+ */
+ private FontUIResource getFont(int id)
+ {
+ FontUIResource font = null;
+ switch (id)
+ {
+ case CONTROL_TEXT_FONT:
+ if (isBoldMetal())
+ font = BOLD_CONTROL_TEXT_FONT;
+ else
+ font = PLAIN_CONTROL_TEXT_FONT;
+ break;
+ case MENU_TEXT_FONT:
+ if (isBoldMetal())
+ font = BOLD_MENU_TEXT_FONT;
+ else
+ font = PLAIN_MENU_TEXT_FONT;
+ break;
+ // TODO: Add other font types and their mapping here.
+ }
+ return font;
+ }
+
+ /**
+ * Determines if the theme should be bold or not. The theme is bold by
+ * default, this can be turned off by setting the system property
+ * swing.boldMetal to true, or by putting the property with the same name
+ * into the current UIManager's defaults.
+ *
+ * @return <code>true</code>, when the theme is bold, <code>false</code>
+ * otherwise
+ */
+ private boolean isBoldMetal()
+ {
+ Object boldMetal = UIManager.get("swing.boldMetal");
+ return (boldMetal == null || ! Boolean.FALSE.equals(boldMetal))
+ && ! ("false".equals(SystemProperties.getProperty("swing.boldMetal")));
+ }
}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalBorders.java b/libjava/classpath/javax/swing/plaf/metal/MetalBorders.java
index 7c41180aeae..d4e3a849781 100644
--- a/libjava/classpath/javax/swing/plaf/metal/MetalBorders.java
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalBorders.java
@@ -926,15 +926,11 @@ public class MetalBorders
/** The border insets. */
protected static Insets borderInsets = new Insets(1, 0, 1, 0);
- // TODO: find where this color really comes from
- private static Color borderColor = new Color(153, 153, 153);
-
/**
* Creates a new border instance.
*/
public MenuBarBorder()
{
- // Nothing to do here.
}
/**
@@ -951,7 +947,17 @@ public class MetalBorders
public void paintBorder(Component c, Graphics g, int x, int y, int w,
int h)
{
- g.setColor(borderColor);
+ // Although it is not correct to decide on the static property
+ // currentTheme which color to use the RI does it like that.
+ // The trouble is that by simply changing the current theme to
+ // e.g. DefaultMetalLookAndFeel this method will use another color
+ // although a change in painting behavior should be expected only
+ // after setting a new look and feel and updating all components.
+ if(MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme)
+ g.setColor(UIManager.getColor("MenuBar.borderColor"));
+ else
+ g.setColor(MetalLookAndFeel.getControlShadow());
+
g.drawLine(x, y + h - 1, x + w, y + h - 1);
}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalButtonUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalButtonUI.java
index 8addfc66c72..be9607927ba 100644
--- a/libjava/classpath/javax/swing/plaf/metal/MetalButtonUI.java
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalButtonUI.java
@@ -54,7 +54,6 @@ import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
-import javax.swing.plaf.basic.BasicButtonListener;
import javax.swing.plaf.basic.BasicButtonUI;
/**
@@ -66,24 +65,46 @@ public class MetalButtonUI
extends BasicButtonUI
{
- /** The color used to draw the focus rectangle around the text and/or icon. */
+ /**
+ * The shared button UI.
+ */
+ private static MetalButtonUI sharedUI;
+
+ /**
+ * The color used to draw the focus rectangle around the text and/or icon.
+ */
protected Color focusColor;
- /** The background color for the button when it is pressed. */
+ /**
+ * The background color for the button when it is pressed.
+ */
protected Color selectColor;
- /** The color for disabled button labels. */
+ /**
+ * The color for disabled button labels.
+ */
protected Color disabledTextColor;
/**
+ * Returns a UI delegate for the specified component.
+ *
+ * @param c the component (should be a subclass of {@link AbstractButton}).
+ *
+ * @return A new instance of <code>MetalButtonUI</code>.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ if (sharedUI == null)
+ sharedUI = new MetalButtonUI();
+ return sharedUI;
+ }
+
+ /**
* Creates a new instance.
*/
public MetalButtonUI()
{
super();
- focusColor = UIManager.getColor(getPropertyPrefix() + "focus");
- selectColor = UIManager.getColor(getPropertyPrefix() + "select");
- disabledTextColor = UIManager.getColor(getPropertyPrefix() + "disabledText");
}
/**
@@ -93,6 +114,7 @@ public class MetalButtonUI
*/
protected Color getFocusColor()
{
+ focusColor = UIManager.getColor(getPropertyPrefix() + "focus");
return focusColor;
}
@@ -103,6 +125,7 @@ public class MetalButtonUI
*/
protected Color getSelectColor()
{
+ selectColor = UIManager.getColor(getPropertyPrefix() + "select");
return selectColor;
}
@@ -113,22 +136,12 @@ public class MetalButtonUI
*/
protected Color getDisabledTextColor()
{
+ disabledTextColor = UIManager.getColor(getPropertyPrefix()
+ + "disabledText");
return disabledTextColor;
}
/**
- * Returns a UI delegate for the specified component.
- *
- * @param c the component (should be a subclass of {@link AbstractButton}).
- *
- * @return A new instance of <code>MetalButtonUI</code>.
- */
- public static ComponentUI createUI(JComponent c)
- {
- return new MetalButtonUI();
- }
-
- /**
* Installs the default settings for the specified button.
*
* @param button the button.
@@ -137,33 +150,20 @@ public class MetalButtonUI
*/
public void installDefaults(AbstractButton button)
{
+ // This is overridden to be public, for whatever reason.
super.installDefaults(button);
- button.setRolloverEnabled(UIManager.getBoolean(
- getPropertyPrefix() + "rollover"));
}
-
+
/**
* Removes the defaults added by {@link #installDefaults(AbstractButton)}.
*/
public void uninstallDefaults(AbstractButton button)
{
+ // This is overridden to be public, for whatever reason.
super.uninstallDefaults(button);
- button.setRolloverEnabled(false);
}
/**
- * Returns a button listener for the specified button.
- *
- * @param button the button.
- *
- * @return A button listener.
- */
- protected BasicButtonListener createButtonListener(AbstractButton button)
- {
- return new MetalButtonListener(button);
- }
-
- /**
* Paints the background of the button to indicate that it is in the
* "pressed" state.
*
@@ -175,7 +175,7 @@ public class MetalButtonUI
if (b.isContentAreaFilled())
{
Rectangle area = b.getVisibleRect();
- g.setColor(selectColor);
+ g.setColor(getSelectColor());
g.fillRect(area.x, area.y, area.width, area.height);
}
}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxIcon.java b/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxIcon.java
index fb8280e44da..30ee93162a9 100644
--- a/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxIcon.java
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxIcon.java
@@ -1,5 +1,5 @@
/* MetalCheckBoxIcon.java -- An icon for JCheckBoxes in the Metal L&F
- Copyright (C) 2005 Free Software Foundation, Inc.
+ Copyright (C) 2005, 2006 Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -42,8 +42,8 @@ import java.awt.Component;
import java.awt.Graphics;
import java.io.Serializable;
+import javax.swing.AbstractButton;
import javax.swing.Icon;
-import javax.swing.JCheckBox;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.plaf.UIResource;
@@ -134,8 +134,9 @@ public class MetalCheckBoxIcon
MetalUtils.paintGradient(g, x, y, getIconWidth(), getIconHeight(),
SwingConstants.VERTICAL, "CheckBox.gradient");
border.paintBorder(c, g, x, y, getIconWidth(), getIconHeight());
- JCheckBox cb = (JCheckBox) c;
- if (cb.isSelected())
- drawCheck(c, g, x, y);
+
+ AbstractButton b = (AbstractButton) c;
+ if (b.isSelected())
+ drawCheck(b, g, x, y);
}
}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java b/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java
index 30ec7e72b28..2817336a8f1 100644
--- a/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java
@@ -54,7 +54,6 @@ import javax.swing.JRadioButtonMenuItem;
import javax.swing.JSlider;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
-import javax.swing.plaf.IconUIResource;
import javax.swing.plaf.UIResource;
@@ -569,8 +568,8 @@ public class MetalIconFactory implements Serializable
*/
public void paintIcon(Component c, Graphics g, int x, int y)
{
- // TODO: pick up appropriate UI colors
- g.setColor(Color.black);
+ y = y + getShift();
+ g.setColor(MetalLookAndFeel.getBlack());
g.drawLine(x, y, x + 9, y);
g.drawLine(x, y + 1, x, y + 15);
g.drawLine(x, y + 15, x + 12, y + 15);
@@ -580,7 +579,7 @@ public class MetalIconFactory implements Serializable
g.drawLine(x + 7, y + 2, x + 11, y + 6);
g.drawLine(x + 8, y + 1, x + 9, y + 1);
- g.setColor(new Color(204, 204, 255));
+ g.setColor(MetalLookAndFeel.getPrimaryControl());
g.drawLine(x + 1, y + 1, x + 7, y + 1);
g.drawLine(x + 1, y + 1, x + 1, y + 14);
g.drawLine(x + 1, y + 14, x + 11, y + 14);
@@ -601,7 +600,9 @@ public class MetalIconFactory implements Serializable
}
/**
- * Returns the shift (???).
+ * Returns the vertical shift, in pixels, applied when painting the icon.
+ * The default value is zero, but subclasses may override this (for
+ * example, see {@link TreeLeafIcon}).
*
* @return The shift.
*/
@@ -649,21 +650,21 @@ public class MetalIconFactory implements Serializable
*/
public void paintIcon(Component c, Graphics g, int x, int y)
{
- // TODO: pick up appropriate UI colors
- g.setColor(Color.black);
- g.drawLine(x, y + 3, x, y + 12);
- g.drawLine(x, y + 12, x + 15, y + 12);
- g.drawLine(x + 15, y + 12, x + 15, y + 2);
- g.drawLine(x + 14, y + 3, x + 9, y + 3);
- g.drawLine(x + 8, y + 2, x + 1, y + 2);
- g.setColor(new Color(204, 204, 255));
- g.fillRect(x + 2, y + 4, 7, 8);
- g.fillRect(x + 9, y + 5, 6, 7);
- g.setColor(new Color(102, 102, 153));
- g.drawLine(x + 9, y + 2, x + 14, y + 2);
- g.setColor(new Color(50, 50, 120));
- g.drawLine(x + 9, y + 1, x + 15, y + 1);
- g.drawLine(x + 10, y, x + 15, y);
+ y = y + getShift();
+ g.setColor(MetalLookAndFeel.getBlack());
+ g.drawLine(x, y + 6, x, y + 15);
+ g.drawLine(x, y + 15, x + 15, y + 15);
+ g.drawLine(x + 15, y + 15, x + 15, y + 5);
+ g.drawLine(x + 14, y + 6, x + 9, y + 6);
+ g.drawLine(x + 8, y + 5, x + 1, y + 5);
+ g.setColor(MetalLookAndFeel.getPrimaryControl());
+ g.fillRect(x + 2, y + 7, 7, 8);
+ g.fillRect(x + 9, y + 8, 6, 7);
+ g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
+ g.drawLine(x + 9, y + 5, x + 14, y + 5);
+ g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
+ g.drawLine(x + 9, y + 4, x + 15, y + 4);
+ g.drawLine(x + 10, y + 3, x + 15, y + 3);
}
/**
@@ -679,7 +680,9 @@ public class MetalIconFactory implements Serializable
}
/**
- * Returns the shift (???).
+ * Returns the vertical shift, in pixels, applied when painting the icon.
+ * The default value is zero, but subclasses may override this (for
+ * example, see {@link TreeFolderIcon}).
*
* @return The shift.
*/
@@ -1036,20 +1039,22 @@ public class MetalIconFactory implements Serializable
g.drawLine(x + 6, y + 14, x, y + 8);
g.drawLine(x, y + 7, x, y + 1);
- // Fill the icon.
- if (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme
- && enabled)
- {
- String gradient;
- if (focus)
- gradient = "Slider.focusGradient";
- else
- gradient = "Slider.gradient";
- MetalUtils.paintGradient(g, x + 1, y + 2, 12, 13,
- SwingConstants.VERTICAL, gradient,
- gradientMask);
- }
- else
+// The following is commented out until the masking for the gradient painting
+// is working correctly
+// // Fill the icon.
+// if (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme
+// && enabled)
+// {
+// String gradient;
+// if (focus)
+// gradient = "Slider.focusGradient";
+// else
+// gradient = "Slider.gradient";
+// MetalUtils.paintGradient(g, x + 1, y + 2, 12, 13,
+// SwingConstants.VERTICAL, gradient,
+// gradientMask);
+// }
+// else
{
if (focus)
g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
@@ -1268,23 +1273,23 @@ public class MetalIconFactory implements Serializable
*/
public void paintIcon(Component c, Graphics g, int x, int y)
{
- g.setColor(new Color(102, 102, 153));
+ g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
g.fillRect(x + 1, y, 14, 2);
g.fillRect(x, y + 1, 2, 14);
g.fillRect(x + 1, y + 14, 14, 2);
g.fillRect(x + 14, y + 1, 2, 14);
g.drawLine(x + 2, y + 5, x + 14, y + 5);
- g.setColor(new Color(204, 204, 255));
+ g.setColor(MetalLookAndFeel.getPrimaryControl());
g.fillRect(x + 2, y + 2, 12, 3);
- g.setColor(new Color(102, 102, 153));
+ g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
g.drawLine(x + 3, y + 3, x + 3, y + 3);
g.drawLine(x + 6, y + 3, x + 6, y + 3);
g.drawLine(x + 9, y + 3, x + 9, y + 3);
g.drawLine(x + 12, y + 3, x + 12, y + 3);
- g.setColor(Color.white);
+ g.setColor(MetalLookAndFeel.getWhite());
g.fillRect(x + 2, y + 6, 12, 8);
g.drawLine(x + 2, y + 2, x + 2, y + 2);
g.drawLine(x + 5, y + 2, x + 5, y + 2);
@@ -1697,20 +1702,22 @@ public class MetalIconFactory implements Serializable
g.drawLine(x + 8, y + 14, x + 1, y + 14);
g.drawLine(x, y + 13, x, y + 1);
- // Fill the icon.
- if (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme
- && enabled)
- {
- String gradient;
- if (focus)
- gradient = "Slider.focusGradient";
- else
- gradient = "Slider.gradient";
- MetalUtils.paintGradient(g, x + 2, y + 1, 13, 12,
- SwingConstants.HORIZONTAL, gradient,
- gradientMask);
- }
- else
+// The following is commented out until the masking for the gradient painting
+// is working correctly
+// // Fill the icon.
+// if (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme
+// && enabled)
+// {
+// String gradient;
+// if (focus)
+// gradient = "Slider.focusGradient";
+// else
+// gradient = "Slider.gradient";
+// MetalUtils.paintGradient(g, x + 2, y + 1, 13, 12,
+// SwingConstants.HORIZONTAL, gradient,
+// gradientMask);
+// }
+// else
{
if (focus)
g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
@@ -1883,7 +1890,8 @@ public class MetalIconFactory implements Serializable
}
/**
- * Returns the shift (???).
+ * Returns the vertical shift, in pixels, applied when painting the icon.
+ * This overridden method returns <code>-1</code>.
*
* @return The shift.
*/
@@ -1918,7 +1926,8 @@ public class MetalIconFactory implements Serializable
}
/**
- * Returns the shift (???).
+ * Returns the vertical shift, in pixels, applied when painting the icon.
+ * This overridden method returns <code>2</code>.
*
* @return The shift.
*/
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java b/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java
index 8a5a61107c1..a9a6790931f 100644
--- a/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java
@@ -38,6 +38,8 @@ exception statement from your version. */
package javax.swing.plaf.metal;
+import gnu.classpath.SystemProperties;
+
import java.awt.Color;
import java.awt.Font;
@@ -81,16 +83,15 @@ public class MetalLookAndFeel extends BasicLookAndFeel
*/
public MetalLookAndFeel()
{
- createDefaultTheme();
+ // Nothing to do here.
}
/**
- * Sets the current theme to a new instance of {@link OceanTheme}.
+ * Sets the current theme to a new instance of {@link DefaultMetalTheme}.
*/
protected void createDefaultTheme()
{
- if (theme == null)
- setCurrentTheme(new OceanTheme());
+ getCurrentTheme();
}
/**
@@ -149,6 +150,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel
public UIDefaults getDefaults()
{
+ createDefaultTheme();
if (LAF_defaults == null)
{
LAF_defaults = super.getDefaults();
@@ -887,7 +889,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel
"CheckBox.border", MetalBorders.getButtonBorder(),
"CheckBox.disabledText", getInactiveControlTextColor(),
"CheckBox.focus", getFocusColor(),
- "CheckBox.font", new FontUIResource("Dialog", Font.BOLD, 12),
+ "CheckBox.font", getControlTextFont(),
"CheckBox.foreground", getControlTextColor(),
"CheckBox.icon",
new UIDefaults.ProxyLazyValue("javax.swing.plaf.metal.MetalCheckBoxIcon"),
@@ -903,7 +905,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel
"CheckBoxMenuItem.commandSound", "sounds/MenuItemCommand.wav",
"CheckBoxMenuItem.checkIcon", MetalIconFactory.getCheckBoxMenuItemIcon(),
"CheckBoxMenuItem.disabledForeground", getMenuDisabledForeground(),
- "CheckBoxMenuItem.font", new FontUIResource("Dialog", Font.BOLD, 12),
+ "CheckBoxMenuItem.font", getMenuTextFont(),
"CheckBoxMenuItem.foreground", getMenuForeground(),
"CheckBoxMenuItem.selectionBackground", getMenuSelectedBackground(),
"CheckBoxMenuItem.selectionForeground", getMenuSelectedForeground(),
@@ -922,7 +924,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel
"ComboBox.buttonShadow", getControlShadow(),
"ComboBox.disabledBackground", getControl(),
"ComboBox.disabledForeground", getInactiveSystemTextColor(),
- "ComboBox.font", new FontUIResource("Dialog", Font.BOLD, 12),
+ "ComboBox.font", getControlTextFont(),
"ComboBox.foreground", getControlTextColor(),
"ComboBox.selectionBackground", getPrimaryControlShadow(),
"ComboBox.selectionForeground", getControlTextColor(),
@@ -933,10 +935,11 @@ public class MetalLookAndFeel extends BasicLookAndFeel
"DesktopIcon.foreground", getControlTextColor(),
"DesktopIcon.width", new Integer(160),
"DesktopIcon.border", MetalBorders.getDesktopIconBorder(),
+ "DesktopIcon.font", getControlTextFont(),
"EditorPane.background", getWindowBackground(),
"EditorPane.caretForeground", getUserTextColor(),
- "EditorPane.font", new FontUIResource("Dialog", Font.BOLD, 12),
+ "EditorPane.font", getControlTextFont(),
"EditorPane.foreground", getUserTextColor(),
"EditorPane.inactiveForeground", getInactiveSystemTextColor(),
"EditorPane.selectionBackground", getTextHighlightColor(),
@@ -1021,7 +1024,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel
"Menu.borderPainted", Boolean.TRUE,
"MenuItem.commandSound", "sounds/MenuItemCommand.wav",
"Menu.disabledForeground", getMenuDisabledForeground(),
- "Menu.font", getControlTextFont(),
+ "Menu.font", getMenuTextFont(),
"Menu.foreground", getMenuForeground(),
"Menu.selectionBackground", getMenuSelectedBackground(),
"Menu.selectionForeground", getMenuSelectedForeground(),
@@ -1030,7 +1033,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel
"MenuBar.background", getMenuBackground(),
"MenuBar.border", new MetalBorders.MenuBarBorder(),
- "MenuBar.font", getControlTextFont(),
+ "MenuBar.font", getMenuTextFont(),
"MenuBar.foreground", getMenuForeground(),
"MenuBar.highlight", getControlHighlight(),
"MenuBar.shadow", getControlShadow(),
@@ -1044,7 +1047,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel
"MenuItem.border", new MetalBorders.MenuItemBorder(),
"MenuItem.borderPainted", Boolean.TRUE,
"MenuItem.disabledForeground", getMenuDisabledForeground(),
- "MenuItem.font", getControlTextFont(),
+ "MenuItem.font", getMenuTextFont(),
"MenuItem.foreground", getMenuForeground(),
"MenuItem.selectionBackground", getMenuSelectedBackground(),
"MenuItem.selectionForeground", getMenuSelectedForeground(),
@@ -1085,13 +1088,13 @@ public class MetalLookAndFeel extends BasicLookAndFeel
"PopupMenu.background", getMenuBackground(),
"PopupMenu.border", new MetalBorders.PopupMenuBorder(),
- "PopupMenu.font", new FontUIResource("Dialog", Font.BOLD, 12),
+ "PopupMenu.font", getMenuTextFont(),
"PopupMenu.foreground", getMenuForeground(),
"PopupMenu.popupSound", "sounds/PopupMenuPopup.wav",
"ProgressBar.background", getControl(),
"ProgressBar.border", new BorderUIResource.LineBorderUIResource(getControlDarkShadow(), 1),
- "ProgressBar.font", new FontUIResource("Dialog", Font.BOLD, 12),
+ "ProgressBar.font", getControlTextFont(),
"ProgressBar.foreground", getPrimaryControlShadow(),
"ProgressBar.selectionBackground", getPrimaryControlDarkShadow(),
"ProgressBar.selectionForeground", getControl(),
@@ -1125,7 +1128,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel
MetalIconFactory.getRadioButtonMenuItemIcon(),
"RadioButtonMenuItem.commandSound", "sounds/MenuItemCommand.wav",
"RadioButtonMenuItem.disabledForeground", getMenuDisabledForeground(),
- "RadioButtonMenuItem.font", MetalLookAndFeel.getControlTextFont(),
+ "RadioButtonMenuItem.font", getMenuTextFont(),
"RadioButtonMenuItem.foreground", getMenuForeground(),
"RadioButtonMenuItem.margin", new InsetsUIResource(2, 2, 2, 2),
"RadioButtonMenuItem.selectionBackground",
@@ -1172,7 +1175,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel
"Spinner.arrowButtonInsets", new InsetsUIResource(0, 0, 0, 0),
"Spinner.background", getControl(),
"Spinner.border", MetalBorders.getTextFieldBorder(),
- "Spinner.font", new FontUIResource("Dialog", Font.BOLD, 12),
+ "Spinner.font", getControlTextFont(),
"Spinner.foreground", getControl(),
"SplitPane.background", getControl(),
@@ -1189,7 +1192,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel
"TabbedPane.contentOpaque", Boolean.TRUE,
"TabbedPane.darkShadow", getControlDarkShadow(),
"TabbedPane.focus", getPrimaryControlDarkShadow(),
- "TabbedPane.font", new FontUIResource("Dialog", Font.BOLD, 12),
+ "TabbedPane.font", getControlTextFont(),
"TabbedPane.foreground", getControlTextColor(),
"TabbedPane.highlight", getControlHighlight(),
"TabbedPane.light", getControl(),
@@ -1200,7 +1203,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel
"TabbedPane.tabAreaBackground", getControl(), // overridden in OceanTheme
"TabbedPane.tabAreaInsets", new InsetsUIResource(4, 2, 0, 6), // dito
"TabbedPane.tabInsets", new InsetsUIResource(0, 9, 1, 9),
-
+
// new properties in OceanTheme:
// TabbedPane.contentAreaColor
// TabbedPane.unselectedBackground
@@ -1252,7 +1255,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel
"TextPane.selectionForeground", getHighlightedTextColor(),
"TitledBorder.border", new LineBorderUIResource(getPrimaryControl(), 1),
- "TitledBorder.font", new FontUIResource("Dialog", Font.BOLD, 12),
+ "TitledBorder.font", getControlTextFont(),
"TitledBorder.titleColor", getSystemTextColor(),
"ToggleButton.background", getControl(),
@@ -1274,7 +1277,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel
"ToolBar.dockingForeground", getPrimaryControlDarkShadow(),
"ToolBar.floatingBackground", getMenuBackground(),
"ToolBar.floatingForeground", getPrimaryControl(),
- "ToolBar.font", new FontUIResource("Dialog", Font.BOLD, 12),
+ "ToolBar.font", getMenuTextFont(),
"ToolBar.foreground", getMenuForeground(),
"ToolBar.highlight", getControlHighlight(),
"ToolBar.light", getControlHighlight(),
@@ -1354,7 +1357,14 @@ public class MetalLookAndFeel extends BasicLookAndFeel
public static MetalTheme getCurrentTheme()
{
if (theme == null)
- theme = new OceanTheme();
+ {
+ // swing.metalTheme property documented here:
+ // http://java.sun.com/j2se/1.5.0/docs/guide/swing/1.5/index.html
+ if ("steel".equals(SystemProperties.getProperty("swing.metalTheme")))
+ theme = new DefaultMetalTheme();
+ else
+ theme = new OceanTheme();
+ }
return theme;
}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalMenuBarUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalMenuBarUI.java
index ff763ea9da9..40661946b1a 100644
--- a/libjava/classpath/javax/swing/plaf/metal/MetalMenuBarUI.java
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalMenuBarUI.java
@@ -1,5 +1,5 @@
/* MetalMenuBarUI.java -- MenuBar UI for the Metal L&F
- Copyright (C) 2005 Free Software Foundation, Inc.
+ Copyright (C) 2005, 2006 Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -76,12 +76,15 @@ public class MetalMenuBarUI extends BasicMenuBarUI
*/
public void update(Graphics g, JComponent c)
{
+ int height = c.getHeight();
if (c.isOpaque()
&& UIManager.get("MenuBar.gradient") != null
- && c.getBackground() instanceof UIResource)
+ && c.getBackground() instanceof UIResource
+ && height > 2)
{
- MetalUtils.paintGradient(g, 0, 0, c.getWidth(), c.getHeight(),
+ MetalUtils.paintGradient(g, 0, 0, c.getWidth(), height - 2,
SwingConstants.VERTICAL, "MenuBar.gradient");
+
paint(g, c);
}
else
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalRadioButtonUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalRadioButtonUI.java
index 046e4942ee1..57f5bbe3e0a 100644
--- a/libjava/classpath/javax/swing/plaf/metal/MetalRadioButtonUI.java
+++ b/libjava/classpath/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/libjava/classpath/javax/swing/plaf/metal/MetalScrollBarUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalScrollBarUI.java
index 75f2750ae9c..4c75fcb4f14 100644
--- a/libjava/classpath/javax/swing/plaf/metal/MetalScrollBarUI.java
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalScrollBarUI.java
@@ -1,5 +1,5 @@
/* MetalScrollBarUI.java
- Copyright (C) 2005 Free Software Foundation, Inc.
+ Copyright (C) 2005, 2006, Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -169,6 +169,7 @@ public class MetalScrollBarUI extends BasicScrollBarUI
Boolean prop = (Boolean) scrollbar.getClientProperty(FREE_STANDING_PROP);
isFreeStanding = prop == null ? true : prop.booleanValue();
scrollBarShadowColor = UIManager.getColor("ScrollBar.shadow");
+ scrollBarWidth = UIManager.getInt("ScrollBar.width");
super.installDefaults();
}
@@ -187,7 +188,10 @@ public class MetalScrollBarUI extends BasicScrollBarUI
/**
* Creates a new button to use as the control at the lower end of the
- * {@link JScrollBar}.
+ * {@link JScrollBar}. This method assigns the new button (an instance of
+ * {@link MetalScrollButton} to the {@link #decreaseButton} field, and also
+ * returns the button. The button width is determined by the
+ * <code>ScrollBar.width</code> setting in the UI defaults.
*
* @param orientation the orientation of the button ({@link #NORTH},
* {@link #SOUTH}, {@link #EAST} or {@link #WEST}).
@@ -196,7 +200,6 @@ public class MetalScrollBarUI extends BasicScrollBarUI
*/
protected JButton createDecreaseButton(int orientation)
{
- scrollBarWidth = UIManager.getInt("ScrollBar.width");
decreaseButton = new MetalScrollButton(orientation, scrollBarWidth,
isFreeStanding);
return decreaseButton;
@@ -204,7 +207,10 @@ public class MetalScrollBarUI extends BasicScrollBarUI
/**
* Creates a new button to use as the control at the upper end of the
- * {@link JScrollBar}.
+ * {@link JScrollBar}. This method assigns the new button (an instance of
+ * {@link MetalScrollButton} to the {@link #increaseButton} field, and also
+ * returns the button. The button width is determined by the
+ * <code>ScrollBar.width</code> setting in the UI defaults.
*
* @param orientation the orientation of the button ({@link #NORTH},
* {@link #SOUTH}, {@link #EAST} or {@link #WEST}).
@@ -213,7 +219,6 @@ public class MetalScrollBarUI extends BasicScrollBarUI
*/
protected JButton createIncreaseButton(int orientation)
{
- scrollBarWidth = UIManager.getInt("ScrollBar.width");
increaseButton = new MetalScrollButton(orientation, scrollBarWidth,
isFreeStanding);
return increaseButton;
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalSliderUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalSliderUI.java
index 0f824418c5d..b3e8707c94d 100644
--- a/libjava/classpath/javax/swing/plaf/metal/MetalSliderUI.java
+++ b/libjava/classpath/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/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java b/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java
index 6081c355c37..a3069daa9c5 100644
--- a/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java
@@ -38,18 +38,14 @@ exception statement from your version. */
package javax.swing.plaf.metal;
import java.awt.Color;
-import java.awt.Component;
-import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
-import java.awt.LayoutManager;
-import java.awt.Point;
+import java.awt.Insets;
+import javax.swing.JButton;
import javax.swing.JSplitPane;
-import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.border.Border;
-import javax.swing.plaf.basic.BasicArrowButton;
import javax.swing.plaf.basic.BasicSplitPaneDivider;
/**
@@ -59,6 +55,143 @@ import javax.swing.plaf.basic.BasicSplitPaneDivider;
*/
class MetalSplitPaneDivider extends BasicSplitPaneDivider
{
+ /**
+ * The button pixel data, as indices into the colors array below.
+ * This is the version for 'left' buttons.
+ *
+ * This is slightly different from the icon in Sun's version, it is
+ * one pixel smaller and is more consistent with BUTTON_SPRITE_R.
+ */
+ static final byte[][] BUTTON_SPRITE_L = {{ 0, 0, 0, 2, 0, 0, 0, 0 },
+ { 0, 0, 2, 1, 1, 0, 0, 0 },
+ { 0, 2, 1, 1, 1, 1, 0, 0 },
+ { 2, 1, 1, 1, 1, 1, 1, 0 },
+ { 0, 3, 3, 3, 3, 3, 3, 3 }};
+
+ /**
+ * The button pixel data, as indices into the colors array below.
+ * This is the version for 'right' buttons.
+ */
+ static final byte[][] BUTTON_SPRITE_R = {{ 2, 2, 2, 2, 2, 2, 2, 2 },
+ { 0, 1, 1, 1, 1, 1, 1, 3 },
+ { 0, 0, 1, 1, 1, 1, 3, 0 },
+ { 0, 0, 0, 1, 1, 3, 0, 0 },
+ { 0, 0, 0, 0, 3, 0, 0, 0 }};
+
+ private class MetalOneTouchButton
+ extends JButton
+ {
+ /**
+ * Denotes a left button.
+ */
+ static final int LEFT = 0;
+
+ /**
+ * Denotes a right button.
+ */
+ static final int RIGHT = 1;
+
+ /**
+ * The colors for the button sprite.
+ */
+ private Color[] colors;
+
+ /**
+ * Either LEFT or RIGHT.
+ */
+ private int direction;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param dir either LEFT or RIGHT
+ */
+ MetalOneTouchButton(int dir)
+ {
+ direction = dir;
+ colors = new Color[4];
+ }
+
+ /**
+ * Never allow borders.
+ */
+ public void setBorder(Border b)
+ {
+ }
+
+ /**
+ * Never allow focus traversal.
+ */
+ public boolean isFocusTraversable()
+ {
+ return false;
+ }
+
+ /**
+ * Paints the one touch button.
+ */
+ public void paint(Graphics g)
+ {
+ if (splitPane != null)
+ {
+ // Update colors here to reflect dynamic changes to the theme.
+ colors[0] = getBackground();
+ colors[1] = MetalLookAndFeel.getPrimaryControlDarkShadow();
+ colors[2] = MetalLookAndFeel.getPrimaryControlInfo();
+ colors[3] = MetalLookAndFeel.getPrimaryControlHighlight();
+
+ // Fill background.
+ g.setColor(getBackground());
+ g.fillRect(0, 0, getWidth(), getHeight());
+
+ // Pressed buttons have slightly different color mapping.
+ if (getModel().isPressed())
+ colors[1] = colors[2];
+
+ byte[][] sprite;
+ if (direction == LEFT)
+ sprite = BUTTON_SPRITE_L;
+ else
+ sprite = BUTTON_SPRITE_R;
+
+ if (orientation == JSplitPane.VERTICAL_SPLIT)
+ {
+ // Draw the sprite as it is.
+ for (int y = 0; y < sprite.length; y++)
+ {
+ byte[] line = sprite[y];
+ for (int x = 0; x < line.length; x++)
+ {
+ int c = line[x];
+ if (c != 0)
+ {
+ g.setColor(colors[c]);
+ g.fillRect(x + 1, y + 1, 1, 1);
+ }
+ }
+ }
+ }
+ else
+ {
+ // Draw the sprite with swapped X and Y axis.
+ for (int y = 0; y < sprite.length; y++)
+ {
+ byte[] line = sprite[y];
+ for (int x = 0; x < line.length; x++)
+ {
+ int c = line[x];
+ if (c != 0)
+ {
+ g.setColor(colors[c]);
+ g.fillRect(y + 1, x + 1, 1, 1);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
/** The dark color in the pattern. */
Color dark;
@@ -79,7 +212,6 @@ class MetalSplitPaneDivider extends BasicSplitPaneDivider
public MetalSplitPaneDivider(MetalSplitPaneUI ui, Color light, Color dark)
{
super(ui);
- setLayout(new MetalDividerLayout());
this.splitPane = super.splitPane;
this.orientation = super.orientation;
this.light = light;
@@ -106,126 +238,27 @@ class MetalSplitPaneDivider extends BasicSplitPaneDivider
if (border != null)
border.paintBorder(this, g, 0, 0, s.width, s.height);
- MetalUtils.fillMetalPattern(splitPane, g, 2, 2, s.width - 4, s.height - 4,
+ Insets i = getInsets();
+ MetalUtils.fillMetalPattern(splitPane, g, i.left + 2, i.top + 2,
+ s.width - i.left - i.right - 4,
+ s.height - i.top - i.bottom - 4,
light, dark);
- if (splitPane.isOneTouchExpandable())
- {
- ((BasicArrowButton) rightButton).paint(g);
- ((BasicArrowButton) leftButton).paint(g);
- }
+ super.paint(g);
}
-
- /**
- * This helper class acts as the Layout Manager for the divider.
- */
- public class MetalDividerLayout implements LayoutManager
- {
- /** The right button. */
- BasicArrowButton rb;
-
- /** The left button. */
- BasicArrowButton lb;
-
- /**
- * Creates a new DividerLayout object.
- */
- public MetalDividerLayout()
- {
- // Nothing to do here
- }
-
- /**
- * This method is called when a Component is added.
- *
- * @param string The constraints string.
- * @param c The Component to add.
- */
- public void addLayoutComponent(String string, Component c)
- {
- // Nothing to do here, constraints are set depending on
- // orientation in layoutContainer
- }
-
- /**
- * This method is called to lay out the container.
- *
- * @param c The container to lay out.
- */
- public void layoutContainer(Container c)
- {
- // The only components we care about setting up are the
- // one touch buttons.
- if (splitPane.isOneTouchExpandable())
- {
- if (c.getComponentCount() == 2)
- {
- Component c1 = c.getComponent(0);
- Component c2 = c.getComponent(1);
- if ((c1 instanceof BasicArrowButton)
- && (c2 instanceof BasicArrowButton))
- {
- lb = (BasicArrowButton) c1;
- rb = (BasicArrowButton) c2;
- }
- }
- if (rb != null && lb != null)
- {
- Point p = getLocation();
- lb.setSize(lb.getPreferredSize());
- rb.setSize(rb.getPreferredSize());
- lb.setLocation(p.x, p.y);
-
- if (orientation == JSplitPane.HORIZONTAL_SPLIT)
- {
- rb.setDirection(SwingConstants.EAST);
- lb.setDirection(SwingConstants.WEST);
- rb.setLocation(p.x, p.y + lb.getHeight());
- }
- else
- {
- rb.setDirection(SwingConstants.SOUTH);
- lb.setDirection(SwingConstants.NORTH);
- rb.setLocation(p.x + lb.getWidth(), p.y);
- }
- }
- }
- }
-
- /**
- * This method returns the minimum layout size.
- *
- * @param c The container to calculate for.
- *
- * @return The minimum layout size.
- */
- public Dimension minimumLayoutSize(Container c)
- {
- return preferredLayoutSize(c);
- }
- /**
- * This method returns the preferred layout size.
- *
- * @param c The container to calculate for.
- *
- * @return The preferred layout size.
- */
- public Dimension preferredLayoutSize(Container c)
- {
- int dividerSize = getDividerSize();
- return new Dimension(dividerSize, dividerSize);
- }
+ protected JButton createLeftOneTouchButton()
+ {
+ JButton b = new MetalOneTouchButton(MetalOneTouchButton.LEFT);
+ b.setMinimumSize(new Dimension(ONE_TOUCH_SIZE, ONE_TOUCH_SIZE));
+ b.setRequestFocusEnabled(false);
+ return b;
+ }
- /**
- * This method is called when a component is removed.
- *
- * @param c The component to remove.
- */
- public void removeLayoutComponent(Component c)
- {
- // Nothing to do here. If buttons are removed
- // they will not be layed out when layoutContainer is
- // called.
- }
+ protected JButton createRightOneTouchButton()
+ {
+ JButton b = new MetalOneTouchButton(MetalOneTouchButton.RIGHT);
+ b.setMinimumSize(new Dimension(ONE_TOUCH_SIZE, ONE_TOUCH_SIZE));
+ b.setRequestFocusEnabled(false);
+ return b;
}
}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java
index 20135fc857e..53eaa3cac5a 100644
--- a/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java
@@ -1159,7 +1159,7 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI
g.drawLine(x + 1, y + 1, x + 1, rect.y + 1);
if (rect.y + rect.height < y + h - 2)
{
- g.drawLine(x + y, rect.y + rect.height + 1, x + 1, y + h + 2);
+ g.drawLine(x + 1, rect.y + rect.height + 1, x + 1, y + h + 2);
}
}
}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalToolTipUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalToolTipUI.java
index d1040347fc6..6647cc02d16 100644
--- a/libjava/classpath/javax/swing/plaf/metal/MetalToolTipUI.java
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalToolTipUI.java
@@ -43,9 +43,6 @@ import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
-import java.awt.Insets;
-import java.awt.Rectangle;
-import java.awt.Toolkit;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
@@ -54,8 +51,6 @@ import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JToolTip;
import javax.swing.KeyStroke;
-import javax.swing.SwingConstants;
-import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI;
@@ -192,32 +187,14 @@ public class MetalToolTipUI
*/
public Dimension getPreferredSize(JComponent c)
{
- if (isAcceleratorHidden())
- return super.getPreferredSize(c);
- else
+ Dimension d = super.getPreferredSize(c);
+ String acc = getAcceleratorString();
+ if (acc != null && ! acc.equals(""))
{
- Insets insets = c.getInsets();
- JToolTip tt = (JToolTip) c;
- String tipText = tt.getTipText();
- if (tipText != null)
- {
- FontMetrics fm = c.getFontMetrics(c.getFont());
- int prefH = fm.getHeight() + insets.top + insets.bottom;
- int prefW = fm.stringWidth(tipText) + insets.left + insets.right;
-
- // this seems to be the first opportunity we have to get the
- // accelerator string from the component (if it has one)
- acceleratorString = fetchAcceleratorString(c);
- if (acceleratorString != null)
- {
- prefW += padSpaceBetweenStrings;
- fm = c.getFontMetrics(acceleratorFont);
- prefW += fm.stringWidth(acceleratorString);
- }
- return new Dimension(prefW, prefH);
- }
- else return new Dimension(0, 0);
+ FontMetrics fm = c.getFontMetrics(c.getFont());
+ d.width += fm.stringWidth(acc);
}
+ return d;
}
/**
@@ -228,39 +205,8 @@ public class MetalToolTipUI
*/
public void paint(Graphics g, JComponent c)
{
- JToolTip tip = (JToolTip) c;
-
- String text = tip.getTipText();
- Toolkit t = tip.getToolkit();
- if (text == null)
- return;
-
- Rectangle vr = new Rectangle();
- vr = SwingUtilities.calculateInnerArea(tip, vr);
- Rectangle ir = new Rectangle();
- Rectangle tr = new Rectangle();
- FontMetrics fm = t.getFontMetrics(tip.getFont());
- int ascent = fm.getAscent();
- SwingUtilities.layoutCompoundLabel(tip, fm, text, null,
- SwingConstants.CENTER, SwingConstants.LEFT,
- SwingConstants.CENTER, SwingConstants.CENTER, vr, ir, tr, 0);
- Color saved = g.getColor();
- g.setColor(Color.BLACK);
-
- g.drawString(text, vr.x, vr.y + ascent);
-
- // paint accelerator
- if (acceleratorString != null)
- {
- g.setFont(acceleratorFont);
- g.setColor(acceleratorForeground);
- fm = t.getFontMetrics(acceleratorFont);
- int width = fm.stringWidth(acceleratorString);
- g.drawString(acceleratorString, vr.x + vr.width - width
- - padSpaceBetweenStrings / 2, vr.y + vr.height - fm.getDescent());
- }
-
- g.setColor(saved);
+ super.paint(g, c);
+ // Somehow paint accelerator. Keep care for possible HTML rendering.
}
/**
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalTreeUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalTreeUI.java
index 3ea37c82f18..ed1e5b4d825 100644
--- a/libjava/classpath/javax/swing/plaf/metal/MetalTreeUI.java
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalTreeUI.java
@@ -38,14 +38,15 @@ exception statement from your version. */
package javax.swing.plaf.metal;
-import gnu.classpath.NotImplementedException;
-
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
import javax.swing.JComponent;
import javax.swing.JTree;
+import javax.swing.UIManager;
import javax.swing.tree.TreePath;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicTreeUI;
@@ -56,6 +57,68 @@ import javax.swing.plaf.basic.BasicTreeUI;
public class MetalTreeUI extends BasicTreeUI
{
/**
+ * Listens for property changes of the line style and updates the
+ * internal setting.
+ */
+ private class LineStyleListener
+ implements PropertyChangeListener
+ {
+
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName().equals(LINE_STYLE_PROPERTY))
+ decodeLineStyle(e.getNewValue());
+ }
+
+ }
+
+ /**
+ * The key to the lineStyle client property.
+ */
+ private static final String LINE_STYLE_PROPERTY = "JTree.lineStyle";
+
+ /**
+ * The property value indicating no line style.
+ */
+ private static final String LINE_STYLE_VALUE_NONE = "None";
+
+ /**
+ * The property value indicating angled line style.
+ */
+ private static final String LINE_STYLE_VALUE_ANGLED = "Angled";
+
+ /**
+ * The property value indicating horizontal line style.
+ */
+ private static final String LINE_STYLE_VALUE_HORIZONTAL = "Horizontal";
+
+ /**
+ * The line style for None.
+ */
+ private static final int LINE_STYLE_NONE = 0;
+
+ /**
+ * The line style for Angled.
+ */
+ private static final int LINE_STYLE_ANGLED = 1;
+
+ /**
+ * The line style for Horizontal.
+ */
+ private static final int LINE_STYLE_HORIZONTAL = 2;
+
+ /**
+ * The current line style.
+ */
+ private int lineStyle;
+
+ /**
+ * Listens for changes on the line style property and updates the
+ * internal settings.
+ */
+ private PropertyChangeListener lineStyleListener;
+
+ /**
* Constructs a new instance of <code>MetalTreeUI</code>.
*/
public MetalTreeUI()
@@ -103,8 +166,13 @@ public class MetalTreeUI extends BasicTreeUI
*/
public void installUI(JComponent c)
{
- // TODO: What to do here, if anything?
super.installUI(c);
+
+ Object lineStyleProp = c.getClientProperty(LINE_STYLE_PROPERTY);
+ decodeLineStyle(lineStyleProp);
+ if (lineStyleListener == null)
+ lineStyleListener = new LineStyleListener();
+ c.addPropertyChangeListener(lineStyleListener);
}
/**
@@ -124,8 +192,10 @@ public class MetalTreeUI extends BasicTreeUI
*/
public void uninstallUI(JComponent c)
{
- // TODO: What to do here?
super.uninstallUI(c);
+ if (lineStyleListener != null)
+ c.removePropertyChangeListener(lineStyleListener);
+ lineStyleListener = null;
}
/**
@@ -135,9 +205,15 @@ public class MetalTreeUI extends BasicTreeUI
* @param lineStyleFlag - String representation
*/
protected void decodeLineStyle(Object lineStyleFlag)
- throws NotImplementedException
{
- // FIXME: not implemented
+ if (lineStyleFlag == null || lineStyleFlag.equals(LINE_STYLE_VALUE_ANGLED))
+ lineStyle = LINE_STYLE_ANGLED;
+ else if (lineStyleFlag.equals(LINE_STYLE_VALUE_HORIZONTAL))
+ lineStyle = LINE_STYLE_HORIZONTAL;
+ else if (lineStyleFlag.equals(LINE_STYLE_VALUE_NONE))
+ lineStyle = LINE_STYLE_NONE;
+ else
+ lineStyle = LINE_STYLE_ANGLED;
}
/**
@@ -170,6 +246,9 @@ public class MetalTreeUI extends BasicTreeUI
// Calls BasicTreeUI's paint since it takes care of painting all
// types of icons.
super.paint(g, c);
+
+ if (lineStyle == LINE_STYLE_HORIZONTAL)
+ paintHorizontalSeparators(g, c);
}
/**
@@ -179,9 +258,28 @@ public class MetalTreeUI extends BasicTreeUI
* @param c - the current component to draw
*/
protected void paintHorizontalSeparators(Graphics g, JComponent c)
- throws NotImplementedException
{
- // FIXME: not implemented
+ g.setColor(UIManager.getColor("Tree.line"));
+ Rectangle clip = g.getClipBounds();
+ int row0 = getRowForPath(tree, getClosestPathForLocation(tree, 0, clip.y));
+ int row1 =
+ getRowForPath(tree, getClosestPathForLocation(tree, 0,
+ clip.y + clip.height - 1));
+ if (row0 >= 0 && row1 >= 0)
+ {
+ for (int i = row0; i <= row1; i++)
+ {
+ TreePath p = getPathForRow(tree, i);
+ if (p != null && p.getPathCount() == 2)
+ {
+ Rectangle r = getPathBounds(tree, getPathForRow(tree, i));
+ if (r != null)
+ {
+ g.drawLine(clip.x, r.y, clip.x + clip.width, r.y);
+ }
+ }
+ }
+ }
}
@@ -197,7 +295,8 @@ public class MetalTreeUI extends BasicTreeUI
protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds,
Insets insets, TreePath path)
{
- super.paintVerticalPartOfLeg(g, clipBounds, insets, path);
+ if (lineStyle == LINE_STYLE_ANGLED)
+ super.paintVerticalPartOfLeg(g, clipBounds, insets, path);
}
/**
@@ -211,7 +310,8 @@ public class MetalTreeUI extends BasicTreeUI
boolean isExpanded, boolean hasBeenExpanded,
boolean isLeaf)
{
- super.paintHorizontalPartOfLeg(g, clipBounds, insets, bounds, path, row,
- isExpanded, hasBeenExpanded, isLeaf);
+ if (lineStyle == LINE_STYLE_ANGLED)
+ super.paintHorizontalPartOfLeg(g, clipBounds, insets, bounds, path, row,
+ isExpanded, hasBeenExpanded, isLeaf);
}
}
diff --git a/libjava/classpath/javax/swing/plaf/metal/OceanTheme.java b/libjava/classpath/javax/swing/plaf/metal/OceanTheme.java
index 9d76ff7e808..1ea0bc24385 100644
--- a/libjava/classpath/javax/swing/plaf/metal/OceanTheme.java
+++ b/libjava/classpath/javax/swing/plaf/metal/OceanTheme.java
@@ -266,6 +266,8 @@ public class OceanTheme extends DefaultMetalTheme
defaults.put("Tree.selectionBorderColor", PRIMARY1);
// Borders.
+ defaults.put("List.focusCellHighlightBorder",
+ new LineBorderUIResource(getPrimary1()));
defaults.put("Table.focusCellHighlightBorder",
new LineBorderUIResource(getPrimary1()));
diff --git a/libjava/classpath/javax/swing/plaf/synth/SynthLookAndFeel.java b/libjava/classpath/javax/swing/plaf/synth/SynthLookAndFeel.java
index 1a2489e7ea6..46a95f09860 100644
--- a/libjava/classpath/javax/swing/plaf/synth/SynthLookAndFeel.java
+++ b/libjava/classpath/javax/swing/plaf/synth/SynthLookAndFeel.java
@@ -216,9 +216,7 @@ public class SynthLookAndFeel
* @throws IllegalArgumentException if one of the parameters is
* <code>null</code>
*/
- // FIXME: The signature in the JDK has a Class<?> here. Should be fixed as
- // soon as we switch to the generics branch.
- public void load(InputStream in, Class resourceBase)
+ public void load(InputStream in, Class<?> resourceBase)
throws ParseException, IllegalArgumentException, NotImplementedException
{
// FIXME: Implement this correctly.
diff --git a/libjava/classpath/javax/swing/table/AbstractTableModel.java b/libjava/classpath/javax/swing/table/AbstractTableModel.java
index 7914e0b3f46..66b6a0743b6 100644
--- a/libjava/classpath/javax/swing/table/AbstractTableModel.java
+++ b/libjava/classpath/javax/swing/table/AbstractTableModel.java
@@ -125,7 +125,7 @@ public abstract class AbstractTableModel implements TableModel, Serializable
*
* @return The class.
*/
- public Class getColumnClass(int columnIndex)
+ public Class<?> getColumnClass(int columnIndex)
{
return Object.class;
}
@@ -294,7 +294,7 @@ public abstract class AbstractTableModel implements TableModel, Serializable
*
* @return An array of listeners (possibly empty).
*/
- public EventListener[] getListeners(Class listenerType)
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
{
return listenerList.getListeners(listenerType);
}
diff --git a/libjava/classpath/javax/swing/table/DefaultTableColumnModel.java b/libjava/classpath/javax/swing/table/DefaultTableColumnModel.java
index 33e68ea9fcd..e4e7201b184 100644
--- a/libjava/classpath/javax/swing/table/DefaultTableColumnModel.java
+++ b/libjava/classpath/javax/swing/table/DefaultTableColumnModel.java
@@ -71,7 +71,7 @@ public class DefaultTableColumnModel
/**
* Storage for the table columns.
*/
- protected Vector tableColumns;
+ protected Vector<TableColumn> tableColumns;
/**
* A selection model that keeps track of column selections.
@@ -187,7 +187,7 @@ public class DefaultTableColumnModel
throw new IllegalArgumentException("Index 'i' out of range.");
if (j < 0 || j >= columnCount)
throw new IllegalArgumentException("Index 'j' out of range.");
- Object column = tableColumns.remove(i);
+ TableColumn column = tableColumns.remove(i);
tableColumns.add(j, column);
fireColumnMoved(new TableColumnModelEvent(this, i, j));
}
@@ -221,7 +221,7 @@ public class DefaultTableColumnModel
*
* @return An enumeration of the columns in the model.
*/
- public Enumeration getColumns()
+ public Enumeration<TableColumn> getColumns()
{
return tableColumns.elements();
}
@@ -597,7 +597,7 @@ public class DefaultTableColumnModel
* @return An array containing the listeners (of the specified type) that
* are registered with this model.
*/
- public EventListener[] getListeners(Class listenerType)
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
{
return listenerList.getListeners(listenerType);
}
diff --git a/libjava/classpath/javax/swing/table/DefaultTableModel.java b/libjava/classpath/javax/swing/table/DefaultTableModel.java
index 79285903c00..1b68ce2be81 100644
--- a/libjava/classpath/javax/swing/table/DefaultTableModel.java
+++ b/libjava/classpath/javax/swing/table/DefaultTableModel.java
@@ -625,7 +625,7 @@ public class DefaultTableModel extends AbstractTableModel
if (columnCount > columnIdentifiers.size())
columnIdentifiers.setSize(columnCount);
- if (rowCount > dataVector.size())
+ if (dataVector != null && rowCount > dataVector.size())
{
int rowsToAdd = rowCount - dataVector.size();
addExtraRows(rowsToAdd, columnCount);
diff --git a/libjava/classpath/javax/swing/table/TableColumnModel.java b/libjava/classpath/javax/swing/table/TableColumnModel.java
index 986c0253323..7e8a70c3a5b 100644
--- a/libjava/classpath/javax/swing/table/TableColumnModel.java
+++ b/libjava/classpath/javax/swing/table/TableColumnModel.java
@@ -102,7 +102,7 @@ public interface TableColumnModel
*
* @return An enumeration of the columns in the model.
*/
- Enumeration getColumns();
+ Enumeration<TableColumn> getColumns();
/**
* Returns the index of the {@link TableColumn} with the given identifier.
diff --git a/libjava/classpath/javax/swing/table/TableModel.java b/libjava/classpath/javax/swing/table/TableModel.java
index 016ae171dd4..7629fa4e404 100644
--- a/libjava/classpath/javax/swing/table/TableModel.java
+++ b/libjava/classpath/javax/swing/table/TableModel.java
@@ -84,7 +84,7 @@ public interface TableModel
*
* @return The class.
*/
- Class getColumnClass(int columnIndex);
+ Class<?> getColumnClass(int columnIndex);
/**
* Returns <code>true</code> if the cell is editable, and <code>false</code>
diff --git a/libjava/classpath/javax/swing/text/AbstractDocument.java b/libjava/classpath/javax/swing/text/AbstractDocument.java
index eb46a8c42f6..eead8de5261 100644
--- a/libjava/classpath/javax/swing/text/AbstractDocument.java
+++ b/libjava/classpath/javax/swing/text/AbstractDocument.java
@@ -38,11 +38,15 @@ exception statement from your version. */
package javax.swing.text;
+import java.awt.font.TextAttribute;
import java.io.PrintStream;
import java.io.Serializable;
+import java.text.Bidi;
+import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.EventListener;
+import java.util.HashMap;
import java.util.Hashtable;
import java.util.Vector;
@@ -105,6 +109,21 @@ public abstract class AbstractDocument implements Document, Serializable
public static final String ElementNameAttribute = "$ename";
/**
+ * Standard name for the bidi root element.
+ */
+ private static final String BidiRootName = "bidi root";
+
+ /**
+ * Key for storing the asynchronous load priority.
+ */
+ private static final String AsyncLoadPriority = "load priority";
+
+ /**
+ * Key for storing the I18N state.
+ */
+ private static final String I18N = "i18n";
+
+ /**
* The actual content model of this <code>Document</code>.
*/
Content content;
@@ -140,14 +159,10 @@ public abstract class AbstractDocument implements Document, Serializable
private int numReaders = 0;
/**
- * Tells if there are one or more writers waiting.
+ * The number of current writers. If this is > 1 then the same thread entered
+ * the write lock more than once.
*/
- private int numWritersWaiting = 0;
-
- /**
- * A condition variable that readers and writers wait on.
- */
- private Object documentCV = new Object();
+ private int numWriters = 0;
/** An instance of a DocumentFilter.FilterBypass which allows calling
* the insert, remove and replace method without checking for an installed
@@ -158,7 +173,13 @@ public abstract class AbstractDocument implements Document, Serializable
/**
* The bidi root element.
*/
- private Element bidiRoot;
+ private BidiRootElement bidiRoot;
+
+ /**
+ * True when we are currently notifying any listeners. This is used
+ * to detect illegal situations in writeLock().
+ */
+ private transient boolean notifyListeners;
/**
* Creates a new <code>AbstractDocument</code> with the specified
@@ -191,12 +212,25 @@ public abstract class AbstractDocument implements Document, Serializable
content = doc;
context = ctx;
+ // FIXME: Fully implement bidi.
+ bidiRoot = new BidiRootElement();
+
// FIXME: This is determined using a Mauve test. Make the document
// actually use this.
- putProperty("i18n", Boolean.FALSE);
+ putProperty(I18N, Boolean.FALSE);
- // FIXME: Fully implement bidi.
- bidiRoot = new BranchElement(null, null);
+ // Add one child to the bidi root.
+ writeLock();
+ try
+ {
+ Element[] children = new Element[1];
+ children[0] = new BidiElement(bidiRoot, 0, 1, 0);
+ bidiRoot.replace(0, 0, children);
+ }
+ finally
+ {
+ writeUnlock();
+ }
}
/** Returns the DocumentFilter.FilterBypass instance for this
@@ -284,7 +318,8 @@ public abstract class AbstractDocument implements Document, Serializable
* @throws BadLocationException if <code>offset</code> is not a valid
* location in the documents content model
*/
- public Position createPosition(final int offset) throws BadLocationException
+ public synchronized Position createPosition(final int offset)
+ throws BadLocationException
{
return content.createPosition(offset);
}
@@ -296,10 +331,17 @@ public abstract class AbstractDocument implements Document, Serializable
*/
protected void fireChangedUpdate(DocumentEvent event)
{
- DocumentListener[] listeners = getDocumentListeners();
-
- for (int index = 0; index < listeners.length; ++index)
- listeners[index].changedUpdate(event);
+ notifyListeners = true;
+ try
+ {
+ DocumentListener[] listeners = getDocumentListeners();
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].changedUpdate(event);
+ }
+ finally
+ {
+ notifyListeners = false;
+ }
}
/**
@@ -310,10 +352,17 @@ public abstract class AbstractDocument implements Document, Serializable
*/
protected void fireInsertUpdate(DocumentEvent event)
{
- DocumentListener[] listeners = getDocumentListeners();
-
- for (int index = 0; index < listeners.length; ++index)
- listeners[index].insertUpdate(event);
+ notifyListeners = true;
+ try
+ {
+ DocumentListener[] listeners = getDocumentListeners();
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].insertUpdate(event);
+ }
+ finally
+ {
+ notifyListeners = false;
+ }
}
/**
@@ -324,10 +373,17 @@ public abstract class AbstractDocument implements Document, Serializable
*/
protected void fireRemoveUpdate(DocumentEvent event)
{
- DocumentListener[] listeners = getDocumentListeners();
-
- for (int index = 0; index < listeners.length; ++index)
- listeners[index].removeUpdate(event);
+ notifyListeners = true;
+ try
+ {
+ DocumentListener[] listeners = getDocumentListeners();
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].removeUpdate(event);
+ }
+ finally
+ {
+ notifyListeners = false;
+ }
}
/**
@@ -352,7 +408,11 @@ public abstract class AbstractDocument implements Document, Serializable
*/
public int getAsynchronousLoadPriority()
{
- return 0;
+ Object val = getProperty(AsyncLoadPriority);
+ int prio = -1;
+ if (val != null)
+ prio = ((Integer) val).intValue();
+ return prio;
}
/**
@@ -397,7 +457,7 @@ public abstract class AbstractDocument implements Document, Serializable
* @return the thread that currently modifies this <code>Document</code>
* if there is one, otherwise <code>null</code>
*/
- protected final Thread getCurrentWriter()
+ protected final synchronized Thread getCurrentWriter()
{
return currentWriter;
}
@@ -407,7 +467,7 @@ public abstract class AbstractDocument implements Document, Serializable
*
* @return the properties of this <code>Document</code>
*/
- public Dictionary getDocumentProperties()
+ public Dictionary<Object, Object> getDocumentProperties()
{
// FIXME: make me thread-safe
if (properties == null)
@@ -425,14 +485,17 @@ public abstract class AbstractDocument implements Document, Serializable
*/
public final Position getEndPosition()
{
- // FIXME: Properly implement this by calling Content.createPosition().
- return new Position()
- {
- public int getOffset()
- {
- return getLength();
- }
- };
+ Position p;
+ try
+ {
+ p = createPosition(content.length());
+ }
+ catch (BadLocationException ex)
+ {
+ // Shouldn't really happen.
+ p = null;
+ }
+ return p;
}
/**
@@ -455,7 +518,7 @@ public abstract class AbstractDocument implements Document, Serializable
*
* @return all registered listeners of the specified type
*/
- public EventListener[] getListeners(Class listenerType)
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
{
return listenerList.getListeners(listenerType);
}
@@ -504,14 +567,17 @@ public abstract class AbstractDocument implements Document, Serializable
*/
public final Position getStartPosition()
{
- // FIXME: Properly implement this using Content.createPosition().
- return new Position()
- {
- public int getOffset()
- {
- return 0;
- }
- };
+ Position p;
+ try
+ {
+ p = createPosition(0);
+ }
+ catch (BadLocationException ex)
+ {
+ // Shouldn't really happen.
+ p = null;
+ }
+ return p;
}
/**
@@ -574,11 +640,19 @@ public abstract class AbstractDocument implements Document, Serializable
// Bail out if we have a bogus insertion (Behavior observed in RI).
if (text == null || text.length() == 0)
return;
-
- if (documentFilter == null)
- insertStringImpl(offset, text, attributes);
- else
- documentFilter.insertString(getBypass(), offset, text, attributes);
+
+ writeLock();
+ try
+ {
+ if (documentFilter == null)
+ insertStringImpl(offset, text, attributes);
+ else
+ documentFilter.insertString(getBypass(), offset, text, attributes);
+ }
+ finally
+ {
+ writeUnlock();
+ }
}
void insertStringImpl(int offset, String text, AttributeSet attributes)
@@ -591,23 +665,30 @@ public abstract class AbstractDocument implements Document, Serializable
new DefaultDocumentEvent(offset, text.length(),
DocumentEvent.EventType.INSERT);
- try
- {
- writeLock();
- UndoableEdit undo = content.insertString(offset, text);
- if (undo != null)
- event.addEdit(undo);
-
- insertUpdate(event, attributes);
+ UndoableEdit undo = content.insertString(offset, text);
+ if (undo != null)
+ event.addEdit(undo);
- fireInsertUpdate(event);
- if (undo != null)
- fireUndoableEditUpdate(new UndoableEditEvent(this, undo));
- }
- finally
+ // Check if we need bidi layout.
+ if (getProperty(I18N).equals(Boolean.FALSE))
{
- writeUnlock();
+ Object dir = getProperty(TextAttribute.RUN_DIRECTION);
+ if (TextAttribute.RUN_DIRECTION_RTL.equals(dir))
+ putProperty(I18N, Boolean.TRUE);
+ else
+ {
+ char[] chars = text.toCharArray();
+ if (Bidi.requiresBidi(chars, 0, chars.length))
+ putProperty(I18N, Boolean.TRUE);
+ }
}
+
+ insertUpdate(event, attributes);
+
+ fireInsertUpdate(event);
+
+ if (undo != null)
+ fireUndoableEditUpdate(new UndoableEditEvent(this, undo));
}
/**
@@ -620,7 +701,8 @@ public abstract class AbstractDocument implements Document, Serializable
*/
protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet attr)
{
- // Do nothing here. Subclasses may want to override this.
+ if (Boolean.TRUE.equals(getProperty(I18N)))
+ updateBidi(chng);
}
/**
@@ -632,7 +714,8 @@ public abstract class AbstractDocument implements Document, Serializable
*/
protected void postRemoveUpdate(DefaultDocumentEvent chng)
{
- // Do nothing here. Subclasses may want to override this.
+ if (Boolean.TRUE.equals(getProperty(I18N)))
+ updateBidi(chng);
}
/**
@@ -647,31 +730,338 @@ public abstract class AbstractDocument implements Document, Serializable
if (properties == null)
properties = new Hashtable();
- properties.put(key, value);
+ if (value == null)
+ properties.remove(key);
+ else
+ properties.put(key, value);
+
+ // Update bidi structure if the RUN_DIRECTION is set.
+ if (TextAttribute.RUN_DIRECTION.equals(key))
+ {
+ if (TextAttribute.RUN_DIRECTION_RTL.equals(value)
+ && Boolean.FALSE.equals(getProperty(I18N)))
+ putProperty(I18N, Boolean.TRUE);
+
+ if (Boolean.TRUE.equals(getProperty(I18N)))
+ {
+ writeLock();
+ try
+ {
+ DefaultDocumentEvent ev =
+ new DefaultDocumentEvent(0, getLength(),
+ DocumentEvent.EventType.INSERT);
+ updateBidi(ev);
+ }
+ finally
+ {
+ writeUnlock();
+ }
+ }
+ }
}
/**
- * Blocks until a read lock can be obtained. Must block if there is
- * currently a writer modifying the <code>Document</code>.
+ * Updates the bidi element structure.
+ *
+ * @param ev the document event for the change
*/
- public final void readLock()
+ private void updateBidi(DefaultDocumentEvent ev)
{
- if (currentWriter != null && currentWriter.equals(Thread.currentThread()))
- return;
- synchronized (documentCV)
+ // Determine start and end offset of the paragraphs to be scanned.
+ int start = 0;
+ int end = 0;
+ DocumentEvent.EventType type = ev.getType();
+ if (type == DocumentEvent.EventType.INSERT
+ || type == DocumentEvent.EventType.CHANGE)
+ {
+ int offs = ev.getOffset();
+ int endOffs = offs + ev.getLength();
+ start = getParagraphElement(offs).getStartOffset();
+ end = getParagraphElement(endOffs).getEndOffset();
+ }
+ else if (type == DocumentEvent.EventType.REMOVE)
{
- while (currentWriter != null || numWritersWaiting > 0)
+ Element par = getParagraphElement(ev.getOffset());
+ start = par.getStartOffset();
+ end = par.getEndOffset();
+ }
+ else
+ assert false : "Unknown event type";
+
+ // Determine the bidi levels for the affected range.
+ Bidi[] bidis = getBidis(start, end);
+
+ int removeFrom = 0;
+ int removeTo = 0;
+
+ int offs = 0;
+ int lastRunStart = 0;
+ int lastRunEnd = 0;
+ int lastRunLevel = 0;
+ ArrayList newEls = new ArrayList();
+ for (int i = 0; i < bidis.length; i++)
+ {
+ Bidi bidi = bidis[i];
+ int numRuns = bidi.getRunCount();
+ for (int r = 0; r < numRuns; r++)
{
- try
+ if (r == 0 && i == 0)
+ {
+ if (start > 0)
+ {
+ // Try to merge with the previous element if it has the
+ // same bidi level as the first run.
+ int prevElIndex = bidiRoot.getElementIndex(start - 1);
+ removeFrom = prevElIndex;
+ Element prevEl = bidiRoot.getElement(prevElIndex);
+ AttributeSet atts = prevEl.getAttributes();
+ int prevElLevel = StyleConstants.getBidiLevel(atts);
+ if (prevElLevel == bidi.getRunLevel(r))
+ {
+ // Merge previous element with current run.
+ lastRunStart = prevEl.getStartOffset() - start;
+ lastRunEnd = bidi.getRunLimit(r);
+ lastRunLevel = bidi.getRunLevel(r);
+ }
+ else if (prevEl.getEndOffset() > start)
+ {
+ // Split previous element and replace by 2 new elements.
+ lastRunStart = 0;
+ lastRunEnd = bidi.getRunLimit(r);
+ lastRunLevel = bidi.getRunLevel(r);
+ newEls.add(new BidiElement(bidiRoot,
+ prevEl.getStartOffset(),
+ start, prevElLevel));
+ }
+ else
+ {
+ // Simply start new run at start location.
+ lastRunStart = 0;
+ lastRunEnd = bidi.getRunLimit(r);
+ lastRunLevel = bidi.getRunLevel(r);
+ removeFrom++;
+ }
+ }
+ else
+ {
+ // Simply start new run at start location.
+ lastRunStart = 0;
+ lastRunEnd = bidi.getRunLimit(r);
+ lastRunLevel = bidi.getRunLevel(r);
+ removeFrom = 0;
+ }
+ }
+ if (i == bidis.length - 1 && r == numRuns - 1)
{
- documentCV.wait();
+ if (end <= getLength())
+ {
+ // Try to merge last element with next element.
+ int nextIndex = bidiRoot.getElementIndex(end);
+ Element nextEl = bidiRoot.getElement(nextIndex);
+ AttributeSet atts = nextEl.getAttributes();
+ int nextLevel = StyleConstants.getBidiLevel(atts);
+ int level = bidi.getRunLevel(r);
+ if (lastRunLevel == level && level == nextLevel)
+ {
+ // Merge runs together.
+ if (lastRunStart + start == nextEl.getStartOffset())
+ removeTo = nextIndex - 1;
+ else
+ {
+ newEls.add(new BidiElement(bidiRoot, start + lastRunStart,
+ nextEl.getEndOffset(), level));
+ removeTo = nextIndex;
+ }
+ }
+ else if (lastRunLevel == level)
+ {
+ // Merge current and last run.
+ int endOffs = offs + bidi.getRunLimit(r);
+ newEls.add(new BidiElement(bidiRoot, start + lastRunStart,
+ start + endOffs, level));
+ if (start + endOffs == nextEl.getStartOffset())
+ removeTo = nextIndex - 1;
+ else
+ {
+ newEls.add(new BidiElement(bidiRoot, start + endOffs,
+ nextEl.getEndOffset(),
+ nextLevel));
+ removeTo = nextIndex;
+ }
+ }
+ else if (level == nextLevel)
+ {
+ // Merge current and next run.
+ newEls.add(new BidiElement(bidiRoot, start + lastRunStart,
+ start + lastRunEnd,
+ lastRunLevel));
+ newEls.add(new BidiElement(bidiRoot, start + lastRunEnd,
+ nextEl.getEndOffset(), level));
+ removeTo = nextIndex;
+ }
+ else
+ {
+ // Split next element.
+ int endOffs = offs + bidi.getRunLimit(r);
+ newEls.add(new BidiElement(bidiRoot, start + lastRunStart,
+ start + lastRunEnd,
+ lastRunLevel));
+ newEls.add(new BidiElement(bidiRoot, start + lastRunEnd,
+ start + endOffs, level));
+ newEls.add(new BidiElement(bidiRoot, start + endOffs,
+ nextEl.getEndOffset(),
+ nextLevel));
+ removeTo = nextIndex;
+ }
+ }
+ else
+ {
+ removeTo = bidiRoot.getElementIndex(end);
+ int level = bidi.getRunLevel(r);
+ int runEnd = offs + bidi.getRunLimit(r);
+
+ if (level == lastRunLevel)
+ {
+ // Merge with previous.
+ lastRunEnd = offs + runEnd;
+ newEls.add(new BidiElement(bidiRoot,
+ start + lastRunStart,
+ start + runEnd, level));
+ }
+ else
+ {
+ // Create element for last run and current run.
+ newEls.add(new BidiElement(bidiRoot, start + lastRunStart,
+ start + lastRunEnd,
+ lastRunLevel));
+ newEls.add(new BidiElement(bidiRoot,
+ start + lastRunEnd,
+ start + runEnd,
+ level));
+ }
+ }
}
- catch (InterruptedException ie)
+ else
{
- throw new Error("interrupted trying to get a readLock");
+ int level = bidi.getRunLevel(r);
+ int runEnd = bidi.getRunLimit(r);
+
+ if (level == lastRunLevel)
+ {
+ // Merge with previous.
+ lastRunEnd = offs + runEnd;
+ }
+ else
+ {
+ // Create element for last run and update values for
+ // current run.
+ newEls.add(new BidiElement(bidiRoot, start + lastRunStart,
+ start + lastRunEnd,
+ lastRunLevel));
+ lastRunStart = lastRunEnd;
+ lastRunEnd = offs + runEnd;
+ lastRunLevel = level;
+ }
}
}
- numReaders++;
+ offs += bidi.getLength();
+ }
+
+ // Determine the bidi elements which are to be removed.
+ int numRemoved = 0;
+ if (bidiRoot.getElementCount() > 0)
+ numRemoved = removeTo - removeFrom + 1;
+ Element[] removed = new Element[numRemoved];
+ for (int i = 0; i < numRemoved; i++)
+ removed[i] = bidiRoot.getElement(removeFrom + i);
+
+ Element[] added = new Element[newEls.size()];
+ added = (Element[]) newEls.toArray(added);
+
+ // Update the event.
+ ElementEdit edit = new ElementEdit(bidiRoot, removeFrom, removed, added);
+ ev.addEdit(edit);
+
+ // Update the structure.
+ bidiRoot.replace(removeFrom, numRemoved, added);
+ }
+
+ /**
+ * Determines the Bidi objects for the paragraphs in the specified range.
+ *
+ * @param start the start of the range
+ * @param end the end of the range
+ *
+ * @return the Bidi analysers for the paragraphs in the range
+ */
+ private Bidi[] getBidis(int start, int end)
+ {
+ // Determine the default run direction from the document property.
+ Boolean defaultDir = null;
+ Object o = getProperty(TextAttribute.RUN_DIRECTION);
+ if (o instanceof Boolean)
+ defaultDir = (Boolean) o;
+
+ // Scan paragraphs and add their level arrays to the overall levels array.
+ ArrayList bidis = new ArrayList();
+ Segment s = new Segment();
+ for (int i = start; i < end;)
+ {
+ Element par = getParagraphElement(i);
+ int pStart = par.getStartOffset();
+ int pEnd = par.getEndOffset();
+
+ // Determine the default run direction of the paragraph.
+ Boolean dir = defaultDir;
+ o = par.getAttributes().getAttribute(TextAttribute.RUN_DIRECTION);
+ if (o instanceof Boolean)
+ dir = (Boolean) o;
+
+ // Bidi over the paragraph.
+ try
+ {
+ getText(pStart, pEnd - pStart, s);
+ }
+ catch (BadLocationException ex)
+ {
+ assert false : "Must not happen";
+ }
+ int flag = Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT;
+ if (dir != null)
+ {
+ if (TextAttribute.RUN_DIRECTION_LTR.equals(dir))
+ flag = Bidi.DIRECTION_LEFT_TO_RIGHT;
+ else
+ flag = Bidi.DIRECTION_RIGHT_TO_LEFT;
+ }
+ Bidi bidi = new Bidi(s.array, s.offset, null, 0, s.count, flag);
+ bidis.add(bidi);
+ i = pEnd;
+ }
+ Bidi[] ret = new Bidi[bidis.size()];
+ ret = (Bidi[]) bidis.toArray(ret);
+ return ret;
+ }
+
+ /**
+ * Blocks until a read lock can be obtained. Must block if there is
+ * currently a writer modifying the <code>Document</code>.
+ */
+ public final synchronized void readLock()
+ {
+ try
+ {
+ while (currentWriter != null)
+ {
+ if (currentWriter == Thread.currentThread())
+ return;
+ wait();
+ }
+ numReaders++;
+ }
+ catch (InterruptedException ex)
+ {
+ throw new Error("Interrupted during grab read lock");
}
}
@@ -679,7 +1069,7 @@ public abstract class AbstractDocument implements Document, Serializable
* Releases the read lock. If this was the only reader on this
* <code>Document</code>, writing may begin now.
*/
- public final void readUnlock()
+ public final synchronized void readUnlock()
{
// Note we could have a problem here if readUnlock was called without a
// prior call to readLock but the specs simply warn users to ensure that
@@ -706,21 +1096,14 @@ public abstract class AbstractDocument implements Document, Serializable
// FIXME: the reference implementation throws a
// javax.swing.text.StateInvariantError here
- if (numReaders == 0)
+ if (numReaders <= 0)
throw new IllegalStateException("document lock failure");
- synchronized (documentCV)
- {
- // If currentWriter is not null, the application code probably had a
- // writeLock and then tried to obtain a readLock, in which case
- // numReaders wasn't incremented
- if (currentWriter == null)
- {
- numReaders --;
- if (numReaders == 0 && numWritersWaiting != 0)
- documentCV.notify();
- }
- }
+ // If currentWriter is not null, the application code probably had a
+ // writeLock and then tried to obtain a readLock, in which case
+ // numReaders wasn't incremented
+ numReaders--;
+ notify();
}
/**
@@ -744,12 +1127,21 @@ public abstract class AbstractDocument implements Document, Serializable
*/
public void remove(int offset, int length) throws BadLocationException
{
- if (documentFilter == null)
- removeImpl(offset, length);
- else
- documentFilter.remove(getBypass(), offset, length);
+ writeLock();
+ try
+ {
+ DocumentFilter f = getDocumentFilter();
+ if (f == null)
+ removeImpl(offset, length);
+ else
+ f.remove(getBypass(), offset, length);
+ }
+ finally
+ {
+ writeUnlock();
+ }
}
-
+
void removeImpl(int offset, int length) throws BadLocationException
{
// The RI silently ignores all requests that have a negative length.
@@ -766,21 +1158,12 @@ public abstract class AbstractDocument implements Document, Serializable
new DefaultDocumentEvent(offset, length,
DocumentEvent.EventType.REMOVE);
- try
- {
- writeLock();
-
- // The order of the operations below is critical!
- removeUpdate(event);
- UndoableEdit temp = content.remove(offset, length);
-
- postRemoveUpdate(event);
- fireRemoveUpdate(event);
- }
- finally
- {
- writeUnlock();
- }
+ // The order of the operations below is critical!
+ removeUpdate(event);
+ UndoableEdit temp = content.remove(offset, length);
+
+ postRemoveUpdate(event);
+ fireRemoveUpdate(event);
}
}
@@ -814,21 +1197,28 @@ public abstract class AbstractDocument implements Document, Serializable
if (length == 0
&& (text == null || text.length() == 0))
return;
-
- if (documentFilter == null)
+
+ writeLock();
+ try
{
- // It is important to call the methods which again do the checks
- // of the arguments and the DocumentFilter because subclasses may
- // have overridden these methods and provide crucial behavior
- // which would be skipped if we call the non-checking variants.
- // An example for this is PlainDocument where insertString can
- // provide a filtering of newlines.
- remove(offset, length);
- insertString(offset, text, attributes);
+ if (documentFilter == null)
+ {
+ // It is important to call the methods which again do the checks
+ // of the arguments and the DocumentFilter because subclasses may
+ // have overridden these methods and provide crucial behavior
+ // which would be skipped if we call the non-checking variants.
+ // An example for this is PlainDocument where insertString can
+ // provide a filtering of newlines.
+ remove(offset, length);
+ insertString(offset, text, attributes);
+ }
+ else
+ documentFilter.replace(getBypass(), offset, length, text, attributes);
+ }
+ finally
+ {
+ writeUnlock();
}
- else
- documentFilter.replace(getBypass(), offset, length, text, attributes);
-
}
void replaceImpl(int offset, int length, String text,
@@ -948,7 +1338,8 @@ public abstract class AbstractDocument implements Document, Serializable
*/
public void setAsynchronousLoadPriority(int p)
{
- // TODO: Implement this properly.
+ Integer val = p >= 0 ? new Integer(p) : null;
+ putProperty(AsyncLoadPriority, val);
}
/**
@@ -956,7 +1347,7 @@ public abstract class AbstractDocument implements Document, Serializable
*
* @param p the document properties to set
*/
- public void setDocumentProperties(Dictionary p)
+ public void setDocumentProperties(Dictionary<Object, Object> p)
{
// FIXME: make me thread-safe
properties = p;
@@ -966,26 +1357,27 @@ public abstract class AbstractDocument implements Document, Serializable
* Blocks until a write lock can be obtained. Must wait if there are
* readers currently reading or another thread is currently writing.
*/
- protected final void writeLock()
+ protected synchronized final void writeLock()
{
- if (currentWriter != null && currentWriter.equals(Thread.currentThread()))
- return;
- synchronized (documentCV)
+ try
{
- numWritersWaiting++;
- while (numReaders > 0)
+ while (numReaders > 0 || currentWriter != null)
{
- try
- {
- documentCV.wait();
- }
- catch (InterruptedException ie)
+ if (Thread.currentThread() == currentWriter)
{
- throw new Error("interruped while trying to obtain write lock");
+ if (notifyListeners)
+ throw new IllegalStateException("Mutation during notify");
+ numWriters++;
+ return;
}
+ wait();
}
- numWritersWaiting --;
currentWriter = Thread.currentThread();
+ numWriters = 1;
+ }
+ catch (InterruptedException ex)
+ {
+ throw new Error("Interupted during grab write lock");
}
}
@@ -993,16 +1385,14 @@ public abstract class AbstractDocument implements Document, Serializable
* Releases the write lock. This allows waiting readers or writers to
* obtain the lock.
*/
- protected final void writeUnlock()
+ protected final synchronized void writeUnlock()
{
- synchronized (documentCV)
- {
- if (Thread.currentThread().equals(currentWriter))
- {
- currentWriter = null;
- documentCV.notifyAll();
- }
- }
+ if (--numWriters <= 0)
+ {
+ numWriters = 0;
+ currentWriter = null;
+ notifyAll();
+ }
}
/**
@@ -1039,6 +1429,7 @@ public abstract class AbstractDocument implements Document, Serializable
public void dump(PrintStream out)
{
((AbstractElement) getDefaultRootElement()).dump(out, 0);
+ ((AbstractElement) getBidiRootElement()).dump(out, 0);
}
/**
@@ -1130,7 +1521,7 @@ public abstract class AbstractDocument implements Document, Serializable
* @return the attributes of <code>old</code> minus the attributes in
* <code>attributes</code>
*/
- AttributeSet removeAttributes(AttributeSet old, Enumeration names);
+ AttributeSet removeAttributes(AttributeSet old, Enumeration<?> names);
}
/**
@@ -1255,7 +1646,7 @@ public abstract class AbstractDocument implements Document, Serializable
AttributeContext ctx = getAttributeContext();
attributes = ctx.getEmptySet();
if (s != null)
- attributes = ctx.addAttributes(attributes, s);
+ addAttributes(s);
}
/**
@@ -1386,7 +1777,7 @@ public abstract class AbstractDocument implements Document, Serializable
*
* @param names the names of the attributes to be removed
*/
- public void removeAttributes(Enumeration names)
+ public void removeAttributes(Enumeration<?> names)
{
attributes = getAttributeContext().removeAttributes(attributes, names);
}
@@ -1481,7 +1872,7 @@ public abstract class AbstractDocument implements Document, Serializable
*
* @return the names of the attributes of this element
*/
- public Enumeration getAttributeNames()
+ public Enumeration<?> getAttributeNames()
{
return attributes.getAttributeNames();
}
@@ -1567,7 +1958,7 @@ public abstract class AbstractDocument implements Document, Serializable
*/
public String getName()
{
- return (String) getAttribute(NameAttribute);
+ return (String) attributes.getAttribute(ElementNameAttribute);
}
/**
@@ -1644,6 +2035,11 @@ public abstract class AbstractDocument implements Document, Serializable
b.append('\n');
}
}
+ if (getAttributeCount() > 0)
+ {
+ for (int i = 0; i < indent; ++i)
+ b.append(' ');
+ }
b.append(">\n");
// Dump element content for leaf elements.
@@ -1705,6 +2101,11 @@ public abstract class AbstractDocument implements Document, Serializable
private int numChildren;
/**
+ * The last found index in getElementIndex(). Used for faster searching.
+ */
+ private int lastIndex;
+
+ /**
* Creates a new <code>BranchElement</code> with the specified
* parent and attributes.
*
@@ -1717,6 +2118,7 @@ public abstract class AbstractDocument implements Document, Serializable
super(parent, attributes);
children = new Element[1];
numChildren = 0;
+ lastIndex = -1;
}
/**
@@ -1726,7 +2128,7 @@ public abstract class AbstractDocument implements Document, Serializable
*/
public Enumeration children()
{
- if (children.length == 0)
+ if (numChildren == 0)
return null;
Vector tmp = new Vector();
@@ -1785,35 +2187,73 @@ public abstract class AbstractDocument implements Document, Serializable
*/
public int getElementIndex(int offset)
{
- // If offset is less than the start offset of our first child,
- // return 0
- if (offset < getStartOffset())
- return 0;
+ // Implemented using an improved linear search.
+ // This makes use of the fact that searches are not random but often
+ // close to the previous search. So we try to start the binary
+ // search at the last found index.
- // XXX: There is surely a better algorithm
- // as beginning from first element each time.
- for (int index = 0; index < numChildren - 1; ++index)
+ int i0 = 0; // The lower bounds.
+ int i1 = numChildren - 1; // The upper bounds.
+ int index = -1; // The found index.
+
+ int p0 = getStartOffset();
+ int p1; // Start and end offset local variables.
+
+ if (numChildren == 0)
+ index = 0;
+ else if (offset >= getEndOffset())
+ index = numChildren - 1;
+ else
{
- Element elem = children[index];
-
- if ((elem.getStartOffset() <= offset)
- && (offset < elem.getEndOffset()))
- return index;
- // If the next element's start offset is greater than offset
- // then we have to return the closest Element, since no Elements
- // will contain the offset
- if (children[index + 1].getStartOffset() > offset)
+ // Try lastIndex.
+ if (lastIndex >= i0 && lastIndex <= i1)
{
- if ((offset - elem.getEndOffset()) > (children[index + 1].getStartOffset() - offset))
- return index + 1;
+ Element last = getElement(lastIndex);
+ p0 = last.getStartOffset();
+ p1 = last.getEndOffset();
+ if (offset >= p0 && offset < p1)
+ index = lastIndex;
else
- return index;
+ {
+ // Narrow the search bounds using the lastIndex, even
+ // if it hasn't been a hit.
+ if (offset < p0)
+ i1 = lastIndex;
+ else
+ i0 = lastIndex;
+ }
+ }
+ // The actual search.
+ int i = 0;
+ while (i0 <= i1 && index == -1)
+ {
+ i = i0 + (i1 - i0) / 2;
+ Element el = getElement(i);
+ p0 = el.getStartOffset();
+ p1 = el.getEndOffset();
+ if (offset >= p0 && offset < p1)
+ {
+ // Found it!
+ index = i;
+ }
+ else if (offset < p0)
+ i1 = i - 1;
+ else
+ i0 = i + 1;
}
- }
- // If offset is greater than the index of the last element, return
- // the index of the last element.
- return getElementCount() - 1;
+ if (index == -1)
+ {
+ // Didn't find it. Return the boundary index.
+ if (offset < p0)
+ index = i;
+ else
+ index = i + 1;
+ }
+
+ lastIndex = index;
+ }
+ return index;
}
/**
@@ -1957,6 +2397,11 @@ public abstract class AbstractDocument implements Document, Serializable
/** The serialization UID (compatible with JDK1.5). */
private static final long serialVersionUID = 5230037221564563284L;
+ /**
+ * The threshold that indicates when we switch to using a Hashtable.
+ */
+ private static final int THRESHOLD = 10;
+
/** The starting offset of the change. */
private int offset;
@@ -1967,15 +2412,18 @@ public abstract class AbstractDocument implements Document, Serializable
private DocumentEvent.EventType type;
/**
- * Maps <code>Element</code> to their change records.
+ * Maps <code>Element</code> to their change records. This is only
+ * used when the changes array gets too big. We can use an
+ * (unsync'ed) HashMap here, since changes to this are (should) always
+ * be performed inside a write lock.
*/
- Hashtable changes;
+ private HashMap changes;
/**
* Indicates if this event has been modified or not. This is used to
* determine if this event is thrown.
*/
- boolean modified;
+ private boolean modified;
/**
* Creates a new <code>DefaultDocumentEvent</code>.
@@ -1990,7 +2438,6 @@ public abstract class AbstractDocument implements Document, Serializable
this.offset = offset;
this.length = length;
this.type = type;
- changes = new Hashtable();
modified = false;
}
@@ -2004,9 +2451,27 @@ public abstract class AbstractDocument implements Document, Serializable
public boolean addEdit(UndoableEdit edit)
{
// XXX - Fully qualify ElementChange to work around gcj bug #2499.
- if (edit instanceof DocumentEvent.ElementChange)
+
+ // Start using Hashtable when we pass a certain threshold. This
+ // gives a good memory/performance compromise.
+ if (changes == null && edits.size() > THRESHOLD)
+ {
+ changes = new HashMap();
+ int count = edits.size();
+ for (int i = 0; i < count; i++)
+ {
+ Object o = edits.elementAt(i);
+ if (o instanceof DocumentEvent.ElementChange)
+ {
+ DocumentEvent.ElementChange ec =
+ (DocumentEvent.ElementChange) o;
+ changes.put(ec.getElement(), ec);
+ }
+ }
+ }
+
+ if (changes != null && edit instanceof DocumentEvent.ElementChange)
{
- modified = true;
DocumentEvent.ElementChange elEdit =
(DocumentEvent.ElementChange) edit;
changes.put(elEdit.getElement(), elEdit);
@@ -2065,7 +2530,27 @@ public abstract class AbstractDocument implements Document, Serializable
public DocumentEvent.ElementChange getChange(Element elem)
{
// XXX - Fully qualify ElementChange to work around gcj bug #2499.
- return (DocumentEvent.ElementChange) changes.get(elem);
+ DocumentEvent.ElementChange change = null;
+ if (changes != null)
+ {
+ change = (DocumentEvent.ElementChange) changes.get(elem);
+ }
+ else
+ {
+ int count = edits.size();
+ for (int i = 0; i < count && change == null; i++)
+ {
+ Object o = edits.get(i);
+ if (o instanceof DocumentEvent.ElementChange)
+ {
+ DocumentEvent.ElementChange ec =
+ (DocumentEvent.ElementChange) o;
+ if (elem.equals(ec.getElement()))
+ change = ec;
+ }
+ }
+ }
+ return change;
}
/**
@@ -2333,7 +2818,63 @@ public abstract class AbstractDocument implements Document, Serializable
+ getStartOffset() + "," + getEndOffset() + "\n");
}
}
-
+
+ /**
+ * The root element for bidirectional text.
+ */
+ private class BidiRootElement
+ extends BranchElement
+ {
+ /**
+ * Creates a new bidi root element.
+ */
+ BidiRootElement()
+ {
+ super(null, null);
+ }
+
+ /**
+ * Returns the name of the element.
+ *
+ * @return the name of the element
+ */
+ public String getName()
+ {
+ return BidiRootName;
+ }
+ }
+
+ /**
+ * A leaf element for the bidi structure.
+ */
+ private class BidiElement
+ extends LeafElement
+ {
+ /**
+ * Creates a new BidiElement.
+ *
+ * @param parent the parent element
+ * @param start the start offset
+ * @param end the end offset
+ * @param level the bidi level
+ */
+ BidiElement(Element parent, int start, int end, int level)
+ {
+ super(parent, new SimpleAttributeSet(), start, end);
+ addAttribute(StyleConstants.BidiLevel, new Integer(level));
+ }
+
+ /**
+ * Returns the name of the element.
+ *
+ * @return the name of the element
+ */
+ public String getName()
+ {
+ return BidiElementName;
+ }
+ }
+
/** A class whose methods delegate to the insert, remove and replace methods
* of this document which do not check for an installed DocumentFilter.
*/
diff --git a/libjava/classpath/javax/swing/text/AttributeSet.java b/libjava/classpath/javax/swing/text/AttributeSet.java
index 01d148c067b..2d39881c28b 100644
--- a/libjava/classpath/javax/swing/text/AttributeSet.java
+++ b/libjava/classpath/javax/swing/text/AttributeSet.java
@@ -158,7 +158,7 @@ public interface AttributeSet
* @return the names of the attributes that are stored in this
* <code>AttributeSet</code>
*/
- Enumeration getAttributeNames();
+ Enumeration<?> getAttributeNames();
/**
* Returns the resolving parent of this <code>AttributeSet</code>.
diff --git a/libjava/classpath/javax/swing/text/BoxView.java b/libjava/classpath/javax/swing/text/BoxView.java
index 27e3c0f9a1b..0754d9b9b8b 100644
--- a/libjava/classpath/javax/swing/text/BoxView.java
+++ b/libjava/classpath/javax/swing/text/BoxView.java
@@ -38,6 +38,7 @@ exception statement from your version. */
package javax.swing.text;
+import java.awt.Container;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Shape;
@@ -92,11 +93,6 @@ public class BoxView
private int[] span = new int[2];
/**
- * The SizeRequirements of the child views along the X_AXIS and Y_AXIS.
- */
- private SizeRequirements[][] childReqs = new SizeRequirements[2][];
-
- /**
* Creates a new <code>BoxView</code> for the given
* <code>Element</code> and axis. Valid values for the axis are
* {@link View#X_AXIS} and {@link View#Y_AXIS}.
@@ -110,6 +106,8 @@ public class BoxView
myAxis = axis;
layoutValid[0] = false;
layoutValid[1] = false;
+ requirementsValid[X_AXIS] = false;
+ requirementsValid[Y_AXIS] = false;
span[0] = 0;
span[1] = 0;
requirements[0] = new SizeRequirements();
@@ -146,7 +144,10 @@ public class BoxView
*/
public void setAxis(int axis)
{
+ boolean changed = axis != myAxis;
myAxis = axis;
+ if (changed)
+ preferenceChanged(null, true, true);
}
/**
@@ -227,56 +228,49 @@ public class BoxView
*/
public void replace(int offset, int length, View[] views)
{
- int numViews = 0;
- if (views != null)
- numViews = views.length;
+ // Actually perform the replace.
+ super.replace(offset, length, views);
// Resize and copy data for cache arrays.
- // The spansX cache.
- int oldSize = getViewCount();
-
- int[] newSpansX = new int[oldSize - length + numViews];
- System.arraycopy(spans[X_AXIS], 0, newSpansX, 0, offset);
- System.arraycopy(spans[X_AXIS], offset + length, newSpansX,
- offset + numViews,
- oldSize - (offset + length));
- spans[X_AXIS] = newSpansX;
-
- // The spansY cache.
- int[] newSpansY = new int[oldSize - length + numViews];
- System.arraycopy(spans[Y_AXIS], 0, newSpansY, 0, offset);
- System.arraycopy(spans[Y_AXIS], offset + length, newSpansY,
- offset + numViews,
- oldSize - (offset + length));
- spans[Y_AXIS] = newSpansY;
-
- // The offsetsX cache.
- int[] newOffsetsX = new int[oldSize - length + numViews];
- System.arraycopy(offsets[X_AXIS], 0, newOffsetsX, 0, offset);
- System.arraycopy(offsets[X_AXIS], offset + length, newOffsetsX,
- offset + numViews,
- oldSize - (offset + length));
- offsets[X_AXIS] = newOffsetsX;
-
- // The offsetsY cache.
- int[] newOffsetsY = new int[oldSize - length + numViews];
- System.arraycopy(offsets[Y_AXIS], 0, newOffsetsY, 0, offset);
- System.arraycopy(offsets[Y_AXIS], offset + length, newOffsetsY,
- offset + numViews,
- oldSize - (offset + length));
- offsets[Y_AXIS] = newOffsetsY;
+ int newItems = views != null ? views.length : 0;
+ int minor = 1 - myAxis;
+ offsets[myAxis] = replaceLayoutArray(offsets[myAxis], offset, newItems);
+ spans[myAxis] = replaceLayoutArray(spans[myAxis], offset, newItems);
+ layoutValid[myAxis] = false;
+ requirementsValid[myAxis] = false;
+ offsets[minor] = replaceLayoutArray(offsets[minor], offset, newItems);
+ spans[minor] = replaceLayoutArray(spans[minor], offset, newItems);
+ layoutValid[minor] = false;
+ requirementsValid[minor] = false;
+ }
- // Actually perform the replace.
- super.replace(offset, length, views);
+ /**
+ * Helper method. This updates the layout cache arrays in response
+ * to a call to {@link #replace(int, int, View[])}.
+ *
+ * @param oldArray the old array
+ *
+ * @return the replaced array
+ */
+ private int[] replaceLayoutArray(int[] oldArray, int offset, int newItems)
- // Invalidate layout information.
- layoutValid[X_AXIS] = false;
- requirementsValid[X_AXIS] = false;
- layoutValid[Y_AXIS] = false;
- requirementsValid[Y_AXIS] = false;
+ {
+ int num = getViewCount();
+ int[] newArray = new int[num];
+ System.arraycopy(oldArray, 0, newArray, 0, offset);
+ System.arraycopy(oldArray, offset, newArray, offset + newItems,
+ num - newItems - offset);
+ return newArray;
}
/**
+ * A Rectangle instance to be reused in the paint() method below.
+ */
+ private final Rectangle tmpRect = new Rectangle();
+
+ private Rectangle clipRect = new Rectangle();
+
+ /**
* Renders the <code>Element</code> that is associated with this
* <code>View</code>.
*
@@ -285,26 +279,20 @@ public class BoxView
*/
public void paint(Graphics g, Shape a)
{
- Rectangle alloc;
- if (a instanceof Rectangle)
- alloc = (Rectangle) a;
- else
- alloc = a.getBounds();
+ // Try to avoid allocation if possible (almost all cases).
+ Rectangle alloc = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
- int x = alloc.x + getLeftInset();
- int y = alloc.y + getTopInset();
+ // This returns a cached instance.
+ alloc = getInsideAllocation(alloc);
- Rectangle clip = g.getClipBounds();
- Rectangle tmp = new Rectangle();
int count = getViewCount();
- for (int i = 0; i < count; ++i)
+ for (int i = 0; i < count; i++)
{
- tmp.x = x + getOffset(X_AXIS, i);
- tmp.y = y + getOffset(Y_AXIS, i);
- tmp.width = getSpan(X_AXIS, i);
- tmp.height = getSpan(Y_AXIS, i);
- if (tmp.intersects(clip))
- paintChild(g, tmp, i);
+ View child = getView(i);
+ tmpRect.setBounds(alloc);
+ childAllocation(i, tmpRect);
+ if (g.hitClip(tmpRect.x, tmpRect.y, tmpRect.width, tmpRect.height))
+ paintChild(g, tmpRect, i);
}
}
@@ -373,9 +361,9 @@ public class BoxView
}
/**
- * This method is obsolete and no longer in use. It is replaced by
- * {@link #calculateMajorAxisRequirements(int, SizeRequirements)} and
- * {@link #calculateMinorAxisRequirements(int, SizeRequirements)}.
+ * Calculates size requirements for a baseline layout. This is not
+ * used by the BoxView itself, but by subclasses that wish to perform
+ * a baseline layout, like the FlowView's rows.
*
* @param axis the axis that is examined
* @param sr the <code>SizeRequirements</code> object to hold the result,
@@ -387,50 +375,94 @@ public class BoxView
protected SizeRequirements baselineRequirements(int axis,
SizeRequirements sr)
{
- updateChildRequirements(axis);
+ // Create new instance if sr == null.
+ if (sr == null)
+ sr = new SizeRequirements();
+ sr.alignment = 0.5F;
+
+ // Calculate overall ascent and descent.
+ int totalAscentMin = 0;
+ int totalAscentPref = 0;
+ int totalAscentMax = 0;
+ int totalDescentMin = 0;
+ int totalDescentPref = 0;
+ int totalDescentMax = 0;
+
+ int count = getViewCount();
+ for (int i = 0; i < count; i++)
+ {
+ View v = getView(i);
+ float align = v.getAlignment(axis);
+ int span = (int) v.getPreferredSpan(axis);
+ int ascent = (int) (align * span);
+ int descent = span - ascent;
+
+ totalAscentPref = Math.max(ascent, totalAscentPref);
+ totalDescentPref = Math.max(descent, totalDescentPref);
+ if (v.getResizeWeight(axis) > 0)
+ {
+ // If the view is resizable, then use the min and max size
+ // of the view.
+ span = (int) v.getMinimumSpan(axis);
+ ascent = (int) (align * span);
+ descent = span - ascent;
+ totalAscentMin = Math.max(ascent, totalAscentMin);
+ totalDescentMin = Math.max(descent, totalDescentMin);
+
+ span = (int) v.getMaximumSpan(axis);
+ ascent = (int) (align * span);
+ descent = span - ascent;
+ totalAscentMax = Math.max(ascent, totalAscentMax);
+ totalDescentMax = Math.max(descent, totalDescentMax);
+ }
+ else
+ {
+ // If the view is not resizable, use the preferred span.
+ totalAscentMin = Math.max(ascent, totalAscentMin);
+ totalDescentMin = Math.max(descent, totalDescentMin);
+ totalAscentMax = Math.max(ascent, totalAscentMax);
+ totalDescentMax = Math.max(descent, totalDescentMax);
+ }
+ }
- SizeRequirements res = sr;
- if (res == null)
- res = new SizeRequirements();
+ // Preferred overall span is the sum of the preferred ascent and descent.
+ // With overflow check.
+ sr.preferred = (int) Math.min((long) totalAscentPref
+ + (long) totalDescentPref,
+ Integer.MAX_VALUE);
+
+ // Align along the baseline.
+ if (sr.preferred > 0)
+ sr.alignment = (float) totalAscentPref / sr.preferred;
- float minLeft = 0;
- float minRight = 0;
- float prefLeft = 0;
- float prefRight = 0;
- float maxLeft = 0;
- float maxRight = 0;
- for (int i = 0; i < childReqs[axis].length; i++)
+ if (sr.alignment == 0)
{
- float myMinLeft = childReqs[axis][i].minimum * childReqs[axis][i].alignment;
- float myMinRight = childReqs[axis][i].minimum - myMinLeft;
- minLeft = Math.max(myMinLeft, minLeft);
- minRight = Math.max(myMinRight, minRight);
- float myPrefLeft = childReqs[axis][i].preferred * childReqs[axis][i].alignment;
- float myPrefRight = childReqs[axis][i].preferred - myPrefLeft;
- prefLeft = Math.max(myPrefLeft, prefLeft);
- prefRight = Math.max(myPrefRight, prefRight);
- float myMaxLeft = childReqs[axis][i].maximum * childReqs[axis][i].alignment;
- float myMaxRight = childReqs[axis][i].maximum - myMaxLeft;
- maxLeft = Math.max(myMaxLeft, maxLeft);
- maxRight = Math.max(myMaxRight, maxRight);
+ // Nothing above the baseline, use the descent.
+ sr.minimum = totalDescentMin;
+ sr.maximum = totalDescentMax;
}
- int minSize = (int) (minLeft + minRight);
- int prefSize = (int) (prefLeft + prefRight);
- int maxSize = (int) (maxLeft + maxRight);
- float align = prefLeft / (prefRight + prefLeft);
- if (Float.isNaN(align))
- align = 0;
-
- res.alignment = align;
- res.maximum = maxSize;
- res.preferred = prefSize;
- res.minimum = minSize;
- return res;
+ else if (sr.alignment == 1.0F)
+ {
+ // Nothing below the baseline, use the descent.
+ sr.minimum = totalAscentMin;
+ sr.maximum = totalAscentMax;
+ }
+ else
+ {
+ sr.minimum = Math.max((int) (totalAscentMin / sr.alignment),
+ (int) (totalDescentMin / (1.0F - sr.alignment)));
+ sr.maximum = Math.min((int) (totalAscentMax / sr.alignment),
+ (int) (totalDescentMax / (1.0F - sr.alignment)));
+ }
+ return sr;
}
/**
- * Calculates the layout of the children of this <code>BoxView</code> along
- * the specified axis.
+ * Calculates the baseline layout of the children of this
+ * <code>BoxView</code> along the specified axis.
+ *
+ * This is not used by the BoxView itself, but by subclasses that wish to
+ * perform a baseline layout, like the FlowView's rows.
*
* @param span the target span
* @param axis the axis that is examined
@@ -440,13 +472,36 @@ public class BoxView
protected void baselineLayout(int span, int axis, int[] offsets,
int[] spans)
{
- updateChildRequirements(axis);
- updateRequirements(axis);
+ int totalAscent = (int) (span * getAlignment(axis));
+ int totalDescent = span - totalAscent;
- // Calculate the spans and offsets using the SizeRequirements uility
- // methods.
- SizeRequirements.calculateAlignedPositions(span, requirements[axis],
- childReqs[axis], offsets, spans);
+ int count = getViewCount();
+ for (int i = 0; i < count; i++)
+ {
+ View v = getView(i);
+ float align = v.getAlignment(axis);
+ int viewSpan;
+ if (v.getResizeWeight(axis) > 0)
+ {
+ // If possible, then resize for best fit.
+ int min = (int) v.getMinimumSpan(axis);
+ int max = (int) v.getMaximumSpan(axis);
+ if (align == 0.0F)
+ viewSpan = Math.max(Math.min(max, totalDescent), min);
+ else if (align == 1.0F)
+ viewSpan = Math.max(Math.min(max, totalAscent), min);
+ else
+ {
+ int fit = (int) Math.min(totalAscent / align,
+ totalDescent / (1.0F - align));
+ viewSpan = Math.max(Math.min(max, fit), min);
+ }
+ }
+ else
+ viewSpan = (int) v.getPreferredSpan(axis);
+ offsets[i] = totalAscent - (int) (viewSpan * align);
+ spans[i] = viewSpan;
+ }
}
/**
@@ -476,8 +531,8 @@ public class BoxView
{
View child = getView(i);
min += child.getMinimumSpan(axis);
- pref = child.getPreferredSpan(axis);
- max = child.getMaximumSpan(axis);
+ pref += child.getPreferredSpan(axis);
+ max += child.getMaximumSpan(axis);
}
res.minimum = (int) min;
@@ -509,7 +564,7 @@ public class BoxView
res.minimum = 0;
res.preferred = 0;
- res.maximum = 0;
+ res.maximum = Integer.MAX_VALUE;
res.alignment = 0.5F;
int n = getViewCount();
for (int i = 0; i < n; i++)
@@ -568,9 +623,9 @@ public class BoxView
boolean result = false;
if (myAxis == X_AXIS)
- result = x > r.x;
+ result = x > r.x + r.width;
else
- result = y > r.y;
+ result = y > r.y + r.height;
return result;
}
@@ -589,24 +644,54 @@ public class BoxView
{
View result = null;
int count = getViewCount();
- Rectangle copy = new Rectangle(r);
-
- for (int i = 0; i < count; ++i)
+ if (myAxis == X_AXIS)
{
- copy.setBounds(r);
- // The next call modifies copy.
- childAllocation(i, copy);
- if (copy.contains(x, y))
+ // Border case. Requested point is left from the box.
+ if (x < r.x + offsets[X_AXIS][0])
{
- // Modify r on success.
- r.setBounds(copy);
- result = getView(i);
- break;
+ childAllocation(0, r);
+ result = getView(0);
+ }
+ else
+ {
+ // Search views inside box.
+ for (int i = 0; i < count && result == null; i++)
+ {
+ if (x < r.x + offsets[X_AXIS][i])
+ {
+ childAllocation(i - 1, r);
+ result = getView(i - 1);
+ }
+ }
}
}
-
- if (result == null && count > 0)
- return getView(count - 1);
+ else // Same algorithm for Y_AXIS.
+ {
+ // Border case. Requested point is above the box.
+ if (y < r.y + offsets[Y_AXIS][0])
+ {
+ childAllocation(0, r);
+ result = getView(0);
+ }
+ else
+ {
+ // Search views inside box.
+ for (int i = 0; i < count && result == null; i++)
+ {
+ if (y < r.y + offsets[Y_AXIS][i])
+ {
+ childAllocation(i - 1, r);
+ result = getView(i - 1);
+ }
+ }
+ }
+ }
+ // Not found, other border case: point is right from or below the box.
+ if (result == null)
+ {
+ childAllocation(count - 1, r);
+ result = getView(count - 1);
+ }
return result;
}
@@ -623,9 +708,6 @@ public class BoxView
*/
protected void childAllocation(int index, Rectangle a)
{
- if (! isAllocationValid())
- layout(a.width, a.height);
-
a.x += offsets[X_AXIS][index];
a.y += offsets[Y_AXIS][index];
a.width = spans[X_AXIS][index];
@@ -643,49 +725,32 @@ public class BoxView
*/
protected void layout(int width, int height)
{
- int[] newSpan = new int[]{ width, height };
- int count = getViewCount();
-
- // Update minor axis as appropriate. We need to first update the minor
- // axis layout because that might affect the children's preferences along
- // the major axis.
- int minorAxis = myAxis == X_AXIS ? Y_AXIS : X_AXIS;
- if ((! isLayoutValid(minorAxis)) || newSpan[minorAxis] != span[minorAxis])
- {
- layoutValid[minorAxis] = false;
- span[minorAxis] = newSpan[minorAxis];
- layoutMinorAxis(span[minorAxis], minorAxis, offsets[minorAxis],
- spans[minorAxis]);
-
- // Update the child view's sizes.
- for (int i = 0; i < count; ++i)
- {
- getView(i).setSize(spans[X_AXIS][i], spans[Y_AXIS][i]);
- }
- layoutValid[minorAxis] = true;
- }
-
+ layoutAxis(X_AXIS, width);
+ layoutAxis(Y_AXIS, height);
+ }
- // Update major axis as appropriate.
- if ((! isLayoutValid(myAxis)) || newSpan[myAxis] != span[myAxis])
+ private void layoutAxis(int axis, int s)
+ {
+ if (span[axis] != s)
+ layoutValid[axis] = false;
+ if (! layoutValid[axis])
{
- layoutValid[myAxis] = false;
- span[myAxis] = newSpan[myAxis];
- layoutMajorAxis(span[myAxis], myAxis, offsets[myAxis],
- spans[myAxis]);
+ span[axis] = s;
+ updateRequirements(axis);
+ if (axis == myAxis)
+ layoutMajorAxis(span[axis], axis, offsets[axis], spans[axis]);
+ else
+ layoutMinorAxis(span[axis], axis, offsets[axis], spans[axis]);
+ layoutValid[axis] = true;
- // Update the child view's sizes.
- for (int i = 0; i < count; ++i)
+ // Push out child layout.
+ int viewCount = getViewCount();
+ for (int i = 0; i < viewCount; i++)
{
- getView(i).setSize(spans[X_AXIS][i], spans[Y_AXIS][i]);
+ View v = getView(i);
+ v.setSize(spans[X_AXIS][i], spans[Y_AXIS][i]);
}
- layoutValid[myAxis] = true;
}
-
- if (layoutValid[myAxis] == false)
- System.err.println("WARNING: Major axis layout must be valid after layout");
- if (layoutValid[minorAxis] == false)
- System.err.println("Minor axis layout must be valid after layout");
}
/**
@@ -708,7 +773,7 @@ public class BoxView
{
View child = getView(i);
spans[i] = (int) child.getPreferredSpan(axis);
- sumPref = spans[i];
+ sumPref += spans[i];
}
// Try to adjust the spans so that we fill the targetSpan.
@@ -776,7 +841,7 @@ public class BoxView
View child = getView(i);
int max = (int) child.getMaximumSpan(axis);
if (max < targetSpan)
- {System.err.println("align: " + child);
+ {
// Align child when it can't be made as wide as the target span.
float align = child.getAlignment(axis);
offsets[i] = (int) ((targetSpan - max) * align);
@@ -811,7 +876,9 @@ public class BoxView
*/
public int getWidth()
{
- return span[X_AXIS];
+ // The RI returns the following here, however, I'd think that is a bug.
+ // return span[X_AXIS] + getLeftInset() - getRightInset();
+ return span[X_AXIS] + getLeftInset() + getRightInset();
}
/**
@@ -821,7 +888,9 @@ public class BoxView
*/
public int getHeight()
{
- return span[Y_AXIS];
+ // The RI returns the following here, however, I'd think that is a bug.
+ // return span[Y_AXIS] + getTopInset() - getBottomInset();
+ return span[Y_AXIS] + getTopInset() + getBottomInset();
}
/**
@@ -833,7 +902,8 @@ public class BoxView
*/
public void setSize(float width, float height)
{
- layout((int) width, (int) height);
+ layout((int) (width - getLeftInset() - getRightInset()),
+ (int) (height - getTopInset() - getBottomInset()));
}
/**
@@ -944,9 +1014,11 @@ public class BoxView
{
if (axis != X_AXIS && axis != Y_AXIS)
throw new IllegalArgumentException("Illegal axis argument");
- int weight = 1;
- if (axis == myAxis)
- weight = 0;
+ updateRequirements(axis);
+ int weight = 0;
+ if ((requirements[axis].preferred != requirements[axis].minimum)
+ || (requirements[axis].preferred != requirements[axis].maximum))
+ weight = 1;
return weight;
}
@@ -973,13 +1045,39 @@ public class BoxView
protected void forwardUpdate(DocumentEvent.ElementChange ec, DocumentEvent e,
Shape a, ViewFactory vf)
{
- // FIXME: What to do here?
+ boolean wasValid = isLayoutValid(myAxis);
super.forwardUpdate(ec, e, a, vf);
+ // Trigger repaint when one of the children changed the major axis.
+ if (wasValid && ! isLayoutValid(myAxis))
+ {
+ Container c = getContainer();
+ if (a != null && c != null)
+ {
+ int pos = e.getOffset();
+ int index = getViewIndexAtPosition(pos);
+ Rectangle r = getInsideAllocation(a);
+ if (myAxis == X_AXIS)
+ {
+ r.x += offsets[myAxis][index];
+ r.width -= offsets[myAxis][index];
+ }
+ else
+ {
+ r.y += offsets[myAxis][index];
+ r.height -= offsets[myAxis][index];
+ }
+ c.repaint(r.x, r.y, r.width, r.height);
+ }
+ }
}
public int viewToModel(float x, float y, Shape a, Position.Bias[] bias)
{
- // FIXME: What to do here?
+ if (! isAllocationValid())
+ {
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ setSize(r.width, r.height);
+ }
return super.viewToModel(x, y, a, bias);
}
@@ -990,32 +1088,6 @@ public class BoxView
}
/**
- * Updates the child requirements along the specified axis. The requirements
- * are only updated if the layout for the specified axis is marked as
- * invalid.
- *
- * @param axis the axis to be updated
- */
- private void updateChildRequirements(int axis)
- {
- if (! isLayoutValid(axis))
- {
- int numChildren = getViewCount();
- if (childReqs[axis] == null || childReqs[axis].length != numChildren)
- childReqs[axis] = new SizeRequirements[numChildren];
- for (int i = 0; i < numChildren; ++i)
- {
- View child = getView(i);
- childReqs[axis][i] =
- new SizeRequirements((int) child.getMinimumSpan(axis),
- (int) child.getPreferredSpan(axis),
- (int) child.getMaximumSpan(axis),
- child.getAlignment(axis));
- }
- }
- }
-
- /**
* Updates the view's cached requirements along the specified axis if
* necessary. The requirements are only updated if the layout for the
* specified axis is marked as invalid.
@@ -1024,6 +1096,8 @@ public class BoxView
*/
private void updateRequirements(int axis)
{
+ if (axis != Y_AXIS && axis != X_AXIS)
+ throw new IllegalArgumentException("Illegal axis: " + axis);
if (! requirementsValid[axis])
{
if (axis == myAxis)
diff --git a/libjava/classpath/javax/swing/text/ComponentView.java b/libjava/classpath/javax/swing/text/ComponentView.java
index a7d237ab73a..8de4de60fa3 100644
--- a/libjava/classpath/javax/swing/text/ComponentView.java
+++ b/libjava/classpath/javax/swing/text/ComponentView.java
@@ -39,11 +39,11 @@ package javax.swing.text;
import java.awt.Component;
import java.awt.Container;
+import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Shape;
-import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
/**
@@ -62,11 +62,161 @@ public class ComponentView extends View
{
/**
+ * A special container that sits between the component and the hosting
+ * container. This is used to propagate invalidate requests and cache
+ * the component's layout sizes.
+ */
+ private class Interceptor
+ extends Container
+ {
+ Dimension min;
+ Dimension pref;
+ Dimension max;
+ float alignX;
+ float alignY;
+
+ /**
+ * Creates a new instance that hosts the specified component.
+ */
+ Interceptor(Component c)
+ {
+ setLayout(null);
+ add(c);
+ cacheComponentSizes();
+ }
+
+ /**
+ * Intercepts the normal invalidate call and propagates the invalidate
+ * request up using the View's preferenceChanged().
+ */
+ public void invalidate()
+ {
+ super.invalidate();
+ if (getParent() != null)
+ preferenceChanged(null, true, true);
+ }
+
+ /**
+ * This is overridden to simply cache the layout sizes.
+ */
+ public void doLayout()
+ {
+ cacheComponentSizes();
+ }
+
+ /**
+ * Overridden to also reshape the component itself.
+ */
+ public void reshape(int x, int y, int w, int h)
+ {
+ super.reshape(x, y, w, h);
+ if (getComponentCount() > 0)
+ getComponent(0).setSize(w, h);
+ cacheComponentSizes();
+ }
+
+ /**
+ * Overridden to also show the component.
+ */
+ public void show()
+ {
+ super.show();
+ if (getComponentCount() > 0)
+ getComponent(0).setVisible(true);
+ }
+
+ /**
+ * Overridden to also hide the component.
+ */
+ public void hide()
+ {
+ super.hide();
+ if (getComponentCount() > 0)
+ getComponent(0).setVisible(false);
+ }
+
+ /**
+ * Overridden to return the cached value.
+ */
+ public Dimension getMinimumSize()
+ {
+ maybeValidate();
+ return min;
+ }
+
+ /**
+ * Overridden to return the cached value.
+ */
+ public Dimension getPreferredSize()
+ {
+ maybeValidate();
+ return pref;
+ }
+
+ /**
+ * Overridden to return the cached value.
+ */
+ public Dimension getMaximumSize()
+ {
+ maybeValidate();
+ return max;
+ }
+
+ /**
+ * Overridden to return the cached value.
+ */
+ public float getAlignmentX()
+ {
+ maybeValidate();
+ return alignX;
+ }
+
+ /**
+ * Overridden to return the cached value.
+ */
+ public float getAlignmentY()
+ {
+ maybeValidate();
+ return alignY;
+ }
+
+ /**
+ * Validates the container only when necessary.
+ */
+ private void maybeValidate()
+ {
+ if (! isValid())
+ validate();
+ }
+
+ /**
+ * Fetches the component layout sizes into the cache.
+ */
+ private void cacheComponentSizes()
+ {
+ if (getComponentCount() > 0)
+ {
+ Component c = getComponent(0);
+ min = c.getMinimumSize();
+ pref = c.getPreferredSize();
+ max = c.getMaximumSize();
+ alignX = c.getAlignmentX();
+ alignY = c.getAlignmentY();
+ }
+ }
+ }
+
+ /**
* The component that is displayed by this view.
*/
private Component comp;
/**
+ * The intercepting container.
+ */
+ private Interceptor interceptor;
+
+ /**
* Creates a new instance of <code>ComponentView</code> for the specified
* <code>Element</code>.
*
@@ -99,13 +249,20 @@ public class ComponentView extends View
*/
public float getAlignment(int axis)
{
- float align;
- if (axis == X_AXIS)
- align = getComponent().getAlignmentX();
- else if (axis == Y_AXIS)
- align = getComponent().getAlignmentY();
+ float align = 0.0F;
+ // I'd rather throw an IllegalArgumentException for illegal axis,
+ // but the Harmony testsuite indicates fallback to super behaviour.
+ if (interceptor != null && (axis == X_AXIS || axis == Y_AXIS))
+ {
+ if (axis == X_AXIS)
+ align = interceptor.getAlignmentX();
+ else if (axis == Y_AXIS)
+ align = interceptor.getAlignmentY();
+ else
+ assert false : "Must not reach here";
+ }
else
- throw new IllegalArgumentException();
+ align = super.getAlignment(axis);
return align;
}
@@ -118,8 +275,6 @@ public class ComponentView extends View
*/
public final Component getComponent()
{
- if (comp == null)
- comp = createComponent();
return comp;
}
@@ -135,49 +290,70 @@ public class ComponentView extends View
*/
public float getMaximumSpan(int axis)
{
- float span;
- if (axis == X_AXIS)
- span = getComponent().getMaximumSize().width;
- else if (axis == Y_AXIS)
- span = getComponent().getMaximumSize().height;
- else
- throw new IllegalArgumentException();
+ if (axis != X_AXIS && axis != Y_AXIS)
+ throw new IllegalArgumentException("Illegal axis");
+ float span = 0;
+ if (interceptor != null)
+ {
+ if (axis == X_AXIS)
+ span = interceptor.getMaximumSize().width;
+ else if (axis == Y_AXIS)
+ span = interceptor.getMaximumSize().height;
+ else
+ assert false : "Must not reach here";
+ }
return span;
}
public float getMinimumSpan(int axis)
{
- float span;
- if (axis == X_AXIS)
- span = getComponent().getMinimumSize().width;
- else if (axis == Y_AXIS)
- span = getComponent().getMinimumSize().height;
- else
- throw new IllegalArgumentException();
+ if (axis != X_AXIS && axis != Y_AXIS)
+ throw new IllegalArgumentException("Illegal axis");
+ float span = 0;
+ if (interceptor != null)
+ {
+ if (axis == X_AXIS)
+ span = interceptor.getMinimumSize().width;
+ else if (axis == Y_AXIS)
+ span = interceptor.getMinimumSize().height;
+ else
+ assert false : "Must not reach here";
+ }
return span;
}
public float getPreferredSpan(int axis)
{
- float span;
- if (axis == X_AXIS)
- span = getComponent().getPreferredSize().width;
- else if (axis == Y_AXIS)
- span = getComponent().getPreferredSize().height;
- else
- throw new IllegalArgumentException();
+ if (axis != X_AXIS && axis != Y_AXIS)
+ throw new IllegalArgumentException("Illegal axis");
+ float span = 0;
+ if (interceptor != null)
+ {
+ if (axis == X_AXIS)
+ span = interceptor.getPreferredSize().width;
+ else if (axis == Y_AXIS)
+ span = interceptor.getPreferredSize().height;
+ else
+ assert false : "Must not reach here";
+ }
return span;
}
public Shape modelToView(int pos, Shape a, Position.Bias b)
throws BadLocationException
{
- Element el = getElement();
- if (pos < el.getStartOffset() || pos >= el.getEndOffset())
- throw new BadLocationException("Illegal offset for this view", pos);
- Rectangle r = a.getBounds();
- Component c = getComponent();
- return new Rectangle(r.x, r.y, c.getWidth(), c.getHeight());
+ int p0 = getStartOffset();
+ int p1 = getEndOffset();
+ if (pos >= p0 && pos <= p1)
+ {
+ Rectangle viewRect = a.getBounds();
+ if (pos == p1)
+ viewRect.x += viewRect.width;
+ viewRect.width = 0;
+ return viewRect;
+ }
+ else
+ throw new BadLocationException("Illegal position", pos);
}
/**
@@ -191,8 +367,11 @@ public class ComponentView extends View
*/
public void paint(Graphics g, Shape a)
{
- Rectangle r = a.getBounds();
- getComponent().setBounds(r.x, r.y, r.width, r.height);
+ if (interceptor != null)
+ {
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ interceptor.setBounds(r.x, r.y, r.width, r.height);
+ }
}
/**
@@ -209,15 +388,33 @@ public class ComponentView extends View
*/
public void setParent(final View p)
{
+ super.setParent(p);
if (SwingUtilities.isEventDispatchThread())
- setParentImpl(p);
+ setParentImpl();
else
SwingUtilities.invokeLater
(new Runnable()
{
public void run()
{
- setParentImpl(p);
+ Document doc = getDocument();
+ try
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ setParentImpl();
+ Container host = getContainer();
+ if (host != null)
+ {
+ preferenceChanged(null, true, true);
+ host.repaint();
+ }
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
}
});
}
@@ -225,23 +422,41 @@ public class ComponentView extends View
/**
* The implementation of {@link #setParent}. This is package private to
* avoid a synthetic accessor method.
- *
- * @param p the parent view to set
*/
- private void setParentImpl(View p)
+ void setParentImpl()
{
- super.setParent(p);
+ View p = getParent();
if (p != null)
{
- Component c = getComponent();
- p.getContainer().add(c);
+ Container c = getContainer();
+ if (c != null)
+ {
+ if (interceptor == null)
+ {
+ // Create component and put it inside the interceptor.
+ Component created = createComponent();
+ if (created != null)
+ {
+ comp = created;
+ interceptor = new Interceptor(comp);
+ }
+ }
+ if (interceptor != null)
+ {
+ // Add the interceptor to the hosting container.
+ if (interceptor.getParent() == null)
+ c.add(interceptor, this);
+ }
+ }
}
else
{
- Component c = getComponent();
- Container parent = c.getParent();
- parent.remove(c);
- comp = null;
+ if (interceptor != null)
+ {
+ Container parent = interceptor.getParent();
+ if (parent != null)
+ parent.remove(interceptor);
+ }
}
}
@@ -259,10 +474,21 @@ public class ComponentView extends View
*/
public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
{
- // The element should only have one character position and it is clear
- // that this position is the position that best matches the given screen
- // coordinates, simply because this view has only this one position.
- Element el = getElement();
- return el.getStartOffset();
+ int pos;
+ // I'd rather do the following. The harmony testsuite indicates
+ // that a simple cast is performed.
+ //Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ Rectangle r = (Rectangle) a;
+ if (x < r.x + r.width / 2)
+ {
+ b[0] = Position.Bias.Forward;
+ pos = getStartOffset();
+ }
+ else
+ {
+ b[0] = Position.Bias.Backward;
+ pos = getEndOffset();
+ }
+ return pos;
}
}
diff --git a/libjava/classpath/javax/swing/text/CompositeView.java b/libjava/classpath/javax/swing/text/CompositeView.java
index 6f487b8981e..570fc955c88 100644
--- a/libjava/classpath/javax/swing/text/CompositeView.java
+++ b/libjava/classpath/javax/swing/text/CompositeView.java
@@ -38,7 +38,6 @@ exception statement from your version. */
package javax.swing.text;
-import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Shape;
@@ -57,20 +56,28 @@ public abstract class CompositeView
/**
* The child views of this <code>CompositeView</code>.
*/
- View[] children;
+ private View[] children;
+
+ /**
+ * The number of child views.
+ */
+ private int numChildren;
/**
* The allocation of this <code>View</code> minus its insets. This is
* initialized in {@link #getInsideAllocation} and reused and modified in
* {@link #childAllocation(int, Rectangle)}.
*/
- Rectangle insideAllocation;
+ private final Rectangle insideAllocation = new Rectangle();
/**
* The insets of this <code>CompositeView</code>. This is initialized
* in {@link #setInsets}.
*/
- Insets insets;
+ private short top;
+ private short bottom;
+ private short left;
+ private short right;
/**
* Creates a new <code>CompositeView</code> for the given
@@ -82,7 +89,10 @@ public abstract class CompositeView
{
super(element);
children = new View[0];
- insets = new Insets(0, 0, 0, 0);
+ top = 0;
+ bottom = 0;
+ left = 0;
+ right = 0;
}
/**
@@ -96,16 +106,22 @@ public abstract class CompositeView
*/
protected void loadChildren(ViewFactory f)
{
- Element el = getElement();
- int count = el.getElementCount();
- View[] newChildren = new View[count];
- for (int i = 0; i < count; ++i)
+ if (f != null)
{
- Element child = el.getElement(i);
- View view = f.create(child);
- newChildren[i] = view;
+ Element el = getElement();
+ int count = el.getElementCount();
+ View[] newChildren = new View[count];
+ for (int i = 0; i < count; ++i)
+ {
+ Element child = el.getElement(i);
+ View view = f.create(child);
+ newChildren[i] = view;
+ }
+ // I'd have called replace(0, getViewCount(), newChildren) here
+ // in order to replace all existing views. However according to
+ // Harmony's tests this is not what the RI does.
+ replace(0, 0, newChildren);
}
- replace(0, getViewCount(), newChildren);
}
/**
@@ -118,7 +134,7 @@ public abstract class CompositeView
public void setParent(View parent)
{
super.setParent(parent);
- if (parent != null && ((children == null) || children.length == 0))
+ if (parent != null && numChildren == 0)
loadChildren(getViewFactory());
}
@@ -129,7 +145,7 @@ public abstract class CompositeView
*/
public int getViewCount()
{
- return children.length;
+ return numChildren;
}
/**
@@ -156,24 +172,42 @@ public abstract class CompositeView
*/
public void replace(int offset, int length, View[] views)
{
- // Check for null views to add.
- for (int i = 0; i < views.length; ++i)
- if (views[i] == null)
- throw new NullPointerException("Added views must not be null");
-
- int endOffset = offset + length;
+ // Make sure we have an array. The Harmony testsuite indicates that we
+ // have to do something like this.
+ if (views == null)
+ views = new View[0];
// First we set the parent of the removed children to null.
+ int endOffset = offset + length;
for (int i = offset; i < endOffset; ++i)
- children[i].setParent(null);
+ {
+ if (children[i].getParent() == this)
+ children[i].setParent(null);
+ children[i] = null;
+ }
- View[] newChildren = new View[children.length - length + views.length];
- System.arraycopy(children, 0, newChildren, 0, offset);
- System.arraycopy(views, 0, newChildren, offset, views.length);
- System.arraycopy(children, offset + length, newChildren,
- offset + views.length,
- children.length - (offset + length));
- children = newChildren;
+ // Update the children array.
+ int delta = views.length - length;
+ int src = offset + length;
+ int numMove = numChildren - src;
+ int dst = src + delta;
+ if (numChildren + delta > children.length)
+ {
+ // Grow array.
+ int newLength = Math.max(2 * children.length, numChildren + delta);
+ View[] newChildren = new View[newLength];
+ System.arraycopy(children, 0, newChildren, 0, offset);
+ System.arraycopy(views, 0, newChildren, offset, views.length);
+ System.arraycopy(children, src, newChildren, dst, numMove);
+ children = newChildren;
+ }
+ else
+ {
+ // Patch existing array.
+ System.arraycopy(children, src, children, dst, numMove);
+ System.arraycopy(views, 0, children, offset, views.length);
+ }
+ numChildren += delta;
// Finally we set the parent of the added children to this.
for (int i = 0; i < views.length; ++i)
@@ -248,34 +282,13 @@ public abstract class CompositeView
}
}
}
- else
- {
- throw new BadLocationException("Position " + pos
- + " is not represented by view.", pos);
- }
}
- return ret;
- }
- /**
- * A helper method for {@link #modelToView(int, Position.Bias, int,
- * Position.Bias, Shape)}. This creates a default location when there is
- * no child view that can take responsibility for mapping the position to
- * view coordinates. Depending on the specified bias this will be the
- * left or right edge of this view's allocation.
- *
- * @param a the allocation for this view
- * @param bias the bias
- *
- * @return a default location
- */
- private Shape createDefaultLocation(Shape a, Position.Bias bias)
- {
- Rectangle alloc = a.getBounds();
- Rectangle location = new Rectangle(alloc.x, alloc.y, 1, alloc.height);
- if (bias == Position.Bias.Forward)
- location.x = alloc.x + alloc.width;
- return location;
+ if (ret == null)
+ throw new BadLocationException("Position " + pos
+ + " is not represented by view.", pos);
+
+ return ret;
}
/**
@@ -394,7 +407,7 @@ public abstract class CompositeView
*/
public int getViewIndex(int pos, Position.Bias b)
{
- if (b == Position.Bias.Backward && pos != 0)
+ if (b == Position.Bias.Backward)
pos -= 1;
int i = -1;
if (pos >= getStartOffset() && pos < getEndOffset())
@@ -514,24 +527,17 @@ public abstract class CompositeView
if (a == null)
return null;
- Rectangle alloc = a.getBounds();
+ // Try to avoid allocation of Rectangle here.
+ Rectangle alloc = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+
// Initialize the inside allocation rectangle. This is done inside
// a synchronized block in order to avoid multiple threads creating
// this instance simultanously.
- Rectangle inside;
- synchronized(this)
- {
- inside = insideAllocation;
- if (inside == null)
- {
- inside = new Rectangle();
- insideAllocation = inside;
- }
- }
- inside.x = alloc.x + insets.left;
- inside.y = alloc.y + insets.top;
- inside.width = alloc.width - insets.left - insets.right;
- inside.height = alloc.height - insets.top - insets.bottom;
+ Rectangle inside = insideAllocation;
+ inside.x = alloc.x + getLeftInset();
+ inside.y = alloc.y + getTopInset();
+ inside.width = alloc.width - getLeftInset() - getRightInset();
+ inside.height = alloc.height - getTopInset() - getBottomInset();
return inside;
}
@@ -546,39 +552,26 @@ public abstract class CompositeView
*/
protected void setParagraphInsets(AttributeSet attributes)
{
- Float l = (Float) attributes.getAttribute(StyleConstants.LeftIndent);
- short left = 0;
- if (l != null)
- left = l.shortValue();
- Float r = (Float) attributes.getAttribute(StyleConstants.RightIndent);
- short right = 0;
- if (r != null)
- right = r.shortValue();
- Float t = (Float) attributes.getAttribute(StyleConstants.SpaceAbove);
- short top = 0;
- if (t != null)
- top = t.shortValue();
- Float b = (Float) attributes.getAttribute(StyleConstants.SpaceBelow);
- short bottom = 0;
- if (b != null)
- bottom = b.shortValue();
- setInsets(top, left, bottom, right);
+ top = (short) StyleConstants.getSpaceAbove(attributes);
+ bottom = (short) StyleConstants.getSpaceBelow(attributes);
+ left = (short) StyleConstants.getLeftIndent(attributes);
+ right = (short) StyleConstants.getRightIndent(attributes);
}
/**
* Sets the insets of this <code>CompositeView</code>.
*
- * @param top the top inset
- * @param left the left inset
- * @param bottom the bottom inset
- * @param right the right inset
+ * @param t the top inset
+ * @param l the left inset
+ * @param b the bottom inset
+ * @param r the right inset
*/
- protected void setInsets(short top, short left, short bottom, short right)
+ protected void setInsets(short t, short l, short b, short r)
{
- insets.top = top;
- insets.left = left;
- insets.bottom = bottom;
- insets.right = right;
+ top = t;
+ left = l;
+ bottom = b;
+ right = r;
}
/**
@@ -588,7 +581,7 @@ public abstract class CompositeView
*/
protected short getLeftInset()
{
- return (short) insets.left;
+ return left;
}
/**
@@ -598,7 +591,7 @@ public abstract class CompositeView
*/
protected short getRightInset()
{
- return (short) insets.right;
+ return right;
}
/**
@@ -608,7 +601,7 @@ public abstract class CompositeView
*/
protected short getTopInset()
{
- return (short) insets.top;
+ return top;
}
/**
@@ -618,7 +611,7 @@ public abstract class CompositeView
*/
protected short getBottomInset()
{
- return (short) insets.bottom;
+ return bottom;
}
/**
diff --git a/libjava/classpath/javax/swing/text/DefaultCaret.java b/libjava/classpath/javax/swing/text/DefaultCaret.java
index 84f47f120de..c4c2580c398 100644
--- a/libjava/classpath/javax/swing/text/DefaultCaret.java
+++ b/libjava/classpath/javax/swing/text/DefaultCaret.java
@@ -804,7 +804,7 @@ public class DefaultCaret extends Rectangle
}
}
}
-
+
private void handleHighlight()
{
Highlighter highlighter = textComponent.getHighlighter();
@@ -946,7 +946,7 @@ public class DefaultCaret extends Rectangle
*
* @return all registered event listeners of the specified type
*/
- public EventListener[] getListeners(Class listenerType)
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
{
return listenerList.getListeners(listenerType);
}
@@ -1075,8 +1075,6 @@ public class DefaultCaret extends Rectangle
handleHighlight();
appear();
-
- adjustVisibility(this);
}
}
@@ -1114,8 +1112,6 @@ public class DefaultCaret extends Rectangle
clearHighlight();
appear();
-
- adjustVisibility(this);
}
}
@@ -1154,7 +1150,12 @@ public class DefaultCaret extends Rectangle
// e.printStackTrace();
}
if (area != null)
- damage(area);
+ {
+ adjustVisibility(area);
+ if (getMagicCaretPosition() == null)
+ setMagicCaretPosition(new Point(area.x, area.y));
+ damage(area);
+ }
}
repaint();
}
diff --git a/libjava/classpath/javax/swing/text/DefaultEditorKit.java b/libjava/classpath/javax/swing/text/DefaultEditorKit.java
index 8602e69f8e7..aa69deca545 100644
--- a/libjava/classpath/javax/swing/text/DefaultEditorKit.java
+++ b/libjava/classpath/javax/swing/text/DefaultEditorKit.java
@@ -38,7 +38,6 @@ exception statement from your version. */
package javax.swing.text;
-import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
@@ -312,19 +311,21 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- int offs = t.getDocument().getLength();
- Caret c = t.getCaret();
- c.setDot(0);
- c.moveDot(offs);
-
- try
- {
- c.setMagicCaretPosition(t.modelToView(offs).getLocation());
- }
- catch(BadLocationException ble)
- {
- // Can't happen.
- }
+ if (t != null)
+ {
+ int offs = t.getDocument().getLength();
+ Caret c = t.getCaret();
+ c.setDot(0);
+ c.moveDot(offs);
+ try
+ {
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
+ }
}
}
@@ -339,15 +340,18 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- Caret c = t.getCaret();
- c.moveDot(0);
- try
- {
- c.setMagicCaretPosition(t.modelToView(0).getLocation());
- }
- catch(BadLocationException ble)
+ if (t != null)
{
- // Can't happen.
+ Caret c = t.getCaret();
+ c.moveDot(0);
+ try
+ {
+ c.setMagicCaretPosition(t.modelToView(0).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
}
}
}
@@ -363,16 +367,19 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- int offs = t.getDocument().getLength();
- Caret c = t.getCaret();
- c.moveDot(offs);
- try
- {
- c.setMagicCaretPosition(t.modelToView(offs).getLocation());
- }
- catch(BadLocationException ble)
+ if (t != null)
{
- // Can't happen.
+ int offs = t.getDocument().getLength();
+ Caret c = t.getCaret();
+ c.moveDot(offs);
+ try
+ {
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
}
}
}
@@ -389,17 +396,19 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- Caret c = t.getCaret();
- try
+ if (t != null)
{
- int offs = Utilities.getRowStart(t, c.getDot());
- c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ Caret c = t.getCaret();
+ try
+ {
+ int offs = Utilities.getRowStart(t, c.getDot());
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
}
- catch(BadLocationException ble)
- {
- // Can't happen.
- }
-
}
}
@@ -414,17 +423,19 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- Caret c = t.getCaret();
- try
- {
- int offs = Utilities.getRowEnd(t, c.getDot());
- c.setMagicCaretPosition(t.modelToView(offs).getLocation());
- }
- catch(BadLocationException ble)
+ if (t != null)
{
- // Can't happen.
+ Caret c = t.getCaret();
+ try
+ {
+ int offs = Utilities.getRowEnd(t, c.getDot());
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
}
-
}
}
@@ -438,20 +449,21 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- Caret c = t.getCaret();
- try
- {
- int offs1 = Utilities.getRowStart(t, c.getDot());
- int offs2 = Utilities.getRowEnd(t, c.getDot());
-
- c.setDot(offs2);
- c.moveDot(offs1);
-
- c.setMagicCaretPosition(t.modelToView(offs2).getLocation());
- }
- catch(BadLocationException ble)
+ if (t != null)
{
- // Can't happen.
+ Caret c = t.getCaret();
+ try
+ {
+ int offs1 = Utilities.getRowStart(t, c.getDot());
+ int offs2 = Utilities.getRowEnd(t, c.getDot());
+ c.setDot(offs2);
+ c.moveDot(offs1);
+ c.setMagicCaretPosition(t.modelToView(offs2).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
}
}
}
@@ -466,51 +478,52 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- Caret c = t.getCaret();
- int dot = c.getDot();
-
- try
+ if (t != null)
{
- int wordStart = Utilities.getWordStart(t, dot);
-
- if (dot == wordStart)
- {
- // Current cursor position is on the first character in a word.
- c.setDot(wordStart);
- c.moveDot(Utilities.getWordEnd(t, wordStart));
- }
- else
+ Caret c = t.getCaret();
+ int dot = c.getDot();
+ try
{
- // Current cursor position is not on the first character
- // in a word.
- int nextWord = Utilities.getNextWord(t, dot);
- int previousWord = Utilities.getPreviousWord(t, dot);
- int previousWordEnd = Utilities.getWordEnd(t, previousWord);
-
- // Cursor position is in the space between two words. In such a
- // situation just select the space.
- if (dot >= previousWordEnd && dot <= nextWord)
+ int wordStart = Utilities.getWordStart(t, dot);
+
+ if (dot == wordStart)
{
- c.setDot(previousWordEnd);
- c.moveDot(nextWord);
+ // Current cursor position is on the first character in a word.
+ c.setDot(wordStart);
+ c.moveDot(Utilities.getWordEnd(t, wordStart));
}
else
{
- // Cursor position is inside a word. Just select it then.
- c.setDot(previousWord);
- c.moveDot(previousWordEnd);
+ // Current cursor position is not on the first character
+ // in a word.
+ int nextWord = Utilities.getNextWord(t, dot);
+ int previousWord = Utilities.getPreviousWord(t, dot);
+ int previousWordEnd = Utilities.getWordEnd(t, previousWord);
+
+ // Cursor position is in the space between two words. In such a
+ // situation just select the space.
+ if (dot >= previousWordEnd && dot <= nextWord)
+ {
+ c.setDot(previousWordEnd);
+ c.moveDot(nextWord);
+ }
+ else
+ {
+ // Cursor position is inside a word. Just select it then.
+ c.setDot(previousWord);
+ c.moveDot(previousWordEnd);
+ }
}
- }
- // If the position was updated change the magic caret position
- // as well.
- if (c.getDot() != dot)
- c.setMagicCaretPosition(t.modelToView(c.getDot()).getLocation());
-
- }
- catch(BadLocationException ble)
- {
- // Can't happen.
+ // If the position was updated change the magic caret position
+ // as well.
+ if (c.getDot() != dot)
+ c.setMagicCaretPosition(t.modelToView(c.getDot()).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
}
}
}
@@ -715,21 +728,23 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- try
- {
- int offs = Utilities.getRowEnd(t, t.getCaretPosition());
-
- if (offs > -1)
- {
- Caret c = t.getCaret();
- c.setDot(offs);
- c.setMagicCaretPosition(t.modelToView(offs).getLocation());
- }
- }
- catch (BadLocationException ble)
- {
- // Nothing to do here
- }
+ if (t != null)
+ {
+ try
+ {
+ int offs = Utilities.getRowEnd(t, t.getCaretPosition());
+ if (offs > -1)
+ {
+ Caret c = t.getCaret();
+ c.setDot(offs);
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ }
+ catch (BadLocationException ble)
+ {
+ // Nothing to do here
+ }
+ }
}
}
@@ -744,21 +759,23 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- try
- {
- int offs = Utilities.getRowStart(t, t.getCaretPosition());
-
- if (offs > -1)
- {
- Caret c = t.getCaret();
- c.setDot(offs);
- c.setMagicCaretPosition(t.modelToView(offs).getLocation());
- }
- }
- catch (BadLocationException ble)
- {
- // Do nothing here.
- }
+ if (t != null)
+ {
+ try
+ {
+ int offs = Utilities.getRowStart(t, t.getCaretPosition());
+ if (offs > -1)
+ {
+ Caret c = t.getCaret();
+ c.setDot(offs);
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ }
+ catch (BadLocationException ble)
+ {
+ // Do nothing here.
+ }
+ }
}
}
@@ -773,16 +790,19 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- Caret c = t.getCaret();
- c.setDot(0);
- try
- {
- c.setMagicCaretPosition(t.modelToView(0).getLocation());
- }
- catch(BadLocationException ble)
- {
- // Can't happen.
- }
+ if (t != null)
+ {
+ Caret c = t.getCaret();
+ c.setDot(0);
+ try
+ {
+ c.setMagicCaretPosition(t.modelToView(0).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
+ }
}
}
@@ -797,16 +817,19 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- int offs = t.getDocument().getLength();
- Caret c = t.getCaret();
- c.setDot(offs);
- try
- {
- c.setMagicCaretPosition(t.modelToView(offs).getLocation());
- }
- catch(BadLocationException ble)
+ if (t != null)
{
- // Can't happen.
+ int offs = t.getDocument().getLength();
+ Caret c = t.getCaret();
+ c.setDot(offs);
+ try
+ {
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
}
}
}
@@ -862,7 +885,9 @@ public class DefaultEditorKit extends EditorKit
*/
public void actionPerformed(ActionEvent event)
{
- getTextComponent(event).copy();
+ JTextComponent target = getTextComponent(event);
+ if (target != null)
+ target.copy();
}
}
@@ -893,7 +918,9 @@ public class DefaultEditorKit extends EditorKit
*/
public void actionPerformed(ActionEvent event)
{
- getTextComponent(event).cut();
+ JTextComponent target = getTextComponent(event);
+ if (target != null)
+ target.cut();
}
}
@@ -922,7 +949,9 @@ public class DefaultEditorKit extends EditorKit
*/
public void actionPerformed(ActionEvent event)
{
- getTextComponent(event).paste();
+ JTextComponent target = getTextComponent(event);
+ if (target != null)
+ target.paste();
}
}
@@ -957,14 +986,26 @@ public class DefaultEditorKit extends EditorKit
{
// first we filter the following events:
// - control characters
- // - key events with the ALT modifier (FIXME: filter that too!)
- int cp = event.getActionCommand().codePointAt(0);
- if (Character.isISOControl(cp))
- return;
-
- JTextComponent t = getTextComponent(event);
- if (t != null && t.isEnabled() && t.isEditable())
- t.replaceSelection(event.getActionCommand());
+ // - key events with the ALT modifier
+ JTextComponent target = getTextComponent(event);
+ if ((target != null) && (event != null))
+ {
+ if ((target.isEditable()) && (target.isEnabled()))
+ {
+ String content = event.getActionCommand();
+ int mod = event.getModifiers();
+ if ((content != null) && (content.length() > 0)
+ && (mod & ActionEvent.ALT_MASK) == 0
+ && (mod & ActionEvent.CTRL_MASK) == 0)
+ {
+ char c = content.charAt(0);
+ if ((c >= 0x20) && (c != 0x7F))
+ {
+ target.replaceSelection(content);
+ }
+ }
+ }
+ }
}
}
@@ -992,7 +1033,8 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- t.replaceSelection("\n");
+ if (t != null)
+ t.replaceSelection("\n");
}
}
@@ -1047,7 +1089,8 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- t.replaceSelection("\t");
+ if (t != null)
+ t.replaceSelection("\t");
}
}
diff --git a/libjava/classpath/javax/swing/text/DefaultFormatter.java b/libjava/classpath/javax/swing/text/DefaultFormatter.java
index e42b1698af8..bf7c02a004d 100644
--- a/libjava/classpath/javax/swing/text/DefaultFormatter.java
+++ b/libjava/classpath/javax/swing/text/DefaultFormatter.java
@@ -216,7 +216,7 @@ public class DefaultFormatter extends JFormattedTextField.AbstractFormatter
*/
public DefaultFormatter()
{
- commitsOnValidEdit = true;
+ commitsOnValidEdit = false;
overwriteMode = true;
allowsInvalid = true;
}
@@ -330,7 +330,7 @@ public class DefaultFormatter extends JFormattedTextField.AbstractFormatter
*
* @return the class that is used for values
*/
- public Class getValueClass()
+ public Class<?> getValueClass()
{
return valueClass;
}
@@ -342,7 +342,7 @@ public class DefaultFormatter extends JFormattedTextField.AbstractFormatter
*
* @see #getValueClass()
*/
- public void setValueClass(Class valueClass)
+ public void setValueClass(Class<?> valueClass)
{
this.valueClass = valueClass;
}
diff --git a/libjava/classpath/javax/swing/text/DefaultHighlighter.java b/libjava/classpath/javax/swing/text/DefaultHighlighter.java
index 59f77316e87..69563e473ac 100644
--- a/libjava/classpath/javax/swing/text/DefaultHighlighter.java
+++ b/libjava/classpath/javax/swing/text/DefaultHighlighter.java
@@ -1,4 +1,4 @@
-/* DefaultHighlighter.java --
+/* DefaultHighlighter.java -- The default highlight for Swing
Copyright (C) 2004, 2006 Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -38,18 +38,21 @@ exception statement from your version. */
package javax.swing.text;
-import gnu.classpath.NotImplementedException;
-
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Shape;
import java.util.ArrayList;
+import java.util.Iterator;
import javax.swing.SwingUtilities;
import javax.swing.plaf.TextUI;
+/**
+ * The default highlight for Swing text components. It highlights text
+ * by filling the background with a rectangle.
+ */
public class DefaultHighlighter extends LayeredHighlighter
{
public static class DefaultHighlightPainter
@@ -68,11 +71,6 @@ public class DefaultHighlighter extends LayeredHighlighter
return color;
}
- private void paintHighlight(Graphics g, Rectangle rect)
- {
- g.fillRect(rect.x, rect.y, rect.width, rect.height);
- }
-
public void paint(Graphics g, int p0, int p1, Shape bounds,
JTextComponent t)
{
@@ -81,30 +79,31 @@ public class DefaultHighlighter extends LayeredHighlighter
Rectangle rect = bounds.getBounds();
- if (color == null)
- g.setColor(t.getSelectionColor());
- else
- g.setColor(color);
+ Color col = getColor();
+ if (col == null)
+ col = t.getSelectionColor();
+ g.setColor(col);
TextUI ui = t.getUI();
try
- {
-
- Rectangle l0 = ui.modelToView(t, p0, null);
- Rectangle l1 = ui.modelToView(t, p1, null);
-
- // Note: The computed locations may lie outside of the allocation
- // area if the text is scrolled.
+ {
+
+ Rectangle l0 = ui.modelToView(t, p0, null);
+ Rectangle l1 = ui.modelToView(t, p1, null);
- if (l0.y == l1.y)
+ // Note: The computed locations may lie outside of the allocation
+ // area if the text is scrolled.
+
+ if (l0.y == l1.y)
{
SwingUtilities.computeUnion(l0.x, l0.y, l0.width, l0.height, l1);
// Paint only inside the allocation area.
- SwingUtilities.computeIntersection(rect.x, rect.y, rect.width, rect.height, l1);
+ SwingUtilities.computeIntersection(rect.x, rect.y, rect.width,
+ rect.height, l1);
- paintHighlight(g, l1);
+ g.fillRect(l1.x, l1.y, l1.width, l1.height);
}
else
{
@@ -115,77 +114,71 @@ public class DefaultHighlighter extends LayeredHighlighter
// out the bounds.
// 3. The final line is painted from the left border to the
// position of p1.
-
- // Highlight first line until the end.
- // If rect.x is non-zero the calculation will properly adjust the
- // area to be painted.
- l0.x -= rect.x;
- l0.width = rect.width - l0.x - rect.x;
-
- paintHighlight(g, l0);
-
- int posBelow = Utilities.getPositionBelow(t, p0, l0.x);
- int p1RowStart = Utilities.getRowStart(t, p1);
- if (posBelow != -1
- && posBelow != p0
- && Utilities.getRowStart(t, posBelow)
- != p1RowStart)
- {
- Rectangle grow = ui.modelToView(t, posBelow);
- grow.x = rect.x;
- grow.width = rect.width;
-
- // Find further lines which have to be highlighted completely.
- int nextPosBelow = posBelow;
- while (nextPosBelow != -1
- && Utilities.getRowStart(t, nextPosBelow) != p1RowStart)
- {
- posBelow = nextPosBelow;
- nextPosBelow = Utilities.getPositionBelow(t, posBelow, l0.x);
-
- if (nextPosBelow == posBelow)
- break;
- }
- // Now posBelow is an offset on the last line which has to be painted
- // completely. (newPosBelow is on the same line as p1)
-
- // Retrieve the rectangle of posBelow and use its y and height
- // value to calculate the final height of the multiple line
- // spanning rectangle.
- Rectangle end = ui.modelToView(t, posBelow);
- grow.height = end.y + end.height - grow.y;
-
- paintHighlight(g, grow);
- }
-
- // Paint last line from its beginning to the position of p1.
- l1.width = l1.x + l1.width - rect.x;
- l1.x = rect.x;
- paintHighlight(g, l1);
- }
+
+ int firstLineWidth = rect.x + rect.width - l0.x;
+ g.fillRect(l0.x, l0.y, firstLineWidth, l0.height);
+ if (l0.y + l0.height != l1.y)
+ {
+ g.fillRect(rect.x, l0.y + l0.height, rect.width,
+ l1.y - l0.y - l0.height);
+ }
+ g.fillRect(rect.x, l1.y, l1.x - rect.x, l1.height);
+ }
}
catch (BadLocationException ex)
{
- AssertionError err = new AssertionError("Unexpected bad location exception");
- err.initCause(ex);
- throw err;
+ // Can't render. Comment out for debugging.
+ // ex.printStackTrace();
}
}
public Shape paintLayer(Graphics g, int p0, int p1, Shape bounds,
JTextComponent c, View view)
{
- throw new InternalError();
+ Color col = getColor();
+ if (col == null)
+ col = c.getSelectionColor();
+ g.setColor(col);
+
+ Rectangle rect = null;
+ if (p0 == view.getStartOffset() && p1 == view.getEndOffset())
+ {
+ // Paint complete bounds region.
+ rect = bounds instanceof Rectangle ? (Rectangle) bounds
+ : bounds.getBounds();
+ }
+ else
+ {
+ // Only partly inside the view.
+ try
+ {
+ Shape s = view.modelToView(p0, Position.Bias.Forward,
+ p1, Position.Bias.Backward,
+ bounds);
+ rect = s instanceof Rectangle ? (Rectangle) s : s.getBounds();
+ }
+ catch (BadLocationException ex)
+ {
+ // Can't render the highlight.
+ }
+ }
+
+ if (rect != null)
+ {
+ g.fillRect(rect.x, rect.y, rect.width, rect.height);
+ }
+ return rect;
}
}
private class HighlightEntry implements Highlighter.Highlight
{
- int p0;
- int p1;
+ Position p0;
+ Position p1;
Highlighter.HighlightPainter painter;
- public HighlightEntry(int p0, int p1, Highlighter.HighlightPainter painter)
+ public HighlightEntry(Position p0, Position p1,
+ Highlighter.HighlightPainter painter)
{
this.p0 = p0;
this.p1 = p1;
@@ -194,12 +187,12 @@ public class DefaultHighlighter extends LayeredHighlighter
public int getStartOffset()
{
- return p0;
+ return p0.getOffset();
}
public int getEndOffset()
{
- return p1;
+ return p1.getOffset();
}
public Highlighter.HighlightPainter getPainter()
@@ -209,6 +202,58 @@ public class DefaultHighlighter extends LayeredHighlighter
}
/**
+ * A HighlightEntry that is used for LayerPainter painters. In addition
+ * to the info maintained by the HighlightEntry, this class maintains
+ * a painting rectangle. This is used as repaint region when the
+ * highlight changes and the text component needs repainting.
+ */
+ private class LayerHighlightEntry
+ extends HighlightEntry
+ {
+
+ /**
+ * The paint rectangle.
+ */
+ Rectangle paintRect = new Rectangle();
+
+ LayerHighlightEntry(Position p0, Position p1,
+ Highlighter.HighlightPainter p)
+ {
+ super(p0, p1, p);
+ }
+
+ /**
+ * Paints the highlight by calling the LayerPainter. This
+ * restricts the area to be painted by startOffset and endOffset
+ * and manages the paint rectangle.
+ */
+ void paintLayeredHighlight(Graphics g, int p0, int p1, Shape bounds,
+ JTextComponent tc, View view)
+ {
+ p0 = Math.max(getStartOffset(), p0);
+ p1 = Math.min(getEndOffset(), p1);
+
+ Highlighter.HighlightPainter painter = getPainter();
+ if (painter instanceof LayerPainter)
+ {
+ LayerPainter layerPainter = (LayerPainter) painter;
+ Shape area = layerPainter.paintLayer(g, p0, p1, bounds, tc, view);
+ Rectangle rect;
+ if (area instanceof Rectangle && paintRect != null)
+ rect = (Rectangle) area;
+ else
+ rect = area.getBounds();
+
+ if (paintRect.width == 0 || paintRect.height == 0)
+ paintRect = rect.getBounds();
+ else
+ paintRect = SwingUtilities.computeUnion(rect.x, rect.y, rect.width,
+ rect.height, paintRect);
+ }
+ }
+ }
+
+ /**
* @specnote final as of 1.4
*/
public static final LayeredHighlighter.LayerPainter DefaultPainter =
@@ -254,11 +299,19 @@ public class DefaultHighlighter extends LayeredHighlighter
textComponent = null;
}
- public Object addHighlight(int p0, int p1, Highlighter.HighlightPainter painter)
+ public Object addHighlight(int p0, int p1,
+ Highlighter.HighlightPainter painter)
throws BadLocationException
{
checkPositions(p0, p1);
- HighlightEntry entry = new HighlightEntry(p0, p1, painter);
+ HighlightEntry entry;
+ Document doc = textComponent.getDocument();
+ Position pos0 = doc.createPosition(p0);
+ Position pos1 = doc.createPosition(p1);
+ if (getDrawsLayeredHighlights() && painter instanceof LayerPainter)
+ entry = new LayerHighlightEntry(pos0, pos1, painter);
+ else
+ entry = new HighlightEntry(pos0, pos1, painter);
highlights.add(entry);
textComponent.getUI().damageRange(textComponent, p0, p1);
@@ -268,16 +321,67 @@ public class DefaultHighlighter extends LayeredHighlighter
public void removeHighlight(Object tag)
{
+ HighlightEntry entry = (HighlightEntry) tag;
+ if (entry instanceof LayerHighlightEntry)
+ {
+ LayerHighlightEntry lEntry = (LayerHighlightEntry) entry;
+ Rectangle paintRect = lEntry.paintRect;
+ textComponent.repaint(paintRect.x, paintRect.y, paintRect.width,
+ paintRect.height);
+ }
+ else
+ {
+ textComponent.getUI().damageRange(textComponent,
+ entry.getStartOffset(),
+ entry.getEndOffset());
+ }
highlights.remove(tag);
- HighlightEntry entry = (HighlightEntry) tag;
- textComponent.getUI().damageRange(textComponent,
- entry.p0,
- entry.p1);
}
public void removeAllHighlights()
{
+ // Repaint damaged region.
+ int minX = 0;
+ int maxX = 0;
+ int minY = 0;
+ int maxY = 0;
+ int p0 = -1;
+ int p1 = -1;
+ for (Iterator i = highlights.iterator(); i.hasNext();)
+ {
+ HighlightEntry e = (HighlightEntry) i.next();
+ if (e instanceof LayerHighlightEntry)
+ {
+ LayerHighlightEntry le = (LayerHighlightEntry) e;
+ Rectangle r = le.paintRect;
+ minX = Math.min(r.x, minX);
+ maxX = Math.max(r.x + r.width, maxX);
+ minY = Math.min(r.y, minY);
+ maxY = Math.max(r.y + r.height, maxY);
+ }
+ else
+ {
+ if (p0 == -1 || p1 == -1)
+ {
+ p0 = e.getStartOffset();
+ p1 = e.getEndOffset();
+ }
+ else
+ {
+ p0 = Math.min(p0, e.getStartOffset());
+ p1 = Math.max(p1, e.getEndOffset());
+ }
+ }
+ if (minX != maxX && minY != maxY)
+ textComponent.repaint(minX, minY, maxX - minX, maxY - minY);
+ if (p0 != -1 && p1 != -1)
+ {
+ TextUI ui = textComponent.getUI();
+ ui.damageRange(textComponent, p0, p1);
+ }
+
+ }
highlights.clear();
}
@@ -290,94 +394,61 @@ public class DefaultHighlighter extends LayeredHighlighter
public void changeHighlight(Object tag, int n0, int n1)
throws BadLocationException
{
- int o0, o1;
-
- checkPositions(n0, n1);
- HighlightEntry entry = (HighlightEntry) tag;
- o0 = entry.p0;
- o1 = entry.p1;
-
- // Prevent useless write & repaint operations.
- if (o0 == n0 && o1 == n1)
- return;
-
- entry.p0 = n0;
- entry.p1 = n1;
-
+ Document doc = textComponent.getDocument();
TextUI ui = textComponent.getUI();
-
- // Special situation where the old area has to be cleared simply.
- if (n0 == n1)
- ui.damageRange(textComponent, o0, o1);
- // Calculates the areas where a change is really neccessary
- else if ((o1 > n0 && o1 <= n1)
- || (n1 > o0 && n1 <= o1))
+ if (tag instanceof LayerHighlightEntry)
{
- // [fds, fde) - the first damage region
- // [sds, sde] - the second damage region
- int fds, sds;
- int fde, sde;
-
- // Calculate first damaged region.
- if(o0 < n0)
- {
- // Damaged region will be cleared as
- // the old highlight region starts first.
- fds = o0;
- fde = n0;
- }
- else
- {
- // Damaged region will be painted as
- // the new highlight region starts first.
- fds = n0;
- fde = o0;
- }
-
- if (o1 < n1)
+ LayerHighlightEntry le = (LayerHighlightEntry) tag;
+ Rectangle r = le.paintRect;
+ if (r.width > 0 && r.height > 0)
+ textComponent.repaint(r.x, r.y, r.width, r.height);
+ r.width = 0;
+ r.height = 0;
+ le.p0 = doc.createPosition(n0);
+ le.p1 = doc.createPosition(n1);
+ ui.damageRange(textComponent, Math.min(n0, n1), Math.max(n0, n1));
+ }
+ else if (tag instanceof HighlightEntry)
+ {
+ HighlightEntry e = (HighlightEntry) tag;
+ int p0 = e.getStartOffset();
+ int p1 = e.getEndOffset();
+ if (p0 == n0)
{
- // Final region will be painted as the
- // old highlight region finishes first
- sds = o1;
- sde = n1;
+ ui.damageRange(textComponent, Math.min(p1, n1),
+ Math.max(p1, n1));
}
- else
+ else if (n1 == p1)
{
- // Final region will be cleared as the
- // new highlight region finishes first.
- sds = n1;
- sde = o1;
+ ui.damageRange(textComponent, Math.min(p0, n0),
+ Math.max(p0, n0));
}
-
- // If there is no undamaged region in between
- // call damageRange only once.
- if (fde == sds)
- ui.damageRange(textComponent, fds, sde);
else
{
- if (fds != fde)
- ui.damageRange(textComponent, fds, fde);
-
- if (sds != sde)
- ui.damageRange(textComponent, sds, sde);
+ ui.damageRange(textComponent, p0, p1);
+ ui.damageRange(textComponent, n0, n1);
}
+ e.p0 = doc.createPosition(n0);
+ e.p1 = doc.createPosition(n1);
}
- else
- {
- // The two regions do not overlap. So mark
- // both areas as damaged.
- ui.damageRange(textComponent, o0, o1);
- ui.damageRange(textComponent, n0, n1);
- }
-
}
public void paintLayeredHighlights(Graphics g, int p0, int p1,
Shape viewBounds, JTextComponent editor,
View view)
- throws NotImplementedException
{
- // TODO: Implement this properly.
+ for (Iterator i = highlights.iterator(); i.hasNext();)
+ {
+ Object o = i.next();
+ if (o instanceof LayerHighlightEntry)
+ {
+ LayerHighlightEntry entry = (LayerHighlightEntry) o;
+ int start = entry.getStartOffset();
+ int end = entry.getEndOffset();
+ if ((p0 < start && p1 > start) || (p0 >= start && p0 < end))
+ entry.paintLayeredHighlight(g, p0, p1, viewBounds, editor, view);
+ }
+ }
}
public void paint(Graphics g)
@@ -399,7 +470,9 @@ public class DefaultHighlighter extends LayeredHighlighter
for (int index = 0; index < size; ++index)
{
HighlightEntry entry = (HighlightEntry) highlights.get(index);
- entry.painter.paint(g, entry.p0, entry.p1, bounds, textComponent);
+ if (! (entry instanceof LayerHighlightEntry))
+ entry.painter.paint(g, entry.getStartOffset(), entry.getEndOffset(),
+ bounds, textComponent);
}
}
}
diff --git a/libjava/classpath/javax/swing/text/DefaultStyledDocument.java b/libjava/classpath/javax/swing/text/DefaultStyledDocument.java
index bd21e55c6fb..3156ca67f66 100644
--- a/libjava/classpath/javax/swing/text/DefaultStyledDocument.java
+++ b/libjava/classpath/javax/swing/text/DefaultStyledDocument.java
@@ -41,7 +41,9 @@ package javax.swing.text;
import java.awt.Color;
import java.awt.Font;
import java.io.Serializable;
+import java.util.ArrayList;
import java.util.Enumeration;
+import java.util.Iterator;
import java.util.Stack;
import java.util.Vector;
@@ -424,6 +426,58 @@ public class DefaultStyledDocument extends AbstractDocument implements
*/
public class ElementBuffer implements Serializable
{
+ /**
+ * Instance of all editing information for an object in the Vector. This class
+ * is used to add information to the DocumentEvent associated with an
+ * insertion/removal/change as well as to store the changes that need to be
+ * made so they can be made all at the same (appropriate) time.
+ */
+ class Edit
+ {
+ /** The element to edit . */
+ Element e;
+
+ /** The index of the change. */
+ int index;
+
+ /** The removed elements. */
+ ArrayList removed = new ArrayList();
+
+ /** The added elements. */
+ ArrayList added = new ArrayList();
+
+ /**
+ * Indicates if this edit contains a fracture.
+ */
+ boolean isFracture;
+
+ /**
+ * Creates a new Edit for the specified element at index i.
+ *
+ * @param el the element
+ * @param i the index
+ */
+ Edit(Element el, int i)
+ {
+ this(el, i, false);
+ }
+
+ /**
+ * Creates a new Edit for the specified element at index i.
+ *
+ * @param el the element
+ * @param i the index
+ * @param frac if this is a fracture edit or not
+ */
+ Edit(Element el, int i, boolean frac)
+ {
+ e = el;
+ index = i;
+ isFracture = frac;
+ }
+
+ }
+
/** The serialization UID (compatible with JDK1.5). */
private static final long serialVersionUID = 1688745877691146623L;
@@ -442,11 +496,20 @@ public class DefaultStyledDocument extends AbstractDocument implements
/** Holds the position of the change. */
private int pos;
- /** Holds the element that was last fractured. */
- private Element lastFractured;
-
- /** True if a fracture was not created during a insertFracture call. */
- private boolean fracNotCreated;
+ /**
+ * The parent of the fracture.
+ */
+ private Element fracturedParent;
+
+ /**
+ * The fractured child.
+ */
+ private Element fracturedChild;
+
+ /**
+ * Indicates if a fracture has been created.
+ */
+ private boolean createdFracture;
/**
* The current position in the element tree. This is used for bulk inserts
@@ -454,10 +517,17 @@ public class DefaultStyledDocument extends AbstractDocument implements
*/
private Stack elementStack;
+ private Edit[] insertPath;
+
+ private boolean recreateLeafs;
+
/**
- * The ElementChange that describes the latest changes.
+ * Vector that contains all the edits. Maybe replace by a HashMap.
*/
- DefaultDocumentEvent documentEvent;
+ private ArrayList edits;
+
+ private boolean offsetLastIndex;
+ private boolean offsetLastIndexReplace;
/**
* Creates a new <code>ElementBuffer</code> for the specified
@@ -469,7 +539,6 @@ public class DefaultStyledDocument extends AbstractDocument implements
public ElementBuffer(Element root)
{
this.root = root;
- elementStack = new Stack();
}
/**
@@ -495,13 +564,9 @@ public class DefaultStyledDocument extends AbstractDocument implements
*/
public void remove(int offs, int len, DefaultDocumentEvent ev)
{
- if (len == 0)
- return;
- offset = offs;
- length = len;
- pos = offset;
- documentEvent = ev;
+ prepareEdit(offs, len);
removeUpdate();
+ finishEdit(ev);
}
/**
@@ -511,109 +576,293 @@ public class DefaultStyledDocument extends AbstractDocument implements
*/
protected void removeUpdate()
{
- int startParagraph = root.getElementIndex(offset);
- int endParagraph = root.getElementIndex(offset + length);
- Element[] empty = new Element[0];
- int removeStart = -1;
- int removeEnd = -1;
- for (int i = startParagraph; i < endParagraph; i++)
+ removeElements(root, offset, endOffset);
+ }
+
+ private boolean removeElements(Element elem, int rmOffs0, int rmOffs1)
+ {
+ boolean ret = false;
+ if (! elem.isLeaf())
{
- BranchElement paragraph = (BranchElement) root.getElement(i);
- int contentStart = paragraph.getElementIndex(offset);
- int contentEnd = paragraph.getElementIndex(offset + length);
- if (contentStart == paragraph.getStartOffset()
- && contentEnd == paragraph.getEndOffset())
+ // Update stack for changes.
+ int index0 = elem.getElementIndex(rmOffs0);
+ int index1 = elem.getElementIndex(rmOffs1);
+ elementStack.push(new Edit(elem, index0));
+ Edit ec = (Edit) elementStack.peek();
+
+ // If the range is contained by one element,
+ // we just forward the request
+ if (index0 == index1)
{
- // In this case we only need to remove the whole paragraph. We
- // do this in one go after this loop and only record the indices
- // here.
- if (removeStart == -1)
+ Element child0 = elem.getElement(index0);
+ if(rmOffs0 <= child0.getStartOffset()
+ && rmOffs1 >= child0.getEndOffset())
{
- removeStart = i;
- removeEnd = i;
+ // Element totally removed.
+ ec.removed.add(child0);
+ }
+ else if (removeElements(child0, rmOffs0, rmOffs1))
+ {
+ ec.removed.add(child0);
}
- else
- removeEnd = i;
}
else
{
- // In this case we remove a couple of child elements from this
- // paragraph.
- int removeLen = contentEnd - contentStart;
- Element[] removed = new Element[removeLen];
- for (int j = contentStart; j < contentEnd; j++)
- removed[j] = paragraph.getElement(j);
- Edit edit = getEditForParagraphAndIndex(paragraph, contentStart);
- edit.addRemovedElements(removed);
+ // The removal range spans elements. If we can join
+ // the two endpoints, do it. Otherwise we remove the
+ // interior and forward to the endpoints.
+ Element child0 = elem.getElement(index0);
+ Element child1 = elem.getElement(index1);
+ boolean containsOffs1 = (rmOffs1 < elem.getEndOffset());
+ if (containsOffs1 && canJoin(child0, child1))
+ {
+ // Remove and join.
+ for (int i = index0; i <= index1; i++)
+ {
+ ec.removed.add(elem.getElement(i));
+ }
+ Element e = join(elem, child0, child1, rmOffs0, rmOffs1);
+ ec.added.add(e);
}
+ else
+ {
+ // Remove interior and forward.
+ int rmIndex0 = index0 + 1;
+ int rmIndex1 = index1 - 1;
+ if (child0.getStartOffset() == rmOffs0
+ || (index0 == 0 && child0.getStartOffset() > rmOffs0
+ && child0.getEndOffset() <= rmOffs1))
+ {
+ // Start element completely consumed.
+ child0 = null;
+ rmIndex0 = index0;
+ }
+ if (! containsOffs1)
+ {
+ child1 = null;
+ rmIndex1++;
+ }
+ else if (child1.getStartOffset() == rmOffs1)
+ {
+ // End element not touched.
+ child1 = null;
+ }
+ if (rmIndex0 <= rmIndex1)
+ {
+ ec.index = rmIndex0;
+ }
+ for (int i = rmIndex0; i <= rmIndex1; i++)
+ {
+ ec.removed.add(elem.getElement(i));
+ }
+ if (child0 != null)
+ {
+ if(removeElements(child0, rmOffs0, rmOffs1))
+ {
+ ec.removed.add(0, child0);
+ ec.index = index0;
+ }
+ }
+ if (child1 != null)
+ {
+ if(removeElements(child1, rmOffs0, rmOffs1))
+ {
+ ec.removed.add(child1);
+ }
+ }
+ }
+ }
+
+ // Perform changes.
+ pop();
+
+ // Return true if we no longer have any children.
+ if(elem.getElementCount() == (ec.removed.size() - ec.added.size()))
+ ret = true;
}
- // Now we remove paragraphs from the root that have been tagged for
- // removal.
- if (removeStart != -1)
- {
- int removeLen = removeEnd - removeStart;
- Element[] removed = new Element[removeLen];
- for (int i = removeStart; i < removeEnd; i++)
- removed[i] = root.getElement(i);
- Edit edit = getEditForParagraphAndIndex((BranchElement) root,
- removeStart);
- edit.addRemovedElements(removed);
- }
+ return ret;
}
/**
- * Performs the actual work for {@link #change}. The elements at the
- * interval boundaries are split up (if necessary) so that the interval
- * boundaries are located at element boundaries.
+ * Creates a document in response to a call to
+ * {@link DefaultStyledDocument#create(ElementSpec[])}.
+ *
+ * @param len the length of the inserted text
+ * @param data the specs for the elements
+ * @param ev the document event
*/
- protected void changeUpdate()
+ void create(int len, ElementSpec[] data, DefaultDocumentEvent ev)
+ {
+ prepareEdit(offset, len);
+ Element el = root;
+ int index = el.getElementIndex(0);
+ while (! el.isLeaf())
+ {
+ Element child = el.getElement(index);
+ Edit edit = new Edit(el, index, false);
+ elementStack.push(edit);
+ el = child;
+ index = el.getElementIndex(0);
+ }
+ Edit ed = (Edit) elementStack.peek();
+ Element child = ed.e.getElement(ed.index);
+ ed.added.add(createLeafElement(ed.e, child.getAttributes(), getLength(),
+ child.getEndOffset()));
+ ed.removed.add(child);
+ while (elementStack.size() > 1)
+ pop();
+ int n = data.length;
+
+ // Reset root element's attributes.
+ AttributeSet newAtts = null;
+ if (n > 0 && data[0].getType() == ElementSpec.StartTagType)
+ newAtts = data[0].getAttributes();
+ if (newAtts == null)
+ newAtts = SimpleAttributeSet.EMPTY;
+ MutableAttributeSet mAtts = (MutableAttributeSet) root.getAttributes();
+ ev.addEdit(new AttributeUndoableEdit(root, newAtts, true));
+ mAtts.removeAttributes(mAtts);
+ mAtts.addAttributes(newAtts);
+
+ // Insert the specified elements.
+ for (int i = 1; i < n; i++)
+ insertElement(data[i]);
+
+ // Pop remaining stack.
+ while (elementStack.size() > 0)
+ pop();
+
+ finishEdit(ev);
+ }
+
+ private boolean canJoin(Element e0, Element e1)
+ {
+ boolean ret = false;
+ if ((e0 != null) && (e1 != null))
+ {
+ // Don't join a leaf to a branch.
+ boolean isLeaf0 = e0.isLeaf();
+ boolean isLeaf1 = e1.isLeaf();
+ if(isLeaf0 == isLeaf1)
+ {
+ if (isLeaf0)
+ {
+ // Only join leaves if the attributes match, otherwise
+ // style information will be lost.
+ ret = e0.getAttributes().isEqual(e1.getAttributes());
+ }
+ else
+ {
+ // Only join non-leafs if the names are equal. This may result
+ // in loss of style information, but this is typically
+ // acceptable for non-leafs.
+ String name0 = e0.getName();
+ String name1 = e1.getName();
+ if (name0 != null)
+ ret = name0.equals(name1);
+ else if (name1 != null)
+ ret = name1.equals(name0);
+ else // Both names null.
+ ret = true;
+ }
+ }
+ }
+ return ret;
+ }
+
+ private Element join(Element p, Element left, Element right, int rmOffs0,
+ int rmOffs1)
{
- // Split up the element at the start offset if necessary.
- Element el = getCharacterElement(offset);
- Element[] res = split(el, offset, 0, el.getElementIndex(offset));
- BranchElement par = (BranchElement) el.getParentElement();
- int index = par.getElementIndex(offset);
- Edit edit = getEditForParagraphAndIndex(par, index);
- if (res[1] != null)
+ Element joined = null;
+ if (left.isLeaf() && right.isLeaf())
+ {
+ joined = createLeafElement(p, left.getAttributes(),
+ left.getStartOffset(),
+ right.getEndOffset());
+ }
+ else if ((! left.isLeaf()) && (! right.isLeaf()))
{
- Element[] removed;
- Element[] added;
- if (res[0] == null)
+ // Join two branch elements. This copies the children before
+ // the removal range on the left element, and after the removal
+ // range on the right element. The two elements on the edge
+ // are joined if possible and needed.
+ joined = createBranchElement(p, left.getAttributes());
+ int ljIndex = left.getElementIndex(rmOffs0);
+ int rjIndex = right.getElementIndex(rmOffs1);
+ Element lj = left.getElement(ljIndex);
+ if (lj.getStartOffset() >= rmOffs0)
+ {
+ lj = null;
+ }
+ Element rj = right.getElement(rjIndex);
+ if (rj.getStartOffset() == rmOffs1)
{
- removed = new Element[0];
- added = new Element[] { res[1] };
- index++;
+ rj = null;
+ }
+ ArrayList children = new ArrayList();
+ // Transfer the left.
+ for (int i = 0; i < ljIndex; i++)
+ {
+ children.add(clone(joined, left.getElement(i)));
+ }
+
+ // Transfer the join/middle.
+ if (canJoin(lj, rj))
+ {
+ Element e = join(joined, lj, rj, rmOffs0, rmOffs1);
+ children.add(e);
}
else
{
- removed = new Element[] { el };
- added = new Element[] { res[0], res[1] };
+ if (lj != null)
+ {
+ children.add(cloneAsNecessary(joined, lj, rmOffs0, rmOffs1));
+ }
+ if (rj != null)
+ {
+ children.add(cloneAsNecessary(joined, rj, rmOffs0, rmOffs1));
+ }
+ }
+
+ // Transfer the right.
+ int n = right.getElementCount();
+ for (int i = (rj == null) ? rjIndex : rjIndex + 1; i < n; i++)
+ {
+ children.add(clone(joined, right.getElement(i)));
}
- edit.addRemovedElements(removed);
- edit.addAddedElements(added);
+ // Install the children.
+ Element[] c = new Element[children.size()];
+ c = (Element[]) children.toArray(c);
+ ((BranchElement) joined).replace(0, 0, c);
+ }
+ else
+ {
+ assert false : "Must not happen";
}
+ return joined;
+ }
- int endOffset = offset + length;
- el = getCharacterElement(endOffset);
- res = split(el, endOffset, 0, el.getElementIndex(endOffset));
- par = (BranchElement) el.getParentElement();
- if (res[0] != null)
+ /**
+ * Performs the actual work for {@link #change}. The elements at the
+ * interval boundaries are split up (if necessary) so that the interval
+ * boundaries are located at element boundaries.
+ */
+ protected void changeUpdate()
+ {
+ boolean didEnd = split(offset, length);
+ if (! didEnd)
{
- Element[] removed;
- Element[] added;
- if (res[1] == null)
- {
- removed = new Element[0];
- added = new Element[] { res[1] };
- }
- else
+ // need to do the other end
+ while (elementStack.size() != 0)
{
- removed = new Element[] { el };
- added = new Element[] { res[0], res[1] };
+ pop();
}
- edit.addRemovedElements(removed);
- edit.addAddedElements(added);
+ split(offset + length, 0);
+ }
+ while (elementStack.size() != 0)
+ {
+ pop();
}
}
@@ -633,13 +882,9 @@ public class DefaultStyledDocument extends AbstractDocument implements
*/
public void change(int offset, int length, DefaultDocumentEvent ev)
{
- if (length == 0)
- return;
- this.offset = offset;
- this.pos = offset;
- this.length = length;
- documentEvent = ev;
+ prepareEdit(offset, length);
changeUpdate();
+ finishEdit(ev);
}
/**
@@ -683,6 +928,39 @@ public class DefaultStyledDocument extends AbstractDocument implements
return clone;
}
+ private Element cloneAsNecessary(Element parent, Element clonee,
+ int rmOffs0, int rmOffs1)
+ {
+ Element cloned;
+ if (clonee.isLeaf())
+ {
+ cloned = createLeafElement(parent, clonee.getAttributes(),
+ clonee.getStartOffset(),
+ clonee.getEndOffset());
+ }
+ else
+ {
+ Element e = createBranchElement(parent, clonee.getAttributes());
+ int n = clonee.getElementCount();
+ ArrayList childrenList = new ArrayList(n);
+ for (int i = 0; i < n; i++)
+ {
+ Element elem = clonee.getElement(i);
+ if (elem.getStartOffset() < rmOffs0
+ || elem.getEndOffset() > rmOffs1)
+ {
+ childrenList.add(cloneAsNecessary(e, elem, rmOffs0,
+ rmOffs1));
+ }
+ }
+ Element[] children = new Element[childrenList.size()];
+ children = (Element[]) childrenList.toArray(children);
+ ((BranchElement) e).replace(0, 0, children);
+ cloned = e;
+ }
+ return cloned;
+ }
+
/**
* Inserts new <code>Element</code> in the document at the specified
* position. Most of the work is done by {@link #insertUpdate}, after some
@@ -701,70 +979,100 @@ public class DefaultStyledDocument extends AbstractDocument implements
public void insert(int offset, int length, ElementSpec[] data,
DefaultDocumentEvent ev)
{
- if (length == 0)
- return;
-
+ if (length > 0)
+ {
+ prepareEdit(offset, length);
+ insertUpdate(data);
+ finishEdit(ev);
+ }
+ }
+
+ /**
+ * Prepares the state of this object for performing an insert.
+ *
+ * @param offset the offset at which is inserted
+ * @param length the length of the inserted region
+ */
+ private void prepareEdit(int offset, int length)
+ {
this.offset = offset;
this.pos = offset;
this.endOffset = offset + length;
this.length = length;
- documentEvent = ev;
-
- edits.removeAllElements();
- elementStack.removeAllElements();
- lastFractured = null;
- fracNotCreated = false;
- insertUpdate(data);
+
+ if (edits == null)
+ edits = new ArrayList();
+ else
+ edits.clear();
+
+ if (elementStack == null)
+ elementStack = new Stack();
+ else
+ elementStack.clear();
+
+ fracturedParent = null;
+ fracturedChild = null;
+ offsetLastIndex = false;
+ offsetLastIndexReplace = false;
+ }
+
+ /**
+ * Finishes an insert. This applies all changes and updates
+ * the DocumentEvent.
+ *
+ * @param ev the document event
+ */
+ private void finishEdit(DefaultDocumentEvent ev)
+ {
// This for loop applies all the changes that were made and updates the
// DocumentEvent.
- int size = edits.size();
- for (int i = 0; i < size; i++)
- {
- Edit curr = (Edit) edits.get(i);
- BranchElement e = (BranchElement) curr.e;
- Element[] removed = curr.getRemovedElements();
- Element[] added = curr.getAddedElements();
- // FIXME: We probably shouldn't create the empty Element[] in the
- // first place.
- if (removed.length > 0 || added.length > 0)
- {
- if (curr.index + removed.length <= e.getElementCount())
- {
- e.replace(curr.index, removed.length, added);
- ElementEdit ee = new ElementEdit(e, curr.index, removed, added);
- ev.addEdit(ee);
- }
- else
- {
- System.err.println("WARNING: Tried to replace elements ");
- System.err.print("beyond boundaries: elementCount: ");
- System.err.println(e.getElementCount());
- System.err.print("index: " + curr.index);
- System.err.println(", removed.length: " + removed.length);
- }
- }
- }
+ for (Iterator i = edits.iterator(); i.hasNext();)
+ {
+ Edit edits = (Edit) i.next();
+ Element[] removed = new Element[edits.removed.size()];
+ removed = (Element[]) edits.removed.toArray(removed);
+ Element[] added = new Element[edits.added.size()];
+ added = (Element[]) edits.added.toArray(added);
+ int index = edits.index;
+ BranchElement parent = (BranchElement) edits.e;
+ parent.replace(index, removed.length, added);
+ ElementEdit ee = new ElementEdit(parent, index, removed, added);
+ ev.addEdit(ee);
+ }
+ edits.clear();
+ elementStack.clear();
}
/**
- * Inserts new content
+ * Inserts new content.
*
- * @param data
- * the element specifications for the elements to be inserted
+ * @param data the element specifications for the elements to be inserted
*/
protected void insertUpdate(ElementSpec[] data)
{
- // Push the root and the paragraph at offset onto the element stack.
+ // Push the current path to the stack.
Element current = root;
- int index;
- while (!current.isLeaf())
+ int index = current.getElementIndex(offset);
+ while (! current.isLeaf())
{
+ Element child = current.getElement(index);
+ int editIndex = child.isLeaf() ? index : index + 1;
+ Edit edit = new Edit(current, editIndex);
+ elementStack.push(edit);
+ current = child;
index = current.getElementIndex(offset);
- elementStack.push(current);
- current = current.getElement(index);
}
-
+
+ // Create a copy of the original path.
+ insertPath = new Edit[elementStack.size()];
+ insertPath = (Edit[]) elementStack.toArray(insertPath);
+
+ // No fracture yet.
+ createdFracture = false;
+
+ // Insert first content tag.
int i = 0;
+ recreateLeafs = false;
int type = data[0].getType();
if (type == ElementSpec.ContentType)
{
@@ -780,127 +1088,132 @@ public class DefaultStyledDocument extends AbstractDocument implements
createFracture(data);
i = 0;
}
-
+
// Handle each ElementSpec individually.
for (; i < data.length; i++)
{
- BranchElement paragraph = (BranchElement) elementStack.peek();
- switch (data[i].getType())
- {
- case ElementSpec.StartTagType:
- switch (data[i].getDirection())
- {
- case ElementSpec.JoinFractureDirection:
- // Fracture the tree and ensure the appropriate element
- // is on top of the stack.
- fracNotCreated = false;
- insertFracture(data[i]);
- if (fracNotCreated)
- {
- if (lastFractured != null)
- elementStack.push(lastFractured.getParentElement());
- else
- elementStack.push(paragraph.getElement(0));
- }
- break;
- case ElementSpec.JoinNextDirection:
- // Push the next paragraph element onto the stack so
- // future insertions are added to it.
- int ix = paragraph.getElementIndex(pos) + 1;
- elementStack.push(paragraph.getElement(ix));
- break;
- default:
- Element br = null;
- if (data.length > i + 1)
- {
- // leaves will be added to paragraph later
- int x = 0;
- if (paragraph.getElementCount() > 0)
- x = paragraph.getElementIndex(pos) + 1;
- Edit e = getEditForParagraphAndIndex(paragraph, x);
- br = (BranchElement) createBranchElement(paragraph,
- data[i].getAttributes());
- e.added.add(br);
- elementStack.push(br);
- }
- else
- // need to add leaves to paragraph now
- br = insertParagraph(paragraph, pos);
- break;
- }
- break;
- case ElementSpec.EndTagType:
- elementStack.pop();
- break;
- case ElementSpec.ContentType:
- insertContentTag(data[i]);
- offset = pos;
- break;
- }
+ insertElement(data[i]);
+ }
+
+ // Fracture if we haven't done yet.
+ if (! createdFracture)
+ fracture(-1);
+
+ // Pop the remaining stack.
+ while (elementStack.size() != 0)
+ pop();
+
+ // Offset last index if necessary.
+ if (offsetLastIndex && offsetLastIndexReplace)
+ insertPath[insertPath.length - 1].index++;
+
+ // Make sure we havea an Edit for each path item that has a change.
+ for (int p = insertPath.length - 1; p >= 0; p--)
+ {
+ Edit edit = insertPath[p];
+ if (edit.e == fracturedParent)
+ edit.added.add(fracturedChild);
+ if ((edit.added.size() > 0 || edit.removed.size() > 0)
+ && ! edits.contains(edit))
+ edits.add(edit);
+ }
+
+ // Remove element that would be created by an insert at 0 with
+ // an initial end tag.
+ if (offset == 0 && fracturedParent != null
+ && data[0].getType() == ElementSpec.EndTagType)
+ {
+ int p;
+ for (p = 0;
+ p < data.length && data[p].getType() == ElementSpec.EndTagType;
+ p++);
+ Edit edit = insertPath[insertPath.length - p - 1];
+ edit.index--;
+ edit.removed.add(0, edit.e.getElement(edit.index));
}
}
-
- /**
- * Inserts a new paragraph.
- *
- * @param par -
- * the parent
- * @param offset -
- * the offset
- * @return the new paragraph
- */
- private Element insertParagraph(BranchElement par, int offset)
+
+ private void pop()
+ {
+ Edit edit = (Edit) elementStack.peek();
+ elementStack.pop();
+ if ((edit.added.size() > 0) || (edit.removed.size() > 0))
+ {
+ edits.add(edit);
+ }
+ else if (! elementStack.isEmpty())
+ {
+ Element e = edit.e;
+ if (e.getElementCount() == 0)
+ {
+ // If we pushed a branch element that didn't get
+ // used, make sure its not marked as having been added.
+ edit = (Edit) elementStack.peek();
+ edit.added.remove(e);
+ }
+ }
+ }
+
+ private void insertElement(ElementSpec spec)
{
- int index = par.getElementIndex(offset);
- Element current = par.getElement(index);
- Element[] res = split(current, offset, 0, 0);
- Edit e = getEditForParagraphAndIndex(par, index + 1);
- Element ret;
- if (res[1] != null)
+ Edit edit = (Edit) elementStack.peek();
+ switch (spec.getType())
{
- Element[] removed;
- Element[] added;
- if (res[0] == null)
+ case ElementSpec.StartTagType:
+ switch (spec.getDirection())
{
- removed = new Element[0];
- if (res[1] instanceof BranchElement)
+ case ElementSpec.JoinFractureDirection:
+ // Fracture the tree and ensure the appropriate element
+ // is on top of the stack.
+ if (! createdFracture)
{
- added = new Element[] { res[1] };
- ret = res[1];
+ fracture(elementStack.size() - 1);
}
- else
+ if (! edit.isFracture)
{
- ret = createBranchElement(par, null);
- added = new Element[] { ret, res[1] };
+ // If the parent isn't a fracture, then the fracture is
+ // in fracturedChild.
+ Edit newEdit = new Edit(fracturedChild, 0, true);
+ elementStack.push(newEdit);
}
- index++;
- }
- else
- {
- removed = new Element[] { current };
- if (res[1] instanceof BranchElement)
+ else
{
- ret = res[1];
- added = new Element[] { res[0], res[1] };
+ // Otherwise use the parent's first child.
+ Element el = edit.e.getElement(0);
+ Edit newEdit = new Edit(el, 0, true);
+ elementStack.push(newEdit);
}
- else
+ break;
+ case ElementSpec.JoinNextDirection:
+ // Push the next paragraph element onto the stack so
+ // future insertions are added to it.
+ Element parent = edit.e.getElement(edit.index);
+ if (parent.isLeaf())
{
- ret = createBranchElement(par, null);
- added = new Element[] { res[0], ret, res[1] };
+ if (edit.index + 1 < edit.e.getElementCount())
+ parent = edit.e.getElement(edit.index + 1);
+ else
+ assert false; // Must not happen.
}
+ elementStack.push(new Edit(parent, 0, true));
+ break;
+ default:
+ Element branch = createBranchElement(edit.e,
+ spec.getAttributes());
+ edit.added.add(branch);
+ elementStack.push(new Edit(branch, 0));
+ break;
}
-
- e.addAddedElements(added);
- e.addRemovedElements(removed);
- }
- else
- {
- ret = createBranchElement(par, null);
- e.addAddedElement(ret);
+ break;
+ case ElementSpec.EndTagType:
+ pop();
+ break;
+ case ElementSpec.ContentType:
+ insertContentTag(spec, edit);
+ break;
}
- return ret;
}
-
+
/**
* Inserts the first tag into the document.
*
@@ -910,67 +1223,71 @@ public class DefaultStyledDocument extends AbstractDocument implements
private void insertFirstContentTag(ElementSpec[] data)
{
ElementSpec first = data[0];
- BranchElement paragraph = (BranchElement) elementStack.peek();
- int index = paragraph.getElementIndex(pos);
- Element current = paragraph.getElement(index);
- int newEndOffset = pos + first.length;
+ Edit edit = (Edit) elementStack.peek();
+ Element current = edit.e.getElement(edit.index);
+ int firstEndOffset = offset + first.length;
boolean onlyContent = data.length == 1;
- Edit edit = getEditForParagraphAndIndex(paragraph, index);
switch (first.getDirection())
{
case ElementSpec.JoinPreviousDirection:
- if (current.getEndOffset() != newEndOffset && !onlyContent)
+ if (current.getEndOffset() != firstEndOffset && ! onlyContent)
{
- Element newEl1 = createLeafElement(paragraph,
+ Element newEl1 = createLeafElement(edit.e,
current.getAttributes(),
current.getStartOffset(),
- newEndOffset);
- edit.addAddedElement(newEl1);
- edit.addRemovedElement(current);
- offset = newEndOffset;
+ firstEndOffset);
+ edit.added.add(newEl1);
+ edit.removed.add(current);
+ if (current.getEndOffset() != endOffset)
+ recreateLeafs = true;
+ else
+ offsetLastIndex = true;
+ }
+ else
+ {
+ offsetLastIndex = true;
+ offsetLastIndexReplace = true;
}
break;
case ElementSpec.JoinNextDirection:
- if (pos != 0)
+ if (offset != 0)
{
- Element newEl1 = createLeafElement(paragraph,
+ Element newEl1 = createLeafElement(edit.e,
current.getAttributes(),
current.getStartOffset(),
- pos);
- edit.addAddedElement(newEl1);
- Element next = paragraph.getElement(index + 1);
-
+ offset);
+ edit.added.add(newEl1);
+ Element next = edit.e.getElement(edit.index + 1);
if (onlyContent)
- newEl1 = createLeafElement(paragraph, next.getAttributes(),
- pos, next.getEndOffset());
+ newEl1 = createLeafElement(edit.e, next.getAttributes(),
+ offset, next.getEndOffset());
else
{
- newEl1 = createLeafElement(paragraph, next.getAttributes(),
- pos, newEndOffset);
- pos = newEndOffset;
+ newEl1 = createLeafElement(edit.e, next.getAttributes(),
+ offset, firstEndOffset);
}
- edit.addAddedElement(newEl1);
- edit.addRemovedElement(current);
- edit.addRemovedElement(next);
+ edit.added.add(newEl1);
+ edit.removed.add(current);
+ edit.removed.add(next);
}
break;
- default:
- if (current.getStartOffset() != pos)
+ default: // OriginateDirection.
+ if (current.getStartOffset() != offset)
{
- Element newEl = createLeafElement(paragraph,
+ Element newEl = createLeafElement(edit.e,
current.getAttributes(),
current.getStartOffset(),
- pos);
- edit.addAddedElement(newEl);
+ offset);
+ edit.added.add(newEl);
}
- edit.addRemovedElement(current);
- Element newEl1 = createLeafElement(paragraph, first.getAttributes(),
- pos, newEndOffset);
- edit.addAddedElement(newEl1);
+ edit.removed.add(current);
+ Element newEl1 = createLeafElement(edit.e, first.getAttributes(),
+ offset, firstEndOffset);
+ edit.added.add(newEl1);
if (current.getEndOffset() != endOffset)
- recreateLeaves(newEndOffset, paragraph, onlyContent);
+ recreateLeafs = true;
else
- offset = newEndOffset;
+ offsetLastIndex = true;
break;
}
}
@@ -981,630 +1298,356 @@ public class DefaultStyledDocument extends AbstractDocument implements
* @param tag -
* the element spec
*/
- private void insertContentTag(ElementSpec tag)
+ private void insertContentTag(ElementSpec tag, Edit edit)
{
- BranchElement paragraph = (BranchElement) elementStack.peek();
int len = tag.getLength();
int dir = tag.getDirection();
- AttributeSet tagAtts = tag.getAttributes();
-
if (dir == ElementSpec.JoinNextDirection)
{
- int index = paragraph.getElementIndex(pos);
- Element target = paragraph.getElement(index);
- Edit edit = getEditForParagraphAndIndex(paragraph, index);
-
- if (paragraph.getStartOffset() > pos)
- {
- Element first = paragraph.getElement(0);
- Element newEl = createLeafElement(paragraph,
- first.getAttributes(), pos,
- first.getEndOffset());
- edit.addAddedElement(newEl);
- edit.addRemovedElement(first);
- }
- else if (paragraph.getElementCount() > (index + 1)
- && (pos == target.getStartOffset() && !target.equals(lastFractured)))
+ if (! edit.isFracture)
{
- Element next = paragraph.getElement(index + 1);
- Element newEl = createLeafElement(paragraph,
- next.getAttributes(), pos,
- next.getEndOffset());
- edit.addAddedElement(newEl);
- edit.addRemovedElement(next);
- edit.addRemovedElement(target);
+ Element first = null;
+ if (insertPath != null)
+ {
+ for (int p = insertPath.length - 1; p >= 0; p--)
+ {
+ if (insertPath[p] == edit)
+ {
+ if (p != insertPath.length - 1)
+ first = edit.e.getElement(edit.index);
+ break;
+ }
+ }
+ }
+ if (first == null)
+ first = edit.e.getElement(edit.index + 1);
+ Element leaf = createLeafElement(edit.e, first.getAttributes(),
+ pos, first.getEndOffset());
+ edit.added.add(leaf);
+ edit.removed.add(first);
}
else
{
- BranchElement parent = (BranchElement) paragraph.getParentElement();
- int i = parent.getElementIndex(pos);
- BranchElement next = (BranchElement) parent.getElement(i + 1);
- AttributeSet atts = tag.getAttributes();
-
- if (next != null)
- {
- Element nextLeaf = next.getElement(0);
- Edit e = getEditForParagraphAndIndex(next, 0);
- Element newEl2 = createLeafElement(next, atts, pos, nextLeaf.getEndOffset());
- e.addAddedElement(newEl2);
- e.addRemovedElement(nextLeaf);
- }
+ Element first = edit.e.getElement(0);
+ Element leaf = createLeafElement(edit.e, first.getAttributes(),
+ pos, first.getEndOffset());
+ edit.added.add(leaf);
+ edit.removed.add(first);
}
}
else
{
- int end = pos + len;
- Element leaf = createLeafElement(paragraph, tag.getAttributes(), pos, end);
-
- // Check for overlap with other leaves/branches
- if (paragraph.getElementCount() > 0)
- {
- int index = paragraph.getElementIndex(pos);
- Element target = paragraph.getElement(index);
- boolean onlyContent = target.isLeaf();
-
- BranchElement toRec = paragraph;
- if (!onlyContent)
- toRec = (BranchElement) target;
-
- // Check if we should place the leaf before or after target
- if (pos > target.getStartOffset())
- index++;
-
- Edit edit = getEditForParagraphAndIndex(paragraph, index);
- edit.addAddedElement(leaf);
- }
- else
- paragraph.replace(0, 0, new Element[] { leaf });
+ Element leaf = createLeafElement(edit.e, tag.getAttributes(), pos,
+ pos + len);
+ edit.added.add(leaf);
}
-
+
pos += len;
+
}
/**
- * This method fractures the child at offset.
+ * This method fractures bottomost leaf in the elementStack. This
+ * happens when the first inserted tag is not content.
*
* @param data
* the ElementSpecs used for the entire insertion
*/
private void createFracture(ElementSpec[] data)
{
- BranchElement paragraph = (BranchElement) elementStack.peek();
- int index = paragraph.getElementIndex(offset);
- Element child = paragraph.getElement(index);
- Edit edit = getEditForParagraphAndIndex(paragraph, index);
- AttributeSet atts = child.getAttributes();
-
+ Edit edit = (Edit) elementStack.peek();
+ Element child = edit.e.getElement(edit.index);
if (offset != 0)
{
- Element newEl1 = createLeafElement(paragraph, atts,
- child.getStartOffset(), offset);
- edit.addAddedElement(newEl1);
- edit.addRemovedElement(child);
- }
- }
-
- /**
- * Recreates a specified part of a the tree after a new leaf
- * has been inserted.
- *
- * @param start - where to start recreating from
- * @param paragraph - the paragraph to recreate
- * @param onlyContent - true if this is the only content
- */
- private void recreateLeaves(int start, BranchElement paragraph, boolean onlyContent)
- {
- int index = paragraph.getElementIndex(start);
- Element child = paragraph.getElement(index);
- AttributeSet atts = child.getAttributes();
-
- if (!onlyContent)
- {
- BranchElement newBranch = (BranchElement) createBranchElement(paragraph,
- atts);
- Element newLeaf = createLeafElement(newBranch, atts, start,
- child.getEndOffset());
- newBranch.replace(0, 0, new Element[] { newLeaf });
-
- BranchElement parent = (BranchElement) paragraph.getParentElement();
- int parSize = parent.getElementCount();
- Edit edit = getEditForParagraphAndIndex(parent, parSize);
- edit.addAddedElement(newBranch);
-
- int paragraphSize = paragraph.getElementCount();
- Element[] removed = new Element[paragraphSize - (index + 1)];
- int s = 0;
- for (int j = index + 1; j < paragraphSize; j++)
- removed[s++] = paragraph.getElement(j);
-
- edit = getEditForParagraphAndIndex(paragraph, index);
- edit.addRemovedElements(removed);
- Element[] added = recreateAfterFracture(removed, newBranch, 0, child.getEndOffset());
- newBranch.replace(1, 0, added);
-
- lastFractured = newLeaf;
- pos = newBranch.getEndOffset();
+ Element newChild = createLeafElement(edit.e, child.getAttributes(),
+ child.getStartOffset(), offset);
+ edit.added.add(newChild);
}
+ edit.removed.add(child);
+ if (child.getEndOffset() != endOffset)
+ recreateLeafs = true;
else
- {
- Element newLeaf = createLeafElement(paragraph, atts, start,
- child.getEndOffset());
- Edit edit = getEditForParagraphAndIndex(paragraph, index);
- edit.addAddedElement(newLeaf);
- }
+ offsetLastIndex = true;
}
-
- /**
- * Splits an element if <code>offset</code> is not already at its
- * boundary.
- *
- * @param el
- * the Element to possibly split
- * @param offset
- * the offset at which to possibly split
- * @param space
- * the amount of space to create between the splitted parts
- * @param editIndex
- * the index of the edit to use
- * @return An array of elements which represent the split result. This array
- * has two elements, the two parts of the split. The first element
- * might be null, which means that the element which should be
- * splitted can remain in place. The second element might also be
- * null, which means that the offset is already at an element
- * boundary and the element doesn't need to be splitted.
- */
- private Element[] split(Element el, int offset, int space, int editIndex)
+
+ private void fracture(int depth)
{
- // If we are at an element boundary, then return an empty array.
- if ((offset == el.getStartOffset() || offset == el.getEndOffset())
- && space == 0 && el.isLeaf())
- return new Element[2];
-
- // If the element is an instance of BranchElement, then we
- // recursivly
- // call this method to perform the split.
- Element[] res = new Element[2];
- if (el instanceof BranchElement)
+ int len = insertPath.length;
+ int lastIndex = -1;
+ boolean recreate = recreateLeafs;
+ Edit lastEdit = insertPath[len - 1];
+ boolean childChanged = lastEdit.index + 1 < lastEdit.e.getElementCount();
+ int deepestChangedIndex = recreate ? len : - 1;
+ int lastChangedIndex = len - 1;
+ createdFracture = true;
+ for (int i = len - 2; i >= 0; i--)
{
- int index = el.getElementIndex(offset);
- Element child = el.getElement(index);
- Element[] result = split(child, offset, space, editIndex);
- Element[] removed;
- Element[] added;
- Element[] newAdded;
-
- int count = el.getElementCount();
- if (result[1] != null)
+ Edit edit = insertPath[i];
+ if (edit.added.size() > 0 || i == depth)
{
- // This is the case when we can keep the first element.
- if (result[0] == null)
- {
- removed = new Element[count - index - 1];
- newAdded = new Element[count - index - 1];
- added = new Element[] {};
-
- }
- // This is the case when we may not keep the first
- // element.
- else
- {
- removed = new Element[count - index];
- newAdded = new Element[count - index];
- added = new Element[] { result[0] };
- }
- newAdded[0] = result[1];
- for (int i = index; i < count; i++)
+ lastIndex = i;
+ if (! recreate && childChanged)
{
- Element el2 = el.getElement(i);
- int ind = i - count + removed.length;
- removed[ind] = el2;
- if (ind != 0)
- newAdded[ind] = el2;
+ recreate = true;
+ if (deepestChangedIndex == -1)
+ deepestChangedIndex = lastChangedIndex + 1;
}
-
- Edit edit = getEditForParagraphAndIndex((BranchElement) el, editIndex);
- edit.addRemovedElements(removed);
- edit.addAddedElements(added);
-
- BranchElement newPar =
- (BranchElement) createBranchElement(el.getParentElement(),
- el.getAttributes());
- newPar.replace(0, 0, newAdded);
- res = new Element[] { null, newPar };
}
- else
+ if (! childChanged && edit.index < edit.e.getElementCount())
{
- removed = new Element[count - index];
- for (int i = index; i < count; ++i)
- removed[i - index] = el.getElement(i);
-
- Edit edit = getEditForParagraphAndIndex((BranchElement) el, editIndex);
- edit.addRemovedElements(removed);
-
- BranchElement newPar = (BranchElement) createBranchElement(el.getParentElement(),
- el.getAttributes());
- newPar.replace(0, 0, removed);
- res = new Element[] { null, newPar };
+ childChanged = true;
+ lastChangedIndex = i;
}
}
- else if (el instanceof LeafElement)
+ if (recreate)
{
- BranchElement par = (BranchElement) el.getParentElement();
- Element el1 = createLeafElement(par, el.getAttributes(),
- el.getStartOffset(), offset);
-
- Element el2 = createLeafElement(par, el.getAttributes(),
- offset + space,
- el.getEndOffset());
- res = new Element[] { el1, el2 };
+ if (lastIndex == -1)
+ lastIndex = len - 1;
+ recreate(lastIndex, deepestChangedIndex);
}
- return res;
}
- /**
- * Inserts a fracture into the document structure.
- *
- * @param tag -
- * the element spec.
- */
- private void insertFracture(ElementSpec tag)
+ private void recreate(int startIndex, int endIndex)
{
- // insert the fracture at offset.
- BranchElement parent = (BranchElement) elementStack.peek();
- int parentIndex = parent.getElementIndex(pos);
- AttributeSet parentAtts = parent.getAttributes();
- Element toFracture = parent.getElement(parentIndex);
- int parSize = parent.getElementCount();
- Edit edit = getEditForParagraphAndIndex(parent, parentIndex);
- Element frac = toFracture;
- int leftIns = 0;
- int indexOfFrac = toFracture.getElementIndex(pos);
- int size = toFracture.getElementCount();
-
- // gets the leaf that falls along the fracture
- frac = toFracture.getElement(indexOfFrac);
- while (!frac.isLeaf())
- frac = frac.getElement(frac.getElementIndex(pos));
-
- AttributeSet atts = frac.getAttributes();
- int fracStart = frac.getStartOffset();
- int fracEnd = frac.getEndOffset();
- if (pos >= fracStart && pos < fracEnd)
+ // Recreate the element representing the inserted index.
+ Edit edit = insertPath[startIndex];
+ Element child;
+ Element newChild;
+ int changeLength = insertPath.length;
+
+ if (startIndex + 1 == changeLength)
+ child = edit.e.getElement(edit.index);
+ else
+ child = edit.e.getElement(edit.index - 1);
+
+ if(child.isLeaf())
+ {
+ newChild = createLeafElement(edit.e, child.getAttributes(),
+ Math.max(endOffset, child.getStartOffset()),
+ child.getEndOffset());
+ }
+ else
{
- // recreate left-side of branch and all its children before offset
- // add the fractured leaves to the right branch
- BranchElement rightBranch =
- (BranchElement) createBranchElement(parent, parentAtts);
-
- // Check if left branch has already been edited. If so, we only
- // need to create the right branch.
- BranchElement leftBranch = null;
- Element[] added = null;
- if (edit.added.size() > 0 || edit.removed.size() > 0)
+ newChild = createBranchElement(edit.e, child.getAttributes());
+ }
+ fracturedParent = edit.e;
+ fracturedChild = newChild;
+
+ // Recreate all the elements to the right of the insertion point.
+ Element parent = newChild;
+ while (++startIndex < endIndex)
+ {
+ boolean isEnd = (startIndex + 1) == endIndex;
+ boolean isEndLeaf = (startIndex + 1) == changeLength;
+
+ // Create the newChild, a duplicate of the elment at
+ // index. This isn't done if isEnd and offsetLastIndex are true
+ // indicating a join previous was done.
+ edit = insertPath[startIndex];
+
+ // Determine the child to duplicate, won't have to duplicate
+ // if at end of fracture, or offseting index.
+ if(isEnd)
{
- added = new Element[] { rightBranch };
-
- // don't try to remove left part of tree
- parentIndex++;
+ if(offsetLastIndex || ! isEndLeaf)
+ child = null;
+ else
+ child = edit.e.getElement(edit.index);
}
else
{
- leftBranch =
- (BranchElement) createBranchElement(parent, parentAtts);
- added = new Element[] { leftBranch, rightBranch };
-
- // add fracture to leftBranch
- if (fracStart != pos)
- {
- Element leftFracturedLeaf =
- createLeafElement(leftBranch, atts, fracStart, pos);
- leftBranch.replace(leftIns, 0,
- new Element[] { leftFracturedLeaf });
- }
+ child = edit.e.getElement(edit.index - 1);
}
- if (!toFracture.isLeaf())
+ // Duplicate it.
+ if(child != null)
{
- // add all non-fracture elements to the branches
- if (indexOfFrac > 0 && leftBranch != null)
+ if(child.isLeaf())
{
- Element[] add = new Element[indexOfFrac];
- for (int i = 0; i < indexOfFrac; i++)
- add[i] = toFracture.getElement(i);
- leftIns = add.length;
- leftBranch.replace(0, 0, add);
+ newChild = createLeafElement(parent, child.getAttributes(),
+ Math.max(endOffset, child.getStartOffset()),
+ child.getEndOffset());
}
-
- int count = size - indexOfFrac - 1;
- if (count > 0)
+ else
{
- Element[] add = new Element[count];
- int j = 0;
- int i = indexOfFrac + 1;
- while (j < count)
- add[j++] = toFracture.getElement(i++);
- rightBranch.replace(0, 0, add);
+ newChild = createBranchElement(parent,
+ child.getAttributes());
}
}
-
- // add to fracture to rightBranch
- // Check if we can join the right frac leaf with the next leaf
- int rm = 0;
- int end = fracEnd;
- Element next = rightBranch.getElement(0);
- if (next != null && next.isLeaf()
- && next.getAttributes().isEqual(atts))
- {
- end = next.getEndOffset();
- rm = 1;
- }
+ else
+ newChild = null;
- Element rightFracturedLeaf = createLeafElement(rightBranch, atts,
- pos, end);
- rightBranch.replace(0, rm, new Element[] { rightFracturedLeaf });
+ // Recreate the remaining children (there may be none).
+ int childrenToMove = edit.e.getElementCount() - edit.index;
+ Element[] children;
+ int moveStartIndex;
+ int childStartIndex = 1;
- // recreate those elements after parentIndex and add/remove all
- // new/old elements to parent
- int remove = parSize - parentIndex;
- Element[] removed = new Element[0];
- Element[] added2 = new Element[0];
- if (remove > 0)
- {
- removed = new Element[remove];
- int s = 0;
- for (int j = parentIndex; j < parSize; j++)
- removed[s++] = parent.getElement(j);
- edit.addRemovedElements(removed);
- added2 = recreateAfterFracture(removed, parent, 1,
- rightBranch.getEndOffset());
+ if (newChild == null)
+ {
+ // Last part of fracture.
+ if (isEndLeaf)
+ {
+ childrenToMove--;
+ moveStartIndex = edit.index + 1;
+ }
+ else
+ {
+ moveStartIndex = edit.index;
+ }
+ childStartIndex = 0;
+ children = new Element[childrenToMove];
+ }
+ else
+ {
+ if (! isEnd)
+ {
+ // Branch.
+ childrenToMove++;
+ moveStartIndex = edit.index;
}
-
- edit.addAddedElements(added);
- edit.addAddedElements(added2);
- elementStack.push(rightBranch);
- lastFractured = rightFracturedLeaf;
+ else
+ {
+ // Last leaf, need to recreate part of it.
+ moveStartIndex = edit.index + 1;
+ }
+ children = new Element[childrenToMove];
+ children[0] = newChild;
}
- else
- fracNotCreated = true;
+
+ for (int c = childStartIndex; c < childrenToMove; c++)
+ {
+ Element toMove = edit.e.getElement(moveStartIndex++);
+ children[c] = recreateFracturedElement(parent, toMove);
+ edit.removed.add(toMove);
+ }
+ ((BranchElement) parent).replace(0, 0, children);
+ parent = newChild;
+ }
+
}
- /**
- * Recreates all the elements from the parent to the element on the top of
- * the stack, starting from startFrom with the starting offset of
- * startOffset.
- *
- * @param recreate -
- * the elements to recreate
- * @param parent -
- * the element to add the new elements to
- * @param startFrom -
- * where to start recreating from
- * @param startOffset -
- * the offset of the first element
- * @return the array of added elements
- */
- private Element[] recreateAfterFracture(Element[] recreate,
- BranchElement parent, int startFrom,
- int startOffset)
+ private Element recreateFracturedElement(Element parent, Element toCopy)
{
- Element[] added = new Element[recreate.length - startFrom];
- int j = 0;
- for (int i = startFrom; i < recreate.length; i++)
+ Element recreated;
+ if(toCopy.isLeaf())
{
- Element curr = recreate[i];
- int len = curr.getEndOffset() - curr.getStartOffset();
- if (curr instanceof LeafElement)
- added[j] = createLeafElement(parent, curr.getAttributes(),
- startOffset, startOffset + len);
- else
+ recreated = createLeafElement(parent, toCopy.getAttributes(),
+ Math.max(toCopy.getStartOffset(), endOffset),
+ toCopy.getEndOffset());
+ }
+ else
+ {
+ Element newParent = createBranchElement(parent,
+ toCopy.getAttributes());
+ int childCount = toCopy.getElementCount();
+ Element[] newChildren = new Element[childCount];
+ for (int i = 0; i < childCount; i++)
{
- BranchElement br =
- (BranchElement) createBranchElement(parent,
- curr.getAttributes());
- int bSize = curr.getElementCount();
- for (int k = 0; k < bSize; k++)
- {
- Element bCurr = curr.getElement(k);
- Element[] add = recreateAfterFracture(new Element[] { bCurr }, br, 0,
- startOffset);
- br.replace(0, 0, add);
-
- }
- added[j] = br;
+ newChildren[i] = recreateFracturedElement(newParent,
+ toCopy.getElement(i));
}
- startOffset += len;
- j++;
+ ((BranchElement) newParent).replace(0, 0, newChildren);
+ recreated = newParent;
}
-
- return added;
- }
- }
-
- /**
- * This method looks through the Vector of Edits to see if there is already an
- * Edit object associated with the given paragraph. If there is, then we
- * return it. Otherwise we create a new Edit object, add it to the vector, and
- * return it. Note: this method is package private to avoid accessors.
- *
- * @param index
- * the index associated with the Edit we want to create
- * @param para
- * the paragraph associated with the Edit we want
- * @return the found or created Edit object
- */
- Edit getEditForParagraphAndIndex(BranchElement para, int index)
- {
- Edit curr;
- int size = edits.size();
- for (int i = 0; i < size; i++)
- {
- curr = (Edit) edits.elementAt(i);
- if (curr.e.equals(para))
- return curr;
- }
- curr = new Edit(para, index, null, null);
- edits.add(curr);
-
- return curr;
- }
- /**
- * Instance of all editing information for an object in the Vector. This class
- * is used to add information to the DocumentEvent associated with an
- * insertion/removal/change as well as to store the changes that need to be
- * made so they can be made all at the same (appropriate) time.
- */
- class Edit
- {
- /** The element to edit . */
- Element e;
-
- /** The index of the change. */
- int index;
-
- /** The removed elements. */
- Vector removed = new Vector();
-
- /** The added elements. */
- Vector added = new Vector();
-
- /**
- * Return an array containing the Elements that have been removed from the
- * paragraph associated with this Edit.
- *
- * @return an array of removed Elements
- */
- public Element[] getRemovedElements()
- {
- int size = removed.size();
- Element[] removedElements = new Element[size];
- for (int i = 0; i < size; i++)
- removedElements[i] = (Element) removed.elementAt(i);
- return removedElements;
+ return recreated;
}
- /**
- * Return an array containing the Elements that have been added to the
- * paragraph associated with this Edit.
- *
- * @return an array of added Elements
- */
- public Element[] getAddedElements()
+ private boolean split(int offs, int len)
{
- int size = added.size();
- Element[] addedElements = new Element[size];
- for (int i = 0; i < size; i++)
- addedElements[i] = (Element) added.elementAt(i);
- return addedElements;
- }
-
- /**
- * Checks if e is already in the vector.
- *
- * @param e - the Element to look for
- * @param v - the vector to search
- * @return true if e is in v.
- */
- private boolean contains(Vector v, Element e)
- {
- if (e == null)
- return false;
-
- int i = v.size();
- for (int j = 0; j < i; j++)
+ boolean splitEnd = false;
+ // Push the path to the stack.
+ Element e = root;
+ int index = e.getElementIndex(offs);
+ while (! e.isLeaf())
{
- Element e1 = (Element) v.get(j);
- if ((e1 != null) && (e1.getAttributes().isEqual(e.getAttributes()))
- && (e1.getName().equals(e.getName()))
- && (e1.getStartOffset() == e.getStartOffset())
- && (e1.getEndOffset() == e.getEndOffset())
- && (e1.getParentElement().equals(e.getParentElement()))
- && (e1.getElementCount() == e.getElementCount()))
- return true;
+ elementStack.push(new Edit(e, index));
+ e = e.getElement(index);
+ index = e.getElementIndex(offs);
}
- return false;
- }
- /**
- * Adds one Element to the vector of removed Elements.
- *
- * @param e
- * the Element to add
- */
- public void addRemovedElement(Element e)
- {
- if (!contains(removed, e))
- removed.add(e);
- }
-
- /**
- * Adds each Element in the given array to the vector of removed Elements
- *
- * @param e
- * the array containing the Elements to be added
- */
- public void addRemovedElements(Element[] e)
- {
- if (e == null || e.length == 0)
- return;
- for (int i = 0; i < e.length; i++)
+ Edit ec = (Edit) elementStack.peek();
+ Element child = ec.e.getElement(ec.index);
+ // Make sure there is something to do. If the
+ // offset is already at a boundary then there is
+ // nothing to do.
+ if (child.getStartOffset() < offs && offs < child.getEndOffset())
{
- if (!contains(removed, e[i]))
- removed.add(e[i]);
- }
- }
+ // We need to split, now see if the other end is within
+ // the same parent.
+ int index0 = ec.index;
+ int index1 = index0;
+ if (((offs + len) < ec.e.getEndOffset()) && (len != 0))
+ {
+ // It's a range split in the same parent.
+ index1 = ec.e.getElementIndex(offs+len);
+ if (index1 == index0)
+ {
+ // It's a three-way split.
+ ec.removed.add(child);
+ e = createLeafElement(ec.e, child.getAttributes(),
+ child.getStartOffset(), offs);
+ ec.added.add(e);
+ e = createLeafElement(ec.e, child.getAttributes(),
+ offs, offs + len);
+ ec.added.add(e);
+ e = createLeafElement(ec.e, child.getAttributes(),
+ offs + len, child.getEndOffset());
+ ec.added.add(e);
+ return true;
+ }
+ else
+ {
+ child = ec.e.getElement(index1);
+ if ((offs + len) == child.getStartOffset())
+ {
+ // End is already on a boundary.
+ index1 = index0;
+ }
+ }
+ splitEnd = true;
+ }
- /**
- * Adds one Element to the vector of added Elements.
- *
- * @param e
- * the Element to add
- */
- public void addAddedElement(Element e)
- {
- if (!contains(added, e))
- added.add(e);
- }
+ // Split the first location.
+ pos = offs;
+ child = ec.e.getElement(index0);
+ ec.removed.add(child);
+ e = createLeafElement(ec.e, child.getAttributes(),
+ child.getStartOffset(), pos);
+ ec.added.add(e);
+ e = createLeafElement(ec.e, child.getAttributes(),
+ pos, child.getEndOffset());
+ ec.added.add(e);
+
+ // Pick up things in the middle.
+ for (int i = index0 + 1; i < index1; i++)
+ {
+ child = ec.e.getElement(i);
+ ec.removed.add(child);
+ ec.added.add(child);
+ }
- /**
- * Adds each Element in the given array to the vector of added Elements.
- *
- * @param e
- * the array containing the Elements to be added
- */
- public void addAddedElements(Element[] e)
- {
- if (e == null || e.length == 0)
- return;
- for (int i = 0; i < e.length; i++)
- {
- if (!contains(added, e[i]))
- added.add(e[i]);
+ if (index1 != index0)
+ {
+ child = ec.e.getElement(index1);
+ pos = offs + len;
+ ec.removed.add(child);
+ e = createLeafElement(ec.e, child.getAttributes(),
+ child.getStartOffset(), pos);
+ ec.added.add(e);
+ e = createLeafElement(ec.e, child.getAttributes(),
+ pos, child.getEndOffset());
+
+ ec.added.add(e);
+ }
}
+ return splitEnd;
+
}
- /**
- * Creates a new Edit object with the given parameters
- *
- * @param e
- * the paragraph Element associated with this Edit
- * @param i
- * the index within the paragraph where changes are started
- * @param removed
- * an array containing Elements that should be removed from the
- * paragraph Element
- * @param added
- * an array containing Elements that should be added to the
- * paragraph Element
- */
- public Edit(Element e, int i, Element[] removed, Element[] added)
- {
- this.e = e;
- this.index = i;
- addRemovedElements(removed);
- addAddedElements(added);
- }
}
+
/**
* An element type for sections. This is a simple BranchElement with a unique
* name.
@@ -1674,11 +1717,6 @@ public class DefaultStyledDocument extends AbstractDocument implements
private StyleChangeListener styleChangeListener;
/**
- * Vector that contains all the edits. Maybe replace by a HashMap.
- */
- Vector edits = new Vector();
-
- /**
* Creates a new <code>DefaultStyledDocument</code>.
*/
public DefaultStyledDocument()
@@ -1939,7 +1977,6 @@ public class DefaultStyledDocument extends AbstractDocument implements
// start and ends at an element end.
buffer.change(offset, length, ev);
- Element root = getDefaultRootElement();
// Visit all paragraph elements within the specified interval
int end = offset + length;
Element curr;
@@ -2079,147 +2116,220 @@ public class DefaultStyledDocument extends AbstractDocument implements
*/
protected void insertUpdate(DefaultDocumentEvent ev, AttributeSet attr)
{
- super.insertUpdate(ev, attr);
- // If the attribute set is null, use an empty attribute set.
+ int offs = ev.getOffset();
+ int len = ev.getLength();
+ int endOffs = offs + len;
if (attr == null)
attr = SimpleAttributeSet.EMPTY;
- int offset = ev.getOffset();
- int length = ev.getLength();
- int endOffset = offset + length;
- AttributeSet paragraphAttributes = getParagraphElement(endOffset).getAttributes();
- Segment txt = new Segment();
+
+ // Paragraph attributes are fetched from the point _after_ the insertion.
+ Element paragraph = getParagraphElement(endOffs);
+ AttributeSet pAttr = paragraph.getAttributes();
+ // Character attributes are fetched from the actual insertion point.
+ Element paragraph2 = getParagraphElement(offs);
+ int contIndex = paragraph2.getElementIndex(offs);
+ Element content = paragraph2.getElement(contIndex);
+ AttributeSet cAttr = content.getAttributes();
+
+ boolean insertAtBoundary = content.getEndOffset() == endOffs;
try
{
- getText(offset, length, txt);
- }
- catch (BadLocationException ex)
- {
- AssertionError ae = new AssertionError("Unexpected bad location");
- ae.initCause(ex);
- throw ae;
- }
+ Segment s = new Segment();
+ ArrayList buf = new ArrayList();
+ ElementSpec lastStartTag = null;
+ boolean insertAfterNewline = false;
+ short lastStartDir = ElementSpec.OriginateDirection;
+
+ // Special handle if we are inserting after a newline.
+ if (offs > 0)
+ {
+ getText(offs - 1, 1, s);
+ if (s.array[s.offset] == '\n')
+ {
+ insertAfterNewline = true;
+ lastStartDir = insertAfterNewline(paragraph, paragraph2,
+ pAttr, buf, offs,
+ endOffs);
+ // Search last start tag.
+ for (int i = buf.size() - 1; i >= 0 && lastStartTag == null;
+ i--)
+ {
+ ElementSpec tag = (ElementSpec) buf.get(i);
+ if (tag.getType() == ElementSpec.StartTagType)
+ {
+ lastStartTag = tag;
+ }
+ }
+ }
- int len = 0;
- Vector specs = new Vector();
- ElementSpec finalStartTag = null;
- short finalStartDirection = ElementSpec.OriginateDirection;
- boolean prevCharWasNewline = false;
- Element prev = getCharacterElement(offset);
- Element next = getCharacterElement(endOffset);
- Element prevParagraph = getParagraphElement(offset);
- Element paragraph = getParagraphElement(endOffset);
+ }
- int segmentEnd = txt.offset + txt.count;
+ // If we are not inserting after a newline, the paragraph attributes
+ // come from the paragraph under the insertion point.
+ if (! insertAfterNewline)
+ pAttr = paragraph2.getAttributes();
- // Check to see if we're inserting immediately after a newline.
- if (offset > 0)
- {
- try
+ // Scan text and build up the specs.
+ getText(offs, len, s);
+ int end = s.offset + s.count;
+ int last = s.offset;
+ for (int i = s.offset; i < end; i++)
{
- String s = getText(offset - 1, 1);
- if (s.equals("\n"))
+ if (s.array[i] == '\n')
{
- finalStartDirection = handleInsertAfterNewline(specs, offset,
- endOffset,
- prevParagraph,
- paragraph,
- paragraphAttributes);
-
- prevCharWasNewline = true;
- // Find the final start tag from the ones just created.
- for (int i = 0; i < specs.size(); i++)
- if (((ElementSpec) specs.get(i)).getType() == ElementSpec.StartTagType)
- finalStartTag = (ElementSpec) specs.get(i);
+ int breakOffs = i + 1;
+ buf.add(new ElementSpec(attr, ElementSpec.ContentType,
+ breakOffs - last));
+ buf.add(new ElementSpec(null, ElementSpec.EndTagType));
+ lastStartTag = new ElementSpec(pAttr,
+ ElementSpec.StartTagType);
+ buf.add(lastStartTag);
+ last = breakOffs;
}
}
- catch (BadLocationException ble)
+
+ // Need to add a tailing content tag if we didn't finish at a boundary.
+ if (last < end)
{
- // This shouldn't happen.
- AssertionError ae = new AssertionError();
- ae.initCause(ble);
- throw ae;
+ buf.add(new ElementSpec(attr, ElementSpec.ContentType,
+ end - last));
}
- }
- for (int i = txt.offset; i < segmentEnd; ++i)
- {
- len++;
- if (txt.array[i] == '\n')
+ // Now we need to fix up the directions of the specs.
+ ElementSpec first = (ElementSpec) buf.get(0);
+ int doclen = getLength();
+
+ // Maybe join-previous the first tag if it is content and has
+ // the same attributes as the previous character run.
+ if (first.getType() == ElementSpec.ContentType && cAttr.isEqual(attr))
+ first.setDirection(ElementSpec.JoinPreviousDirection);
+
+ // Join-fracture or join-next the last start tag if necessary.
+ if (lastStartTag != null)
+ {
+ if (insertAfterNewline)
+ lastStartTag.setDirection(lastStartDir);
+ else if (paragraph2.getEndOffset() != endOffs)
+ lastStartTag.setDirection(ElementSpec.JoinFractureDirection);
+ else
+ {
+ Element par = paragraph2.getParentElement();
+ int par2Index = par.getElementIndex(offs);
+ if (par2Index + 1 < par.getElementCount()
+ && ! par.getElement(par2Index + 1).isLeaf())
+ lastStartTag.setDirection(ElementSpec.JoinNextDirection);
+ }
+ }
+
+ // Join-next last tag if possible.
+ if (insertAtBoundary && endOffs < doclen)
{
- // Add the ElementSpec for the content.
- specs.add(new ElementSpec(attr, ElementSpec.ContentType, len));
-
- // Add ElementSpecs for the newline.
- specs.add(new ElementSpec(null, ElementSpec.EndTagType));
- finalStartTag = new ElementSpec(paragraphAttributes,
- ElementSpec.StartTagType);
- specs.add(finalStartTag);
- len = 0;
+ ElementSpec lastTag = (ElementSpec) buf.get(buf.size() - 1);
+ if (lastTag.getType() == ElementSpec.ContentType
+ && ((lastStartTag == null
+ && (paragraph == paragraph2 || insertAfterNewline))
+ || (lastStartTag != null
+ && lastStartTag.getDirection() != ElementSpec.OriginateDirection)))
+ {
+ int nextIndex = paragraph.getElementIndex(endOffs);
+ Element nextRun = paragraph.getElement(nextIndex);
+ if (nextRun.isLeaf() && attr.isEqual(nextRun.getAttributes()))
+ lastTag.setDirection(ElementSpec.JoinNextDirection);
+ }
+ }
+
+ else if (! insertAtBoundary && lastStartTag != null
+ && lastStartTag.getDirection() == ElementSpec.JoinFractureDirection)
+ {
+ ElementSpec lastTag = (ElementSpec) buf.get(buf.size() - 1);
+ if (lastTag.getType() == ElementSpec.ContentType
+ && lastTag.getDirection() != ElementSpec.JoinPreviousDirection
+ && attr.isEqual(cAttr))
+ {
+ lastTag.setDirection(ElementSpec.JoinNextDirection);
+ }
}
- }
- // Create last element if last character hasn't been a newline.
- if (len > 0)
- specs.add(new ElementSpec(attr, ElementSpec.ContentType, len));
+ ElementSpec[] specs = new ElementSpec[buf.size()];
+ specs = (ElementSpec[]) buf.toArray(specs);
+ buffer.insert(offs, len, specs, ev);
+ }
+ catch (BadLocationException ex)
+ {
+ // Ignore this. Comment out for debugging.
+ ex.printStackTrace();
+ }
+ super.insertUpdate(ev, attr);
+ }
- // Set the direction of the last spec of type StartTagType.
- // If we are inserting after a newline then this value comes from
- // handleInsertAfterNewline.
- if (finalStartTag != null)
+ private short insertAfterNewline(Element par1, Element par2,
+ AttributeSet attr, ArrayList buf,
+ int offs, int endOffs)
+ {
+ short dir = 0;
+ if (par1.getParentElement() == par2.getParentElement())
{
- if (prevCharWasNewline)
- finalStartTag.setDirection(finalStartDirection);
- else if (prevParagraph.getEndOffset() != endOffset)
- finalStartTag.setDirection(ElementSpec.JoinFractureDirection);
+ ElementSpec tag = new ElementSpec(attr, ElementSpec.EndTagType);
+ buf.add(tag);
+ tag = new ElementSpec(attr, ElementSpec.StartTagType);
+ buf.add(tag);
+ if (par2.getEndOffset() != endOffs)
+ dir = ElementSpec.JoinFractureDirection;
else
{
- // If there is an element AFTER this one, then set the
- // direction to JoinNextDirection.
- Element parent = prevParagraph.getParentElement();
- int index = parent.getElementIndex(offset);
- if (index + 1 < parent.getElementCount()
- && !parent.getElement(index + 1).isLeaf())
- finalStartTag.setDirection(ElementSpec.JoinNextDirection);
+ Element par = par2.getParentElement();
+ if (par.getElementIndex(offs) + 1 < par.getElementCount())
+ dir = ElementSpec.JoinNextDirection;
}
}
-
- // If we are at the last index, then check if we could probably be
- // joined with the next element.
- // This means:
- // - we must be a ContentTag
- // - if there is a next Element, we must have the same attributes
- // - if there is no next Element, but one will be created,
- // we must have the same attributes as the higher-level run.
- ElementSpec last = (ElementSpec) specs.lastElement();
- if (last.getType() == ElementSpec.ContentType)
+ else
{
- Element currentRun = prevParagraph.getElement(prevParagraph.getElementIndex(offset));
- if (currentRun.getEndOffset() == endOffset)
+ // For text with more than 2 levels, find the common parent of
+ // par1 and par2.
+ ArrayList parentsLeft = new ArrayList();
+ ArrayList parentsRight = new ArrayList();
+ Element e = par2;
+ while (e != null)
{
- if (endOffset < getLength() && next.getAttributes().isEqual(attr)
- && last.getType() == ElementSpec.ContentType)
- last.setDirection(ElementSpec.JoinNextDirection);
+ parentsLeft.add(e);
+ e = e.getParentElement();
}
- else
+ e = par1;
+ int leftIndex = -1;
+ while (e != null && (leftIndex = parentsLeft.indexOf(e)) == 1)
{
- if (finalStartTag != null
- && finalStartTag.getDirection() == ElementSpec.JoinFractureDirection
- && currentRun.getAttributes().isEqual(attr))
+ parentsRight.add(e);
+ e = e.getParentElement();
+ }
+
+ if (e != null)
+
+ {
+ // e is now the common parent.
+ // Insert the end tags.
+ for (int c = 0; c < leftIndex; c++)
+ {
+ buf.add(new ElementSpec(null, ElementSpec.EndTagType));
+ }
+ // Insert the start tags.
+ for (int c = parentsRight.size() - 1; c >= 0; c--)
{
- last.setDirection(ElementSpec.JoinNextDirection);
+ Element el = (Element) parentsRight.get(c);
+ ElementSpec tag = new ElementSpec(el.getAttributes(),
+ ElementSpec.StartTagType);
+ if (c > 0)
+ tag.setDirection(ElementSpec.JoinNextDirection);
+ buf.add(tag);
}
+ if (parentsRight.size() > 0)
+ dir = ElementSpec.JoinNextDirection;
+ else
+ dir = ElementSpec.JoinFractureDirection;
}
+ else
+ assert false;
}
-
- // If we are at the first new element, then check if it could be
- // joined with the previous element.
- ElementSpec first = (ElementSpec) specs.firstElement();
- if (prev.getAttributes().isEqual(attr)
- && first.getType() == ElementSpec.ContentType)
- first.setDirection(ElementSpec.JoinPreviousDirection);
-
- ElementSpec[] elSpecs = (ElementSpec[]) specs.toArray(new ElementSpec[specs.size()]);
- buffer.insert(offset, length, elSpecs, ev);
+ return dir;
}
/**
@@ -2267,7 +2377,7 @@ public class DefaultStyledDocument extends AbstractDocument implements
*
* @return an enumeration of all style names
*/
- public Enumeration getStyleNames()
+ public Enumeration<?> getStyleNames()
{
StyleContext context = (StyleContext) getAttributeContext();
return context.getStyleNames();
@@ -2322,18 +2432,24 @@ public class DefaultStyledDocument extends AbstractDocument implements
if (length == 0)
return;
- UndoableEdit edit = content.insertString(offset,
- contentBuffer.toString());
+ Content c = getContent();
+ UndoableEdit edit = c.insertString(offset,
+ contentBuffer.toString());
// Create the DocumentEvent with the ElementEdit added
DefaultDocumentEvent ev = new DefaultDocumentEvent(offset,
length,
DocumentEvent.EventType.INSERT);
+
ev.addEdit(edit);
// Finally we must update the document structure and fire the insert
// update event.
buffer.insert(offset, length, data, ev);
+
+ super.insertUpdate(ev, null);
+
+ ev.end();
fireInsertUpdate(ev);
fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
}
@@ -2353,14 +2469,16 @@ public class DefaultStyledDocument extends AbstractDocument implements
*/
protected void create(ElementSpec[] data)
{
- writeLock();
try
{
+
// Clear content if there is some.
int len = getLength();
if (len > 0)
remove(0, len);
+ writeLock();
+
// Now we insert the content.
StringBuilder b = new StringBuilder();
for (int i = 0; i < data.length; ++i)
@@ -2372,38 +2490,18 @@ public class DefaultStyledDocument extends AbstractDocument implements
Content content = getContent();
UndoableEdit cEdit = content.insertString(0, b.toString());
+ len = b.length();
DefaultDocumentEvent ev =
new DefaultDocumentEvent(0, b.length(),
DocumentEvent.EventType.INSERT);
ev.addEdit(cEdit);
- // We do a little trick here to get the new structure: We instantiate
- // a new ElementBuffer with a new root element, insert into that root
- // and then reparent the newly created elements to the old root
- // element.
- BranchElement createRoot =
- (BranchElement) createBranchElement(null, null);
- Element dummyLeaf = createLeafElement(createRoot, null, 0, 1);
- createRoot.replace(0, 0, new Element[]{ dummyLeaf });
- ElementBuffer createBuffer = new ElementBuffer(createRoot);
- createBuffer.insert(0, b.length(), data, new DefaultDocumentEvent(0, b.length(), DocumentEvent.EventType.INSERT));
- // Now the new root is the first child of the createRoot.
- Element newRoot = createRoot.getElement(0);
- BranchElement root = (BranchElement) getDefaultRootElement();
- Element[] added = new Element[newRoot.getElementCount()];
- for (int i = 0; i < added.length; ++i)
- {
- added[i] = newRoot.getElement(i);
- ((AbstractElement) added[i]).element_parent = root;
- }
- Element[] removed = new Element[root.getElementCount()];
- for (int i = 0; i < removed.length; ++i)
- removed[i] = root.getElement(i);
+ buffer.create(len, data, ev);
- // Replace the old elements in root with the new and update the event.
- root.replace(0, removed.length, added);
- ev.addEdit(new ElementEdit(root, 0, removed, added));
+ // For the bidi update.
+ super.insertUpdate(ev, null);
+ ev.end();
fireInsertUpdate(ev);
fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
}
diff --git a/libjava/classpath/javax/swing/text/ElementIterator.java b/libjava/classpath/javax/swing/text/ElementIterator.java
index a6a5ff618bd..112d55e96d0 100644
--- a/libjava/classpath/javax/swing/text/ElementIterator.java
+++ b/libjava/classpath/javax/swing/text/ElementIterator.java
@@ -37,6 +37,8 @@ exception statement from your version. */
package javax.swing.text;
+import java.util.Stack;
+
/**
* This class can be used to iterate over the {@link Element} tree of
* a {@link Document} or an {@link Element}. This iterator performs
@@ -46,20 +48,41 @@ package javax.swing.text;
*/
public class ElementIterator implements Cloneable
{
+ /**
+ * Uses to track the iteration on the stack.
+ */
+ private class ElementRef
+ {
+ /**
+ * The element.
+ */
+ Element element;
+
+ /**
+ * The child index. -1 means the element itself. >= 0 values mean the
+ * n-th child of the element.
+ */
+ int index;
+
+ /**
+ * Creates a new ElementRef.
+ *
+ * @param el the element
+ */
+ ElementRef(Element el)
+ {
+ element = el;
+ index = -1;
+ }
+ }
+
// The root element.
private Element root;
- // The current element.
- private Element currentElement;
- // The depth to which we have descended in the tree.
- private int currentDepth;
-
- // This is at least as big as the current depth, and at index N
- // contains the index of the child element we're currently
- // examining.
- private int[] state;
- // The previous item.
- private Element previousItem;
+ /**
+ * Holds ElementRefs.
+ */
+ private Stack stack;
/**
* Create a new ElementIterator to iterate over the given document.
@@ -67,9 +90,7 @@ public class ElementIterator implements Cloneable
*/
public ElementIterator(Document document)
{
- this.root = document.getDefaultRootElement();
- this.currentElement = root;
- this.state = new int[5];
+ root = document.getDefaultRootElement();
}
/**
@@ -79,8 +100,6 @@ public class ElementIterator implements Cloneable
public ElementIterator(Element root)
{
this.root = root;
- this.currentElement = root;
- this.state = new int[5];
}
/**
@@ -105,7 +124,24 @@ public class ElementIterator implements Cloneable
*/
public Element current()
{
- return currentElement;
+ Element current;
+ if (stack == null)
+ current = first();
+ else
+ {
+ current = null;
+ if (! stack.isEmpty())
+ {
+ ElementRef ref = (ElementRef) stack.peek();
+ Element el = ref.element;
+ int index = ref.index;
+ if (index == -1)
+ current = el;
+ else
+ current = el.getElement(index);
+ }
+ }
+ return current;
}
/**
@@ -113,7 +149,10 @@ public class ElementIterator implements Cloneable
*/
public int depth()
{
- return currentDepth;
+ int depth = 0;
+ if (stack != null)
+ depth = stack.size();
+ return depth;
}
/**
@@ -121,11 +160,15 @@ public class ElementIterator implements Cloneable
*/
public Element first()
{
- // Reset the iterator.
- currentElement = root;
- currentDepth = 0;
- previousItem = null;
- return root;
+ Element first = null;
+ if (root != null)
+ {
+ stack = new Stack();
+ if (root.getElementCount() > 0)
+ stack.push(new ElementRef(root));
+ first = root;
+ }
+ return first;
}
/**
@@ -134,48 +177,96 @@ public class ElementIterator implements Cloneable
*/
public Element next()
{
- previousItem = currentElement;
- if (currentElement == null)
- return null;
- if (! currentElement.isLeaf())
+ Element next;
+ if (stack == null)
+ next = first();
+ else
{
- ++currentDepth;
- if (currentDepth > state.length)
- {
- int[] newState = new int[state.length * 2];
- System.arraycopy(state, 0, newState, 0, state.length);
- state = newState;
- }
- state[currentDepth] = 0;
- currentElement = currentElement.getElement(0);
- return currentElement;
+ next = null;
+ if (! stack.isEmpty())
+ {
+ ElementRef ref = (ElementRef) stack.peek();
+ Element el = ref.element;
+ int index = ref.index;
+ if (el.getElementCount() > index + 1)
+ {
+ Element child = el.getElement(index + 1);
+ if (child.isLeaf())
+ ref.index++;
+ else
+ stack.push(new ElementRef(child));
+ next = child;
+ next = child;
+ }
+ else
+ {
+ stack.pop();
+ if (! stack.isEmpty())
+ {
+ ElementRef top = (ElementRef) stack.peek();
+ top.index++;
+ next = next();
+ }
+ }
+ }
+ // else return null.
}
+ return next;
+ }
- while (currentDepth > 0)
+ /**
+ * Returns the previous item. Does not modify the iterator state.
+ */
+ public Element previous()
+ {
+ Element previous = null;
+ int stackSize;
+ if (stack != null && (stackSize = stack.size()) > 0)
{
- // At a leaf, or done with a non-leaf's children, so go up a
- // level.
- --currentDepth;
- currentElement = currentElement.getParentElement();
- ++state[currentDepth];
- if (state[currentDepth] < currentElement.getElementCount())
- {
- currentElement = currentElement.getElement(state[currentDepth]);
- return currentElement;
- }
+ ElementRef ref = (ElementRef) stack.peek();
+ Element el = ref.element;
+ int index = ref.index;
+ if (index > 0)
+ {
+ previous = deepestLeaf(el.getElement(--index));
+ }
+ else if (index == 0)
+ {
+ previous = el;
+ }
+ else if (index == -1)
+ {
+ ElementRef top = (ElementRef) stack.pop();
+ ElementRef item = (ElementRef) stack.peek();
+ stack.push(top);
+ index = item.index;
+ el = item.element;
+ previous = index == -1 ? el : deepestLeaf(el.getElement(index));
+ }
}
-
- currentElement = null;
- return currentElement;
+ return previous;
}
/**
- * Returns the previous item. Does not modify the iterator state.
+ * Determines and returns the deepest leaf of the element <code>el</code>.
+ *
+ * @param el the base element
+ *
+ * @returnthe deepest leaf of the element <code>el</code>
*/
- public Element previous()
+ private Element deepestLeaf(Element el)
{
- if (currentElement == null || currentElement == root)
- return null;
- return previousItem;
+ Element leaf;
+ if (el.isLeaf())
+ leaf = el;
+ else
+ {
+ int count = el.getElementCount();
+ if (count == 0)
+ leaf = el;
+ else
+ leaf = deepestLeaf(el.getElement(count - 1));
+ }
+ return leaf;
}
}
diff --git a/libjava/classpath/javax/swing/text/FieldView.java b/libjava/classpath/javax/swing/text/FieldView.java
index f41f9013092..0a078e53dca 100644
--- a/libjava/classpath/javax/swing/text/FieldView.java
+++ b/libjava/classpath/javax/swing/text/FieldView.java
@@ -45,8 +45,6 @@ import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Shape;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
import javax.swing.BoundedRangeModel;
import javax.swing.JTextField;
@@ -225,7 +223,7 @@ public class FieldView extends PlainView
public int getResizeWeight(int axis)
{
- return axis = axis == X_AXIS ? 1 : 0;
+ return axis == X_AXIS ? 1 : 0;
}
public Shape modelToView(int pos, Shape a, Position.Bias bias)
diff --git a/libjava/classpath/javax/swing/text/FlowView.java b/libjava/classpath/javax/swing/text/FlowView.java
index 3de95ed7f8d..c2bed399f3a 100644
--- a/libjava/classpath/javax/swing/text/FlowView.java
+++ b/libjava/classpath/javax/swing/text/FlowView.java
@@ -38,6 +38,8 @@ exception statement from your version. */
package javax.swing.text;
+import java.awt.Component;
+import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Shape;
@@ -85,7 +87,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(alloc.x, alloc.y, alloc.width, alloc.height);
+ }
}
/**
@@ -101,7 +113,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(alloc.x, alloc.y, alloc.width, alloc.height);
+ }
}
/**
@@ -117,7 +139,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(alloc.x, alloc.y, alloc.width, alloc.height);
+ }
}
/**
@@ -143,18 +175,35 @@ public abstract class FlowView extends BoxView
*/
public void layout(FlowView fv)
{
+ 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);
+ }
+
+ // Then remove all views from the flow view.
fv.removeAll();
- Element el = fv.getElement();
- int rowStart = el.getStartOffset();
- int end = el.getEndOffset();
- int rowIndex = 0;
- while (rowStart >= 0 && rowStart < end)
+ 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 +228,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();
}
/**
@@ -246,15 +318,11 @@ public abstract class FlowView extends BoxView
int rowIndex)
{
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());
- }
+ int index = logicalView.getViewIndex(startOffset,
+ Position.Bias.Forward);
+ View retVal = logicalView.getView(index);
+ if (retVal.getStartOffset() != startOffset)
+ retVal = retVal.createFragment(startOffset, retVal.getEndOffset());
return retVal;
}
@@ -279,37 +347,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);
}
}
}
@@ -320,14 +433,135 @@ public abstract class FlowView extends BoxView
* visual representation, this is handled by the physical view implemented
* in the <code>FlowView</code>.
*/
- class LogicalView extends BoxView
+ class LogicalView extends CompositeView
{
/**
* Creates a new LogicalView instance.
*/
- LogicalView(Element el, int axis)
+ LogicalView(Element el)
+ {
+ super(el);
+ }
+
+ /**
+ * Overridden to return the attributes of the parent
+ * (== the FlowView instance).
+ */
+ public AttributeSet getAttributes()
+ {
+ View p = getParent();
+ return p != null ? p.getAttributes() : null;
+ }
+
+ protected void childAllocation(int index, Rectangle a)
+ {
+ // Nothing to do here (not visual).
+ }
+
+ protected View getViewAtPoint(int x, int y, Rectangle r)
+ {
+ // Nothing to do here (not visual).
+ return null;
+ }
+
+ protected boolean isAfter(int x, int y, Rectangle r)
+ {
+ // Nothing to do here (not visual).
+ return false;
+ }
+
+ protected boolean isBefore(int x, int y, Rectangle r)
+ {
+ // Nothing to do here (not visual).
+ return false;
+ }
+
+ public float getPreferredSpan(int axis)
+ {
+ float max = 0;
+ float pref = 0;
+ int n = getViewCount();
+ for (int i = 0; i < n; i++)
+ {
+ View v = getView(i);
+ pref += v.getPreferredSpan(axis);
+ if (v.getBreakWeight(axis, 0, Integer.MAX_VALUE)
+ >= ForcedBreakWeight)
+ {
+ max = Math.max(max, pref);
+ pref = 0;
+ }
+ }
+ max = Math.max(max, pref);
+ return max;
+ }
+
+ public float getMinimumSpan(int axis)
+ {
+ float max = 0;
+ float min = 0;
+ boolean wrap = true;
+ int n = getViewCount();
+ for (int i = 0; i < n; i++)
+ {
+ View v = getView(i);
+ if (v.getBreakWeight(axis, 0, Integer.MAX_VALUE)
+ == BadBreakWeight)
+ {
+ min += v.getPreferredSpan(axis);
+ wrap = false;
+ }
+ else if (! wrap)
+ {
+ max = Math.max(min, max);
+ wrap = true;
+ min = 0;
+ }
+ }
+ max = Math.max(max, min);
+ return max;
+ }
+
+ public void paint(Graphics g, Shape s)
+ {
+ // Nothing to do here (not visual).
+ }
+
+ /**
+ * Overridden to handle possible leaf elements.
+ */
+ protected void loadChildren(ViewFactory f)
{
- super(el, axis);
+ Element el = getElement();
+ if (el.isLeaf())
+ {
+ View v = new LabelView(el);
+ append(v);
+ }
+ else
+ super.loadChildren(f);
+ }
+
+ /**
+ * Overridden to reparent the children to this logical view, in case
+ * they have been parented by a row.
+ */
+ protected void forwardUpdateToView(View v, DocumentEvent e, Shape a,
+ ViewFactory f)
+ {
+ v.setParent(this);
+ super.forwardUpdateToView(v, e, a, f);
+ }
+
+ /**
+ * Overridden to handle possible leaf element.
+ */
+ protected int getViewIndexAtPosition(int pos)
+ {
+ int index = 0;
+ if (! getElement().isLeaf())
+ index = super.getViewIndexAtPosition(pos);
+ return index;
}
}
@@ -357,11 +591,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>.
*
@@ -374,7 +603,7 @@ public abstract class FlowView extends BoxView
{
super(element, axis);
strategy = sharedStrategy;
- layoutDirty = true;
+ layoutSpan = Short.MAX_VALUE;
}
/**
@@ -423,7 +652,7 @@ public abstract class FlowView extends BoxView
*/
public int getFlowStart(int index)
{
- return getLeftInset(); // TODO: Is this correct?
+ return 0;
}
/**
@@ -449,9 +678,11 @@ public abstract class FlowView extends BoxView
{
if (layoutPool == null)
{
- layoutPool = new LogicalView(getElement(), getAxis());
- layoutPool.setParent(this);
+ layoutPool = new LogicalView(getElement());
}
+ layoutPool.setParent(this);
+ // Initialize the flow strategy.
+ strategy.insertUpdate(this, null, null);
}
/**
@@ -466,32 +697,33 @@ public abstract class FlowView extends BoxView
protected void layout(int width, int height)
{
int flowAxis = getFlowAxis();
+ int span;
if (flowAxis == X_AXIS)
- {
- if (layoutSpan != width)
- {
- layoutChanged(Y_AXIS);
- layoutSpan = width;
- }
- }
+ span = (int) width;
else
+ span = (int) height;
+
+ if (layoutSpan != span)
{
- if (layoutSpan != height)
- {
- layoutChanged(X_AXIS);
- layoutSpan = height;
- }
+ layoutChanged(flowAxis);
+ layoutChanged(getAxis());
+ layoutSpan = span;
}
- 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);
}
@@ -510,7 +742,6 @@ public abstract class FlowView extends BoxView
// be updated accordingly.
layoutPool.insertUpdate(changes, a, vf);
strategy.insertUpdate(this, changes, getInsideAllocation(a));
- layoutDirty = true;
}
/**
@@ -526,7 +757,6 @@ public abstract class FlowView extends BoxView
{
layoutPool.removeUpdate(changes, a, vf);
strategy.removeUpdate(this, changes, getInsideAllocation(a));
- layoutDirty = true;
}
/**
@@ -542,7 +772,6 @@ public abstract class FlowView extends BoxView
{
layoutPool.changedUpdate(changes, a, vf);
strategy.changedUpdate(this, changes, getInsideAllocation(a));
- layoutDirty = true;
}
/**
@@ -604,7 +833,7 @@ public abstract class FlowView extends BoxView
res = new SizeRequirements();
res.minimum = (int) layoutPool.getMinimumSpan(axis);
res.preferred = Math.max(res.minimum,
- (int) layoutPool.getMinimumSpan(axis));
+ (int) layoutPool.getPreferredSpan(axis));
res.maximum = Integer.MAX_VALUE;
res.alignment = 0.5F;
return res;
diff --git a/libjava/classpath/javax/swing/text/GapContent.java b/libjava/classpath/javax/swing/text/GapContent.java
index 760e396a223..08a318d8bb4 100644
--- a/libjava/classpath/javax/swing/text/GapContent.java
+++ b/libjava/classpath/javax/swing/text/GapContent.java
@@ -39,16 +39,13 @@ exception statement from your version. */
package javax.swing.text;
import java.io.Serializable;
-import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
-import java.util.Set;
import java.util.Vector;
-import java.util.WeakHashMap;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
@@ -71,7 +68,7 @@ public class GapContent
/**
* A {@link Position} implementation for <code>GapContent</code>.
*/
- private class GapContentPosition
+ class GapContentPosition
implements Position
{
@@ -82,39 +79,6 @@ public class GapContent
Mark mark;
/**
- * Creates a new GapContentPosition object.
- *
- * @param offset the offset of this Position
- */
- GapContentPosition(int offset)
- {
- // Try to find the mark in the positionMarks array, and store the index
- // to it.
- synchronized (GapContent.this)
- {
- // Try to make space.
- garbageCollect();
- Mark m = new Mark(offset);
- int i = search(marks, m);
- if (i >= 0) // mark found
- {
- m = (Mark) marks.get(i);
- }
- else
- {
- i = -i - 1;
- marks.add(i, m);
- }
- m.refCount++;
- mark = m;
- }
-
- // Register this position in the death queue, so we can cleanup the marks
- // when this position object gets GC'ed.
- new WeakReference(this, queueOfDeath);
- }
-
- /**
* Returns the current offset of this Position within the content.
*
* @return the current offset of this Position within the content.
@@ -133,7 +97,7 @@ public class GapContent
* be garbage collected while we still hold a reference to the Mark object.
*/
private class Mark
- implements Comparable
+ extends WeakReference
{
/**
* The actual mark into the buffer.
@@ -141,21 +105,20 @@ public class GapContent
int mark;
/**
- * The number of GapContentPosition object that reference this mark. If
- * it reaches zero, it get's deleted by {@link GapContent#garbageCollect()}.
- */
- int refCount;
-
- /**
* Creates a new Mark object for the specified offset.
*
* @param offset the offset
*/
Mark(int offset)
{
+ super(null);
+ mark = offset;
+ }
+
+ Mark(int offset, GapContentPosition pos, ReferenceQueue queue)
+ {
+ super(pos, queue);
mark = offset;
- if (mark >= gapStart && mark != 0)
- mark += (gapEnd - gapStart);
}
/**
@@ -165,33 +128,62 @@ public class GapContent
*/
int getOffset()
{
- assert mark == 0 || mark < gapStart || mark >= gapEnd :
- "Invalid mark: " + mark + ", gapStart: " + gapStart
- + ", gapEnd: " + gapEnd;
-
int res = mark;
- if (mark >= gapEnd)
+ if (mark >= gapStart)
res -= (gapEnd - gapStart);
- return res;
+ return Math.max(0, res);
+ }
+
+ /**
+ * Returns the GapContentPosition that is associated ith this mark.
+ * This fetches the weakly referenced position object.
+ *
+ * @return the GapContentPosition that is associated ith this mark
+ */
+ GapContentPosition getPosition()
+ {
+ return (GapContentPosition) get();
}
+ }
+
+ /**
+ * Stores a reference to a mark that can be resetted to the original value
+ * after a mark has been moved. This is used for undoing actions.
+ */
+ private class UndoPosRef
+ {
+ /**
+ * The mark that might need to be reset.
+ */
+ private Mark mark;
+
/**
- * Implementation of Comparable.
+ * The original offset to reset the mark to.
*/
- public int compareTo(Object o)
+ private int undoOffset;
+
+ /**
+ * Creates a new UndoPosRef.
+ *
+ * @param m the mark
+ */
+ UndoPosRef(Mark m)
{
- Mark other = (Mark) o;
- return mark - other.mark;
+ mark = m;
+ undoOffset = mark.getOffset();
}
+
/**
- * Adjustment for equals().
+ * Resets the position of the mark to the value that it had when
+ * creating this UndoPosRef.
*/
- public boolean equals(Object o)
+ void reset()
{
- if (o == null || !(o instanceof Mark))
- return false;
+ if (undoOffset <= gapStart)
+ mark.mark = undoOffset;
else
- return ((Mark) o).mark == mark;
+ mark.mark = (gapEnd - gapStart) + undoOffset;
}
}
@@ -199,6 +191,8 @@ public class GapContent
{
public int where, length;
String text;
+ private Vector positions;
+
public InsertUndo(int start, int len)
{
where = start;
@@ -209,27 +203,33 @@ public class GapContent
{
super.undo();
try
- {
- text = getString(where, length);
- remove(where, length);
- }
+ {
+ positions = getPositionsInRange(null, where, length);
+ text = getString(where, length);
+ remove(where, length);
+ }
catch (BadLocationException ble)
- {
- throw new CannotUndoException();
- }
+ {
+ throw new CannotUndoException();
+ }
}
public void redo () throws CannotUndoException
{
super.redo();
try
- {
- insertString(where, text);
- }
+ {
+ insertString(where, text);
+ if (positions != null)
+ {
+ updateUndoPositions(positions, where, length);
+ positions = null;
+ }
+ }
catch (BadLocationException ble)
- {
- throw new CannotRedoException();
- }
+ {
+ throw new CannotRedoException();
+ }
}
}
@@ -238,10 +238,17 @@ public class GapContent
{
public int where;
String text;
+
+ /**
+ * The positions in the removed range.
+ */
+ private Vector positions;
+
public UndoRemove(int start, String removedText)
{
where = start;
text = removedText;
+ positions = getPositionsInRange(null, start, removedText.length());
}
public void undo () throws CannotUndoException
@@ -250,6 +257,8 @@ public class GapContent
try
{
insertString(where, text);
+ if (positions != null)
+ updateUndoPositions(positions, where, text.length());
}
catch (BadLocationException ble)
{
@@ -261,13 +270,15 @@ public class GapContent
{
super.redo();
try
- {
- remove(where, text.length());
- }
+ {
+ text = getString(where, text.length());
+ positions = getPositionsInRange(null, where, text.length());
+ remove(where, text.length());
+ }
catch (BadLocationException ble)
- {
- throw new CannotRedoException();
- }
+ {
+ throw new CannotRedoException();
+ }
}
}
@@ -308,7 +319,15 @@ public class GapContent
*/
ArrayList marks;
- WeakHashMap positions;
+ /**
+ * The number of unused marks.
+ */
+ private int garbageMarks;
+
+ /**
+ * A 'static' mark that is used for searching.
+ */
+ private Mark searchMark = new Mark(0);
/**
* Queues all references to GapContentPositions that are about to be
@@ -339,7 +358,6 @@ public class GapContent
gapStart = 1;
gapEnd = size;
buffer[0] = '\n';
- positions = new WeakHashMap();
marks = new ArrayList();
queueOfDeath = new ReferenceQueue();
}
@@ -403,9 +421,10 @@ public class GapContent
throw new BadLocationException("The where argument cannot be greater"
+ " than the content length", where);
+ InsertUndo undo = new InsertUndo(where, strLen);
replace(where, 0, str.toCharArray(), strLen);
- return new InsertUndo(where, strLen);
+ return undo;
}
/**
@@ -429,9 +448,10 @@ public class GapContent
+ " than the content length", where + nitems);
String removedText = getString(where, nitems);
+ UndoRemove undoRemove = new UndoRemove(where, removedText);
replace(where, nitems, null, 0);
- return new UndoRemove(where, removedText);
+ return undoRemove;
}
/**
@@ -495,29 +515,43 @@ public class GapContent
if (len < 0)
throw new BadLocationException("negative length not allowed: ", len);
- // check if requested segment is contiguous
- if ((where < gapStart) && ((gapStart - where) < len))
- {
- // requested segment is not contiguous -> copy the pieces together
- char[] copy = new char[len];
- int lenFirst = gapStart - where; // the length of the first segment
- System.arraycopy(buffer, where, copy, 0, lenFirst);
- System.arraycopy(buffer, gapEnd, copy, lenFirst, len - lenFirst);
- txt.array = copy;
- txt.offset = 0;
- txt.count = len;
- }
- else
- {
- // requested segment is contiguous -> we can simply return the
- // actual content
- txt.array = buffer;
- if (where < gapStart)
+ // Optimized to copy only when really needed.
+ if (where + len <= gapStart)
+ {
+ // Simple case: completely before gap.
+ txt.array = buffer;
txt.offset = where;
- else
- txt.offset = where + (gapEnd - gapStart);
- txt.count = len;
- }
+ txt.count = len;
+ }
+ else if (where > gapStart)
+ {
+ // Completely after gap, adjust offset.
+ txt.array = buffer;
+ txt.offset = gapEnd + where - gapStart;
+ txt.count = len;
+ }
+ else
+ {
+ // Spans the gap.
+ int beforeGap = gapStart - where;
+ if (txt.isPartialReturn())
+ {
+ // Return the part before the gap when partial return is allowed.
+ txt.array = buffer;
+ txt.offset = where;
+ txt.count = beforeGap;
+ }
+ else
+ {
+ // Copy pieces together otherwise.
+ txt.array = new char[len];
+ txt.offset = 0;
+ System.arraycopy(buffer, where, txt.array, 0, beforeGap);
+ System.arraycopy(buffer, gapEnd, txt.array, beforeGap,
+ len - beforeGap);
+ txt.count = len;
+ }
+ }
}
/**
@@ -537,27 +571,33 @@ public class GapContent
// and luckily enough the GapContent can very well deal with offsets
// outside the buffer bounds. So I removed that check.
+ // First do some garbage collections.
+ while (queueOfDeath.poll() != null)
+ garbageMarks++;
+ if (garbageMarks > Math.max(5, marks.size() / 10))
+ garbageCollect();
+
// We try to find a GapContentPosition at the specified offset and return
// that. Otherwise we must create a new one.
- GapContentPosition pos = null;
- Set positionSet = positions.keySet();
- for (Iterator i = positionSet.iterator(); i.hasNext();)
- {
- GapContentPosition p = (GapContentPosition) i.next();
- if (p.getOffset() == offset)
- {
- pos = p;
- break;
- }
- }
-
- // If none was found, then create and return a new one.
- if (pos == null)
+ Mark m;
+ GapContentPosition pos;
+ int index = offset;
+ if (offset >= gapStart)
+ index += (gapEnd - gapStart);
+ searchMark.mark = index;
+ int insertIndex = search(searchMark);
+ if (!(insertIndex < marks.size()
+ && (m = (Mark) marks.get(insertIndex)).mark == index
+ && (pos = m.getPosition()) != null))
{
- pos = new GapContentPosition(offset);
- positions.put(pos, null);
+ // Create new position if none was found.
+ pos = new GapContentPosition();
+ m = new Mark(index, pos, queueOfDeath);
+ pos.mark = m;
+ marks.add(insertIndex, m);
}
-
+ // Otherwise use the found position.
+
return pos;
}
@@ -574,18 +614,29 @@ public class GapContent
assert newSize > (gapEnd - gapStart) : "The new gap size must be greater "
+ "than the old gap size";
- int delta = newSize - gapEnd + gapStart;
- // Update the marks after the gapEnd.
- adjustPositionsInRange(gapEnd, -1, delta);
+ int oldEnd = getGapEnd();
+ int oldSize = getArrayLength();
+ int upper = oldSize - oldEnd;
+ int size = (newSize + 1) * 2;
+ int newEnd = size - upper;
// Copy the data around.
- char[] newBuf = (char[]) allocateArray(length() + newSize);
- System.arraycopy(buffer, 0, newBuf, 0, gapStart);
- System.arraycopy(buffer, gapEnd, newBuf, gapStart + newSize, buffer.length
- - gapEnd);
- gapEnd = gapStart + newSize;
+ char[] newBuf = (char[]) allocateArray(size);
+ System.arraycopy(buffer, 0, newBuf, 0, Math.min(size, oldSize));
buffer = newBuf;
-
+ gapEnd = newEnd;
+ if (upper != 0)
+ System.arraycopy(buffer, oldEnd, buffer, newEnd, upper);
+
+ // Adjust marks.
+ int delta = gapEnd - oldEnd;
+ int adjIndex = searchFirst(oldEnd);
+ int count = marks.size();
+ for (int i = adjIndex; i < count; i++)
+ {
+ Mark m = (Mark) marks.get(i);
+ m.mark += delta;
+ }
}
/**
@@ -595,28 +646,44 @@ public class GapContent
*/
protected void shiftGap(int newGapStart)
{
- if (newGapStart == gapStart)
- return;
- int newGapEnd = newGapStart + gapEnd - gapStart;
- if (newGapStart < gapStart)
+ int oldStart = gapStart;
+ int delta = newGapStart - oldStart;
+ int oldEnd = gapEnd;
+ int newGapEnd = oldEnd + delta;
+ int size = oldEnd - oldStart;
+
+ // Shift gap in array.
+ gapStart = newGapStart;
+ gapEnd = newGapEnd;
+ if (delta > 0)
+ System.arraycopy(buffer, oldEnd, buffer, oldStart, delta);
+ else
+ System.arraycopy(buffer, newGapStart, buffer, newGapEnd, -delta);
+
+ // Adjust marks.
+ if (delta > 0)
{
- // Update the positions between newGapStart and (old) gapStart. The marks
- // must be shifted by (gapEnd - gapStart).
- adjustPositionsInRange(newGapStart, gapStart, gapEnd - gapStart);
- System.arraycopy(buffer, newGapStart, buffer, newGapEnd, gapStart
- - newGapStart);
- gapStart = newGapStart;
- gapEnd = newGapEnd;
+ int adjIndex = searchFirst(oldStart);
+ int count = marks.size();
+ for (int i = adjIndex; i < count; i++)
+ {
+ Mark m = (Mark) marks.get(i);
+ if (m.mark >= newGapEnd)
+ break;
+ m.mark -= size;
+ }
}
- else
+ else if (delta < 0)
{
- // Update the positions between newGapEnd and (old) gapEnd. The marks
- // must be shifted by (gapEnd - gapStart).
- adjustPositionsInRange(gapEnd, newGapEnd, -(gapEnd - gapStart));
- System.arraycopy(buffer, gapEnd, buffer, gapStart, newGapStart
- - gapStart);
- gapStart = newGapStart;
- gapEnd = newGapEnd;
+ int adjIndex = searchFirst(newGapStart);
+ int count = marks.size();
+ for (int i = adjIndex; i < count; i++)
+ {
+ Mark m = (Mark) marks.get(i);
+ if (m.mark >= oldEnd)
+ break;
+ m.mark += size;
+ }
}
resetMarksAtZero();
}
@@ -636,7 +703,18 @@ public class GapContent
assert newGapStart < gapStart : "The new gap start must be less than the "
+ "old gap start.";
- setPositionsInRange(newGapStart, gapStart, false);
+
+ // Adjust positions.
+ int adjIndex = searchFirst(newGapStart);
+ int count = marks.size();
+ for (int i = adjIndex; i < count; i++)
+ {
+ Mark m = (Mark) marks.get(i);
+ if (m.mark > gapStart)
+ break;
+ m.mark = gapEnd;
+ }
+
gapStart = newGapStart;
resetMarksAtZero();
}
@@ -656,7 +734,19 @@ public class GapContent
assert newGapEnd > gapEnd : "The new gap end must be greater than the "
+ "old gap end.";
- setPositionsInRange(gapEnd, newGapEnd, false);
+
+ // Adjust marks.
+ int adjIndex = searchFirst(gapEnd);
+ int count = marks.size();
+ for (int i = adjIndex; i < count; i++)
+ {
+ Mark m = (Mark) marks.get(i);
+ if (m.mark >= newGapEnd)
+ break;
+ m.mark = newGapEnd;
+ }
+
+
gapEnd = newGapEnd;
resetMarksAtZero();
}
@@ -682,23 +772,88 @@ public class GapContent
protected void replace(int position, int rmSize, Object addItems,
int addSize)
{
- if (gapStart != position)
- shiftGap(position);
-
- // Remove content
- if (rmSize > 0)
- shiftGapEndUp(gapEnd + rmSize);
+ if (addSize == 0)
+ {
+ removeImpl(position, rmSize);
+ return;
+ }
+ else if (rmSize > addSize)
+ {
+ removeImpl(position + addSize, rmSize - addSize);
+ }
+ else
+ {
+ int endSize = addSize - rmSize;
+ int end = addImpl(position + rmSize, endSize);
+ System.arraycopy(addItems, rmSize, buffer, end, endSize);
+ addSize = rmSize;
+ }
+ System.arraycopy(addItems, 0, buffer, position, addSize);
+ }
+
+ /**
+ * Adjusts the positions and gap in response to a remove operation.
+ *
+ * @param pos the position at which to remove
+ * @param num the number of removed items
+ */
+ private void removeImpl(int pos, int num)
+ {
+ if (num > 0)
+ {
+ int end = pos + num;
+ int newGapSize = (gapEnd - gapStart) + num;
+ if (end <= gapStart)
+ {
+ if (gapStart != end)
+ {
+ shiftGap(end);
+ }
+ shiftGapStartDown(gapStart - num);
+ }
+ else if (pos >= gapStart)
+ {
+ if (gapStart != pos)
+ {
+ shiftGap(pos);
+ }
+ shiftGapEndUp(gapStart + newGapSize);
+ }
+ else
+ {
+ shiftGapStartDown(pos);
+ shiftGapEndUp(gapStart + newGapSize);
+ }
+ }
+ }
- // If gap is too small, enlarge the gap.
- if ((gapEnd - gapStart) <= addSize)
- shiftEnd((addSize - gapEnd + gapStart + 1) * 2 + gapEnd + DEFAULT_BUFSIZE);
+ /**
+ * Adjusts the positions and gap in response to an add operation.
+ *
+ * @param pos the position at which to add
+ * @param num the number of added items
+ *
+ * @return the adjusted position
+ */
+ private int addImpl(int pos, int num)
+ {
+ int size = gapEnd - gapStart;
+ if (num == 0)
+ {
+ if (pos > gapStart)
+ pos += size;
+ return pos;
+ }
- // Add new items to the buffer.
- if (addItems != null)
+ shiftGap(pos);
+ if (num >= size)
{
- System.arraycopy(addItems, 0, buffer, gapStart, addSize);
- gapStart += addSize;
+ shiftEnd(getArrayLength() - size + num);
+ size = gapEnd - gapStart;
}
+
+ gapStart += num;
+ return pos;
}
/**
@@ -733,97 +888,34 @@ public class GapContent
*/
protected Vector getPositionsInRange(Vector v, int offset, int length)
{
- Vector res = v;
- if (res == null)
- res = new Vector();
- else
- res.clear();
-
- int endOffs = offset + length;
-
- Set positionSet = positions.keySet();
- for (Iterator i = positionSet.iterator(); i.hasNext();)
+ int end = offset + length;
+ int startIndex;
+ int endIndex;
+ if (offset < gapStart)
{
- GapContentPosition p = (GapContentPosition) i.next();
- int offs = p.getOffset();
- if (offs >= offset && offs < endOffs)
- res.add(p);
+ if (offset == 0)
+ startIndex = 0;
+ else
+ startIndex = searchFirst(offset);
+ if (end >= gapStart)
+ endIndex = searchFirst(end + (gapEnd - gapStart) + 1);
+ else
+ endIndex = searchFirst(end + 1);
}
-
- return res;
- }
-
- /**
- * Crunches all positions in the specified range to either the start or
- * end of that interval. The interval boundaries are meant to be inclusive
- * [start, end].
- *
- * @param start the start offset of the range
- * @param end the end offset of the range
- * @param toStart a boolean indicating if the positions should be crunched
- * to the start (true) or to the end (false)
- */
- private void setPositionsInRange(int start, int end, boolean toStart)
- {
- synchronized (this)
+ else
{
- // Find the start and end indices in the positionMarks array.
- Mark m = new Mark(0); // For comparison / search only.
- m.mark = start;
- int startIndex = search(marks, m);
- if (startIndex < 0) // Translate to insertion index, if not found.
- startIndex = - startIndex - 1;
- m.mark = end;
- int endIndex = search(marks, m);
- if (endIndex < 0) // Translate to insertion index - 1, if not found.
- endIndex = - endIndex - 2;
-
- // Actually adjust the marks.
- for (int i = startIndex; i <= endIndex; i++)
- ((Mark) marks.get(i)).mark = toStart ? start : end;
+ startIndex = searchFirst(offset + (gapEnd - gapStart));
+ endIndex = searchFirst(end + (gapEnd - gapStart) + 1);
}
-
- }
-
- /**
- * Adjusts the mark of all <code>Position</code>s that are in the range
- * specified by <code>offset</code> and </code>length</code> within
- * the buffer array by <code>increment</code>
- *
- * @param startOffs the start offset of the range to search
- * @param endOffs the length of the range to search, -1 means all to the end
- * @param incr the increment
- */
- private void adjustPositionsInRange(int startOffs, int endOffs, int incr)
- {
- synchronized (this)
+ if (v == null)
+ v = new Vector();
+ for (int i = startIndex; i < endIndex; i++)
{
- // Find the start and end indices in the positionMarks array.
- Mark m = new Mark(0); // For comparison / search only.
-
- m.mark = startOffs;
- int startIndex = search(marks, m);
- if (startIndex < 0) // Translate to insertion index, if not found.
- startIndex = - startIndex - 1;
-
- m.mark = endOffs;
- int endIndex;
- if (endOffs == -1)
- endIndex = marks.size() - 1;
- else
- {
- endIndex = search(marks, m);
- if (endIndex < 0) // Translate to insertion index - 1, if not found.
- endIndex = - endIndex - 2;
- }
- // Actually adjust the marks.
- for (int i = startIndex; i <= endIndex; i++) {
- ((Mark) marks.get(i)).mark += incr;
- }
+ v.add(new UndoPosRef((Mark) marks.get(i)));
}
-
+ return v;
}
-
+
/**
* Resets all <code>Position</code> that have an offset of <code>0</code>,
* to also have an array index of <code>0</code>. This might be necessary
@@ -844,14 +936,26 @@ public class GapContent
}
/**
- * @specnote This method is not very well specified and the positions vector
- * is implementation specific. The undo positions are managed
- * differently in this implementation, this method is only here
- * for binary compatibility.
+ * Resets the positions in the specified range to their original offset
+ * after a undo operation is performed. For example, after removing some
+ * content, the positions in the removed range will all be set to one
+ * offset. This method restores the positions to their original offsets
+ * after an undo.
+ *
+ * @param positions the positions to update
+ * @param offset
+ * @param length
*/
protected void updateUndoPositions(Vector positions, int offset, int length)
{
- // We do nothing here.
+ for (Iterator i = positions.iterator(); i.hasNext();)
+ {
+ UndoPosRef undoPosRef = (UndoPosRef) i.next();
+ undoPosRef.reset();
+ }
+
+ // Resort marks.
+ Collections.sort(marks);
}
/**
@@ -892,30 +996,6 @@ public class GapContent
}
/**
- * Polls the queue of death for GapContentPositions, updates the
- * corresponding reference count and removes the corresponding mark
- * if the refcount reaches zero.
- *
- * This is package private to avoid accessor synthetic methods.
- */
- void garbageCollect()
- {
- Reference ref = queueOfDeath.poll();
- while (ref != null)
- {
- if (ref != null)
- {
- GapContentPosition pos = (GapContentPosition) ref.get();
- Mark m = pos.mark;
- m.refCount--;
- if (m.refCount == 0)
- marks.remove(m);
- }
- ref = queueOfDeath.poll();
- }
- }
-
- /**
* Searches the first occurance of object <code>o</code> in list
* <code>l</code>. This performs a binary search by calling
* {@link Collections#binarySearch(List, Object)} and when an object has been
@@ -923,22 +1003,93 @@ public class GapContent
* list. The meaning of the return value is the same as in
* <code>Collections.binarySearch()</code>.
*
- * @param l the list to search through
* @param o the object to be searched
*
* @return the index of the first occurance of o in l, or -i + 1 if not found
*/
- private int search(List l, Object o)
+ int search(Mark o)
{
- int i = Collections.binarySearch(l, o);
- while (i > 0)
+ int foundInd = 0;
+ boolean found = false;
+ int low = 0;
+ int up = marks.size() - 1;
+ int mid = 0;
+ if (up > -1)
{
- Object o2 = l.get(i - 1);
- if (o2.equals(o))
- i--;
+ int cmp = 0;
+ Mark last = (Mark) marks.get(up);
+ cmp = compare(o, last);
+ if (cmp > 0)
+ {
+ foundInd = up + 1;
+ found = true;
+ }
else
+ {
+ while (low <= up && ! found)
+ {
+ mid = low + (up - low) / 2;
+ Mark m = (Mark) marks.get(mid);
+ cmp = compare(o, m);
+ if (cmp == 0)
+ {
+ foundInd = mid;
+ found = true;
+ }
+ else if (cmp < 0)
+ up = mid - 1;
+ else
+ low = mid + 1;
+ }
+
+ if (! found)
+ foundInd = cmp < 0 ? mid : mid + 1;
+ }
+ }
+ return foundInd;
+ }
+
+ private int searchFirst(int index)
+ {
+ searchMark.mark = Math.max(index, 1);
+ int i = search(searchMark);
+ for (int j = i - 1; j >= 0; j--)
+ {
+ Mark m = (Mark) marks.get(j);
+ if (m.mark != index)
break;
+ i--;
}
return i;
}
+
+ /**
+ * Compares two marks.
+ *
+ * @param m1 the first mark
+ * @param m2 the second mark
+ *
+ * @return negative when m1 < m2, positive when m1 > m2 and 0 when equal
+ */
+ private int compare(Mark m1, Mark m2)
+ {
+ return m1.mark - m2.mark;
+ }
+
+ /**
+ * Collects and frees unused marks.
+ */
+ private void garbageCollect()
+ {
+ int count = marks.size();
+ ArrayList clean = new ArrayList();
+ for (int i = 0; i < count; i++)
+ {
+ Mark m = (Mark) marks.get(i);
+ if (m.get() != null)
+ clean.add(m);
+ }
+ marks = clean;
+ garbageMarks = 0;
+ }
}
diff --git a/libjava/classpath/javax/swing/text/GlyphView.java b/libjava/classpath/javax/swing/text/GlyphView.java
index d505274c91f..1e418d2e06a 100644
--- a/libjava/classpath/javax/swing/text/GlyphView.java
+++ b/libjava/classpath/javax/swing/text/GlyphView.java
@@ -38,14 +38,21 @@ exception statement from your version. */
package javax.swing.text;
+import gnu.classpath.SystemProperties;
+
import java.awt.Color;
+import java.awt.Container;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
+import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Toolkit;
-import java.text.BreakIterator;
+import java.awt.font.FontRenderContext;
+import java.awt.font.TextHitInfo;
+import java.awt.font.TextLayout;
+import java.awt.geom.Rectangle2D;
import javax.swing.SwingConstants;
import javax.swing.event.DocumentEvent;
@@ -248,10 +255,164 @@ public class GlyphView extends View implements TabableView, Cloneable
}
/**
+ * A GlyphPainter implementation based on TextLayout. This should give
+ * better performance in Java2D environments.
+ */
+ private static class J2DGlyphPainter
+ extends GlyphPainter
+ {
+
+ /**
+ * The text layout.
+ */
+ TextLayout textLayout;
+
+ /**
+ * Creates a new J2DGlyphPainter.
+ *
+ * @param str the string
+ * @param font the font
+ * @param frc the font render context
+ */
+ J2DGlyphPainter(String str, Font font, FontRenderContext frc)
+ {
+ textLayout = new TextLayout(str, font, frc);
+ }
+
+ /**
+ * Returns null so that GlyphView.checkPainter() creates a new instance.
+ */
+ public GlyphPainter getPainter(GlyphView v, int p0, int p1)
+ {
+ return null;
+ }
+
+ /**
+ * Delegates to the text layout.
+ */
+ public float getAscent(GlyphView v)
+ {
+ return textLayout.getAscent();
+ }
+
+ /**
+ * Delegates to the text layout.
+ */
+ public int getBoundedPosition(GlyphView v, int p0, float x, float len)
+ {
+ int pos;
+ TextHitInfo hit = textLayout.hitTestChar(len, 0);
+ if (hit.getCharIndex() == -1 && ! textLayout.isLeftToRight())
+ pos = v.getEndOffset();
+ else
+ {
+ pos = hit.isLeadingEdge() ? hit.getInsertionIndex()
+ : hit.getInsertionIndex() - 1;
+ pos += v.getStartOffset();
+ }
+ return pos;
+ }
+
+ /**
+ * Delegates to the text layout.
+ */
+ public float getDescent(GlyphView v)
+ {
+ return textLayout.getDescent();
+ }
+
+ /**
+ * Delegates to the text layout.
+ */
+ public float getHeight(GlyphView view)
+ {
+ return textLayout.getAscent() + textLayout.getDescent()
+ + textLayout.getLeading();
+ }
+
+ /**
+ * Delegates to the text layout.
+ */
+ public float getSpan(GlyphView v, int p0, int p1, TabExpander te, float x)
+ {
+ float span;
+ if (p0 == v.getStartOffset() && p1 == v.getEndOffset())
+ span = textLayout.getAdvance();
+ else
+ {
+ int start = v.getStartOffset();
+ int i0 = p0 - start;
+ int i1 = p1 - start;
+ TextHitInfo hit0 = TextHitInfo.afterOffset(i0);
+ TextHitInfo hit1 = TextHitInfo.afterOffset(i1);
+ float x0 = textLayout.getCaretInfo(hit0)[0];
+ float x1 = textLayout.getCaretInfo(hit1)[0];
+ span = Math.abs(x1 - x0);
+ }
+ return span;
+ }
+
+ /**
+ * Delegates to the text layout.
+ */
+ public Shape modelToView(GlyphView v, int pos, Bias b, Shape a)
+ throws BadLocationException
+ {
+ int offs = pos - v.getStartOffset();
+ // Create copy here to protect original shape.
+ Rectangle2D bounds = a.getBounds2D();
+ TextHitInfo hit =
+ b == Position.Bias.Forward ? TextHitInfo.afterOffset(offs)
+ : TextHitInfo.beforeOffset(offs);
+ float[] loc = textLayout.getCaretInfo(hit);
+ bounds.setRect(bounds.getX() + loc[0], bounds.getY(), 1,
+ bounds.getHeight());
+ return bounds;
+ }
+
+ /**
+ * Delegates to the text layout.
+ */
+ public void paint(GlyphView view, Graphics g, Shape a, int p0, int p1)
+ {
+ // Can't paint this with plain graphics.
+ if (g instanceof Graphics2D)
+ {
+ Graphics2D g2d = (Graphics2D) g;
+ Rectangle2D b = a instanceof Rectangle2D ? (Rectangle2D) a
+ : a.getBounds2D();
+ float x = (float) b.getX();
+ float y = (float) b.getY() + textLayout.getAscent()
+ + textLayout.getLeading();
+ // TODO: Try if clipping makes things faster for narrow views.
+ textLayout.draw(g2d, x, y);
+ }
+ }
+
+ /**
+ * Delegates to the text layout.
+ */
+ public int viewToModel(GlyphView v, float x, float y, Shape a,
+ Bias[] biasRet)
+ {
+ Rectangle2D bounds = a instanceof Rectangle2D ? (Rectangle2D) a
+ : a.getBounds2D();
+ TextHitInfo hit = textLayout.hitTestChar(x - (float) bounds.getX(), 0);
+ int pos = hit.getInsertionIndex();
+ biasRet[0] = hit.isLeadingEdge() ? Position.Bias.Forward
+ : Position.Bias.Backward;
+ return pos + v.getStartOffset();
+ }
+
+ }
+
+ /**
* The default <code>GlyphPainter</code> used in <code>GlyphView</code>.
*/
static class DefaultGlyphPainter extends GlyphPainter
{
+ FontMetrics fontMetrics;
+
/**
* Returns the full height of the rendered text.
*
@@ -259,9 +420,8 @@ public class GlyphView extends View implements TabableView, Cloneable
*/
public float getHeight(GlyphView view)
{
- Font font = view.getFont();
- FontMetrics metrics = Toolkit.getDefaultToolkit().getFontMetrics(font);
- float height = metrics.getHeight();
+ updateFontMetrics(view);
+ float height = fontMetrics.getHeight();
return height;
}
@@ -277,53 +437,27 @@ public class GlyphView extends View implements TabableView, Cloneable
public void paint(GlyphView view, Graphics g, Shape a, int p0,
int p1)
{
- Color oldColor = g.getColor();
- int height = (int) getHeight(view);
+ updateFontMetrics(view);
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ TabExpander tabEx = view.getTabExpander();
Segment txt = view.getText(p0, p1);
- Rectangle bounds = a.getBounds();
- TabExpander tabEx = null;
- View parent = view.getParent();
- if (parent instanceof TabExpander)
- tabEx = (TabExpander) parent;
-
- int width = Utilities.getTabbedTextWidth(txt, g.getFontMetrics(),
- bounds.x, tabEx, txt.offset);
- // Fill the background of the text run.
- Color background = view.getBackground();
- if (background != null)
- {
- g.setColor(background);
- g.fillRect(bounds.x, bounds.y, width, height);
- }
- // Draw the actual text.
- g.setColor(view.getForeground());
- g.setFont(view.getFont());
- int ascent = g.getFontMetrics().getAscent();
- if (view.isSuperscript())
- // TODO: Adjust font for superscripting.
- Utilities.drawTabbedText(txt, bounds.x, bounds.y + ascent - height / 2,
- g, tabEx, txt.offset);
- else if (view.isSubscript())
- // TODO: Adjust font for subscripting.
- Utilities.drawTabbedText(txt, bounds.x, bounds.y + ascent + height / 2,
- g, tabEx, txt.offset);
- else
- Utilities.drawTabbedText(txt, bounds.x, bounds.y + ascent, g, tabEx,
- txt.offset);
- if (view.isStrikeThrough())
- {
- int strikeHeight = (int) (getAscent(view) / 2);
- g.drawLine(bounds.x, bounds.y + strikeHeight, bounds.height + width,
- bounds.y + strikeHeight);
- }
- if (view.isUnderline())
+ // Find out the X location at which we have to paint.
+ int x = r.x;
+ int p = view.getStartOffset();
+ if (p != p0)
{
- int lineHeight = (int) getAscent(view);
- g.drawLine(bounds.x, bounds.y + lineHeight, bounds.height + width,
- bounds.y + lineHeight);
+ int width = Utilities.getTabbedTextWidth(txt, fontMetrics,x, tabEx,
+ p);
+ x += width;
}
- g.setColor(oldColor);
+ // Find out Y location.
+ int y = r.y + fontMetrics.getHeight() - fontMetrics.getDescent();
+
+ // Render the thing.
+ g.setFont(fontMetrics.getFont());
+ Utilities.drawTabbedText(txt, x, y, g, tabEx, p0);
+
}
/**
@@ -350,15 +484,18 @@ public class GlyphView extends View implements TabableView, Cloneable
Shape a)
throws BadLocationException
{
+ updateFontMetrics(view);
Element el = view.getElement();
- Font font = view.getFont();
- FontMetrics fm = view.getContainer().getFontMetrics(font);
Segment txt = view.getText(el.getStartOffset(), pos);
- int width = fm.charsWidth(txt.array, txt.offset, txt.count);
- int height = fm.getHeight();
- Rectangle bounds = a.getBounds();
+ Rectangle bounds = a instanceof Rectangle ? (Rectangle) a
+ : a.getBounds();
+ TabExpander expander = view.getTabExpander();
+ int width = Utilities.getTabbedTextWidth(txt, fontMetrics, bounds.x,
+ expander,
+ view.getStartOffset());
+ int height = fontMetrics.getHeight();
Rectangle result = new Rectangle(bounds.x + width, bounds.y,
- bounds.x + width, height);
+ 0, height);
return result;
}
@@ -381,11 +518,10 @@ public class GlyphView extends View implements TabableView, Cloneable
public float getSpan(GlyphView view, int p0, int p1,
TabExpander te, float x)
{
- Element el = view.getElement();
- Font font = view.getFont();
- FontMetrics fm = Toolkit.getDefaultToolkit().getFontMetrics(font);
+ updateFontMetrics(view);
Segment txt = view.getText(p0, p1);
- int span = Utilities.getTabbedTextWidth(txt, fm, (int) x, te, p0);
+ int span = Utilities.getTabbedTextWidth(txt, fontMetrics, (int) x, te,
+ p0);
return span;
}
@@ -402,9 +538,8 @@ public class GlyphView extends View implements TabableView, Cloneable
*/
public float getAscent(GlyphView v)
{
- Font font = v.getFont();
- FontMetrics fm = v.getContainer().getFontMetrics(font);
- return fm.getAscent();
+ updateFontMetrics(v);
+ return fontMetrics.getAscent();
}
/**
@@ -420,9 +555,8 @@ public class GlyphView extends View implements TabableView, Cloneable
*/
public float getDescent(GlyphView v)
{
- Font font = v.getFont();
- FontMetrics fm = v.getContainer().getFontMetrics(font);
- return fm.getDescent();
+ updateFontMetrics(v);
+ return fontMetrics.getDescent();
}
/**
@@ -437,13 +571,12 @@ public class GlyphView extends View implements TabableView, Cloneable
*/
public int getBoundedPosition(GlyphView v, int p0, float x, float len)
{
+ updateFontMetrics(v);
TabExpander te = v.getTabExpander();
Segment txt = v.getText(p0, v.getEndOffset());
- Font font = v.getFont();
- FontMetrics fm = v.getContainer().getFontMetrics(font);
- int pos = Utilities.getTabbedTextOffset(txt, fm, (int) x,
+ int pos = Utilities.getTabbedTextOffset(txt, fontMetrics, (int) x,
(int) (x + len), te, p0, false);
- return pos;
+ return pos + p0;
}
/**
@@ -460,9 +593,33 @@ public class GlyphView extends View implements TabableView, Cloneable
public int viewToModel(GlyphView v, float x, float y, Shape a,
Bias[] biasRet)
{
- Rectangle b = a.getBounds();
- int pos = getBoundedPosition(v, v.getStartOffset(), b.x, x - b.x);
- return pos;
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ int p0 = v.getStartOffset();
+ int p1 = v.getEndOffset();
+ TabExpander te = v.getTabExpander();
+ Segment s = v.getText(p0, p1);
+ int offset = Utilities.getTabbedTextOffset(s, fontMetrics, r.x, (int) x,
+ te, p0);
+ int ret = p0 + offset;
+ if (ret == p1)
+ ret--;
+ biasRet[0] = Position.Bias.Forward;
+ return ret;
+ }
+
+ private void updateFontMetrics(GlyphView v)
+ {
+ Font font = v.getFont();
+ if (fontMetrics == null || ! font.equals(fontMetrics.getFont()))
+ {
+ Container c = v.getContainer();
+ FontMetrics fm;
+ if (c != null)
+ fm = c.getFontMetrics(font);
+ else
+ fm = Toolkit.getDefaultToolkit().getFontMetrics(font);
+ fontMetrics = fm;
+ }
}
}
@@ -474,12 +631,22 @@ 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;
+
+ /**
+ * The x location against which the tab expansion is done.
+ */
+ private float tabX;
+
+ /**
+ * The tab expander that is used in this view.
+ */
+ private TabExpander tabExpander;
/**
* Creates a new <code>GlyphView</code> for the given <code>Element</code>.
@@ -489,8 +656,8 @@ public class GlyphView extends View implements TabableView, Cloneable
public GlyphView(Element element)
{
super(element);
- startOffset = -1;
- endOffset = -1;
+ offset = 0;
+ length = 0;
}
/**
@@ -524,7 +691,21 @@ public class GlyphView extends View implements TabableView, Cloneable
protected void checkPainter()
{
if (glyphPainter == null)
- glyphPainter = new DefaultGlyphPainter();
+ {
+ if ("true".equals(
+ SystemProperties.getProperty("gnu.javax.swing.noGraphics2D")))
+ {
+ glyphPainter = new DefaultGlyphPainter();
+ }
+ else
+ {
+ Segment s = getText(getStartOffset(), getEndOffset());
+ glyphPainter = new J2DGlyphPainter(s.toString(), getFont(),
+ new FontRenderContext(null,
+ false,
+ false));
+ }
+ }
}
/**
@@ -536,9 +717,80 @@ public class GlyphView extends View implements TabableView, Cloneable
*/
public void paint(Graphics g, Shape a)
{
- Element el = getElement();
checkPainter();
- getGlyphPainter().paint(this, g, a, getStartOffset(), getEndOffset());
+ int p0 = getStartOffset();
+ int p1 = getEndOffset();
+
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ Container c = getContainer();
+
+ Color fg = getForeground();
+ JTextComponent tc = null;
+ if (c instanceof JTextComponent)
+ {
+ tc = (JTextComponent) c;
+ if (! tc.isEnabled())
+ fg = tc.getDisabledTextColor();
+ }
+ Color bg = getBackground();
+ if (bg != null)
+ {
+ g.setColor(bg);
+ System.err.println("fill background: " + bg);
+ g.fillRect(r.x, r.y, r.width, r.height);
+ }
+
+
+ // Paint layered highlights if there are any.
+ if (tc != null)
+ {
+ Highlighter h = tc.getHighlighter();
+ if (h instanceof LayeredHighlighter)
+ {
+ LayeredHighlighter lh = (LayeredHighlighter) h;
+ lh.paintLayeredHighlights(g, p0, p1, a, tc, this);
+ }
+ }
+
+ g.setColor(fg);
+ glyphPainter.paint(this, g, a, p0, p1);
+ boolean underline = isUnderline();
+ boolean striked = isStrikeThrough();
+ if (underline || striked)
+ {
+ View parent = getParent();
+ // X coordinate.
+ if (parent != null && parent.getEndOffset() == p1)
+ {
+ // Strip whitespace.
+ Segment s = getText(p0, p1);
+ while (s.count > 0 && Character.isWhitespace(s.array[s.count - 1]))
+ {
+ p1--;
+ s.count--;
+ }
+ }
+ int x0 = r.x;
+ int p = getStartOffset();
+ TabExpander tabEx = getTabExpander();
+ if (p != p0)
+ x0 += (int) glyphPainter.getSpan(this, p, p0, tabEx, x0);
+ int x1 = x0 + (int) glyphPainter.getSpan(this, p0, p1, tabEx, x0);
+ // Y coordinate.
+ int y = r.y + r.height - (int) glyphPainter.getDescent(this);
+ if (underline)
+ {
+ int yTmp = y;
+ yTmp += 1;
+ g.drawLine(x0, yTmp, x1, yTmp);
+ }
+ if (striked)
+ {
+ int yTmp = y;
+ yTmp -= (int) glyphPainter.getAscent(this);
+ g.drawLine(x0, yTmp, x1, yTmp);
+ }
+ }
}
@@ -555,19 +807,24 @@ public class GlyphView extends View implements TabableView, Cloneable
float span = 0;
checkPainter();
GlyphPainter painter = getGlyphPainter();
- if (axis == X_AXIS)
+ switch (axis)
{
- Element el = getElement();
+ case X_AXIS:
TabExpander tabEx = null;
View parent = getParent();
if (parent instanceof TabExpander)
tabEx = (TabExpander) parent;
span = painter.getSpan(this, getStartOffset(), getEndOffset(),
tabEx, 0.F);
+ break;
+ case Y_AXIS:
+ span = painter.getHeight(this);
+ if (isSuperscript())
+ span += span / 3;
+ break;
+ default:
+ throw new IllegalArgumentException("Illegal axis");
}
- else
- span = painter.getHeight(this);
-
return span;
}
@@ -623,13 +880,7 @@ public class GlyphView extends View implements TabableView, Cloneable
*/
public TabExpander getTabExpander()
{
- TabExpander te = null;
- View parent = getParent();
-
- if (parent instanceof TabExpander)
- te = (TabExpander) parent;
-
- return te;
+ return tabExpander;
}
/**
@@ -642,9 +893,17 @@ public class GlyphView extends View implements TabableView, Cloneable
*/
public float getTabbedSpan(float x, TabExpander te)
{
- Element el = getElement();
- return getGlyphPainter().getSpan(this, el.getStartOffset(),
- el.getEndOffset(), te, x);
+ checkPainter();
+ TabExpander old = tabExpander;
+ tabExpander = te;
+ if (tabExpander != old)
+ {
+ // Changing the tab expander will lead to a relayout in the X_AXIS.
+ preferenceChanged(null, true, false);
+ }
+ tabX = x;
+ return getGlyphPainter().getSpan(this, getStartOffset(),
+ getEndOffset(), tabExpander, x);
}
/**
@@ -658,23 +917,8 @@ public class GlyphView extends View implements TabableView, Cloneable
*/
public float getPartialSpan(int p0, int p1)
{
- Element el = getElement();
- Document doc = el.getDocument();
- Segment seg = new Segment();
- try
- {
- doc.getText(p0, p1 - p0, seg);
- }
- catch (BadLocationException ex)
- {
- AssertionError ae;
- ae = new AssertionError("BadLocationException must not be thrown "
- + "here");
- ae.initCause(ex);
- throw ae;
- }
- FontMetrics fm = null; // Fetch font metrics somewhere.
- return Utilities.getTabbedTextWidth(seg, fm, 0, null, p0);
+ checkPainter();
+ return glyphPainter.getSpan(this, p0, p1, tabExpander, tabX);
}
/**
@@ -686,10 +930,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;
}
/**
@@ -701,12 +946,17 @@ 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;
}
+ private Segment cached = new Segment();
+
/**
* Returns the text segment that this view is responsible for.
*
@@ -717,10 +967,9 @@ public class GlyphView extends View implements TabableView, Cloneable
*/
public Segment getText(int p0, int p1)
{
- Segment txt = new Segment();
try
{
- getDocument().getText(p0, p1 - p0, txt);
+ getDocument().getText(p0, p1 - p0, cached);
}
catch (BadLocationException ex)
{
@@ -731,7 +980,7 @@ public class GlyphView extends View implements TabableView, Cloneable
throw ae;
}
- return txt;
+ return cached;
}
/**
@@ -743,16 +992,19 @@ public class GlyphView extends View implements TabableView, Cloneable
*/
public Font getFont()
{
- Element el = getElement();
- AttributeSet atts = el.getAttributes();
- String family = StyleConstants.getFontFamily(atts);
- int size = StyleConstants.getFontSize(atts);
- int style = Font.PLAIN;
- if (StyleConstants.isBold(atts))
- style |= Font.BOLD;
- if (StyleConstants.isItalic(atts))
- style |= Font.ITALIC;
- Font font = new Font(family, style, size);
+ Document doc = getDocument();
+ Font font = null;
+ if (doc instanceof StyledDocument)
+ {
+ StyledDocument styledDoc = (StyledDocument) doc;
+ font = styledDoc.getFont(getAttributes());
+ }
+ else
+ {
+ Container c = getContainer();
+ if (c != null)
+ font = c.getFont();
+ }
return font;
}
@@ -885,33 +1137,21 @@ public class GlyphView extends View implements TabableView, Cloneable
*/
public View breakView(int axis, int p0, float pos, float len)
{
- if (axis == Y_AXIS)
- return this;
-
- checkPainter();
- GlyphPainter painter = getGlyphPainter();
-
- // Try to find a suitable line break.
- BreakIterator lineBreaker = BreakIterator.getLineInstance();
- Segment txt = new Segment();
- try
- {
- int start = getStartOffset();
- int length = getEndOffset() - start;
- getDocument().getText(start, length, txt);
- }
- catch (BadLocationException ex)
+ View brokenView = this;
+ if (axis == X_AXIS)
{
- AssertionError err = new AssertionError("BadLocationException must not "
- + "be thrown here.");
- err.initCause(ex);
- throw err;
+ checkPainter();
+ int end = glyphPainter.getBoundedPosition(this, p0, pos, len);
+ int breakLoc = getBreakLocation(p0, end);
+ if (breakLoc != -1)
+ end = breakLoc;
+ if (p0 != getStartOffset() || end != getEndOffset())
+ {
+ brokenView = createFragment(p0, end);
+ if (brokenView instanceof GlyphView)
+ ((GlyphView) brokenView).tabX = pos;
+ }
}
- int breakLocation =
- Utilities.getBreakLocation(txt, getContainer().getFontMetrics(getFont()),
- (int) pos, (int) (pos + len),
- getTabExpander(), p0);
- View brokenView = createFragment(p0, breakLocation);
return brokenView;
}
@@ -937,28 +1177,36 @@ public class GlyphView extends View implements TabableView, Cloneable
weight = super.getBreakWeight(axis, pos, len);
else
{
- // FIXME: Commented out because the Utilities.getBreakLocation method
- // is still buggy. The GoodBreakWeight is a reasonable workaround for
- // now.
-// int startOffset = getStartOffset();
-// int endOffset = getEndOffset() - 1;
-// Segment s = getText(startOffset, endOffset);
-// Container c = getContainer();
-// FontMetrics fm = c.getFontMetrics(c.getFont());
-// int x0 = (int) pos;
-// int x = (int) (pos + len);
-// int breakLoc = Utilities.getBreakLocation(s, fm, x0, x,
-// getTabExpander(),
-// startOffset);
-// if (breakLoc == startOffset || breakLoc == endOffset)
-// weight = GoodBreakWeight;
-// else
-// weight = ExcellentBreakWeight;
- weight = GoodBreakWeight;
+ checkPainter();
+ int start = getStartOffset();
+ int end = glyphPainter.getBoundedPosition(this, start, pos, len);
+ if (end == 0)
+ weight = BadBreakWeight;
+ else
+ {
+ if (getBreakLocation(start, end) != -1)
+ weight = ExcellentBreakWeight;
+ else
+ weight = GoodBreakWeight;
+ }
}
return weight;
}
+ private int getBreakLocation(int start, int end)
+ {
+ int loc = -1;
+ Segment s = getText(start, end);
+ for (char c = s.last(); c != Segment.DONE && loc == -1; c = s.previous())
+ {
+ if (Character.isWhitespace(c))
+ {
+ loc = s.getIndex() - s.getBeginIndex() + 1 + start;
+ }
+ }
+ return loc;
+ }
+
/**
* Receives notification that some text attributes have changed within the
* text fragment that this view is responsible for. This calls
@@ -971,7 +1219,7 @@ public class GlyphView extends View implements TabableView, Cloneable
*/
public void changedUpdate(DocumentEvent e, Shape a, ViewFactory vf)
{
- preferenceChanged(this, true, true);
+ preferenceChanged(null, true, true);
}
/**
@@ -986,7 +1234,7 @@ public class GlyphView extends View implements TabableView, Cloneable
*/
public void insertUpdate(DocumentEvent e, Shape a, ViewFactory vf)
{
- preferenceChanged(this, true, false);
+ preferenceChanged(null, true, false);
}
/**
@@ -1001,7 +1249,7 @@ public class GlyphView extends View implements TabableView, Cloneable
*/
public void removeUpdate(DocumentEvent e, Shape a, ViewFactory vf)
{
- preferenceChanged(this, true, false);
+ preferenceChanged(null, true, false);
}
/**
@@ -1015,11 +1263,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;
}
@@ -1031,14 +1280,21 @@ public class GlyphView extends View implements TabableView, Cloneable
*/
public float getAlignment(int axis)
{
+ checkPainter();
float align;
if (axis == Y_AXIS)
{
- checkPainter();
GlyphPainter painter = getGlyphPainter();
float height = painter.getHeight(this);
float descent = painter.getDescent(this);
- align = (height - descent) / height;
+ float ascent = painter.getAscent(this);
+ if (isSuperscript())
+ align = 1.0F;
+ else if (isSubscript())
+ align = height > 0 ? (height - (descent + (ascent / 2))) / height
+ : 0;
+ else
+ align = height > 0 ? (height - descent) / height : 0;
}
else
align = super.getAlignment(axis);
diff --git a/libjava/classpath/javax/swing/text/InternationalFormatter.java b/libjava/classpath/javax/swing/text/InternationalFormatter.java
index 8db435c18f3..d6f2359e6f7 100644
--- a/libjava/classpath/javax/swing/text/InternationalFormatter.java
+++ b/libjava/classpath/javax/swing/text/InternationalFormatter.java
@@ -285,7 +285,7 @@ public class InternationalFormatter
if (minimum != null && minimum.compareTo(o) > 0)
throw new ParseException("The value may not be less than the"
+ " specified minimum", 0);
- if (maximum != null && minimum.compareTo(o) < 0)
+ if (maximum != null && maximum.compareTo(o) < 0)
throw new ParseException("The value may not be greater than the"
+ " specified maximum", 0);
return o;
diff --git a/libjava/classpath/javax/swing/text/JTextComponent.java b/libjava/classpath/javax/swing/text/JTextComponent.java
index 6da84bfe7d8..68ba1f4284c 100644
--- a/libjava/classpath/javax/swing/text/JTextComponent.java
+++ b/libjava/classpath/javax/swing/text/JTextComponent.java
@@ -38,8 +38,6 @@ exception statement from your version. */
package javax.swing.text;
-import gnu.classpath.NotImplementedException;
-
import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.Container;
@@ -47,6 +45,7 @@ import java.awt.Dimension;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
+import java.awt.Shape;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
@@ -59,6 +58,7 @@ import java.awt.event.MouseEvent;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
+import java.text.BreakIterator;
import java.util.Enumeration;
import java.util.Hashtable;
@@ -67,6 +67,7 @@ import javax.accessibility.AccessibleAction;
import javax.accessibility.AccessibleContext;
import javax.accessibility.AccessibleEditableText;
import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleState;
import javax.accessibility.AccessibleStateSet;
import javax.accessibility.AccessibleText;
import javax.swing.Action;
@@ -105,12 +106,7 @@ public abstract class JTextComponent extends JComponent
/**
* The caret's offset.
*/
- int dot = 0;
-
- /**
- * The current JTextComponent.
- */
- JTextComponent textComp = JTextComponent.this;
+ private int caretDot;
/**
* Construct an AccessibleJTextComponent.
@@ -118,7 +114,8 @@ public abstract class JTextComponent extends JComponent
public AccessibleJTextComponent()
{
super();
- textComp.addCaretListener(this);
+ JTextComponent.this.addCaretListener(this);
+ caretDot = getCaretPosition();
}
/**
@@ -129,8 +126,7 @@ public abstract class JTextComponent extends JComponent
*/
public int getCaretPosition()
{
- dot = textComp.getCaretPosition();
- return dot;
+ return JTextComponent.this.getCaretPosition();
}
/**
@@ -141,7 +137,7 @@ public abstract class JTextComponent extends JComponent
*/
public String getSelectedText()
{
- return textComp.getSelectedText();
+ return JTextComponent.this.getSelectedText();
}
/**
@@ -156,9 +152,10 @@ public abstract class JTextComponent extends JComponent
*/
public int getSelectionStart()
{
- if (getSelectedText() == null || (textComp.getText().equals("")))
+ if (getSelectedText() == null
+ || (JTextComponent.this.getText().equals("")))
return 0;
- return textComp.getSelectionStart();
+ return JTextComponent.this.getSelectionStart();
}
/**
@@ -173,9 +170,7 @@ public abstract class JTextComponent extends JComponent
*/
public int getSelectionEnd()
{
- if (getSelectedText() == null || (textComp.getText().equals("")))
- return 0;
- return textComp.getSelectionEnd();
+ return JTextComponent.this.getSelectionEnd();
}
/**
@@ -185,10 +180,20 @@ public abstract class JTextComponent extends JComponent
* @param e - the caret update event
*/
public void caretUpdate(CaretEvent e)
- throws NotImplementedException
{
- // TODO: fire appropriate event.
- dot = e.getDot();
+ int dot = e.getDot();
+ int mark = e.getMark();
+ if (caretDot != dot)
+ {
+ firePropertyChange(ACCESSIBLE_CARET_PROPERTY, new Integer(caretDot),
+ new Integer(dot));
+ caretDot = dot;
+ }
+ if (mark != dot)
+ {
+ firePropertyChange(ACCESSIBLE_SELECTION_PROPERTY, null,
+ getSelectedText());
+ }
}
/**
@@ -197,10 +202,10 @@ public abstract class JTextComponent extends JComponent
* @return the accessible state set of this component
*/
public AccessibleStateSet getAccessibleStateSet()
- throws NotImplementedException
{
AccessibleStateSet state = super.getAccessibleStateSet();
- // TODO: Figure out what state must be added here to the super's state.
+ if (isEditable())
+ state.add(AccessibleState.EDITABLE);
return state;
}
@@ -248,9 +253,9 @@ public abstract class JTextComponent extends JComponent
* @param e - the insertion event
*/
public void insertUpdate(DocumentEvent e)
- throws NotImplementedException
{
- // TODO
+ firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null,
+ new Integer(e.getOffset()));
}
/**
@@ -261,9 +266,9 @@ public abstract class JTextComponent extends JComponent
* @param e - the removal event
*/
public void removeUpdate(DocumentEvent e)
- throws NotImplementedException
{
- // TODO
+ firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null,
+ new Integer(e.getOffset()));
}
/**
@@ -274,9 +279,9 @@ public abstract class JTextComponent extends JComponent
* @param e - text change event
*/
public void changedUpdate(DocumentEvent e)
- throws NotImplementedException
{
- // TODO
+ firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null,
+ new Integer(e.getOffset()));
}
/**
@@ -289,9 +294,8 @@ public abstract class JTextComponent extends JComponent
* @return a character index, or -1
*/
public int getIndexAtPoint(Point p)
- throws NotImplementedException
{
- return 0; // TODO
+ return viewToModel(p);
}
/**
@@ -305,9 +309,51 @@ public abstract class JTextComponent extends JComponent
* @return a character's bounding box, or null
*/
public Rectangle getCharacterBounds(int index)
- throws NotImplementedException
{
- return null; // TODO
+ // This is basically the same as BasicTextUI.modelToView().
+
+ Rectangle bounds = null;
+ if (index >= 0 && index < doc.getLength() - 1)
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ TextUI ui = getUI();
+ if (ui != null)
+ {
+ // Get editor rectangle.
+ Rectangle rect = new Rectangle();
+ Insets insets = getInsets();
+ rect.x = insets.left;
+ rect.y = insets.top;
+ rect.width = getWidth() - insets.left - insets.right;
+ rect.height = getHeight() - insets.top - insets.bottom;
+ View rootView = ui.getRootView(JTextComponent.this);
+ if (rootView != null)
+ {
+ rootView.setSize(rect.width, rect.height);
+ Shape s = rootView.modelToView(index,
+ Position.Bias.Forward,
+ index + 1,
+ Position.Bias.Backward,
+ rect);
+ if (s != null)
+ bounds = s.getBounds();
+ }
+ }
+ }
+ catch (BadLocationException ex)
+ {
+ // Ignore (return null).
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ }
+ return bounds;
}
/**
@@ -317,7 +363,7 @@ public abstract class JTextComponent extends JComponent
*/
public int getCharCount()
{
- return textComp.getText().length();
+ return JTextComponent.this.getText().length();
}
/**
@@ -329,9 +375,26 @@ public abstract class JTextComponent extends JComponent
* @return the character's attributes
*/
public AttributeSet getCharacterAttribute(int index)
- throws NotImplementedException
{
- return null; // TODO
+ AttributeSet atts;
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ Element el = doc.getDefaultRootElement();
+ while (! el.isLeaf())
+ {
+ int i = el.getElementIndex(index);
+ el = el.getElement(i);
+ }
+ atts = el.getAttributes();
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ return atts;
}
/**
@@ -344,9 +407,8 @@ public abstract class JTextComponent extends JComponent
* @return the part of text at that index, or null
*/
public String getAtIndex(int part, int index)
- throws NotImplementedException
{
- return null; // TODO
+ return getAtIndexImpl(part, index, 0);
}
/**
@@ -359,9 +421,8 @@ public abstract class JTextComponent extends JComponent
* @return the part of text after that index, or null
*/
public String getAfterIndex(int part, int index)
- throws NotImplementedException
{
- return null; // TODO
+ return getAtIndexImpl(part, index, 1);
}
/**
@@ -374,11 +435,84 @@ public abstract class JTextComponent extends JComponent
* @return the part of text before that index, or null
*/
public String getBeforeIndex(int part, int index)
- throws NotImplementedException
{
- return null; // TODO
+ return getAtIndexImpl(part, index, -1);
}
-
+
+ /**
+ * Implements getAtIndex(), getBeforeIndex() and getAfterIndex().
+ *
+ * @param part the part to return, either CHARACTER, WORD or SENTENCE
+ * @param index the index
+ * @param dir the direction, -1 for backwards, 0 for here, +1 for forwards
+ *
+ * @return the resulting string
+ */
+ private String getAtIndexImpl(int part, int index, int dir)
+ {
+ String ret = null;
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ BreakIterator iter = null;
+ switch (part)
+ {
+ case CHARACTER:
+ iter = BreakIterator.getCharacterInstance(getLocale());
+ break;
+ case WORD:
+ iter = BreakIterator.getWordInstance(getLocale());
+ break;
+ case SENTENCE:
+ iter = BreakIterator.getSentenceInstance(getLocale());
+ break;
+ default:
+ break;
+ }
+ String text = doc.getText(0, doc.getLength() - 1);
+ iter.setText(text);
+ int start = index;
+ int end = index;
+ switch (dir)
+ {
+ case 0:
+ if (iter.isBoundary(index))
+ {
+ start = index;
+ end = iter.following(index);
+ }
+ else
+ {
+ start = iter.preceding(index);
+ end = iter.next();
+ }
+ break;
+ case 1:
+ start = iter.following(index);
+ end = iter.next();
+ break;
+ case -1:
+ end = iter.preceding(index);
+ start = iter.previous();
+ break;
+ default:
+ assert false;
+ }
+ ret = text.substring(start, end);
+ }
+ catch (BadLocationException ex)
+ {
+ // Ignore (return null).
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ return ret;
+ }
+
/**
* Returns the number of actions for this object. The zero-th
* object represents the default action.
@@ -386,9 +520,8 @@ public abstract class JTextComponent extends JComponent
* @return the number of actions (0-based).
*/
public int getAccessibleActionCount()
- throws NotImplementedException
{
- return 0; // TODO
+ return getActions().length;
}
/**
@@ -400,10 +533,12 @@ public abstract class JTextComponent extends JComponent
* @return description of the i-th action
*/
public String getAccessibleActionDescription(int i)
- throws NotImplementedException
{
- // TODO: Not implemented fully
- return super.getAccessibleDescription();
+ String desc = null;
+ Action[] actions = getActions();
+ if (i >= 0 && i < actions.length)
+ desc = (String) actions[i].getValue(Action.NAME);
+ return desc;
}
/**
@@ -415,9 +550,17 @@ public abstract class JTextComponent extends JComponent
* @return true if the action was performed successfully
*/
public boolean doAccessibleAction(int i)
- throws NotImplementedException
{
- return false; // TODO
+ boolean ret = false;
+ Action[] actions = getActions();
+ if (i >= 0 && i < actions.length)
+ {
+ ActionEvent ev = new ActionEvent(JTextComponent.this,
+ ActionEvent.ACTION_PERFORMED, null);
+ actions[i].actionPerformed(ev);
+ ret = true;
+ }
+ return ret;
}
/**
@@ -426,9 +569,8 @@ public abstract class JTextComponent extends JComponent
* @param s - the new text contents.
*/
public void setTextContents(String s)
- throws NotImplementedException
{
- // TODO
+ setText(s);
}
/**
@@ -438,9 +580,16 @@ public abstract class JTextComponent extends JComponent
* @param s - the new text
*/
public void insertTextAtIndex(int index, String s)
- throws NotImplementedException
{
- replaceText(index, index, s);
+ try
+ {
+ doc.insertString(index, s, null);
+ }
+ catch (BadLocationException ex)
+ {
+ // What should we do with this?
+ ex.printStackTrace();
+ }
}
/**
@@ -453,7 +602,7 @@ public abstract class JTextComponent extends JComponent
{
try
{
- return textComp.getText(start, end - start);
+ return JTextComponent.this.getText(start, end - start);
}
catch (BadLocationException ble)
{
@@ -481,8 +630,8 @@ public abstract class JTextComponent extends JComponent
*/
public void cut(int start, int end)
{
- textComp.select(start, end);
- textComp.cut();
+ JTextComponent.this.select(start, end);
+ JTextComponent.this.cut();
}
/**
@@ -492,8 +641,8 @@ public abstract class JTextComponent extends JComponent
*/
public void paste(int start)
{
- textComp.setCaretPosition(start);
- textComp.paste();
+ JTextComponent.this.setCaretPosition(start);
+ JTextComponent.this.paste();
}
/**
@@ -506,8 +655,8 @@ public abstract class JTextComponent extends JComponent
*/
public void replaceText(int start, int end, String s)
{
- textComp.select(start, end);
- textComp.replaceSelection(s);
+ JTextComponent.this.select(start, end);
+ JTextComponent.this.replaceSelection(s);
}
/**
@@ -518,7 +667,7 @@ public abstract class JTextComponent extends JComponent
*/
public void selectText(int start, int end)
{
- textComp.select(start, end);
+ JTextComponent.this.select(start, end);
}
/**
@@ -529,9 +678,12 @@ public abstract class JTextComponent extends JComponent
* @param s - the new attribute set for the text in the range
*/
public void setAttributes(int start, int end, AttributeSet s)
- throws NotImplementedException
{
- // TODO
+ if (doc instanceof StyledDocument)
+ {
+ StyledDocument sdoc = (StyledDocument) doc;
+ sdoc.setCharacterAttributes(start, end - start, s, true);
+ }
}
}
diff --git a/libjava/classpath/javax/swing/text/LabelView.java b/libjava/classpath/javax/swing/text/LabelView.java
index 03279c4b2b5..7cfeae86229 100644
--- a/libjava/classpath/javax/swing/text/LabelView.java
+++ b/libjava/classpath/javax/swing/text/LabelView.java
@@ -39,9 +39,11 @@ exception statement from your version. */
package javax.swing.text;
import java.awt.Color;
+import java.awt.Container;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Shape;
+import java.awt.Toolkit;
import javax.swing.event.DocumentEvent;
@@ -90,6 +92,11 @@ public class LabelView extends GlyphView
boolean superscript;
/**
+ * Indicates if the attributes must be refetched.
+ */
+ private boolean valid;
+
+ /**
* Creates a new <code>GlyphView</code> for the given <code>Element</code>.
*
* @param element the element that is rendered by this GlyphView
@@ -97,7 +104,7 @@ public class LabelView extends GlyphView
public LabelView(Element element)
{
super(element);
- setPropertiesFromAttributes();
+ valid = false;
}
/**
@@ -107,28 +114,25 @@ public class LabelView extends GlyphView
*/
protected void setPropertiesFromAttributes()
{
- Element el = getElement();
- AttributeSet atts = el.getAttributes();
- // We cannot use StyleConstants.getBackground() here, because that returns
- // BLACK as default (when background == null). What we need is the
- // background setting of the text component instead, which is what we get
- // when background == null anyway.
- background = (Color) atts.getAttribute(StyleConstants.Background);
- foreground = StyleConstants.getForeground(atts);
- strikeThrough = StyleConstants.isStrikeThrough(atts);
- subscript = StyleConstants.isSubscript(atts);
- superscript = StyleConstants.isSuperscript(atts);
- underline = StyleConstants.isUnderline(atts);
-
- // Determine the font.
- String family = StyleConstants.getFontFamily(atts);
- int size = StyleConstants.getFontSize(atts);
- int style = Font.PLAIN;
- if (StyleConstants.isBold(atts))
- style |= Font.BOLD;
- if (StyleConstants.isItalic(atts))
- style |= Font.ITALIC;
- font = new Font(family, style, size);
+ AttributeSet atts = getAttributes();
+ setStrikeThrough(StyleConstants.isStrikeThrough(atts));
+ setSubscript(StyleConstants.isSubscript(atts));
+ setSuperscript(StyleConstants.isSuperscript(atts));
+ setUnderline(StyleConstants.isUnderline(atts));
+
+ // Determine the font and colors.
+ Document d = getDocument();
+ if (d instanceof StyledDocument)
+ {
+ StyledDocument doc = (StyledDocument) d;
+ font = doc.getFont(atts);
+ if (atts.isDefined(StyleConstants.Background))
+ background = doc.getBackground(atts);
+ else
+ background = null;
+ foreground = doc.getForeground(atts);
+ }
+ valid = true;
}
/**
@@ -142,7 +146,8 @@ public class LabelView extends GlyphView
*/
public void changedUpdate(DocumentEvent e, Shape a, ViewFactory vf)
{
- setPropertiesFromAttributes();
+ valid = false;
+ super.changedUpdate(e, a, vf);
}
/**
@@ -152,6 +157,8 @@ public class LabelView extends GlyphView
*/
public Color getBackground()
{
+ if (! valid)
+ setPropertiesFromAttributes();
return background;
}
@@ -175,6 +182,8 @@ public class LabelView extends GlyphView
*/
public Color getForeground()
{
+ if (! valid)
+ setPropertiesFromAttributes();
return foreground;
}
@@ -185,6 +194,8 @@ public class LabelView extends GlyphView
*/
public Font getFont()
{
+ if (! valid)
+ setPropertiesFromAttributes();
return font;
}
@@ -197,7 +208,16 @@ public class LabelView extends GlyphView
*/
protected FontMetrics getFontMetrics()
{
- return getContainer().getGraphics().getFontMetrics(font);
+ if (! valid)
+ setPropertiesFromAttributes();
+
+ Container c = getContainer();
+ FontMetrics fm;
+ if (c != null)
+ fm = c.getFontMetrics(font);
+ else
+ fm = Toolkit.getDefaultToolkit().getFontMetrics(font);
+ return fm;
}
/**
@@ -209,6 +229,8 @@ public class LabelView extends GlyphView
*/
public boolean isUnderline()
{
+ if (! valid)
+ setPropertiesFromAttributes();
return underline;
}
@@ -232,6 +254,8 @@ public class LabelView extends GlyphView
*/
public boolean isSubscript()
{
+ if (! valid)
+ setPropertiesFromAttributes();
return subscript;
}
@@ -255,6 +279,8 @@ public class LabelView extends GlyphView
*/
public boolean isSuperscript()
{
+ if (! valid)
+ setPropertiesFromAttributes();
return superscript;
}
@@ -278,6 +304,8 @@ public class LabelView extends GlyphView
*/
public boolean isStrikeThrough()
{
+ if (! valid)
+ setPropertiesFromAttributes();
return strikeThrough;
}
diff --git a/libjava/classpath/javax/swing/text/MaskFormatter.java b/libjava/classpath/javax/swing/text/MaskFormatter.java
index d12b9ea29d7..581cceb617d 100644
--- a/libjava/classpath/javax/swing/text/MaskFormatter.java
+++ b/libjava/classpath/javax/swing/text/MaskFormatter.java
@@ -110,9 +110,7 @@ public class MaskFormatter extends DefaultFormatter
*/
public MaskFormatter (String mask) throws java.text.ParseException
{
- // Override super's default behaviour, in MaskFormatter the default
- // is not to allow invalid values
- setAllowsInvalid(false);
+ this();
setMask (mask);
}
@@ -307,60 +305,124 @@ public class MaskFormatter extends DefaultFormatter
*/
public Object stringToValue (String value) throws ParseException
{
- int vLength = value.length();
-
- // For value to be a valid it must be the same length as the mask
- // note this doesn't take into account symbols that occupy more than
- // one character, this is something we may possibly need to fix.
- if (maskLength != vLength)
- throw new ParseException ("stringToValue passed invalid value", vLength);
-
- // Check if the value is valid according to the mask and valid/invalid
- // sets.
- try
- {
- convertValue(value, false);
- }
- catch (ParseException pe)
- {
- throw new ParseException("stringToValue passed invalid value",
- pe.getErrorOffset());
- }
-
- if (!getValueContainsLiteralCharacters())
- value = stripLiterals(value);
- return super.stringToValue(value);
+ return super.stringToValue(convertStringToValue(value));
}
- /**
- * Strips the literal characters from the given String.
- * @param value the String to strip
- * @return the stripped String
- */
- String stripLiterals(String value)
+ private String convertStringToValue(String value)
+ throws ParseException
{
StringBuffer result = new StringBuffer();
- for (int i = 0; i < value.length(); i++)
+ char valueChar;
+ boolean isPlaceHolder;
+
+ int length = mask.length();
+ for (int i = 0, j = 0; j < length; j++)
{
- // Only append the characters that don't correspond to literal
- // characters in the mask.
- switch (mask.charAt(i))
+ char maskChar = mask.charAt(j);
+
+ if (i < value.length())
+ {
+ isPlaceHolder = false;
+ valueChar = value.charAt(i);
+ if (maskChar != ESCAPE_CHAR && maskChar != valueChar)
+ {
+ if (invalidChars != null
+ && invalidChars.indexOf(valueChar) != -1)
+ throw new ParseException("Invalid character: " + valueChar, i);
+ if (validChars != null
+ && validChars.indexOf(valueChar) == -1)
+ throw new ParseException("Invalid character: " + valueChar, i);
+ }
+ }
+ else if (placeHolder != null && i < placeHolder.length())
+ {
+ isPlaceHolder = true;
+ valueChar = placeHolder.charAt(i);
+ }
+ else
+ {
+ isPlaceHolder = true;
+ valueChar = placeHolderChar;
+ }
+
+ // This switch block on the mask character checks that the character
+ // within <code>value</code> at that point is valid according to the
+ // mask and also converts to upper/lowercase as needed.
+ switch (maskChar)
{
case NUM_CHAR:
+ if (! Character.isDigit(valueChar))
+ throw new ParseException("Number expected: " + valueChar, i);
+ result.append(valueChar);
+ i++;
+ break;
case UPPERCASE_CHAR:
+ if (! Character.isLetter(valueChar))
+ throw new ParseException("Letter expected", i);
+ result.append(Character.toUpperCase(valueChar));
+ i++;
+ break;
case LOWERCASE_CHAR:
+ if (! Character.isLetter(valueChar))
+ throw new ParseException("Letter expected", i);
+ result.append(Character.toLowerCase(valueChar));
+ i++;
+ break;
case ALPHANUM_CHAR:
+ if (! Character.isLetterOrDigit(valueChar))
+ throw new ParseException("Letter or number expected", i);
+ result.append(valueChar);
+ i++;
+ break;
case LETTER_CHAR:
+ if (! Character.isLetter(valueChar))
+ throw new ParseException("Letter expected", i);
+ result.append(valueChar);
+ i++;
+ break;
case HEX_CHAR:
+ if (hexString.indexOf(valueChar) == -1 && ! isPlaceHolder)
+ throw new ParseException("Hexadecimal character expected", i);
+ result.append(valueChar);
+ i++;
+ break;
case ANYTHING_CHAR:
- result.append(value.charAt(i));
+ result.append(valueChar);
+ i++;
+ break;
+ case ESCAPE_CHAR:
+ // Escape character, check the next character to make sure that
+ // the literals match
+ j++;
+ if (j < length)
+ {
+ maskChar = mask.charAt(j);
+ if (! isPlaceHolder && getValueContainsLiteralCharacters()
+ && valueChar != maskChar)
+ throw new ParseException ("Invalid character: "+ valueChar, i);
+ if (getValueContainsLiteralCharacters())
+ {
+ result.append(maskChar);
+ }
+ i++;
+ }
+ else if (! isPlaceHolder)
+ throw new ParseException("Bad match at trailing escape: ", i);
break;
default:
+ if (! isPlaceHolder && getValueContainsLiteralCharacters()
+ && valueChar != maskChar)
+ throw new ParseException ("Invalid character: "+ valueChar, i);
+ if (getValueContainsLiteralCharacters())
+ {
+ result.append(maskChar);
+ }
+ i++;
}
}
return result.toString();
}
-
+
/**
* Returns a String representation of the Object value based on the mask.
*
@@ -368,21 +430,10 @@ public class MaskFormatter extends DefaultFormatter
* @throws ParseException if value is invalid for this mask and valid/invalid
* character sets
*/
- public String valueToString (Object value) throws ParseException
+ public String valueToString(Object value) throws ParseException
{
- String result = super.valueToString(value);
- int rLength = result.length();
-
- // If value is longer than the mask, truncate it. Note we may need to
- // account for symbols that are more than one character long.
- if (rLength > maskLength)
- result = result.substring(0, maskLength);
-
- // Verify the validity and convert to upper/lowercase as needed.
- result = convertValue(result, true);
- if (rLength < maskLength)
- return pad(result, rLength);
- return result;
+ String string = value != null ? value.toString() : "";
+ return convertValueToString(string);
}
/**
@@ -390,194 +441,116 @@ public class MaskFormatter extends DefaultFormatter
* sure that it is valid. If <code>convert</code> is true, it also
* converts letters to upper/lowercase as required by the mask.
* @param value the String to convert
- * @param convert true if we should convert letters to upper/lowercase
* @return the converted String
* @throws ParseException if the given String isn't valid for the mask
*/
- String convertValue(String value, boolean convert) throws ParseException
+ private String convertValueToString(String value)
+ throws ParseException
{
- StringBuffer result = new StringBuffer(value);
- char markChar;
- char resultChar;
- boolean literal;
-
- // this boolean is specifically to avoid calling the isCharValid method
- // when neither invalidChars or validChars has been set
- boolean checkCharSets = (invalidChars != null || validChars != null);
+ StringBuffer result = new StringBuffer();
+ char valueChar;
+ boolean isPlaceHolder;
- for (int i = 0, j = 0; i < value.length(); i++, j++)
+ int length = mask.length();
+ for (int i = 0, j = 0; j < length; j++)
{
- literal = false;
- resultChar = result.charAt(i);
+ char maskChar = mask.charAt(j);
+ if (i < value.length())
+ {
+ isPlaceHolder = false;
+ valueChar = value.charAt(i);
+ if (maskChar != ESCAPE_CHAR && valueChar != maskChar)
+ {
+ if (invalidChars != null
+ && invalidChars.indexOf(valueChar) != -1)
+ throw new ParseException("Invalid character: " + valueChar,
+ i);
+ if (validChars != null && validChars.indexOf(valueChar) == -1)
+ throw new ParseException("Invalid character: " + valueChar +" maskChar: " + maskChar,
+ i);
+ }
+ }
+ else if (placeHolder != null && i < placeHolder.length())
+ {
+ isPlaceHolder = true;
+ valueChar = placeHolder.charAt(i);
+ }
+ else
+ {
+ isPlaceHolder = true;
+ valueChar = placeHolderChar;
+ }
+
// This switch block on the mask character checks that the character
// within <code>value</code> at that point is valid according to the
// mask and also converts to upper/lowercase as needed.
- switch (mask.charAt(j))
+ switch (maskChar)
{
case NUM_CHAR:
- if (!Character.isDigit(resultChar))
- throw new ParseException("Number expected", i);
+ if ( ! isPlaceHolder && ! Character.isDigit(valueChar))
+ throw new ParseException("Number expected: " + valueChar, i);
+ result.append(valueChar);
+ i++;
break;
case UPPERCASE_CHAR:
- if (!Character.isLetter(resultChar))
+ if (! Character.isLetter(valueChar))
throw new ParseException("Letter expected", i);
- if (convert)
- result.setCharAt(i, Character.toUpperCase(resultChar));
+ result.append(Character.toUpperCase(valueChar));
+ i++;
break;
case LOWERCASE_CHAR:
- if (!Character.isLetter(resultChar))
+ if (! Character.isLetter(valueChar))
throw new ParseException("Letter expected", i);
- if (convert)
- result.setCharAt(i, Character.toLowerCase(resultChar));
+ result.append(Character.toLowerCase(valueChar));
+ i++;
break;
case ALPHANUM_CHAR:
- if (!Character.isLetterOrDigit(resultChar))
+ if (! Character.isLetterOrDigit(valueChar))
throw new ParseException("Letter or number expected", i);
+ result.append(valueChar);
+ i++;
break;
case LETTER_CHAR:
- if (!Character.isLetter(resultChar))
+ if (! Character.isLetter(valueChar))
throw new ParseException("Letter expected", i);
+ result.append(valueChar);
+ i++;
break;
case HEX_CHAR:
- if (hexString.indexOf(resultChar) == -1)
+ if (hexString.indexOf(valueChar) == -1 && ! isPlaceHolder)
throw new ParseException("Hexadecimal character expected", i);
+ result.append(valueChar);
+ i++;
break;
case ANYTHING_CHAR:
+ result.append(valueChar);
+ i++;
break;
case ESCAPE_CHAR:
// Escape character, check the next character to make sure that
// the literals match
j++;
- literal = true;
- if (resultChar != mask.charAt(j))
- throw new ParseException ("Invalid character: "+resultChar, i);
+ if (j < length)
+ {
+ maskChar = mask.charAt(j);
+ if (! isPlaceHolder && getValueContainsLiteralCharacters()
+ && valueChar != maskChar)
+ throw new ParseException ("Invalid character: "+ valueChar, i);
+ if (getValueContainsLiteralCharacters())
+ i++;
+ result.append(maskChar);
+ }
break;
default:
- literal = true;
- if (!getValueContainsLiteralCharacters() && convert)
- throw new ParseException ("Invalid character: "+resultChar, i);
- else if (resultChar != mask.charAt(j))
- throw new ParseException ("Invalid character: "+resultChar, i);
+ if (! isPlaceHolder && getValueContainsLiteralCharacters()
+ && valueChar != maskChar)
+ throw new ParseException ("Invalid character: "+ valueChar, i);
+ if (getValueContainsLiteralCharacters())
+ i++;
+ result.append(maskChar);
}
- // If necessary, check if the character is valid.
- if (!literal && checkCharSets && !isCharValid(resultChar))
- throw new ParseException("invalid character: "+resultChar, i);
-
- }
- return result.toString();
- }
-
- /**
- * Convenience method used by many other methods to check if a character is
- * valid according to the mask, the validChars, and the invalidChars. To
- * be valid a character must:
- * 1. be allowed by the mask
- * 2. be present in any non-null validChars String
- * 3. not be present in any non-null invalidChars String
- * @param testChar the character to test
- * @return true if the character is valid
- */
- boolean isCharValid(char testChar)
- {
- char lower = Character.toLowerCase(testChar);
- char upper = Character.toUpperCase(testChar);
- // If validChars isn't null, the character must appear in it.
- if (validChars != null)
- if (validChars.indexOf(lower) == -1 && validChars.indexOf(upper) == -1)
- return false;
- // If invalidChars isn't null, the character must not appear in it.
- if (invalidChars != null)
- if (invalidChars.indexOf(lower) != -1
- || invalidChars.indexOf(upper) != -1)
- return false;
- return true;
- }
-
- /**
- * Pads the value with literals, the placeholder String and/or placeholder
- * character as appropriate.
- * @param value the value to pad
- * @param currLength the current length of the value
- * @return the padded String
- */
- String pad (String value, int currLength)
- {
- StringBuffer result = new StringBuffer(value);
- int index = currLength;
- while (result.length() < maskLength)
- {
- // The character used to pad may be a literal, a character from the
- // place holder string, or the place holder character. getPadCharAt
- // will find the proper one for us.
- result.append (getPadCharAt(index));
- index++;
}
return result.toString();
}
- /**
- * Returns the character with which to pad the value at the given index
- * position. If the mask has a literal at this position, this is returned
- * otherwise if the place holder string is initialized and is longer than
- * <code>i</code> characters then the character at position <code>i</code>
- * from this String is returned. Else, the place holder character is
- * returned.
- * @param i the index at which we want to pad the value
- * @return the character with which we should pad the value
- */
- char getPadCharAt(int i)
- {
- boolean escaped = false;
- int target = i;
- char maskChar;
- int holderLength = placeHolder == null ? -1 : placeHolder.length();
- // We must iterate through the mask from the beginning, because the given
- // index doesn't account for escaped characters. For example, with the
- // mask "1A'A''A1" index 2 refers to the literalized A, not to the
- // single quotation.
- for (int n = 0; n < mask.length(); n++)
- {
- maskChar = mask.charAt(n);
- if (maskChar == ESCAPE_CHAR && !escaped)
- {
- target++;
- escaped = true;
- }
- else if (escaped == true)
- {
- // Check if target == n which means we've come to the character
- // we want to return and since it is a literal (because escaped
- // is true), we return it.
- if (target == n)
- return maskChar;
- escaped = false;
- }
- if (target == n)
- {
- // We've come to the character we want to return. It wasn't
- // escaped so if it isn't a literal we should return either
- // the character from place holder string or the place holder
- // character, depending on whether or not the place holder
- // string is long enough.
- switch (maskChar)
- {
- case NUM_CHAR:
- case UPPERCASE_CHAR:
- case LOWERCASE_CHAR:
- case ALPHANUM_CHAR:
- case LETTER_CHAR:
- case HEX_CHAR:
- case ANYTHING_CHAR:
- if (holderLength > i)
- return placeHolder.charAt(i);
- else
- return placeHolderChar;
- default:
- return maskChar;
- }
- }
- }
- // This shouldn't happen
- throw new AssertionError("MaskFormatter.getMaskCharAt failed");
- }
}
diff --git a/libjava/classpath/javax/swing/text/MutableAttributeSet.java b/libjava/classpath/javax/swing/text/MutableAttributeSet.java
index 3728b9ce126..5dd2406a3a9 100644
--- a/libjava/classpath/javax/swing/text/MutableAttributeSet.java
+++ b/libjava/classpath/javax/swing/text/MutableAttributeSet.java
@@ -90,7 +90,7 @@ public interface MutableAttributeSet extends AttributeSet
* @throws NullPointerException if <code>names</code> is <code>null</code>
* or contains any <code>null</code> values.
*/
- void removeAttributes(Enumeration names);
+ void removeAttributes(Enumeration<?> names);
/**
* Removes attributes from this set if they are found in the
diff --git a/libjava/classpath/javax/swing/text/ParagraphView.java b/libjava/classpath/javax/swing/text/ParagraphView.java
index c4857863d35..fb4ac65d835 100644
--- a/libjava/classpath/javax/swing/text/ParagraphView.java
+++ b/libjava/classpath/javax/swing/text/ParagraphView.java
@@ -38,8 +38,12 @@ exception statement from your version. */
package javax.swing.text;
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Rectangle;
import java.awt.Shape;
+import javax.swing.SizeRequirements;
import javax.swing.event.DocumentEvent;
/**
@@ -64,30 +68,45 @@ public class ParagraphView extends FlowView implements TabExpander
super(el, X_AXIS);
}
+ /**
+ * Overridden to adjust when we are the first line, and firstLineIndent
+ * is not 0.
+ */
+ public short getLeftInset()
+ {
+ short leftInset = super.getLeftInset();
+ View parent = getParent();
+ if (parent != null)
+ {
+ if (parent.getView(0) == this)
+ leftInset += firstLineIndent;
+ }
+ return leftInset;
+ }
+
public float getAlignment(int axis)
{
float align;
if (axis == X_AXIS)
- align = 0.0F; // TODO: Implement according to justification
+ switch (justification)
+ {
+ case StyleConstants.ALIGN_RIGHT:
+ align = 1.0F;
+ break;
+ case StyleConstants.ALIGN_CENTER:
+ case StyleConstants.ALIGN_JUSTIFIED:
+ align = 0.5F;
+ break;
+ case StyleConstants.ALIGN_LEFT:
+ default:
+ align = 0.0F;
+ }
else
align = super.getAlignment(axis);
return align;
}
/**
- * Allows rows to span the whole parent view.
- */
- public float getMaximumSpan(int axis)
- {
- float max;
- if (axis == X_AXIS)
- max = Float.MAX_VALUE;
- else
- max = super.getMaximumSpan(axis);
- return max;
- }
-
- /**
* Overridden because child views are not necessarily laid out in model
* order.
*/
@@ -107,10 +126,63 @@ public class ParagraphView extends FlowView implements TabExpander
return index;
}
+
+ /**
+ * Overridden to perform a baseline layout. The normal BoxView layout
+ * isn't completely suitable for rows.
+ */
+ protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets,
+ int[] spans)
+ {
+ baselineLayout(targetSpan, axis, offsets, spans);
+ }
+
+ /**
+ * Overridden to perform a baseline layout. The normal BoxView layout
+ * isn't completely suitable for rows.
+ */
+ protected SizeRequirements calculateMinorAxisRequirements(int axis,
+ SizeRequirements r)
+ {
+ return baselineRequirements(axis, r);
+ }
+
protected void loadChildren(ViewFactory vf)
{
// 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;
+ }
}
/**
@@ -192,11 +264,14 @@ public class ParagraphView extends FlowView implements TabExpander
*
* @param ev the document event
* @param a the allocation of this view
- * @param fv the view factory to use for creating new child views
+ * @param vf the view factory to use for creating new child views
*/
- public void changedUpdate(DocumentEvent ev, Shape a, ViewFactory fv)
+ public void changedUpdate(DocumentEvent ev, Shape a, ViewFactory vf)
{
setPropertiesFromAttributes();
+ layoutChanged(X_AXIS);
+ layoutChanged(Y_AXIS);
+ super.changedUpdate(ev, a, vf);
}
/**
diff --git a/libjava/classpath/javax/swing/text/PlainView.java b/libjava/classpath/javax/swing/text/PlainView.java
index 48fe37ce880..e048d5f7168 100644
--- a/libjava/classpath/javax/swing/text/PlainView.java
+++ b/libjava/classpath/javax/swing/text/PlainView.java
@@ -87,6 +87,16 @@ public class PlainView extends View implements TabExpander
*/
private transient Segment lineBuffer;
+ /**
+ * The base offset for tab calculations.
+ */
+ private int tabBase;
+
+ /**
+ * The tab size.
+ */
+ private int tabSize;
+
public PlainView(Element elem)
{
super(elem);
@@ -104,6 +114,7 @@ public class PlainView extends View implements TabExpander
{
this.font = font;
metrics = component.getFontMetrics(font);
+ tabSize = getTabSize() * metrics.charWidth('m');
}
}
@@ -115,7 +126,7 @@ public class PlainView extends View implements TabExpander
// Ensure metrics are up-to-date.
updateMetrics();
- Rectangle rect = a.getBounds();
+ Rectangle rect = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
int fontHeight = metrics.getHeight();
return new Rectangle(rect.x, rect.y + (line * fontHeight),
rect.width, fontHeight);
@@ -132,13 +143,14 @@ public class PlainView extends View implements TabExpander
// Get rectangle of the line containing position.
int lineIndex = getElement().getElementIndex(position);
Rectangle rect = lineToRect(a, lineIndex);
+ tabBase = rect.x;
// Get the rectangle for position.
Element line = getElement().getElement(lineIndex);
int lineStart = line.getStartOffset();
Segment segment = getLineBuffer();
document.getText(lineStart, position - lineStart, segment);
- int xoffset = Utilities.getTabbedTextWidth(segment, metrics, rect.x,
+ int xoffset = Utilities.getTabbedTextWidth(segment, metrics, tabBase,
this, lineStart);
// Calc the real rectangle.
@@ -262,17 +274,47 @@ public class PlainView extends View implements TabExpander
selectionStart = textComponent.getSelectionStart();
selectionEnd = textComponent.getSelectionEnd();
- Rectangle rect = s.getBounds();
+ Rectangle rect = s instanceof Rectangle ? (Rectangle) s : s.getBounds();
+ tabBase = rect.x;
// FIXME: Text may be scrolled.
Document document = textComponent.getDocument();
- Element root = document.getDefaultRootElement();
- int y = rect.y + metrics.getAscent();
+ Element root = getElement();
int height = metrics.getHeight();
-
+
+ // For layered highlighters we need to paint the layered highlights
+ // before painting any text.
+ LayeredHighlighter hl = null;
+ Highlighter h = textComponent.getHighlighter();
+ if (h instanceof LayeredHighlighter)
+ hl = (LayeredHighlighter) h;
+
int count = root.getElementCount();
- for (int i = 0; i < count; i++)
+
+ // Determine first and last line inside the clip.
+ Rectangle clip = g.getClipBounds();
+ SwingUtilities.computeIntersection(rect.x, rect.y, rect.width, rect.height,
+ clip);
+ int line0 = (clip.y - rect.y) / height;
+ line0 = Math.max(0, Math.min(line0, count - 1));
+ int line1 = (clip.y + clip.height - rect.y) / height;
+ line1 = Math.max(0, Math.min(line1, count - 1));
+ int y = rect.y + metrics.getAscent() + height * line0;
+ for (int i = line0; i <= line1; i++)
{
+ if (hl != null)
+ {
+ Element lineEl = root.getElement(i);
+ // Exclude the trailing newline from beeing highlighted.
+ if (i == count)
+ hl.paintLayeredHighlights(g, lineEl.getStartOffset(),
+ lineEl.getEndOffset(), s, textComponent,
+ this);
+ else
+ hl.paintLayeredHighlights(g, lineEl.getStartOffset(),
+ lineEl.getEndOffset() - 1, s,
+ textComponent, this);
+ }
drawLine(i, g, rect.x, y);
y += height;
}
@@ -303,8 +345,13 @@ public class PlainView extends View implements TabExpander
*/
public float nextTabStop(float x, int tabStop)
{
- float tabSizePixels = getTabSize() * metrics.charWidth('m');
- return (float) (Math.floor(x / tabSizePixels) + 1) * tabSizePixels;
+ float next = x;
+ if (tabSize != 0)
+ {
+ int numTabs = (((int) x) - tabBase) / tabSize;
+ next = tabBase + (numTabs + 1) * tabSize;
+ }
+ return next;
}
/**
@@ -390,41 +437,58 @@ public class PlainView extends View implements TabExpander
*/
public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
{
- Rectangle rec = a.getBounds();
- Document doc = getDocument();
- Element root = doc.getDefaultRootElement();
-
- // PlainView doesn't support line-wrapping so we can find out which
- // Element was clicked on just by the y-position.
- // Since the coordinates may be outside of the coordinate space
- // of the allocation area (e.g. user dragged mouse outside
- // the component) we have to limit the values.
- // This has the nice effect that the user can drag the
- // mouse above or below the component and it will still
- // react to the x values (e.g. when selecting).
- int lineClicked
- = Math.min(Math.max((int) (y - rec.y) / metrics.getHeight(), 0),
- root.getElementCount() - 1);
-
- Element line = root.getElement(lineClicked);
-
- Segment s = getLineBuffer();
- int start = line.getStartOffset();
- // We don't want the \n at the end of the line.
- int end = line.getEndOffset() - 1;
- try
- {
- doc.getText(start, end - start, s);
- }
- catch (BadLocationException ble)
+ Rectangle rec = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ tabBase = rec.x;
+
+ int pos;
+ if ((int) y < rec.y)
+ // Above our area vertically. Return start offset.
+ pos = getStartOffset();
+ else if ((int) y > rec.y + rec.height)
+ // Below our area vertically. Return end offset.
+ pos = getEndOffset() - 1;
+ else
{
- AssertionError ae = new AssertionError("Unexpected bad location");
- ae.initCause(ble);
- throw ae;
+ // Inside the allocation vertically. Determine line and X offset.
+ Document doc = getDocument();
+ Element root = doc.getDefaultRootElement();
+ int line = Math.abs(((int) y - rec.y) / metrics.getHeight());
+ if (line >= root.getElementCount())
+ pos = getEndOffset() - 1;
+ else
+ {
+ Element lineEl = root.getElement(line);
+ if (x < rec.x)
+ // To the left of the allocation area.
+ pos = lineEl.getStartOffset();
+ else if (x > rec.x + rec.width)
+ // To the right of the allocation area.
+ pos = lineEl.getEndOffset() - 1;
+ else
+ {
+ try
+ {
+ int p0 = lineEl.getStartOffset();
+ int p1 = lineEl.getEndOffset();
+ Segment s = new Segment();
+ doc.getText(p0, p1 - p0, s);
+ tabBase = rec.x;
+ pos = p0 + Utilities.getTabbedTextOffset(s, metrics,
+ tabBase, (int) x,
+ this, p0);
+ }
+ catch (BadLocationException ex)
+ {
+ // Should not happen.
+ pos = -1;
+ }
+ }
+
+ }
}
-
- int pos = Utilities.getTabbedTextOffset(s, metrics, rec.x, (int)x, this, start);
- return Math.max (0, pos);
+ // Bias is always forward.
+ b[0] = Position.Bias.Forward;
+ return pos;
}
/**
@@ -654,7 +718,7 @@ public class PlainView extends View implements TabExpander
throw err;
}
- return Utilities.getTabbedTextWidth(buffer, metrics, 0, this,
+ return Utilities.getTabbedTextWidth(buffer, metrics, tabBase, this,
lineEl.getStartOffset());
}
}
diff --git a/libjava/classpath/javax/swing/text/Position.java b/libjava/classpath/javax/swing/text/Position.java
index bb1449e187a..d02eb834dd9 100644
--- a/libjava/classpath/javax/swing/text/Position.java
+++ b/libjava/classpath/javax/swing/text/Position.java
@@ -42,8 +42,8 @@ public interface Position
{
static final class Bias
{
- public static final Bias Backward = new Bias("backward");
- public static final Bias Forward = new Bias("forward");
+ public static final Bias Backward = new Bias("Backward");
+ public static final Bias Forward = new Bias("Forward");
private String name;
diff --git a/libjava/classpath/javax/swing/text/SimpleAttributeSet.java b/libjava/classpath/javax/swing/text/SimpleAttributeSet.java
index 8684ef87d34..701fa8a7c90 100644
--- a/libjava/classpath/javax/swing/text/SimpleAttributeSet.java
+++ b/libjava/classpath/javax/swing/text/SimpleAttributeSet.java
@@ -123,9 +123,17 @@ public class SimpleAttributeSet
*/
public Object clone()
{
- SimpleAttributeSet s = new SimpleAttributeSet();
- s.tab = (Hashtable) tab.clone();
- return s;
+ SimpleAttributeSet attr = null;
+ try
+ {
+ attr = (SimpleAttributeSet) super.clone();
+ attr.tab = (Hashtable) tab.clone();
+ }
+ catch (CloneNotSupportedException ex)
+ {
+ assert false;
+ }
+ return attr;
}
/**
@@ -253,7 +261,7 @@ public class SimpleAttributeSet
*
* @return An enumeration of the attribute names.
*/
- public Enumeration getAttributeNames()
+ public Enumeration<?> getAttributeNames()
{
return tab.keys();
}
@@ -367,7 +375,7 @@ public class SimpleAttributeSet
* @throws NullPointerException if <code>names</code> is <code>null</code>
* or contains any <code>null</code> values.
*/
- public void removeAttributes(Enumeration names)
+ public void removeAttributes(Enumeration<?> names)
{
while (names.hasMoreElements())
{
diff --git a/libjava/classpath/javax/swing/text/StringContent.java b/libjava/classpath/javax/swing/text/StringContent.java
index 8014dc3bce6..4a3f9d75222 100644
--- a/libjava/classpath/javax/swing/text/StringContent.java
+++ b/libjava/classpath/javax/swing/text/StringContent.java
@@ -39,6 +39,9 @@ exception statement from your version. */
package javax.swing.text;
import java.io.Serializable;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
import java.util.Iterator;
import java.util.Vector;
@@ -57,6 +60,76 @@ import javax.swing.undo.UndoableEdit;
public final class StringContent
implements AbstractDocument.Content, Serializable
{
+ /**
+ * Stores a reference to a mark that can be resetted to the original value
+ * after a mark has been moved. This is used for undoing actions.
+ */
+ private class UndoPosRef
+ {
+ /**
+ * The mark that might need to be reset.
+ */
+ private Mark mark;
+
+ /**
+ * The original offset to reset the mark to.
+ */
+ private int undoOffset;
+
+ /**
+ * Creates a new UndoPosRef.
+ *
+ * @param m the mark
+ */
+ UndoPosRef(Mark m)
+ {
+ mark = m;
+ undoOffset = mark.mark;
+ }
+
+ /**
+ * Resets the position of the mark to the value that it had when
+ * creating this UndoPosRef.
+ */
+ void reset()
+ {
+ mark.mark = undoOffset;
+ }
+ }
+
+ /**
+ * Holds a mark into the buffer that is used by StickyPosition to find
+ * the actual offset of the position. This is pulled out of the
+ * GapContentPosition object so that the mark and position can be handled
+ * independently, and most important, so that the StickyPosition can
+ * be garbage collected while we still hold a reference to the Mark object.
+ */
+ private class Mark
+ {
+ /**
+ * The actual mark into the buffer.
+ */
+ int mark;
+
+
+ /**
+ * The number of GapContentPosition object that reference this mark. If
+ * it reaches zero, it get's deleted by
+ * {@link StringContent#garbageCollect()}.
+ */
+ int refCount;
+
+ /**
+ * Creates a new Mark object for the specified offset.
+ *
+ * @param offset the offset
+ */
+ Mark(int offset)
+ {
+ mark = offset;
+ }
+ }
+
/** The serialization UID (compatible with JDK1.5). */
private static final long serialVersionUID = 4755994433709540381L;
@@ -65,7 +138,12 @@ public final class StringContent
private int count;
- private Vector positions = new Vector();
+ /**
+ * Holds the marks for the positions.
+ *
+ * This is package private to avoid accessor methods.
+ */
+ Vector marks;
private class InsertUndo extends AbstractUndoableEdit
{
@@ -75,6 +153,8 @@ public final class StringContent
private String redoContent;
+ private Vector positions;
+
public InsertUndo(int start, int length)
{
super();
@@ -87,10 +167,10 @@ public final class StringContent
super.undo();
try
{
- StringContent.this.checkLocation(this.start, this.length);
- this.redoContent = new String(StringContent.this.content, this.start,
- this.length);
- StringContent.this.remove(this.start, this.length);
+ if (marks != null)
+ positions = getPositionsInRange(null, start, length);
+ redoContent = getString(start, length);
+ remove(start, length);
}
catch (BadLocationException b)
{
@@ -103,7 +183,13 @@ public final class StringContent
super.redo();
try
{
- StringContent.this.insertString(this.start, this.redoContent);
+ insertString(start, redoContent);
+ redoContent = null;
+ if (positions != null)
+ {
+ updateUndoPositions(positions);
+ positions = null;
+ }
}
catch (BadLocationException b)
{
@@ -115,14 +201,19 @@ public final class StringContent
private class RemoveUndo extends AbstractUndoableEdit
{
private int start;
-
+ private int len;
private String undoString;
+ Vector positions;
+
public RemoveUndo(int start, String str)
{
super();
this.start = start;
+ len = str.length();
this.undoString = str;
+ if (marks != null)
+ positions = getPositionsInRange(null, start, str.length());
}
public void undo()
@@ -131,6 +222,12 @@ public final class StringContent
try
{
StringContent.this.insertString(this.start, this.undoString);
+ if (positions != null)
+ {
+ updateUndoPositions(positions);
+ positions = null;
+ }
+ undoString = null;
}
catch (BadLocationException bad)
{
@@ -143,8 +240,10 @@ public final class StringContent
super.redo();
try
{
- int end = this.undoString.length();
- StringContent.this.remove(this.start, end);
+ undoString = getString(start, len);
+ if (marks != null)
+ positions = getPositionsInRange(null, start, len);
+ remove(this.start, len);
}
catch (BadLocationException bad)
{
@@ -155,17 +254,18 @@ public final class StringContent
private class StickyPosition implements Position
{
- private int offset = -1;
+ Mark mark;
public StickyPosition(int offset)
{
- this.offset = offset;
- }
+ // Try to make space.
+ garbageCollect();
- // This is package-private to avoid an accessor method.
- void setOffset(int offset)
- {
- this.offset = this.offset >= 0 ? offset : -1;
+ mark = new Mark(offset);
+ mark.refCount++;
+ marks.add(mark);
+
+ new WeakReference(this, queueOfDeath);
}
/**
@@ -173,11 +273,25 @@ public final class StringContent
*/
public int getOffset()
{
- return offset < 0 ? 0 : offset;
+ return mark.mark;
}
}
/**
+ * Used in {@link #remove(int,int)}.
+ */
+ private static final char[] EMPTY = new char[0];
+
+ /**
+ * Queues all references to GapContentPositions that are about to be
+ * GC'ed. This is used to remove the corresponding marks from the
+ * positionMarks array if the number of references to that mark reaches zero.
+ *
+ * This is package private to avoid accessor synthetic methods.
+ */
+ ReferenceQueue queueOfDeath;
+
+ /**
* Creates a new instance containing the string "\n". This is equivalent
* to calling {@link #StringContent(int)} with an <code>initialLength</code>
* of 10.
@@ -196,6 +310,7 @@ public final class StringContent
public StringContent(int initialLength)
{
super();
+ queueOfDeath = new ReferenceQueue();
if (initialLength < 1)
initialLength = 1;
this.content = new char[initialLength];
@@ -207,14 +322,13 @@ public final class StringContent
int offset,
int length)
{
- Vector refPos = new Vector();
- Iterator iter = this.positions.iterator();
+ Vector refPos = v == null ? new Vector() : v;
+ Iterator iter = marks.iterator();
while(iter.hasNext())
{
- Position p = (Position) iter.next();
- if ((offset <= p.getOffset())
- && (p.getOffset() <= (offset + length)))
- refPos.add(p);
+ Mark m = (Mark) iter.next();
+ if (offset <= m.mark && m.mark <= offset + length)
+ refPos.add(new UndoPosRef(m));
}
return refPos;
}
@@ -231,10 +345,10 @@ public final class StringContent
*/
public Position createPosition(int offset) throws BadLocationException
{
- if (offset < this.count || offset > this.count)
- checkLocation(offset, 0);
+ // Lazily create marks vector.
+ if (marks == null)
+ marks = new Vector();
StickyPosition sp = new StickyPosition(offset);
- this.positions.add(sp);
return sp;
}
@@ -246,7 +360,7 @@ public final class StringContent
*/
public int length()
{
- return this.count;
+ return count;
}
/**
@@ -268,27 +382,23 @@ public final class StringContent
if (str == null)
throw new NullPointerException();
char[] insert = str.toCharArray();
- char[] temp = new char[this.content.length + insert.length];
- this.count += insert.length;
- // Copy array and insert the string.
- if (where > 0)
- System.arraycopy(this.content, 0, temp, 0, where);
- System.arraycopy(insert, 0, temp, where, insert.length);
- System.arraycopy(this.content, where, temp, (where + insert.length),
- (temp.length - where - insert.length));
- if (this.content.length < temp.length)
- this.content = new char[temp.length];
- // Copy the result in the original char array.
- System.arraycopy(temp, 0, this.content, 0, temp.length);
+ replace(where, 0, insert);
+
// Move all the positions.
- Vector refPos = getPositionsInRange(this.positions, where,
- temp.length - where);
- Iterator iter = refPos.iterator();
- while (iter.hasNext())
+ if (marks != null)
{
- StickyPosition p = (StickyPosition)iter.next();
- p.setOffset(p.getOffset() + str.length());
+ Iterator iter = marks.iterator();
+ int start = where;
+ if (start == 0)
+ start = 1;
+ while (iter.hasNext())
+ {
+ Mark m = (Mark) iter.next();
+ if (m.mark >= start)
+ m.mark += str.length();
+ }
}
+
InsertUndo iundo = new InsertUndo(where, insert.length);
return iundo;
}
@@ -308,32 +418,51 @@ public final class StringContent
public UndoableEdit remove(int where, int nitems) throws BadLocationException
{
checkLocation(where, nitems + 1);
- char[] temp = new char[(this.content.length - nitems)];
- this.count = this.count - nitems;
RemoveUndo rundo = new RemoveUndo(where, new String(this.content, where,
nitems));
- // Copy array.
- System.arraycopy(this.content, 0, temp, 0, where);
- System.arraycopy(this.content, where + nitems, temp, where,
- this.content.length - where - nitems);
- this.content = new char[temp.length];
- // Then copy the result in the original char array.
- System.arraycopy(temp, 0, this.content, 0, this.content.length);
+
+ replace(where, nitems, EMPTY);
// Move all the positions.
- Vector refPos = getPositionsInRange(this.positions, where,
- this.content.length + nitems - where);
- Iterator iter = refPos.iterator();
- while (iter.hasNext())
+ if (marks != null)
{
- StickyPosition p = (StickyPosition)iter.next();
- int result = p.getOffset() - nitems;
- p.setOffset(result);
- if (result < 0)
- this.positions.remove(p);
+ Iterator iter = marks.iterator();
+ while (iter.hasNext())
+ {
+ Mark m = (Mark) iter.next();
+ if (m.mark >= where + nitems)
+ m.mark -= nitems;
+ else if (m.mark >= where)
+ m.mark = where;
+ }
}
return rundo;
}
-
+
+ private void replace(int offs, int numRemove, char[] insert)
+ {
+ int insertLength = insert.length;
+ int delta = insertLength - numRemove;
+ int src = offs + numRemove;
+ int numMove = count - src;
+ int dest = src + delta;
+ if (count + delta >= content.length)
+ {
+ // Grow data array.
+ int newLength = Math.max(2 * content.length, count + delta);
+ char[] newContent = new char[newLength];
+ System.arraycopy(content, 0, newContent, 0, offs);
+ System.arraycopy(insert, 0, newContent, offs, insertLength);
+ System.arraycopy(content, src, newContent, dest, numMove);
+ content = newContent;
+ }
+ else
+ {
+ System.arraycopy(content, src, content, dest, numMove);
+ System.arraycopy(insert, 0, content, offs, insertLength);
+ }
+ count += delta;
+ }
+
/**
* Returns a new <code>String</code> containing the characters in the
* specified range.
@@ -348,6 +477,8 @@ public final class StringContent
*/
public String getString(int where, int len) throws BadLocationException
{
+ // The RI throws a StringIndexOutOfBoundsException here, which
+ // smells like a bug. We throw a BadLocationException instead.
checkLocation(where, len);
return new String(this.content, where, len);
}
@@ -368,22 +499,28 @@ public final class StringContent
public void getChars(int where, int len, Segment txt)
throws BadLocationException
{
- checkLocation(where, len);
- txt.array = this.content;
+ if (where + len > count)
+ throw new BadLocationException("Invalid location", where + len);
+ txt.array = content;
txt.offset = where;
txt.count = len;
}
/**
- * @specnote This method is not very well specified and the positions vector
- * is implementation specific. The undo positions are managed
- * differently in this implementation, this method is only here
- * for binary compatibility.
+ * Resets the positions in the specified vector to their original offset
+ * after a undo operation is performed. For example, after removing some
+ * content, the positions in the removed range will all be set to one
+ * offset. This method restores the positions to their original offsets
+ * after an undo.
*/
protected void updateUndoPositions(Vector positions)
{
- // We do nothing here.
+ for (Iterator i = positions.iterator(); i.hasNext();)
+ {
+ UndoPosRef pos = (UndoPosRef) i.next();
+ pos.reset();
+ }
}
/**
@@ -405,6 +542,29 @@ public final class StringContent
else if ((where + len) > this.count)
throw new BadLocationException("Invalid range", this.count);
}
-
+
+ /**
+ * Polls the queue of death for GapContentPositions, updates the
+ * corresponding reference count and removes the corresponding mark
+ * if the refcount reaches zero.
+ *
+ * This is package private to avoid accessor synthetic methods.
+ */
+ void garbageCollect()
+ {
+ Reference ref = queueOfDeath.poll();
+ while (ref != null)
+ {
+ if (ref != null)
+ {
+ StickyPosition pos = (StickyPosition) ref.get();
+ Mark m = pos.mark;
+ m.refCount--;
+ if (m.refCount == 0)
+ marks.remove(m);
+ }
+ ref = queueOfDeath.poll();
+ }
+ }
}
diff --git a/libjava/classpath/javax/swing/text/StyleConstants.java b/libjava/classpath/javax/swing/text/StyleConstants.java
index c7906b8ad32..4e5005c6bb2 100644
--- a/libjava/classpath/javax/swing/text/StyleConstants.java
+++ b/libjava/classpath/javax/swing/text/StyleConstants.java
@@ -40,6 +40,7 @@ package javax.swing.text;
import java.awt.Color;
import java.awt.Component;
+import java.util.ArrayList;
import javax.swing.Icon;
@@ -163,6 +164,12 @@ public class StyleConstants
public static final Object ResolveAttribute = new StyleConstants("resolver");
+ /**
+ * All StyleConstants keys. This is used in StyleContext to register
+ * all known keys as static attribute keys for serialization.
+ */
+ static ArrayList keys;
+
String keyname;
// Package-private to avoid accessor constructor for use by
@@ -170,6 +177,9 @@ public class StyleConstants
StyleConstants(String k)
{
keyname = k;
+ if (keys == null)
+ keys = new ArrayList();
+ keys.add(this);
}
/**
@@ -729,6 +739,7 @@ public class StyleConstants
*/
public static void setIcon(MutableAttributeSet a, Icon c)
{
+ a.addAttribute(AbstractDocument.ElementNameAttribute, IconElementName);
a.addAttribute(IconAttribute, c);
}
diff --git a/libjava/classpath/javax/swing/text/StyleContext.java b/libjava/classpath/javax/swing/text/StyleContext.java
index 63df3df6a91..4dded0d0402 100644
--- a/libjava/classpath/javax/swing/text/StyleContext.java
+++ b/libjava/classpath/javax/swing/text/StyleContext.java
@@ -43,19 +43,25 @@ import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Toolkit;
import java.io.IOException;
+import java.io.NotSerializableException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
+import java.lang.ref.WeakReference;
+import java.util.Collections;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.WeakHashMap;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.EventListenerList;
public class StyleContext
- implements Serializable, AbstractDocument.AttributeContext
+ implements Serializable, AbstractDocument.AttributeContext
{
/** The serialization UID (compatible with JDK1.5). */
private static final long serialVersionUID = 8042858831190784241L;
@@ -66,11 +72,10 @@ public class StyleContext
/** The serialization UID (compatible with JDK1.5). */
private static final long serialVersionUID = -6690628971806226374L;
- protected ChangeEvent changeEvent;
+ protected transient ChangeEvent changeEvent;
protected EventListenerList listenerList;
- AttributeSet attributes;
- String name;
+ private transient AttributeSet attributes;
public NamedStyle()
{
@@ -84,22 +89,26 @@ public class StyleContext
public NamedStyle(String name, Style parent)
{
- this.name = name;
- this.attributes = getEmptySet();
- this.changeEvent = new ChangeEvent(this);
- this.listenerList = new EventListenerList();
- setResolveParent(parent);
+ attributes = getEmptySet();
+ listenerList = new EventListenerList();
+ if (name != null)
+ setName(name);
+ if (parent != null)
+ setResolveParent(parent);
}
public String getName()
{
+ String name = null;
+ if (isDefined(StyleConstants.NameAttribute))
+ name = getAttribute(StyleConstants.NameAttribute).toString();
return name;
}
public void setName(String n)
{
- name = n;
- fireStateChanged();
+ if (n != null)
+ addAttribute(StyleConstants.NameAttribute, n);
}
public void addChangeListener(ChangeListener l)
@@ -112,7 +121,7 @@ public class StyleContext
listenerList.remove(ChangeListener.class, l);
}
- public EventListener[] getListeners(Class listenerType)
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
{
return listenerList.getListeners(listenerType);
}
@@ -127,6 +136,9 @@ public class StyleContext
ChangeListener[] listeners = getChangeListeners();
for (int i = 0; i < listeners.length; ++i)
{
+ // Lazily create event.
+ if (changeEvent == null)
+ changeEvent = new ChangeEvent(this);
listeners[i].stateChanged(changeEvent);
}
}
@@ -155,7 +167,10 @@ public class StyleContext
public AttributeSet copyAttributes()
{
- return attributes.copyAttributes();
+ // The RI returns a NamedStyle as copy, so do we.
+ NamedStyle copy = new NamedStyle();
+ copy.attributes = attributes.copyAttributes();
+ return copy;
}
public Object getAttribute(Object attrName)
@@ -168,7 +183,7 @@ public class StyleContext
return attributes.getAttributeCount();
}
- public Enumeration getAttributeNames()
+ public Enumeration<?> getAttributeNames()
{
return attributes.getAttributeNames();
}
@@ -195,7 +210,7 @@ public class StyleContext
fireStateChanged();
}
- public void removeAttributes(Enumeration names)
+ public void removeAttributes(Enumeration<?> names)
{
attributes = StyleContext.this.removeAttributes(attributes, names);
fireStateChanged();
@@ -210,112 +225,125 @@ public class StyleContext
public void setResolveParent(AttributeSet parent)
{
if (parent != null)
- {
- attributes = StyleContext.this.addAttribute
- (attributes, ResolveAttribute, parent);
- }
- fireStateChanged();
+ addAttribute(StyleConstants.ResolveAttribute, parent);
+ else
+ removeAttribute(StyleConstants.ResolveAttribute);
}
public String toString()
{
- return ("[NamedStyle: name=" + name + ", attrs=" + attributes.toString() + "]");
- }
+ return "NamedStyle:" + getName() + " " + attributes;
+ }
+
+ private void writeObject(ObjectOutputStream s)
+ throws IOException
+ {
+ s.defaultWriteObject();
+ writeAttributeSet(s, attributes);
+ }
+
+ private void readObject(ObjectInputStream s)
+ throws ClassNotFoundException, IOException
+ {
+ s.defaultReadObject();
+ attributes = SimpleAttributeSet.EMPTY;
+ readAttributeSet(s, this);
+ }
}
public class SmallAttributeSet
implements AttributeSet
{
final Object [] attrs;
+ private AttributeSet resolveParent;
public SmallAttributeSet(AttributeSet a)
{
- if (a == null)
- attrs = new Object[0];
- else
+ int n = a.getAttributeCount();
+ int i = 0;
+ attrs = new Object[n * 2];
+ Enumeration e = a.getAttributeNames();
+ while (e.hasMoreElements())
{
- int n = a.getAttributeCount();
- int i = 0;
- attrs = new Object[n * 2];
- Enumeration e = a.getAttributeNames();
- while (e.hasMoreElements())
- {
- Object name = e.nextElement();
- attrs[i++] = name;
- attrs[i++] = a.getAttribute(name);
- }
+ Object name = e.nextElement();
+ Object value = a.getAttribute(name);
+ if (name == ResolveAttribute)
+ resolveParent = (AttributeSet) value;
+ attrs[i++] = name;
+ attrs[i++] = value;
}
}
public SmallAttributeSet(Object [] a)
{
- if (a == null)
- attrs = new Object[0];
- else
+ attrs = a;
+ for (int i = 0; i < attrs.length; i += 2)
{
- attrs = new Object[a.length];
- System.arraycopy(a, 0, attrs, 0, a.length);
+ if (attrs[i] == ResolveAttribute)
+ resolveParent = (AttributeSet) attrs[i + 1];
}
}
public Object clone()
{
- return new SmallAttributeSet(this.attrs);
+ return this;
}
public boolean containsAttribute(Object name, Object value)
{
- for (int i = 0; i < attrs.length; i += 2)
- {
- if (attrs[i].equals(name) &&
- attrs[i+1].equals(value))
- return true;
- }
- return false;
+ return value.equals(getAttribute(name));
}
public boolean containsAttributes(AttributeSet a)
{
+ boolean res = true;
Enumeration e = a.getAttributeNames();
- while (e.hasMoreElements())
+ while (e.hasMoreElements() && res)
{
Object name = e.nextElement();
- Object val = a.getAttribute(name);
- if (!containsAttribute(name, val))
- return false;
+ res = a.getAttribute(name).equals(getAttribute(name));
}
- return true;
+ return res;
}
public AttributeSet copyAttributes()
{
- return (AttributeSet) clone();
+ return this;
}
public boolean equals(Object obj)
{
- return
- (obj instanceof AttributeSet)
- && this.isEqual((AttributeSet)obj);
+ boolean eq = false;
+ if (obj instanceof AttributeSet)
+ {
+ AttributeSet atts = (AttributeSet) obj;
+ eq = getAttributeCount() == atts.getAttributeCount()
+ && containsAttributes(atts);
+ }
+ return eq;
}
public Object getAttribute(Object key)
{
- for (int i = 0; i < attrs.length; i += 2)
+ Object att = null;
+ if (key == StyleConstants.ResolveAttribute)
+ att = resolveParent;
+
+ for (int i = 0; i < attrs.length && att == null; i += 2)
{
if (attrs[i].equals(key))
- return attrs[i+1];
+ att = attrs[i + 1];
}
-
+
// Check the resolve parent, unless we're looking for the
- // ResolveAttribute, which would cause an infinite loop
- if (!(key.equals(ResolveAttribute)))
+ // ResolveAttribute, which must not be looked up
+ if (att == null)
{
- Object p = getResolveParent();
- if (p != null && p instanceof AttributeSet)
- return (((AttributeSet)p).getAttribute(key));
+ AttributeSet parent = getResolveParent();
+ if (parent != null)
+ att = parent.getAttribute(key);
}
- return null;
+ return att;
}
public int getAttributeCount()
@@ -323,7 +351,7 @@ public class StyleContext
return attrs.length / 2;
}
- public Enumeration getAttributeNames()
+ public Enumeration<?> getAttributeNames()
{
return new Enumeration()
{
@@ -342,7 +370,7 @@ public class StyleContext
public AttributeSet getResolveParent()
{
- return (AttributeSet) getAttribute(ResolveAttribute);
+ return resolveParent;
}
public int hashCode()
@@ -362,68 +390,96 @@ public class StyleContext
public boolean isEqual(AttributeSet attr)
{
- return getAttributeCount() == attr.getAttributeCount()
+ boolean eq;
+ // If the other one is also a SmallAttributeSet, it is only considered
+ // equal if it's the same instance.
+ if (attr instanceof SmallAttributeSet)
+ eq = attr == this;
+ else
+ eq = getAttributeCount() == attr.getAttributeCount()
&& this.containsAttributes(attr);
+ return eq;
}
public String toString()
{
- StringBuffer sb = new StringBuffer();
- sb.append("[StyleContext.SmallattributeSet:");
- for (int i = 0; i < attrs.length - 1; ++i)
+ StringBuilder sb = new StringBuilder();
+ sb.append('{');
+ for (int i = 0; i < attrs.length; i += 2)
{
- sb.append(" (");
- sb.append(attrs[i].toString());
- sb.append("=");
- sb.append(attrs[i+1].toString());
- sb.append(")");
+ if (attrs[i + 1] instanceof AttributeSet)
+ {
+ sb.append(attrs[i]);
+ sb.append("=AttributeSet,");
+ }
+ else
+ {
+ sb.append(attrs[i]);
+ sb.append('=');
+ sb.append(attrs[i + 1]);
+ sb.append(',');
+ }
}
- sb.append("]");
+ sb.append("}");
return sb.toString();
}
}
- // FIXME: official javadocs suggest that these might be more usefully
- // implemented using a WeakHashMap, but not sure if that works most
- // places or whether it really matters anyways.
- //
- // FIXME: also not sure if these tables ought to be static (singletons),
- // shared across all StyleContexts. I think so, but it's not clear in
- // docs. revert to non-shared if you think it matters.
-
/**
- * The name of the default style.
+ * Register StyleConstant keys as static attribute keys for serialization.
*/
- public static final String DEFAULT_STYLE = "default";
-
+ static
+ {
+ // Don't let problems while doing this prevent class loading.
+ try
+ {
+ for (Iterator i = StyleConstants.keys.iterator(); i.hasNext();)
+ registerStaticAttributeKey(i.next());
+ }
+ catch (Throwable t)
+ {
+ t.printStackTrace();
+ }
+ }
+
/**
- * The default style for this style context.
+ * The name of the default style.
*/
- NamedStyle defaultStyle = new NamedStyle(DEFAULT_STYLE, null);
+ public static final String DEFAULT_STYLE = "default";
static Hashtable sharedAttributeSets = new Hashtable();
static Hashtable sharedFonts = new Hashtable();
- static StyleContext defaultStyleContext = new StyleContext();
+ static StyleContext defaultStyleContext;
static final int compressionThreshold = 9;
/**
* These attribute keys are handled specially in serialization.
*/
- private static Hashtable staticAttributeKeys = new Hashtable();
+ private static Hashtable writeAttributeKeys;
+ private static Hashtable readAttributeKeys;
+
+ private NamedStyle styles;
+
+ /**
+ * Used for searching attributes in the pool.
+ */
+ private transient MutableAttributeSet search = new SimpleAttributeSet();
+
+ /**
+ * A pool of immutable AttributeSets.
+ */
+ private transient Map attributeSetPool =
+ Collections.synchronizedMap(new WeakHashMap());
- EventListenerList listenerList;
- Hashtable styleTable;
-
/**
* Creates a new instance of the style context. Add the default style
* to the style table.
*/
public StyleContext()
{
- listenerList = new EventListenerList();
- styleTable = new Hashtable();
- styleTable.put(DEFAULT_STYLE, defaultStyle);
+ styles = new NamedStyle(null);
+ addStyle(DEFAULT_STYLE, null);
}
protected SmallAttributeSet createSmallAttributeSet(AttributeSet a)
@@ -438,30 +494,30 @@ public class StyleContext
public void addChangeListener(ChangeListener listener)
{
- listenerList.add(ChangeListener.class, listener);
+ styles.addChangeListener(listener);
}
public void removeChangeListener(ChangeListener listener)
{
- listenerList.remove(ChangeListener.class, listener);
+ styles.removeChangeListener(listener);
}
public ChangeListener[] getChangeListeners()
{
- return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
+ return styles.getChangeListeners();
}
public Style addStyle(String name, Style parent)
{
Style newStyle = new NamedStyle(name, parent);
if (name != null)
- styleTable.put(name, newStyle);
+ styles.addAttribute(name, newStyle);
return newStyle;
}
public void removeStyle(String name)
{
- styleTable.remove(name);
+ styles.removeAttribute(name);
}
/**
@@ -476,16 +532,31 @@ public class StyleContext
*/
public Style getStyle(String name)
{
- return (Style) styleTable.get(name);
+ return (Style) styles.getAttribute(name);
}
/**
* Get the names of the style. The returned enumeration always
* contains at least one member, the default style.
*/
- public Enumeration getStyleNames()
+ public Enumeration<?> getStyleNames()
+ {
+ return styles.getAttributeNames();
+ }
+
+ private void readObject(ObjectInputStream in)
+ throws ClassNotFoundException, IOException
{
- return styleTable.keys();
+ search = new SimpleAttributeSet();
+ attributeSetPool = Collections.synchronizedMap(new WeakHashMap());
+ in.defaultReadObject();
+ }
+
+ private void writeObject(ObjectOutputStream out)
+ throws IOException
+ {
+ cleanupPool();
+ out.defaultWriteObject();
}
//
@@ -577,132 +648,125 @@ public class StyleContext
public static StyleContext getDefaultStyleContext()
{
+ if (defaultStyleContext == null)
+ defaultStyleContext = new StyleContext();
return defaultStyleContext;
}
- public AttributeSet addAttribute(AttributeSet old, Object name, Object value)
+ public synchronized AttributeSet addAttribute(AttributeSet old, Object name,
+ Object value)
{
- if (old instanceof MutableAttributeSet)
+ AttributeSet ret;
+ if (old.getAttributeCount() + 1 < getCompressionThreshold())
{
- ((MutableAttributeSet)old).addAttribute(name, value);
- return old;
+ search.removeAttributes(search);
+ search.addAttributes(old);
+ search.addAttribute(name, value);
+ reclaim(old);
+ ret = searchImmutableSet();
}
- else
+ else
{
- MutableAttributeSet mutable = createLargeAttributeSet(old);
- mutable.addAttribute(name, value);
- if (mutable.getAttributeCount() >= getCompressionThreshold())
- return mutable;
- else
- {
- SmallAttributeSet small = createSmallAttributeSet(mutable);
- if (sharedAttributeSets.containsKey(small))
- small = (SmallAttributeSet) sharedAttributeSets.get(small);
- else
- sharedAttributeSets.put(small,small);
- return small;
- }
+ MutableAttributeSet mas = getMutableAttributeSet(old);
+ mas.addAttribute(name, value);
+ ret = mas;
}
+ return ret;
}
- public AttributeSet addAttributes(AttributeSet old, AttributeSet attributes)
+ public synchronized AttributeSet addAttributes(AttributeSet old,
+ AttributeSet attributes)
{
- if (old instanceof MutableAttributeSet)
+ AttributeSet ret;
+ if (old.getAttributeCount() + attributes.getAttributeCount()
+ < getCompressionThreshold())
{
- ((MutableAttributeSet)old).addAttributes(attributes);
- return old;
+ search.removeAttributes(search);
+ search.addAttributes(old);
+ search.addAttributes(attributes);
+ reclaim(old);
+ ret = searchImmutableSet();
}
- else
+ else
{
- MutableAttributeSet mutable = createLargeAttributeSet(old);
- mutable.addAttributes(attributes);
- if (mutable.getAttributeCount() >= getCompressionThreshold())
- return mutable;
- else
- {
- SmallAttributeSet small = createSmallAttributeSet(mutable);
- if (sharedAttributeSets.containsKey(small))
- small = (SmallAttributeSet) sharedAttributeSets.get(small);
- else
- sharedAttributeSets.put(small,small);
- return small;
- }
+ MutableAttributeSet mas = getMutableAttributeSet(old);
+ mas.addAttributes(attributes);
+ ret = mas;
}
+ return ret;
}
public AttributeSet getEmptySet()
{
- AttributeSet e = createSmallAttributeSet(null);
- if (sharedAttributeSets.containsKey(e))
- e = (AttributeSet) sharedAttributeSets.get(e);
- else
- sharedAttributeSets.put(e, e);
- return e;
+ return SimpleAttributeSet.EMPTY;
}
public void reclaim(AttributeSet attributes)
{
- if (sharedAttributeSets.containsKey(attributes))
- sharedAttributeSets.remove(attributes);
+ cleanupPool();
}
- public AttributeSet removeAttribute(AttributeSet old, Object name)
+ public synchronized AttributeSet removeAttribute(AttributeSet old,
+ Object name)
{
- if (old instanceof MutableAttributeSet)
+ AttributeSet ret;
+ if (old.getAttributeCount() - 1 <= getCompressionThreshold())
{
- ((MutableAttributeSet)old).removeAttribute(name);
- if (old.getAttributeCount() < getCompressionThreshold())
- {
- SmallAttributeSet small = createSmallAttributeSet(old);
- if (!sharedAttributeSets.containsKey(small))
- sharedAttributeSets.put(small,small);
- old = (AttributeSet) sharedAttributeSets.get(small);
- }
- return old;
+ search.removeAttributes(search);
+ search.addAttributes(old);
+ search.removeAttribute(name);
+ reclaim(old);
+ ret = searchImmutableSet();
}
- else
- {
- MutableAttributeSet mutable = createLargeAttributeSet(old);
- mutable.removeAttribute(name);
- SmallAttributeSet small = createSmallAttributeSet(mutable);
- if (sharedAttributeSets.containsKey(small))
- small = (SmallAttributeSet) sharedAttributeSets.get(small);
- else
- sharedAttributeSets.put(small,small);
- return small;
+ else
+ {
+ MutableAttributeSet mas = getMutableAttributeSet(old);
+ mas.removeAttribute(name);
+ ret = mas;
}
+ return ret;
}
- public AttributeSet removeAttributes(AttributeSet old, AttributeSet attributes)
+ public synchronized AttributeSet removeAttributes(AttributeSet old,
+ AttributeSet attributes)
{
- return removeAttributes(old, attributes.getAttributeNames());
+ AttributeSet ret;
+ if (old.getAttributeCount() <= getCompressionThreshold())
+ {
+ search.removeAttributes(search);
+ search.addAttributes(old);
+ search.removeAttributes(attributes);
+ reclaim(old);
+ ret = searchImmutableSet();
+ }
+ else
+ {
+ MutableAttributeSet mas = getMutableAttributeSet(old);
+ mas.removeAttributes(attributes);
+ ret = mas;
+ }
+ return ret;
}
- public AttributeSet removeAttributes(AttributeSet old, Enumeration names)
+ public synchronized AttributeSet removeAttributes(AttributeSet old,
+ Enumeration<?> names)
{
- if (old instanceof MutableAttributeSet)
+ AttributeSet ret;
+ if (old.getAttributeCount() <= getCompressionThreshold())
{
- ((MutableAttributeSet)old).removeAttributes(names);
- if (old.getAttributeCount() < getCompressionThreshold())
- {
- SmallAttributeSet small = createSmallAttributeSet(old);
- if (!sharedAttributeSets.containsKey(small))
- sharedAttributeSets.put(small,small);
- old = (AttributeSet) sharedAttributeSets.get(small);
- }
- return old;
+ search.removeAttributes(search);
+ search.addAttributes(old);
+ search.removeAttributes(names);
+ reclaim(old);
+ ret = searchImmutableSet();
}
- else
- {
- MutableAttributeSet mutable = createLargeAttributeSet(old);
- mutable.removeAttributes(names);
- SmallAttributeSet small = createSmallAttributeSet(mutable);
- if (sharedAttributeSets.containsKey(small))
- small = (SmallAttributeSet) sharedAttributeSets.get(small);
- else
- sharedAttributeSets.put(small,small);
- return small;
- }
+ else
+ {
+ MutableAttributeSet mas = getMutableAttributeSet(old);
+ mas.removeAttributes(names);
+ ret = mas;
+ }
+ return ret;
}
/**
@@ -715,7 +779,7 @@ public class StyleContext
{
if (key == null)
return null;
- return staticAttributeKeys.get(key);
+ return readAttributeKeys.get(key);
}
/**
@@ -742,27 +806,25 @@ public class StyleContext
* stream
* @throws IOException - any I/O error
*/
- public static void readAttributeSet(ObjectInputStream in, MutableAttributeSet a)
+ public static void readAttributeSet(ObjectInputStream in,
+ MutableAttributeSet a)
throws ClassNotFoundException, IOException
{
- if (in == null || a == null)
- return;
-
- Object key = in.readObject();
- Object val = in.readObject();
- while (key != null && val != null)
+ int count = in.readInt();
+ for (int i = 0; i < count; i++)
{
- Object staticKey = staticAttributeKeys.get(key);
- Object staticVal = staticAttributeKeys.get(val);
-
- if (staticKey != null)
- key = staticKey;
- if (staticVal != null)
- val = staticVal;
-
+ Object key = in.readObject();
+ Object val = in.readObject();
+ if (readAttributeKeys != null)
+ {
+ Object staticKey = readAttributeKeys.get(key);
+ if (staticKey != null)
+ key = staticKey;
+ Object staticVal = readAttributeKeys.get(val);
+ if (staticVal != null)
+ val = staticVal;
+ }
a.addAttribute(key, val);
- key = in.readObject();
- val = in.readObject();
}
}
@@ -778,18 +840,35 @@ public class StyleContext
public static void writeAttributeSet(ObjectOutputStream out, AttributeSet a)
throws IOException
{
+ int count = a.getAttributeCount();
+ out.writeInt(count);
Enumeration e = a.getAttributeNames();
while (e.hasMoreElements())
{
- Object oldKey = e.nextElement();
- Object newKey = getStaticAttribute(oldKey);
- Object key = (newKey == null) ? oldKey : newKey;
-
- out.writeObject(key);
- out.writeObject(a.getAttribute(oldKey));
+ Object key = e.nextElement();
+ // Write key.
+ if (key instanceof Serializable)
+ out.writeObject(key);
+ else
+ {
+ Object io = writeAttributeKeys.get(key);
+ if (io == null)
+ throw new NotSerializableException(key.getClass().getName()
+ + ", key: " + key);
+ out.writeObject(io);
+ }
+ // Write value.
+ Object val = a.getAttribute(key);
+ Object io = writeAttributeKeys.get(val);
+ if (val instanceof Serializable)
+ out.writeObject(io != null ? io : val);
+ else
+ {
+ if (io == null)
+ throw new NotSerializableException(val.getClass().getName());
+ out.writeObject(io);
+ }
}
- out.writeObject(null);
- out.writeObject(null);
}
/**
@@ -833,8 +912,79 @@ public class StyleContext
*/
public static void registerStaticAttributeKey(Object key)
{
- if (key != null)
- staticAttributeKeys.put(key.getClass().getName() + "." + key.toString(),
- key);
+ String io = key.getClass().getName() + "." + key.toString();
+ if (writeAttributeKeys == null)
+ writeAttributeKeys = new Hashtable();
+ if (readAttributeKeys == null)
+ readAttributeKeys = new Hashtable();
+ writeAttributeKeys.put(key, io);
+ readAttributeKeys.put(io, key);
+ }
+
+ /**
+ * Returns a string representation of this StyleContext.
+ *
+ * @return a string representation of this StyleContext
+ */
+ public String toString()
+ {
+ cleanupPool();
+ StringBuilder b = new StringBuilder();
+ Iterator i = attributeSetPool.keySet().iterator();
+ while (i.hasNext())
+ {
+ Object att = i.next();
+ b.append(att);
+ b.append('\n');
+ }
+ return b.toString();
+ }
+
+ /**
+ * Searches the AttributeSet pool and returns a pooled instance if available,
+ * or pool a new one.
+ *
+ * @return an immutable attribute set that equals the current search key
+ */
+ private AttributeSet searchImmutableSet()
+ {
+ SmallAttributeSet k = createSmallAttributeSet(search);
+ WeakReference ref = (WeakReference) attributeSetPool.get(k);
+ SmallAttributeSet a;
+ if (ref == null || (a = (SmallAttributeSet) ref.get()) == null)
+ {
+ a = k;
+ attributeSetPool.put(a, new WeakReference(a));
+ }
+ return a;
+ }
+
+ /**
+ * Cleans up the attribute set pool from entries that are no longer
+ * referenced.
+ */
+ private void cleanupPool()
+ {
+ // TODO: How else can we force cleaning up the WeakHashMap?
+ attributeSetPool.size();
+ }
+
+ /**
+ * Returns a MutableAttributeSet that holds a. If a itself is mutable,
+ * this returns a itself, otherwise it creates a new SimpleAtttributeSet
+ * via {@link #createLargeAttributeSet(AttributeSet)}.
+ *
+ * @param a the AttributeSet to create a mutable set for
+ *
+ * @return a mutable attribute set that corresponds to a
+ */
+ private MutableAttributeSet getMutableAttributeSet(AttributeSet a)
+ {
+ MutableAttributeSet mas;
+ if (a instanceof MutableAttributeSet)
+ mas = (MutableAttributeSet) a;
+ else
+ mas = createLargeAttributeSet(a);
+ return mas;
}
}
diff --git a/libjava/classpath/javax/swing/text/StyledEditorKit.java b/libjava/classpath/javax/swing/text/StyledEditorKit.java
index c4eef4463fb..568694387f3 100644
--- a/libjava/classpath/javax/swing/text/StyledEditorKit.java
+++ b/libjava/classpath/javax/swing/text/StyledEditorKit.java
@@ -142,7 +142,7 @@ public class StyledEditorKit extends DefaultEditorKit
Element el = doc.getCharacterElement(editor.getSelectionStart());
boolean isBold = StyleConstants.isBold(el.getAttributes());
SimpleAttributeSet atts = new SimpleAttributeSet();
- StyleConstants.setItalic(atts, ! isBold);
+ StyleConstants.setBold(atts, ! isBold);
setCharacterAttributes(editor, atts, false);
}
}
@@ -335,35 +335,21 @@ public class StyledEditorKit extends DefaultEditorKit
AttributeSet atts,
boolean replace)
{
- Document doc = editor.getDocument();
- if (doc instanceof StyledDocument)
- {
- StyledDocument styleDoc = (StyledDocument) editor.getDocument();
- EditorKit kit = editor.getEditorKit();
- if (!(kit instanceof StyledEditorKit))
- {
- StyledEditorKit styleKit = (StyledEditorKit) kit;
- int start = editor.getSelectionStart();
- int end = editor.getSelectionEnd();
- int dot = editor.getCaret().getDot();
- if (start == dot && end == dot)
- {
- // If there is no selection, then we only update the
- // input attributes.
- MutableAttributeSet inputAttributes =
- styleKit.getInputAttributes();
- inputAttributes.addAttributes(atts);
- }
- else
- styleDoc.setCharacterAttributes(start, end, atts, replace);
- }
- else
- throw new AssertionError("The EditorKit for StyledTextActions "
- + "is expected to be a StyledEditorKit");
- }
- else
- throw new AssertionError("The Document for StyledTextActions is "
- + "expected to be a StyledDocument.");
+ int p0 = editor.getSelectionStart();
+ int p1 = editor.getSelectionEnd();
+ if (p0 != p1)
+ {
+ StyledDocument doc = getStyledDocument(editor);
+ doc.setCharacterAttributes(p0, p1 - p0, atts, replace);
+ }
+ // Update input attributes.
+ StyledEditorKit kit = getStyledEditorKit(editor);
+ MutableAttributeSet inputAtts = kit.getInputAttributes();
+ if (replace)
+ {
+ inputAtts.removeAttributes(inputAtts);
+ }
+ inputAtts.addAttributes(atts);
}
/**
diff --git a/libjava/classpath/javax/swing/text/TextAction.java b/libjava/classpath/javax/swing/text/TextAction.java
index 144166e9cdb..49c49cb9d7f 100644
--- a/libjava/classpath/javax/swing/text/TextAction.java
+++ b/libjava/classpath/javax/swing/text/TextAction.java
@@ -38,14 +38,15 @@ exception statement from your version. */
package javax.swing.text;
+import java.awt.Component;
+import java.awt.KeyboardFocusManager;
import java.awt.Point;
import java.awt.event.ActionEvent;
-import java.util.ArrayList;
-import java.util.HashSet;
+import java.util.HashMap;
+import java.util.Iterator;
import javax.swing.AbstractAction;
import javax.swing.Action;
-import javax.swing.SwingConstants;
/**
* TextAction
@@ -73,10 +74,16 @@ public abstract class TextAction extends AbstractAction
*/
protected final JTextComponent getTextComponent(ActionEvent event)
{
- if (event.getSource() instanceof JTextComponent)
- return (JTextComponent) event.getSource();
-
- return getFocusedComponent();
+ JTextComponent target = null;
+ if (event != null)
+ {
+ Object source = event.getSource();
+ if (source instanceof JTextComponent)
+ target = (JTextComponent) source;
+ }
+ if (target == null)
+ target = getFocusedComponent();
+ return target;
}
/**
@@ -89,16 +96,28 @@ public abstract class TextAction extends AbstractAction
*/
public static final Action[] augmentList(Action[] list1, Action[] list2)
{
- HashSet actionSet = new HashSet();
+ HashMap<Object,Action> actions = new HashMap<Object,Action>();
for (int i = 0; i < list1.length; ++i)
- actionSet.add(list1[i]);
+ {
+ Action a = list1[i];
+ Object name = a.getValue(Action.NAME);
+ actions.put(name != null ? name : "", a);
+ }
for (int i = 0; i < list2.length; ++i)
- actionSet.add(list2[i]);
+ {
+ Action a = list2[i];
+ Object name = a.getValue(Action.NAME);
+ actions.put(name != null ? name : "", a);
+ }
+ Action[] augmented = new Action[actions.size()];
+
+ int i = 0;
+ for (Iterator<Action> it = actions.values().iterator(); it.hasNext(); i++)
+ augmented[i] = it.next();
+ return augmented;
- ArrayList list = new ArrayList(actionSet);
- return (Action[]) list.toArray(new Action[actionSet.size()]);
}
/**
@@ -108,7 +127,13 @@ public abstract class TextAction extends AbstractAction
*/
protected final JTextComponent getFocusedComponent()
{
- return null; // TODO
+ KeyboardFocusManager kfm =
+ KeyboardFocusManager.getCurrentKeyboardFocusManager();
+ Component focused = kfm.getPermanentFocusOwner();
+ JTextComponent textComp = null;
+ if (focused instanceof JTextComponent)
+ textComp = (JTextComponent) focused;
+ return textComp;
}
/** Abstract helper class which implements everything needed for an
diff --git a/libjava/classpath/javax/swing/text/Utilities.java b/libjava/classpath/javax/swing/text/Utilities.java
index 36361f49796..d49d806cfa3 100644
--- a/libjava/classpath/javax/swing/text/Utilities.java
+++ b/libjava/classpath/javax/swing/text/Utilities.java
@@ -43,7 +43,6 @@ import java.awt.Graphics;
import java.awt.Point;
import java.text.BreakIterator;
-import javax.swing.SwingConstants;
import javax.swing.text.Position.Bias;
/**
@@ -55,10 +54,6 @@ import javax.swing.text.Position.Bias;
*/
public class Utilities
{
- /**
- * The length of the char buffer that holds the characters to be drawn.
- */
- private static final int BUF_LENGTH = 64;
/**
* Creates a new <code>Utilities</code> object.
@@ -94,13 +89,12 @@ public class Utilities
// The font metrics of the current selected font.
FontMetrics metrics = g.getFontMetrics();
+
int ascent = metrics.getAscent();
// The current x and y pixel coordinates.
int pixelX = x;
- int pixelY = y - ascent;
- int pixelWidth = 0;
int pos = s.offset;
int len = 0;
@@ -109,44 +103,43 @@ public class Utilities
for (int offset = s.offset; offset < end; ++offset)
{
char c = buffer[offset];
- if (c == '\t' || c == '\n')
+ switch (c)
{
+ case '\t':
if (len > 0) {
- g.drawChars(buffer, pos, len, pixelX, pixelY + ascent);
- pixelX += pixelWidth;
- pixelWidth = 0;
+ g.drawChars(buffer, pos, len, pixelX, y);
+ pixelX += metrics.charsWidth(buffer, pos, len);
+ len = 0;
}
pos = offset+1;
- len = 0;
+ if (e != null)
+ pixelX = (int) e.nextTabStop((float) pixelX, startOffset + offset
+ - s.offset);
+ else
+ pixelX += metrics.charWidth(' ');
+ x = pixelX;
+ break;
+ case '\n':
+ case '\r':
+ if (len > 0) {
+ g.drawChars(buffer, pos, len, pixelX, y);
+ pixelX += metrics.charsWidth(buffer, pos, len);
+ len = 0;
+ }
+ x = pixelX;
+ break;
+ default:
+ len += 1;
}
-
- switch (c)
- {
- case '\t':
- // In case we have a tab, we just 'jump' over the tab.
- // When we have no tab expander we just use the width of ' '.
- if (e != null)
- pixelX = (int) e.nextTabStop((float) pixelX,
- startOffset + offset - s.offset);
- else
- pixelX += metrics.charWidth(' ');
- break;
- case '\n':
- // In case we have a newline, we must jump to the next line.
- pixelY += metrics.getHeight();
- pixelX = x;
- break;
- default:
- ++len;
- pixelWidth += metrics.charWidth(buffer[offset]);
- break;
- }
}
if (len > 0)
- g.drawChars(buffer, pos, len, pixelX, pixelY + ascent);
+ {
+ g.drawChars(buffer, pos, len, pixelX, y);
+ pixelX += metrics.charsWidth(buffer, pos, len);
+ }
- return pixelX + pixelWidth;
+ return pixelX;
}
/**
@@ -174,7 +167,9 @@ public class Utilities
// The current maximum width.
int maxWidth = 0;
- for (int offset = s.offset; offset < (s.offset + s.count); ++offset)
+ int end = s.offset + s.count;
+ int count = 0;
+ for (int offset = s.offset; offset < end; offset++)
{
switch (buffer[offset])
{
@@ -182,7 +177,7 @@ public class Utilities
// In case we have a tab, we just 'jump' over the tab.
// When we have no tab expander we just use the width of 'm'.
if (e != null)
- pixelX = (int) e.nextTabStop((float) pixelX,
+ pixelX = (int) e.nextTabStop(pixelX,
startOffset + offset - s.offset);
else
pixelX += metrics.charWidth(' ');
@@ -190,21 +185,18 @@ public class Utilities
case '\n':
// In case we have a newline, we must 'draw'
// the buffer and jump on the next line.
- pixelX += metrics.charWidth(buffer[offset]);
- maxWidth = Math.max(maxWidth, pixelX - x);
- pixelX = x;
- break;
- default:
- // Here we draw the char.
- pixelX += metrics.charWidth(buffer[offset]);
- break;
- }
+ pixelX += metrics.charsWidth(buffer, offset - count, count);
+ count = 0;
+ break;
+ default:
+ count++;
+ }
}
// Take the last line into account.
- maxWidth = Math.max(maxWidth, pixelX - x);
+ pixelX += metrics.charsWidth(buffer, end - count, count);
- return maxWidth;
+ return pixelX - x;
}
/**
@@ -239,43 +231,41 @@ public class Utilities
int x, TabExpander te, int p0,
boolean round)
{
- // At the end of the for loop, this holds the requested model location
- int pos;
+ int found = s.count;
int currentX = x0;
- int width = 0;
+ int nextX = currentX;
- for (pos = 0; pos < s.count; pos++)
+ int end = s.offset + s.count;
+ for (int pos = s.offset; pos < end && found == s.count; pos++)
{
- char nextChar = s.array[s.offset+pos];
-
- if (nextChar == 0)
- break;
+ char nextChar = s.array[pos];
if (nextChar != '\t')
- width = fm.charWidth(nextChar);
+ nextX += fm.charWidth(nextChar);
else
{
if (te == null)
- width = fm.charWidth(' ');
+ nextX += fm.charWidth(' ');
else
- width = ((int) te.nextTabStop(currentX, pos)) - currentX;
+ nextX += ((int) te.nextTabStop(nextX, p0 + pos - s.offset));
}
- if (round)
+ if (x >= currentX && x < nextX)
{
- if (currentX + (width>>1) > x)
- break;
- }
- else
- {
- if (currentX + width > x)
- break;
+ // Found position.
+ if ((! round) || ((x - currentX) < (nextX - x)))
+ {
+ found = pos - s.offset;
+ }
+ else
+ {
+ found = pos + 1 - s.offset;
+ }
}
-
- currentX += width;
+ currentX = nextX;
}
- return pos + p0;
+ return found;
}
/**
@@ -543,28 +533,39 @@ public class Utilities
int x0, int x, TabExpander e,
int startOffset)
{
- int mark = Utilities.getTabbedTextOffset(s, metrics, x0, x, e, startOffset, false);
- BreakIterator breaker = BreakIterator.getWordInstance();
- breaker.setText(s);
-
- // If startOffset and s.offset differ then we need to use
- // that difference two convert the offset between the two metrics.
- int shift = startOffset - s.offset;
-
+ int mark = Utilities.getTabbedTextOffset(s, metrics, x0, x, e, startOffset,
+ false);
+ int breakLoc = mark;
// If mark is equal to the end of the string, just use that position.
- if (mark >= shift + s.count)
- return mark;
-
- // Try to find a word boundary previous to the mark at which we
- // can break the text.
- int preceding = breaker.preceding(mark + 1 - shift);
-
- if (preceding != 0)
- return preceding + shift;
-
- // If preceding is 0 we couldn't find a suitable word-boundary so
- // just break it on the character boundary
- return mark;
+ if (mark < s.count - 1)
+ {
+ for (int i = s.offset + mark; i >= s.offset; i--)
+ {
+ char ch = s.array[i];
+ if (ch < 256)
+ {
+ // For ASCII simply scan backwards for whitespace.
+ if (Character.isWhitespace(ch))
+ {
+ breakLoc = i - s.offset + 1;
+ break;
+ }
+ }
+ else
+ {
+ // Only query BreakIterator for complex chars.
+ BreakIterator bi = BreakIterator.getLineInstance();
+ bi.setText(s);
+ int pos = bi.preceding(i + 1);
+ if (pos > s.offset)
+ {
+ breakLoc = breakLoc - s.offset;
+ }
+ break;
+ }
+ }
+ }
+ return breakLoc;
}
/**
@@ -712,12 +713,12 @@ public class Utilities
offset,
Bias.Forward,
direction,
- null)
+ new Position.Bias[1])
: t.getUI().getNextVisualPositionFrom(t,
offset,
Bias.Forward,
direction,
- null);
+ new Position.Bias[1]);
}
catch (BadLocationException ble)
{
diff --git a/libjava/classpath/javax/swing/text/View.java b/libjava/classpath/javax/swing/text/View.java
index 55a63f6b668..c63ddbce776 100644
--- a/libjava/classpath/javax/swing/text/View.java
+++ b/libjava/classpath/javax/swing/text/View.java
@@ -57,7 +57,6 @@ public abstract class View implements SwingConstants
public static final int X_AXIS = 0;
public static final int Y_AXIS = 1;
- private float width, height;
private Element elt;
private View parent;
@@ -93,7 +92,14 @@ public abstract class View implements SwingConstants
{
int numChildren = getViewCount();
for (int i = 0; i < numChildren; i++)
- getView(i).setParent(null);
+ {
+ View child = getView(i);
+ // It is important that we only reset the parent on views that
+ // actually belong to us. In FlowView the child may already be
+ // reparented.
+ if (child.getParent() == this)
+ child.setParent(null);
+ }
}
this.parent = parent;
@@ -263,7 +269,7 @@ public abstract class View implements SwingConstants
public void removeAll()
{
- replace(0, getViewCount(), new View[0]);
+ replace(0, getViewCount(), null);
}
public void remove(int index)
@@ -307,15 +313,16 @@ public abstract class View implements SwingConstants
{
int index = getViewIndex(x, y, allocation);
- if (index < -1)
- return null;
-
- Shape childAllocation = getChildAllocation(index, allocation);
-
- if (childAllocation.getBounds().contains(x, y))
- return getView(index).getToolTipText(x, y, childAllocation);
-
- return null;
+ String text = null;
+ if (index >= 0)
+ {
+ allocation = getChildAllocation(index, allocation);
+ Rectangle r = allocation instanceof Rectangle ? (Rectangle) allocation
+ : allocation.getBounds();
+ if (r.contains(x, y))
+ text = getView(index).getToolTipText(x, y, allocation);
+ }
+ return text;
}
/**
@@ -328,13 +335,17 @@ public abstract class View implements SwingConstants
public void preferenceChanged(View child, boolean width, boolean height)
{
- if (parent != null)
- parent.preferenceChanged(this, width, height);
+ View p = getParent();
+ if (p != null)
+ p.preferenceChanged(this, width, height);
}
public int getBreakWeight(int axis, float pos, float len)
{
- return BadBreakWeight;
+ int weight = BadBreakWeight;
+ if (len > getPreferredSpan(axis))
+ weight = GoodBreakWeight;
+ return weight;
}
public View breakView(int axis, int offset, float pos, float len)
@@ -370,12 +381,18 @@ public abstract class View implements SwingConstants
*/
public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
{
- Element el = getElement();
- DocumentEvent.ElementChange ec = ev.getChange(el);
- if (ec != null)
- updateChildren(ec, ev, vf);
- forwardUpdate(ec, ev, shape, vf);
- updateLayout(ec, ev, shape);
+ if (getViewCount() > 0)
+ {
+ Element el = getElement();
+ DocumentEvent.ElementChange ec = ev.getChange(el);
+ if (ec != null)
+ {
+ if (! updateChildren(ec, ev, vf))
+ ec = null;
+ }
+ forwardUpdate(ec, ev, shape, vf);
+ updateLayout(ec, ev, shape);
+ }
}
/**
@@ -429,12 +446,18 @@ public abstract class View implements SwingConstants
*/
public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
{
- Element el = getElement();
- DocumentEvent.ElementChange ec = ev.getChange(el);
- if (ec != null)
- updateChildren(ec, ev, vf);
- forwardUpdate(ec, ev, shape, vf);
- updateLayout(ec, ev, shape);
+ if (getViewCount() > 0)
+ {
+ Element el = getElement();
+ DocumentEvent.ElementChange ec = ev.getChange(el);
+ if (ec != null)
+ {
+ if (! updateChildren(ec, ev, vf))
+ ec = null;
+ }
+ forwardUpdate(ec, ev, shape, vf);
+ updateLayout(ec, ev, shape);
+ }
}
/**
@@ -465,10 +488,15 @@ public abstract class View implements SwingConstants
Element[] removed = ec.getChildrenRemoved();
int index = ec.getIndex();
- View[] newChildren = new View[added.length];
- for (int i = 0; i < added.length; ++i)
- newChildren[i] = vf.create(added[i]);
- replace(index, removed.length, newChildren);
+ View[] newChildren = null;
+ if (added != null)
+ {
+ newChildren = new View[added.length];
+ for (int i = 0; i < added.length; ++i)
+ newChildren[i] = vf.create(added[i]);
+ }
+ int numRemoved = removed != null ? removed.length : 0;
+ replace(index, numRemoved, newChildren);
return true;
}
@@ -598,10 +626,12 @@ public abstract class View implements SwingConstants
DocumentEvent ev, Shape shape)
{
if (ec != null && shape != null)
- preferenceChanged(null, true, true);
- Container c = getContainer();
- if (c != null)
- c.repaint();
+ {
+ preferenceChanged(null, true, true);
+ Container c = getContainer();
+ if (c != null)
+ c.repaint();
+ }
}
/**
@@ -750,7 +780,9 @@ public abstract class View implements SwingConstants
*/
public int viewToModel(float x, float y, Shape a)
{
- return viewToModel(x, y, a, new Position.Bias[0]);
+ Position.Bias[] biasRet = new Position.Bias[1];
+ biasRet[0] = Position.Bias.Forward;
+ return viewToModel(x, y, a, biasRet);
}
/**
diff --git a/libjava/classpath/javax/swing/text/WrappedPlainView.java b/libjava/classpath/javax/swing/text/WrappedPlainView.java
index a6c369a4c25..00e12b1129e 100644
--- a/libjava/classpath/javax/swing/text/WrappedPlainView.java
+++ b/libjava/classpath/javax/swing/text/WrappedPlainView.java
@@ -83,7 +83,17 @@ public class WrappedPlainView extends BoxView implements TabExpander
/** The height of the line (used while painting) **/
int lineHeight;
-
+
+ /**
+ * The base offset for tab calculations.
+ */
+ private int tabBase;
+
+ /**
+ * The tab size.
+ */
+ private int tabSize;
+
/**
* The instance returned by {@link #getLineBuffer()}.
*/
@@ -121,10 +131,13 @@ public class WrappedPlainView extends BoxView implements TabExpander
*/
public float nextTabStop(float x, int tabStop)
{
- JTextComponent host = (JTextComponent)getContainer();
- float tabSizePixels = getTabSize()
- * host.getFontMetrics(host.getFont()).charWidth('m');
- return (float) (Math.floor(x / tabSizePixels) + 1) * tabSizePixels;
+ int next = (int) x;
+ if (tabSize != 0)
+ {
+ int numTabs = ((int) x - tabBase) / tabSize;
+ next = tabBase + (numTabs + 1) * tabSize;
+ }
+ return next;
}
/**
@@ -274,44 +287,32 @@ public class WrappedPlainView extends BoxView implements TabExpander
*/
protected int calculateBreakPosition(int p0, int p1)
{
- Container c = getContainer();
-
- int li = getLeftInset();
- int ti = getTopInset();
-
- Rectangle alloc = new Rectangle(li, ti,
- getWidth()-getRightInset()-li,
- getHeight()-getBottomInset()-ti);
-
- // Mimic a behavior observed in the RI.
- if (alloc.isEmpty())
- return 0;
-
- updateMetrics();
-
+ Segment s = new Segment();
try
{
- getDocument().getText(p0, p1 - p0, getLineBuffer());
+ getDocument().getText(p0, p1 - p0, s);
}
- catch (BadLocationException ble)
+ catch (BadLocationException ex)
{
- // this shouldn't happen
- throw new InternalError("Invalid offsets p0: " + p0 + " - p1: " + p1);
+ assert false : "Couldn't load text";
}
-
+ int width = getWidth();
+ int pos;
if (wordWrap)
- return Utilities.getBreakLocation(lineBuffer, metrics, alloc.x,
- alloc.x + alloc.width, this, p0);
+ pos = p0 + Utilities.getBreakLocation(s, metrics, tabBase,
+ tabBase + width, this, p0);
else
- return p0 + Utilities.getTabbedTextOffset(lineBuffer, metrics, alloc.x,
- alloc.x + alloc.width, this, 0,
- true);
+ pos = p0 + Utilities.getTabbedTextOffset(s, metrics, tabBase,
+ tabBase + width, this, p0,
+ false);
+ return pos;
}
void updateMetrics()
{
Container component = getContainer();
metrics = component.getFontMetrics(component.getFont());
+ tabSize = getTabSize()* metrics.charWidth('m');
}
/**
@@ -350,9 +351,15 @@ public class WrappedPlainView extends BoxView implements TabExpander
*/
public void insertUpdate (DocumentEvent e, Shape a, ViewFactory f)
{
- super.insertUpdate(e, a, viewFactory);
+ // Update children efficiently.
+ updateChildren(e, a);
- // No repaint needed, as this is done by the WrappedLine instances.
+ // Notify children.
+ Rectangle r = a != null && isAllocationValid() ? getInsideAllocation(a)
+ : null;
+ View v = getViewAtPosition(e.getOffset(), r);
+ if (v != null)
+ v.insertUpdate(e, r, f);
}
/**
@@ -361,9 +368,15 @@ public class WrappedPlainView extends BoxView implements TabExpander
*/
public void removeUpdate (DocumentEvent e, Shape a, ViewFactory f)
{
- super.removeUpdate(e, a, viewFactory);
-
- // No repaint needed, as this is done by the WrappedLine instances.
+ // Update children efficiently.
+ updateChildren(e, a);
+
+ // Notify children.
+ Rectangle r = a != null && isAllocationValid() ? getInsideAllocation(a)
+ : null;
+ View v = getViewAtPosition(e.getOffset(), r);
+ if (v != null)
+ v.removeUpdate(e, r, f);
}
/**
@@ -373,11 +386,39 @@ public class WrappedPlainView extends BoxView implements TabExpander
*/
public void changedUpdate (DocumentEvent e, Shape a, ViewFactory f)
{
- super.changedUpdate(e, a, viewFactory);
-
- // No repaint needed, as this is done by the WrappedLine instances.
+ // Update children efficiently.
+ updateChildren(e, a);
}
-
+
+ /**
+ * Helper method. Updates the child views in response to
+ * insert/remove/change updates. This is here to be a little more efficient
+ * than the BoxView implementation.
+ *
+ * @param ev the document event
+ * @param a the shape
+ */
+ private void updateChildren(DocumentEvent ev, Shape a)
+ {
+ Element el = getElement();
+ DocumentEvent.ElementChange ec = ev.getChange(el);
+ if (ec != null)
+ {
+ Element[] removed = ec.getChildrenRemoved();
+ Element[] added = ec.getChildrenAdded();
+ View[] addedViews = new View[added.length];
+ for (int i = 0; i < added.length; i++)
+ addedViews[i] = new WrappedLine(added[i]);
+ replace(ec.getIndex(), removed.length, addedViews);
+ if (a != null)
+ {
+ preferenceChanged(null, true, true);
+ getContainer().repaint();
+ }
+ }
+ updateMetrics();
+ }
+
class WrappedLineCreator implements ViewFactory
{
// Creates a new WrappedLine
@@ -397,6 +438,9 @@ public class WrappedPlainView extends BoxView implements TabExpander
*/
public void paint(Graphics g, Shape a)
{
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ tabBase = r.x;
+
JTextComponent comp = (JTextComponent)getContainer();
// Ensure metrics are up-to-date.
updateMetrics();
@@ -434,7 +478,6 @@ public class WrappedPlainView extends BoxView implements TabExpander
public WrappedLine(Element elem)
{
super(elem);
- determineNumLines();
}
/**
@@ -449,10 +492,34 @@ public class WrappedPlainView extends BoxView implements TabExpander
int currStart = getStartOffset();
int currEnd;
int count = 0;
+
+ // Determine layered highlights.
+ Container c = getContainer();
+ LayeredHighlighter lh = null;
+ JTextComponent tc = null;
+ if (c instanceof JTextComponent)
+ {
+ tc = (JTextComponent) c;
+ Highlighter h = tc.getHighlighter();
+ if (h instanceof LayeredHighlighter)
+ lh = (LayeredHighlighter) h;
+ }
+
while (currStart < end)
{
currEnd = calculateBreakPosition(currStart, end);
+ // Paint layered highlights, if any.
+ if (lh != null)
+ {
+ // Exclude trailing newline in last line.
+ if (currEnd == end)
+ lh.paintLayeredHighlights(g, currStart, currEnd - 1, s, tc,
+ this);
+ else
+ lh.paintLayeredHighlights(g, currStart, currEnd, s, tc, this);
+
+ }
drawLine(currStart, currEnd, g, rect.x, rect.y + metrics.getAscent());
rect.y += lineHeight;
@@ -472,37 +539,29 @@ public class WrappedPlainView extends BoxView implements TabExpander
}
}
-
+
/**
* Calculates the number of logical lines that the Element
* needs to be displayed and updates the variable numLines
* accordingly.
*/
- void determineNumLines()
+ private int determineNumLines()
{
- numLines = 0;
+ int nLines = 0;
int end = getEndOffset();
- if (end == 0)
- return;
-
- int breakPoint;
for (int i = getStartOffset(); i < end;)
{
- numLines ++;
+ nLines++;
// careful: check that there's no off-by-one problem here
// depending on which position calculateBreakPosition returns
- breakPoint = calculateBreakPosition(i, end);
-
- if (breakPoint == 0)
- return;
+ int breakPoint = calculateBreakPosition(i, end);
- // If breakPoint is equal to the current index no further
- // line is needed and we can end the loop.
if (breakPoint == i)
- break;
+ i = breakPoint + 1;
else
i = breakPoint;
}
+ return nLines;
}
/**
@@ -547,7 +606,7 @@ public class WrappedPlainView extends BoxView implements TabExpander
// Throwing a BadLocationException is an observed behavior of the RI.
if (rect.isEmpty())
throw new BadLocationException("Unable to calculate view coordinates "
- + "when allocation area is empty.", 5);
+ + "when allocation area is empty.", pos);
Segment s = getLineBuffer();
int lineHeight = metrics.getHeight();
@@ -624,7 +683,7 @@ public class WrappedPlainView extends BoxView implements TabExpander
return currLineStart;
if (y > rect.y + rect.height)
- return end;
+ return end - 1;
// Note: rect.x and rect.width do not represent the width of painted
// text but the area where text *may* be painted. This means the width
@@ -685,22 +744,14 @@ public class WrappedPlainView extends BoxView implements TabExpander
*/
void updateDamage (Rectangle a)
{
- // If the allocation area is empty we can't do anything useful.
- // As determining the number of lines is impossible in that state we
- // reset it to an invalid value which can then be recalculated at a
- // later point.
- if (a == null || a.isEmpty())
+ int nLines = determineNumLines();
+ if (numLines != nLines)
{
- numLines = 1;
- return;
+ numLines = nLines;
+ preferenceChanged(this, false, true);
+ getContainer().repaint();
}
-
- int oldNumLines = numLines;
- determineNumLines();
-
- if (numLines != oldNumLines)
- preferenceChanged(this, false, true);
- else
+ else if (a != null)
getContainer().repaint(a.x, a.y, a.width, a.height);
}
@@ -714,7 +765,8 @@ public class WrappedPlainView extends BoxView implements TabExpander
*/
public void insertUpdate (DocumentEvent changes, Shape a, ViewFactory f)
{
- updateDamage((Rectangle)a);
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ updateDamage(r);
}
/**
@@ -736,7 +788,8 @@ public class WrappedPlainView extends BoxView implements TabExpander
// However this seems to cause no trouble and as it reduces the
// number of method calls it can stay this way.
- updateDamage((Rectangle)a);
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ updateDamage(r);
}
}
}
diff --git a/libjava/classpath/javax/swing/text/ZoneView.java b/libjava/classpath/javax/swing/text/ZoneView.java
new file mode 100644
index 00000000000..6cabc6c200c
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/ZoneView.java
@@ -0,0 +1,442 @@
+/* ZoneView.java -- An effective BoxView subclass
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text;
+
+import java.awt.Shape;
+import java.util.ArrayList;
+import java.util.LinkedList;
+
+import javax.swing.event.DocumentEvent;
+
+/**
+ * A View implementation that delays loading of sub views until they are
+ * needed for display or internal transformations. This can be used for
+ * editors that need to handle large documents more effectivly than the
+ * standard {@link BoxView}.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ *
+ * @since 1.3
+ */
+public class ZoneView
+ extends BoxView
+{
+
+ /**
+ * The default zone view implementation. The specs suggest that this is
+ * a subclass of AsyncBoxView, so do we.
+ */
+ static class Zone
+ extends AsyncBoxView
+ {
+ /**
+ * The start position for this zone.
+ */
+ private Position p0;
+
+ /**
+ * The end position for this zone.
+ */
+ private Position p1;
+
+ /**
+ * Creates a new Zone for the specified element, start and end positions.
+ *
+ * @param el the element
+ * @param pos0 the start position
+ * @param pos1 the end position
+ * @param axis the major axis
+ */
+ Zone(Element el, Position pos0, Position pos1, int axis)
+ {
+ super(el, axis);
+ p0 = pos0;
+ p1 = pos1;
+ }
+
+ /**
+ * Returns the start offset of the zone.
+ *
+ * @return the start offset of the zone
+ */
+ public int getStartOffset()
+ {
+ return p0.getOffset();
+ }
+
+ /**
+ * Returns the end offset of the zone.
+ *
+ * @return the end offset of the zone
+ */
+ public int getEndOffset()
+ {
+ return p1.getOffset();
+ }
+ }
+
+ /**
+ * The maximumZoneSize.
+ */
+ private int maximumZoneSize;
+
+ /**
+ * The maximum number of loaded zones.
+ */
+ private int maxZonesLoaded;
+
+ /**
+ * A queue of loaded zones. When the number of loaded zones exceeds the
+ * maximum number of zones, the oldest zone(s) get unloaded.
+ */
+ private LinkedList loadedZones;
+
+ /**
+ * Creates a new ZoneView for the specified element and axis.
+ *
+ * @param element the element for which to create a ZoneView
+ * @param axis the major layout axis for the box
+ */
+ public ZoneView(Element element, int axis)
+ {
+ super(element, axis);
+ maximumZoneSize = 8192;
+ maxZonesLoaded = 3;
+ loadedZones = new LinkedList();
+ }
+
+ /**
+ * Sets the maximum zone size. Note that zones might still become larger
+ * then the size specified when a singe child view is larger for itself,
+ * because zones are formed on child view boundaries.
+ *
+ * @param size the maximum zone size to set
+ *
+ * @see #getMaximumZoneSize()
+ */
+ public void setMaximumZoneSize(int size)
+ {
+ maximumZoneSize = size;
+ }
+
+ /**
+ * Returns the maximum zone size. Note that zones might still become larger
+ * then the size specified when a singe child view is larger for itself,
+ * because zones are formed on child view boundaries.
+ *
+ * @return the maximum zone size
+ *
+ * @see #setMaximumZoneSize(int)
+ */
+ public int getMaximumZoneSize()
+ {
+ return maximumZoneSize;
+ }
+
+ /**
+ * Sets the maximum number of zones that are allowed to be loaded at the
+ * same time. If the new number of allowed zones is smaller then the
+ * previous settings, this unloads all zones the aren't allowed to be
+ * loaded anymore.
+ *
+ * @param num the number of zones allowed to be loaded at the same time
+ *
+ * @throws IllegalArgumentException if <code>num &lt;= 0</code>
+ *
+ * @see #getMaxZonesLoaded()
+ */
+ public void setMaxZonesLoaded(int num)
+ {
+ if (num < 1)
+ throw new IllegalArgumentException("Illegal number of zones");
+ maxZonesLoaded = num;
+ unloadOldestZones();
+ }
+
+ /**
+ * Returns the number of zones that are allowed to be loaded.
+ *
+ * @return the number of zones that are allowed to be loaded
+ *
+ * @see #setMaxZonesLoaded(int)
+ */
+ public int getMaxZonesLoaded()
+ {
+ return maxZonesLoaded;
+ }
+
+ /**
+ * Gets called after a zone has been loaded. This unloads the oldest zone(s)
+ * when the maximum number of zones is reached.
+ *
+ * @param zone the zone that has been loaded
+ */
+ protected void zoneWasLoaded(View zone)
+ {
+ loadedZones.addLast(zone);
+ unloadOldestZones();
+ }
+
+ /**
+ * This unloads the specified zone. This is implemented to simply remove
+ * all child views from that zone.
+ *
+ * @param zone the zone to be unloaded
+ */
+ protected void unloadZone(View zone)
+ {
+ zone.removeAll();
+ }
+
+ /**
+ * Returns <code>true</code> when the specified zone is loaded,
+ * <code>false</code> otherwise. The default implementation checks if
+ * the zone view has child elements.
+ *
+ * @param zone the zone view to check
+ *
+ * @return <code>true</code> when the specified zone is loaded,
+ * <code>false</code> otherwise
+ */
+ protected boolean isZoneLoaded(View zone)
+ {
+ return zone.getViewCount() > 0;
+ }
+
+ /**
+ * Creates a zone for the specified range. Subclasses can override this
+ * to provide a custom implementation for the zones.
+ *
+ * @param p0 the start of the range
+ * @param p1 the end of the range
+ *
+ * @return the zone
+ */
+ protected View createZone(int p0, int p1)
+ {
+ Document doc = getDocument();
+ Position pos0 = null;
+ Position pos1 = null;
+ try
+ {
+ pos0 = doc.createPosition(p0);
+ pos1 = doc.createPosition(p1);
+ }
+ catch (BadLocationException ex)
+ {
+ assert false : "Must not happen";
+ }
+ Zone zone = new Zone(getElement(), pos0, pos1, getAxis());
+ return zone;
+ }
+
+ // --------------------------------------------------------------------------
+ // CompositeView methods.
+ // --------------------------------------------------------------------------
+
+ /**
+ * Overridden to not load all the child views. This methods creates
+ * initial zones without actually loading them.
+ *
+ * @param vf not used
+ */
+ protected void loadChildren(ViewFactory vf)
+ {
+ int p0 = getStartOffset();
+ int p1 = getEndOffset();
+ append(createZone(p0, p1));
+ checkZoneAt(p0);
+ }
+
+ /**
+ * Returns the index of the child view at the document position
+ * <code>pos</code>.
+ *
+ * This overrides the CompositeView implementation because the ZoneView does
+ * not provide a one to one mapping from Elements to Views.
+ *
+ * @param pos the document position
+ *
+ * @return the index of the child view at the document position
+ * <code>pos</code>
+ */
+ protected int getViewIndexAtPosition(int pos)
+ {
+ int index = -1;
+ boolean found = false;
+ if (pos >= getStartOffset() && pos <= getEndOffset())
+ {
+ int upper = getViewCount() - 1;
+ int lower = 0;
+ index = (upper - lower) / 2 + lower;
+ int bias = 0;
+ do
+ {
+ View child = getView(index);
+ int childStart = child.getStartOffset();
+ int childEnd = child.getEndOffset();
+ if (pos >= childStart && pos < childEnd)
+ found = true;
+ else if (pos < childStart)
+ {
+ upper = index;
+ bias = -1;
+ }
+ else if (pos >= childEnd)
+ {
+ lower = index;
+ bias = 1;
+ }
+ if (! found)
+ {
+ int newIndex = (upper - lower) / 2 + lower;
+ if (newIndex == index)
+ index = newIndex + bias;
+ else
+ index = newIndex;
+ }
+ } while (upper != lower && ! found);
+ }
+ // If no child view actually covers the specified offset, reset index to
+ // -1.
+ if (! found)
+ index = -1;
+ return index;
+ }
+
+ // --------------------------------------------------------------------------
+ // View methods.
+ // --------------------------------------------------------------------------
+
+ public void insertUpdate(DocumentEvent e, Shape a, ViewFactory vf)
+ {
+ // TODO: Implement this.
+ }
+
+ public void removeUpdate(DocumentEvent e, Shape a, ViewFactory vf)
+ {
+ // TODO: Implement this.
+ }
+
+ protected boolean updateChildren(DocumentEvent.ElementChange ec,
+ DocumentEvent e, ViewFactory vf)
+ {
+ // TODO: Implement this.
+ return false;
+ }
+
+ // --------------------------------------------------------------------------
+ // Internal helper methods.
+ // --------------------------------------------------------------------------
+
+ /**
+ * A helper method to unload the oldest zones when there are more loaded
+ * zones then allowed.
+ */
+ private void unloadOldestZones()
+ {
+ int maxZones = getMaxZonesLoaded();
+ while (loadedZones.size() > maxZones)
+ {
+ View zone = (View) loadedZones.removeFirst();
+ unloadZone(zone);
+ }
+ }
+
+ /**
+ * Checks if the zone view at position <code>pos</code> should be split
+ * (its size is greater than maximumZoneSize) and tries to split it.
+ *
+ * @param pos the document position to check
+ */
+ private void checkZoneAt(int pos)
+ {
+ int viewIndex = getViewIndexAtPosition(pos); //, Position.Bias.Forward);
+ View view = getView(viewIndex);
+ int p0 = view.getStartOffset();
+ int p1 = view.getEndOffset();
+ if (p1 - p0 > maximumZoneSize)
+ splitZone(viewIndex, p0, p1);
+ }
+
+ /**
+ * Tries to break the view at the specified index and inside the specified
+ * range into pieces that are acceptable with respect to the maximum zone
+ * size.
+ *
+ * @param index the index of the view to split
+ * @param p0 the start offset
+ * @param p1 the end offset
+ */
+ private void splitZone(int index, int p0, int p1)
+ {
+ ArrayList newZones = new ArrayList();
+ int p = p0;
+ do
+ {
+ p0 = p;
+ p = Math.min(getPreferredZoneEnd(p0), p1);
+ newZones.add(createZone(p0, p));
+ } while (p < p1);
+ View[] newViews = new View[newZones.size()];
+ newViews = (View[]) newZones.toArray(newViews);
+ replace(index, 1, newViews);
+ }
+
+ /**
+ * Calculates the positions at which a zone split is performed. This
+ * tries to create zones sized close to half the maximum zone size.
+ *
+ * @param start the start offset
+ *
+ * @return the preferred end offset
+ */
+ private int getPreferredZoneEnd(int start)
+ {
+ Element el = getElement();
+ int index = el.getElementIndex(start + (maximumZoneSize / 2));
+ Element child = el.getElement(index);
+ int p0 = child.getStartOffset();
+ int p1 = child.getEndOffset();
+ int end = p1;
+ if (p0 - start > maximumZoneSize && p0 > start)
+ end = p0;
+ return end;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/BRView.java b/libjava/classpath/javax/swing/text/html/BRView.java
index 5521fed8edf..7d0d5164d49 100644
--- a/libjava/classpath/javax/swing/text/html/BRView.java
+++ b/libjava/classpath/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/libjava/classpath/javax/swing/text/html/BlockView.java b/libjava/classpath/javax/swing/text/html/BlockView.java
index 6274e7b1756..b05c983e922 100644
--- a/libjava/classpath/javax/swing/text/html/BlockView.java
+++ b/libjava/classpath/javax/swing/text/html/BlockView.java
@@ -38,9 +38,12 @@ exception statement from your version. */
package javax.swing.text.html;
+import gnu.javax.swing.text.html.css.Length;
+
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Shape;
+import java.util.HashMap;
import javax.swing.SizeRequirements;
import javax.swing.event.DocumentEvent;
@@ -55,7 +58,106 @@ import javax.swing.text.ViewFactory;
*/
public class BlockView extends BoxView
{
-
+
+ /**
+ * Stores information about child positioning according to the
+ * CSS attributes position, left, right, top and bottom.
+ */
+ private static class PositionInfo
+ {
+ // TODO: Use enums when available.
+
+ /**
+ * Static positioning. This is the default and is thus rarely really
+ * used.
+ */
+ static final int STATIC = 0;
+
+ /**
+ * Relative positioning. The box is teaked relative to its static
+ * computed bounds.
+ */
+ static final int RELATIVE = 1;
+
+ /**
+ * Absolute positioning. The box is moved relative to the parent's box.
+ */
+ static final int ABSOLUTE = 2;
+
+ /**
+ * Like ABSOLUTE, with some fixation against the viewport (not yet
+ * implemented).
+ */
+ static final int FIXED = 3;
+
+ /**
+ * The type according to the constants of this class.
+ */
+ int type;
+
+ /**
+ * The left constraint, null if not set.
+ */
+ Length left;
+
+ /**
+ * The right constraint, null if not set.
+ */
+ Length right;
+
+ /**
+ * The top constraint, null if not set.
+ */
+ Length top;
+
+ /**
+ * The bottom constraint, null if not set.
+ */
+ Length bottom;
+
+ /**
+ * Creates a new PositionInfo object.
+ *
+ * @param typ the type to set
+ * @param l the left constraint
+ * @param r the right constraint
+ * @param t the top constraint
+ * @param b the bottom constraint
+ */
+ PositionInfo(int typ, Length l, Length r, Length t, Length b)
+ {
+ type = typ;
+ left = l;
+ right = r;
+ top = t;
+ bottom = b;
+ }
+ }
+
+ /**
+ * The attributes for this view.
+ */
+ private AttributeSet attributes;
+
+ /**
+ * The box painter for this view.
+ *
+ * This is package private because the TableView needs access to it.
+ */
+ StyleSheet.BoxPainter painter;
+
+ /**
+ * The width and height as specified in the stylesheet, null if not
+ * specified. The first value is the X_AXIS, the second the Y_AXIS. You
+ * can index this directly by the X_AXIS and Y_AXIS constants.
+ */
+ private Length[] cssSpans;
+
+ /**
+ * Stores additional CSS layout information.
+ */
+ private HashMap positionInfo;
+
/**
* Creates a new view that represents an html box.
* This can be used for a number of elements.
@@ -66,8 +168,10 @@ public class BlockView extends BoxView
public BlockView(Element elem, int axis)
{
super(elem, axis);
+ cssSpans = new Length[2];
+ positionInfo = new HashMap();
}
-
+
/**
* Creates the parent view for this. It is called before
* any other methods, if the parent view is working properly.
@@ -99,12 +203,27 @@ public class BlockView extends BoxView
protected SizeRequirements calculateMajorAxisRequirements(int axis,
SizeRequirements r)
{
- SizeRequirements sr = super.calculateMajorAxisRequirements(axis, r);
- // FIXME: adjust it if the CSS width or height attribute is specified
- // and applicable
- return sr;
+ if (r == null)
+ r = new SizeRequirements();
+
+ if (setCSSSpan(r, axis))
+ {
+ // If we have set the span from CSS, then we need to adjust
+ // the margins.
+ SizeRequirements parent = super.calculateMajorAxisRequirements(axis,
+ null);
+ int margin = axis == X_AXIS ? getLeftInset() + getRightInset()
+ : getTopInset() + getBottomInset();
+ r.minimum -= margin;
+ r.preferred -= margin;
+ r.maximum -= margin;
+ constrainSize(axis, r, parent);
+ }
+ else
+ r = super.calculateMajorAxisRequirements(axis, r);
+ return r;
}
-
+
/**
* Calculates the requirements along the minor axis.
* This is implemented to call the superclass and then
@@ -118,12 +237,89 @@ public class BlockView extends BoxView
protected SizeRequirements calculateMinorAxisRequirements(int axis,
SizeRequirements r)
{
- SizeRequirements sr = super.calculateMinorAxisRequirements(axis, r);
- // FIXME: adjust it if the CSS width or height attribute is specified
- // and applicable.
- return sr;
+ if (r == null)
+ r = new SizeRequirements();
+
+ if (setCSSSpan(r, axis))
+ {
+ // If we have set the span from CSS, then we need to adjust
+ // the margins.
+ SizeRequirements parent = super.calculateMinorAxisRequirements(axis,
+ null);
+ int margin = axis == X_AXIS ? getLeftInset() + getRightInset()
+ : getTopInset() + getBottomInset();
+ r.minimum -= margin;
+ r.preferred -= margin;
+ r.maximum -= margin;
+ constrainSize(axis, r, parent);
+ }
+ else
+ r = super.calculateMinorAxisRequirements(axis, r);
+
+ // Apply text alignment if appropriate.
+ if (axis == X_AXIS)
+ {
+ Object o = getAttributes().getAttribute(CSS.Attribute.TEXT_ALIGN);
+ if (o != null)
+ {
+ String al = o.toString().trim();
+ if (al.equals("center"))
+ r.alignment = 0.5f;
+ else if (al.equals("right"))
+ r.alignment = 1.0f;
+ else
+ r.alignment = 0.0f;
+ }
+ }
+ return r;
+ }
+
+ /**
+ * Sets the span on the SizeRequirements object according to the
+ * according CSS span value, when it is set.
+ *
+ * @param r the size requirements
+ * @param axis the axis
+ *
+ * @return <code>true</code> when the CSS span has been set,
+ * <code>false</code> otherwise
+ */
+ private boolean setCSSSpan(SizeRequirements r, int axis)
+ {
+ boolean ret = false;
+ Length span = cssSpans[axis];
+ // We can't set relative CSS spans here because we don't know
+ // yet about the allocated span. Instead we use the view's
+ // normal requirements.
+ if (span != null && ! span.isPercentage())
+ {
+ r.minimum = (int) span.getValue();
+ r.preferred = (int) span.getValue();
+ r.maximum = (int) span.getValue();
+ ret = true;
+ }
+ return ret;
}
-
+
+ /**
+ * Constrains the <code>r</code> requirements according to
+ * <code>min</code>.
+ *
+ * @param axis the axis
+ * @param r the requirements to constrain
+ * @param min the constraining requirements
+ */
+ private void constrainSize(int axis, SizeRequirements r,
+ SizeRequirements min)
+ {
+ if (min.minimum > r.minimum)
+ {
+ r.minimum = min.minimum;
+ r.preferred = min.minimum;
+ r.maximum = Math.max(r.maximum, min.maximum);
+ }
+ }
+
/**
* Lays out the box along the minor axis (the axis that is
* perpendicular to the axis that it represents). The results
@@ -142,10 +338,133 @@ public class BlockView extends BoxView
protected void layoutMinorAxis(int targetSpan, int axis,
int[] offsets, int[] spans)
{
- // FIXME: Not implemented.
- super.layoutMinorAxis(targetSpan, axis, offsets, spans);
+ int viewCount = getViewCount();
+ for (int i = 0; i < viewCount; i++)
+ {
+ View view = getView(i);
+ int min = (int) view.getMinimumSpan(axis);
+ int max;
+ // Handle CSS span value of child.
+ Length length = cssSpans[axis];
+ if (length != null)
+ {
+ min = Math.max((int) length.getValue(targetSpan), min);
+ max = min;
+ }
+ else
+ max = (int) view.getMaximumSpan(axis);
+
+ if (max < targetSpan)
+ {
+ // Align child.
+ float align = view.getAlignment(axis);
+ offsets[i] = (int) ((targetSpan - max) * align);
+ spans[i] = max;
+ }
+ else
+ {
+ offsets[i] = 0;
+ spans[i] = Math.max(min, targetSpan);
+ }
+
+ // Adjust according to CSS position info.
+ positionView(targetSpan, axis, i, offsets, spans);
+ }
}
-
+
+ /**
+ * Overridden to perform additional CSS layout (absolute/relative
+ * positioning).
+ */
+ protected void layoutMajorAxis(int targetSpan, int axis,
+ int[] offsets, int[] spans)
+ {
+ super.layoutMajorAxis(targetSpan, axis, offsets, spans);
+
+ // Adjust according to CSS position info.
+ int viewCount = getViewCount();
+ for (int i = 0; i < viewCount; i++)
+ {
+ positionView(targetSpan, axis, i, offsets, spans);
+ }
+ }
+
+ /**
+ * Positions a view according to any additional CSS constraints.
+ *
+ * @param targetSpan the target span
+ * @param axis the axis
+ * @param i the index of the view
+ * @param offsets the offsets get placed here
+ * @param spans the spans get placed here
+ */
+ private void positionView(int targetSpan, int axis, int i, int[] offsets,
+ int[] spans)
+ {
+ View view = getView(i);
+ PositionInfo pos = (PositionInfo) positionInfo.get(view);
+ if (pos != null)
+ {
+ int p0 = -1;
+ int p1 = -1;
+ if (axis == X_AXIS)
+ {
+ Length l = pos.left;
+ if (l != null)
+ p0 = (int) l.getValue(targetSpan);
+ l = pos.right;
+ if (l != null)
+ p1 = (int) l.getValue(targetSpan);
+ }
+ else
+ {
+ Length l = pos.top;
+ if (l != null)
+ p0 = (int) l.getValue(targetSpan);
+ l = pos.bottom;
+ if (l != null)
+ p1 = (int) l.getValue(targetSpan);
+ }
+ if (pos.type == PositionInfo.ABSOLUTE
+ || pos.type == PositionInfo.FIXED)
+ {
+ if (p0 != -1)
+ {
+ offsets[i] = p0;
+ if (p1 != -1)
+ {
+ // Overrides computed width. (Possibly overconstrained
+ // when the width attribute was set too.)
+ spans[i] = targetSpan - p1 - offsets[i];
+ }
+ }
+ else if (p1 != -1)
+ {
+ // Preserve any computed width.
+ offsets[i] = targetSpan - p1 - spans[i];
+ }
+ }
+ else if (pos.type == PositionInfo.RELATIVE)
+ {
+ if (p0 != -1)
+ {
+ offsets[i] += p0;
+ if (p1 != -1)
+ {
+ // Overrides computed width. (Possibly overconstrained
+ // when the width attribute was set too.)
+ spans[i] = spans[i] - p0 - p1 - offsets[i];
+ }
+ }
+ else if (p1 != -1)
+ {
+ // Preserve any computed width.
+ offsets[i] -= p1;
+ }
+ }
+ }
+ }
+
/**
* Paints using the given graphics configuration and shape.
* This delegates to the css box painter to paint the
@@ -156,14 +475,16 @@ public class BlockView extends BoxView
*/
public void paint(Graphics g, Shape a)
{
- Rectangle rect = (Rectangle) a;
- // FIXME: not fully implemented
- getStyleSheet().getBoxPainter(getAttributes()).paint(g, rect.x, rect.y,
- rect.width,
- rect.height, this);
+ Rectangle rect = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+
+ // Debug output. Shows blocks in green rectangles.
+ // g.setColor(Color.GREEN);
+ // g.drawRect(rect.x, rect.y, rect.width, rect.height);
+
+ painter.paint(g, rect.x, rect.y, rect.width, rect.height, this);
super.paint(g, a);
}
-
+
/**
* Fetches the attributes to use when painting.
*
@@ -171,7 +492,9 @@ public class BlockView extends BoxView
*/
public AttributeSet getAttributes()
{
- return getStyleSheet().getViewAttributes(this);
+ if (attributes == null)
+ attributes = getStyleSheet().getViewAttributes(this);
+ return attributes;
}
/**
@@ -200,14 +523,17 @@ public class BlockView extends BoxView
public float getAlignment(int axis)
{
if (axis == X_AXIS)
- return 0.0F;
+ return super.getAlignment(axis);
if (axis == Y_AXIS)
{
if (getViewCount() == 0)
return 0.0F;
float prefHeight = getPreferredSpan(Y_AXIS);
- float firstRowHeight = getView(0).getPreferredSpan(Y_AXIS);
- return (firstRowHeight / 2.F) / prefHeight;
+ View first = getView(0);
+ float firstRowHeight = first.getPreferredSpan(Y_AXIS);
+ return prefHeight != 0 ? (firstRowHeight * first.getAlignment(Y_AXIS))
+ / prefHeight
+ : 0;
}
throw new IllegalArgumentException("Invalid Axis");
}
@@ -227,7 +553,8 @@ public class BlockView extends BoxView
// If more elements were added, then need to set the properties for them
int currPos = ev.getOffset();
- if (currPos <= getStartOffset() && (currPos + ev.getLength()) >= getEndOffset())
+ if (currPos <= getStartOffset()
+ && (currPos + ev.getLength()) >= getEndOffset())
setPropertiesFromAttributes();
}
@@ -284,9 +611,33 @@ public class BlockView extends BoxView
*/
protected void setPropertiesFromAttributes()
{
- // FIXME: Not implemented (need to use StyleSheet).
+ // Fetch attributes.
+ StyleSheet ss = getStyleSheet();
+ attributes = ss.getViewAttributes(this);
+
+ // Fetch painter.
+ painter = ss.getBoxPainter(attributes);
+
+ // Update insets.
+ if (attributes != null)
+ {
+ setInsets((short) painter.getInset(TOP, this),
+ (short) painter.getInset(LEFT, this),
+ (short) painter.getInset(BOTTOM, this),
+ (short) painter.getInset(RIGHT, this));
+ }
+
+ // Fetch width and height.
+ float emBase = ss.getEMBase(attributes);
+ float exBase = ss.getEXBase(attributes);
+ cssSpans[X_AXIS] = (Length) attributes.getAttribute(CSS.Attribute.WIDTH);
+ if (cssSpans[X_AXIS] != null)
+ cssSpans[X_AXIS].setFontBases(emBase, exBase);
+ cssSpans[Y_AXIS] = (Length) attributes.getAttribute(CSS.Attribute.HEIGHT);
+ if (cssSpans[Y_AXIS] != null)
+ cssSpans[Y_AXIS].setFontBases(emBase, exBase);
}
-
+
/**
* Gets the default style sheet.
*
@@ -294,8 +645,77 @@ public class BlockView extends BoxView
*/
protected StyleSheet getStyleSheet()
{
- StyleSheet styleSheet = new StyleSheet();
- styleSheet.importStyleSheet(getClass().getResource(HTMLEditorKit.DEFAULT_CSS));
- return styleSheet;
+ HTMLDocument doc = (HTMLDocument) getDocument();
+ return doc.getStyleSheet();
+ }
+
+ /**
+ * Overridden to fetch additional CSS layout information.
+ */
+ public void replace(int offset, int length, View[] views)
+ {
+ // First remove unneeded stuff.
+ for (int i = 0; i < length; i++)
+ {
+ View child = getView(i + offset);
+ positionInfo.remove(child);
+ }
+
+ // Call super to actually replace the views.
+ super.replace(offset, length, views);
+
+ // Now fetch the position infos for the new views.
+ for (int i = 0; i < views.length; i++)
+ {
+ fetchLayoutInfo(views[i]);
+ }
+ }
+
+ /**
+ * Fetches and stores the layout info for the specified view.
+ *
+ * @param view the view for which the layout info is stored
+ */
+ private void fetchLayoutInfo(View view)
+ {
+ AttributeSet atts = view.getAttributes();
+ Object o = atts.getAttribute(CSS.Attribute.POSITION);
+ if (o != null && o instanceof String && ! o.equals("static"))
+ {
+ int type;
+ if (o.equals("relative"))
+ type = PositionInfo.RELATIVE;
+ else if (o.equals("absolute"))
+ type = PositionInfo.ABSOLUTE;
+ else if (o.equals("fixed"))
+ type = PositionInfo.FIXED;
+ else
+ type = PositionInfo.STATIC;
+
+ if (type != PositionInfo.STATIC)
+ {
+ StyleSheet ss = getStyleSheet();
+ float emBase = ss.getEMBase(atts);
+ float exBase = ss.getEXBase(atts);
+ Length left = (Length) atts.getAttribute(CSS.Attribute.LEFT);
+ if (left != null)
+ left.setFontBases(emBase, exBase);
+ Length right = (Length) atts.getAttribute(CSS.Attribute.RIGHT);
+ if (right != null)
+ right.setFontBases(emBase, exBase);
+ Length top = (Length) atts.getAttribute(CSS.Attribute.TOP);
+ if (top != null)
+ top.setFontBases(emBase, exBase);
+ Length bottom = (Length) atts.getAttribute(CSS.Attribute.BOTTOM);
+ if (bottom != null)
+ bottom.setFontBases(emBase, exBase);
+ if (left != null || right != null || top != null || bottom != null)
+ {
+ PositionInfo pos = new PositionInfo(type, left, right, top,
+ bottom);
+ positionInfo.put(view, pos);
+ }
+ }
+ }
}
}
diff --git a/libjava/classpath/javax/swing/text/html/CSS.java b/libjava/classpath/javax/swing/text/html/CSS.java
index c248e758ec2..77f94a60878 100644
--- a/libjava/classpath/javax/swing/text/html/CSS.java
+++ b/libjava/classpath/javax/swing/text/html/CSS.java
@@ -37,8 +37,19 @@ exception statement from your version. */
package javax.swing.text.html;
+import gnu.javax.swing.text.html.css.BorderStyle;
+import gnu.javax.swing.text.html.css.BorderWidth;
+import gnu.javax.swing.text.html.css.CSSColor;
+import gnu.javax.swing.text.html.css.FontSize;
+import gnu.javax.swing.text.html.css.FontStyle;
+import gnu.javax.swing.text.html.css.FontWeight;
+import gnu.javax.swing.text.html.css.Length;
+
import java.io.Serializable;
import java.util.HashMap;
+import java.util.StringTokenizer;
+
+import javax.swing.text.MutableAttributeSet;
/**
* Provides CSS attributes to be used by the HTML view classes. The constants
@@ -388,6 +399,36 @@ public class CSS implements Serializable
public static final Attribute WORD_SPACING =
new Attribute("word-spacing", true, "normal");
+ // Some GNU Classpath specific extensions.
+ static final Attribute BORDER_TOP_STYLE =
+ new Attribute("border-top-style", false, null);
+ static final Attribute BORDER_BOTTOM_STYLE =
+ new Attribute("border-bottom-style", false, null);
+ static final Attribute BORDER_LEFT_STYLE =
+ new Attribute("border-left-style", false, null);
+ static final Attribute BORDER_RIGHT_STYLE =
+ new Attribute("border-right-style", false, null);
+ static final Attribute BORDER_TOP_COLOR =
+ new Attribute("border-top-color", false, null);
+ static final Attribute BORDER_BOTTOM_COLOR =
+ new Attribute("border-bottom-color", false, null);
+ static final Attribute BORDER_LEFT_COLOR =
+ new Attribute("border-left-color", false, null);
+ static final Attribute BORDER_RIGHT_COLOR =
+ new Attribute("border-right-color", false, null);
+ static final Attribute BORDER_SPACING =
+ new Attribute("border-spacing", false, null);
+ static final Attribute POSITION =
+ new Attribute("position", false, null);
+ static final Attribute LEFT =
+ new Attribute("left", false, null);
+ static final Attribute RIGHT =
+ new Attribute("right", false, null);
+ static final Attribute TOP =
+ new Attribute("top", false, null);
+ static final Attribute BOTTOM =
+ new Attribute("bottom", false, null);
+
/**
* The attribute string.
*/
@@ -459,4 +500,237 @@ public class CSS implements Serializable
return defaultValue;
}
}
+
+ /**
+ * Maps attribute values (String) to some converter class, based on the
+ * key.
+ *
+ * @param att the key
+ * @param v the value
+ *
+ * @return the wrapped value
+ */
+ static Object getValue(Attribute att, String v)
+ {
+ Object o;
+ if (att == Attribute.FONT_SIZE)
+ o = new FontSize(v);
+ else if (att == Attribute.FONT_WEIGHT)
+ o = new FontWeight(v);
+ else if (att == Attribute.FONT_STYLE)
+ o = new FontStyle(v);
+ else if (att == Attribute.COLOR || att == Attribute.BACKGROUND_COLOR
+ || att == Attribute.BORDER_COLOR
+ || att == Attribute.BORDER_TOP_COLOR
+ || att == Attribute.BORDER_BOTTOM_COLOR
+ || att == Attribute.BORDER_LEFT_COLOR
+ || att == Attribute.BORDER_RIGHT_COLOR)
+ o = new CSSColor(v);
+ else if (att == Attribute.MARGIN || att == Attribute.MARGIN_BOTTOM
+ || att == Attribute.MARGIN_LEFT || att == Attribute.MARGIN_RIGHT
+ || att == Attribute.MARGIN_TOP || att == Attribute.WIDTH
+ || att == Attribute.HEIGHT
+ || att == Attribute.PADDING || att == Attribute.PADDING_BOTTOM
+ || att == Attribute.PADDING_LEFT || att == Attribute.PADDING_RIGHT
+ || att == Attribute.PADDING_TOP
+ || att == Attribute.LEFT || att == Attribute.RIGHT
+ || att == Attribute.TOP || att == Attribute.BOTTOM)
+ o = new Length(v);
+ else if (att == Attribute.BORDER_WIDTH || att == Attribute.BORDER_TOP_WIDTH
+ || att == Attribute.BORDER_LEFT_WIDTH
+ || att == Attribute.BORDER_RIGHT_WIDTH
+ || att == Attribute.BORDER_BOTTOM_WIDTH)
+ o = new BorderWidth(v);
+ else
+ o = v;
+ return o;
+ }
+
+ static void addInternal(MutableAttributeSet atts, Attribute a, String v)
+ {
+ if (a == Attribute.BACKGROUND)
+ parseBackgroundShorthand(atts, v);
+ else if (a == Attribute.PADDING)
+ parsePaddingShorthand(atts, v);
+ else if (a == Attribute.MARGIN)
+ parseMarginShorthand(atts, v);
+ else if (a == Attribute.BORDER || a == Attribute.BORDER_LEFT
+ || a == Attribute.BORDER_RIGHT || a == Attribute.BORDER_TOP
+ || a == Attribute.BORDER_BOTTOM)
+ parseBorderShorthand(atts, v, a);
+ }
+
+ /**
+ * Parses the background shorthand and translates it to more specific
+ * background attributes.
+ *
+ * @param atts the attributes
+ * @param v the value
+ */
+ private static void parseBackgroundShorthand(MutableAttributeSet atts,
+ String v)
+ {
+ StringTokenizer tokens = new StringTokenizer(v, " ");
+ while (tokens.hasMoreElements())
+ {
+ String token = tokens.nextToken();
+ if (CSSColor.isValidColor(token))
+ atts.addAttribute(Attribute.BACKGROUND_COLOR,
+ new CSSColor(token));
+ }
+ }
+
+ /**
+ * Parses the padding shorthand and translates to the specific padding
+ * values.
+ *
+ * @param atts the attributes
+ * @param v the actual value
+ */
+ private static void parsePaddingShorthand(MutableAttributeSet atts, String v)
+ {
+ StringTokenizer tokens = new StringTokenizer(v, " ");
+ int numTokens = tokens.countTokens();
+ if (numTokens == 1)
+ {
+ Length l = new Length(tokens.nextToken());
+ atts.addAttribute(Attribute.PADDING_BOTTOM, l);
+ atts.addAttribute(Attribute.PADDING_LEFT, l);
+ atts.addAttribute(Attribute.PADDING_RIGHT, l);
+ atts.addAttribute(Attribute.PADDING_TOP, l);
+ }
+ else if (numTokens == 2)
+ {
+ Length l1 = new Length(tokens.nextToken());
+ Length l2 = new Length(tokens.nextToken());
+ atts.addAttribute(Attribute.PADDING_BOTTOM, l1);
+ atts.addAttribute(Attribute.PADDING_TOP, l1);
+ atts.addAttribute(Attribute.PADDING_LEFT, l2);
+ atts.addAttribute(Attribute.PADDING_RIGHT, l2);
+ }
+ else if (numTokens == 3)
+ {
+ Length l1 = new Length(tokens.nextToken());
+ Length l2 = new Length(tokens.nextToken());
+ Length l3 = new Length(tokens.nextToken());
+ atts.addAttribute(Attribute.PADDING_TOP, l1);
+ atts.addAttribute(Attribute.PADDING_LEFT, l2);
+ atts.addAttribute(Attribute.PADDING_RIGHT, l2);
+ atts.addAttribute(Attribute.PADDING_BOTTOM, l3);
+ }
+ else
+ {
+ Length l1 = new Length(tokens.nextToken());
+ Length l2 = new Length(tokens.nextToken());
+ Length l3 = new Length(tokens.nextToken());
+ Length l4 = new Length(tokens.nextToken());
+ atts.addAttribute(Attribute.PADDING_TOP, l1);
+ atts.addAttribute(Attribute.PADDING_RIGHT, l2);
+ atts.addAttribute(Attribute.PADDING_BOTTOM, l3);
+ atts.addAttribute(Attribute.PADDING_LEFT, l4);
+ }
+ }
+
+ /**
+ * Parses the margin shorthand and translates to the specific margin
+ * values.
+ *
+ * @param atts the attributes
+ * @param v the actual value
+ */
+ private static void parseMarginShorthand(MutableAttributeSet atts, String v)
+ {
+ StringTokenizer tokens = new StringTokenizer(v, " ");
+ int numTokens = tokens.countTokens();
+ if (numTokens == 1)
+ {
+ Length l = new Length(tokens.nextToken());
+ atts.addAttribute(Attribute.MARGIN_BOTTOM, l);
+ atts.addAttribute(Attribute.MARGIN_LEFT, l);
+ atts.addAttribute(Attribute.MARGIN_RIGHT, l);
+ atts.addAttribute(Attribute.MARGIN_TOP, l);
+ }
+ else if (numTokens == 2)
+ {
+ Length l1 = new Length(tokens.nextToken());
+ Length l2 = new Length(tokens.nextToken());
+ atts.addAttribute(Attribute.MARGIN_BOTTOM, l1);
+ atts.addAttribute(Attribute.MARGIN_TOP, l1);
+ atts.addAttribute(Attribute.MARGIN_LEFT, l2);
+ atts.addAttribute(Attribute.MARGIN_RIGHT, l2);
+ }
+ else if (numTokens == 3)
+ {
+ Length l1 = new Length(tokens.nextToken());
+ Length l2 = new Length(tokens.nextToken());
+ Length l3 = new Length(tokens.nextToken());
+ atts.addAttribute(Attribute.MARGIN_TOP, l1);
+ atts.addAttribute(Attribute.MARGIN_LEFT, l2);
+ atts.addAttribute(Attribute.MARGIN_RIGHT, l2);
+ atts.addAttribute(Attribute.MARGIN_BOTTOM, l3);
+ }
+ else
+ {
+ Length l1 = new Length(tokens.nextToken());
+ Length l2 = new Length(tokens.nextToken());
+ Length l3 = new Length(tokens.nextToken());
+ Length l4 = new Length(tokens.nextToken());
+ atts.addAttribute(Attribute.MARGIN_TOP, l1);
+ atts.addAttribute(Attribute.MARGIN_RIGHT, l2);
+ atts.addAttribute(Attribute.MARGIN_BOTTOM, l3);
+ atts.addAttribute(Attribute.MARGIN_LEFT, l4);
+ }
+ }
+
+ /**
+ * Parses the CSS border shorthand attribute and translates it to the
+ * more specific border attributes.
+ *
+ * @param atts the attribute
+ * @param value the value
+ */
+ private static void parseBorderShorthand(MutableAttributeSet atts,
+ String value, Attribute cssAtt)
+ {
+ StringTokenizer tokens = new StringTokenizer(value, " ");
+ while (tokens.hasMoreTokens())
+ {
+ String token = tokens.nextToken();
+ if (BorderStyle.isValidStyle(token))
+ {
+ if (cssAtt == Attribute.BORDER_LEFT || cssAtt == Attribute.BORDER)
+ atts.addAttribute(Attribute.BORDER_LEFT_STYLE, token);
+ if (cssAtt == Attribute.BORDER_RIGHT || cssAtt == Attribute.BORDER)
+ atts.addAttribute(Attribute.BORDER_RIGHT_STYLE, token);
+ if (cssAtt == Attribute.BORDER_BOTTOM || cssAtt == Attribute.BORDER)
+ atts.addAttribute(Attribute.BORDER_BOTTOM_STYLE, token);
+ if (cssAtt == Attribute.BORDER_TOP || cssAtt == Attribute.BORDER)
+ atts.addAttribute(Attribute.BORDER_TOP_STYLE, token);
+ }
+ else if (BorderWidth.isValid(token))
+ {
+ BorderWidth w = new BorderWidth(token);
+ if (cssAtt == Attribute.BORDER_LEFT || cssAtt == Attribute.BORDER)
+ atts.addAttribute(Attribute.BORDER_LEFT_WIDTH, w);
+ if (cssAtt == Attribute.BORDER_RIGHT || cssAtt == Attribute.BORDER)
+ atts.addAttribute(Attribute.BORDER_RIGHT_WIDTH, w);
+ if (cssAtt == Attribute.BORDER_BOTTOM || cssAtt == Attribute.BORDER)
+ atts.addAttribute(Attribute.BORDER_BOTTOM_WIDTH, w);
+ if (cssAtt == Attribute.BORDER_TOP || cssAtt == Attribute.BORDER)
+ atts.addAttribute(Attribute.BORDER_TOP_WIDTH, w);
+ }
+ else if (CSSColor.isValidColor(token))
+ {
+ CSSColor c = new CSSColor(token);
+ if (cssAtt == Attribute.BORDER_LEFT || cssAtt == Attribute.BORDER)
+ atts.addAttribute(Attribute.BORDER_LEFT_COLOR, c);
+ if (cssAtt == Attribute.BORDER_RIGHT || cssAtt == Attribute.BORDER)
+ atts.addAttribute(Attribute.BORDER_RIGHT_COLOR, c);
+ if (cssAtt == Attribute.BORDER_BOTTOM || cssAtt == Attribute.BORDER)
+ atts.addAttribute(Attribute.BORDER_BOTTOM_COLOR, c);
+ if (cssAtt == Attribute.BORDER_TOP || cssAtt == Attribute.BORDER)
+ atts.addAttribute(Attribute.BORDER_TOP_COLOR, c);
+ }
+ }
+ }
}
diff --git a/libjava/classpath/javax/swing/text/html/CSSBorder.java b/libjava/classpath/javax/swing/text/html/CSSBorder.java
new file mode 100644
index 00000000000..fff6b01a170
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/CSSBorder.java
@@ -0,0 +1,421 @@
+/* CSSBorder.java -- A border for rendering CSS border styles
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text.html;
+
+import gnu.javax.swing.text.html.css.BorderWidth;
+import gnu.javax.swing.text.html.css.CSSColor;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Insets;
+
+import javax.swing.border.Border;
+import javax.swing.text.AttributeSet;
+
+/**
+ * A border implementation to render CSS border styles.
+ */
+class CSSBorder
+ implements Border
+{
+
+ /**
+ * The CSS border styles.
+ */
+
+ private static final int STYLE_NOT_SET = -1;
+ private static final int STYLE_NONE = 0;
+ private static final int STYLE_HIDDEN = 1;
+ private static final int STYLE_DOTTED = 2;
+ private static final int STYLE_DASHED = 3;
+ private static final int STYLE_SOLID = 4;
+ private static final int STYLE_DOUBLE = 5;
+ private static final int STYLE_GROOVE = 6;
+ private static final int STYLE_RIDGE = 7;
+ private static final int STYLE_INSET = 8;
+ private static final int STYLE_OUTSET = 9;
+
+ /**
+ * The left insets.
+ */
+ private int left;
+
+ /**
+ * The right insets.
+ */
+ private int right;
+
+ /**
+ * The top insets.
+ */
+ private int top;
+
+ /**
+ * The bottom insets.
+ */
+ private int bottom;
+
+ /**
+ * The border style on the left.
+ */
+ private int leftStyle;
+
+ /**
+ * The border style on the right.
+ */
+ private int rightStyle;
+
+ /**
+ * The border style on the top.
+ */
+ private int topStyle;
+
+ /**
+ * The color for the top border.
+ */
+ private Color topColor;
+
+ /**
+ * The color for the bottom border.
+ */
+ private Color bottomColor;
+
+ /**
+ * The color for the left border.
+ */
+ private Color leftColor;
+
+ /**
+ * The color for the right border.
+ */
+ private Color rightColor;
+
+ /**
+ * The border style on the bottom.
+ */
+ private int bottomStyle;
+
+ /**
+ * Creates a new CSS border and fetches its attributes from the specified
+ * attribute set.
+ *
+ * @param atts the attribute set that contains the border spec
+ */
+ CSSBorder(AttributeSet atts, StyleSheet ss)
+ {
+ // Determine the border styles.
+ int style = getBorderStyle(atts, CSS.Attribute.BORDER_STYLE);
+ if (style == STYLE_NOT_SET)
+ style = STYLE_NONE; // Default to none.
+ topStyle = bottomStyle = leftStyle = rightStyle = style;
+ style = getBorderStyle(atts, CSS.Attribute.BORDER_TOP_STYLE);
+ if (style != STYLE_NOT_SET)
+ topStyle = style;
+ style = getBorderStyle(atts, CSS.Attribute.BORDER_BOTTOM_STYLE);
+ if (style != STYLE_NOT_SET)
+ bottomStyle = style;
+ style = getBorderStyle(atts, CSS.Attribute.BORDER_LEFT_STYLE);
+ if (style != STYLE_NOT_SET)
+ leftStyle = style;
+ style = getBorderStyle(atts, CSS.Attribute.BORDER_RIGHT_STYLE);
+ if (style != STYLE_NOT_SET)
+ rightStyle = style;
+
+ // Determine the border colors.
+ Color color = getBorderColor(atts, CSS.Attribute.BORDER_COLOR);
+ if (color == null)
+ color = Color.BLACK;
+ topColor = bottomColor = leftColor = rightColor = color;
+ color = getBorderColor(atts, CSS.Attribute.BORDER_TOP_COLOR);
+ if (color != null)
+ topColor = color;
+ color = getBorderColor(atts, CSS.Attribute.BORDER_BOTTOM_COLOR);
+ if (color != null)
+ bottomColor = color;
+ color = getBorderColor(atts, CSS.Attribute.BORDER_LEFT_COLOR);
+ if (color != null)
+ leftColor = color;
+ color = getBorderColor(atts, CSS.Attribute.BORDER_RIGHT_COLOR);
+ if (color != null)
+ rightColor = color;
+
+ // Determine the border widths.
+ int width = getBorderWidth(atts, CSS.Attribute.BORDER_WIDTH, ss);
+ if (width == -1)
+ width = 0;
+ top = bottom = left = right = width;
+ width = getBorderWidth(atts, CSS.Attribute.BORDER_TOP_WIDTH, ss);
+ if (width >= 0)
+ top = width;
+ width = getBorderWidth(atts, CSS.Attribute.BORDER_BOTTOM_WIDTH, ss);
+ if (width >= 0)
+ bottom = width;
+ width = getBorderWidth(atts, CSS.Attribute.BORDER_LEFT_WIDTH, ss);
+ if (width >= 0)
+ left = width;
+ width = getBorderWidth(atts, CSS.Attribute.BORDER_RIGHT_WIDTH, ss);
+ if (width >= 0)
+ right = width;
+ }
+
+ /**
+ * Determines the border style for a given CSS attribute.
+ *
+ * @param atts the attribute set
+ * @param key the CSS key
+ *
+ * @return the border style according to the constants defined in this class
+ */
+ private int getBorderStyle(AttributeSet atts, CSS.Attribute key)
+ {
+ int style = STYLE_NOT_SET;
+ Object o = atts.getAttribute(key);
+ if (o != null)
+ {
+ String cssStyle = o.toString();
+ if (cssStyle.equals("none"))
+ style = STYLE_NONE;
+ else if (cssStyle.equals("hidden"))
+ style = STYLE_HIDDEN;
+ else if (cssStyle.equals("dotted"))
+ style = STYLE_DOTTED;
+ else if (cssStyle.equals("dashed"))
+ style = STYLE_DASHED;
+ else if (cssStyle.equals("solid"))
+ style = STYLE_SOLID;
+ else if (cssStyle.equals("double"))
+ style = STYLE_DOUBLE;
+ else if (cssStyle.equals("groove"))
+ style = STYLE_GROOVE;
+ else if (cssStyle.equals("ridge"))
+ style = STYLE_RIDGE;
+ else if (cssStyle.equals("inset"))
+ style = STYLE_INSET;
+ else if (cssStyle.equals("outset"))
+ style = STYLE_OUTSET;
+ }
+ return style;
+ }
+
+ /**
+ * Determines the border color for the specified key.
+ *
+ * @param atts the attribute set from which to fetch the color
+ * @param key the CSS key
+ *
+ * @return the border color
+ */
+ private Color getBorderColor(AttributeSet atts, CSS.Attribute key)
+ {
+ Object o = atts.getAttribute(key);
+ Color color = null;
+ if (o instanceof CSSColor)
+ {
+ CSSColor cssColor = (CSSColor) o;
+ color = cssColor.getValue();
+ }
+ return color;
+ }
+
+ /**
+ * Returns the width for the specified key.
+ *
+ * @param atts the attributes to fetch the width from
+ * @param key the CSS key
+ *
+ * @return the width, or -1 of none has been set
+ */
+ private int getBorderWidth(AttributeSet atts, CSS.Attribute key,
+ StyleSheet ss)
+ {
+ int width = -1;
+ Object o = atts.getAttribute(key);
+ if (o instanceof BorderWidth)
+ {
+ BorderWidth w = (BorderWidth) o;
+ w.setFontBases(ss.getEMBase(atts), ss.getEXBase(atts));
+ width = (int) ((BorderWidth) o).getValue();
+ }
+ return width;
+ }
+
+ /**
+ * Returns the border insets.
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return new Insets(top, left, bottom, right);
+ }
+
+ /**
+ * CSS borders are generally opaque so return true here.
+ */
+ public boolean isBorderOpaque()
+ {
+ return true;
+ }
+
+ public void paintBorder(Component c, Graphics g, int x, int y, int width,
+ int height)
+ {
+ // Top border.
+ paintBorderLine(g, x, y + top / 2, x + width, y + top / 2, topStyle, top,
+ topColor, false);
+ // Left border.
+ paintBorderLine(g, x + left / 2, y, x + left / 2, y + height, leftStyle,
+ left, leftColor, true);
+ // Bottom border.
+ paintBorderLine(g, x, y + height - bottom / 2, x + width,
+ y + height - bottom / 2, topStyle, bottom, bottomColor,
+ false);
+ // Right border.
+ paintBorderLine(g, x + width - right / 2, y, x + width - right / 2,
+ y + height, topStyle, right, rightColor, true);
+
+ }
+
+ private void paintBorderLine(Graphics g, int x1, int y1, int x2, int y2,
+ int style, int width, Color color,
+ boolean vertical)
+ {
+ switch (style)
+ {
+ case STYLE_DOTTED:
+ paintDottedLine(g, x1, y1, x2, y2, width, color, vertical);
+ break;
+ case STYLE_DASHED:
+ paintDashedLine(g, x1, y1, x2, y2, width, color, vertical);
+ break;
+ case STYLE_SOLID:
+ paintSolidLine(g, x1, y1, x2, y2, width, color, vertical);
+ break;
+ case STYLE_DOUBLE:
+ paintDoubleLine(g, x1, y1, x2, y2, width, color, vertical);
+ break;
+ case STYLE_GROOVE:
+ paintGrooveLine(g, x1, y1, x2, y2, width, color, vertical);
+ break;
+ case STYLE_RIDGE:
+ paintRidgeLine(g, x1, y1, x2, y2, width, color, vertical);
+ break;
+ case STYLE_OUTSET:
+ paintOutsetLine(g, x1, y1, x2, y2, width, color, vertical);
+ break;
+ case STYLE_INSET:
+ paintInsetLine(g, x1, y1, x2, y2, width, color, vertical);
+ break;
+ case STYLE_NONE:
+ case STYLE_HIDDEN:
+ default:
+ // Nothing to do in these cases.
+ }
+ }
+
+ private void paintDottedLine(Graphics g, int x1, int y1, int x2, int y2,
+ int width, Color color, boolean vertical)
+ {
+ // FIXME: Implement this.
+ paintSolidLine(g, x1, y1, x2, y2, width, color, vertical);
+ }
+
+ private void paintDashedLine(Graphics g, int x1, int y1, int x2, int y2,
+ int width, Color color, boolean vertical)
+ {
+ // FIXME: Implement this.
+ paintSolidLine(g, x1, y1, x2, y2, width, color, vertical);
+ }
+
+ private void paintSolidLine(Graphics g, int x1, int y1, int x2, int y2,
+ int width, Color color, boolean vertical)
+ {
+ int x = Math.min(x1, x2);
+ int y = Math.min(y1, y1);
+ int w = Math.abs(x2 - x1);
+ int h = Math.abs(y2 - y1);
+ if (vertical)
+ {
+ w = width;
+ x -= width / 2;
+ }
+ else
+ {
+ h = width;
+ y -= width / 2;
+ }
+ g.setColor(color);
+ g.fillRect(x, y, w, h);
+ }
+
+ private void paintDoubleLine(Graphics g, int x1, int y1, int x2, int y2,
+ int width, Color color, boolean vertical)
+ {
+ // FIXME: Implement this.
+ paintSolidLine(g, x1, y1, x2, y2, width, color, vertical);
+ }
+
+ private void paintGrooveLine(Graphics g, int x1, int y1, int x2, int y2,
+ int width, Color color, boolean vertical)
+ {
+ // FIXME: Implement this.
+ paintSolidLine(g, x1, y1, x2, y2, width, color, vertical);
+ }
+
+ private void paintRidgeLine(Graphics g, int x1, int y1, int x2, int y2,
+ int width, Color color, boolean vertical)
+ {
+ // FIXME: Implement this.
+ paintSolidLine(g, x1, y1, x2, y2, width, color, vertical);
+ }
+
+ private void paintOutsetLine(Graphics g, int x1, int y1, int x2, int y2,
+ int width, Color color, boolean vertical)
+ {
+ // FIXME: Implement this.
+ paintSolidLine(g, x1, y1, x2, y2, width, color, vertical);
+ }
+
+ private void paintInsetLine(Graphics g, int x1, int y1, int x2, int y2,
+ int width, Color color, boolean vertical)
+ {
+ // FIXME: Implement this.
+ paintSolidLine(g, x1, y1, x2, y2, width, color, vertical);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/text/html/FormSubmitEvent.java b/libjava/classpath/javax/swing/text/html/FormSubmitEvent.java
new file mode 100644
index 00000000000..bc7c36f4b27
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/FormSubmitEvent.java
@@ -0,0 +1,123 @@
+/* FormSubmitEvent.java -- Event fired on form submit
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text.html;
+
+import java.net.URL;
+
+import javax.swing.text.Element;
+
+/**
+ * The event fired on form submit.
+ *
+ * @since 1.5
+ */
+public class FormSubmitEvent
+ extends HTMLFrameHyperlinkEvent
+{
+
+ // FIXME: Use enums when available.
+ /**
+ * The submit method.
+ */
+ public static class MethodType
+ {
+ /**
+ * Indicates a form submit with HTTP method POST.
+ */
+ public static final MethodType POST = new MethodType();
+
+ /**
+ * Indicates a form submit with HTTP method GET.
+ */
+ public static final MethodType GET = new MethodType();
+
+ private MethodType()
+ {
+ }
+ }
+
+ /**
+ * The submit method.
+ */
+ private MethodType method;
+
+ /**
+ * The actual submit data.
+ */
+ private String data;
+
+ /**
+ * Creates a new FormSubmitEvent.
+ *
+ * @param source the source
+ * @param type the type of hyperlink update
+ * @param url the action url
+ * @param el the associated element
+ * @param target the target attribute
+ * @param m the submit method
+ * @param d the submit data
+ */
+ FormSubmitEvent(Object source, EventType type, URL url, Element el,
+ String target, MethodType m, String d)
+ {
+ super(source, type, url, el, target);
+ method = m;
+ data = d;
+ }
+
+ /**
+ * Returns the submit data.
+ *
+ * @return the submit data
+ */
+ public String getData()
+ {
+ return data;
+ }
+
+ /**
+ * Returns the HTTP submit method.
+ *
+ * @return the HTTP submit method
+ */
+ public MethodType getMethod()
+ {
+ return method;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/FormView.java b/libjava/classpath/javax/swing/text/html/FormView.java
index d54021066d0..ef362bd3d9b 100644
--- a/libjava/classpath/javax/swing/text/html/FormView.java
+++ b/libjava/classpath/javax/swing/text/html/FormView.java
@@ -44,16 +44,36 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLEncoder;
+import javax.swing.ButtonModel;
+import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JEditorPane;
+import javax.swing.JList;
import javax.swing.JPasswordField;
import javax.swing.JRadioButton;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
import javax.swing.JTextField;
+import javax.swing.ListSelectionModel;
+import javax.swing.SwingUtilities;
import javax.swing.UIManager;
+import javax.swing.event.HyperlinkEvent;
import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
import javax.swing.text.ComponentView;
+import javax.swing.text.Document;
import javax.swing.text.Element;
+import javax.swing.text.ElementIterator;
import javax.swing.text.StyleConstants;
/**
@@ -105,6 +125,231 @@ public class FormView
}
/**
+ * Actually submits the form data.
+ */
+ private class SubmitThread
+ extends Thread
+ {
+ /**
+ * The submit data.
+ */
+ private String data;
+
+ /**
+ * Creates a new SubmitThread.
+ *
+ * @param d the submit data
+ */
+ SubmitThread(String d)
+ {
+ data = d;
+ }
+
+ /**
+ * Actually performs the submit.
+ */
+ public void run()
+ {
+ if (data.length() > 0)
+ {
+ final String method = getMethod();
+ final URL actionURL = getActionURL();
+ final String target = getTarget();
+ URLConnection conn;
+ final JEditorPane editor = (JEditorPane) getContainer();
+ final HTMLDocument doc = (HTMLDocument) editor.getDocument();
+ HTMLEditorKit kit = (HTMLEditorKit) editor.getEditorKit();
+ if (kit.isAutoFormSubmission())
+ {
+ try
+ {
+ final URL url;
+ if (method != null && method.equals("post"))
+ {
+ // Perform POST.
+ url = actionURL;
+ conn = url.openConnection();
+ postData(conn, data);
+ }
+ else
+ {
+ // Default to GET.
+ url = new URL(actionURL + "?" + data);
+ }
+ Runnable loadDoc = new Runnable()
+ {
+ public void run()
+ {
+ if (doc.isFrameDocument())
+ {
+ editor.fireHyperlinkUpdate(createSubmitEvent(method,
+ actionURL,
+ target));
+ }
+ else
+ {
+ try
+ {
+ editor.setPage(url);
+ }
+ catch (IOException ex)
+ {
+ // Oh well.
+ ex.printStackTrace();
+ }
+ }
+ }
+ };
+ SwingUtilities.invokeLater(loadDoc);
+ }
+ catch (MalformedURLException ex)
+ {
+ ex.printStackTrace();
+ }
+ catch (IOException ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+ else
+ {
+ editor.fireHyperlinkUpdate(createSubmitEvent(method,actionURL,
+ target));
+ }
+ }
+ }
+
+ /**
+ * Determines the submit method.
+ *
+ * @return the submit method
+ */
+ private String getMethod()
+ {
+ AttributeSet formAtts = getFormAttributes();
+ String method = null;
+ if (formAtts != null)
+ {
+ method = (String) formAtts.getAttribute(HTML.Attribute.METHOD);
+ }
+ return method;
+ }
+
+ /**
+ * Determines the action URL.
+ *
+ * @return the action URL
+ */
+ private URL getActionURL()
+ {
+ AttributeSet formAtts = getFormAttributes();
+ HTMLDocument doc = (HTMLDocument) getElement().getDocument();
+ URL url = doc.getBase();
+ if (formAtts != null)
+ {
+ String action =
+ (String) formAtts.getAttribute(HTML.Attribute.ACTION);
+ if (action != null)
+ {
+ try
+ {
+ url = new URL(url, action);
+ }
+ catch (MalformedURLException ex)
+ {
+ url = null;
+ }
+ }
+ }
+ return url;
+ }
+
+ /**
+ * Fetches the target attribute.
+ *
+ * @return the target attribute or _self if none is present
+ */
+ private String getTarget()
+ {
+ AttributeSet formAtts = getFormAttributes();
+ String target = null;
+ if (formAtts != null)
+ {
+ target = (String) formAtts.getAttribute(HTML.Attribute.TARGET);
+ if (target != null)
+ target = target.toLowerCase();
+ }
+ if (target == null)
+ target = "_self";
+ return target;
+ }
+
+ /**
+ * Posts the form data over the specified connection.
+ *
+ * @param conn the connection
+ */
+ private void postData(URLConnection conn, String data)
+ {
+ conn.setDoOutput(true);
+ PrintWriter out = null;
+ try
+ {
+ out = new PrintWriter(new OutputStreamWriter(conn.getOutputStream()));
+ out.print(data);
+ out.flush();
+ }
+ catch (IOException ex)
+ {
+ // Deal with this!
+ ex.printStackTrace();
+ }
+ finally
+ {
+ if (out != null)
+ out.close();
+ }
+ }
+
+ /**
+ * Determines the attributes from the relevant form tag.
+ *
+ * @return the attributes from the relevant form tag, <code>null</code>
+ * when there is no form tag
+ */
+ private AttributeSet getFormAttributes()
+ {
+ AttributeSet atts = null;
+ Element form = getFormElement();
+ if (form != null)
+ atts = form.getAttributes();
+ return atts;
+ }
+
+ /**
+ * Creates the submit event that should be fired.
+ *
+ * This is package private to avoid accessor methods.
+ *
+ * @param method the submit method
+ * @param actionURL the action URL
+ * @param target the target
+ *
+ * @return the submit event
+ */
+ FormSubmitEvent createSubmitEvent(String method, URL actionURL,
+ String target)
+ {
+ FormSubmitEvent.MethodType m = "post".equals(method)
+ ? FormSubmitEvent.MethodType.POST
+ : FormSubmitEvent.MethodType.GET;
+ return new FormSubmitEvent(FormView.this,
+ HyperlinkEvent.EventType.ACTIVATED,
+ actionURL, getElement(), target, m, data);
+ }
+ }
+
+ /**
* If the value attribute of an <code>&lt;input type=&quot;submit&quot;&gt>
* tag is not specified, then this string is used.
*
@@ -125,6 +370,11 @@ public class FormView
UIManager.getString("FormView.resetButtonText");
/**
+ * If this is true, the maximum size is set to the preferred size.
+ */
+ private boolean maxIsPreferred;
+
+ /**
* Creates a new <code>FormView</code>.
*
* @param el the element that is displayed by this view.
@@ -141,39 +391,161 @@ public class FormView
{
Component comp = null;
Element el = getElement();
- Object tag = el.getAttributes().getAttribute(StyleConstants.NameAttribute);
+ AttributeSet atts = el.getAttributes();
+ Object tag = atts.getAttribute(StyleConstants.NameAttribute);
+ Object model = atts.getAttribute(StyleConstants.ModelAttribute);
if (tag.equals(HTML.Tag.INPUT))
{
- AttributeSet atts = el.getAttributes();
String type = (String) atts.getAttribute(HTML.Attribute.TYPE);
- String value = (String) atts.getAttribute(HTML.Attribute.VALUE);
if (type.equals("button"))
- comp = new JButton(value);
+ {
+ String value = (String) atts.getAttribute(HTML.Attribute.VALUE);
+ JButton b = new JButton(value);
+ if (model != null)
+ {
+ b.setModel((ButtonModel) model);
+ b.addActionListener(this);
+ }
+ comp = b;
+ maxIsPreferred = true;
+ }
else if (type.equals("checkbox"))
- comp = new JCheckBox(value);
+ {
+ if (model instanceof ResetableToggleButtonModel)
+ {
+ ResetableToggleButtonModel m =
+ (ResetableToggleButtonModel) model;
+ JCheckBox c = new JCheckBox();
+ c.setModel(m);
+ comp = c;
+ maxIsPreferred = true;
+ }
+ }
else if (type.equals("image"))
- comp = new JButton(value); // FIXME: Find out how to fetch the image.
+ {
+ String src = (String) atts.getAttribute(HTML.Attribute.SRC);
+ JButton b;
+ try
+ {
+ URL base = ((HTMLDocument) el.getDocument()).getBase();
+ URL srcURL = new URL(base, src);
+ ImageIcon icon = new ImageIcon(srcURL);
+ b = new JButton(icon);
+ }
+ catch (MalformedURLException ex)
+ {
+ b = new JButton(src);
+ }
+ if (model != null)
+ {
+ b.setModel((ButtonModel) model);
+ b.addActionListener(this);
+ }
+ comp = b;
+ maxIsPreferred = true;
+ }
else if (type.equals("password"))
- comp = new JPasswordField(value);
+ {
+ int size = HTML.getIntegerAttributeValue(atts, HTML.Attribute.SIZE,
+ -1);
+ JTextField tf = new JPasswordField();
+ if (size > 0)
+ tf.setColumns(size);
+ else
+ tf.setColumns(20);
+ if (model != null)
+ tf.setDocument((Document) model);
+ tf.addActionListener(this);
+ comp = tf;
+ maxIsPreferred = true;
+ }
else if (type.equals("radio"))
- comp = new JRadioButton(value);
+ {
+ if (model instanceof ResetableToggleButtonModel)
+ {
+ ResetableToggleButtonModel m =
+ (ResetableToggleButtonModel) model;
+ JRadioButton c = new JRadioButton();
+ c.setModel(m);
+ comp = c;
+ maxIsPreferred = true;
+ }
+ }
else if (type.equals("reset"))
{
- if (value == null || value.equals(""))
- value = RESET;
- comp = new JButton(value);
+ String value = (String) atts.getAttribute(HTML.Attribute.VALUE);
+ if (value == null)
+ value = UIManager.getString("FormView.resetButtonText");
+ JButton b = new JButton(value);
+ if (model != null)
+ {
+ b.setModel((ButtonModel) model);
+ b.addActionListener(this);
+ }
+ comp = b;
+ maxIsPreferred = true;
}
else if (type.equals("submit"))
{
- if (value == null || value.equals(""))
- value = SUBMIT;
- comp = new JButton(value);
+ String value = (String) atts.getAttribute(HTML.Attribute.VALUE);
+ if (value == null)
+ value = UIManager.getString("FormView.submitButtonText");
+ JButton b = new JButton(value);
+ if (model != null)
+ {
+ b.setModel((ButtonModel) model);
+ b.addActionListener(this);
+ }
+ comp = b;
+ maxIsPreferred = true;
}
else if (type.equals("text"))
- comp = new JTextField(value);
-
+ {
+ int size = HTML.getIntegerAttributeValue(atts, HTML.Attribute.SIZE,
+ -1);
+ JTextField tf = new JTextField();
+ if (size > 0)
+ tf.setColumns(size);
+ else
+ tf.setColumns(20);
+ if (model != null)
+ tf.setDocument((Document) model);
+ tf.addActionListener(this);
+ comp = tf;
+ maxIsPreferred = true;
+ }
+ }
+ else if (tag == HTML.Tag.TEXTAREA)
+ {
+ JTextArea textArea = new JTextArea((Document) model);
+ int rows = HTML.getIntegerAttributeValue(atts, HTML.Attribute.ROWS, 1);
+ textArea.setRows(rows);
+ int cols = HTML.getIntegerAttributeValue(atts, HTML.Attribute.COLS, 20);
+ textArea.setColumns(cols);
+ maxIsPreferred = true;
+ comp = new JScrollPane(textArea,
+ JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+ JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
+ }
+ else if (tag == HTML.Tag.SELECT)
+ {
+ if (model instanceof SelectListModel)
+ {
+ SelectListModel slModel = (SelectListModel) model;
+ JList list = new JList(slModel);
+ int size = HTML.getIntegerAttributeValue(atts, HTML.Attribute.SIZE,
+ 1);
+ list.setVisibleRowCount(size);
+ list.setSelectionModel(slModel.getSelectionModel());
+ comp = new JScrollPane(list);
+ }
+ else if (model instanceof SelectComboBoxModel)
+ {
+ SelectComboBoxModel scbModel = (SelectComboBoxModel) model;
+ comp = new JComboBox(scbModel);
+ }
+ maxIsPreferred = true;
}
- // FIXME: Implement the remaining components.
return comp;
}
@@ -188,16 +560,11 @@ public class FormView
*/
public float getMaximumSpan(int axis)
{
- // FIXME: The specs say that for some components the maximum span == the
- // preferred span of the component. This should be figured out and
- // implemented accordingly.
float span;
- if (axis == X_AXIS)
- span = getComponent().getMaximumSize().width;
- else if (axis == Y_AXIS)
- span = getComponent().getMaximumSize().height;
+ if (maxIsPreferred)
+ span = getPreferredSpan(axis);
else
- throw new IllegalArgumentException("Invalid axis parameter");
+ span = super.getMaximumSpan(axis);
return span;
}
@@ -222,7 +589,9 @@ public class FormView
AttributeSet atts = el.getAttributes();
String type = (String) atts.getAttribute(HTML.Attribute.TYPE);
if (type.equals("submit"))
- submitData(""); // FIXME: How to fetch the actual form data?
+ submitData(getFormData());
+ else if (type.equals("reset"))
+ resetForm();
}
// FIXME: Implement the remaining actions.
}
@@ -235,7 +604,8 @@ public class FormView
*/
protected void submitData(String data)
{
- // FIXME: Implement this.
+ SubmitThread submitThread = new SubmitThread(data);
+ submitThread.start();
}
/**
@@ -272,4 +642,229 @@ public class FormView
}
return data;
}
+
+ /**
+ * Determines and returns the enclosing form element if there is any.
+ *
+ * This is package private to avoid accessor methods.
+ *
+ * @return the enclosing form element, or <code>null</code> if there is no
+ * enclosing form element
+ */
+ Element getFormElement()
+ {
+ Element form = null;
+ Element el = getElement();
+ while (el != null && form == null)
+ {
+ AttributeSet atts = el.getAttributes();
+ if (atts.getAttribute(StyleConstants.NameAttribute) == HTML.Tag.FORM)
+ form = el;
+ else
+ el = el.getParentElement();
+ }
+ return form;
+ }
+
+ /**
+ * Determines the form data that is about to be submitted.
+ *
+ * @return the form data
+ */
+ private String getFormData()
+ {
+ Element form = getFormElement();
+ StringBuilder b = new StringBuilder();
+ if (form != null)
+ {
+ ElementIterator i = new ElementIterator(form);
+ Element next;
+ while ((next = i.next()) != null)
+ {
+ if (next.isLeaf())
+ {
+ AttributeSet atts = next.getAttributes();
+ String type = (String) atts.getAttribute(HTML.Attribute.TYPE);
+ if (type != null && type.equals("submit")
+ && next != getElement())
+ {
+ // Skip this. This is not the actual submit trigger.
+ }
+ else if (type == null || ! type.equals("image"))
+ {
+ getElementFormData(next, b);
+ }
+ }
+ }
+ }
+ return b.toString();
+ }
+
+ /**
+ * Fetches the form data from the specified element and appends it to
+ * the data string.
+ *
+ * @param el the element from which to fetch form data
+ * @param b the data string
+ */
+ private void getElementFormData(Element el, StringBuilder b)
+ {
+ AttributeSet atts = el.getAttributes();
+ String name = (String) atts.getAttribute(HTML.Attribute.NAME);
+ if (name != null)
+ {
+ String value = null;
+ HTML.Tag tag = (HTML.Tag) atts.getAttribute(StyleConstants.NameAttribute);
+ if (tag == HTML.Tag.SELECT)
+ {
+ getSelectData(atts, b);
+ }
+ else
+ {
+ if (tag == HTML.Tag.INPUT)
+ value = getInputFormData(atts);
+ else if (tag == HTML.Tag.TEXTAREA)
+ value = getTextAreaData(atts);
+ if (name != null && value != null)
+ {
+ addData(b, name, value);
+ }
+ }
+ }
+ }
+
+ /**
+ * Fetches form data from select boxes.
+ *
+ * @param atts the attributes of the element
+ *
+ * @param b the form data string to append to
+ */
+ private void getSelectData(AttributeSet atts, StringBuilder b)
+ {
+ String name = (String) atts.getAttribute(HTML.Attribute.NAME);
+ if (name != null)
+ {
+ Object m = atts.getAttribute(StyleConstants.ModelAttribute);
+ if (m instanceof SelectListModel)
+ {
+ SelectListModel sl = (SelectListModel) m;
+ ListSelectionModel lsm = sl.getSelectionModel();
+ for (int i = 0; i < sl.getSize(); i++)
+ {
+ if (lsm.isSelectedIndex(i))
+ {
+ Option o = (Option) sl.getElementAt(i);
+ addData(b, name, o.getValue());
+ }
+ }
+ }
+ else if (m instanceof SelectComboBoxModel)
+ {
+ SelectComboBoxModel scb = (SelectComboBoxModel) m;
+ Option o = (Option) scb.getSelectedItem();
+ if (o != null)
+ addData(b, name, o.getValue());
+ }
+ }
+ }
+
+ /**
+ * Fetches form data from a textarea.
+ *
+ * @param atts the attributes
+ *
+ * @return the form data
+ */
+ private String getTextAreaData(AttributeSet atts)
+ {
+ Document doc = (Document) atts.getAttribute(StyleConstants.ModelAttribute);
+ String data;
+ try
+ {
+ data = doc.getText(0, doc.getLength());
+ }
+ catch (BadLocationException ex)
+ {
+ data = null;
+ }
+ return data;
+ }
+
+ /**
+ * Fetches form data from an input tag.
+ *
+ * @param atts the attributes from which to fetch the data
+ *
+ * @return the field value
+ */
+ private String getInputFormData(AttributeSet atts)
+ {
+ String type = (String) atts.getAttribute(HTML.Attribute.TYPE);
+ Object model = atts.getAttribute(StyleConstants.ModelAttribute);
+ String value = null;
+ if (type.equals("text") || type.equals("password"))
+ {
+ Document doc = (Document) model;
+ try
+ {
+ value = doc.getText(0, doc.getLength());
+ }
+ catch (BadLocationException ex)
+ {
+ // Sigh.
+ assert false;
+ }
+ }
+ else if (type.equals("hidden") || type.equals("submit"))
+ {
+ value = (String) atts.getAttribute(HTML.Attribute.VALUE);
+ if (value == null)
+ value = "";
+ }
+ // TODO: Implement the others. radio, checkbox and file.
+ return value;
+ }
+
+ /**
+ * Actually adds the specified data to the string. It URL encodes
+ * the name and value and handles separation of the fields.
+ *
+ * @param b the string at which the form data to be added
+ * @param name the name of the field
+ * @param value the value
+ */
+ private void addData(StringBuilder b, String name, String value)
+ {
+ if (b.length() > 0)
+ b.append('&');
+ String encName = URLEncoder.encode(name);
+ b.append(encName);
+ b.append('=');
+ String encValue = URLEncoder.encode(value);
+ b.append(encValue);
+ }
+
+ /**
+ * Resets the form data to their initial state.
+ */
+ private void resetForm()
+ {
+ Element form = getFormElement();
+ if (form != null)
+ {
+ ElementIterator iter = new ElementIterator(form);
+ Element next;
+ while ((next = iter.next()) != null)
+ {
+ if (next.isLeaf())
+ {
+ AttributeSet atts = next.getAttributes();
+ Object m = atts.getAttribute(StyleConstants.ModelAttribute);
+ if (m instanceof ResetableModel)
+ ((ResetableModel) m).reset();
+ }
+ }
+ }
+ }
}
diff --git a/libjava/classpath/javax/swing/text/html/FrameSetView.java b/libjava/classpath/javax/swing/text/html/FrameSetView.java
new file mode 100644
index 00000000000..e3252d79caf
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/FrameSetView.java
@@ -0,0 +1,274 @@
+/* FrameSetView.java -- Implements HTML frameset
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text.html;
+
+import java.util.StringTokenizer;
+
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BoxView;
+import javax.swing.text.Element;
+import javax.swing.text.View;
+import javax.swing.text.ViewFactory;
+
+/**
+ * Implements HTML framesets. This is implemented as a vertical box that
+ * holds the rows of the frameset. Each row is again a horizontal box that
+ * holds the actual columns.
+ */
+public class FrameSetView
+ extends BoxView
+{
+
+ /**
+ * A row of a frameset.
+ */
+ private class FrameSetRow
+ extends BoxView
+ {
+ private int row;
+ FrameSetRow(Element el, int r)
+ {
+ super(el, X_AXIS);
+ row = r;
+ }
+
+ protected void loadChildren(ViewFactory f)
+ {
+ // Load the columns here.
+ Element el = getElement();
+ View[] columns = new View[numViews[X_AXIS]];
+ int offset = row * numViews[X_AXIS];
+ for (int c = 0; c < numViews[X_AXIS]; c++)
+ {
+ Element child = el.getElement(offset + c);
+ columns[c] = f.create(child);
+ }
+ replace(0, 0, columns);
+ }
+
+ protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets,
+ int[] spans)
+ {
+ int numRows = numViews[X_AXIS];
+ int[] abs = absolute[X_AXIS];
+ int[] rel = relative[X_AXIS];
+ int[] perc = percent[X_AXIS];
+ layoutViews(targetSpan, axis, offsets, spans, numRows, abs, rel, perc);
+ }
+ }
+
+ /**
+ * Holds the absolute layout information for the views along one axis. The
+ * indices are absolute[axis][index], where axis is either X_AXIS (columns)
+ * or Y_AXIS (rows). Rows or columns that don't have absolute layout have
+ * a -1 in this array.
+ */
+ int[][] absolute;
+
+ /**
+ * Holds the relative (*) layout information for the views along one axis.
+ * The indices are relative[axis][index], where axis is either X_AXIS
+ * (columns) or Y_AXIS (rows). Rows or columns that don't have relative
+ * layout have a Float.NaN in this array.
+ */
+ int[][] relative;
+
+ /**
+ * Holds the relative (%) layout information for the views along one axis.
+ * The indices are relative[axis][index], where axis is either X_AXIS
+ * (columns) or Y_AXIS (rows). Rows or columns that don't have relative
+ * layout have a Float.NaN in this array.
+ *
+ * The percentage is divided by 100 so that we hold the actual fraction here.
+ */
+ int[][] percent;
+
+ /**
+ * The number of children in each direction.
+ */
+ int[] numViews;
+
+ FrameSetView(Element el)
+ {
+ super(el, Y_AXIS);
+ numViews = new int[2];
+ absolute = new int[2][];
+ relative = new int[2][];
+ percent = new int[2][];
+ }
+
+ /**
+ * Loads the children and places them inside the grid.
+ */
+ protected void loadChildren(ViewFactory f)
+ {
+ parseRowsCols();
+ // Set up the rows.
+ View[] rows = new View[numViews[Y_AXIS]];
+ for (int r = 0; r < numViews[Y_AXIS]; r++)
+ {
+ rows[r] = new FrameSetRow(getElement(), r);
+ }
+ replace(0, 0, rows);
+ }
+
+ /**
+ * Parses the rows and cols attributes and sets up the layout info.
+ */
+ private void parseRowsCols()
+ {
+ Element el = getElement();
+ AttributeSet atts = el.getAttributes();
+ String cols = (String) atts.getAttribute(HTML.Attribute.COLS);
+ if (cols == null) // Defaults to '100%' when not specified.
+ cols = "100%";
+ parseLayout(cols, X_AXIS);
+ String rows = (String) atts.getAttribute(HTML.Attribute.ROWS);
+ if (rows == null) // Defaults to '100%' when not specified.
+ rows = "100%";
+ parseLayout(rows, Y_AXIS);
+ }
+
+ /**
+ * Parses the cols or rows attribute and places the layout info in the
+ * appropriate arrays.
+ *
+ * @param att the attributes to parse
+ * @param axis the axis
+ */
+ private void parseLayout(String att, int axis)
+ {
+ StringTokenizer tokens = new StringTokenizer(att, ",");
+ numViews[axis] = tokens.countTokens();
+ absolute[axis] = new int[numViews[axis]];
+ relative[axis] = new int[numViews[axis]];
+ percent[axis] = new int[numViews[axis]];
+ for (int index = 0; tokens.hasMoreTokens(); index++)
+ {
+ String token = tokens.nextToken();
+ int p = token.indexOf('%');
+ int s = token.indexOf('*');
+ if (p != -1)
+ {
+ // Percent value.
+ String number = token.substring(0, p);
+ try
+ {
+ percent[axis][index] = Integer.parseInt(number);
+ }
+ catch (NumberFormatException ex)
+ {
+ // Leave value as 0 then.
+ }
+ }
+ else if (s != -1)
+ {
+ // Star relative value.
+ String number = token.substring(0, s);
+ try
+ {
+ relative[axis][index] = Integer.parseInt(number);
+ }
+ catch (NumberFormatException ex)
+ {
+ // Leave value as 0 then.
+ }
+ }
+ else
+ {
+ // Absolute value.
+ try
+ {
+ absolute[axis][index] = Integer.parseInt(token);
+ }
+ catch (NumberFormatException ex)
+ {
+ // Leave value as 0 then.
+ }
+ }
+ }
+ }
+
+ protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets,
+ int[] spans)
+ {
+ int numRows = numViews[Y_AXIS];
+ int[] abs = absolute[Y_AXIS];
+ int[] rel = relative[Y_AXIS];
+ int[] perc = percent[Y_AXIS];
+ layoutViews(targetSpan, axis, offsets, spans, numRows, abs, rel, perc);
+ }
+
+ void layoutViews(int targetSpan, int axis, int[] offsets, int[] spans,
+ int numViews, int[] abs, int[] rel, int[] perc)
+ {
+ // We need two passes. In the first pass we layout the absolute and
+ // percent values and accumulate the needed space. In the second pass
+ // the relative values are distributed and the offsets are set.
+ int total = 0;
+ int relTotal = 0;
+ for (int i = 0; i < numViews; i++)
+ {
+ if (abs[i] > 0)
+ {
+ spans[i] = abs[i];
+ total += spans[i];
+ }
+ else if (perc[i] > 0)
+ {
+ spans[i] = (targetSpan * perc[i]) / 100;
+ total += spans[i];
+ }
+ else if (rel[i] > 0)
+ {
+ relTotal += rel[i];
+ }
+ }
+ int offs = 0;
+ for (int i = 0; i < numViews; i++)
+ {
+ if (relTotal > 0 && rel[i] > 0)
+ {
+ spans[i] = targetSpan * (rel[i] / relTotal);
+ }
+ offsets[i] = offs;
+ offs += spans[i];
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/FrameView.java b/libjava/classpath/javax/swing/text/html/FrameView.java
new file mode 100644
index 00000000000..cd4e44a98ce
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/FrameView.java
@@ -0,0 +1,233 @@
+/* FrameView.java -- Renders HTML frame tags
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text.html;
+
+import java.awt.Component;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.swing.JEditorPane;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.event.HyperlinkListener;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.ComponentView;
+import javax.swing.text.Element;
+import javax.swing.text.View;
+
+/**
+ * A view that is responsible for rendering HTML frame tags.
+ * This is accomplished by a specialized {@link ComponentView}
+ * that embeds a JEditorPane with an own document.
+ */
+class FrameView
+ extends ComponentView
+ implements HyperlinkListener
+{
+
+ /**
+ * Creates a new FrameView for the specified element.
+ *
+ * @param el the element for the view
+ */
+ FrameView(Element el)
+ {
+ super(el);
+ }
+
+ /**
+ * Creates the element that will be embedded in the view.
+ * This will be a JEditorPane with the appropriate content set.
+ *
+ * @return the element that will be embedded in the view
+ */
+ protected Component createComponent()
+ {
+ Element el = getElement();
+ AttributeSet atts = el.getAttributes();
+ JEditorPane html = new JEditorPane();
+ html.addHyperlinkListener(this);
+ URL base = ((HTMLDocument) el.getDocument()).getBase();
+ String srcAtt = (String) atts.getAttribute(HTML.Attribute.SRC);
+ if (srcAtt != null && ! srcAtt.equals(""))
+ {
+ try
+ {
+ URL page = new URL(base, srcAtt);
+ html.setPage(page);
+ ((HTMLDocument) html.getDocument()).setFrameDocument(true);
+ }
+ catch (MalformedURLException ex)
+ {
+ // Leave page empty.
+ }
+ catch (IOException ex)
+ {
+ // Leave page empty.
+ }
+ }
+ return html;
+ }
+
+ /**
+ * Catches hyperlink events on that frame's editor and forwards it to
+ * the outermost editorpane.
+ */
+ public void hyperlinkUpdate(HyperlinkEvent event)
+ {
+ JEditorPane outer = getTopEditorPane();
+ if (outer != null)
+ {
+ if (event instanceof HTMLFrameHyperlinkEvent)
+ {
+ HTMLFrameHyperlinkEvent hfhe = (HTMLFrameHyperlinkEvent) event;
+ if (hfhe.getEventType() == HyperlinkEvent.EventType.ACTIVATED)
+ {
+ String target = hfhe.getTarget();
+ if (event instanceof FormSubmitEvent)
+ {
+ handleFormSubmitEvent(hfhe, outer, target);
+ }
+ else // No FormSubmitEvent.
+ {
+ handleHyperlinkEvent(hfhe, outer, target);
+ }
+ }
+ }
+ else
+ {
+ // Simply forward this event.
+ outer.fireHyperlinkUpdate(event);
+ }
+ }
+ }
+
+ /**
+ * Handles normal hyperlink events.
+ *
+ * @param event the event
+ * @param outer the top editor
+ * @param target the target
+ */
+ private void handleHyperlinkEvent(HyperlinkEvent event,
+ JEditorPane outer, String target)
+ {
+ if (target.equals("_top"))
+ {
+ try
+ {
+ outer.setPage(event.getURL());
+ }
+ catch (IOException ex)
+ {
+ // Well...
+ ex.printStackTrace();
+ }
+ }
+ if (! outer.isEditable())
+ {
+ outer.fireHyperlinkUpdate
+ (new HTMLFrameHyperlinkEvent(outer,
+ event.getEventType(),
+ event.getURL(),
+ event.getDescription(),
+ getElement(),
+ target));
+ }
+ }
+
+ /**
+ * Handles form submit events.
+ *
+ * @param event the event
+ * @param outer the top editor
+ * @param target the target
+ */
+ private void handleFormSubmitEvent(HTMLFrameHyperlinkEvent event,
+ JEditorPane outer,
+ String target)
+ {
+ HTMLEditorKit kit = (HTMLEditorKit) outer.getEditorKit();
+ if (kit != null && kit.isAutoFormSubmission())
+ {
+ if (target.equals("_top"))
+ {
+ try
+ {
+ outer.setPage(event.getURL());
+ }
+ catch (IOException ex)
+ {
+ // Well...
+ ex.printStackTrace();
+ }
+ }
+ else
+ {
+ HTMLDocument doc =
+ (HTMLDocument) outer.getDocument();
+ doc.processHTMLFrameHyperlinkEvent(event);
+ }
+ }
+ else
+ {
+ outer.fireHyperlinkUpdate(event);
+ }
+ }
+
+ /**
+ * Determines the topmost editor in a nested frameset.
+ *
+ * @return the topmost editor in a nested frameset
+ */
+ private JEditorPane getTopEditorPane()
+ {
+ View parent = getParent();
+ View top = null;
+ while (parent != null)
+ {
+ if (parent instanceof FrameSetView)
+ top = parent;
+ }
+ JEditorPane editor = null;
+ if (top != null)
+ editor = (JEditorPane) top.getContainer();
+ return editor;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/HTML.java b/libjava/classpath/javax/swing/text/html/HTML.java
index 2c908f6fc6e..93c05daa2f8 100644
--- a/libjava/classpath/javax/swing/text/html/HTML.java
+++ b/libjava/classpath/javax/swing/text/html/HTML.java
@@ -465,6 +465,16 @@ public class HTML
public static final Attribute WIDTH = new Attribute("width");
/**
+ * This is used to reflect the pseudo class for the a tag.
+ */
+ static final Attribute PSEUDO_CLASS = new Attribute("_pseudo");
+
+ /**
+ * This is used to reflect the dynamic class for the a tag.
+ */
+ static final Attribute DYNAMIC_CLASS = new Attribute("_dynamic");
+
+ /**
* The attribute name.
*/
private final String name;
@@ -1119,8 +1129,8 @@ public class HTML
static final int BLOCK = 2;
static final int PREFORMATTED = 4;
static final int SYNTHETIC = 8;
- private static Map tagMap;
- private static Map attrMap;
+ private static Map<String,Tag> tagMap;
+ private static Map<String,Attribute> attrMap;
/**
* The public constructor (does nothing). It it seldom required to have
@@ -1159,7 +1169,7 @@ public class HTML
if (attrMap == null)
{
// Create the map on demand.
- attrMap = new TreeMap();
+ attrMap = new TreeMap<String,Attribute>();
Attribute[] attrs = getAllAttributeKeys();
@@ -1169,7 +1179,7 @@ public class HTML
}
}
- return (Attribute) attrMap.get(attName.toLowerCase());
+ return attrMap.get(attName.toLowerCase());
}
/**
@@ -1228,7 +1238,7 @@ public class HTML
if (tagMap == null)
{
// Create the mao on demand.
- tagMap = new TreeMap();
+ tagMap = new TreeMap<String,Tag>();
Tag[] tags = getAllTags();
@@ -1238,6 +1248,6 @@ public class HTML
}
}
- return (Tag) tagMap.get(tagName.toLowerCase());
+ return tagMap.get(tagName.toLowerCase());
}
}
diff --git a/libjava/classpath/javax/swing/text/html/HTMLDocument.java b/libjava/classpath/javax/swing/text/html/HTMLDocument.java
index 0bfc338df45..f3d3ce3faaf 100644
--- a/libjava/classpath/javax/swing/text/html/HTMLDocument.java
+++ b/libjava/classpath/javax/swing/text/html/HTMLDocument.java
@@ -39,19 +39,22 @@ exception statement from your version. */
package javax.swing.text.html;
import gnu.classpath.NotImplementedException;
-import gnu.javax.swing.text.html.CharacterAttributeTranslator;
-import gnu.javax.swing.text.html.parser.htmlAttributeSet;
import java.io.IOException;
import java.io.StringReader;
+import java.net.MalformedURLException;
import java.net.URL;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.Stack;
import java.util.Vector;
+import javax.swing.ButtonGroup;
+import javax.swing.DefaultButtonModel;
import javax.swing.JEditorPane;
+import javax.swing.ListSelectionModel;
import javax.swing.event.DocumentEvent;
-import javax.swing.event.HyperlinkEvent.EventType;
+import javax.swing.event.UndoableEditEvent;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
@@ -60,6 +63,7 @@ import javax.swing.text.Element;
import javax.swing.text.ElementIterator;
import javax.swing.text.GapContent;
import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.PlainDocument;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.html.HTML.Tag;
@@ -87,16 +91,24 @@ public class HTMLDocument extends DefaultStyledDocument
boolean preservesUnknownTags = true;
int tokenThreshold = Integer.MAX_VALUE;
HTMLEditorKit.Parser parser;
- StyleSheet styleSheet;
- AbstractDocument.Content content;
-
+
+ /**
+ * Indicates whether this document is inside a frame or not.
+ */
+ private boolean frameDocument;
+
+ /**
+ * Package private to avoid accessor methods.
+ */
+ String baseTarget;
+
/**
* Constructs an HTML document using the default buffer size and a default
* StyleSheet.
*/
public HTMLDocument()
{
- this(null);
+ this(new GapContent(BUFFER_SIZE_DEFAULT), new StyleSheet());
}
/**
@@ -119,14 +131,7 @@ public class HTMLDocument extends DefaultStyledDocument
*/
public HTMLDocument(AbstractDocument.Content c, StyleSheet styles)
{
- this.content = c;
- if (styles == null)
- {
- styles = new StyleSheet();
- styles.importStyleSheet(getClass().getResource(HTMLEditorKit.
- DEFAULT_CSS));
- }
- this.styleSheet = styles;
+ super(c, styles);
}
/**
@@ -137,7 +142,7 @@ public class HTMLDocument extends DefaultStyledDocument
*/
public StyleSheet getStyleSheet()
{
- return styleSheet;
+ return (StyleSheet) getAttributeContext();
}
/**
@@ -191,8 +196,6 @@ public class HTMLDocument extends DefaultStyledDocument
protected Element createLeafElement(Element parent, AttributeSet a, int p0,
int p1)
{
- RunElement el = new RunElement(parent, a, p0, p1);
- el.addAttribute(StyleConstants.NameAttribute, HTML.Tag.CONTENT);
return new RunElement(parent, a, p0, p1);
}
@@ -269,7 +272,7 @@ public class HTMLDocument extends DefaultStyledDocument
public void setBase(URL u)
{
baseURL = u;
- styleSheet.setBase(u);
+ getStyleSheet().setBase(u);
}
/**
@@ -374,11 +377,119 @@ public class HTMLDocument extends DefaultStyledDocument
}
public void processHTMLFrameHyperlinkEvent(HTMLFrameHyperlinkEvent event)
- throws NotImplementedException
{
- // TODO: Implement this properly.
+ String target = event.getTarget();
+ Element el = event.getSourceElement();
+ URL url = event.getURL();
+ if (target.equals("_self"))
+ {
+ updateFrame(el, url);
+ }
+ else if (target.equals("_parent"))
+ {
+ updateFrameSet(el.getParentElement(), url);
+ }
+ else
+ {
+ Element targetFrame = findFrame(target);
+ if (targetFrame != null)
+ updateFrame(targetFrame, url);
+ }
}
-
+
+ /**
+ * Finds the named frame inside this document.
+ *
+ * @param target the name to look for
+ *
+ * @return the frame if there is a matching frame, <code>null</code>
+ * otherwise
+ */
+ private Element findFrame(String target)
+ {
+ ElementIterator i = new ElementIterator(this);
+ Element next = null;
+ while ((next = i.next()) != null)
+ {
+ AttributeSet atts = next.getAttributes();
+ if (atts.getAttribute(StyleConstants.NameAttribute) == HTML.Tag.FRAME)
+ {
+ String name = (String) atts.getAttribute(HTML.Attribute.NAME);
+ if (name != null && name.equals(target))
+ break;
+ }
+ }
+ return next;
+ }
+
+ /**
+ * Updates the frame that is represented by the specified element to
+ * refer to the specified URL.
+ *
+ * @param el the element
+ * @param url the new url
+ */
+ private void updateFrame(Element el, URL url)
+ {
+ try
+ {
+ writeLock();
+ DefaultDocumentEvent ev =
+ new DefaultDocumentEvent(el.getStartOffset(), 1,
+ DocumentEvent.EventType.CHANGE);
+ AttributeSet elAtts = el.getAttributes();
+ AttributeSet copy = elAtts.copyAttributes();
+ MutableAttributeSet matts = (MutableAttributeSet) elAtts;
+ ev.addEdit(new AttributeUndoableEdit(el, copy, false));
+ matts.removeAttribute(HTML.Attribute.SRC);
+ matts.addAttribute(HTML.Attribute.SRC, url.toString());
+ ev.end();
+ fireChangedUpdate(ev);
+ fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
+ }
+ finally
+ {
+ writeUnlock();
+ }
+ }
+
+ /**
+ * Updates the frameset that is represented by the specified element
+ * to create a frame that refers to the specified URL.
+ *
+ * @param el the element
+ * @param url the url
+ */
+ private void updateFrameSet(Element el, URL url)
+ {
+ int start = el.getStartOffset();
+ int end = el.getEndOffset();
+
+ StringBuilder html = new StringBuilder();
+ html.append("<frame");
+ if (url != null)
+ {
+ html.append(" src=\"");
+ html.append(url.toString());
+ html.append("\"");
+ }
+ html.append('>');
+ if (getParser() == null)
+ setParser(new HTMLEditorKit().getParser());
+ try
+ {
+ setOuterHTML(el, html.toString());
+ }
+ catch (BadLocationException ex)
+ {
+ ex.printStackTrace();
+ }
+ catch (IOException ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+
/**
* Gets an iterator for the given HTML.Tag.
* @param t the requested HTML.Tag
@@ -461,6 +572,8 @@ public class HTMLDocument extends DefaultStyledDocument
String name = null;
if (tag != null)
name = tag.toString();
+ if (name == null)
+ name = super.getName();
return name;
}
}
@@ -497,6 +610,8 @@ public class HTMLDocument extends DefaultStyledDocument
String name = null;
if (tag != null)
name = tag.toString();
+ if (name == null)
+ name = super.getName();
return name;
}
@@ -518,24 +633,33 @@ public class HTMLDocument extends DefaultStyledDocument
* @author Anthony Balkissoon abalkiss at redhat dot com
*/
public class HTMLReader extends HTMLEditorKit.ParserCallback
- {
+ {
+ /**
+ * The maximum token threshold. We don't grow it larger than this.
+ */
+ private static final int MAX_THRESHOLD = 10000;
+
+ /**
+ * The threshold growth factor.
+ */
+ private static final int GROW_THRESHOLD = 5;
+
/**
* Holds the current character attribute set *
*/
protected MutableAttributeSet charAttr = new SimpleAttributeSet();
- protected Vector parseBuffer = new Vector();
-
+ protected Vector<ElementSpec> parseBuffer = new Vector<ElementSpec>();
+
+ /**
+ * The parse stack. It holds the current element tree path.
+ */
+ private Stack<HTML.Tag> parseStack = new Stack<HTML.Tag>();
+
/**
* A stack for character attribute sets *
*/
Stack charAttrStack = new Stack();
-
- /**
- * The parse stack. This stack holds HTML.Tag objects that reflect the
- * current position in the parsing process.
- */
- Stack parseStack = new Stack();
/** A mapping between HTML.Tag objects and the actions that handle them **/
HashMap tagToAction;
@@ -571,13 +695,68 @@ public class HTMLDocument extends DefaultStyledDocument
/** A temporary variable that helps with the printing out of debug information **/
boolean debug = false;
-
- void print (String line)
- {
- if (debug)
- System.out.println (line);
- }
-
+
+ /**
+ * This is true when we are inside a pre tag.
+ */
+ boolean inPreTag = false;
+
+ /**
+ * This is true when we are inside a style tag. This will add text
+ * content inside this style tag beeing parsed as CSS.
+ *
+ * This is package private to avoid accessor methods.
+ */
+ boolean inStyleTag = false;
+
+ /**
+ * This is true when we are inside a &lt;textarea&gt; tag. Any text
+ * content will then be added to the text area.
+ *
+ * This is package private to avoid accessor methods.
+ */
+ boolean inTextArea = false;
+
+ /**
+ * This contains all stylesheets that are somehow read, either
+ * via embedded style tags, or via linked stylesheets. The
+ * elements will be String objects containing a stylesheet each.
+ */
+ ArrayList styles;
+
+ /**
+ * The document model for a textarea.
+ *
+ * This is package private to avoid accessor methods.
+ */
+ ResetablePlainDocument textAreaDocument;
+
+ /**
+ * The current model of a select tag. Can be a ComboBoxModel or a
+ * ListModel depending on the type of the select box.
+ */
+ Object selectModel;
+
+ /**
+ * The current option beeing read.
+ */
+ Option option;
+
+ /**
+ * The current number of options in the current select model.
+ */
+ int numOptions;
+
+ /**
+ * The current button groups mappings.
+ */
+ HashMap buttonGroups;
+
+ /**
+ * The token threshold. This gets increased while loading.
+ */
+ private int threshold;
+
public class TagAction
{
/**
@@ -633,13 +812,12 @@ public class HTMLDocument extends DefaultStyledDocument
// Put the old attribute set on the stack.
pushCharacterStyle();
- // Translate tag.. return if succesful.
- if(CharacterAttributeTranslator.translateTag(charAttr, t, a))
- return;
+ // Initialize with link pseudo class.
+ if (t == HTML.Tag.A)
+ a.addAttribute(HTML.Attribute.PSEUDO_CLASS, "link");
// Just add the attributes in <code>a</code>.
- if (a != null)
- charAttr.addAttribute(t, a.copyAttributes());
+ charAttr.addAttribute(t, a.copyAttributes());
}
/**
@@ -651,7 +829,11 @@ public class HTMLDocument extends DefaultStyledDocument
popCharacterStyle();
}
}
-
+
+ /**
+ * Processes elements that make up forms: &lt;input&gt;, &lt;textarea&gt;,
+ * &lt;select&gt; and &lt;option&gt;.
+ */
public class FormAction extends SpecialAction
{
/**
@@ -659,10 +841,73 @@ public class HTMLDocument extends DefaultStyledDocument
* of tags associated with this Action.
*/
public void start(HTML.Tag t, MutableAttributeSet a)
- throws NotImplementedException
{
- // FIXME: Implement.
- print ("FormAction.start not implemented");
+ if (t == HTML.Tag.INPUT)
+ {
+ String type = (String) a.getAttribute(HTML.Attribute.TYPE);
+ if (type == null)
+ {
+ type = "text"; // Default to 'text' when nothing was specified.
+ a.addAttribute(HTML.Attribute.TYPE, type);
+ }
+ setModel(type, a);
+ }
+ else if (t == HTML.Tag.TEXTAREA)
+ {
+ inTextArea = true;
+ textAreaDocument = new ResetablePlainDocument();
+ a.addAttribute(StyleConstants.ModelAttribute, textAreaDocument);
+ }
+ else if (t == HTML.Tag.SELECT)
+ {
+ int size = HTML.getIntegerAttributeValue(a, HTML.Attribute.SIZE,
+ 1);
+ boolean multi = a.getAttribute(HTML.Attribute.MULTIPLE) != null;
+ if (size > 1 || multi)
+ {
+ SelectListModel m = new SelectListModel();
+ if (multi)
+ m.getSelectionModel().setSelectionMode(ListSelectionModel
+ .MULTIPLE_INTERVAL_SELECTION);
+ selectModel = m;
+ }
+ else
+ {
+ selectModel = new SelectComboBoxModel();
+ }
+ a.addAttribute(StyleConstants.ModelAttribute, selectModel);
+ }
+ if (t == HTML.Tag.OPTION)
+ {
+ option = new Option(a);
+ if (selectModel instanceof SelectListModel)
+ {
+ SelectListModel m = (SelectListModel) selectModel;
+ m.addElement(option);
+ if (option.isSelected())
+ {
+ m.getSelectionModel().addSelectionInterval(numOptions,
+ numOptions);
+ m.addInitialSelection(numOptions);
+ }
+ }
+ else if (selectModel instanceof SelectComboBoxModel)
+ {
+ SelectComboBoxModel m = (SelectComboBoxModel) selectModel;
+ m.addElement(option);
+ if (option.isSelected())
+ {
+ m.setSelectedItem(option);
+ m.setInitialSelection(option);
+ }
+ }
+ numOptions++;
+ }
+ else
+ {
+ // Build the element.
+ super.start(t, a);
+ }
}
/**
@@ -670,13 +915,106 @@ public class HTMLDocument extends DefaultStyledDocument
* with this Action.
*/
public void end(HTML.Tag t)
- throws NotImplementedException
{
- // FIXME: Implement.
- print ("FormAction.end not implemented");
+ if (t == HTML.Tag.OPTION)
+ {
+ option = null;
+ }
+ else
+ {
+ if (t == HTML.Tag.TEXTAREA)
+ {
+ inTextArea = false;
+ }
+ else if (t == HTML.Tag.SELECT)
+ {
+ selectModel = null;
+ numOptions = 0;
+ }
+ // Finish the element.
+ super.end(t);
+ }
+ }
+
+ private void setModel(String type, MutableAttributeSet attrs)
+ {
+ if (type.equals("submit") || type.equals("reset")
+ || type.equals("image"))
+ {
+ // Create button.
+ attrs.addAttribute(StyleConstants.ModelAttribute,
+ new DefaultButtonModel());
+ }
+ else if (type.equals("text") || type.equals("password"))
+ {
+ String text = (String) attrs.getAttribute(HTML.Attribute.VALUE);
+ ResetablePlainDocument doc = new ResetablePlainDocument();
+ if (text != null)
+ {
+ doc.setInitialText(text);
+ try
+ {
+ doc.insertString(0, text, null);
+ }
+ catch (BadLocationException ex)
+ {
+ // Shouldn't happen.
+ assert false;
+ }
+ }
+ attrs.addAttribute(StyleConstants.ModelAttribute, doc);
+ }
+ else if (type.equals("file"))
+ {
+ attrs.addAttribute(StyleConstants.ModelAttribute,
+ new PlainDocument());
+ }
+ else if (type.equals("checkbox") || type.equals("radio"))
+ {
+ ResetableToggleButtonModel model =
+ new ResetableToggleButtonModel();
+ if (attrs.getAttribute(HTML.Attribute.SELECTED) != null)
+ {
+ model.setSelected(true);
+ model.setInitial(true);
+ }
+ if (type.equals("radio"))
+ {
+ String name = (String) attrs.getAttribute(HTML.Attribute.NAME);
+ if (name != null)
+ {
+ if (buttonGroups == null)
+ buttonGroups = new HashMap();
+ ButtonGroup group = (ButtonGroup) buttonGroups.get(name);
+ if (group == null)
+ {
+ group = new ButtonGroup();
+ buttonGroups.put(name, group);
+ }
+ model.setGroup(group);
+ }
+ }
+ attrs.addAttribute(StyleConstants.ModelAttribute, model);
+ }
+ }
+ }
+
+ /**
+ * Called for form tags.
+ */
+ class FormTagAction
+ extends BlockAction
+ {
+ /**
+ * Clears the button group mapping.
+ */
+ public void end(HTML.Tag t)
+ {
+ super.end(t);
+ buttonGroups = null;
}
}
-
+
/**
* This action indicates that the content between starting and closing HTML
* elements (like script - /script) should not be visible. The content is
@@ -707,7 +1045,10 @@ public class HTMLDocument extends DefaultStyledDocument
blockClose(t);
}
}
-
+
+ /**
+ * Handles &lt;isindex&gt; tags.
+ */
public class IsindexAction extends TagAction
{
/**
@@ -715,10 +1056,10 @@ public class HTMLDocument extends DefaultStyledDocument
* of tags associated with this Action.
*/
public void start(HTML.Tag t, MutableAttributeSet a)
- throws NotImplementedException
{
- // FIXME: Implement.
- print ("IsindexAction.start not implemented");
+ blockOpen(HTML.Tag.IMPLIED, new SimpleAttributeSet());
+ addSpecialElement(t, a);
+ blockClose(HTML.Tag.IMPLIED);
}
}
@@ -730,7 +1071,7 @@ public class HTMLDocument extends DefaultStyledDocument
*/
public void start(HTML.Tag t, MutableAttributeSet a)
{
- blockOpen(t, a);
+ super.start(t, a);
}
/**
@@ -739,10 +1080,13 @@ public class HTMLDocument extends DefaultStyledDocument
*/
public void end(HTML.Tag t)
{
- blockClose(t);
+ super.end(t);
}
}
-
+
+ /**
+ * This action is performed when a &lt;pre&gt; tag is parsed.
+ */
public class PreAction extends BlockAction
{
/**
@@ -750,11 +1094,11 @@ public class HTMLDocument extends DefaultStyledDocument
* of tags associated with this Action.
*/
public void start(HTML.Tag t, MutableAttributeSet a)
- throws NotImplementedException
{
- // FIXME: Implement.
- print ("PreAction.start not implemented");
- super.start(t, a);
+ inPreTag = true;
+ blockOpen(t, a);
+ a.addAttribute(CSS.Attribute.WHITE_SPACE, "pre");
+ blockOpen(HTML.Tag.IMPLIED, a);
}
/**
@@ -762,11 +1106,10 @@ public class HTMLDocument extends DefaultStyledDocument
* with this Action.
*/
public void end(HTML.Tag t)
- throws NotImplementedException
{
- // FIXME: Implement.
- print ("PreAction.end not implemented");
- super.end(t);
+ blockClose(HTML.Tag.IMPLIED);
+ inPreTag = false;
+ blockClose(t);
}
}
@@ -798,7 +1141,6 @@ public class HTMLDocument extends DefaultStyledDocument
throws NotImplementedException
{
// FIXME: Implement.
- print ("AreaAction.start not implemented");
}
/**
@@ -809,10 +1151,44 @@ public class HTMLDocument extends DefaultStyledDocument
throws NotImplementedException
{
// FIXME: Implement.
- print ("AreaAction.end not implemented");
}
}
-
+
+ /**
+ * Converts HTML tags to CSS attributes.
+ */
+ class ConvertAction
+ extends TagAction
+ {
+
+ public void start(HTML.Tag tag, MutableAttributeSet atts)
+ {
+ pushCharacterStyle();
+ charAttr.addAttribute(tag, atts.copyAttributes());
+ StyleSheet styleSheet = getStyleSheet();
+ // TODO: Add other tags here.
+ if (tag == HTML.Tag.FONT)
+ {
+ String color = (String) atts.getAttribute(HTML.Attribute.COLOR);
+ if (color != null)
+ styleSheet.addCSSAttribute(charAttr, CSS.Attribute.COLOR, color);
+ String face = (String) atts.getAttribute(HTML.Attribute.FACE);
+ if (face != null)
+ styleSheet.addCSSAttribute(charAttr, CSS.Attribute.FONT_FAMILY,
+ face);
+ String size = (String) atts.getAttribute(HTML.Attribute.SIZE);
+ if (size != null)
+ styleSheet.addCSSAttribute(charAttr, CSS.Attribute.FONT_SIZE,
+ size);
+ }
+ }
+
+ public void end(HTML.Tag tag)
+ {
+ popCharacterStyle();
+ }
+ }
+
class BaseAction extends TagAction
{
/**
@@ -820,22 +1196,9 @@ public class HTMLDocument extends DefaultStyledDocument
* of tags associated with this Action.
*/
public void start(HTML.Tag t, MutableAttributeSet a)
- throws NotImplementedException
{
- // FIXME: Implement.
- print ("BaseAction.start not implemented");
+ baseTarget = (String) a.getAttribute(HTML.Attribute.TARGET);
}
-
- /**
- * Called when an end tag is seen for one of the types of tags associated
- * with this Action.
- */
- public void end(HTML.Tag t)
- throws NotImplementedException
- {
- // FIXME: Implement.
- print ("BaseAction.end not implemented");
- }
}
class HeadAction extends BlockAction
@@ -848,7 +1211,6 @@ public class HTMLDocument extends DefaultStyledDocument
throws NotImplementedException
{
// FIXME: Implement.
- print ("HeadAction.start not implemented: "+t);
super.start(t, a);
}
@@ -857,37 +1219,87 @@ public class HTMLDocument extends DefaultStyledDocument
* with this Action.
*/
public void end(HTML.Tag t)
- throws NotImplementedException
{
- // FIXME: Implement.
- print ("HeadAction.end not implemented: "+t);
+ // We read in all the stylesheets that are embedded or referenced
+ // inside the header.
+ if (styles != null)
+ {
+ int numStyles = styles.size();
+ for (int i = 0; i < numStyles; i++)
+ {
+ String style = (String) styles.get(i);
+ getStyleSheet().addRule(style);
+ }
+ }
super.end(t);
- }
+ }
}
- class LinkAction extends TagAction
+ class LinkAction extends HiddenAction
{
/**
* This method is called when a start tag is seen for one of the types
* of tags associated with this Action.
*/
public void start(HTML.Tag t, MutableAttributeSet a)
- throws NotImplementedException
{
- // FIXME: Implement.
- print ("LinkAction.start not implemented");
+ super.start(t, a);
+ String type = (String) a.getAttribute(HTML.Attribute.TYPE);
+ if (type == null)
+ type = "text/css";
+ if (type.equals("text/css"))
+ {
+ String rel = (String) a.getAttribute(HTML.Attribute.REL);
+ String media = (String) a.getAttribute(HTML.Attribute.MEDIA);
+ String title = (String) a.getAttribute(HTML.Attribute.TITLE);
+ if (media == null)
+ media = "all";
+ else
+ media = media.toLowerCase();
+ if (rel != null)
+ {
+ rel = rel.toLowerCase();
+ if ((media.indexOf("all") != -1
+ || media.indexOf("screen") != -1)
+ && (rel.equals("stylesheet")))
+ {
+ String href = (String) a.getAttribute(HTML.Attribute.HREF);
+ URL url = null;
+ try
+ {
+ url = new URL(baseURL, href);
+ }
+ catch (MalformedURLException ex)
+ {
+ try
+ {
+ url = new URL(href);
+ }
+ catch (MalformedURLException ex2)
+ {
+ url = null;
+ }
+ }
+ if (url != null)
+ {
+ try
+ {
+ getStyleSheet().importStyleSheet(url);
+ }
+ catch (Exception ex)
+ {
+ // Don't let exceptions and runtime exceptions
+ // in CSS parsing disprupt the HTML parsing
+ // process. But inform the user/developer
+ // on the console about it.
+ ex.printStackTrace();
+ }
+ }
+ }
+ }
+ }
}
- /**
- * Called when an end tag is seen for one of the types of tags associated
- * with this Action.
- */
- public void end(HTML.Tag t)
- throws NotImplementedException
- {
- // FIXME: Implement.
- print ("LinkAction.end not implemented");
- }
}
class MapAction extends TagAction
@@ -900,7 +1312,6 @@ public class HTMLDocument extends DefaultStyledDocument
throws NotImplementedException
{
// FIXME: Implement.
- print ("MapAction.start not implemented");
}
/**
@@ -911,7 +1322,6 @@ public class HTMLDocument extends DefaultStyledDocument
throws NotImplementedException
{
// FIXME: Implement.
- print ("MapAction.end not implemented");
}
}
@@ -925,7 +1335,6 @@ public class HTMLDocument extends DefaultStyledDocument
throws NotImplementedException
{
// FIXME: Implement.
- print ("MetaAction.start not implemented");
}
/**
@@ -936,10 +1345,9 @@ public class HTMLDocument extends DefaultStyledDocument
throws NotImplementedException
{
// FIXME: Implement.
- print ("MetaAction.end not implemented");
}
}
-
+
class StyleAction extends TagAction
{
/**
@@ -947,10 +1355,8 @@ public class HTMLDocument extends DefaultStyledDocument
* of tags associated with this Action.
*/
public void start(HTML.Tag t, MutableAttributeSet a)
- throws NotImplementedException
{
- // FIXME: Implement.
- print ("StyleAction.start not implemented");
+ inStyleTag = true;
}
/**
@@ -958,10 +1364,8 @@ public class HTMLDocument extends DefaultStyledDocument
* with this Action.
*/
public void end(HTML.Tag t)
- throws NotImplementedException
{
- // FIXME: Implement.
- print ("StyleAction.end not implemented");
+ inStyleTag = false;
}
}
@@ -975,7 +1379,6 @@ public class HTMLDocument extends DefaultStyledDocument
throws NotImplementedException
{
// FIXME: Implement.
- print ("TitleAction.start not implemented");
}
/**
@@ -986,7 +1389,6 @@ public class HTMLDocument extends DefaultStyledDocument
throws NotImplementedException
{
// FIXME: Implement.
- print ("TitleAction.end not implemented");
}
}
@@ -998,13 +1400,11 @@ public class HTMLDocument extends DefaultStyledDocument
public HTMLReader(int offset, int popDepth, int pushDepth,
HTML.Tag insertTag)
{
- print ("HTMLReader created with pop: "+popDepth
- + " push: "+pushDepth + " offset: "+offset
- + " tag: "+insertTag);
this.insertTag = insertTag;
this.offset = offset;
this.popDepth = popDepth;
this.pushDepth = pushDepth;
+ threshold = getTokenThreshold();
initTags();
}
@@ -1028,7 +1428,7 @@ public class HTMLDocument extends DefaultStyledDocument
StyleAction styleAction = new StyleAction();
TitleAction titleAction = new TitleAction();
-
+ ConvertAction convertAction = new ConvertAction();
tagToAction.put(HTML.Tag.A, characterAction);
tagToAction.put(HTML.Tag.ADDRESS, characterAction);
tagToAction.put(HTML.Tag.APPLET, hiddenAction);
@@ -1051,8 +1451,8 @@ public class HTMLDocument extends DefaultStyledDocument
tagToAction.put(HTML.Tag.DL, blockAction);
tagToAction.put(HTML.Tag.DT, paragraphAction);
tagToAction.put(HTML.Tag.EM, characterAction);
- tagToAction.put(HTML.Tag.FONT, characterAction);
- tagToAction.put(HTML.Tag.FORM, blockAction);
+ tagToAction.put(HTML.Tag.FONT, convertAction);
+ tagToAction.put(HTML.Tag.FORM, new FormTagAction());
tagToAction.put(HTML.Tag.FRAME, specialAction);
tagToAction.put(HTML.Tag.FRAMESET, blockAction);
tagToAction.put(HTML.Tag.H1, paragraphAction);
@@ -1142,18 +1542,28 @@ public class HTMLDocument extends DefaultStyledDocument
*/
public void flush() throws BadLocationException
{
- DefaultStyledDocument.ElementSpec[] elements;
- elements = new DefaultStyledDocument.ElementSpec[parseBuffer.size()];
- parseBuffer.copyInto(elements);
- parseBuffer.removeAllElements();
- if (offset == 0)
- create(elements);
- else
- insert(offset, elements);
+ flushImpl();
+ }
- offset += HTMLDocument.this.getLength() - offset;
+ /**
+ * Flushes the buffer and handle partial inserts.
+ *
+ */
+ private void flushImpl()
+ throws BadLocationException
+ {
+ int oldLen = getLength();
+ int size = parseBuffer.size();
+ ElementSpec[] elems = new ElementSpec[size];
+ parseBuffer.copyInto(elems);
+ if (oldLen == 0)
+ create(elems);
+ else
+ insert(offset, elems);
+ parseBuffer.removeAllElements();
+ offset += getLength() - oldLen;
}
-
+
/**
* This method is called by the parser to indicate a block of
* text was encountered. Should insert the text appropriately.
@@ -1163,8 +1573,24 @@ public class HTMLDocument extends DefaultStyledDocument
*/
public void handleText(char[] data, int pos)
{
- if (data != null && data.length > 0)
- addContent(data, 0, data.length);
+ if (shouldInsert() && data != null && data.length > 0)
+ {
+ if (inTextArea)
+ textAreaContent(data);
+ else if (inPreTag)
+ preContent(data);
+ else if (option != null)
+ option.setLabel(new String(data));
+ else if (inStyleTag)
+ {
+ if (styles == null)
+ styles = new ArrayList();
+ styles.add(new String(data));
+ }
+ else
+ addContent(data, 0, data.length);
+
+ }
}
/**
@@ -1214,8 +1640,7 @@ public class HTMLDocument extends DefaultStyledDocument
TagAction action = (TagAction) tagToAction.get(HTML.Tag.COMMENT);
if (action != null)
{
- action.start(HTML.Tag.COMMENT,
- htmlAttributeSet.EMPTY_HTML_ATTRIBUTE_SET);
+ action.start(HTML.Tag.COMMENT, new SimpleAttributeSet());
action.end(HTML.Tag.COMMENT);
}
}
@@ -1277,7 +1702,6 @@ public class HTMLDocument extends DefaultStyledDocument
public void handleEndOfLineString(String eol)
{
// FIXME: Implement.
- print ("HTMLReader.handleEndOfLineString not implemented yet");
}
/**
@@ -1287,22 +1711,48 @@ public class HTMLDocument extends DefaultStyledDocument
* @param data the text to add to the textarea
*/
protected void textAreaContent(char[] data)
- throws NotImplementedException
{
- // FIXME: Implement.
- print ("HTMLReader.textAreaContent not implemented yet");
+ try
+ {
+ int offset = textAreaDocument.getLength();
+ String text = new String(data);
+ textAreaDocument.setInitialText(text);
+ textAreaDocument.insertString(offset, text, null);
+ }
+ catch (BadLocationException ex)
+ {
+ // Must not happen as we insert at a model location that we
+ // got from the document itself.
+ assert false;
+ }
}
/**
* Adds the given text that was encountered in a <PRE> element.
- *
+ * This adds synthesized lines to hold the text runs.
+ *
* @param data the text
*/
protected void preContent(char[] data)
- throws NotImplementedException
{
- // FIXME: Implement
- print ("HTMLReader.preContent not implemented yet");
+ int start = 0;
+ for (int i = 0; i < data.length; i++)
+ {
+ if (data[i] == '\n')
+ {
+ addContent(data, start, i - start + 1);
+ blockClose(HTML.Tag.IMPLIED);
+ MutableAttributeSet atts = new SimpleAttributeSet();
+ atts.addAttribute(CSS.Attribute.WHITE_SPACE, "pre");
+ blockOpen(HTML.Tag.IMPLIED, atts);
+ start = i + 1;
+ }
+ }
+ if (start < data.length)
+ {
+ // Add remaining last line.
+ addContent(data, start, data.length - start);
+ }
}
/**
@@ -1314,17 +1764,48 @@ public class HTMLDocument extends DefaultStyledDocument
*/
protected void blockOpen(HTML.Tag t, MutableAttributeSet attr)
{
- printBuffer();
- DefaultStyledDocument.ElementSpec element;
+ if (inImpliedParagraph())
+ blockClose(HTML.Tag.IMPLIED);
+ // Push the new tag on top of the stack.
parseStack.push(t);
+
+ DefaultStyledDocument.ElementSpec element;
+
AbstractDocument.AttributeContext ctx = getAttributeContext();
AttributeSet copy = attr.copyAttributes();
copy = ctx.addAttribute(copy, StyleConstants.NameAttribute, t);
element = new DefaultStyledDocument.ElementSpec(copy,
DefaultStyledDocument.ElementSpec.StartTagType);
parseBuffer.addElement(element);
- printBuffer();
+ }
+
+ /**
+ * Returns true when we are currently inside a paragraph, either
+ * a real one or an implied, false otherwise.
+ *
+ * @return
+ */
+ private boolean inParagraph()
+ {
+ boolean inParagraph = false;
+ if (! parseStack.isEmpty())
+ {
+ HTML.Tag top = parseStack.peek();
+ inParagraph = top == HTML.Tag.P || top == HTML.Tag.IMPLIED;
+ }
+ return inParagraph;
+ }
+
+ private boolean inImpliedParagraph()
+ {
+ boolean inParagraph = false;
+ if (! parseStack.isEmpty())
+ {
+ HTML.Tag top = parseStack.peek();
+ inParagraph = top == HTML.Tag.IMPLIED;
+ }
+ return inParagraph;
}
/**
@@ -1335,32 +1816,29 @@ public class HTMLDocument extends DefaultStyledDocument
*/
protected void blockClose(HTML.Tag t)
{
- printBuffer();
DefaultStyledDocument.ElementSpec element;
+ if (inImpliedParagraph() && t != HTML.Tag.IMPLIED)
+ blockClose(HTML.Tag.IMPLIED);
+
+ // Pull the token from the stack.
+ if (! parseStack.isEmpty()) // Just to be sure.
+ parseStack.pop();
+
// If the previous tag is a start tag then we insert a synthetic
// content tag.
DefaultStyledDocument.ElementSpec prev;
- prev = (DefaultStyledDocument.ElementSpec)
- parseBuffer.get(parseBuffer.size() - 1);
- if (prev.getType() == DefaultStyledDocument.ElementSpec.StartTagType)
+ prev = parseBuffer.size() > 0 ? (DefaultStyledDocument.ElementSpec)
+ parseBuffer.get(parseBuffer.size() - 1) : null;
+ if (prev != null &&
+ prev.getType() == DefaultStyledDocument.ElementSpec.StartTagType)
{
- AbstractDocument.AttributeContext ctx = getAttributeContext();
- AttributeSet attributes = ctx.getEmptySet();
- attributes = ctx.addAttribute(attributes, StyleConstants.NameAttribute,
- HTML.Tag.CONTENT);
- element = new DefaultStyledDocument.ElementSpec(attributes,
- DefaultStyledDocument.ElementSpec.ContentType,
- new char[0], 0, 0);
- parseBuffer.add(element);
+ addContent(new char[]{' '}, 0, 1);
}
element = new DefaultStyledDocument.ElementSpec(null,
DefaultStyledDocument.ElementSpec.EndTagType);
parseBuffer.addElement(element);
- printBuffer();
- if (parseStack.size() > 0)
- parseStack.pop();
}
/**
@@ -1389,6 +1867,11 @@ public class HTMLDocument extends DefaultStyledDocument
protected void addContent(char[] data, int offs, int length,
boolean generateImpliedPIfNecessary)
{
+ if (generateImpliedPIfNecessary && ! inParagraph())
+ {
+ blockOpen(HTML.Tag.IMPLIED, new SimpleAttributeSet());
+ }
+
AbstractDocument.AttributeContext ctx = getAttributeContext();
DefaultStyledDocument.ElementSpec element;
AttributeSet attributes = null;
@@ -1405,16 +1888,16 @@ public class HTMLDocument extends DefaultStyledDocument
DefaultStyledDocument.ElementSpec.ContentType,
data, offs, length);
- printBuffer();
// Add the element to the buffer
parseBuffer.addElement(element);
- printBuffer();
- if (parseBuffer.size() > HTMLDocument.this.getTokenThreshold())
+ if (parseBuffer.size() > threshold)
{
+ if (threshold <= MAX_THRESHOLD)
+ threshold *= GROW_THRESHOLD;
try
{
- flush();
+ flushImpl();
}
catch (BadLocationException ble)
{
@@ -1431,29 +1914,23 @@ public class HTMLDocument extends DefaultStyledDocument
*/
protected void addSpecialElement(HTML.Tag t, MutableAttributeSet a)
{
+ if (t != HTML.Tag.FRAME && ! inParagraph())
+ {
+ blockOpen(HTML.Tag.IMPLIED, new SimpleAttributeSet());
+ }
+
a.addAttribute(StyleConstants.NameAttribute, t);
- // Migrate from the rather htmlAttributeSet to the faster, lighter and
- // unchangeable alternative implementation.
- AttributeSet copy = a.copyAttributes();
-
// The two spaces are required because some special elements like HR
// must be broken. At least two characters are needed to break into the
// two parts.
DefaultStyledDocument.ElementSpec spec =
- new DefaultStyledDocument.ElementSpec(copy,
+ new DefaultStyledDocument.ElementSpec(a.copyAttributes(),
DefaultStyledDocument.ElementSpec.ContentType,
- new char[] {' ', ' '}, 0, 2 );
+ new char[] {' '}, 0, 1 );
parseBuffer.add(spec);
}
- void printBuffer()
- {
- print ("\n*********BUFFER**********");
- for (int i = 0; i < parseBuffer.size(); i ++)
- print (" "+parseBuffer.get(i));
- print ("***************************");
- }
}
/**
@@ -1533,10 +2010,6 @@ public class HTMLDocument extends DefaultStyledDocument
}
};
- // Set the parent HTML tag.
- reader.parseStack.push(parent.getAttributes().getAttribute(
- StyleConstants.NameAttribute));
-
return reader;
}
@@ -1728,4 +2201,98 @@ public void setOuterHTML(Element elem, String htmlText)
// TODO charset
getParser().parse(new StringReader(htmlText), reader, true);
}
+
+ /**
+ * Overridden to tag content with the synthetic HTML.Tag.CONTENT
+ * tag.
+ */
+ protected void insertUpdate(DefaultDocumentEvent evt, AttributeSet att)
+ {
+ if (att == null)
+ {
+ SimpleAttributeSet sas = new SimpleAttributeSet();
+ sas.addAttribute(StyleConstants.NameAttribute, HTML.Tag.CONTENT);
+ att = sas;
+ }
+ super.insertUpdate(evt, att);
+ }
+
+ /**
+ * Returns <code>true</code> when this document is inside a frame,
+ * <code>false</code> otherwise.
+ *
+ * @return <code>true</code> when this document is inside a frame,
+ * <code>false</code> otherwise
+ */
+ boolean isFrameDocument()
+ {
+ return frameDocument;
+ }
+
+ /**
+ * Set <code>true</code> when this document is inside a frame,
+ * <code>false</code> otherwise.
+ *
+ * @param frameDoc <code>true</code> when this document is inside a frame,
+ * <code>false</code> otherwise
+ */
+ void setFrameDocument(boolean frameDoc)
+ {
+ frameDocument = frameDoc;
+ }
+
+ /**
+ * Returns the target that is specified in the base tag, if this is the case.
+ *
+ * @return the target that is specified in the base tag, if this is the case
+ */
+ String getBaseTarget()
+ {
+ return baseTarget;
+ }
+
+ /**
+ * Updates the A tag's pseudo class value in response to a hyperlink
+ * action.
+ *
+ * @param el the corresponding element
+ * @param value the new value
+ */
+ void updateSpecialClass(Element el, HTML.Attribute cl, String value)
+ {
+ try
+ {
+ writeLock();
+ DefaultDocumentEvent ev =
+ new DefaultDocumentEvent(el.getStartOffset(), 1,
+ DocumentEvent.EventType.CHANGE);
+ AttributeSet elAtts = el.getAttributes();
+ AttributeSet anchorAtts = (AttributeSet) elAtts.getAttribute(HTML.Tag.A);
+ if (anchorAtts != null)
+ {
+ AttributeSet copy = elAtts.copyAttributes();
+ StyleSheet ss = getStyleSheet();
+ if (value != null)
+ {
+ anchorAtts = ss.addAttribute(anchorAtts, cl, value);
+ }
+ else
+ {
+ anchorAtts = ss.removeAttribute(anchorAtts, cl);
+ }
+ MutableAttributeSet matts = (MutableAttributeSet) elAtts;
+ ev.addEdit(new AttributeUndoableEdit(el, copy, false));
+ matts.removeAttribute(HTML.Tag.A);
+ matts.addAttribute(HTML.Tag.A, anchorAtts);
+ ev.end();
+ fireChangedUpdate(ev);
+ fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
+ }
+ }
+ finally
+ {
+ writeUnlock();
+ }
+ }
+
}
diff --git a/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java b/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java
index 5d77be8fdd4..0ede1c74ed9 100644
--- a/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java
+++ b/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java
@@ -39,32 +39,38 @@ exception statement from your version. */
package javax.swing.text.html;
-import gnu.classpath.NotImplementedException;
-
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.Cursor;
+import java.awt.Point;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Serializable;
import java.io.StringReader;
import java.io.Writer;
+import java.net.MalformedURLException;
+import java.net.URL;
import javax.accessibility.Accessible;
import javax.accessibility.AccessibleContext;
import javax.swing.Action;
import javax.swing.JEditorPane;
+import javax.swing.SwingUtilities;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.EditorKit;
import javax.swing.text.Element;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.StyleConstants;
-import javax.swing.text.StyleContext;
+import javax.swing.text.StyledDocument;
import javax.swing.text.StyledEditorKit;
import javax.swing.text.TextAction;
import javax.swing.text.View;
@@ -74,7 +80,7 @@ import javax.swing.text.html.parser.ParserDelegator;
/* Move these imports here after javax.swing.text.html to make it compile
with jikes. */
import gnu.javax.swing.text.html.parser.GnuParserDelegator;
-import gnu.javax.swing.text.html.parser.HTML_401Swing;
+import gnu.javax.swing.text.html.parser.HTML_401F;
/**
* @author Lillian Angel (langel at redhat dot com)
@@ -92,7 +98,12 @@ public class HTMLEditorKit
extends MouseAdapter
implements MouseMotionListener, Serializable
{
-
+
+ /**
+ * The element of the last anchor tag.
+ */
+ private Element lastAnchorElement;
+
/**
* Constructor
*/
@@ -110,11 +121,14 @@ public class HTMLEditorKit
*/
public void mouseClicked(MouseEvent e)
{
- /*
- These MouseInputAdapter methods generate mouse appropriate events around
- hyperlinks (entering, exiting, and activating).
- */
- // FIXME: Not implemented.
+ JEditorPane editor = (JEditorPane) e.getSource();
+ if (! editor.isEditable() && SwingUtilities.isLeftMouseButton(e))
+ {
+ Point loc = e.getPoint();
+ int pos = editor.viewToModel(loc);
+ if (pos >= 0)
+ activateLink(pos, editor, e.getX(), e.getY());
+ }
}
/**
@@ -124,11 +138,7 @@ public class HTMLEditorKit
*/
public void mouseDragged(MouseEvent e)
{
- /*
- These MouseInputAdapter methods generate mouse appropriate events around
- hyperlinks (entering, exiting, and activating).
- */
- // FIXME: Not implemented.
+ // Nothing to do here.
}
/**
@@ -138,29 +148,159 @@ public class HTMLEditorKit
*/
public void mouseMoved(MouseEvent e)
{
- /*
- These MouseInputAdapter methods generate mouse appropriate events around
- hyperlinks (entering, exiting, and activating).
- */
- // FIXME: Not implemented.
+ JEditorPane editor = (JEditorPane) e.getSource();
+ HTMLEditorKit kit = (HTMLEditorKit) editor.getEditorKit();
+ if (! editor.isEditable())
+ {
+ Document doc = editor.getDocument();
+ if (doc instanceof HTMLDocument)
+ {
+ Cursor newCursor = kit.getDefaultCursor();
+ HTMLDocument htmlDoc = (HTMLDocument) doc;
+ Point loc = e.getPoint();
+ int pos = editor.viewToModel(loc);
+ Element el = htmlDoc.getCharacterElement(pos);
+ if (pos < el.getStartOffset() || pos >= el.getEndOffset())
+ el = null;
+ if (el != null)
+ {
+ AttributeSet aAtts = (AttributeSet)
+ el.getAttributes().getAttribute(HTML.Tag.A);
+ if (aAtts != null)
+ {
+ if (el != lastAnchorElement)
+ {
+ if (lastAnchorElement != null)
+ htmlDoc.updateSpecialClass(lastAnchorElement,
+ HTML.Attribute.DYNAMIC_CLASS,
+ null);
+ lastAnchorElement = el;
+ htmlDoc.updateSpecialClass(el,
+ HTML.Attribute.DYNAMIC_CLASS,
+ "hover");
+ }
+ newCursor = kit.getLinkCursor();
+ }
+ else
+ {
+ if (lastAnchorElement != null)
+ htmlDoc.updateSpecialClass(lastAnchorElement,
+ HTML.Attribute.DYNAMIC_CLASS,
+ null);
+ lastAnchorElement = null;
+ }
+ }
+ else
+ {
+ if (lastAnchorElement != null)
+ htmlDoc.updateSpecialClass(lastAnchorElement,
+ HTML.Attribute.DYNAMIC_CLASS,
+ null);
+ lastAnchorElement = null;
+ }
+ if (editor.getCursor() != newCursor)
+ {
+ editor.setCursor(newCursor);
+ }
+ }
+ }
}
-
+
/**
* If the given position represents a link, then linkActivated is called
- * on the JEditorPane. Implemented to forward to the method with the same
- * name, but pos == editor == -1.
- *
- * @param pos - the position
- * @param editor - the editor pane
+ * on the JEditorPane.
+ *
+ * @param pos the position
+ * @param editor the editor pane
+ */
+ protected void activateLink(int pos, JEditorPane editor)
+ {
+ activateLink(pos, editor);
+ }
+
+ private void activateLink(int pos, JEditorPane editor, int x, int y)
+ {
+ // TODO: This is here for future extension for mapped links support.
+ // For the time beeing we implement simple hyperlinks.
+ Document doc = editor.getDocument();
+ if (doc instanceof HTMLDocument)
+ {
+ HTMLDocument htmlDoc = (HTMLDocument) doc;
+ Element el = htmlDoc.getCharacterElement(pos);
+ AttributeSet atts = el.getAttributes();
+ AttributeSet anchorAtts =
+ (AttributeSet) atts.getAttribute(HTML.Tag.A);
+ String href = null;
+ if (anchorAtts != null)
+ {
+ href = (String) anchorAtts.getAttribute(HTML.Attribute.HREF);
+ htmlDoc.updateSpecialClass(el, HTML.Attribute.PSEUDO_CLASS,
+ "visited");
+ }
+ else
+ {
+ // TODO: Implement link maps here.
+ }
+ HyperlinkEvent event = null;
+ if (href != null)
+ event = createHyperlinkEvent(editor, htmlDoc, href,
+ anchorAtts, el);
+ if (event != null)
+ editor.fireHyperlinkUpdate(event);
+ }
+
+ }
+
+ /**
+ * Creates a HyperlinkEvent for the specified href and anchor if
+ * possible. If for some reason this won't work, return null.
+ *
+ * @param editor the editor
+ * @param doc the document
+ * @param href the href link
+ * @param anchor the anchor
+ * @param el the element
+ *
+ * @return the hyperlink event, or <code>null</code> if we couldn't
+ * create one
*/
- protected void activateLink(int pos,
- JEditorPane editor)
+ private HyperlinkEvent createHyperlinkEvent(JEditorPane editor,
+ HTMLDocument doc,
+ String href,
+ AttributeSet anchor,
+ Element el)
{
- /*
- This method creates and fires a HyperlinkEvent if the document is an
- instance of HTMLDocument and the href tag of the link is not null.
- */
- // FIXME: Not implemented.
+ URL url;
+ try
+ {
+ URL base = doc.getBase();
+ url = new URL(base, href);
+
+ }
+ catch (MalformedURLException ex)
+ {
+ url = null;
+ }
+ HyperlinkEvent ev;
+ if (doc.isFrameDocument())
+ {
+ String target = null;
+ if (anchor != null)
+ target = (String) anchor.getAttribute(HTML.Attribute.TARGET);
+ if (target == null || target.equals(""))
+ target = doc.getBaseTarget();
+ if (target == null || target.equals(""))
+ target = "_self";
+ ev = new HTMLFrameHyperlinkEvent(editor,
+ HyperlinkEvent.EventType.ACTIVATED,
+ url, href, el, target);
+ }
+ else
+ {
+ ev = new HyperlinkEvent(editor, HyperlinkEvent.EventType.ACTIVATED,
+ url, href, el);
+ }
+ return ev;
}
}
@@ -201,7 +341,7 @@ public class HTMLEditorKit
* Tag to check for in the document.
*/
protected HTML.Tag parentTag;
-
+
/**
* Initializes all fields.
*
@@ -305,20 +445,9 @@ public class HTMLEditorKit
Element insertElement,
String html, HTML.Tag parentTag,
HTML.Tag addTag)
- throws NotImplementedException
{
- /*
- As its name implies, this protected method is used when HTML is inserted at a
- boundary. (A boundary in this case is an offset in doc that exactly matches the
- beginning offset of the parentTag.) It performs the extra work required to keep
- the tag stack in shape and then calls insertHTML(). The editor and doc argu-
- ments are the editor pane and document where the HTML should go. The offset
- argument represents the cursor location or selection start in doc. The insert-
- Element and parentTag arguments are used to calculate the proper number of
- tag pops and pushes before inserting the HTML (via html and addTag, which are
- passed directly to insertHTML()).
- */
- // FIXME: not implemented
+ insertAtBoundry(editor, doc, offset, insertElement,
+ html, parentTag, addTag);
}
/**
@@ -344,8 +473,50 @@ public class HTMLEditorKit
String html, HTML.Tag parentTag,
HTML.Tag addTag)
{
- insertAtBoundary(editor, doc, offset, insertElement,
- html, parentTag, addTag);
+ Element parent = insertElement;
+ Element el;
+ // Find common parent element.
+ if (offset > 0 || insertElement == null)
+ {
+ el = doc.getDefaultRootElement();
+ while (el != null && el.getStartOffset() != offset
+ && ! el.isLeaf())
+ el = el.getElement(el.getElementIndex(offset));
+ parent = el != null ? el.getParentElement() : null;
+ }
+ if (parent != null)
+ {
+ int pops = 0;
+ int pushes = 0;
+ if (offset == 0 && insertElement != null)
+ {
+ el = parent;
+ while (el != null && ! el.isLeaf())
+ {
+ el = el.getElement(el.getElementIndex(offset));
+ pops++;
+ }
+ }
+ else
+ {
+ el = parent;
+ offset--;
+ while (el != null && ! el.isLeaf())
+ {
+ el = el.getElement(el.getElementIndex(offset));
+ pops++;
+ }
+ el = parent;
+ offset++;
+ while (el != null && el != insertElement)
+ {
+ el = el.getElement(el.getElementIndex(offset));
+ pushes++;
+ }
+ }
+ pops = Math.max(0, pops - 1);
+ insertHTML(editor, doc, offset, html, pops, pushes, addTag);
+ }
}
/**
@@ -355,16 +526,97 @@ public class HTMLEditorKit
*/
public void actionPerformed(ActionEvent ae)
{
- Object source = ae.getSource();
- if (source instanceof JEditorPane)
+ JEditorPane source = getEditor(ae);
+ if (source != null)
+ {
+ HTMLDocument d = getHTMLDocument(source);
+ int offset = source.getSelectionStart();
+ int length = d.getLength();
+ boolean inserted = true;
+ if (! tryInsert(source, d, offset, parentTag, addTag))
+ {
+ inserted = tryInsert(source, d, offset, alternateParentTag,
+ alternateAddTag);
+ }
+ if (inserted)
+ adjustSelection(source, d, offset, length);
+ }
+ }
+
+ /**
+ * Tries to insert the html chunk to the specified <code>addTag</code>.
+ *
+ * @param pane the editor
+ * @param doc the document
+ * @param offset the offset at which to insert
+ * @param tag the tag at which to insert
+ * @param addTag the add tag
+ *
+ * @return <code>true</code> when the html has been inserted successfully,
+ * <code>false</code> otherwise
+ */
+ private boolean tryInsert(JEditorPane pane, HTMLDocument doc, int offset,
+ HTML.Tag tag, HTML.Tag addTag)
+ {
+ boolean inserted = false;
+ Element el = findElementMatchingTag(doc, offset, tag);
+ if (el != null && el.getStartOffset() == offset)
+ {
+ insertAtBoundary(pane, doc, offset, el, html, tag, addTag);
+ inserted = true;
+ }
+ else if (offset > 0)
+ {
+ int depth = elementCountToTag(doc, offset - 1, tag);
+ if (depth != -1)
+ {
+ insertHTML(pane, doc, offset, html, depth, 0, addTag);
+ inserted = true;
+ }
+ }
+ return inserted;
+ }
+
+ /**
+ * Adjusts the selection after an insertion has been performed.
+ *
+ * @param pane the editor pane
+ * @param doc the document
+ * @param offset the insert offset
+ * @param oldLen the old document length
+ */
+ private void adjustSelection(JEditorPane pane, HTMLDocument doc,
+ int offset, int oldLen)
+ {
+ int newLen = doc.getLength();
+ if (newLen != oldLen && offset < newLen)
{
- JEditorPane pane = ((JEditorPane) source);
- Document d = pane.getDocument();
- if (d instanceof HTMLDocument)
- insertHTML(pane, (HTMLDocument) d, 0, html, 0, 0, addTag);
- // FIXME: is this correct parameters?
+ if (offset > 0)
+ {
+ String text;
+ try
+ {
+ text = doc.getText(offset - 1, 1);
+ }
+ catch (BadLocationException ex)
+ {
+ text = null;
+ }
+ if (text != null && text.length() > 0
+ && text.charAt(0) == '\n')
+ {
+ pane.select(offset, offset);
+ }
+ else
+ {
+ pane.select(offset + 1, offset + 1);
+ }
+ }
+ else
+ {
+ pane.select(1, 1);
+ }
}
- // FIXME: else not implemented
}
}
@@ -540,53 +792,56 @@ public class HTMLEditorKit
{
HTML.Tag tag = (HTML.Tag) attr;
- if (tag.equals(HTML.Tag.IMPLIED) || tag.equals(HTML.Tag.P)
- || tag.equals(HTML.Tag.H1) || tag.equals(HTML.Tag.H2)
- || tag.equals(HTML.Tag.H3) || tag.equals(HTML.Tag.H4)
- || tag.equals(HTML.Tag.H5) || tag.equals(HTML.Tag.H6)
- || tag.equals(HTML.Tag.DT))
+ if (tag == HTML.Tag.IMPLIED || tag == HTML.Tag.P
+ || tag == HTML.Tag.H1 || tag == HTML.Tag.H2
+ || tag == HTML.Tag.H3 || tag == HTML.Tag.H4
+ || tag == HTML.Tag.H5 || tag == HTML.Tag.H6
+ || tag == HTML.Tag.DT)
view = new ParagraphView(element);
- else if (tag.equals(HTML.Tag.LI) || tag.equals(HTML.Tag.DL)
- || tag.equals(HTML.Tag.DD) || tag.equals(HTML.Tag.BODY)
- || tag.equals(HTML.Tag.HTML) || tag.equals(HTML.Tag.CENTER)
- || tag.equals(HTML.Tag.DIV)
- || tag.equals(HTML.Tag.BLOCKQUOTE)
- || tag.equals(HTML.Tag.PRE))
+ else if (tag == HTML.Tag.LI || tag == HTML.Tag.DL
+ || tag == HTML.Tag.DD || tag == HTML.Tag.BODY
+ || tag == HTML.Tag.HTML || tag == HTML.Tag.CENTER
+ || tag == HTML.Tag.DIV
+ || tag == HTML.Tag.BLOCKQUOTE
+ || tag == HTML.Tag.PRE
+ || tag == HTML.Tag.FORM
+ // Misplaced TD and TH tags get mapped as vertical block.
+ // Note that correctly placed tags get mapped in TableView.
+ || tag == HTML.Tag.TD || tag == HTML.Tag.TH)
view = new BlockView(element, View.Y_AXIS);
- else if (tag.equals(HTML.Tag.IMG))
+ else if (tag == HTML.Tag.TR)
+ // Misplaced TR tags get mapped as horizontal blocks.
+ // Note that correctly placed tags get mapped in TableView.
+ view = new BlockView(element, View.X_AXIS);
+ else if (tag == HTML.Tag.IMG)
view = new ImageView(element);
- // FIXME: Uncomment when the views have been implemented
- else if (tag.equals(HTML.Tag.CONTENT))
+ else if (tag == HTML.Tag.CONTENT)
view = new InlineView(element);
else if (tag == HTML.Tag.HEAD)
view = new NullView(element);
- else if (tag.equals(HTML.Tag.TABLE))
+ else if (tag == HTML.Tag.TABLE)
view = new javax.swing.text.html.TableView(element);
- else if (tag.equals(HTML.Tag.TD))
- view = new ParagraphView(element);
- else if (tag.equals(HTML.Tag.HR))
+ else if (tag == HTML.Tag.HR)
view = new HRuleView(element);
- else if (tag.equals(HTML.Tag.BR))
+ else if (tag == HTML.Tag.BR)
view = new BRView(element);
+ else if (tag == HTML.Tag.INPUT || tag == HTML.Tag.SELECT
+ || tag == HTML.Tag.TEXTAREA)
+ view = new FormView(element);
- /*
- else if (tag.equals(HTML.Tag.MENU) || tag.equals(HTML.Tag.DIR)
- || tag.equals(HTML.Tag.UL) || tag.equals(HTML.Tag.OL))
+ else if (tag == HTML.Tag.MENU || tag == HTML.Tag.DIR
+ || tag == HTML.Tag.UL || tag == HTML.Tag.OL)
view = new ListView(element);
- else if (tag.equals(HTML.Tag.INPUT) || tag.equals(HTML.Tag.SELECT)
- || tag.equals(HTML.Tag.TEXTAREA))
- view = new FormView(element);
- else if (tag.equals(HTML.Tag.OBJECT))
- view = new ObjectView(element);
- else if (tag.equals(HTML.Tag.FRAMESET))
+ else if (tag == HTML.Tag.FRAMESET)
view = new FrameSetView(element);
- else if (tag.equals(HTML.Tag.FRAME))
- view = new FrameView(element); */
+ else if (tag == HTML.Tag.FRAME)
+ view = new FrameView(element);
+ else if (tag == HTML.Tag.OBJECT)
+ view = new ObjectView(element);
}
if (view == null)
{
- System.err.println("missing tag->view mapping for: " + element);
view = new NullView(element);
}
return view;
@@ -797,14 +1052,42 @@ public class HTMLEditorKit
/**
* Actions for HTML
*/
- private static final Action[] defaultActions = {
- // FIXME: Add default actions for html
+ private static final Action[] defaultActions =
+ {
+ new InsertHTMLTextAction("InsertTable",
+ "<table border=1><tr><td></td></tr></table>",
+ HTML.Tag.BODY, HTML.Tag.TABLE),
+ new InsertHTMLTextAction("InsertTableRow",
+ "<table border=1><tr><td></td></tr></table>",
+ HTML.Tag.TABLE, HTML.Tag.TR,
+ HTML.Tag.BODY, HTML.Tag.TABLE),
+ new InsertHTMLTextAction("InsertTableCell",
+ "<table border=1><tr><td></td></tr></table>",
+ HTML.Tag.TR, HTML.Tag.TD,
+ HTML.Tag.BODY, HTML.Tag.TABLE),
+ new InsertHTMLTextAction("InsertUnorderedList",
+ "<ul><li></li></ul>",
+ HTML.Tag.BODY, HTML.Tag.UL),
+ new InsertHTMLTextAction("InsertUnorderedListItem",
+ "<ul><li></li></ul>",
+ HTML.Tag.UL, HTML.Tag.LI,
+ HTML.Tag.BODY, HTML.Tag.UL),
+ new InsertHTMLTextAction("InsertOrderedList",
+ "<ol><li></li></ol>",
+ HTML.Tag.BODY, HTML.Tag.OL),
+ new InsertHTMLTextAction("InsertOrderedListItem",
+ "<ol><li></li></ol>",
+ HTML.Tag.OL, HTML.Tag.LI,
+ HTML.Tag.BODY, HTML.Tag.OL),
+ new InsertHTMLTextAction("InsertPre",
+ "<pre></pre>", HTML.Tag.BODY, HTML.Tag.PRE)
+ // TODO: The reference impl has an InsertHRAction too.
};
/**
* The current style sheet.
*/
- StyleSheet styleSheet;
+ private StyleSheet styleSheet;
/**
* The ViewFactory for HTMLFactory.
@@ -829,12 +1112,7 @@ public class HTMLEditorKit
/**
* The mouse listener used for links.
*/
- LinkController mouseListener;
-
- /**
- * Style context for this editor.
- */
- StyleContext styleContext;
+ private LinkController linkController;
/** The content type */
String contentType = "text/html";
@@ -844,17 +1122,22 @@ public class HTMLEditorKit
/** The editor pane used. */
JEditorPane editorPane;
-
+
+ /**
+ * Whether or not the editor kit handles form submissions.
+ *
+ * @see #isAutoFormSubmission()
+ * @see #setAutoFormSubmission(boolean)
+ */
+ private boolean autoFormSubmission;
+
/**
* Constructs an HTMLEditorKit, creates a StyleContext, and loads the style sheet.
*/
public HTMLEditorKit()
{
- super();
- styleContext = new StyleContext();
- styleSheet = new StyleSheet();
- styleSheet.importStyleSheet(getClass().getResource(DEFAULT_CSS));
- // FIXME: Set inputAttributes with default.css
+ linkController = new LinkController();
+ autoFormSubmission = true;
}
/**
@@ -877,8 +1160,15 @@ public class HTMLEditorKit
*/
public Document createDefaultDocument()
{
- HTMLDocument document = new HTMLDocument(getStyleSheet());
+ // Protect the shared stylesheet.
+ StyleSheet styleSheet = getStyleSheet();
+ StyleSheet ss = new StyleSheet();
+ ss.addStyleSheet(styleSheet);
+
+ HTMLDocument document = new HTMLDocument(ss);
document.setParser(getParser());
+ document.setAsynchronousLoadPriority(4);
+ document.setTokenThreshold(100);
return document;
}
@@ -892,7 +1182,7 @@ public class HTMLEditorKit
{
if (parser == null)
{
- parser = new GnuParserDelegator(HTML_401Swing.getInstance());
+ parser = new GnuParserDelegator(HTML_401F.getInstance());
}
return parser;
}
@@ -923,8 +1213,7 @@ public class HTMLEditorKit
if (parser == null)
throw new IOException("Parser is null.");
- ParserCallback pc = ((HTMLDocument) doc).getReader
- (offset, popDepth, pushDepth, insertTag);
+ ParserCallback pc = doc.getReader(offset, popDepth, pushDepth, insertTag);
// FIXME: What should ignoreCharSet be set to?
@@ -991,8 +1280,15 @@ public class HTMLEditorKit
{
if (doc instanceof HTMLDocument)
{
- // FIXME: Not implemented. Use HTMLWriter.
- out.write(doc.getText(pos, len));
+ HTMLWriter writer = new HTMLWriter(out, (HTMLDocument) doc, pos, len);
+ writer.write();
+ }
+ else if (doc instanceof StyledDocument)
+ {
+ MinimalHTMLWriter writer = new MinimalHTMLWriter(out,
+ (StyledDocument) doc,
+ pos, len);
+ writer.write();
}
else
super.write(out, doc, pos, len);
@@ -1017,7 +1313,9 @@ public class HTMLEditorKit
public Object clone()
{
// FIXME: Need to clone all fields
- return (HTMLEditorKit) super.clone();
+ HTMLEditorKit copy = (HTMLEditorKit) super.clone();
+ copy.linkController = new LinkController();
+ return copy;
}
/**
@@ -1044,10 +1342,9 @@ public class HTMLEditorKit
public void install(JEditorPane c)
{
super.install(c);
- mouseListener = new LinkController();
- c.addMouseListener(mouseListener);
+ c.addMouseListener(linkController);
+ c.addMouseMotionListener(linkController);
editorPane = c;
- // FIXME: need to set up hyperlinklistener object
}
/**
@@ -1059,8 +1356,8 @@ public class HTMLEditorKit
public void deinstall(JEditorPane c)
{
super.deinstall(c);
- c.removeMouseListener(mouseListener);
- mouseListener = null;
+ c.removeMouseListener(linkController);
+ c.removeMouseMotionListener(linkController);
editorPane = null;
}
@@ -1154,8 +1451,19 @@ public class HTMLEditorKit
{
if (styleSheet == null)
{
- styleSheet = new StyleSheet();
- styleSheet.importStyleSheet(getClass().getResource(DEFAULT_CSS));
+ try
+ {
+ styleSheet = new StyleSheet();
+ Class c = HTMLEditorKit.class;
+ InputStream in = c.getResourceAsStream(DEFAULT_CSS);
+ InputStreamReader r = new InputStreamReader(in);
+ styleSheet.loadRules(r, null);
+ r.close();
+ }
+ catch (IOException ex)
+ {
+ // No style available.
+ }
}
return styleSheet;
}
@@ -1173,4 +1481,40 @@ public class HTMLEditorKit
{
styleSheet = s;
}
+
+ /**
+ * Returns <code>true</code> when forms should be automatically submitted
+ * by the editor kit. Set this to <code>false</code> when you want to
+ * intercept form submission. In this case you'd want to listen for
+ * hyperlink events on the document and handle FormSubmitEvents specially.
+ *
+ * The default is <code>true</code>.
+ *
+ * @return <code>true</code> when forms should be automatically submitted
+ * by the editor kit, <code>false</code> otherwise
+ *
+ * @since 1.5
+ *
+ * @see #setAutoFormSubmission(boolean)
+ * @see FormSubmitEvent
+ */
+ public boolean isAutoFormSubmission()
+ {
+ return autoFormSubmission;
+ }
+
+ /**
+ * Sets whether or not the editor kit should automatically submit forms.
+ *
+ * @param auto <code>true</code> when the editor kit should handle form
+ * submission, <code>false</code> otherwise
+ *
+ * @since 1.5
+ *
+ * @see #isAutoFormSubmission()
+ */
+ public void setAutoFormSubmission(boolean auto)
+ {
+ autoFormSubmission = auto;
+ }
}
diff --git a/libjava/classpath/javax/swing/text/html/HTMLWriter.java b/libjava/classpath/javax/swing/text/html/HTMLWriter.java
new file mode 100644
index 00000000000..44119c73286
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/HTMLWriter.java
@@ -0,0 +1,1084 @@
+/* HTMLWriter.java --
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package javax.swing.text.html;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import java.util.Enumeration;
+import java.util.HashSet;
+
+import javax.swing.ComboBoxModel;
+
+import javax.swing.text.AbstractWriter;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.Element;
+import javax.swing.text.StyleConstants;
+
+import javax.swing.text.html.HTML;
+import javax.swing.text.html.HTMLDocument;
+import javax.swing.text.html.Option;
+
+/**
+ * HTMLWriter,
+ * A Writer for HTMLDocuments.
+ *
+ * @author David Fu (fchoong at netbeans.jp)
+ */
+
+public class HTMLWriter
+ extends AbstractWriter
+{
+ /**
+ * We keep a reference of the writer passed by the construct.
+ */
+ private Writer outWriter = null;
+
+ /**
+ * We keep a reference of the HTMLDocument passed by the construct.
+ */
+ private HTMLDocument htmlDoc = null;
+
+ /**
+ * Used to keep track of which embeded has been written out.
+ */
+ private HashSet openEmbededTagHashSet = null;
+
+ private String new_line_str = "" + NEWLINE;
+
+ private char[] html_entity_char_arr = {'<', '>', '&', '"'};
+
+ private String[] html_entity_escape_str_arr = {"&lt;", "&gt;", "&amp;",
+ "&quot;"};
+
+ // variables used to output Html Fragment
+ private int doc_pos = -1;
+ private int doc_len = -1;
+ private int doc_offset_remaining = -1;
+ private int doc_len_remaining = -1;
+ private HashSet htmlFragmentParentHashSet = null;
+ private Element startElem = null;
+ private Element endElem = null;
+ private boolean fg_pass_start_elem = false;
+ private boolean fg_pass_end_elem = false;
+
+ /**
+ * Constructs a HTMLWriter.
+ *
+ * @param writer writer to write output to
+ * @param doc the HTMLDocument to output
+ */
+ public HTMLWriter(Writer writer, HTMLDocument doc)
+ {
+ super(writer, doc);
+ outWriter = writer;
+ htmlDoc = doc;
+ openEmbededTagHashSet = new HashSet();
+ } // public HTMLWriter(Writer writer, HTMLDocument doc)
+
+ /**
+ * Constructs a HTMLWriter which outputs a Html Fragment.
+ *
+ * @param writer <code>Writer</code> to write output to
+ * @param doc the <code>javax.swing.text.html.HTMLDocument</code>
+ * to output
+ * @param pos position to start outputing the document
+ * @param len amount to output the document
+ */
+ public HTMLWriter(Writer writer, HTMLDocument doc, int pos, int len)
+ {
+ super(writer, doc, pos, len);
+ outWriter = writer;
+ htmlDoc = doc;
+ openEmbededTagHashSet = new HashSet();
+
+ doc_pos = pos;
+ doc_offset_remaining = pos;
+ doc_len = len;
+ doc_len_remaining = len;
+ htmlFragmentParentHashSet = new HashSet();
+ } // public HTMLWriter(Writer writer, HTMLDocument doc, int pos, int len)
+
+ /**
+ * Call this method to start outputing HTML.
+ *
+ * @throws IOException on any I/O exceptions
+ * @throws BadLocationException if a pos is not a valid position in the
+ * html doc element
+ */
+ public void write()
+ throws IOException, BadLocationException
+ {
+ Element rootElem = htmlDoc.getDefaultRootElement();
+
+ if (doc_pos == -1 && doc_len == -1)
+ {
+ // Normal traversal.
+ traverse(rootElem);
+ } // if(doc_pos == -1 && doc_len == -1)
+ else
+ {
+ // Html fragment traversal.
+ if (doc_pos == -1 || doc_len == -1)
+ throw new BadLocationException("Bad Location("
+ + doc_pos + ", " + doc_len + ")", doc_pos);
+
+ startElem = htmlDoc.getCharacterElement(doc_pos);
+
+ int start_offset = startElem.getStartOffset();
+
+ // Positions before start_offset will not be traversed, and thus
+ // will not be counted.
+ if (start_offset > 0)
+ doc_offset_remaining = doc_offset_remaining - start_offset;
+
+ Element tempParentElem = startElem;
+
+ while ((tempParentElem = tempParentElem.getParentElement()) != null)
+ {
+ if (!htmlFragmentParentHashSet.contains(tempParentElem))
+ htmlFragmentParentHashSet.add(tempParentElem);
+ } // while((tempParentElem = tempParentElem.getParentElement())
+ // != null)
+
+ // NOTE: 20061030 - fchoong - the last index should not be included.
+ endElem = htmlDoc.getCharacterElement(doc_pos + doc_len - 1);
+
+ tempParentElem = endElem;
+
+ while ((tempParentElem = tempParentElem.getParentElement()) != null)
+ {
+ if (!htmlFragmentParentHashSet.contains(tempParentElem))
+ htmlFragmentParentHashSet.add(tempParentElem);
+ } // while((tempParentElem = tempParentElem.getParentElement())
+ // != null)
+
+ traverseHtmlFragment(rootElem);
+
+ } // else
+
+ // NOTE: close out remaining open embeded tags.
+ Object[] tag_arr = openEmbededTagHashSet.toArray();
+
+ for (int i = 0; i < tag_arr.length; i++)
+ {
+ writeRaw("</" + tag_arr[i].toString() + ">");
+ } // for(int i = 0; i < tag_arr.length; i++)
+
+ } // public void write() throws IOException, BadLocationException
+
+ /**
+ * Writes all the attributes in the attrSet, except for attrbutes with
+ * keys of <code>javax.swing.text.html.HTML.Tag</code>,
+ * <code>javax.swing.text.StyleConstants</code> or
+ * <code>javax.swing.text.html.HTML.Attribute.ENDTAG</code>.
+ *
+ * @param attrSet attrSet to write out
+ *
+ * @throws IOException on any I/O exceptions
+ */
+ protected void writeAttributes(AttributeSet attrSet)
+ throws IOException
+ {
+ Enumeration attrNameEnum = attrSet.getAttributeNames();
+
+ while (attrNameEnum.hasMoreElements())
+ {
+ Object key = attrNameEnum.nextElement();
+ Object value = attrSet.getAttribute(key);
+
+ // HTML.Attribute.ENDTAG is an instance, not a class.
+ if (!((key instanceof HTML.Tag) || (key instanceof StyleConstants)
+ || (key == HTML.Attribute.ENDTAG)))
+ {
+ if (key == HTML.Attribute.SELECTED)
+ writeRaw(" selected");
+ else if (key == HTML.Attribute.CHECKED)
+ writeRaw(" checked");
+ else
+ writeRaw(" " + key + "=\"" + value + "\"");
+ } // if(!((key instanceof HTML.Tag) || (key instanceof
+ // StyleConstants) || (key == HTML.Attribute.ENDTAG)))
+ } // while(attrNameEnum.hasMoreElements())
+
+ } // protected void writeAttributes(AttributeSet attrSet) throws IOException
+
+ /**
+ * Writes out an empty tag. i.e. a tag without any child elements.
+ *
+ * @param paramElem the element to output as an empty tag
+ *
+ * @throws IOException on any I/O exceptions
+ * @throws BadLocationException if a pos is not a valid position in the
+ * html doc element
+ */
+ protected void emptyTag(Element paramElem)
+ throws IOException, BadLocationException
+ {
+ String elem_name = paramElem.getName();
+ AttributeSet attrSet = paramElem.getAttributes();
+
+ writeRaw("<" + elem_name);
+ writeAttributes(attrSet);
+ writeRaw(">");
+
+ if (isBlockTag(attrSet))
+ {
+ writeRaw("</" + elem_name + ">");
+ } // if(isBlockTag(attrSet))
+
+ } // protected void emptyTag(Element paramElem)
+ // throws IOException, BadLocationException
+
+ /**
+ * Determines if it is a block tag or not.
+ *
+ * @param attrSet the attrSet of the element
+ *
+ * @return <code>true</code> if it is a block tag
+ * <code>false</code> if it is a not block tag
+ */
+ protected boolean isBlockTag(AttributeSet attrSet)
+ {
+ return ((HTML.Tag)
+ attrSet.getAttribute(StyleConstants.NameAttribute)).isBlock();
+ } // protected boolean isBlockTag(AttributeSet attrSet)
+
+ /**
+ * Writes out a start tag. Synthesized elements are skipped.
+ *
+ * @param paramElem the element to output as a start tag
+ * @throws IOException on any I/O exceptions
+ * @throws BadLocationException if a pos is not a valid position in the
+ * html doc element
+ */
+ protected void startTag(Element paramElem)
+ throws IOException, BadLocationException
+ {
+ // NOTE: Sysnthesized elements do no call this method at all.
+ String elem_name = paramElem.getName();
+ AttributeSet attrSet = paramElem.getAttributes();
+
+ indent();
+ writeRaw("<" + elem_name);
+ writeAttributes(attrSet);
+ writeRaw(">");
+ writeLineSeparator(); // Extra formatting to look more like the RI.
+ incrIndent();
+
+ } // protected void startTag(Element paramElem)
+ // throws IOException, BadLocationException
+
+ /**
+ * Writes out the contents of a textarea.
+ *
+ * @param attrSet the attrSet of the element to output as a text area
+ * @throws IOException on any I/O exceptions
+ * @throws BadLocationException if a pos is not a valid position in the
+ * html doc element
+ */
+ protected void textAreaContent(AttributeSet attrSet)
+ throws IOException, BadLocationException
+ {
+ writeLineSeparator(); // Extra formatting to look more like the RI.
+ indent();
+ writeRaw("<textarea");
+ writeAttributes(attrSet);
+ writeRaw(">");
+
+ Document tempDocument =
+ (Document) attrSet.getAttribute(StyleConstants.ModelAttribute);
+
+ writeRaw(tempDocument.getText(0, tempDocument.getLength()));
+ indent();
+ writeRaw("</textarea>");
+
+ } // protected void textAreaContent(AttributeSet attrSet)
+ // throws IOException, BadLocationException
+
+ /**
+ * Writes out text, within the appropriate range if it is specified.
+ *
+ * @param paramElem the element to output as a text
+ * @throws IOException on any I/O exceptions
+ * @throws BadLocationException if a pos is not a valid position in the
+ * html doc element
+ */
+ protected void text(Element paramElem)
+ throws IOException, BadLocationException
+ {
+ int offset = paramElem.getStartOffset();
+ int len = paramElem.getEndOffset() - paramElem.getStartOffset();
+ String txt_value = htmlDoc.getText(offset, len);
+
+ writeContent(txt_value);
+
+ } // protected void text(Element paramElem)
+ // throws IOException, BadLocationException
+
+ /**
+ * Writes out the contents of a select element.
+ *
+ * @param attrSet the attrSet of the element to output as a select box
+ *
+ * @throws IOException on any I/O exceptions
+ */
+ protected void selectContent(AttributeSet attrSet)
+ throws IOException
+ {
+ writeLineSeparator(); // Extra formatting to look more like the RI.
+ indent();
+ writeRaw("<select");
+ writeAttributes(attrSet);
+ writeRaw(">");
+ incrIndent();
+ writeLineSeparator(); // extra formatting to look more like the RI.
+
+ ComboBoxModel comboBoxModel =
+ (ComboBoxModel) attrSet.getAttribute(StyleConstants.ModelAttribute);
+
+ for (int i = 0; i < comboBoxModel.getSize(); i++)
+ {
+ writeOption((Option) comboBoxModel.getElementAt(i));
+ } // for(int i = 0; i < comboBoxModel.getSize(); i++)
+
+ decrIndent();
+ indent();
+ writeRaw("</select>");
+
+ } // protected void selectContent(AttributeSet attrSet) throws IOException
+
+ /**
+ * Writes out the contents of an option element.
+ *
+ * @param option the option object to output as a select option
+ *
+ * @throws IOException on any I/O exceptions
+ */
+ protected void writeOption(Option option)
+ throws IOException
+ {
+ indent();
+ writeRaw("<option");
+ writeAttributes(option.getAttributes());
+ writeRaw(">");
+
+ writeContent(option.getLabel());
+
+ writeRaw("</option>");
+ writeLineSeparator(); // extra formatting to look more like the RI.
+
+ } // protected void writeOption(Option option) throws IOException
+
+ /**
+ * Writes out an end tag.
+ *
+ * @param paramElem the element to output as an end tag
+ *
+ * @throws IOException on any I/O exceptions
+ */
+ protected void endTag(Element paramElem)
+ throws IOException
+ {
+ String elem_name = paramElem.getName();
+
+ //writeLineSeparator(); // Extra formatting to look more like the RI.
+ decrIndent();
+ indent();
+ writeRaw("</" + elem_name + ">");
+ writeLineSeparator(); // Extra formatting to look more like the RI.
+
+ } // protected void endTag(Element paramElem) throws IOException
+
+ /**
+ * Writes out the comment.
+ *
+ * @param paramElem the element to output as a comment
+ */
+ protected void comment(Element paramElem)
+ throws IOException, BadLocationException
+ {
+ AttributeSet attrSet = paramElem.getAttributes();
+
+ String comment_str = (String) attrSet.getAttribute(HTML.Attribute.COMMENT);
+
+ writeRaw("<!--" + comment_str + "-->");
+
+ } // protected void comment(Element paramElem)
+ // throws IOException, BadLocationException
+
+ /**
+ * Determines if element is a synthesized
+ * <code>javax.swing.text.Element</code> or not.
+ *
+ * @param element the element to test
+ *
+ * @return <code>true</code> if it is a synthesized element,
+ * <code>false</code> if it is a not synthesized element
+ */
+ protected boolean synthesizedElement(Element element)
+ {
+ AttributeSet attrSet = element.getAttributes();
+ Object tagType = attrSet.getAttribute(StyleConstants.NameAttribute);
+
+ if (tagType == HTML.Tag.CONTENT || tagType == HTML.Tag.COMMENT
+ || tagType == HTML.Tag.IMPLIED)
+ return true;
+ else
+ return false;
+ } // protected boolean synthesizedElement(Element element)
+
+ /**
+ * Determines if
+ * <code>javax.swing.text.StyleConstants.NameAttribute</code>
+ * matches tag or not.
+ *
+ * @param attrSet the <code>javax.swing.text.AttributeSet</code> of
+ * element to be matched
+ * @param tag the HTML.Tag to match
+ *
+ * @return <code>true</code> if it matches,
+ * <code>false</code> if it does not match
+ */
+ protected boolean matchNameAttribute(AttributeSet attrSet, HTML.Tag tag)
+ {
+ Object tagType = attrSet.getAttribute(StyleConstants.NameAttribute);
+
+ if (tagType == tag)
+ return true;
+ else
+ return false;
+ } // protected boolean matchNameAttribute(AttributeSet attrSet,
+ // HTML.Tag tag)
+
+ /**
+ * Writes out an embedded tag. The tags not already in
+ * openEmbededTagHashSet will written out.
+ *
+ * @param attrSet the <code>javax.swing.text.AttributeSet</code> of
+ * the element to write out
+ *
+ * @throws IOException on any I/O exceptions
+ */
+ protected void writeEmbeddedTags(AttributeSet attrSet)
+ throws IOException
+ {
+ Enumeration attrNameEnum = attrSet.getAttributeNames();
+
+ while (attrNameEnum.hasMoreElements())
+ {
+ Object key = attrNameEnum.nextElement();
+ Object value = attrSet.getAttribute(key);
+
+ if (key instanceof HTML.Tag)
+ {
+ if (!openEmbededTagHashSet.contains(key))
+ {
+ writeRaw("<" + key);
+ writeAttributes((AttributeSet) value);
+ writeRaw(">");
+ openEmbededTagHashSet.add(key);
+ } // if(!openEmbededTagHashSet.contains(key))
+ } // if(key instanceof HTML.Tag)
+ } // while(attrNameEnum.hasMoreElements())
+
+ } // protected void writeEmbeddedTags(AttributeSet attrSet)
+ // throws IOException
+
+ /**
+ * Closes out an unwanted embedded tag. The tags from the
+ * openEmbededTagHashSet not found in attrSet will be written out.
+ *
+ * @param attrSet the AttributeSet of the element to write out
+ *
+ * @throws IOException on any I/O exceptions
+ */
+ protected void closeOutUnwantedEmbeddedTags(AttributeSet attrSet)
+ throws IOException
+ {
+ Object[] tag_arr = openEmbededTagHashSet.toArray();
+
+ for (int i = 0; i < tag_arr.length; i++)
+ {
+ HTML.Tag key = (HTML.Tag) tag_arr[i];
+
+ if (!attrSet.isDefined(key))
+ {
+ writeRaw("</" + key.toString() + ">");
+ openEmbededTagHashSet.remove(key);
+ } // if(!attrSet.isDefined(key))
+ } // for(int i = 0; i < tag_arr.length; i++)
+
+ } // protected void closeOutUnwantedEmbeddedTags(AttributeSet attrSet)
+ // throws IOException
+
+ /**
+ * Writes out a line separator. Overwrites the parent to write out a new
+ * line.
+ *
+ * @throws IOException on any I/O exceptions.
+ */
+ protected void writeLineSeparator()
+ throws IOException
+ {
+ writeRaw(new_line_str);
+ } // protected void writeLineSeparator() throws IOException
+
+ /**
+ * Write to the writer. Character entites such as &lt;, &gt;
+ * are escaped appropriately.
+ *
+ * @param chars char array to write out
+ * @param off offset
+ * @param len length
+ *
+ * @throws IOException on any I/O exceptions
+ */
+ protected void output(char[] chars, int off, int len)
+ throws IOException
+ {
+ StringBuffer strBuffer = new StringBuffer();
+
+ for (int i = 0; i < chars.length; i++)
+ {
+ if (isCharHtmlEntity(chars[i]))
+ strBuffer.append(escapeCharHtmlEntity(chars[i]));
+ else
+ strBuffer.append(chars[i]);
+ } // for(int i = 0; i < chars.length; i++)
+
+ writeRaw(strBuffer.toString());
+
+ } // protected void output(char[] chars, int off, int len)
+ // throws IOException
+
+ //-------------------------------------------------------------------------
+ // private methods
+
+ /**
+ * The main method used to traverse through the elements.
+ *
+ * @param paramElem element to traverse
+ *
+ * @throws IOException on any I/O exceptions
+ */
+ private void traverse(Element paramElem)
+ throws IOException, BadLocationException
+ {
+ Element currElem = paramElem;
+
+ AttributeSet attrSet = currElem.getAttributes();
+
+ closeOutUnwantedEmbeddedTags(attrSet);
+
+ // handle the tag
+ if (synthesizedElement(paramElem))
+ {
+ if (matchNameAttribute(attrSet, HTML.Tag.CONTENT))
+ {
+ writeEmbeddedTags(attrSet);
+ text(currElem);
+ } // if(matchNameAttribute(attrSet, HTML.Tag.CONTENT))
+ else if (matchNameAttribute(attrSet, HTML.Tag.COMMENT))
+ {
+ comment(currElem);
+ } // else if(matchNameAttribute(attrSet, HTML.Tag.COMMENT))
+ else if (matchNameAttribute(attrSet, HTML.Tag.IMPLIED))
+ {
+ int child_elem_count = currElem.getElementCount();
+
+ if (child_elem_count > 0)
+ {
+ for (int i = 0; i < child_elem_count; i++)
+ {
+ Element childElem = paramElem.getElement(i);
+
+ traverse(childElem);
+
+ } // for(int i = 0; i < child_elem_count; i++)
+ } // if(child_elem_count > 0)
+ } // else if(matchNameAttribute(attrSet, HTML.Tag.IMPLIED))
+ } // if(synthesizedElement(paramElem))
+ else
+ {
+ // NOTE: 20061030 - fchoong - title is treated specially here.
+ // based on RI behavior.
+ if (matchNameAttribute(attrSet, HTML.Tag.TITLE))
+ {
+ boolean fg_is_end_tag = false;
+ Enumeration attrNameEnum = attrSet.getAttributeNames();
+
+ while (attrNameEnum.hasMoreElements())
+ {
+ Object key = attrNameEnum.nextElement();
+ Object value = attrSet.getAttribute(key);
+
+ if (key == HTML.Attribute.ENDTAG && value.equals("true"))
+ fg_is_end_tag = true;
+ } // while(attrNameEnum.hasMoreElements())
+
+ if (fg_is_end_tag)
+ writeRaw("</title>");
+ else
+ {
+ indent();
+ writeRaw("<title>");
+
+ String title_str =
+ (String) htmlDoc.getProperty(HTMLDocument.TitleProperty);
+
+ if (title_str != null)
+ writeContent(title_str);
+
+ } // else
+ } // if(matchNameAttribute(attrSet, HTML.Tag.TITLE))
+ else if (matchNameAttribute(attrSet, HTML.Tag.PRE))
+ {
+ // We pursue more stringent formating here.
+ attrSet = paramElem.getAttributes();
+
+ indent();
+ writeRaw("<pre");
+ writeAttributes(attrSet);
+ writeRaw(">");
+
+ int child_elem_count = currElem.getElementCount();
+
+ for (int i = 0; i < child_elem_count; i++)
+ {
+ Element childElem = paramElem.getElement(i);
+
+ traverse(childElem);
+
+ } // for(int i = 0; i < child_elem_count; i++)
+
+ writeRaw("</pre>");
+
+ } // else if(matchNameAttribute(attrSet, HTML.Tag.PRE))
+ else if (matchNameAttribute(attrSet, HTML.Tag.SELECT))
+ {
+ selectContent(attrSet);
+ } // else if(matchNameAttribute(attrSet, HTML.Tag.SELECT))
+ else if (matchNameAttribute(attrSet, HTML.Tag.TEXTAREA))
+ {
+ textAreaContent(attrSet);
+ } // else if(matchNameAttribute(attrSet, HTML.Tag.TEXTAREA))
+ else
+ {
+ int child_elem_count = currElem.getElementCount();
+
+ if (child_elem_count > 0)
+ {
+ startTag(currElem);
+
+ for (int i = 0; i < child_elem_count; i++)
+ {
+ Element childElem = paramElem.getElement(i);
+
+ traverse(childElem);
+
+ } // for(int i = 0; i < child_elem_count; i++)
+
+ endTag(currElem);
+
+ } // if(child_elem_count > 0)
+ else
+ {
+ emptyTag(currElem);
+ } // else
+ } // else
+ } // else
+
+ } // private void traverse(Element paramElem)
+ // throws IOException, BadLocationException
+
+ /**
+ * The method used to traverse through a html fragment.
+ *
+ * @param paramElem element to traverse
+ *
+ * @throws IOException on any I/O exceptions
+ */
+ private void traverseHtmlFragment(Element paramElem)
+ throws IOException, BadLocationException
+ {
+ // NOTE: This method is similar to traverse(Element paramElem)
+ Element currElem = paramElem;
+
+ boolean fg_is_fragment_parent_elem = false;
+ boolean fg_is_start_and_end_elem = false;
+
+ if (htmlFragmentParentHashSet.contains(paramElem))
+ fg_is_fragment_parent_elem = true;
+
+ if (paramElem == startElem)
+ fg_pass_start_elem = true;
+
+ if (paramElem == startElem && paramElem == endElem)
+ fg_is_start_and_end_elem = true;
+
+ AttributeSet attrSet = currElem.getAttributes();
+
+ closeOutUnwantedEmbeddedTags(attrSet);
+
+ if (fg_is_fragment_parent_elem || (fg_pass_start_elem
+ && fg_pass_end_elem == false) || fg_is_start_and_end_elem)
+ {
+ // handle the tag
+ if (synthesizedElement(paramElem))
+ {
+ if (matchNameAttribute(attrSet, HTML.Tag.CONTENT))
+ {
+ writeEmbeddedTags(attrSet);
+
+ int content_offset = paramElem.getStartOffset();
+ int content_length = currElem.getEndOffset() - content_offset;
+
+ if (doc_offset_remaining > 0)
+ {
+ if (content_length > doc_offset_remaining)
+ {
+ int split_len = content_length;
+
+ split_len = split_len - doc_offset_remaining;
+
+ if (split_len > doc_len_remaining)
+ split_len = doc_len_remaining;
+
+ // we need to split it.
+ String txt_value = htmlDoc.getText(content_offset
+ + doc_offset_remaining, split_len);
+
+ writeContent(txt_value);
+
+ doc_offset_remaining = 0; // the offset is used up.
+ doc_len_remaining = doc_len_remaining - split_len;
+ } // if(content_length > doc_offset_remaining)
+ else
+ {
+ // doc_offset_remaining is greater than the entire
+ // length of content
+ doc_offset_remaining = doc_offset_remaining
+ - content_length;
+ } // else
+ } // if(doc_offset_remaining > 0)
+ else if (content_length <= doc_len_remaining)
+ {
+ // we can fit the entire content.
+ text(currElem);
+ doc_len_remaining = doc_len_remaining - content_length;
+ } // else if(content_length <= doc_len_remaining)
+ else
+ {
+ // we need to split it.
+ String txt_value = htmlDoc.getText(content_offset,
+ doc_len_remaining);
+
+ writeContent(txt_value);
+
+ doc_len_remaining = 0;
+ } // else
+
+ } // if(matchNameAttribute(attrSet, HTML.Tag.CONTENT))
+ else if (matchNameAttribute(attrSet, HTML.Tag.COMMENT))
+ {
+ comment(currElem);
+ } // else if(matchNameAttribute(attrSet, HTML.Tag.COMMENT))
+ else if (matchNameAttribute(attrSet, HTML.Tag.IMPLIED))
+ {
+ int child_elem_count = currElem.getElementCount();
+
+ if (child_elem_count > 0)
+ {
+ for (int i = 0; i < child_elem_count; i++)
+ {
+ Element childElem = paramElem.getElement(i);
+
+ traverseHtmlFragment(childElem);
+
+ } // for(int i = 0; i < child_elem_count; i++)
+ } // if(child_elem_count > 0)
+ } // else if(matchNameAttribute(attrSet, HTML.Tag.IMPLIED))
+ } // if(synthesizedElement(paramElem))
+ else
+ {
+ // NOTE: 20061030 - fchoong - the isLeaf() condition seems to
+ // generate the closest behavior to the RI.
+ if (paramElem.isLeaf())
+ {
+ if (doc_offset_remaining > 0)
+ {
+ doc_offset_remaining--;
+ } // if(doc_offset_remaining > 0)
+ else if (doc_len_remaining > 0)
+ {
+ doc_len_remaining--;
+ } // else if(doc_len_remaining > 0)
+ } // if(paramElem.isLeaf())
+
+ // NOTE: 20061030 - fchoong - title is treated specially here.
+ // based on RI behavior.
+ if (matchNameAttribute(attrSet, HTML.Tag.TITLE))
+ {
+ boolean fg_is_end_tag = false;
+ Enumeration attrNameEnum = attrSet.getAttributeNames();
+
+ while (attrNameEnum.hasMoreElements())
+ {
+ Object key = attrNameEnum.nextElement();
+ Object value = attrSet.getAttribute(key);
+
+ if (key == HTML.Attribute.ENDTAG && value.equals("true"))
+ fg_is_end_tag = true;
+ } // while(attrNameEnum.hasMoreElements())
+
+ if (fg_is_end_tag)
+ writeRaw("</title>");
+ else
+ {
+ indent();
+ writeRaw("<title>");
+
+ String title_str =
+ (String) htmlDoc.getProperty(HTMLDocument.TitleProperty);
+
+ if (title_str != null)
+ writeContent(title_str);
+
+ } // else
+ } // if(matchNameAttribute(attrSet, HTML.Tag.TITLE))
+ else if (matchNameAttribute(attrSet, HTML.Tag.PRE))
+ {
+ // We pursue more stringent formating here.
+ attrSet = paramElem.getAttributes();
+
+ indent();
+ writeRaw("<pre");
+ writeAttributes(attrSet);
+ writeRaw(">");
+
+ int child_elem_count = currElem.getElementCount();
+
+ for (int i = 0; i < child_elem_count; i++)
+ {
+ Element childElem = paramElem.getElement(i);
+
+ traverseHtmlFragment(childElem);
+
+ } // for(int i = 0; i < child_elem_count; i++)
+
+ writeRaw("</pre>");
+
+ } // else if(matchNameAttribute(attrSet, HTML.Tag.PRE))
+ else if (matchNameAttribute(attrSet, HTML.Tag.SELECT))
+ {
+ selectContent(attrSet);
+ } // else if(matchNameAttribute(attrSet, HTML.Tag.SELECT))
+ else if (matchNameAttribute(attrSet, HTML.Tag.TEXTAREA))
+ {
+ textAreaContent(attrSet);
+ } // else if(matchNameAttribute(attrSet, HTML.Tag.TEXTAREA))
+ else
+ {
+ int child_elem_count = currElem.getElementCount();
+
+ if (child_elem_count > 0)
+ {
+ startTag(currElem);
+
+ for (int i = 0; i < child_elem_count; i++)
+ {
+ Element childElem = paramElem.getElement(i);
+
+ traverseHtmlFragment(childElem);
+
+ } // for(int i = 0; i < child_elem_count; i++)
+
+ endTag(currElem);
+
+ } // if(child_elem_count > 0)
+ else
+ {
+ emptyTag(currElem);
+ } // else
+ } // else
+ } // else
+
+ } // if(fg_is_fragment_parent_elem || (fg_pass_start_elem
+ // && fg_pass_end_elem == false) || fg_is_start_and_end_elem)
+
+ if (paramElem == endElem)
+ fg_pass_end_elem = true;
+
+ } // private void traverseHtmlFragment(Element paramElem)
+ // throws IOException, BadLocationException
+
+ /**
+ * Write to the writer without any modifications.
+ *
+ * @param param_str the str to write out
+ *
+ * @throws IOException on any I/O exceptions
+ */
+ private void writeRaw(String param_str)
+ throws IOException
+ {
+ super.output(param_str.toCharArray(), 0, param_str.length());
+ } // private void writeRaw(char[] chars, int off, int len)
+ // throws IOException
+
+ /**
+ * Write to the writer, escaping HTML character entitie where neccessary.
+ *
+ * @param param_str the str to write out
+ *
+ * @throws IOException on any I/O exceptions
+ */
+ private void writeContent(String param_str)
+ throws IOException
+ {
+ char[] str_char_arr = param_str.toCharArray();
+
+ if (hasHtmlEntity(param_str))
+ output(str_char_arr, 0, str_char_arr.length);
+ else
+ super.output(str_char_arr, 0, str_char_arr.length);
+
+ } // private void writeContent(String param_str) throws IOException
+
+ /**
+ * Use this for debugging. Writes out all attributes regardless of type.
+ *
+ * @param attrSet the <code>javax.swing.text.AttributeSet</code> to
+ * write out
+ *
+ * @throws IOException on any I/O exceptions
+ */
+ private void writeAllAttributes(AttributeSet attrSet)
+ throws IOException
+ {
+ Enumeration attrNameEnum = attrSet.getAttributeNames();
+
+ while (attrNameEnum.hasMoreElements())
+ {
+ Object key = attrNameEnum.nextElement();
+ Object value = attrSet.getAttribute(key);
+
+ writeRaw(" " + key + "=\"" + value + "\"");
+ writeRaw(" " + key.getClass().toString() + "=\""
+ + value.getClass().toString() + "\"");
+ } // while(attrNameEnum.hasMoreElements())
+
+ } // private void writeAllAttributes(AttributeSet attrSet)
+ // throws IOException
+
+ /**
+ * Tests if the str contains any html entities.
+ *
+ * @param param_str the str to test
+ *
+ * @return <code>true</code> if it has a html entity
+ * <code>false</code> if it does not have a html entity
+ */
+ private boolean hasHtmlEntity(String param_str)
+ {
+ boolean ret_bool = false;
+
+ for (int i = 0; i < html_entity_char_arr.length; i++)
+ {
+ if (param_str.indexOf(html_entity_char_arr[i]) != -1)
+ {
+ ret_bool = true;
+ break;
+ } // if(param_str.indexOf(html_entity_char_arr[i]) != -1)
+ } // for(int i = 0; i < html_entity_char_arr.length; i++)
+
+ return ret_bool;
+ } // private boolean hasHtmlEntity(String param_str)
+
+ /**
+ * Tests if the char is a html entities.
+ *
+ * @param param_char the char to test
+ *
+ * @return <code>true</code> if it is a html entity
+ * <code>false</code> if it is not a html entity.
+ */
+ private boolean isCharHtmlEntity(char param_char)
+ {
+ boolean ret_bool = false;
+
+ for (int i = 0; i < html_entity_char_arr.length; i++)
+ {
+ if (param_char == html_entity_char_arr[i])
+ {
+ ret_bool = true;
+ break;
+ } // if(param_char == html_entity_char_arr[i])
+ } // for(int i = 0; i < html_entity_char_arr.length; i++)
+
+ return ret_bool;
+ } // private boolean hasHtmlEntity(String param_str)
+
+ /**
+ * Escape html entities.
+ *
+ * @param param_char the char to escape
+ *
+ * @return escaped html entity. Original char is returned as a str if is
+ * is not a html entity
+ */
+ private String escapeCharHtmlEntity(char param_char)
+ {
+ String ret_str = "" + param_char;
+
+ for (int i = 0; i < html_entity_char_arr.length; i++)
+ {
+ if (param_char == html_entity_char_arr[i])
+ {
+ ret_str = html_entity_escape_str_arr[i];
+ break;
+ } // if(param_char == html_entity_char_arr[i])
+ } // for(int i = 0; i < html_entity_char_arr.length; i++)
+
+ return ret_str;
+ } // private String escapeCharHtmlEntity(char param_char)
+
+} // public class HTMLWriter extends AbstractWriter \ No newline at end of file
diff --git a/libjava/classpath/javax/swing/text/html/ImageView.java b/libjava/classpath/javax/swing/text/html/ImageView.java
index 84b021070a9..bf906e4500e 100644
--- a/libjava/classpath/javax/swing/text/html/ImageView.java
+++ b/libjava/classpath/javax/swing/text/html/ImageView.java
@@ -1,18 +1,21 @@
package javax.swing.text.html;
-import gnu.javax.swing.text.html.CombinedAttributes;
import gnu.javax.swing.text.html.ImageViewIconFactory;
+import gnu.javax.swing.text.html.css.Length;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Rectangle;
import java.awt.Shape;
+import java.awt.Toolkit;
+import java.awt.image.ImageObserver;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.Icon;
-import javax.swing.ImageIcon;
+import javax.swing.SwingUtilities;
+import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
@@ -29,15 +32,38 @@ import javax.swing.text.html.HTML.Attribute;
public class ImageView extends View
{
/**
+ * Tracks image loading state and performs the necessary layout updates.
+ */
+ class Observer
+ implements ImageObserver
+ {
+
+ public boolean imageUpdate(Image image, int flags, int x, int y, int width, int height)
+ {
+ boolean widthChanged = false;
+ if ((flags & ImageObserver.WIDTH) != 0 && spans[X_AXIS] == null)
+ widthChanged = true;
+ boolean heightChanged = false;
+ if ((flags & ImageObserver.HEIGHT) != 0 && spans[Y_AXIS] == null)
+ heightChanged = true;
+ if (widthChanged || heightChanged)
+ safePreferenceChanged(ImageView.this, widthChanged, heightChanged);
+ boolean ret = (flags & ALLBITS) != 0;
+ return ret;
+ }
+
+ }
+
+ /**
* True if the image loads synchronuosly (on demand). By default, the image
* loads asynchronuosly.
*/
boolean loadOnDemand;
-
+
/**
* The image icon, wrapping the image,
*/
- ImageIcon imageIcon;
+ Image image;
/**
* The image state.
@@ -45,6 +71,58 @@ public class ImageView extends View
byte imageState = MediaTracker.LOADING;
/**
+ * True when the image needs re-loading, false otherwise.
+ */
+ private boolean reloadImage;
+
+ /**
+ * True when the image properties need re-loading, false otherwise.
+ */
+ private boolean reloadProperties;
+
+ /**
+ * True when the width is set as CSS/HTML attribute.
+ */
+ private boolean haveWidth;
+
+ /**
+ * True when the height is set as CSS/HTML attribute.
+ */
+ private boolean haveHeight;
+
+ /**
+ * True when the image is currently loading.
+ */
+ private boolean loading;
+
+ /**
+ * The current width of the image.
+ */
+ private int width;
+
+ /**
+ * The current height of the image.
+ */
+ private int height;
+
+ /**
+ * Our ImageObserver for tracking the loading state.
+ */
+ private ImageObserver observer;
+
+ /**
+ * The CSS width and height.
+ *
+ * Package private to avoid synthetic accessor methods.
+ */
+ Length[] spans;
+
+ /**
+ * The cached attributes.
+ */
+ private AttributeSet attributes;
+
+ /**
* Creates the image view that represents the given element.
*
* @param element the element, represented by this image view.
@@ -52,25 +130,36 @@ public class ImageView extends View
public ImageView(Element element)
{
super(element);
+ spans = new Length[2];
+ observer = new Observer();
+ reloadProperties = true;
+ reloadImage = true;
+ loadOnDemand = false;
}
/**
* Load or reload the image. This method initiates the image reloading. After
* the image is ready, the repaint event will be scheduled. The current image,
* if it already exists, will be discarded.
- *
- * @param itsTime
- * also load if the "on demand" property is set
*/
- void reloadImage(boolean itsTime)
+ private void reloadImage()
{
- URL url = getImageURL();
- if (url == null)
- imageState = (byte) MediaTracker.ERRORED;
- else if (!(loadOnDemand && !itsTime))
- imageIcon = new ImageIcon(url);
- else
- imageState = (byte) MediaTracker.LOADING;
+ loading = true;
+ reloadImage = false;
+ haveWidth = false;
+ haveHeight = false;
+ image = null;
+ width = 0;
+ height = 0;
+ try
+ {
+ loadImage();
+ updateSize();
+ }
+ finally
+ {
+ loading = false;
+ }
}
/**
@@ -146,12 +235,9 @@ public class ImageView extends View
*/
public AttributeSet getAttributes()
{
- StyleSheet styles = getStyleSheet();
- if (styles == null)
- return super.getAttributes();
- else
- return CombinedAttributes.combine(super.getAttributes(),
- styles.getViewAttributes(this));
+ if (attributes == null)
+ attributes = getStyleSheet().getViewAttributes(this);
+ return attributes;
}
/**
@@ -159,10 +245,8 @@ public class ImageView extends View
*/
public Image getImage()
{
- if (imageIcon == null)
- return null;
- else
- return imageIcon.getImage();
+ updateState();
+ return image;
}
/**
@@ -175,19 +259,22 @@ public class ImageView extends View
*/
public URL getImageURL()
{
- Object url = getAttributes().getAttribute(Attribute.SRC);
- if (url == null)
- return null;
-
- try
+ Element el = getElement();
+ String src = (String) el.getAttributes().getAttribute(Attribute.SRC);
+ URL url = null;
+ if (src != null)
{
- return new URL(url.toString());
- }
- catch (MalformedURLException e)
- {
- // The URL is malformed - no image.
- return null;
+ URL base = ((HTMLDocument) getDocument()).getBase();
+ try
+ {
+ url = new URL(base, src);
+ }
+ catch (MalformedURLException ex)
+ {
+ // Return null.
+ }
}
+ return url;
}
/**
@@ -242,9 +329,8 @@ public class ImageView extends View
if (axis == View.X_AXIS)
{
- Object w = attrs.getAttribute(Attribute.WIDTH);
- if (w != null)
- return Integer.parseInt(w.toString());
+ if (spans[axis] != null)
+ return spans[axis].getValue();
else if (image != null)
return image.getWidth(getContainer());
else
@@ -252,9 +338,8 @@ public class ImageView extends View
}
else if (axis == View.Y_AXIS)
{
- Object w = attrs.getAttribute(Attribute.HEIGHT);
- if (w != null)
- return Integer.parseInt(w.toString());
+ if (spans[axis] != null)
+ return spans[axis].getValue();
else if (image != null)
return image.getHeight(getContainer());
else
@@ -271,11 +356,8 @@ public class ImageView extends View
*/
protected StyleSheet getStyleSheet()
{
- Document d = getElement().getDocument();
- if (d instanceof HTMLDocument)
- return ((HTMLDocument) d).getStyleSheet();
- else
- return null;
+ HTMLDocument doc = (HTMLDocument) getDocument();
+ return doc.getStyleSheet();
}
/**
@@ -288,7 +370,7 @@ public class ImageView extends View
{
return getAltText();
}
-
+
/**
* Paints the image or one of the two image state icons. The image is resized
* to the shape bounds. If there is no image available, the alternative text
@@ -302,83 +384,22 @@ public class ImageView extends View
*/
public void paint(Graphics g, Shape bounds)
{
- Rectangle r = bounds.getBounds();
-
- if (imageIcon == null)
-
- {
- // Loading image on demand, rendering the loading icon so far.
- reloadImage(true);
-
- // The reloadImage sets the imageIcon, unless the URL is broken
- // or malformed.
- if (imageIcon != null)
- {
- if (imageIcon.getImageLoadStatus() != MediaTracker.COMPLETE)
- {
- // Render "not ready" icon, unless the image is ready
- // immediately.
- renderIcon(g, r, getLoadingImageIcon());
- // Add the listener to repaint when the icon will be ready.
- imageIcon.setImageObserver(getContainer());
- return;
- }
- }
- else
- {
- renderIcon(g, r, getNoImageIcon());
- return;
- }
- }
-
- imageState = (byte) imageIcon.getImageLoadStatus();
-
- switch (imageState)
- {
- case MediaTracker.ABORTED:
- case MediaTracker.ERRORED:
- renderIcon(g, r, getNoImageIcon());
- break;
- case MediaTracker.LOADING:
- // If the image is not loaded completely, we still render it, as the
- // partial image may be available.
- case MediaTracker.COMPLETE:
+ updateState();
+ Rectangle r = bounds instanceof Rectangle ? (Rectangle) bounds
+ : bounds.getBounds();
+ Image image = getImage();
+ if (image != null)
{
- // Paint the scaled image.
- Image scaled = imageIcon.getImage().getScaledInstance(
- r.width,
- r.height,
- Image.SCALE_DEFAULT);
- ImageIcon painter = new ImageIcon(scaled);
- painter.paintIcon(getContainer(), g, r.x, r.y);
- }
- break;
+ g.drawImage(image, r.x, r.y, r.width, r.height, observer);
}
- }
-
- /**
- * Render "no image" icon and the alternative "no image" text. The text is
- * rendered right from the icon and is aligned to the icon bottom.
- */
- private void renderIcon(Graphics g, Rectangle bounds, Icon icon)
- {
- Shape current = g.getClip();
- try
+ else
{
- g.setClip(bounds);
+ Icon icon = getNoImageIcon();
if (icon != null)
- {
- icon.paintIcon(getContainer(), g, bounds.x, bounds.y);
- g.drawString(getAltText(), bounds.x + icon.getIconWidth(),
- bounds.y + icon.getIconHeight());
- }
- }
- finally
- {
- g.setClip(current);
+ icon.paintIcon(getContainer(), g, r.x, r.y);
}
}
-
+
/**
* Set if the image should be loaded only when needed (synchronuosly). By
* default, the image loads asynchronuosly. If the image is not yet ready, the
@@ -395,9 +416,20 @@ public class ImageView extends View
*/
protected void setPropertiesFromAttributes()
{
- // In the current implementation, nothing is cached yet, unless the image
- // itself.
- imageIcon = null;
+ AttributeSet atts = getAttributes();
+ StyleSheet ss = getStyleSheet();
+ float emBase = ss.getEMBase(atts);
+ float exBase = ss.getEXBase(atts);
+ spans[X_AXIS] = (Length) atts.getAttribute(CSS.Attribute.WIDTH);
+ if (spans[X_AXIS] != null)
+ {
+ spans[X_AXIS].setFontBases(emBase, exBase);
+ }
+ spans[Y_AXIS] = (Length) atts.getAttribute(CSS.Attribute.HEIGHT);
+ if (spans[Y_AXIS] != null)
+ {
+ spans[Y_AXIS].setFontBases(emBase, exBase);
+ }
}
/**
@@ -433,9 +465,130 @@ public class ImageView extends View
*/
public void setSize(float width, float height)
{
- if (imageIcon == null)
- reloadImage(false);
+ updateState();
+ // TODO: Implement this when we have an alt view for the alt=... attribute.
}
-
+ /**
+ * This makes sure that the image and properties have been loaded.
+ */
+ private void updateState()
+ {
+ if (reloadImage)
+ reloadImage();
+ if (reloadProperties)
+ setPropertiesFromAttributes();
+ }
+
+ /**
+ * Actually loads the image.
+ */
+ private void loadImage()
+ {
+ URL src = getImageURL();
+ Image newImage = null;
+ if (src != null)
+ {
+ // Call getImage(URL) to allow the toolkit caching of that image URL.
+ Toolkit tk = Toolkit.getDefaultToolkit();
+ newImage = tk.getImage(src);
+ tk.prepareImage(newImage, -1, -1, observer);
+ if (newImage != null && getLoadsSynchronously())
+ {
+ // Load image synchronously.
+ MediaTracker tracker = new MediaTracker(getContainer());
+ tracker.addImage(newImage, 0);
+ try
+ {
+ tracker.waitForID(0);
+ }
+ catch (InterruptedException ex)
+ {
+ Thread.interrupted();
+ }
+
+ }
+ }
+ image = newImage;
+ }
+
+ /**
+ * Updates the size parameters of the image.
+ */
+ private void updateSize()
+ {
+ int newW = 0;
+ int newH = 0;
+ Image newIm = getImage();
+ if (newIm != null)
+ {
+ AttributeSet atts = getAttributes();
+ // Fetch width.
+ Length l = spans[X_AXIS];
+ if (l != null)
+ {
+ newW = (int) l.getValue();
+ haveWidth = true;
+ }
+ else
+ {
+ newW = newIm.getWidth(observer);
+ }
+ // Fetch height.
+ l = spans[Y_AXIS];
+ if (l != null)
+ {
+ newH = (int) l.getValue();
+ haveHeight = true;
+ }
+ else
+ {
+ newW = newIm.getWidth(observer);
+ }
+ // Go and trigger loading.
+ Toolkit tk = Toolkit.getDefaultToolkit();
+ if (haveWidth || haveHeight)
+ tk.prepareImage(newIm, width, height, observer);
+ else
+ tk.prepareImage(newIm, -1, -1, observer);
+ }
+ }
+
+ /**
+ * Calls preferenceChanged from the event dispatch thread and within
+ * a read lock to protect us from threading issues.
+ *
+ * @param v the view
+ * @param width true when the width changed
+ * @param height true when the height changed
+ */
+ void safePreferenceChanged(final View v, final boolean width,
+ final boolean height)
+ {
+ if (SwingUtilities.isEventDispatchThread())
+ {
+ Document doc = getDocument();
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ preferenceChanged(v, width, height);
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ }
+ else
+ {
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ safePreferenceChanged(v, width, height);
+ }
+ });
+ }
+ }
}
diff --git a/libjava/classpath/javax/swing/text/html/InlineView.java b/libjava/classpath/javax/swing/text/html/InlineView.java
index 77ec86e8263..58edc738526 100644
--- a/libjava/classpath/javax/swing/text/html/InlineView.java
+++ b/libjava/classpath/javax/swing/text/html/InlineView.java
@@ -38,13 +38,17 @@ exception statement from your version. */
package javax.swing.text.html;
+import java.awt.FontMetrics;
import java.awt.Shape;
+import java.text.BreakIterator;
import javax.swing.event.DocumentEvent;
import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.LabelView;
+import javax.swing.text.Segment;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;
@@ -60,6 +64,23 @@ public class InlineView
{
/**
+ * The attributes used by this view.
+ */
+ private AttributeSet attributes;
+
+ /**
+ * The span of the longest word in this view.
+ *
+ * @see #getLongestWord()
+ */
+ private float longestWord;
+
+ /**
+ * Indicates if we may wrap or not.
+ */
+ private boolean nowrap;
+
+ /**
* Creates a new <code>InlineView</code> that renders the specified element.
*
* @param element the element for this view
@@ -115,6 +136,9 @@ public class InlineView
public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f)
{
super.changedUpdate(e, a, f);
+ StyleSheet ss = getStyleSheet();
+ attributes = ss.getViewAttributes(this);
+ preferenceChanged(null, true, true);
setPropertiesFromAttributes();
}
@@ -126,15 +150,23 @@ public class InlineView
*/
public AttributeSet getAttributes()
{
- // FIXME: Implement this.
- return super.getAttributes();
+ if (attributes == null)
+ {
+ StyleSheet ss = getStyleSheet();
+ attributes = ss.getViewAttributes(this);
+ }
+ return attributes;
}
public int getBreakWeight(int axis, float pos, float len)
{
- // FIXME: Implement this.
- return super.getBreakWeight(axis, pos, len);
+ int weight;
+ if (nowrap)
+ weight = BadBreakWeight;
+ else
+ weight = super.getBreakWeight(axis, pos, len);
+ return weight;
}
public View breakView(int axis, int offset, float pos, float len)
@@ -143,10 +175,48 @@ public class InlineView
return super.breakView(axis, offset, pos, len);
}
+ /**
+ * Loads the character style properties from the stylesheet.
+ */
protected void setPropertiesFromAttributes()
{
- // FIXME: Implement this.
super.setPropertiesFromAttributes();
+ AttributeSet atts = getAttributes();
+ Object o = atts.getAttribute(CSS.Attribute.TEXT_DECORATION);
+
+ // Check for underline.
+ boolean b = false;
+ if (o != null && o.toString().contains("underline"))
+ b = true;
+ setUnderline(b);
+
+ // Check for line-through.
+ b = false;
+ if (o != null && o.toString().contains("line-through"))
+ b = true;
+ setStrikeThrough(b);
+
+ // Check for vertical alignment (subscript/superscript).
+ o = atts.getAttribute(CSS.Attribute.VERTICAL_ALIGN);
+
+ // Subscript.
+ b = false;
+ if (o != null && o.toString().contains("sub"))
+ b = true;
+ setSubscript(b);
+
+ // Superscript.
+ b = false;
+ if (o != null && o.toString().contains("sup"))
+ b = true;
+ setSuperscript(b);
+
+ // Fetch nowrap setting.
+ o = atts.getAttribute(CSS.Attribute.WHITE_SPACE);
+ if (o != null && o.equals("nowrap"))
+ nowrap = true;
+ else
+ nowrap = false;
}
/**
@@ -163,4 +233,75 @@ public class InlineView
styleSheet = ((HTMLDocument) doc).getStyleSheet();
return styleSheet;
}
+
+ /**
+ * Returns the minimum span for the specified axis. This returns the
+ * width of the longest word for the X axis and the super behaviour for
+ * the Y axis. This is a slight deviation from the reference implementation.
+ * IMO this should improve rendering behaviour so that an InlineView never
+ * gets smaller than the longest word in it.
+ */
+ public float getMinimumSpan(int axis)
+ {
+ float min = super.getMinimumSpan(axis);
+ if (axis == X_AXIS)
+ min = Math.max(getLongestWord(), min);
+ return min;
+ }
+
+ /**
+ * Returns the span of the longest word in this view.
+ *
+ * @return the span of the longest word in this view
+ */
+ private float getLongestWord()
+ {
+ if (longestWord == -1)
+ longestWord = calculateLongestWord();
+ return longestWord;
+ }
+
+ /**
+ * Calculates the span of the longest word in this view.
+ *
+ * @return the span of the longest word in this view
+ */
+ private float calculateLongestWord()
+ {
+ float span = 0;
+ try
+ {
+ Document doc = getDocument();
+ int p0 = getStartOffset();
+ int p1 = getEndOffset();
+ Segment s = new Segment();
+ doc.getText(p0, p1 - p0, s);
+ BreakIterator iter = BreakIterator.getWordInstance();
+ iter.setText(s);
+ int wordStart = p0;
+ int wordEnd = p0;
+ int start = iter.first();
+ for (int end = iter.next(); end != BreakIterator.DONE;
+ start = end, end = iter.next())
+ {
+ if ((end - start) > (wordEnd - wordStart))
+ {
+ wordStart = start;
+ wordEnd = end;
+ }
+ }
+ if (wordEnd - wordStart > 0)
+ {
+ FontMetrics fm = getFontMetrics();
+ int offset = s.offset + wordStart - s.getBeginIndex();
+ span = fm.charsWidth(s.array, offset, wordEnd - wordStart);
+ }
+ }
+ catch (BadLocationException ex)
+ {
+ // Return 0.
+ }
+ return span;
+ }
+
}
diff --git a/libjava/classpath/javax/swing/text/html/ListView.java b/libjava/classpath/javax/swing/text/html/ListView.java
index c07d3598c92..3e809bbd209 100644
--- a/libjava/classpath/javax/swing/text/html/ListView.java
+++ b/libjava/classpath/javax/swing/text/html/ListView.java
@@ -94,9 +94,6 @@ public class ListView
public void paint(Graphics g, Shape allocation)
{
super.paint(g, allocation);
- // FIXME: Why is this overridden? I think that painting would be done
- // by the superclass and the stylesheet... Maybe find out when this
- // stuff is implemented properly.
}
/**
diff --git a/libjava/classpath/javax/swing/text/html/MultiAttributeSet.java b/libjava/classpath/javax/swing/text/html/MultiAttributeSet.java
new file mode 100644
index 00000000000..0f1145084e1
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/MultiAttributeSet.java
@@ -0,0 +1,213 @@
+/* MultiAttributeSet.java -- Multiplexes between a set of AttributeSets
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text.html;
+
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+import javax.swing.text.AttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+
+/**
+ * An AttributeSet impl that multiplexes between a set of other AttributeSets.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+class MultiAttributeSet
+ implements AttributeSet
+{
+
+ /**
+ * The Enumeration for the multiplexed names.
+ */
+ private class MultiNameEnumeration
+ implements Enumeration
+ {
+ /**
+ * The index of the current AttributeSet.
+ */
+ private int index;
+
+ /**
+ * The names Enumeration of the current AttributeSet.
+ */
+ private Enumeration current;
+
+ /**
+ * Creates a new instance.
+ */
+ MultiNameEnumeration()
+ {
+ index = 0;
+ current = multi[0].getAttributeNames();
+ }
+
+ public boolean hasMoreElements()
+ {
+ return current.hasMoreElements() || index < multi.length - 1;
+ }
+
+ public Object nextElement()
+ {
+ if (! current.hasMoreElements())
+ {
+ if (index < multi.length - 1)
+ {
+ index++;
+ current = multi[index].getAttributeNames();
+ }
+ else
+ throw new NoSuchElementException();
+ }
+ return current.nextElement();
+ }
+
+ }
+
+ /**
+ * The AttributeSets to multiplex.
+ */
+ AttributeSet[] multi;
+
+ /**
+ * Provided for subclasses that need to initialize via {@link #init}.
+ */
+ MultiAttributeSet()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param m the AttributeSets to multiplex
+ */
+ MultiAttributeSet(AttributeSet[] m)
+ {
+ init(m);
+ }
+
+ /**
+ * Provided for subclasses to initialize the attribute set.
+ *
+ * @param m the attributes to multiplex
+ */
+ void init(AttributeSet[] m)
+ {
+ multi = m;
+ }
+
+ public boolean containsAttribute(Object name, Object value)
+ {
+ boolean ret = false;
+ for (int i = 0; i < multi.length && ret == false; i++)
+ {
+ if (multi[i].containsAttribute(name, value))
+ ret = true;
+ }
+ return ret;
+ }
+
+ public boolean containsAttributes(AttributeSet attributes)
+ {
+ boolean ret = true;
+ Enumeration e = attributes.getAttributeNames();
+ while (ret && e.hasMoreElements())
+ {
+ Object key = e.nextElement();
+ ret = attributes.getAttribute(key).equals(getAttribute(key));
+ }
+ return ret;
+ }
+
+ public AttributeSet copyAttributes()
+ {
+ SimpleAttributeSet copy = new SimpleAttributeSet();
+ for (int i = 0; i < multi.length; i++)
+ {
+ copy.addAttributes(multi[i]);
+ }
+ return copy;
+ }
+
+ public Object getAttribute(Object key)
+ {
+ Object ret = null;
+ for (int i = 0; i < multi.length && ret == null; i++)
+ {
+ ret = multi[i].getAttribute(key);
+ }
+ return ret;
+ }
+
+ public int getAttributeCount()
+ {
+ int n = 0;
+ for (int i = 0; i < multi.length; i++)
+ {
+ n += multi[i].getAttributeCount();
+ }
+ return n;
+ }
+
+ public Enumeration getAttributeNames()
+ {
+ return new MultiNameEnumeration();
+ }
+
+ public AttributeSet getResolveParent()
+ {
+ return null;
+ }
+
+ public boolean isDefined(Object attrName)
+ {
+ boolean ret = false;
+ for (int i = 0; i < multi.length && ! ret; i++)
+ ret = multi[i].isDefined(attrName);
+ return ret;
+ }
+
+ public boolean isEqual(AttributeSet attr)
+ {
+ return getAttributeCount() == attr.getAttributeCount()
+ && containsAttributes(attr);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/text/html/MultiStyle.java b/libjava/classpath/javax/swing/text/html/MultiStyle.java
new file mode 100644
index 00000000000..3937bff75a9
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/MultiStyle.java
@@ -0,0 +1,136 @@
+/* MultiStyle.java -- Multiplexes between several Styles
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text.html;
+
+import java.util.Enumeration;
+
+import javax.swing.event.ChangeListener;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.Style;
+
+/**
+ * A Style implementation that is able to multiplex between several other
+ * Styles. This is used for CSS style resolving.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class MultiStyle
+ extends MultiAttributeSet
+ implements Style
+{
+
+ // FIXME: Fix the implementation to also return attributes that
+ // are added to this style, etc. However, this is not really needed
+ // now for CSS, but would be nice for correctness.
+
+ /**
+ * The name of the style.
+ */
+ private String name;
+
+ /**
+ * The attributes added to this style.
+ */
+ private SimpleAttributeSet attributes;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param n the name
+ * @param m the styles to multiplex
+ */
+ public MultiStyle(String n, AttributeSet[] m)
+ {
+ super(m);
+ name = n;
+ attributes = new SimpleAttributeSet();
+ }
+
+ /**
+ * Returns the name of the style.
+ *
+ * @return the name of the style
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ public void addChangeListener(ChangeListener listener)
+ {
+ // TODO: Implement.
+ }
+
+ public void removeChangeListener(ChangeListener listener)
+ {
+ // TODO: Implement.
+ }
+
+ public void addAttribute(Object name, Object value)
+ {
+ attributes.addAttribute(name, value);
+ }
+
+ public void addAttributes(AttributeSet atts)
+ {
+ attributes.addAttributes(atts);
+ }
+
+ public void removeAttribute(Object name)
+ {
+ attributes.removeAttribute(name);
+ }
+
+ public void removeAttributes(Enumeration names)
+ {
+ attributes.removeAttribute(names);
+ }
+
+ public void removeAttributes(AttributeSet atts)
+ {
+ attributes.removeAttribute(atts);
+ }
+
+ public void setResolveParent(AttributeSet parent)
+ {
+ // TODO: Implement.
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/text/html/Option.java b/libjava/classpath/javax/swing/text/html/Option.java
index 1def51b2f59..18d5c2bd86f 100644
--- a/libjava/classpath/javax/swing/text/html/Option.java
+++ b/libjava/classpath/javax/swing/text/html/Option.java
@@ -72,10 +72,10 @@ public class Option
*/
public Option(AttributeSet attr)
{
- attributes = attr;
+ // Protect the attribute set.
+ attributes = attr.copyAttributes();
label = null;
- selected = false;
- // FIXME: Probably initialize something using the attributes.
+ selected = attr.getAttribute(HTML.Attribute.SELECTED) != null;
}
/**
@@ -151,7 +151,9 @@ public class Option
*/
public String getValue()
{
- // FIXME: Return some attribute here if specified.
- return label;
+ String value = (String) attributes.getAttribute(HTML.Attribute.VALUE);
+ if (value == null)
+ value = label;
+ return value;
}
}
diff --git a/libjava/classpath/javax/swing/text/html/ParagraphView.java b/libjava/classpath/javax/swing/text/html/ParagraphView.java
index 2339f4e661d..d149627ff1c 100644
--- a/libjava/classpath/javax/swing/text/html/ParagraphView.java
+++ b/libjava/classpath/javax/swing/text/html/ParagraphView.java
@@ -38,13 +38,17 @@ exception statement from your version. */
package javax.swing.text.html;
+import gnu.javax.swing.text.html.css.Length;
+
import java.awt.Graphics;
+import java.awt.Rectangle;
import java.awt.Shape;
import javax.swing.SizeRequirements;
import javax.swing.text.AttributeSet;
import javax.swing.text.Document;
import javax.swing.text.Element;
+import javax.swing.text.StyleConstants;
import javax.swing.text.View;
/**
@@ -55,10 +59,30 @@ import javax.swing.text.View;
* @author Roman Kennke (kennke@aicas.com)
*/
public class ParagraphView
- extends javax.swing.text.ParagraphView
+ extends javax.swing.text.ParagraphView
{
/**
+ * The attributes used by this view.
+ */
+ private AttributeSet attributes;
+
+ /**
+ * The stylesheet's box painter.
+ */
+ private StyleSheet.BoxPainter painter;
+
+ /**
+ * The width as specified in the stylesheet or null if not specified.
+ */
+ private Length cssWidth;
+
+ /**
+ * The height as specified in the stylesheet or null if not specified.
+ */
+ private Length cssHeight;
+
+ /**
* Creates a new ParagraphView for the specified element.
*
* @param element the element
@@ -88,8 +112,11 @@ public class ParagraphView
*/
public AttributeSet getAttributes()
{
- // FIXME: Implement this multiplexing thing.
- return super.getAttributes();
+ if (attributes == null)
+ {
+ attributes = getStyleSheet().getViewAttributes(this);
+ }
+ return attributes;
}
/**
@@ -98,7 +125,44 @@ public class ParagraphView
*/
protected void setPropertiesFromAttributes()
{
- // FIXME: Implement this.
+ super.setPropertiesFromAttributes();
+
+ // Fetch CSS attributes.
+ attributes = getAttributes();
+ if (attributes != null)
+ {
+ super.setPropertiesFromAttributes();
+ Object o = attributes.getAttribute(CSS.Attribute.TEXT_ALIGN);
+ if (o != null)
+ {
+ String align = o.toString();
+ if (align.equals("left"))
+ setJustification(StyleConstants.ALIGN_LEFT);
+ else if (align.equals("right"))
+ setJustification(StyleConstants.ALIGN_RIGHT);
+ else if (align.equals("center"))
+ setJustification(StyleConstants.ALIGN_CENTER);
+ else if (align.equals("justify"))
+ setJustification(StyleConstants.ALIGN_JUSTIFIED);
+ }
+
+ // Fetch StyleSheet's box painter.
+ painter = getStyleSheet().getBoxPainter(attributes);
+ setInsets((short) painter.getInset(TOP, this),
+ (short) painter.getInset(LEFT, this),
+ (short) painter.getInset(BOTTOM, this),
+ (short) painter.getInset(RIGHT, this));
+
+ StyleSheet ss = getStyleSheet();
+ float emBase = ss.getEMBase(attributes);
+ float exBase = ss.getEXBase(attributes);
+ cssWidth = (Length) attributes.getAttribute(CSS.Attribute.WIDTH);
+ if (cssWidth != null)
+ cssWidth.setFontBases(emBase, exBase);
+ cssHeight = (Length) attributes.getAttribute(CSS.Attribute.WIDTH);
+ if (cssHeight != null)
+ cssHeight.setFontBases(emBase, exBase);
+ }
}
/**
@@ -129,8 +193,52 @@ public class ParagraphView
protected SizeRequirements calculateMinorAxisRequirements(int axis,
SizeRequirements r)
{
- // FIXME: Implement the above specified behaviour.
- return super.calculateMinorAxisRequirements(axis, r);
+ r = super.calculateMinorAxisRequirements(axis, r);
+ if (! setCSSSpan(r, axis))
+ {
+ int margin = axis == X_AXIS ? getLeftInset() + getRightInset()
+ : getTopInset() + getBottomInset();
+ r.minimum -= margin;
+ r.preferred -= margin;
+ r.maximum -= margin;
+ }
+ return r;
+ }
+
+ /**
+ * Sets the span on the SizeRequirements object according to the
+ * according CSS span value, when it is set.
+ *
+ * @param r the size requirements
+ * @param axis the axis
+ *
+ * @return <code>true</code> when the CSS span has been set,
+ * <code>false</code> otherwise
+ */
+ private boolean setCSSSpan(SizeRequirements r, int axis)
+ {
+ boolean ret = false;
+ if (axis == X_AXIS)
+ {
+ if (cssWidth != null && ! cssWidth.isPercentage())
+ {
+ r.minimum = (int) cssWidth.getValue();
+ r.preferred = (int) cssWidth.getValue();
+ r.maximum = (int) cssWidth.getValue();
+ ret = true;
+ }
+ }
+ else
+ {
+ if (cssHeight != null && ! cssWidth.isPercentage())
+ {
+ r.minimum = (int) cssHeight.getValue();
+ r.preferred = (int) cssHeight.getValue();
+ r.maximum = (int) cssHeight.getValue();
+ ret = true;
+ }
+ }
+ return ret;
}
/**
@@ -147,15 +255,20 @@ public class ParagraphView
}
/**
- * Paints this view. This delegates to the superclass after the coordinates
- * have been updated for tab calculations.
+ * Paints this view. This paints the box using the stylesheet's
+ * box painter for this view and delegates to the super class paint()
+ * afterwards.
*
* @param g the graphics object
* @param a the current allocation of this view
*/
public void paint(Graphics g, Shape a)
{
- // FIXME: Implement the above specified behaviour.
+ if (a != null)
+ {
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ painter.paint(g, r.x, r.y, r.width, r.height, this);
+ }
super.paint(g, a);
}
diff --git a/libjava/classpath/javax/swing/text/html/ResetableModel.java b/libjava/classpath/javax/swing/text/html/ResetableModel.java
new file mode 100644
index 00000000000..17f65b97d13
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/ResetableModel.java
@@ -0,0 +1,50 @@
+/* ResetableModel.java -- Form models that can be resetted
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text.html;
+
+/**
+ * Form models that can be resetted implement this.
+ */
+interface ResetableModel
+{
+ /**
+ * Resets the model.
+ */
+ void reset();
+}
diff --git a/libjava/classpath/javax/swing/text/html/ResetablePlainDocument.java b/libjava/classpath/javax/swing/text/html/ResetablePlainDocument.java
new file mode 100644
index 00000000000..6177f9b86cf
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/ResetablePlainDocument.java
@@ -0,0 +1,82 @@
+/* ResetablePlainDocument.java -- A plain document for use in the HTML renderer
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text.html;
+
+import javax.swing.text.BadLocationException;
+import javax.swing.text.PlainDocument;
+
+/**
+ * A PlainDocument that can be resetted.
+ */
+class ResetablePlainDocument
+ extends PlainDocument
+ implements ResetableModel
+{
+ /**
+ * The initial text.
+ */
+ private String initial;
+
+ /**
+ * Stores the initial text.
+ *
+ * @param text the initial text
+ */
+ void setInitialText(String text)
+ {
+ initial = text;
+ }
+
+ /**
+ * Resets the model.
+ */
+ public void reset()
+ {
+ try
+ {
+ replace(0, getLength(), initial, null);
+ }
+ catch (BadLocationException ex)
+ {
+ // Shouldn't happen.
+ assert false;
+ }
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/text/html/ResetableToggleButtonModel.java b/libjava/classpath/javax/swing/text/html/ResetableToggleButtonModel.java
new file mode 100644
index 00000000000..619c24e477c
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/ResetableToggleButtonModel.java
@@ -0,0 +1,71 @@
+/* ResetableToggleButtonModel.java -- A toggle button model with reset support
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text.html;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JToggleButton.ToggleButtonModel;
+
+class ResetableToggleButtonModel
+ extends ToggleButtonModel
+ implements ResetableModel
+{
+
+ /**
+ * The initial state.
+ */
+ private boolean initial;
+
+ /**
+ * Sets the initial selection value.
+ *
+ * @param state the initial value
+ */
+ public void setInitial(boolean state)
+ {
+ initial = state;
+ }
+
+ /**
+ * Resets the model.
+ */
+ public void reset()
+ {
+ setSelected(initial);
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/SelectComboBoxModel.java b/libjava/classpath/javax/swing/text/html/SelectComboBoxModel.java
new file mode 100644
index 00000000000..999746413c8
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/SelectComboBoxModel.java
@@ -0,0 +1,84 @@
+/* SelectComboBoxModel.java -- A special ComboBoxModel for use in HTML renderer
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text.html;
+
+import javax.swing.DefaultComboBoxModel;
+
+/**
+ * A special ComboBoxModel that supports storing the initial value so that
+ * the combobox can be resetted later.
+ */
+class SelectComboBoxModel
+ extends DefaultComboBoxModel
+ implements ResetableModel
+{
+
+ /**
+ * The initial selection.
+ */
+ private Option initial;
+
+ /**
+ * Sets the initial selection.
+ *
+ * @param option the initial selection
+ */
+ void setInitialSelection(Option option)
+ {
+ initial = option;
+ }
+
+ /**
+ * Returns the initial selection.
+ *
+ * @return the initial selection
+ */
+ Option getInitialSelection()
+ {
+ return initial;
+ }
+
+ /**
+ * Resets the model.
+ */
+ public void reset()
+ {
+ setSelectedItem(initial);
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/SelectListModel.java b/libjava/classpath/javax/swing/text/html/SelectListModel.java
new file mode 100644
index 00000000000..23bfaa11b86
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/SelectListModel.java
@@ -0,0 +1,106 @@
+/* OptionListModel.java -- A special ListModel for use in the HTML renderer
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text.html;
+
+import java.util.BitSet;
+
+import javax.swing.DefaultListModel;
+import javax.swing.DefaultListSelectionModel;
+import javax.swing.ListSelectionModel;
+
+/**
+ * A special list model that encapsulates its selection model and supports
+ * storing of the initial value so that it can be resetted.
+ */
+class SelectListModel
+ extends DefaultListModel
+ implements ResetableModel
+{
+ /**
+ * The selection model.
+ */
+ private DefaultListSelectionModel selectionModel;
+
+ /**
+ * The initial selection.
+ */
+ private BitSet initialSelection;
+
+ /**
+ * Creates a new SelectListModel.
+ */
+ SelectListModel()
+ {
+ selectionModel = new DefaultListSelectionModel();
+ initialSelection = new BitSet();
+ }
+
+ /**
+ * Sets the initial selection.
+ *
+ * @param init the initial selection
+ */
+ void addInitialSelection(int init)
+ {
+ initialSelection.set(init);
+ }
+
+ /**
+ * Resets the model.
+ */
+ public void reset()
+ {
+ selectionModel.clearSelection();
+ for (int i = initialSelection.size(); i >= 0; i--)
+ {
+ if (initialSelection.get(i))
+ selectionModel.addSelectionInterval(i, i);
+ }
+ }
+
+ /**
+ * Returns the associated selection model.
+ *
+ * @return the associated selection model
+ */
+ ListSelectionModel getSelectionModel()
+ {
+ return selectionModel;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/StyleSheet.java b/libjava/classpath/javax/swing/text/html/StyleSheet.java
index d92abde7825..01f19fd7bdd 100644
--- a/libjava/classpath/javax/swing/text/html/StyleSheet.java
+++ b/libjava/classpath/javax/swing/text/html/StyleSheet.java
@@ -38,28 +38,47 @@ exception statement from your version. */
package javax.swing.text.html;
-import gnu.javax.swing.text.html.CharacterAttributeTranslator;
+import gnu.javax.swing.text.html.css.BorderWidth;
+import gnu.javax.swing.text.html.css.CSSColor;
+import gnu.javax.swing.text.html.css.CSSParser;
+import gnu.javax.swing.text.html.css.CSSParserCallback;
+import gnu.javax.swing.text.html.css.FontSize;
+import gnu.javax.swing.text.html.css.FontStyle;
+import gnu.javax.swing.text.html.css.FontWeight;
+import gnu.javax.swing.text.html.css.Length;
+import gnu.javax.swing.text.html.css.Selector;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
-
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.font.FontRenderContext;
+import java.awt.geom.Rectangle2D;
+import java.io.BufferedReader;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Serializable;
import java.io.StringReader;
-
-import java.net.MalformedURLException;
import java.net.URL;
-
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Enumeration;
-import java.util.Vector;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import javax.swing.border.Border;
+import javax.swing.event.ChangeListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.Element;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.Style;
+import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.View;
@@ -79,21 +98,168 @@ import javax.swing.text.View;
*
* The rules are stored as named styles, and other information is stored to
* translate the context of an element to a rule.
- *
+ *
* @author Lillian Angel (langel@redhat.com)
*/
public class StyleSheet extends StyleContext
{
+ /**
+ * Parses CSS stylesheets using the parser in gnu/javax/swing/html/css.
+ *
+ * This is package private to avoid accessor methods.
+ */
+ class CSSStyleSheetParserCallback
+ implements CSSParserCallback
+ {
+ /**
+ * The current styles.
+ */
+ private CSSStyle[] styles;
+
+ /**
+ * The precedence of the stylesheet to be parsed.
+ */
+ private int precedence;
+
+ /**
+ * Creates a new CSS parser. This parser parses a CSS stylesheet with
+ * the specified precedence.
+ *
+ * @param prec the precedence, according to the constants defined in
+ * CSSStyle
+ */
+ CSSStyleSheetParserCallback(int prec)
+ {
+ precedence = prec;
+ }
+
+ /**
+ * Called at the beginning of a statement.
+ *
+ * @param sel the selector
+ */
+ public void startStatement(Selector[] sel)
+ {
+ styles = new CSSStyle[sel.length];
+ for (int i = 0; i < sel.length; i++)
+ styles[i] = new CSSStyle(precedence, sel[i]);
+ }
+
+ /**
+ * Called at the end of a statement.
+ */
+ public void endStatement()
+ {
+ for (int i = 0; i < styles.length; i++)
+ css.add(styles[i]);
+ styles = null;
+ }
+
+ /**
+ * Called when a declaration is parsed.
+ *
+ * @param property the property
+ * @param value the value
+ */
+ public void declaration(String property, String value)
+ {
+ CSS.Attribute cssAtt = CSS.getAttribute(property);
+ Object val = CSS.getValue(cssAtt, value);
+ for (int i = 0; i < styles.length; i++)
+ {
+ CSSStyle style = styles[i];
+ CSS.addInternal(style, cssAtt, value);
+ if (cssAtt != null)
+ style.addAttribute(cssAtt, val);
+ }
+ }
+
+ }
+
+ /**
+ * Represents a style that is defined by a CSS rule.
+ */
+ private class CSSStyle
+ extends SimpleAttributeSet
+ implements Style, Comparable
+ {
+
+ static final int PREC_UA = 0;
+ static final int PREC_NORM = 100000;
+ static final int PREC_AUTHOR_NORMAL = 200000;
+ static final int PREC_AUTHOR_IMPORTANT = 300000;
+ static final int PREC_USER_IMPORTANT = 400000;
+
+ /**
+ * The priority of this style when matching CSS selectors.
+ */
+ private int precedence;
+
+ /**
+ * The selector for this rule.
+ *
+ * This is package private to avoid accessor methods.
+ */
+ Selector selector;
+
+ CSSStyle(int prec, Selector sel)
+ {
+ precedence = prec;
+ selector = sel;
+ }
+
+ public String getName()
+ {
+ // TODO: Implement this for correctness.
+ return null;
+ }
+
+ public void addChangeListener(ChangeListener listener)
+ {
+ // TODO: Implement this for correctness.
+ }
+
+ public void removeChangeListener(ChangeListener listener)
+ {
+ // TODO: Implement this for correctness.
+ }
+
+ /**
+ * Sorts the rule according to the style's precedence and the
+ * selectors specificity.
+ */
+ public int compareTo(Object o)
+ {
+ CSSStyle other = (CSSStyle) o;
+ return other.precedence + other.selector.getSpecificity()
+ - precedence - selector.getSpecificity();
+ }
+
+ }
+
/** The base URL */
URL base;
/** Base font size (int) */
int baseFontSize;
- /** The style sheets stored. */
- StyleSheet[] styleSheet;
-
+ /**
+ * The linked style sheets stored.
+ */
+ private ArrayList linked;
+
+ /**
+ * Maps element names (selectors) to AttributSet (the corresponding style
+ * information).
+ */
+ ArrayList css = new ArrayList();
+
+ /**
+ * Maps selectors to their resolved styles.
+ */
+ private HashMap resolvedStyles;
+
/**
* Constructs a StyleSheet.
*/
@@ -101,6 +267,7 @@ public class StyleSheet extends StyleContext
{
super();
baseFontSize = 4; // Default font size from CSS
+ resolvedStyles = new HashMap();
}
/**
@@ -114,10 +281,198 @@ public class StyleSheet extends StyleContext
*/
public Style getRule(HTML.Tag t, Element e)
{
- // FIXME: Not implemented.
- return null;
+ // Create list of the element and all of its parents, starting
+ // with the bottommost element.
+ ArrayList path = new ArrayList();
+ Element el;
+ AttributeSet atts;
+ for (el = e; el != null; el = el.getParentElement())
+ path.add(el);
+
+ // Create fully qualified selector.
+ StringBuilder selector = new StringBuilder();
+ int count = path.size();
+ // We append the actual element after this loop.
+ for (int i = count - 1; i > 0; i--)
+ {
+ el = (Element) path.get(i);
+ atts = el.getAttributes();
+ Object name = atts.getAttribute(StyleConstants.NameAttribute);
+ selector.append(name.toString());
+ if (atts.isDefined(HTML.Attribute.ID))
+ {
+ selector.append('#');
+ selector.append(atts.getAttribute(HTML.Attribute.ID));
+ }
+ if (atts.isDefined(HTML.Attribute.CLASS))
+ {
+ selector.append('.');
+ selector.append(atts.getAttribute(HTML.Attribute.CLASS));
+ }
+ if (atts.isDefined(HTML.Attribute.DYNAMIC_CLASS))
+ {
+ selector.append(':');
+ selector.append(atts.getAttribute(HTML.Attribute.DYNAMIC_CLASS));
+ }
+ if (atts.isDefined(HTML.Attribute.PSEUDO_CLASS))
+ {
+ selector.append(':');
+ selector.append(atts.getAttribute(HTML.Attribute.PSEUDO_CLASS));
+ }
+ selector.append(' ');
+ }
+ selector.append(t.toString());
+ el = (Element) path.get(0);
+ atts = el.getAttributes();
+ // For leaf elements, we have to fetch the tag specific attributes.
+ if (el.isLeaf())
+ {
+ Object o = atts.getAttribute(t);
+ if (o instanceof AttributeSet)
+ atts = (AttributeSet) o;
+ else
+ atts = null;
+ }
+ if (atts != null)
+ {
+ if (atts.isDefined(HTML.Attribute.ID))
+ {
+ selector.append('#');
+ selector.append(atts.getAttribute(HTML.Attribute.ID));
+ }
+ if (atts.isDefined(HTML.Attribute.CLASS))
+ {
+ selector.append('.');
+ selector.append(atts.getAttribute(HTML.Attribute.CLASS));
+ }
+ if (atts.isDefined(HTML.Attribute.DYNAMIC_CLASS))
+ {
+ selector.append(':');
+ selector.append(atts.getAttribute(HTML.Attribute.DYNAMIC_CLASS));
+ }
+ if (atts.isDefined(HTML.Attribute.PSEUDO_CLASS))
+ {
+ selector.append(':');
+ selector.append(atts.getAttribute(HTML.Attribute.PSEUDO_CLASS));
+ }
+ }
+ return getResolvedStyle(selector.toString(), path, t);
}
-
+
+ /**
+ * Fetches a resolved style. If there is no resolved style for the
+ * specified selector, the resolve the style using
+ * {@link #resolveStyle(String, List, HTML.Tag)}.
+ *
+ * @param selector the selector for which to resolve the style
+ * @param path the Element path, used in the resolving algorithm
+ * @param tag the tag for which to resolve
+ *
+ * @return the resolved style
+ */
+ private Style getResolvedStyle(String selector, List path, HTML.Tag tag)
+ {
+ Style style = (Style) resolvedStyles.get(selector);
+ if (style == null)
+ style = resolveStyle(selector, path, tag);
+ return style;
+ }
+
+ /**
+ * Resolves a style. This creates arrays that hold the tag names,
+ * class and id attributes and delegates the work to
+ * {@link #resolveStyle(String, String[], Map[])}.
+ *
+ * @param selector the selector
+ * @param path the Element path
+ * @param tag the tag
+ *
+ * @return the resolved style
+ */
+ private Style resolveStyle(String selector, List path, HTML.Tag tag)
+ {
+ int count = path.size();
+ String[] tags = new String[count];
+ Map[] attributes = new Map[count];
+ for (int i = 0; i < count; i++)
+ {
+ Element el = (Element) path.get(i);
+ AttributeSet atts = el.getAttributes();
+ if (i == 0 && el.isLeaf())
+ {
+ Object o = atts.getAttribute(tag);
+ if (o instanceof AttributeSet)
+ atts = (AttributeSet) o;
+ else
+ atts = null;
+ }
+ if (atts != null)
+ {
+ HTML.Tag t =
+ (HTML.Tag) atts.getAttribute(StyleConstants.NameAttribute);
+ if (t != null)
+ tags[i] = t.toString();
+ else
+ tags[i] = null;
+ attributes[i] = attributeSetToMap(atts);
+ }
+ else
+ {
+ tags[i] = null;
+ attributes[i] = null;
+ }
+ }
+ tags[0] = tag.toString();
+ return resolveStyle(selector, tags, attributes);
+ }
+
+ /**
+ * Performs style resolving.
+ *
+ * @param selector the selector
+ * @param tags the tags
+ * @param attributes the attributes of the tags
+ *
+ * @return the resolved style
+ */
+ private Style resolveStyle(String selector, String[] tags, Map[] attributes)
+ {
+ // FIXME: This style resolver is not correct. But it works good enough for
+ // the default.css.
+ int count = tags.length;
+ ArrayList styles = new ArrayList();
+ for (Iterator i = css.iterator(); i.hasNext();)
+ {
+ CSSStyle style = (CSSStyle) i.next();
+ if (style.selector.matches(tags, attributes))
+ styles.add(style);
+ }
+
+ // Add styles from linked stylesheets.
+ if (linked != null)
+ {
+ for (int i = linked.size() - 1; i >= 0; i--)
+ {
+ StyleSheet ss = (StyleSheet) linked.get(i);
+ for (int j = ss.css.size() - 1; j >= 0; j--)
+ {
+ CSSStyle style = (CSSStyle) ss.css.get(j);
+ if (style.selector.matches(tags, attributes))
+ styles.add(style);
+ }
+ }
+ }
+
+ // Sort selectors.
+ Collections.sort(styles);
+ Style[] styleArray = new Style[styles.size()];
+ styleArray = (Style[]) styles.toArray(styleArray);
+ Style resolved = new MultiStyle(selector,
+ (Style[]) styles.toArray(styleArray));
+ resolvedStyles.put(selector, resolved);
+ return resolved;
+ }
+
/**
* Gets the rule that best matches the selector. selector is a space
* separated String of element names. The attributes of the returned
@@ -128,27 +483,40 @@ public class StyleSheet extends StyleContext
*/
public Style getRule(String selector)
{
- // FIXME: Not implemented.
- return null;
+ CSSStyle best = null;
+ for (Iterator i = css.iterator(); i.hasNext();)
+ {
+ CSSStyle style = (CSSStyle) i.next();
+ if (style.compareTo(best) < 0)
+ best = style;
+ }
+ return best;
}
/**
- * Adds a set if rules to the sheet. The rules are expected to be in valid
+ * Adds a set of rules to the sheet. The rules are expected to be in valid
* CSS format. This is called as a result of parsing a <style> tag
*
* @param rule - the rule to add to the sheet
*/
public void addRule(String rule)
{
- CssParser cp = new CssParser();
+ CSSStyleSheetParserCallback cb =
+ new CSSStyleSheetParserCallback(CSSStyle.PREC_AUTHOR_NORMAL);
+ // FIXME: Handle ref.
+ StringReader in = new StringReader(rule);
+ CSSParser parser = new CSSParser(in, cb);
try
- {
- cp.parse(base, new StringReader(rule), false, false);
- }
- catch (IOException io)
- {
- // Do nothing here.
- }
+ {
+ parser.parse();
+ }
+ catch (IOException ex)
+ {
+ // Shouldn't happen. And if, then don't let it bork the outside code.
+ }
+ // Clean up resolved styles cache so that the new styles are recognized
+ // on next stylesheet request.
+ resolvedStyles.clear();
}
/**
@@ -176,10 +544,14 @@ public class StyleSheet extends StyleContext
* parameter.
* @throws IOException - For any IO error while reading
*/
- public void loadRules(Reader in, URL ref) throws IOException
+ public void loadRules(Reader in, URL ref)
+ throws IOException
{
- CssParser cp = new CssParser();
- cp.parse(ref, in, false, false);
+ CSSStyleSheetParserCallback cb =
+ new CSSStyleSheetParserCallback(CSSStyle.PREC_UA);
+ // FIXME: Handle ref.
+ CSSParser parser = new CSSParser(in, cb);
+ parser.parse();
}
/**
@@ -191,8 +563,7 @@ public class StyleSheet extends StyleContext
*/
public AttributeSet getViewAttributes(View v)
{
- // FIXME: Not implemented.
- return null;
+ return new ViewAttributeSet(v, this);
}
/**
@@ -215,11 +586,9 @@ public class StyleSheet extends StyleContext
*/
public void addStyleSheet(StyleSheet ss)
{
- if (styleSheet == null)
- styleSheet = new StyleSheet[] {ss};
- else
- System.arraycopy(new StyleSheet[] {ss}, 0, styleSheet,
- styleSheet.length, 1);
+ if (linked == null)
+ linked = new ArrayList();
+ linked.add(ss);
}
/**
@@ -229,31 +598,9 @@ public class StyleSheet extends StyleContext
*/
public void removeStyleSheet(StyleSheet ss)
{
- if (styleSheet.length == 1 && styleSheet[0].equals(ss))
- styleSheet = null;
- else
+ if (linked != null)
{
- for (int i = 0; i < styleSheet.length; i++)
- {
- StyleSheet curr = styleSheet[i];
- if (curr.equals(ss))
- {
- StyleSheet[] tmp = new StyleSheet[styleSheet.length - 1];
- if (i != 0 && i != (styleSheet.length - 1))
- {
- System.arraycopy(styleSheet, 0, tmp, 0, i);
- System.arraycopy(styleSheet, i + 1, tmp, i,
- styleSheet.length - i - 1);
- }
- else if (i == 0)
- System.arraycopy(styleSheet, 1, tmp, 0, styleSheet.length - 1);
- else
- System.arraycopy(styleSheet, 0, tmp, 0, styleSheet.length - 1);
-
- styleSheet = tmp;
- break;
- }
- }
+ linked.remove(ss);
}
}
@@ -264,18 +611,41 @@ public class StyleSheet extends StyleContext
*/
public StyleSheet[] getStyleSheets()
{
- return styleSheet;
+ StyleSheet[] linkedSS;
+ if (linked != null)
+ {
+ linkedSS = new StyleSheet[linked.size()];
+ linkedSS = (StyleSheet[]) linked.toArray(linkedSS);
+ }
+ else
+ {
+ linkedSS = null;
+ }
+ return linkedSS;
}
/**
* Imports a style sheet from the url. The rules are directly added to the
- * receiver.
+ * receiver. This is usually called when a <link> tag is resolved in an
+ * HTML document.
*
- * @param url - the URL to import the StyleSheet from.
+ * @param url the URL to import the StyleSheet from
*/
public void importStyleSheet(URL url)
{
- // FIXME: Not implemented
+ try
+ {
+ InputStream in = url.openStream();
+ Reader r = new BufferedReader(new InputStreamReader(in));
+ CSSStyleSheetParserCallback cb =
+ new CSSStyleSheetParserCallback(CSSStyle.PREC_AUTHOR_NORMAL);
+ CSSParser parser = new CSSParser(r, cb);
+ parser.parse();
+ }
+ catch (IOException ex)
+ {
+ // We can't do anything about it I guess.
+ }
}
/**
@@ -310,7 +680,9 @@ public class StyleSheet extends StyleContext
public void addCSSAttribute(MutableAttributeSet attr, CSS.Attribute key,
String value)
{
- attr.addAttribute(key, value);
+ Object val = CSS.getValue(key, value);
+ CSS.addInternal(attr, key, value);
+ attr.addAttribute(key, val);
}
/**
@@ -340,8 +712,90 @@ public class StyleSheet extends StyleContext
*/
public AttributeSet translateHTMLToCSS(AttributeSet htmlAttrSet)
{
- // FIXME: Not implemented.
- return null;
+ AttributeSet cssAttr = htmlAttrSet.copyAttributes();
+
+ // The HTML align attribute maps directly to the CSS text-align attribute.
+ Object o = htmlAttrSet.getAttribute(HTML.Attribute.ALIGN);
+ if (o != null)
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.TEXT_ALIGN, o);
+
+ // The HTML width attribute maps directly to CSS width.
+ o = htmlAttrSet.getAttribute(HTML.Attribute.WIDTH);
+ if (o != null)
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.WIDTH,
+ new Length(o.toString()));
+
+ // The HTML height attribute maps directly to CSS height.
+ o = htmlAttrSet.getAttribute(HTML.Attribute.HEIGHT);
+ if (o != null)
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.HEIGHT,
+ new Length(o.toString()));
+
+ o = htmlAttrSet.getAttribute(HTML.Attribute.NOWRAP);
+ if (o != null)
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.WHITE_SPACE, "nowrap");
+
+ // Map cellspacing attr of tables to CSS border-spacing.
+ o = htmlAttrSet.getAttribute(HTML.Attribute.CELLSPACING);
+ if (o != null)
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.BORDER_SPACING,
+ new Length(o.toString()));
+
+ // For table cells and headers, fetch the cellpadding value from the
+ // parent table and set it as CSS padding attribute.
+ HTML.Tag tag = (HTML.Tag)
+ htmlAttrSet.getAttribute(StyleConstants.NameAttribute);
+ if ((tag == HTML.Tag.TD || tag == HTML.Tag.TH)
+ && htmlAttrSet instanceof Element)
+ {
+ Element el = (Element) htmlAttrSet;
+ AttributeSet tableAttrs = el.getParentElement().getParentElement()
+ .getAttributes();
+ o = tableAttrs.getAttribute(HTML.Attribute.CELLPADDING);
+ if (o != null)
+ {
+ Length l = new Length(o.toString());
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_BOTTOM, l);
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_LEFT, l);
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_RIGHT, l);
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_TOP, l);
+ }
+ o = tableAttrs.getAttribute(HTML.Attribute.BORDER);
+ cssAttr = translateBorder(cssAttr, o);
+ }
+
+ // Translate border attribute.
+ o = cssAttr.getAttribute(HTML.Attribute.BORDER);
+ cssAttr = translateBorder(cssAttr, o);
+
+ // TODO: Add more mappings.
+ return cssAttr;
+ }
+
+ /**
+ * Translates a HTML border attribute to a corresponding set of CSS
+ * attributes.
+ *
+ * @param cssAttr the original set of CSS attributes to add to
+ * @param o the value of the border attribute
+ *
+ * @return the new set of CSS attributes
+ */
+ private AttributeSet translateBorder(AttributeSet cssAttr, Object o)
+ {
+ if (o != null)
+ {
+ BorderWidth l = new BorderWidth(o.toString());
+ if (l.getValue() > 0)
+ {
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.BORDER_WIDTH, l);
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.BORDER_STYLE,
+ "solid");
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.BORDER_COLOR,
+ new CSSColor("black"));
+ }
+ }
+ return cssAttr;
}
/**
@@ -416,10 +870,10 @@ public class StyleSheet extends StyleContext
* @param names - the attribute names
* @return the update attribute set
*/
- public AttributeSet removeAttributes(AttributeSet old, Enumeration names)
+ public AttributeSet removeAttributes(AttributeSet old, Enumeration<?> names)
{
// FIXME: Not implemented.
- return super.removeAttributes(old, names);
+ return super.removeAttributes(old, names);
}
/**
@@ -455,9 +909,95 @@ public class StyleSheet extends StyleContext
*/
public Font getFont(AttributeSet a)
{
- return super.getFont(a);
+ int realSize = getFontSize(a);
+
+ // Decrement size for subscript and superscript.
+ Object valign = a.getAttribute(CSS.Attribute.VERTICAL_ALIGN);
+ if (valign != null)
+ {
+ String v = valign.toString();
+ if (v.contains("sup") || v.contains("sub"))
+ realSize -= 2;
+ }
+
+ // TODO: Convert font family.
+ String family = "SansSerif";
+
+ int style = Font.PLAIN;
+ FontWeight weight = (FontWeight) a.getAttribute(CSS.Attribute.FONT_WEIGHT);
+ if (weight != null)
+ style |= weight.getValue();
+ FontStyle fStyle = (FontStyle) a.getAttribute(CSS.Attribute.FONT_STYLE);
+ if (fStyle != null)
+ style |= fStyle.getValue();
+ return new Font(family, style, realSize);
}
-
+
+ /**
+ * Determines the EM base value based on the specified attributes.
+ *
+ * @param atts the attibutes
+ *
+ * @return the EM base value
+ */
+ float getEMBase(AttributeSet atts)
+ {
+ Font font = getFont(atts);
+ FontRenderContext ctx = new FontRenderContext(null, false, false);
+ Rectangle2D bounds = font.getStringBounds("M", ctx);
+ return (float) bounds.getWidth();
+ }
+
+ /**
+ * Determines the EX base value based on the specified attributes.
+ *
+ * @param atts the attibutes
+ *
+ * @return the EX base value
+ */
+ float getEXBase(AttributeSet atts)
+ {
+ Font font = getFont(atts);
+ FontRenderContext ctx = new FontRenderContext(null, false, false);
+ Rectangle2D bounds = font.getStringBounds("x", ctx);
+ return (float) bounds.getHeight();
+ }
+
+ /**
+ * Resolves the fontsize for a given set of attributes.
+ *
+ * @param atts the attributes
+ *
+ * @return the resolved font size
+ */
+ private int getFontSize(AttributeSet atts)
+ {
+ int size = 12;
+ if (atts.isDefined(CSS.Attribute.FONT_SIZE))
+ {
+ FontSize fs = (FontSize) atts.getAttribute(CSS.Attribute.FONT_SIZE);
+ if (fs.isRelative())
+ {
+ int parSize = 12;
+ AttributeSet resolver = atts.getResolveParent();
+ if (resolver != null)
+ parSize = getFontSize(resolver);
+ size = fs.getValue(parSize);
+ }
+ else
+ {
+ size = fs.getValue();
+ }
+ }
+ else
+ {
+ AttributeSet resolver = atts.getResolveParent();
+ if (resolver != null)
+ size = getFontSize(resolver);
+ }
+ return size;
+ }
+
/**
* Takes a set of attributes and turns it into a foreground
* color specification. This is used to specify things like, brigher, more hue
@@ -468,7 +1008,11 @@ public class StyleSheet extends StyleContext
*/
public Color getForeground(AttributeSet a)
{
- return super.getForeground(a);
+ CSSColor c = (CSSColor) a.getAttribute(CSS.Attribute.COLOR);
+ Color color = null;
+ if (c != null)
+ color = c.getValue();
+ return color;
}
/**
@@ -481,7 +1025,11 @@ public class StyleSheet extends StyleContext
*/
public Color getBackground(AttributeSet a)
{
- return super.getBackground(a);
+ CSSColor c = (CSSColor) a.getAttribute(CSS.Attribute.BACKGROUND_COLOR);
+ Color color = null;
+ if (c != null)
+ color = c.getValue();
+ return color;
}
/**
@@ -492,7 +1040,7 @@ public class StyleSheet extends StyleContext
*/
public BoxPainter getBoxPainter(AttributeSet a)
{
- return new BoxPainter(a);
+ return new BoxPainter(a, this);
}
/**
@@ -503,7 +1051,7 @@ public class StyleSheet extends StyleContext
*/
public ListPainter getListPainter(AttributeSet a)
{
- return new ListPainter(a);
+ return new ListPainter(a, this);
}
/**
@@ -595,7 +1143,7 @@ public class StyleSheet extends StyleContext
*/
public Color stringToColor(String colorName)
{
- return CharacterAttributeTranslator.getColor(colorName);
+ return CSSColor.convertValue(colorName);
}
/**
@@ -609,22 +1157,112 @@ public class StyleSheet extends StyleContext
*/
public static class BoxPainter extends Object implements Serializable
{
-
+
/**
- * Attribute set for painter
+ * The left inset.
*/
- AttributeSet as;
-
+ private float leftInset;
+
+ /**
+ * The right inset.
+ */
+ private float rightInset;
+
+ /**
+ * The top inset.
+ */
+ private float topInset;
+
+ /**
+ * The bottom inset.
+ */
+ private float bottomInset;
+
+ /**
+ * The border of the box.
+ */
+ private Border border;
+
+ private float leftPadding;
+ private float rightPadding;
+ private float topPadding;
+ private float bottomPadding;
+
+ /**
+ * The background color.
+ */
+ private Color background;
+
/**
* Package-private constructor.
*
* @param as - AttributeSet for painter
*/
- BoxPainter(AttributeSet as)
+ BoxPainter(AttributeSet as, StyleSheet ss)
{
- this.as = as;
+ float emBase = ss.getEMBase(as);
+ float exBase = ss.getEXBase(as);
+ // Fetch margins.
+ Length l = (Length) as.getAttribute(CSS.Attribute.MARGIN_LEFT);
+ if (l != null)
+ {
+ l.setFontBases(emBase, exBase);
+ leftInset = l.getValue();
+ }
+ l = (Length) as.getAttribute(CSS.Attribute.MARGIN_RIGHT);
+ if (l != null)
+ {
+ l.setFontBases(emBase, exBase);
+ rightInset = l.getValue();
+ }
+ l = (Length) as.getAttribute(CSS.Attribute.MARGIN_TOP);
+ if (l != null)
+ {
+ l.setFontBases(emBase, exBase);
+ topInset = l.getValue();
+ }
+ l = (Length) as.getAttribute(CSS.Attribute.MARGIN_BOTTOM);
+ if (l != null)
+ {
+ l.setFontBases(emBase, exBase);
+ bottomInset = l.getValue();
+ }
+
+ // Fetch padding.
+ l = (Length) as.getAttribute(CSS.Attribute.PADDING_LEFT);
+ if (l != null)
+ {
+ l.setFontBases(emBase, exBase);
+ leftPadding = l.getValue();
+ }
+ l = (Length) as.getAttribute(CSS.Attribute.PADDING_RIGHT);
+ if (l != null)
+ {
+ l.setFontBases(emBase, exBase);
+ rightPadding = l.getValue();
+ }
+ l = (Length) as.getAttribute(CSS.Attribute.PADDING_TOP);
+ if (l != null)
+ {
+ l.setFontBases(emBase, exBase);
+ topPadding = l.getValue();
+ }
+ l = (Length) as.getAttribute(CSS.Attribute.PADDING_BOTTOM);
+ if (l != null)
+ {
+ l.setFontBases(emBase, exBase);
+ bottomPadding = l.getValue();
+ }
+
+ // Determine border.
+ border = new CSSBorder(as, ss);
+
+ // Determine background.
+ background = ss.getBackground(as);
+
}
+
/**
* Gets the inset needed on a given side to account for the margin, border
* and padding.
@@ -638,8 +1276,37 @@ public class StyleSheet extends StyleContext
*/
public float getInset(int size, View v)
{
- // FIXME: Not implemented.
- return 0;
+ float inset;
+ switch (size)
+ {
+ case View.TOP:
+ inset = topInset;
+ if (border != null)
+ inset += border.getBorderInsets(null).top;
+ inset += topPadding;
+ break;
+ case View.BOTTOM:
+ inset = bottomInset;
+ if (border != null)
+ inset += border.getBorderInsets(null).bottom;
+ inset += bottomPadding;
+ break;
+ case View.LEFT:
+ inset = leftInset;
+ if (border != null)
+ inset += border.getBorderInsets(null).left;
+ inset += leftPadding;
+ break;
+ case View.RIGHT:
+ inset = rightInset;
+ if (border != null)
+ inset += border.getBorderInsets(null).right;
+ inset += rightPadding;
+ break;
+ default:
+ inset = 0.0F;
+ }
+ return inset;
}
/**
@@ -655,7 +1322,19 @@ public class StyleSheet extends StyleContext
*/
public void paint(Graphics g, float x, float y, float w, float h, View v)
{
- // FIXME: Not implemented.
+ int inX = (int) (x + leftInset);
+ int inY = (int) (y + topInset);
+ int inW = (int) (w - leftInset - rightInset);
+ int inH = (int) (h - topInset - bottomInset);
+ if (background != null)
+ {
+ g.setColor(background);
+ g.fillRect(inX, inY, inW, inH);
+ }
+ if (border != null)
+ {
+ border.paintBorder(null, g, inX, inY, inW, inH);
+ }
}
}
@@ -666,24 +1345,41 @@ public class StyleSheet extends StyleContext
*
* @author Lillian Angel (langel@redhat.com)
*/
- public static class ListPainter extends Object implements Serializable
+ public static class ListPainter implements Serializable
{
-
+
/**
* Attribute set for painter
*/
- AttributeSet as;
-
+ private AttributeSet attributes;
+
+ /**
+ * The associated style sheet.
+ */
+ private StyleSheet styleSheet;
+
+ /**
+ * The bullet type.
+ */
+ private String type;
+
/**
* Package-private constructor.
*
* @param as - AttributeSet for painter
*/
- ListPainter(AttributeSet as)
+ ListPainter(AttributeSet as, StyleSheet ss)
{
- this.as = as;
+ attributes = as;
+ styleSheet = ss;
+ type = (String) as.getAttribute(CSS.Attribute.LIST_STYLE_TYPE);
}
-
+
+ /**
+ * Cached rectangle re-used in the paint method below.
+ */
+ private final Rectangle tmpRect = new Rectangle();
+
/**
* Paints the CSS list decoration according to the attributes given.
*
@@ -698,210 +1394,66 @@ public class StyleSheet extends StyleContext
public void paint(Graphics g, float x, float y, float w, float h, View v,
int item)
{
- // FIXME: Not implemented.
- }
- }
-
- /**
- * The parser callback for the CSSParser.
- */
- class CssParser implements CSSParser.CSSParserCallback
- {
- /**
- * A vector of all the selectors.
- * Each element is an array of all the selector tokens
- * in a single rule.
- */
- Vector selectors;
-
- /** A vector of all the selector tokens in a rule. */
- Vector selectorTokens;
-
- /** Name of the current property. */
- String propertyName;
-
- /** The set of CSS declarations */
- MutableAttributeSet declaration;
-
- /**
- * True if parsing a declaration, that is the Reader will not
- * contain a selector.
- */
- boolean parsingDeclaration;
-
- /** True if the attributes are coming from a linked/imported style. */
- boolean isLink;
-
- /** The base URL */
- URL base;
-
- /** The parser */
- CSSParser parser;
-
- /**
- * Constructor
- */
- CssParser()
- {
- selectors = new Vector();
- selectorTokens = new Vector();
- parser = new CSSParser();
- base = StyleSheet.this.base;
- declaration = new SimpleAttributeSet();
- }
-
- /**
- * Parses the passed in CSS declaration into an AttributeSet.
- *
- * @param s - the declaration
- * @return the set of attributes containing the property and value.
- */
- public AttributeSet parseDeclaration(String s)
- {
- try
- {
- return parseDeclaration(new StringReader(s));
- }
- catch (IOException e)
- {
- // Do nothing here.
- }
- return null;
- }
-
- /**
- * Parses the passed in CSS declaration into an AttributeSet.
- *
- * @param r - the reader
- * @return the attribute set
- * @throws IOException from the reader
- */
- public AttributeSet parseDeclaration(Reader r) throws IOException
- {
- parse(base, r, true, false);
- return declaration;
- }
-
- /**
- * Parse the given CSS stream
- *
- * @param base - the url
- * @param r - the reader
- * @param parseDec - True if parsing a declaration
- * @param isLink - True if parsing a link
- */
- public void parse(URL base, Reader r, boolean parseDec, boolean isLink) throws IOException
- {
- parsingDeclaration = parseDec;
- this.isLink = isLink;
- this.base = base;
-
- // flush out all storage
- propertyName = null;
- selectors.clear();
- selectorTokens.clear();
- declaration.removeAttributes(declaration);
-
- parser.parse(r, this, parseDec);
- }
-
- /**
- * Invoked when a valid @import is encountered,
- * will call importStyleSheet if a MalformedURLException
- * is not thrown in creating the URL.
- *
- * @param s - the string after @import
- */
- public void handleImport(String s)
- {
- if (s != null)
+ // FIXME: This is a very simplistic list rendering. We still need
+ // to implement different bullet types (see type field) and custom
+ // bullets via images.
+ View itemView = v.getView(item);
+ AttributeSet viewAtts = itemView.getAttributes();
+ Object tag = viewAtts.getAttribute(StyleConstants.NameAttribute);
+ // Only paint something here when the child view is an LI tag
+ // and the calling view is some of the list tags then).
+ if (tag != null && tag == HTML.Tag.LI)
{
- try
+ g.setColor(Color.BLACK);
+ int centerX = (int) (x - 12);
+ int centerY = -1;
+ // For paragraphs (almost all cases) center bullet vertically
+ // in the middle of the first line.
+ tmpRect.setBounds((int) x, (int) y, (int) w, (int) h);
+ if (itemView.getViewCount() > 0)
{
- if (s.startsWith("url(") && s.endsWith(")"))
- s = s.substring(4, s.length() - 1);
- if (s.indexOf("\"") >= 0)
- s = s.replaceAll("\"","");
-
- URL url = new URL(s);
- if (url == null && base != null)
- url = new URL(base, s);
-
- importStyleSheet(url);
+ View v1 = itemView.getView(0);
+ if (v1 instanceof ParagraphView && v1.getViewCount() > 0)
+ {
+ Shape a1 = itemView.getChildAllocation(0, tmpRect);
+ Rectangle r1 = a1 instanceof Rectangle ? (Rectangle) a1
+ : a1.getBounds();
+ ParagraphView par = (ParagraphView) v1;
+ Shape a = par.getChildAllocation(0, r1);
+ if (a != null)
+ {
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a
+ : a.getBounds();
+ centerY = (int) (r.height / 2 + r.y);
+ }
+ }
}
- catch (MalformedURLException e)
+ if (centerY == -1)
{
- // Do nothing here.
+ centerY =(int) (h / 2 + y);
}
+ g.fillOval(centerX - 3, centerY - 3, 6, 6);
}
}
+ }
- /**
- * A selector has been encountered.
- *
- * @param s - a selector (e.g. P or UL or even P,)
- */
- public void handleSelector(String s)
- {
- if (s.endsWith(","))
- s = s.substring(0, s.length() - 1);
-
- selectorTokens.addElement(s);
- addSelector();
- }
-
- /**
- * Invoked when the start of a rule is encountered.
- */
- public void startRule()
- {
- addSelector();
- }
-
- /**
- * Invoked when a property name is encountered.
- *
- * @param s - the property
- */
- public void handleProperty(String s)
- {
- propertyName = s;
- }
-
- /**
- * Invoked when a property value is encountered.
+ /**
+ * Converts an AttributeSet to a Map. This is used for CSS resolving.
*
- * @param s - the value
- */
- public void handleValue(String s)
- {
- // call addCSSAttribute
- // FIXME: Not implemented
- }
-
- /**
- * Invoked when the end of a rule is encountered.
- */
- public void endRule()
- {
- // FIXME: Not implemented
- // add rules
- propertyName = null;
- }
-
- /**
- * Adds the selector to the vector.
- */
- private void addSelector()
- {
- int length = selectorTokens.size();
- if (length > 0)
- {
- Object[] sel = new Object[length];
- System.arraycopy(selectorTokens.toArray(), 0, sel, 0, length);
- selectors.add(sel);
- selectorTokens.clear();
- }
- }
+ * @param atts the attributes to convert
+ *
+ * @return the converted map
+ */
+ private Map attributeSetToMap(AttributeSet atts)
+ {
+ HashMap map = new HashMap();
+ Enumeration keys = atts.getAttributeNames();
+ while (keys.hasMoreElements())
+ {
+ Object key = keys.nextElement();
+ Object value = atts.getAttribute(key);
+ map.put(key.toString(), value.toString());
+ }
+ return map;
}
}
diff --git a/libjava/classpath/javax/swing/text/html/TableView.java b/libjava/classpath/javax/swing/text/html/TableView.java
index c2edc8cdd64..f87d7b35fc5 100644
--- a/libjava/classpath/javax/swing/text/html/TableView.java
+++ b/libjava/classpath/javax/swing/text/html/TableView.java
@@ -38,49 +38,318 @@ exception statement from your version. */
package javax.swing.text.html;
-import javax.swing.text.Document;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+
+import gnu.javax.swing.text.html.css.Length;
+
+import javax.swing.SizeRequirements;
+import javax.swing.event.DocumentEvent;
+import javax.swing.text.AttributeSet;
import javax.swing.text.Element;
+import javax.swing.text.StyleConstants;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;
/**
- * A conrete implementation of TableView that renders HTML tables.
- *
- * @author Roman Kennke (kennke@aicas.com)
+ * A view implementation that renders HTML tables.
+ *
+ * This is basically a vertical BoxView that contains the rows of the table
+ * and the rows are horizontal BoxViews that contain the actual columns.
*/
class TableView
- extends javax.swing.text.TableView
+ extends BlockView
+ implements ViewFactory
{
+
/**
* Represents a single table row.
*/
- public class RowView extends TableRow
+ class RowView
+ extends BlockView
{
/**
- * Creates a new instance of the <code>RowView</code>.
+ * Has true at column positions where an above row's cell overlaps into
+ * this row.
+ */
+ boolean[] overlap;
+
+ /**
+ * Stores the row index of this row.
+ */
+ int rowIndex;
+
+ /**
+ * Creates a new RowView.
*
- * @param el the element for which to create a row view
+ * @param el the element for the row view
+ */
+ RowView(Element el)
+ {
+ super(el, X_AXIS);
+ }
+
+ public void replace(int offset, int len, View[] views)
+ {
+ gridValid = false;
+ super.replace(offset, len, views);
+ }
+
+ /**
+ * Overridden to make rows not resizable along the Y axis.
+ */
+ public float getMaximumSpan(int axis)
+ {
+ float span;
+ if (axis == Y_AXIS)
+ span = super.getPreferredSpan(axis);
+ else
+ span = Integer.MAX_VALUE;
+ return span;
+ }
+
+ public float getMinimumSpan(int axis)
+ {
+ float span;
+ if (axis == X_AXIS)
+ span = totalColumnRequirements.minimum;
+ else
+ span = super.getMinimumSpan(axis);
+ return span;
+ }
+
+ public float getPreferredSpan(int axis)
+ {
+ float span;
+ if (axis == X_AXIS)
+ span = totalColumnRequirements.preferred;
+ else
+ span = super.getPreferredSpan(axis);
+ return span;
+ }
+
+ /**
+ * Calculates the overall size requirements for the row along the
+ * major axis. This will be the sum of the column requirements.
+ */
+ protected SizeRequirements calculateMajorAxisRequirements(int axis,
+ SizeRequirements r)
+ {
+ if (r == null)
+ r = new SizeRequirements();
+ int adjust = (columnRequirements.length + 1) * cellSpacing;
+ r.minimum = totalColumnRequirements.minimum + adjust;
+ r.preferred = totalColumnRequirements.preferred + adjust;
+ r.maximum = totalColumnRequirements.maximum + adjust;
+ r.alignment = 0.0F;
+ return r;
+ }
+
+ /**
+ * Lays out the columns in this row.
*/
- public RowView(Element el)
+ protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets,
+ int spans[])
{
- super(el);
+ super.layoutMinorAxis(targetSpan, axis, offsets, spans);
+
+ // Adjust columns that have rowSpan > 1.
+ int numCols = getViewCount();
+ for (int i = 0; i < numCols; i++)
+ {
+ View v = getView(i);
+ if (v instanceof CellView)
+ {
+ CellView cell = (CellView) v;
+ if (cell.rowSpan > 1)
+ {
+ for (int r = 1; r < cell.rowSpan; r++)
+ {
+ spans[i] += TableView.this.getSpan(axis, rowIndex + r);
+ spans[i] += cellSpacing;
+ }
+ }
+ }
+ }
}
-
+
+ /**
+ * Lays out the columns in this row.
+ */
+ protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets,
+ int spans[])
+ {
+ updateGrid();
+ int numCols = offsets.length;
+ int realColumn = 0;
+ int colCount = getViewCount();
+ for (int i = 0; i < numColumns;)
+ {
+ if (! overlap[i] && realColumn < colCount)
+ {
+ View v = getView(realColumn);
+ if (v instanceof CellView)
+ {
+ CellView cv = (CellView) v;
+ offsets[realColumn] = columnOffsets[i];
+ spans[realColumn] = 0;
+ for (int j = 0; j < cv.colSpan; j++, i++)
+ {
+ spans[realColumn] += columnSpans[i];
+ if (j < cv.colSpan - 1)
+ spans[realColumn] += cellSpacing;
+ }
+ }
+ realColumn++;
+ }
+ else
+ {
+ i++;
+ }
+ }
+ }
+ }
+
/**
- * Get the associated style sheet from the document.
- *
- * @return the associated style sheet.
+ * A view that renders HTML table cells (TD and TH tags).
*/
- protected StyleSheet getStyleSheet()
+ class CellView
+ extends BlockView
+ {
+
+ /**
+ * The number of columns that this view spans.
+ */
+ int colSpan;
+
+ /**
+ * The number of rows that this cell spans.
+ */
+ int rowSpan;
+
+ /**
+ * Creates a new CellView for the specified element.
+ *
+ * @param el the element for which to create the colspan
+ */
+ CellView(Element el)
{
- Document d = getElement().getDocument();
- if (d instanceof HTMLDocument)
- return ((HTMLDocument) d).getStyleSheet();
- else
- return null;
- }
+ super(el, Y_AXIS);
+ }
+
+ protected SizeRequirements calculateMajorAxisRequirements(int axis,
+ SizeRequirements r)
+ {
+ r = super.calculateMajorAxisRequirements(axis, r);
+ r.maximum = Integer.MAX_VALUE;
+ return r;
+ }
+
+ /**
+ * Overridden to fetch the columnSpan attibute.
+ */
+ protected void setPropertiesFromAttributes()
+ {
+ super.setPropertiesFromAttributes();
+ colSpan = 1;
+ AttributeSet atts = getAttributes();
+ Object o = atts.getAttribute(HTML.Attribute.COLSPAN);
+ if (o != null)
+ {
+ try
+ {
+ colSpan = Integer.parseInt(o.toString());
+ }
+ catch (NumberFormatException ex)
+ {
+ // Couldn't parse the colspan, assume 1.
+ colSpan = 1;
+ }
+ }
+ rowSpan = 1;
+ o = atts.getAttribute(HTML.Attribute.ROWSPAN);
+ if (o != null)
+ {
+ try
+ {
+ rowSpan = Integer.parseInt(o.toString());
+ }
+ catch (NumberFormatException ex)
+ {
+ // Couldn't parse the colspan, assume 1.
+ rowSpan = 1;
+ }
+ }
+ }
}
+
+ /**
+ * The attributes of this view.
+ */
+ private AttributeSet attributes;
+
+ /**
+ * The column requirements.
+ *
+ * Package private to avoid accessor methods.
+ */
+ SizeRequirements[] columnRequirements;
+
+ /**
+ * The overall requirements across all columns.
+ *
+ * Package private to avoid accessor methods.
+ */
+ SizeRequirements totalColumnRequirements;
+
+ /**
+ * The column layout, offsets.
+ *
+ * Package private to avoid accessor methods.
+ */
+ int[] columnOffsets;
+
+ /**
+ * The column layout, spans.
+ *
+ * Package private to avoid accessor methods.
+ */
+ int[] columnSpans;
+
+ /**
+ * The widths of the columns that have been explicitly specified.
+ */
+ Length[] columnWidths;
+
+ /**
+ * The total number of columns.
+ */
+ int numColumns;
+
+ /**
+ * The table width.
+ */
+ private Length width;
+
+ /**
+ * Indicates if the grid setup is ok.
+ */
+ boolean gridValid = false;
+
+ /**
+ * Additional space that is added _between_ table cells.
+ *
+ * This is package private to avoid accessor methods.
+ */
+ int cellSpacing;
+
+ /**
+ * A cached Rectangle object for reuse in paint().
+ */
+ private Rectangle tmpRect;
+
/**
* Creates a new HTML table view for the specified element.
*
@@ -88,50 +357,619 @@ class TableView
*/
public TableView(Element el)
{
- super(el);
+ super(el, Y_AXIS);
+ totalColumnRequirements = new SizeRequirements();
+ tmpRect = new Rectangle();
}
-
+
/**
- * Get the associated style sheet from the document.
- *
- * @return the associated style sheet.
+ * Implementation of the ViewFactory interface for creating the
+ * child views correctly.
*/
- protected StyleSheet getStyleSheet()
+ public View create(Element elem)
{
- Document d = getElement().getDocument();
- if (d instanceof HTMLDocument)
- return ((HTMLDocument) d).getStyleSheet();
+ View view = null;
+ AttributeSet atts = elem.getAttributes();
+ Object name = atts.getAttribute(StyleConstants.NameAttribute);
+ AttributeSet pAtts = elem.getParentElement().getAttributes();
+ Object pName = pAtts.getAttribute(StyleConstants.NameAttribute);
+
+ if (name == HTML.Tag.TR && pName == HTML.Tag.TABLE)
+ view = new RowView(elem);
+ else if ((name == HTML.Tag.TD || name == HTML.Tag.TH)
+ && pName == HTML.Tag.TR)
+ view = new CellView(elem);
+ else if (name == HTML.Tag.CAPTION)
+ view = new ParagraphView(elem);
else
- return null;
- }
-
+ {
+ // If we haven't mapped the element, then fall back to the standard
+ // view factory.
+ View parent = getParent();
+ if (parent != null)
+ {
+ ViewFactory vf = parent.getViewFactory();
+ if (vf != null)
+ view = vf.create(elem);
+ }
+ }
+ return view;
+ }
+
+ /**
+ * Returns this object as view factory so that we get our TR, TD, TH
+ * and CAPTION subelements created correctly.
+ */
+ public ViewFactory getViewFactory()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the attributes of this view. This is overridden to provide
+ * the attributes merged with the CSS stuff.
+ */
+ public AttributeSet getAttributes()
+ {
+ if (attributes == null)
+ attributes = getStyleSheet().getViewAttributes(this);
+ return attributes;
+ }
+
+ /**
+ * Returns the stylesheet associated with this view.
+ *
+ * @return the stylesheet associated with this view
+ */
+ protected StyleSheet getStyleSheet()
+ {
+ HTMLDocument doc = (HTMLDocument) getDocument();
+ return doc.getStyleSheet();
+ }
+
+ /**
+ * Overridden to calculate the size requirements according to the
+ * columns distribution.
+ */
+ protected SizeRequirements calculateMinorAxisRequirements(int axis,
+ SizeRequirements r)
+ {
+ updateGrid();
+ calculateColumnRequirements();
+
+ // Calculate the horizontal requirements according to the superclass.
+ // This will return the maximum of the row's widths.
+ r = super.calculateMinorAxisRequirements(axis, r);
+
+ // Try to set the CSS width if it fits.
+ if (width != null)
+ {
+ int w = (int) width.getValue();
+ if (r.minimum < w)
+ r.minimum = w;
+ }
+
+ // Adjust requirements when we have cell spacing.
+ int adjust = (columnRequirements.length + 1) * cellSpacing;
+ r.minimum += adjust;
+ r.preferred += adjust;
+
+ // Apply the alignment.
+ AttributeSet atts = getAttributes();
+ Object o = atts.getAttribute(CSS.Attribute.TEXT_ALIGN);
+ r.alignment = 0.0F;
+ if (o != null)
+ {
+ String al = o.toString();
+ if (al.equals("left"))
+ r.alignment = 0.0F;
+ else if (al.equals("center"))
+ r.alignment = 0.5F;
+ else if (al.equals("right"))
+ r.alignment = 1.0F;
+ }
+
+ // Make it not resize in the horizontal direction.
+ r.maximum = r.preferred;
+ return r;
+ }
+
+ /**
+ * Overridden to perform the table layout before calling the super
+ * implementation.
+ */
+ protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets,
+ int[] spans)
+ {
+ updateGrid();
+
+ // Mark all rows as invalid along their minor axis to force correct
+ // layout of multi-row cells.
+ int n = getViewCount();
+ for (int i = 0; i < n; i++)
+ {
+ View row = getView(i);
+ if (row instanceof RowView)
+ ((RowView) row).layoutChanged(axis);
+ }
+
+ layoutColumns(targetSpan);
+ super.layoutMinorAxis(targetSpan, axis, offsets, spans);
+ }
+
+ /**
+ * Calculates the size requirements for the columns.
+ */
+ private void calculateColumnRequirements()
+ {
+ int numRows = getViewCount();
+ totalColumnRequirements.minimum = 0;
+ totalColumnRequirements.preferred = 0;
+ totalColumnRequirements.maximum = 0;
+
+ // In this first pass we find out a suitable total width to fit in
+ // all columns of all rows.
+ for (int r = 0; r < numRows; r++)
+ {
+ View rowView = getView(r);
+ int numCols;
+ if (rowView instanceof RowView)
+ numCols = ((RowView) rowView).getViewCount();
+ else
+ numCols = 0;
+
+ // We collect the normal (non-relative) column requirements in the
+ // total variable and the relative requirements in the relTotal
+ // variable. In the end we create the maximum of both to get the
+ // real requirements.
+ SizeRequirements total = new SizeRequirements();
+ SizeRequirements relTotal = new SizeRequirements();
+ float totalPercent = 0.F;
+ int realCol = 0;
+ for (int c = 0; c < numCols; c++)
+ {
+ View v = rowView.getView(c);
+ if (v instanceof CellView)
+ {
+ CellView cellView = (CellView) v;
+ int colSpan = cellView.colSpan;
+ if (colSpan > 1)
+ {
+ int cellMin = (int) cellView.getMinimumSpan(X_AXIS);
+ int cellPref = (int) cellView.getPreferredSpan(X_AXIS);
+ int cellMax = (int) cellView.getMaximumSpan(X_AXIS);
+ int currentMin = 0;
+ int currentPref = 0;
+ long currentMax = 0;
+ for (int i = 0; i < colSpan; i++)
+ {
+ SizeRequirements req = columnRequirements[realCol];
+ currentMin += req.minimum;
+ currentPref += req.preferred;
+ currentMax += req.maximum;
+ }
+ int deltaMin = cellMin - currentMin;
+ int deltaPref = cellPref - currentPref;
+ int deltaMax = (int) (cellMax - currentMax);
+ // Distribute delta.
+ for (int i = 0; i < colSpan; i++)
+ {
+ SizeRequirements req = columnRequirements[realCol];
+ if (deltaMin > 0)
+ req.minimum += deltaMin / colSpan;
+ if (deltaPref > 0)
+ req.preferred += deltaPref / colSpan;
+ if (deltaMax > 0)
+ req.maximum += deltaMax / colSpan;
+ if (columnWidths[realCol] == null
+ || ! columnWidths[realCol].isPercentage())
+ {
+ total.minimum += req.minimum;
+ total.preferred += req.preferred;
+ total.maximum += req.maximum;
+ }
+ else
+ {
+ relTotal.minimum =
+ Math.max(relTotal.minimum,
+ (int) (req.minimum
+ * columnWidths[realCol].getValue()));
+ relTotal.preferred =
+ Math.max(relTotal.preferred,
+ (int) (req.preferred
+ * columnWidths[realCol].getValue()));
+ relTotal.maximum =
+ Math.max(relTotal.maximum,
+ (int) (req.maximum
+ * columnWidths[realCol].getValue()));
+ totalPercent += columnWidths[realCol].getValue();
+ }
+ }
+ realCol += colSpan;
+ }
+ else
+ {
+ // Shortcut for colSpan == 1.
+ SizeRequirements req = columnRequirements[realCol];
+ req.minimum = Math.max(req.minimum,
+ (int) cellView.getMinimumSpan(X_AXIS));
+ req.preferred = Math.max(req.preferred,
+ (int) cellView.getPreferredSpan(X_AXIS));
+ req.maximum = Math.max(req.maximum,
+ (int) cellView.getMaximumSpan(X_AXIS));
+ if (columnWidths[realCol] == null
+ || ! columnWidths[realCol].isPercentage())
+ {
+ total.minimum += columnRequirements[realCol].minimum;
+ total.preferred +=
+ columnRequirements[realCol].preferred;
+ total.maximum += columnRequirements[realCol].maximum;
+ }
+ else
+ {
+ relTotal.minimum =
+ Math.max(relTotal.minimum,
+ (int) (req.minimum
+ / columnWidths[c].getValue()));
+ relTotal.preferred =
+ Math.max(relTotal.preferred,
+ (int) (req.preferred
+ / columnWidths[c].getValue()));
+ relTotal.maximum =
+ Math.max(relTotal.maximum,
+ (int) (req.maximum
+ / columnWidths[c].getValue()));
+ totalPercent += columnWidths[c].getValue();
+ }
+ realCol += 1;
+ }
+ }
+ }
+
+ // Update the total requirements as follows:
+ // 1. Multiply the absolute requirements with 1 - totalPercent. This
+ // gives the total requirements based on the wishes of the absolute
+ // cells.
+ // 2. Take the maximum of this value and the total relative
+ // requirements. Now we should have enough space for whatever cell
+ // in this column.
+ // 3. Take the maximum of this value and the previous maximum value.
+ total.minimum *= 1.F / (1.F - totalPercent);
+ total.preferred *= 1.F / (1.F - totalPercent);
+ total.maximum *= 1.F / (1.F - totalPercent);
+
+ int rowTotalMin = Math.max(total.minimum, relTotal.minimum);
+ int rowTotalPref = Math.max(total.preferred, relTotal.preferred);
+ int rowTotalMax = Math.max(total.maximum, relTotal.maximum);
+ totalColumnRequirements.minimum =
+ Math.max(totalColumnRequirements.minimum, rowTotalMin);
+ totalColumnRequirements.preferred =
+ Math.max(totalColumnRequirements.preferred, rowTotalPref);
+ totalColumnRequirements.maximum =
+ Math.max(totalColumnRequirements.maximum, rowTotalMax);
+ }
+
+ // Now we know what we want and can fix up the actual relative
+ // column requirements.
+ int numCols = columnRequirements.length;
+ for (int i = 0; i < numCols; i++)
+ {
+ if (columnWidths[i] != null)
+ {
+ columnRequirements[i].minimum = (int)
+ columnWidths[i].getValue(totalColumnRequirements.minimum);
+ columnRequirements[i].preferred = (int)
+ columnWidths[i].getValue(totalColumnRequirements.preferred);
+ columnRequirements[i].maximum = (int)
+ columnWidths[i].getValue(totalColumnRequirements.maximum);
+ }
+ }
+ }
+
/**
- * Creates a view for a table row.
- *
- * @param el the element that represents the table row
- * @return a view for rendering the table row
- * (and instance of {@link RowView}).
+ * Lays out the columns.
+ *
+ * @param targetSpan the target span into which the table is laid out
*/
- protected TableRow createTableRow(Element el)
+ private void layoutColumns(int targetSpan)
{
- return new RowView(el);
- }
-
+ // Set the spans to the preferred sizes. Determine the space
+ // that we have to adjust the sizes afterwards.
+ long sumPref = 0;
+ int n = columnRequirements.length;
+ for (int i = 0; i < n; i++)
+ {
+ SizeRequirements col = columnRequirements[i];
+ if (columnWidths[i] != null)
+ columnSpans[i] = (int) columnWidths[i].getValue(targetSpan);
+ else
+ columnSpans[i] = col.preferred;
+ sumPref += columnSpans[i];
+ }
+
+ // Try to adjust the spans so that we fill the targetSpan.
+ // For adjustments we have to use the targetSpan minus the cumulated
+ // cell spacings.
+ long diff = targetSpan - (n + 1) * cellSpacing - sumPref;
+ float factor = 0.0F;
+ int[] diffs = null;
+ if (diff != 0)
+ {
+ long total = 0;
+ diffs = new int[n];
+ for (int i = 0; i < n; i++)
+ {
+ // Only adjust the width if we haven't set a column width here.
+ if (columnWidths[i] == null)
+ {
+ SizeRequirements col = columnRequirements[i];
+ int span;
+ if (diff < 0)
+ {
+ span = col.minimum;
+ diffs[i] = columnSpans[i] - span;
+ }
+ else
+ {
+ span = col.maximum;
+ diffs[i] = span - columnSpans[i];
+ }
+ total += span;
+ }
+ else
+ total += columnSpans[i];
+ }
+
+ float maxAdjust = Math.abs(total - sumPref);
+ factor = diff / maxAdjust;
+ factor = Math.min(factor, 1.0F);
+ factor = Math.max(factor, -1.0F);
+ }
+
+ // Actually perform adjustments.
+ int totalOffs = cellSpacing;
+ for (int i = 0; i < n; i++)
+ {
+ columnOffsets[i] = totalOffs;
+ if (diff != 0)
+ {
+ float adjust = factor * diffs[i];
+ columnSpans[i] += Math.round(adjust);
+ }
+ // Avoid overflow here.
+ totalOffs = (int) Math.min((long) totalOffs + (long) columnSpans[i]
+ + (long) cellSpacing, Integer.MAX_VALUE);
+ }
+ }
+
/**
- * Loads the children of the Table. This completely bypasses the ViewFactory
- * and creates instances of TableRow instead.
+ * Updates the arrays that contain the row and column data in response
+ * to a change to the table structure.
*
- * @param vf ignored
+ * Package private to avoid accessor methods.
+ */
+ void updateGrid()
+ {
+ if (! gridValid)
+ {
+ AttributeSet atts = getAttributes();
+ StyleSheet ss = getStyleSheet();
+ float emBase = ss.getEMBase(atts);
+ float exBase = ss.getEXBase(atts);
+ int maxColumns = 0;
+ int numRows = getViewCount();
+ for (int r = 0; r < numRows; r++)
+ {
+ View rowView = getView(r);
+ int numCols = 0;
+ if (rowView instanceof RowView)
+ {
+ int numCells = ((RowView) rowView).getViewCount();
+ for (int i = 0; i < numCells; i++)
+ {
+ View v = rowView.getView(i);
+ if (v instanceof CellView)
+ numCols += ((CellView) v).colSpan;
+ }
+ }
+ maxColumns = Math.max(numCols, maxColumns);
+ }
+ numColumns = maxColumns;
+ columnWidths = new Length[maxColumns];
+ int[] rowSpans = new int[maxColumns];
+ for (int r = 0; r < numRows; r++)
+ {
+ View view = getView(r);
+ if (view instanceof RowView)
+ {
+ RowView rowView = (RowView) view;
+ rowView.rowIndex = r;
+ rowView.overlap = new boolean[maxColumns];
+ int colIndex = 0;
+ int colCount = rowView.getViewCount();
+ for (int c = 0; c < maxColumns;)
+ {
+ if (rowSpans[c] > 0)
+ {
+ rowSpans[c]--;
+ rowView.overlap[c] = true;
+ c++;
+ }
+ else if (colIndex < colCount)
+ {
+ View v = rowView.getView(colIndex);
+ colIndex++;
+ if (v instanceof CellView)
+ {
+ CellView cv = (CellView) v;
+ Object o =
+ cv.getAttributes().getAttribute(CSS.Attribute.WIDTH);
+ if (o != null && columnWidths[c] == null
+ && o instanceof Length)
+ {
+ columnWidths[c]= (Length) o;
+ columnWidths[c].setFontBases(emBase, exBase);
+ }
+ int rs = cv.rowSpan - 1;
+ for (int col = cv.colSpan - 1; col >= 0; col--)
+ {
+ rowSpans[c] = rs;
+ c++;
+ }
+ }
+ }
+ else
+ {
+ c++;
+ }
+ }
+ }
+ }
+ columnRequirements = new SizeRequirements[maxColumns];
+ for (int i = 0; i < maxColumns; i++)
+ columnRequirements[i] = new SizeRequirements();
+ columnOffsets = new int[maxColumns];
+ columnSpans = new int[maxColumns];
+
+ gridValid = true;
+ }
+ }
+
+ /**
+ * Overridden to restrict the table width to the preferred size.
+ */
+ public float getMaximumSpan(int axis)
+ {
+ float span;
+ if (axis == X_AXIS)
+ span = super.getPreferredSpan(axis);
+ else
+ span = super.getMaximumSpan(axis);
+ return span;
+ }
+
+ /**
+ * Overridden to fetch the CSS attributes when view gets connected.
+ */
+ public void setParent(View parent)
+ {
+ super.setParent(parent);
+ if (parent != null)
+ setPropertiesFromAttributes();
+ }
+
+ /**
+ * Fetches CSS and HTML layout attributes.
+ */
+ protected void setPropertiesFromAttributes()
+ {
+ super.setPropertiesFromAttributes();
+
+ // Fetch and parse cell spacing.
+ AttributeSet atts = getAttributes();
+ StyleSheet ss = getStyleSheet();
+ float emBase = ss.getEMBase(atts);
+ float exBase = ss.getEXBase(atts);
+ Object o = atts.getAttribute(CSS.Attribute.BORDER_SPACING);
+ if (o != null && o instanceof Length)
+ {
+ Length l = (Length) o;
+ l.setFontBases(emBase, exBase);
+ cellSpacing = (int) l.getValue();
+ }
+ o = atts.getAttribute(CSS.Attribute.WIDTH);
+ if (o != null && o instanceof Length)
+ {
+ width = (Length) o;
+ width.setFontBases(emBase, exBase);
+ }
+ }
+
+ /**
+ * Overridden to adjust for cellSpacing.
+ */
+ protected SizeRequirements calculateMajorAxisRequirements(int axis,
+ SizeRequirements r)
+ {
+ r = super.calculateMajorAxisRequirements(axis, r);
+ int adjust = (getViewCount() + 1) * cellSpacing;
+ r.minimum += adjust;
+ r.preferred += adjust;
+ r.maximum += adjust;
+ return r;
+ }
+
+ /**
+ * Overridden to adjust for cellSpacing.
+ */
+ protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets,
+ int spans[])
+ {
+ // Mark all rows as invalid along their minor axis to force correct
+ // layout of multi-row cells.
+ int n = getViewCount();
+ for (int i = 0; i < n; i++)
+ {
+ View row = getView(i);
+ if (row instanceof RowView)
+ ((RowView) row).layoutChanged(axis);
+ }
+
+ int adjust = (getViewCount() + 1) * cellSpacing;
+ super.layoutMajorAxis(targetSpan - adjust, axis, offsets, spans);
+ for (int i = 0; i < offsets.length; i++)
+ {
+ offsets[i] += (i + 1) * cellSpacing;
+ }
+ }
+
+ /**
+ * Overridden to replace view factory with this one.
+ */
+ public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f)
+ {
+ super.insertUpdate(e, a, this);
+ }
+
+ /**
+ * Overridden to replace view factory with this one.
+ */
+ public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f)
+ {
+ super.removeUpdate(e, a, this);
+ }
+
+ /**
+ * Overridden to replace view factory with this one.
*/
- protected void loadChildren(ViewFactory vf)
+ public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f)
{
- Element el = getElement();
- int numChildren = el.getElementCount();
- View[] rows = new View[numChildren];
- for (int i = 0; i < numChildren; ++i)
+ super.changedUpdate(e, a, this);
+ }
+
+ public void replace(int offset, int len, View[] views)
+ {
+ gridValid = false;
+ super.replace(offset, len, views);
+ }
+
+ /**
+ * We can't use the super class's paint() method because it might cut
+ * off multi-row children. Instead we trigger painting for all rows
+ * and let the rows sort out what to paint and what not.
+ */
+ public void paint(Graphics g, Shape a)
+ {
+ Rectangle rect = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ painter.paint(g, rect.x, rect.y, rect.width, rect.height, this);
+ int nRows = getViewCount();
+ Rectangle inside = getInsideAllocation(a);
+ for (int r = 0; r < nRows; r++)
{
- rows[i] = createTableRow(el.getElement(i));
+ tmpRect.setBounds(inside);
+ childAllocation(r, tmpRect);
+ paintChild(g, tmpRect, r);
}
- replace(0, getViewCount(), rows);
}
+
}
diff --git a/libjava/classpath/javax/swing/text/html/ViewAttributeSet.java b/libjava/classpath/javax/swing/text/html/ViewAttributeSet.java
new file mode 100644
index 00000000000..25db89fc405
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/ViewAttributeSet.java
@@ -0,0 +1,163 @@
+/* ViewAttributeSet.java -- The AttributeSet used by HTML views
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text.html;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+
+import javax.swing.text.AttributeSet;
+import javax.swing.text.Element;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.View;
+
+/**
+ * An AttributeSet implemenation that is used by the HTML views. This
+ * AttributeSet is created by StyleSheet.getViewAttributes() and combines
+ * the following attributes:
+ * - The original attributes of the View's element.
+ * - Any translated (HTML->CSS) attributes, as returned by
+ * StyleSheet.translateHTMLToCS().
+ * - CSS Styles as resolved by the CSS stylesheet.
+ *
+ * In addition to that, it resolves attributes to the parent views, if
+ * a CSS attribute is requested that is inheritable.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+class ViewAttributeSet
+ extends MultiAttributeSet
+{
+
+ /**
+ * The view for which we are the AttributeSet.
+ */
+ private View view;
+
+ /**
+ * The stylesheet to use.
+ */
+ private StyleSheet styleSheet;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param v the view for which to do the AttributeSet
+ */
+ ViewAttributeSet(View v, StyleSheet ss)
+ {
+ styleSheet = ss;
+ view = v;
+ ArrayList atts = new ArrayList();
+
+ Element el = v.getElement();
+ AttributeSet elAtts = el.getAttributes();
+ AttributeSet htmlAtts = styleSheet.translateHTMLToCSS(elAtts);
+ if (htmlAtts.getAttributeCount() > 0)
+ atts.add(htmlAtts);
+
+ if (el.isLeaf())
+ {
+ Enumeration n = elAtts.getAttributeNames();
+ while (n.hasMoreElements())
+ {
+ Object key = n.nextElement();
+ if (key instanceof HTML.Tag)
+ {
+ AttributeSet rule = styleSheet.getRule((HTML.Tag) key, el);
+ if (rule != null)
+ atts.add(rule);
+ }
+ }
+ }
+ else
+ {
+ HTML.Tag tag =
+ (HTML.Tag) elAtts.getAttribute(StyleConstants.NameAttribute);
+ AttributeSet rule = styleSheet.getRule(tag, el);
+ if (rule != null)
+ atts.add(rule);
+ }
+
+ AttributeSet[] atts1 = new AttributeSet[atts.size()];
+ atts1 = (AttributeSet[]) atts.toArray(atts1);
+ init(atts1);
+ }
+
+ /**
+ * Fetches the attribute for the specific ckey. If the attribute
+ * can't be found and the key is a CSS.Attribute that is inherited,
+ * then the attribute is looked up in the resolve parent.
+ */
+ public Object getAttribute(Object key)
+ {
+ Object val = super.getAttribute(key);
+ if (val == null)
+ {
+ // Didn't find value. If the key is a CSS.Attribute, and is
+ // inherited, then ask the resolve parent.
+ if (key instanceof CSS.Attribute)
+ {
+ CSS.Attribute cssKey = (CSS.Attribute) key;
+ if (cssKey.isInherited())
+ {
+ AttributeSet resolveParent = getResolveParent();
+ if (resolveParent != null)
+ val = resolveParent.getAttribute(cssKey);
+ }
+ }
+ }
+ return val;
+ }
+
+ /**
+ * Returns the resolve parent of this AttributeSet. This is the AttributeSet
+ * returned by the parent view if available.
+ */
+ public AttributeSet getResolveParent()
+ {
+ AttributeSet parent = null;
+ if (view != null)
+ {
+ View parentView = view.getParent();
+ if (parentView != null)
+ parent = parentView.getAttributes();
+ }
+ return parent;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/parser/AttributeList.java b/libjava/classpath/javax/swing/text/html/parser/AttributeList.java
index 5bca0bfa7db..d48266d4730 100644
--- a/libjava/classpath/javax/swing/text/html/parser/AttributeList.java
+++ b/libjava/classpath/javax/swing/text/html/parser/AttributeList.java
@@ -122,7 +122,7 @@ public final class AttributeList
* null, if this parameter was not specified.
* Values, defined in DTD, are case insensitive.
*/
- public Vector values;
+ public Vector<?> values;
/**
* The modifier of this attribute. This field contains one of the
@@ -176,7 +176,7 @@ public final class AttributeList
* Equals to null for the last attribute definition.
*/
public AttributeList(String a_name, int a_type, int a_modifier,
- String a_default, Vector allowed_values,
+ String a_default, Vector<?> allowed_values,
AttributeList a_next
)
{
@@ -251,7 +251,7 @@ public final class AttributeList
/**
* Get the allowed values of this attribute.
*/
- public Enumeration getValues()
+ public Enumeration<?> getValues()
{
return values.elements();
}
diff --git a/libjava/classpath/javax/swing/text/html/parser/ContentModel.java b/libjava/classpath/javax/swing/text/html/parser/ContentModel.java
index 70e9c2acbff..d5c4418de27 100644
--- a/libjava/classpath/javax/swing/text/html/parser/ContentModel.java
+++ b/libjava/classpath/javax/swing/text/html/parser/ContentModel.java
@@ -151,13 +151,15 @@ public final class ContentModel
* discarded.
* @param elements - a vector to add the values to.
*/
- public void getElements(Vector elements)
+ public void getElements(Vector<Element> elements)
{
ContentModel c = this;
while (c != null)
{
- elements.add(c.content);
+ // FIXME: correct?
+ if (c.content instanceof Element)
+ elements.add((Element) c.content);
c = c.next;
}
}
diff --git a/libjava/classpath/javax/swing/text/html/parser/DTD.java b/libjava/classpath/javax/swing/text/html/parser/DTD.java
index 16bc5b0d6af..ae3c184f153 100644
--- a/libjava/classpath/javax/swing/text/html/parser/DTD.java
+++ b/libjava/classpath/javax/swing/text/html/parser/DTD.java
@@ -88,7 +88,7 @@ public class DTD
/**
* The table of existing available DTDs.
*/
- static Hashtable dtdHash = new Hashtable();
+ static Hashtable<String,DTD> dtdHash = new Hashtable<String,DTD>();
/**
* The applet element for this DTD.
@@ -148,12 +148,13 @@ public class DTD
/**
* The element for accessing all DTD elements by name.
*/
- public Hashtable elementHash = new Hashtable();
+ public Hashtable<String,Element> elementHash =
+ new Hashtable<String,Element>();
/**
* The entity table for accessing all DTD entities by name.
*/
- public Hashtable entityHash = new Hashtable();
+ public Hashtable<Object, Entity> entityHash = new Hashtable<Object, Entity>();
/**
* The name of this DTD.
@@ -165,7 +166,7 @@ public class DTD
* javax.swing.text.html.parser.Element#index field of all elements
* in this vector is set to the element position in this vector.
*/
- public Vector elements = new Vector();
+ public Vector<Element> elements = new Vector<Element>();
/** Create a new DTD with the specified name. */
protected DTD(String a_name)
@@ -224,7 +225,7 @@ public class DTD
String name = Entity.mapper.get(id);
if (name != null)
- return (Entity) entityHash.get(name);
+ return entityHash.get(name);
else
return null;
}
@@ -269,7 +270,7 @@ public class DTD
*/
public void defineAttributes(String forElement, AttributeList attributes)
{
- Element e = (Element) elementHash.get(forElement.toLowerCase());
+ Element e = elementHash.get(forElement.toLowerCase());
if (e == null)
e = newElement(forElement);
@@ -420,7 +421,7 @@ public class DTD
if (allowed_values != null)
{
StringTokenizer st = new StringTokenizer(allowed_values, " \t|");
- Vector v = new Vector(st.countTokens());
+ Vector<String> v = new Vector<String>(st.countTokens());
while (st.hasMoreTokens())
v.add(st.nextToken());
@@ -571,7 +572,7 @@ public class DTD
*/
private Element newElement(String name)
{
- Element e = (Element) elementHash.get(name.toLowerCase());
+ Element e = elementHash.get(name.toLowerCase());
if (e == null)
{
diff --git a/libjava/classpath/javax/swing/text/html/parser/DocumentParser.java b/libjava/classpath/javax/swing/text/html/parser/DocumentParser.java
index 062606d17ba..f717d69cbda 100644
--- a/libjava/classpath/javax/swing/text/html/parser/DocumentParser.java
+++ b/libjava/classpath/javax/swing/text/html/parser/DocumentParser.java
@@ -38,13 +38,13 @@ exception statement from your version. */
package javax.swing.text.html.parser;
-import gnu.javax.swing.text.html.parser.htmlAttributeSet;
import javax.swing.text.html.parser.Parser;
import java.io.IOException;
import java.io.Reader;
import javax.swing.text.BadLocationException;
+import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.html.HTMLEditorKit;
/**
@@ -117,7 +117,7 @@ public class DocumentParser
protected final void handleStartTag(TagElement tag)
{
parser.handleStartTag(tag);
- htmlAttributeSet attributes = gnu.getAttributes();
+ SimpleAttributeSet attributes = gnu.getAttributes();
if (tag.fictional())
attributes.addAttribute(HTMLEditorKit.ParserCallback.IMPLIED,
diff --git a/libjava/classpath/javax/swing/text/html/parser/ParserDelegator.java b/libjava/classpath/javax/swing/text/html/parser/ParserDelegator.java
index 70636d92923..cdd339b8f21 100644
--- a/libjava/classpath/javax/swing/text/html/parser/ParserDelegator.java
+++ b/libjava/classpath/javax/swing/text/html/parser/ParserDelegator.java
@@ -38,13 +38,13 @@ exception statement from your version. */
package javax.swing.text.html.parser;
import gnu.javax.swing.text.html.parser.HTML_401F;
-import gnu.javax.swing.text.html.parser.htmlAttributeSet;
import java.io.IOException;
import java.io.Reader;
import java.io.Serializable;
import javax.swing.text.BadLocationException;
+import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.HTMLEditorKit.ParserCallback;
@@ -93,7 +93,7 @@ public class ParserDelegator
protected final void handleStartTag(TagElement tag)
{
- htmlAttributeSet attributes = gnu.getAttributes();
+ SimpleAttributeSet attributes = gnu.getAttributes();
if (tag.fictional())
attributes.addAttribute(ParserCallback.IMPLIED, Boolean.TRUE);
diff --git a/libjava/classpath/javax/swing/tree/AbstractLayoutCache.java b/libjava/classpath/javax/swing/tree/AbstractLayoutCache.java
index 155343f5bcc..4a6899fbeae 100644
--- a/libjava/classpath/javax/swing/tree/AbstractLayoutCache.java
+++ b/libjava/classpath/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);
+
}
/**
@@ -337,7 +344,7 @@ public abstract class AbstractLayoutCache
*
* @return Enumeration
*/
- public abstract Enumeration getVisiblePathsFrom(TreePath path);
+ public abstract Enumeration<TreePath> getVisiblePathsFrom(TreePath path);
/**
* getVisibleChildCount
@@ -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/libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java b/libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java
index 6951b960005..9f587946fc2 100644
--- a/libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java
+++ b/libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java
@@ -67,7 +67,7 @@ public class DefaultMutableTreeNode
* An empty enumeration, returned by {@link #children()} if a node has no
* children.
*/
- public static final Enumeration EMPTY_ENUMERATION =
+ public static final Enumeration<TreeNode> EMPTY_ENUMERATION =
EmptyEnumeration.getInstance();
/**
@@ -78,7 +78,7 @@ public class DefaultMutableTreeNode
/**
* The child nodes for this node (may be empty).
*/
- protected Vector children = new Vector();
+ protected Vector<MutableTreeNode> children = new Vector<MutableTreeNode>();
/**
* userObject
@@ -480,7 +480,7 @@ public class DefaultMutableTreeNode
public TreeNode getSharedAncestor(DefaultMutableTreeNode node)
{
TreeNode current = this;
- ArrayList list = new ArrayList();
+ ArrayList<TreeNode> list = new ArrayList<TreeNode>();
while (current != null)
{
@@ -527,7 +527,7 @@ public class DefaultMutableTreeNode
|| children.size() == 0)
return 0;
- Stack stack = new Stack();
+ Stack<Integer> stack = new Stack<Integer>();
stack.push(new Integer(0));
TreeNode node = getChildAt(0);
int depth = 0;
@@ -765,7 +765,7 @@ public class DefaultMutableTreeNode
throw new IllegalArgumentException();
TreeNode parent = this;
- Vector nodes = new Vector();
+ Vector<TreeNode> nodes = new Vector<TreeNode>();
nodes.add(this);
while (parent != node && parent != null)
@@ -1148,7 +1148,7 @@ public class DefaultMutableTreeNode
static class PostorderEnumeration implements Enumeration
{
- Stack nodes = new Stack();
+ Stack<TreeNode> nodes = new Stack<TreeNode>();
Stack childrenEnums = new Stack();
PostorderEnumeration(TreeNode node)
diff --git a/libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java b/libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java
index b0a4d8db823..4c10bfe1af2 100644
--- a/libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java
+++ b/libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java
@@ -43,7 +43,6 @@ import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
-import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
@@ -59,10 +58,10 @@ import javax.swing.Icon;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
+import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.event.CellEditorListener;
-import javax.swing.event.ChangeEvent;
import javax.swing.event.EventListenerList;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
@@ -77,12 +76,6 @@ public class DefaultTreeCellEditor
implements ActionListener, TreeCellEditor, TreeSelectionListener
{
/**
- * The number of the fast mouse clicks, required to start the editing
- * session.
- */
- static int CLICK_COUNT_TO_START = 3;
-
- /**
* This container that appears on the tree during editing session.
* It contains the editing component displays various other editor -
* specific parts like editing icon.
@@ -99,7 +92,7 @@ public class DefaultTreeCellEditor
*/
public EditorContainer()
{
- // Do nothing here.
+ setLayout(null);
}
/**
@@ -111,12 +104,6 @@ public class DefaultTreeCellEditor
// Do nothing here.
}
- public void setBounds(Rectangle bounds)
- {
- super.setBounds(bounds);
- doLayout();
- }
-
/**
* Overrides Container.paint to paint the node's icon and use the selection
* color for the background.
@@ -126,11 +113,20 @@ public class DefaultTreeCellEditor
*/
public void paint(Graphics g)
{
+ // Paint editing icon.
if (editingIcon != null)
{
// From the previous version, the left margin is taken as half
// of the icon width.
- editingIcon.paintIcon(this, g, 0, 0);
+ int y = Math.max(0, (getHeight() - editingIcon.getIconHeight()) / 2);
+ editingIcon.paintIcon(this, g, 0, y);
+ }
+ // Paint border.
+ Color c = getBorderSelectionColor();
+ if (c != null)
+ {
+ g.setColor(c);
+ g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
}
super.paint(g);
}
@@ -141,27 +137,33 @@ public class DefaultTreeCellEditor
*/
public void doLayout()
{
- // The offset of the editing component.
- int eOffset;
+ if (editingComponent != null)
+ {
+ editingComponent.getPreferredSize();
+ editingComponent.setBounds(offset, 0, getWidth() - offset,
+ getHeight());
+ }
+ }
- // Move the component to the left, leaving room for the editing icon:
- if (editingIcon != null)
- eOffset = editingIcon.getIconWidth();
+ public Dimension getPreferredSize()
+ {
+ Dimension dim;
+ if (editingComponent != null)
+ {
+ dim = editingComponent.getPreferredSize();
+ dim.width += offset + 5;
+ if (renderer != null)
+ {
+ Dimension r = renderer.getPreferredSize();
+ dim.height = Math.max(dim.height, r.height);
+ }
+ if (editingIcon != null)
+ dim.height = Math.max(dim.height, editingIcon.getIconHeight());
+ dim.width = Math.max(100, dim.width);
+ }
else
- eOffset = 0;
-
- Rectangle bounds = getBounds();
- Component c = getComponent(0);
- c.setLocation(eOffset, 0);
-
- // Span the editing component near over all window width.
- c.setSize(bounds.width - eOffset, bounds.height);
- /*
- * @specnote the Sun sets some more narrow editing component width (it is
- * not documented how does it is calculated). However as our text field is
- * still not able to auto - scroll horizontally, replicating such strategy
- * would prevent adding extra characters to the text being edited.
- */
+ dim = new Dimension(0, 0);
+ return dim;
}
}
@@ -227,46 +229,15 @@ public class DefaultTreeCellEditor
*/
public Dimension getPreferredSize()
{
- String s = getText();
-
- Font f = getFont();
-
- if (f != null)
+ Dimension size = super.getPreferredSize();
+ if (renderer != null && DefaultTreeCellEditor.this.getFont() == null)
{
- FontMetrics fm = getToolkit().getFontMetrics(f);
-
- return new Dimension(SwingUtilities.computeStringWidth(fm, s),
- fm.getHeight());
+ size.height = renderer.getPreferredSize().height;
}
return renderer.getPreferredSize();
}
}
- /**
- * Listens for the events from the realEditor.
- */
- class RealEditorListener implements CellEditorListener
- {
- /**
- * The method is called when the editing has been cancelled.
- * @param event unused
- */
- public void editingCanceled(ChangeEvent event)
- {
- cancelCellEditing();
- }
-
- /**
- * The method is called after completing the editing session.
- *
- * @param event unused
- */
- public void editingStopped(ChangeEvent event)
- {
- stopCellEditing();
- }
- }
-
private EventListenerList listenerList = new EventListenerList();
/**
@@ -367,21 +338,14 @@ public class DefaultTreeCellEditor
public DefaultTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer,
TreeCellEditor editor)
{
- setTree(tree);
this.renderer = renderer;
-
- if (editor == null)
- editor = createTreeCellEditor();
- else
- editor.addCellEditorListener(new RealEditorListener());
-
realEditor = editor;
-
- lastPath = tree.getLeadSelectionPath();
- tree.addTreeSelectionListener(this);
+ if (realEditor == null)
+ realEditor = createTreeCellEditor();
editingContainer = createContainer();
- setFont(UIManager.getFont("Tree.font"));
- setBorderSelectionColor(UIManager.getColor("Tree.selectionBorderColor"));
+ setTree(tree);
+ Color c = UIManager.getColor("Tree.editorBorderSelectionColor");
+ setBorderSelectionColor(c);
}
/**
@@ -505,19 +469,36 @@ public class DefaultTreeCellEditor
* @return the component for editing
*/
public Component getTreeCellEditorComponent(JTree tree, Object value,
- boolean isSelected, boolean expanded,
+ boolean isSelected,
+ boolean expanded,
boolean leaf, int row)
{
- if (realEditor == null)
- realEditor = createTreeCellEditor();
-
- return realEditor.getTreeCellEditorComponent(tree, value, isSelected,
- expanded, leaf, row);
+ setTree(tree);
+ lastRow = row;
+ determineOffset(tree, value, isSelected, expanded, leaf, row);
+ if (editingComponent != null)
+ editingContainer.remove(editingComponent);
+
+ editingComponent = realEditor.getTreeCellEditorComponent(tree, value,
+ isSelected,
+ expanded, leaf,
+ row);
+ Font f = getFont();
+ if (f == null)
+ {
+ if (renderer != null)
+ f = renderer.getFont();
+ if (f == null)
+ f = tree.getFont();
+ }
+ editingContainer.setFont(f);
+ prepareForEditing();
+ return editingContainer;
}
/**
* Returns the value currently being edited (requests it from the
- * {@link realEditor}.
+ * {@link #realEditor}.
*
* @return the value currently being edited
*/
@@ -535,16 +516,48 @@ public class DefaultTreeCellEditor
* @return true if editing can be started
*/
public boolean isCellEditable(EventObject event)
- {
- if (editingComponent == null)
- configureEditingComponent(tree, renderer, realEditor);
-
- if (editingComponent != null && realEditor.isCellEditable(event))
+ {
+ boolean ret = false;
+ boolean ed = false;
+ if (event != null)
{
- prepareForEditing();
- return true;
+ if (event.getSource() instanceof JTree)
+ {
+ setTree((JTree) event.getSource());
+ if (event instanceof MouseEvent)
+ {
+ MouseEvent me = (MouseEvent) event;
+ TreePath path = tree.getPathForLocation(me.getX(), me.getY());
+ ed = lastPath != null && path != null && lastPath.equals(path);
+ if (path != null)
+ {
+ lastRow = tree.getRowForPath(path);
+ Object val = path.getLastPathComponent();
+ boolean isSelected = tree.isRowSelected(lastRow);
+ boolean isExpanded = tree.isExpanded(path);
+ TreeModel m = tree.getModel();
+ boolean isLeaf = m.isLeaf(val);
+ determineOffset(tree, val, isSelected, isExpanded, isLeaf,
+ lastRow);
+ }
+ }
+ }
}
- return false;
+ if (! realEditor.isCellEditable(event))
+ ret = false;
+ else
+ {
+ if (canEditImmediately(event))
+ ret = true;
+ else if (ed && shouldStartEditingTimer(event))
+ startEditingTimer();
+ else if (timer != null && timer.isRunning())
+ timer.stop();
+ }
+ if (ret)
+ prepareForEditing();
+ return ret;
+
}
/**
@@ -567,14 +580,13 @@ public class DefaultTreeCellEditor
*/
public boolean stopCellEditing()
{
- if (editingComponent != null)
+ boolean ret = false;
+ if (realEditor.stopCellEditing())
{
- stopEditingTimer();
- tree.stopEditing();
- editingComponent = null;
- return true;
+ finish();
+ ret = true;
}
- return false;
+ return ret;
}
/**
@@ -583,21 +595,15 @@ public class DefaultTreeCellEditor
*/
public void cancelCellEditing()
{
- if (editingComponent != null)
- {
- tree.cancelEditing();
- editingComponent = null;
- }
- stopEditingTimer();
+ realEditor.cancelCellEditing();
+ finish();
}
-
- /**
- * Stop the editing timer, if it is installed and running.
- */
- private void stopEditingTimer()
+
+ private void finish()
{
- if (timer != null && timer.isRunning())
- timer.stop();
+ if (editingComponent != null)
+ editingContainer.remove(editingComponent);
+ editingComponent = null;
}
/**
@@ -640,10 +646,18 @@ public class DefaultTreeCellEditor
*/
public void valueChanged(TreeSelectionEvent e)
{
- tPath = lastPath;
- lastPath = e.getNewLeadSelectionPath();
- lastRow = tree.getRowForPath(lastPath);
- stopCellEditing();
+ if (tree != null)
+ {
+ if (tree.getSelectionCount() == 1)
+ lastPath = tree.getSelectionPath();
+ else
+ lastPath = null;
+ }
+ // TODO: We really should do the following here, but can't due
+ // to buggy DefaultTreeSelectionModel. This selection model
+ // should only fire if the selection actually changes.
+// if (timer != null)
+// timer.stop();
}
/**
@@ -653,6 +667,8 @@ public class DefaultTreeCellEditor
*/
public void actionPerformed(ActionEvent e)
{
+ if (tree != null && lastPath != null)
+ tree.startEditingAtPath(lastPath);
}
/**
@@ -664,7 +680,17 @@ public class DefaultTreeCellEditor
*/
protected void setTree(JTree newTree)
{
- tree = newTree;
+ if (tree != newTree)
+ {
+ if (tree != null)
+ tree.removeTreeSelectionListener(this);
+ tree = newTree;
+ if (tree != null)
+ tree.addTreeSelectionListener(this);
+
+ if (timer != null)
+ timer.stop();
+ }
}
/**
@@ -675,10 +701,14 @@ public class DefaultTreeCellEditor
*/
protected boolean shouldStartEditingTimer(EventObject event)
{
- if ((event instanceof MouseEvent) &&
- ((MouseEvent) event).getClickCount() == 1)
- return true;
- return false;
+ boolean ret = false;
+ if (event instanceof MouseEvent)
+ {
+ MouseEvent me = (MouseEvent) event;
+ ret = SwingUtilities.isLeftMouseButton(me) && me.getClickCount() == 1
+ && inHitRegion(me.getX(), me.getY());
+ }
+ return ret;
}
/**
@@ -686,8 +716,12 @@ public class DefaultTreeCellEditor
*/
protected void startEditingTimer()
{
- if (timer != null)
- timer.start();
+ if (timer == null)
+ {
+ timer = new Timer(1200, this);
+ timer.setRepeats(false);
+ }
+ timer.start();
}
/**
@@ -723,7 +757,6 @@ public class DefaultTreeCellEditor
protected boolean inHitRegion(int x, int y)
{
Rectangle bounds = tree.getPathBounds(lastPath);
-
return bounds.contains(x, y);
}
@@ -739,13 +772,24 @@ public class DefaultTreeCellEditor
protected void determineOffset(JTree tree, Object value, boolean isSelected,
boolean expanded, boolean leaf, int row)
{
- renderer.getTreeCellRendererComponent(tree, value, isSelected, expanded,
- leaf, row, true);
- Icon c = renderer.getIcon();
- if (c != null)
- offset = renderer.getIconTextGap() + c.getIconWidth();
+ if (renderer != null)
+ {
+ if (leaf)
+ editingIcon = renderer.getLeafIcon();
+ else if (expanded)
+ editingIcon = renderer.getOpenIcon();
+ else
+ editingIcon = renderer.getClosedIcon();
+ if (editingIcon != null)
+ offset = renderer.getIconTextGap() + editingIcon.getIconWidth();
+ else
+ offset = renderer.getIconTextGap();
+ }
else
- offset = 0;
+ {
+ editingIcon = null;
+ offset = 0;
+ }
}
/**
@@ -754,8 +798,8 @@ public class DefaultTreeCellEditor
*/
protected void prepareForEditing()
{
- editingContainer.removeAll();
- editingContainer.add(editingComponent);
+ if (editingComponent != null)
+ editingContainer.add(editingComponent);
}
/**
@@ -776,10 +820,10 @@ public class DefaultTreeCellEditor
*/
protected TreeCellEditor createTreeCellEditor()
{
- DefaultCellEditor editor = new DefaultCellEditor(new DefaultTreeCellEditor.DefaultTextField(
- UIManager.getBorder("Tree.selectionBorder")));
- editor.addCellEditorListener(new RealEditorListener());
- editor.setClickCountToStart(CLICK_COUNT_TO_START);
+ Border border = UIManager.getBorder("Tree.editorBorder");
+ JTextField tf = new DefaultTreeCellEditor.DefaultTextField(border);
+ DefaultCellEditor editor = new DefaultCellEditor(tf);
+ editor.setClickCountToStart(1);
realEditor = editor;
return editor;
}
diff --git a/libjava/classpath/javax/swing/tree/DefaultTreeCellRenderer.java b/libjava/classpath/javax/swing/tree/DefaultTreeCellRenderer.java
index e120b71c167..3766485abdb 100644
--- a/libjava/classpath/javax/swing/tree/DefaultTreeCellRenderer.java
+++ b/libjava/classpath/javax/swing/tree/DefaultTreeCellRenderer.java
@@ -77,7 +77,7 @@ public class DefaultTreeCellRenderer
protected boolean hasFocus;
/**
- * drawsFocusBorderAroundIcon // FIXME: is this used?
+ * Indicates if the focus border is also drawn around the icon.
*/
private boolean drawsFocusBorderAroundIcon;
@@ -152,6 +152,8 @@ public class DefaultTreeCellRenderer
setBackgroundNonSelectionColor(UIManager.getColor("Tree.textBackground"));
setBackgroundSelectionColor(UIManager.getColor("Tree.selectionBackground"));
setBorderSelectionColor(UIManager.getColor("Tree.selectionBorderColor"));
+ Object val = UIManager.get("Tree.drawsFocusBorderAroundIcon");
+ drawsFocusBorderAroundIcon = val != null && ((Boolean) val).booleanValue();
}
/**
@@ -499,67 +501,75 @@ public class DefaultTreeCellRenderer
*/
public void paint(Graphics g)
{
- // paint background
- Rectangle vr = new Rectangle();
- Rectangle ir = new Rectangle();
- Rectangle tr = new Rectangle();
-
- Insets insets = new Insets(0, 0, 0, 0);
- Border border = UIManager.getBorder("Tree.selectionBorder");
- if (border != null)
- insets = border.getBorderInsets(this);
-
- FontMetrics fm = getToolkit().getFontMetrics(getFont());
- SwingUtilities.layoutCompoundLabel((JLabel) this, fm, getText(),
- getIcon(), getVerticalAlignment(),
- getHorizontalAlignment(),
- getVerticalTextPosition(),
- getHorizontalTextPosition(), vr, ir, tr,
- getIconTextGap());
-
- // Reusing one rectangle.
- Rectangle bounds = getBounds(ir);
-
- bounds.x = tr.x - insets.left;
- bounds.width = tr.width + insets.left + insets.right;
-
- g.setColor(super.getBackground());
- g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
+ // Determine background color.
+ Color bgColor;
+ if (selected)
+ bgColor = getBackgroundSelectionColor();
+ else
+ {
+ bgColor = getBackgroundNonSelectionColor();
+ if (bgColor == null)
+ bgColor = getBackground();
+ }
+ // Paint background.
+ int xOffset = -1;
+ if (bgColor != null)
+ {
+ Icon i = getIcon();
+ xOffset = getXOffset();
+ g.setColor(bgColor);
+ g.fillRect(xOffset, 0, getWidth() - xOffset, getHeight());
+ }
- super.paint(g);
-
- // Paint the border of the focused element only (lead selection)
if (hasFocus)
{
- Color b = getBorderSelectionColor();
- if (b != null)
- {
- g.setColor(b);
- g.drawRect(bounds.x, bounds.y, bounds.width, bounds.height - 1);
- }
+ if (drawsFocusBorderAroundIcon)
+ xOffset = 0;
+ else if (xOffset == -1)
+ xOffset = getXOffset();
+ paintFocus(g, xOffset, 0, getWidth() - xOffset, getHeight());
+ }
+ super.paint(g);
+ }
+
+ /**
+ * Paints the focus indicator.
+ */
+ private void paintFocus(Graphics g, int x, int y, int w, int h)
+ {
+ Color col = getBorderSelectionColor();
+ if (col != null)
+ {
+ g.setColor(col);
+ g.drawRect(x, y, w - 1, h - 1);
}
}
/**
+ * Determines the X offset of the label that is caused by
+ * the icon.
+ *
+ * @return the X offset of the label
+ */
+ private int getXOffset()
+ {
+ Icon i = getIcon();
+ int offs = 0;
+ if (i != null && getText() != null)
+ offs = i.getIconWidth() + Math.max(0, getIconTextGap() - 1);
+ return offs;
+ }
+
+ /**
* Returns the preferred size of the cell.
*
* @return The preferred size of the cell.
*/
public Dimension getPreferredSize()
{
- Rectangle vr = new Rectangle();
- Rectangle ir = new Rectangle();
- Rectangle tr = new Rectangle();
-
- FontMetrics fm = getToolkit().getFontMetrics(getFont());
- SwingUtilities.layoutCompoundLabel((JLabel) this, fm, getText(),
- getIcon(), getVerticalAlignment(),
- getHorizontalAlignment(),
- getVerticalTextPosition(),
- getHorizontalTextPosition(), vr, ir, tr,
- getIconTextGap());
- Rectangle cr = ir.union(tr);
- return new Dimension(cr.width, cr.height);
+ Dimension size = super.getPreferredSize();
+ size.width += 3;
+ return size;
}
/**
diff --git a/libjava/classpath/javax/swing/tree/DefaultTreeModel.java b/libjava/classpath/javax/swing/tree/DefaultTreeModel.java
index 5819d15b627..afee7ea22fa 100644
--- a/libjava/classpath/javax/swing/tree/DefaultTreeModel.java
+++ b/libjava/classpath/javax/swing/tree/DefaultTreeModel.java
@@ -210,17 +210,32 @@ public class DefaultTreeModel
}
/**
- * isLeaf
+ * Returns if the specified node is a leaf or not. When
+ * {@link #asksAllowsChildren} is true, then this checks if the TreeNode
+ * allows children, otherwise it returns the TreeNode's <code>leaf</code>
+ * property.
*
- * @param node TODO
- * @return boolean
+ * @param node the node to check
+ *
+ * @return boolean <code>true</code> if the node is a leaf node,
+ * <code>false</code> otherwise
+ *
+ * @throws ClassCastException if the specified node is not a
+ * <code>TreeNode</code> instance
+ *
+ * @see TreeNode#getAllowsChildren()
+ * @see TreeNode#isLeaf()
*/
public boolean isLeaf(Object node)
{
- if (node instanceof TreeNode)
- return ((TreeNode) node).isLeaf();
+ // The RI throws a ClassCastException when node isn't a TreeNode, so do we.
+ TreeNode treeNode = (TreeNode) node;
+ boolean leaf;
+ if (asksAllowsChildren)
+ leaf = ! treeNode.getAllowsChildren();
else
- return true;
+ leaf = treeNode.isLeaf();
+ return leaf;
}
/**
@@ -600,7 +615,7 @@ public class DefaultTreeModel
*
* @since 1.3
*/
- public EventListener[] getListeners(Class listenerType)
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
{
return listenerList.getListeners(listenerType);
}
diff --git a/libjava/classpath/javax/swing/tree/DefaultTreeSelectionModel.java b/libjava/classpath/javax/swing/tree/DefaultTreeSelectionModel.java
index 0684ef76659..3d9c67728bd 100644
--- a/libjava/classpath/javax/swing/tree/DefaultTreeSelectionModel.java
+++ b/libjava/classpath/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;
- }
}
/**
@@ -638,7 +776,7 @@ public class DefaultTreeSelectionModel
* @return an array of listeners
* @since 1.3
*/
- public EventListener[] getListeners(Class listenerType)
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
{
return listenerList.getListeners(listenerType);
}
@@ -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/libjava/classpath/javax/swing/tree/FixedHeightLayoutCache.java b/libjava/classpath/javax/swing/tree/FixedHeightLayoutCache.java
index a699a6c9f21..dff9298e8f5 100644
--- a/libjava/classpath/javax/swing/tree/FixedHeightLayoutCache.java
+++ b/libjava/classpath/javax/swing/tree/FixedHeightLayoutCache.java
@@ -480,7 +480,7 @@ public class FixedHeightLayoutCache
* @param parentPath the parent path
* @return the enumeration over pathes
*/
- public Enumeration getVisiblePathsFrom(TreePath parentPath)
+ public Enumeration<TreePath> getVisiblePathsFrom(TreePath parentPath)
{
if (dirty)
update();
diff --git a/libjava/classpath/javax/swing/tree/VariableHeightLayoutCache.java b/libjava/classpath/javax/swing/tree/VariableHeightLayoutCache.java
index 0a787f7ca8c..8c70c13afd2 100644
--- a/libjava/classpath/javax/swing/tree/VariableHeightLayoutCache.java
+++ b/libjava/classpath/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;
}
@@ -339,25 +351,21 @@ public class VariableHeightLayoutCache
return null;
if (dirty)
update();
+
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;
}
/**
@@ -370,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;
}
/**
@@ -390,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)
@@ -451,8 +464,8 @@ public class VariableHeightLayoutCache
{
if (y < r.y)
return r.y - y;
- else if (y > r.y + r.height)
- return y - (r.y + r.height);
+ else if (y > r.y + r.height - 1)
+ return y - (r.y + r.height - 1);
else
return 0;
}
@@ -468,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());
@@ -481,7 +494,7 @@ public class VariableHeightLayoutCache
* @param parentPath the parent path
* @return the enumeration over pathes
*/
- public Enumeration getVisiblePathsFrom(TreePath parentPath)
+ public Enumeration<TreePath> getVisiblePathsFrom(TreePath parentPath)
{
if (dirty)
update();
@@ -493,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();
@@ -558,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;
}
}
@@ -590,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;
}
/**
@@ -614,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;
+ }
}
diff --git a/libjava/classpath/javax/swing/undo/CompoundEdit.java b/libjava/classpath/javax/swing/undo/CompoundEdit.java
index e1cfbb619b3..fbff2a26418 100644
--- a/libjava/classpath/javax/swing/undo/CompoundEdit.java
+++ b/libjava/classpath/javax/swing/undo/CompoundEdit.java
@@ -1,5 +1,5 @@
/* CompoundEdit.java -- Combines multiple UndoableEdits.
- Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
+ Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -75,7 +75,7 @@ public class CompoundEdit
* The <code>UndoableEdit</code>s being combined into a compound
* editing action.
*/
- protected Vector edits;
+ protected Vector<UndoableEdit> edits;
/**
@@ -92,7 +92,7 @@ public class CompoundEdit
*/
public CompoundEdit()
{
- edits = new Vector();
+ edits = new Vector<UndoableEdit>();
inProgress = true;
}
@@ -118,7 +118,7 @@ public class CompoundEdit
super.undo();
for (int i = edits.size() - 1; i >= 0; i--)
- ((UndoableEdit) edits.elementAt(i)).undo();
+ edits.elementAt(i).undo();
}
@@ -143,7 +143,7 @@ public class CompoundEdit
super.redo();
for (int i = 0; i < edits.size(); i++)
- ((UndoableEdit) edits.elementAt(i)).redo();
+ edits.elementAt(i).redo();
}
@@ -156,7 +156,7 @@ public class CompoundEdit
if (edits.size() == 0)
return null;
else
- return (UndoableEdit) edits.elementAt(edits.size() - 1);
+ return edits.elementAt(edits.size() - 1);
}
@@ -172,7 +172,7 @@ public class CompoundEdit
public void die()
{
for (int i = edits.size() - 1; i >= 0; i--)
- ((UndoableEdit) edits.elementAt(i)).die();
+ edits.elementAt(i).die();
super.die();
}
@@ -316,7 +316,7 @@ public class CompoundEdit
public boolean isSignificant()
{
for (int i = edits.size() - 1; i >= 0; i--)
- if (((UndoableEdit) edits.elementAt(i)).isSignificant())
+ if (edits.elementAt(i).isSignificant())
return true;
return false;
diff --git a/libjava/classpath/javax/swing/undo/StateEdit.java b/libjava/classpath/javax/swing/undo/StateEdit.java
index 326abea1f4e..91fc88faa60 100644
--- a/libjava/classpath/javax/swing/undo/StateEdit.java
+++ b/libjava/classpath/javax/swing/undo/StateEdit.java
@@ -121,14 +121,14 @@ public class StateEdit
* The state of <code>object</code> at the time of constructing
* this <code>StateEdit</code>.
*/
- protected Hashtable preState;
+ protected Hashtable<Object, Object> preState;
/**
* The state of <code>object</code> at the time when {@link #end()}
* was called.
*/
- protected Hashtable postState;
+ protected Hashtable<Object, Object> postState;
/**
diff --git a/libjava/classpath/javax/swing/undo/StateEditable.java b/libjava/classpath/javax/swing/undo/StateEditable.java
index 459025be7da..7e6cc97856f 100644
--- a/libjava/classpath/javax/swing/undo/StateEditable.java
+++ b/libjava/classpath/javax/swing/undo/StateEditable.java
@@ -100,7 +100,7 @@ public interface StateEditable
* @param state a hash table containing the relevant state
* information.
*/
- void restoreState(Hashtable state);
+ void restoreState(Hashtable<?, ?> state);
/**
@@ -110,5 +110,5 @@ public interface StateEditable
* @param state a hash table for storing relevant state
* information.
*/
- void storeState(Hashtable state);
+ void storeState(Hashtable<Object, Object> state);
}
diff --git a/libjava/classpath/javax/swing/undo/UndoableEditSupport.java b/libjava/classpath/javax/swing/undo/UndoableEditSupport.java
index 6d7bbea0728..b5a93341954 100644
--- a/libjava/classpath/javax/swing/undo/UndoableEditSupport.java
+++ b/libjava/classpath/javax/swing/undo/UndoableEditSupport.java
@@ -69,7 +69,8 @@ public class UndoableEditSupport
/**
* The currently registered listeners.
*/
- protected Vector listeners = new Vector();
+ protected Vector<UndoableEditListener> listeners =
+ new Vector<UndoableEditListener>();
/**
@@ -148,7 +149,7 @@ public class UndoableEditSupport
public synchronized UndoableEditListener[] getUndoableEditListeners()
{
UndoableEditListener[] result = new UndoableEditListener[listeners.size()];
- return (UndoableEditListener[]) listeners.toArray(result);
+ return listeners.toArray(result);
}