aboutsummaryrefslogtreecommitdiff
path: root/libjava/classpath/javax/swing/text
diff options
context:
space:
mode:
authorMark Wielaard <mark@gcc.gnu.org>2006-01-17 18:09:40 +0000
committerMark Wielaard <mark@gcc.gnu.org>2006-01-17 18:09:40 +0000
commit2127637945ea6b763966398130e0770fa993c860 (patch)
treec976ca91e3ef0bda3b34b37c0195145638d8d08e /libjava/classpath/javax/swing/text
parentbcb36c3e02e3bd2843aad1b9888513dfb5d6e337 (diff)
downloadgcc-2127637945ea6b763966398130e0770fa993c860.zip
gcc-2127637945ea6b763966398130e0770fa993c860.tar.gz
gcc-2127637945ea6b763966398130e0770fa993c860.tar.bz2
Imported GNU Classpath 0.20
Imported GNU Classpath 0.20 * Makefile.am (AM_CPPFLAGS): Add classpath/include. * java/nio/charset/spi/CharsetProvider.java: New override file. * java/security/Security.java: Likewise. * sources.am: Regenerated. * Makefile.in: Likewise. From-SVN: r109831
Diffstat (limited to 'libjava/classpath/javax/swing/text')
-rw-r--r--libjava/classpath/javax/swing/text/AbstractDocument.java29
-rw-r--r--libjava/classpath/javax/swing/text/ComponentView.java30
-rw-r--r--libjava/classpath/javax/swing/text/CompositeView.java30
-rw-r--r--libjava/classpath/javax/swing/text/DefaultCaret.java81
-rw-r--r--libjava/classpath/javax/swing/text/DefaultEditorKit.java79
-rw-r--r--libjava/classpath/javax/swing/text/DefaultFormatter.java2
-rw-r--r--libjava/classpath/javax/swing/text/DefaultFormatterFactory.java280
-rw-r--r--libjava/classpath/javax/swing/text/DefaultStyledDocument.java1271
-rw-r--r--libjava/classpath/javax/swing/text/FlowView.java31
-rw-r--r--libjava/classpath/javax/swing/text/GapContent.java49
-rw-r--r--libjava/classpath/javax/swing/text/GlyphView.java30
-rw-r--r--libjava/classpath/javax/swing/text/IconView.java30
-rw-r--r--libjava/classpath/javax/swing/text/InternationalFormatter.java4
-rw-r--r--libjava/classpath/javax/swing/text/JTextComponent.java35
-rw-r--r--libjava/classpath/javax/swing/text/MaskFormatter.java583
-rw-r--r--libjava/classpath/javax/swing/text/NumberFormatter.java86
-rw-r--r--libjava/classpath/javax/swing/text/PasswordView.java38
-rw-r--r--libjava/classpath/javax/swing/text/PlainDocument.java40
-rw-r--r--libjava/classpath/javax/swing/text/PlainView.java34
-rw-r--r--libjava/classpath/javax/swing/text/StyleContext.java5
-rw-r--r--libjava/classpath/javax/swing/text/StyledEditorKit.java33
-rw-r--r--libjava/classpath/javax/swing/text/TableView.java465
-rw-r--r--libjava/classpath/javax/swing/text/Utilities.java19
-rw-r--r--libjava/classpath/javax/swing/text/View.java40
-rw-r--r--libjava/classpath/javax/swing/text/WrappedPlainView.java30
-rw-r--r--libjava/classpath/javax/swing/text/html/BlockView.java301
-rw-r--r--libjava/classpath/javax/swing/text/html/CSS.java3
-rw-r--r--libjava/classpath/javax/swing/text/html/CSSParser.java568
-rw-r--r--libjava/classpath/javax/swing/text/html/HTMLDocument.java1337
-rw-r--r--libjava/classpath/javax/swing/text/html/HTMLEditorKit.java945
-rw-r--r--libjava/classpath/javax/swing/text/html/StyleSheet.java937
-rw-r--r--libjava/classpath/javax/swing/text/html/default.css378
32 files changed, 7158 insertions, 665 deletions
diff --git a/libjava/classpath/javax/swing/text/AbstractDocument.java b/libjava/classpath/javax/swing/text/AbstractDocument.java
index a324425..c735388 100644
--- a/libjava/classpath/javax/swing/text/AbstractDocument.java
+++ b/libjava/classpath/javax/swing/text/AbstractDocument.java
@@ -541,6 +541,9 @@ public abstract class AbstractDocument implements Document, Serializable
writeLock();
UndoableEdit undo = content.insertString(offset, text);
+ if (undo != null)
+ event.addEdit(undo);
+
insertUpdate(event, attributes);
writeUnlock();
@@ -1326,7 +1329,14 @@ public abstract class AbstractDocument implements Document, Serializable
*/
public Object getAttribute(Object key)
{
- return attributes.getAttribute(key);
+ Object result = attributes.getAttribute(key);
+ if (result == null && element_parent != null)
+ {
+ AttributeSet parentSet = element_parent.getAttributes();
+ if (parentSet != null)
+ result = parentSet.getAttribute(key);
+ }
+ return result;
}
/**
@@ -1809,6 +1819,12 @@ public abstract class AbstractDocument implements Document, Serializable
Hashtable changes;
/**
+ * Indicates if this event has been modified or not. This is used to
+ * determine if this event is thrown.
+ */
+ boolean modified;
+
+ /**
* Creates a new <code>DefaultDocumentEvent</code>.
*
* @param offset the starting offset of the change
@@ -1822,6 +1838,7 @@ public abstract class AbstractDocument implements Document, Serializable
this.length = length;
this.type = type;
changes = new Hashtable();
+ modified = false;
}
/**
@@ -1836,6 +1853,7 @@ public abstract class AbstractDocument implements Document, Serializable
// XXX - Fully qualify ElementChange to work around gcj bug #2499.
if (edit instanceof DocumentEvent.ElementChange)
{
+ modified = true;
DocumentEvent.ElementChange elEdit =
(DocumentEvent.ElementChange) edit;
changes.put(elEdit.getElement(), elEdit);
@@ -1896,6 +1914,15 @@ public abstract class AbstractDocument implements Document, Serializable
// XXX - Fully qualify ElementChange to work around gcj bug #2499.
return (DocumentEvent.ElementChange) changes.get(elem);
}
+
+ /**
+ * Returns a String description of the change event. This returns the
+ * toString method of the Vector of edits.
+ */
+ public String toString()
+ {
+ return edits.toString();
+ }
}
/**
diff --git a/libjava/classpath/javax/swing/text/ComponentView.java b/libjava/classpath/javax/swing/text/ComponentView.java
index 830dda3..2846f8b 100644
--- a/libjava/classpath/javax/swing/text/ComponentView.java
+++ b/libjava/classpath/javax/swing/text/ComponentView.java
@@ -264,34 +264,4 @@ public class ComponentView extends View
Element el = getElement();
return el.getStartOffset();
}
-
- /**
- * Returns the document position that is (visually) nearest to the given
- * document position <code>pos</code> in the given direction <code>d</code>.
- *
- * @param c the text component
- * @param pos the document position
- * @param b the bias for <code>pos</code>
- * @param d the direction, must be either {@link SwingConstants#NORTH},
- * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or
- * {@link SwingConstants#EAST}
- * @param biasRet an array of {@link Position.Bias} that can hold at least
- * one element, which is filled with the bias of the return position
- * on method exit
- *
- * @return the document position that is (visually) nearest to the given
- * document position <code>pos</code> in the given direction
- * <code>d</code>
- *
- * @throws BadLocationException if <code>pos</code> is not a valid offset in
- * the document model
- */
- public int getNextVisualPositionFrom(JTextComponent c, int pos,
- Position.Bias b, int d,
- Position.Bias[] biasRet)
- throws BadLocationException
- {
- // FIXME: Implement this method.
- throw new AssertionError("Not yet implemented");
- }
}
diff --git a/libjava/classpath/javax/swing/text/CompositeView.java b/libjava/classpath/javax/swing/text/CompositeView.java
index e6c2e4c..cd66452 100644
--- a/libjava/classpath/javax/swing/text/CompositeView.java
+++ b/libjava/classpath/javax/swing/text/CompositeView.java
@@ -661,34 +661,4 @@ public abstract class CompositeView
{
return false;
}
-
- /**
- * Returns the document position that is (visually) nearest to the given
- * document position <code>pos</code> in the given direction <code>d</code>.
- *
- * @param c the text component
- * @param pos the document position
- * @param b the bias for <code>pos</code>
- * @param d the direction, must be either {@link SwingConstants#NORTH},
- * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or
- * {@link SwingConstants#EAST}
- * @param biasRet an array of {@link Position.Bias} that can hold at least
- * one element, which is filled with the bias of the return position
- * on method exit
- *
- * @return the document position that is (visually) nearest to the given
- * document position <code>pos</code> in the given direction
- * <code>d</code>
- *
- * @throws BadLocationException if <code>pos</code> is not a valid offset in
- * the document model
- */
- public int getNextVisualPositionFrom(JTextComponent c, int pos,
- Position.Bias b, int d,
- Position.Bias[] biasRet)
- throws BadLocationException
- {
- // TODO: Implement this properly.
- throw new AssertionError("Not implemented yet.");
- }
}
diff --git a/libjava/classpath/javax/swing/text/DefaultCaret.java b/libjava/classpath/javax/swing/text/DefaultCaret.java
index 3ebeceb..776ef69 100644
--- a/libjava/classpath/javax/swing/text/DefaultCaret.java
+++ b/libjava/classpath/javax/swing/text/DefaultCaret.java
@@ -1,5 +1,5 @@
/* DefaultCaret.java --
- Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+ Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -430,18 +430,45 @@ public class DefaultCaret extends Rectangle
*/
public void focusGained(FocusEvent event)
{
- setVisible(true);
+ setVisible(true);
+ updateTimerStatus();
}
/**
* Sets the caret to <code>invisible</code>.
- *
+ *
* @param event the <code>FocusEvent</code>
*/
public void focusLost(FocusEvent event)
{
if (event.isTemporary() == false)
- setVisible(false);
+ {
+ setVisible(false);
+ // Stop the blinker, if running.
+ if (blinkTimer != null && blinkTimer.isRunning())
+ blinkTimer.stop();
+ }
+ }
+
+ /**
+ * Install (if not present) and start the timer, if the caret must blink. The
+ * caret does not blink if it is invisible, or the component is disabled or
+ * not editable.
+ */
+ private void updateTimerStatus()
+ {
+ if (textComponent.isEnabled() && textComponent.isEditable())
+ {
+ if (blinkTimer == null)
+ initBlinkTimer();
+ if (!blinkTimer.isRunning())
+ blinkTimer.start();
+ }
+ else
+ {
+ if (blinkTimer != null)
+ blinkTimer.stop();
+ }
}
/**
@@ -641,8 +668,10 @@ public class DefaultCaret extends Rectangle
}
catch (BadLocationException e)
{
- assert false : "Unexpected bad caret location: " + dot;
- return;
+ AssertionError ae;
+ ae = new AssertionError("Unexpected bad caret location: " + dot);
+ ae.initCause(e);
+ throw ae;
}
if (rect == null)
@@ -802,9 +831,12 @@ public class DefaultCaret extends Rectangle
public void setDot(int dot)
{
if (dot >= 0)
- {
+ {
+ Document doc = textComponent.getDocument();
+ if (doc != null)
+ this.dot = Math.min(dot, doc.getLength());
+ this.dot = Math.max(this.dot, 0);
this.mark = dot;
- this.dot = dot;
handleHighlight();
adjustVisibility(this);
appear();
@@ -833,13 +865,17 @@ public class DefaultCaret extends Rectangle
visible = true;
Rectangle area = null;
+ int dot = getDot();
try
{
- area = getComponent().modelToView(getDot());
+ area = getComponent().modelToView(dot);
}
- catch (BadLocationException ex)
+ catch (BadLocationException e)
{
- assert false : "Unexpected bad caret location: " + getDot();
+ AssertionError ae;
+ ae = new AssertionError("Unexpected bad caret location: " + dot);
+ ae.initCause(e);
+ throw ae;
}
if (area != null)
damage(area);
@@ -870,26 +906,19 @@ public class DefaultCaret extends Rectangle
if (v != visible)
{
visible = v;
- if (visible)
- if (textComponent.isEnabled() && textComponent.isEditable())
- {
- if (blinkTimer == null)
- initBlinkTimer();
- blinkTimer.start();
- }
- else
- {
- if (blinkTimer != null)
- blinkTimer.stop();
- }
+ updateTimerStatus();
Rectangle area = null;
+ int dot = getDot();
try
{
- area = getComponent().modelToView(getDot());
+ area = getComponent().modelToView(dot);
}
- catch (BadLocationException ex)
+ catch (BadLocationException e)
{
- assert false: "Unexpected bad caret location: " + getDot();
+ AssertionError ae;
+ ae = new AssertionError("Unexpected bad caret location: " + dot);
+ ae.initCause(e);
+ throw ae;
}
if (area != null)
damage(area);
diff --git a/libjava/classpath/javax/swing/text/DefaultEditorKit.java b/libjava/classpath/javax/swing/text/DefaultEditorKit.java
index 3b3fc1f..88094b8 100644
--- a/libjava/classpath/javax/swing/text/DefaultEditorKit.java
+++ b/libjava/classpath/javax/swing/text/DefaultEditorKit.java
@@ -38,8 +38,10 @@ exception statement from your version. */
package javax.swing.text;
+import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
+
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
@@ -112,8 +114,7 @@ public class DefaultEditorKit extends EditorKit
*/
public void actionPerformed(ActionEvent event)
{
- // FIXME: Implement me. Tookit.getSystemClipboard should be used
- // for that.
+ getTextComponent(event).copy();
}
}
@@ -144,8 +145,7 @@ public class DefaultEditorKit extends EditorKit
*/
public void actionPerformed(ActionEvent event)
{
- // FIXME: Implement me. Tookit.getSystemClipboard should be used
- // for that.
+ getTextComponent(event).cut();
}
}
@@ -174,8 +174,7 @@ public class DefaultEditorKit extends EditorKit
*/
public void actionPerformed(ActionEvent event)
{
- // FIXME: Implement me. Tookit.getSystemClipboard should be used
- // for that.
+ getTextComponent(event).paste();
}
}
@@ -216,19 +215,9 @@ public class DefaultEditorKit extends EditorKit
return;
JTextComponent t = getTextComponent(event);
- if (t != null)
- {
- try
- {
- t.getDocument().insertString(t.getCaret().getDot(),
- event.getActionCommand(), null);
- }
- catch (BadLocationException be)
- {
- // FIXME: we're not authorized to throw this.. swallow it?
- }
- }
- }
+ if (t != null && t.isEnabled() && t.isEditable())
+ t.replaceSelection(event.getActionCommand());
+ }
}
/**
@@ -309,7 +298,8 @@ public class DefaultEditorKit extends EditorKit
*/
public void actionPerformed(ActionEvent event)
{
- // FIXME: Implement this.
+ JTextComponent t = getTextComponent(event);
+ t.replaceSelection("\t");
}
}
@@ -710,6 +700,53 @@ public class DefaultEditorKit extends EditorKit
new InsertContentAction(),
new InsertTabAction(),
new PasteAction(),
+ new TextAction(beginLineAction)
+ {
+ public void actionPerformed(ActionEvent event)
+ {
+ JTextComponent t = getTextComponent(event);
+ try
+ {
+ // TODO: There is a more efficent solution, but
+ // viewToModel doesn't work properly.
+ Point p = t.modelToView(t.getCaret().getDot()).getLocation();
+ int cur = t.getCaretPosition();
+ int y = p.y;
+ while (y == p.y && cur > 0)
+ y = t.modelToView(--cur).getLocation().y;
+ if (cur != 0)
+ cur++;
+ t.setCaretPosition(cur);
+ }
+ catch (BadLocationException ble)
+ {
+ // Do nothing here.
+ }
+ }
+ },
+ new TextAction(endLineAction)
+ {
+ public void actionPerformed(ActionEvent event)
+ {
+ JTextComponent t = getTextComponent(event);
+ try
+ {
+ Point p = t.modelToView(t.getCaret().getDot()).getLocation();
+ int cur = t.getCaretPosition();
+ int y = p.y;
+ int length = t.getDocument().getLength();
+ while (y == p.y && cur < length)
+ y = t.modelToView(++cur).getLocation().y;
+ if (cur != length)
+ cur--;
+ t.setCaretPosition(cur);
+ }
+ catch (BadLocationException ble)
+ {
+ // Nothing to do here
+ }
+ }
+ },
new TextAction(deleteNextCharAction)
{
public void actionPerformed(ActionEvent event)
@@ -911,7 +948,7 @@ public class DefaultEditorKit extends EditorKit
content.append("\n");
}
- document.insertString(offset, content.toString(),
+ document.insertString(offset, content.substring(0, content.length() - 1),
SimpleAttributeSet.EMPTY);
}
diff --git a/libjava/classpath/javax/swing/text/DefaultFormatter.java b/libjava/classpath/javax/swing/text/DefaultFormatter.java
index f9e0f10..493699d 100644
--- a/libjava/classpath/javax/swing/text/DefaultFormatter.java
+++ b/libjava/classpath/javax/swing/text/DefaultFormatter.java
@@ -400,6 +400,8 @@ public class DefaultFormatter extends JFormattedTextField.AbstractFormatter
public String valueToString(Object value)
throws ParseException
{
+ if (value == null)
+ return "";
return value.toString();
}
diff --git a/libjava/classpath/javax/swing/text/DefaultFormatterFactory.java b/libjava/classpath/javax/swing/text/DefaultFormatterFactory.java
new file mode 100644
index 0000000..84a1676
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/DefaultFormatterFactory.java
@@ -0,0 +1,280 @@
+/* DefaultFormatterFactory.java -- FIXME: briefly describe file purpose
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text;
+
+import java.io.Serializable;
+
+import javax.swing.JFormattedTextField;
+import javax.swing.JFormattedTextField.AbstractFormatter;
+import javax.swing.JFormattedTextField.AbstractFormatterFactory;
+
+/**
+ * This class is Swing's only concrete implementation of
+ * JFormattedTextField.AbstractFormatterFactory. It holds several
+ * formatters and determines the best one to be used based on the
+ * passed-in value from the text field.
+ *
+ * @author Anthony Balkissoon abalkiss at redhat dot com
+ * @since 1.4
+ */
+public class DefaultFormatterFactory extends AbstractFormatterFactory implements
+ Serializable
+{
+ /**
+ * The default formatter.
+ **/
+ AbstractFormatter defaultFormatter;
+
+ /**
+ * The formatter to use when the JFormattedTextField has focus and either the
+ * value isn't null or the value is null but no <code>nullFormatter</code>
+ * has been specified.
+ */
+ AbstractFormatter editFormatter;
+
+ /**
+ * The formatter to use when the JFormattedTextField doesn't havefocus and
+ * either the value isn't null or the value is null but no
+ * <code>nullFormatter</code> has been specified.
+ */
+ AbstractFormatter displayFormatter;
+
+ /**
+ * The formatter to use when the value of the JFormattedTextField is null.
+ */
+ AbstractFormatter nullFormatter;
+
+ /**
+ * Creates a DefaultFormatterFactory with no formatters
+ */
+ public DefaultFormatterFactory()
+ {
+ // Nothing to be done here.
+ }
+
+ /**
+ * Creates a new DefaultFormatterFactory with the specified formatters.
+ * @param defaultFormat the formatter to use if no other appropriate non-null
+ * formatted can be found.
+ */
+ public DefaultFormatterFactory(AbstractFormatter defaultFormat)
+ {
+ defaultFormatter = defaultFormat;
+ }
+
+ /**
+ * Creates a new DefaultFormatterFactory with the specified formatters.
+ * @param defaultFormat the formatter to use if no other appropriate non-null
+ * formatted can be found.
+ * @param displayFormat the formatter to use if the JFormattedTextField
+ * doesn't have focus and either the value is not null or the value is null
+ * but no <code>nullFormatter</code> has been specified.
+ */
+ public DefaultFormatterFactory(AbstractFormatter defaultFormat,
+ AbstractFormatter displayFormat)
+ {
+ defaultFormatter = defaultFormat;
+ displayFormatter = displayFormat;
+ }
+
+ /**
+ * Creates a new DefaultFormatterFactory with the specified formatters.
+ * @param defaultFormat the formatter to use if no other appropriate non-null
+ * formatted can be found.
+ * @param displayFormat the formatter to use if the JFormattedTextField
+ * doesn't have focus and either the value is not null or the value is null
+ * but no <code>nullFormatter</code> has been specified.
+ * @param editFormat the formatter to use if the JFormattedTextField has
+ * focus and either the value is not null or the value is null but not
+ * <code>nullFormatter</code> has been specified.
+ */
+ public DefaultFormatterFactory(AbstractFormatter defaultFormat,
+ AbstractFormatter displayFormat,
+ AbstractFormatter editFormat)
+ {
+ defaultFormatter = defaultFormat;
+ displayFormatter = displayFormat;
+ editFormatter = editFormat;
+ }
+
+ /**
+ * Creates a new DefaultFormatterFactory with the specified formatters.
+ * @param defaultFormat the formatter to use if no other appropriate non-null
+ * formatted can be found.
+ * @param displayFormat the formatter to use if the JFormattedTextField
+ * doesn't have focus and either the value is not null or the value is null
+ * but no <code>nullFormatter</code> has been specified.
+ * @param editFormat the formatter to use if the JFormattedTextField has
+ * focus and either the value is not null or the value is null but not
+ * <code>nullFormatter</code> has been specified.
+ * @param nullFormat the formatter to use when the value of the
+ * JFormattedTextField is null.
+ */
+ public DefaultFormatterFactory(AbstractFormatter defaultFormat,
+ AbstractFormatter displayFormat,
+ AbstractFormatter editFormat,
+ AbstractFormatter nullFormat)
+ {
+ defaultFormatter = defaultFormat;
+ displayFormatter = displayFormat;
+ editFormatter = editFormat;
+ nullFormatter = nullFormat;
+ }
+
+ /**
+ * Returns the formatted to be used if no other appropriate non-null
+ * formatter can be found.
+ * @return the formatted to be used if no other appropriate non-null
+ * formatter can be found.
+ */
+ public AbstractFormatter getDefaultFormatter()
+ {
+ return defaultFormatter;
+ }
+
+ /**
+ * Sets the formatted to be used if no other appropriate non-null formatter
+ * can be found.
+ * @param defaultFormatter the formatted to be used if no other appropriate
+ * non-null formatter can be found.
+ */
+ public void setDefaultFormatter(AbstractFormatter defaultFormatter)
+ {
+ this.defaultFormatter = defaultFormatter;
+ }
+
+ /**
+ * Gets the <code>displayFormatter</code>. This is the formatter to use if
+ * the JFormattedTextField is not being edited and either the value is not
+ * null or the value is null and no <code>nullFormatter<code> has been
+ * specified.
+ * @return the formatter to use if
+ * the JFormattedTextField is not being edited and either the value is not
+ * null or the value is null and no <code>nullFormatter<code> has been
+ * specified.
+ */
+ public AbstractFormatter getDisplayFormatter()
+ {
+ return displayFormatter;
+ }
+
+ /**
+ * Sets the <code>displayFormatter</code>. This is the formatter to use if
+ * the JFormattedTextField is not being edited and either the value is not
+ * null or the value is null and no <code>nullFormatter<code> has been
+ * specified.
+ * @param displayFormatter the formatter to use.
+ */
+ public void setDisplayFormatter(AbstractFormatter displayFormatter)
+ {
+ this.displayFormatter = displayFormatter;
+ }
+
+ /**
+ * Gets the <code>editFormatter</code>. This is the formatter to use if the
+ * JFormattedTextField is being edited and either the value is not null or
+ * the value is null and no <code>nullFormatter<code> has been specified.
+ * @return the formatter to use if the JFormattedTextField is being edited
+ * and the value is not null or the value is null but no nullFormatted has
+ * been specified.
+ */
+ public AbstractFormatter getEditFormatter()
+ {
+ return editFormatter;
+ }
+
+ /**
+ * Sets the <code>editFormatter</code>. This is the formatter to use if the
+ * JFormattedTextField is being edited and either the value is not null or
+ * the value is null and no <code>nullFormatter<code> has been specified.
+ * @param editFormatter the formatter to use.
+ */
+ public void setEditFormatter(AbstractFormatter editFormatter)
+ {
+ this.editFormatter = editFormatter;
+ }
+
+ /**
+ * Gets the formatter to use if the value of the JFormattedTextField is null.
+ * @return the formatter to use for null values.
+ */
+ public AbstractFormatter getNullFormatter()
+ {
+ return nullFormatter;
+ }
+
+ /**
+ * Sets the <code>nullFormatter</code>. This is the formatter to use if the
+ * value of the JFormattedTextField is null.
+ * @param nullFormatter the formatter to use for null values.
+ */
+ public void setNullFormatter(AbstractFormatter nullFormatter)
+ {
+ this.nullFormatter = nullFormatter;
+ }
+
+ /**
+ * Returns the appropriate formatter based on the state of
+ * <code>tf</code>. If <code>tf<code> is null we return null, otherwise
+ * we return one of the following:
+ * 1. Returns <code>nullFormatter</code> if <code>tf.getValue()</code> is
+ * null and <code>nullFormatter</code> is not.
+ * 2. Returns <code>editFormatter</code> if <code>tf.hasFocus()</code> is
+ * true and <code>editFormatter</code> is not null.
+ * 3. Returns <code>displayFormatter</code> if <code>tf.hasFocus()</code> is
+ * false and <code>displayFormatter</code> is not null.
+ * 4. Otherwise returns <code>defaultFormatter</code>.
+ */
+ public AbstractFormatter getFormatter(JFormattedTextField tf)
+ {
+ if (tf == null)
+ return null;
+
+ if (tf.getValue() == null && nullFormatter != null)
+ return nullFormatter;
+
+ if (tf.hasFocus() && editFormatter != null)
+ return editFormatter;
+
+ if (!tf.hasFocus() && displayFormatter != null)
+ return displayFormatter;
+
+ return defaultFormatter;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/DefaultStyledDocument.java b/libjava/classpath/javax/swing/text/DefaultStyledDocument.java
index eb56bb0..46b8225 100644
--- a/libjava/classpath/javax/swing/text/DefaultStyledDocument.java
+++ b/libjava/classpath/javax/swing/text/DefaultStyledDocument.java
@@ -42,11 +42,13 @@ import java.awt.Color;
import java.awt.Font;
import java.io.Serializable;
import java.util.Enumeration;
+import java.util.Stack;
import java.util.Vector;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
+import javax.swing.event.UndoableEditEvent;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.UndoableEdit;
@@ -365,7 +367,6 @@ public class DefaultStyledDocument extends AbstractDocument
public String toString()
{
StringBuilder b = new StringBuilder();
- b.append('<');
switch (type)
{
case StartTagType:
@@ -427,6 +428,29 @@ public class DefaultStyledDocument extends AbstractDocument
/** Holds the length of structural changes. */
private int length;
+
+ /** Holds the end offset for structural changes. **/
+ private int endOffset;
+
+ /**
+ * The number of inserted end tags. This is a counter which always gets
+ * incremented when an end tag is inserted. This is evaluated before
+ * content insertion to go up the element stack.
+ */
+ private int numEndTags;
+
+ /**
+ * The number of inserted start tags. This is a counter which always gets
+ * incremented when an end tag is inserted. This is evaluated before
+ * content insertion to go up the element stack.
+ */
+ private int numStartTags;
+
+ /**
+ * The current position in the element tree. This is used for bulk inserts
+ * using ElementSpecs.
+ */
+ private Stack elementStack;
/**
* Holds fractured elements during insertion of end and start tags.
@@ -450,6 +474,7 @@ public class DefaultStyledDocument extends AbstractDocument
public ElementBuffer(Element root)
{
this.root = root;
+ elementStack = new Stack();
}
/**
@@ -463,6 +488,85 @@ public class DefaultStyledDocument extends AbstractDocument
}
/**
+ * Updates the element structure of the document in response to removal of
+ * content. It removes the affected {@link Element}s from the document
+ * structure.
+ *
+ * This method sets some internal parameters and delegates the work
+ * to {@link #removeUpdate}.
+ *
+ * @param offs the offset from which content is remove
+ * @param len the length of the removed content
+ * @param ev the document event that records the changes
+ */
+ public void remove(int offs, int len, DefaultDocumentEvent ev)
+ {
+ offset = offs;
+ length = len;
+ documentEvent = ev;
+ removeUpdate();
+ }
+
+ /**
+ * Updates the element structure of the document in response to removal of
+ * content. It removes the affected {@link Element}s from the document
+ * structure.
+ */
+ protected void removeUpdate()
+ {
+ int startParagraph = root.getElementIndex(offset);
+ int endParagraph = root.getElementIndex(offset + length);
+ Element[] empty = new Element[0];
+ int removeStart = -1;
+ int removeEnd = -1;
+ for (int i = startParagraph; i < endParagraph; i++)
+ {
+ Element paragraph = root.getElement(i);
+ int contentStart = paragraph.getElementIndex(offset);
+ int contentEnd = paragraph.getElementIndex(offset + length);
+ if (contentStart == paragraph.getStartOffset()
+ && contentEnd == paragraph.getEndOffset())
+ {
+ // In this case we only need to remove the whole paragraph. We
+ // do this in one go after this loop and only record the indices
+ // here.
+ if (removeStart == -1)
+ {
+ removeStart = i;
+ removeEnd = i;
+ }
+ else
+ removeEnd = i;
+ }
+ else
+ {
+ // In this case we remove a couple of child elements from this
+ // paragraph.
+ int removeLen = contentEnd - contentStart;
+ Element[] removed = new Element[removeLen];
+ for (int j = contentStart; j < contentEnd; j++)
+ removed[j] = paragraph.getElement(j);
+ ((BranchElement) paragraph).replace(contentStart, removeLen,
+ empty);
+ documentEvent.addEdit(new ElementEdit(paragraph, contentStart,
+ removed, empty));
+ }
+ }
+ // Now we remove paragraphs from the root that have been tagged for
+ // removal.
+ if (removeStart != -1)
+ {
+ int removeLen = removeEnd - removeStart;
+ Element[] removed = new Element[removeLen];
+ for (int i = removeStart; i < removeEnd; i++)
+ removed[i] = root.getElement(i);
+ ((BranchElement) root).replace(removeStart, removeLen, empty);
+ documentEvent.addEdit(new ElementEdit(root, removeStart, removed,
+ empty));
+ }
+ }
+
+ /**
* Modifies the element structure so that the specified interval starts
* and ends at an element boundary. Content and paragraph elements
* are split and created as necessary.
@@ -493,11 +597,50 @@ public class DefaultStyledDocument extends AbstractDocument
{
// Split up the element at the start offset if necessary.
Element el = getCharacterElement(offset);
- split(el, offset);
+ Element[] res = split(el, offset, 0);
+ BranchElement par = (BranchElement) el.getParentElement();
+ if (res[1] != null)
+ {
+ int index = par.getElementIndex(offset);
+ Element[] removed;
+ Element[] added;
+ if (res[0] == null)
+ {
+ removed = new Element[0];
+ added = new Element[]{ res[1] };
+ index++;
+ }
+ else
+ {
+ removed = new Element[]{ el };
+ added = new Element[]{ res[0], res[1] };
+ }
+ par.replace(index, removed.length, added);
+ addEdit(par, index, removed, added);
+ }
int endOffset = offset + length;
el = getCharacterElement(endOffset);
- split(el, endOffset);
+ res = split(el, endOffset, 0);
+ par = (BranchElement) el.getParentElement();
+ if (res[1] != null)
+ {
+ int index = par.getElementIndex(offset);
+ Element[] removed;
+ Element[] added;
+ if (res[1] == null)
+ {
+ removed = new Element[0];
+ added = new Element[]{ res[1] };
+ }
+ else
+ {
+ removed = new Element[]{ el };
+ added = new Element[]{ res[0], res[1] };
+ }
+ par.replace(index, removed.length, added);
+ addEdit(par, index, removed, added);
+ }
}
/**
@@ -505,42 +648,96 @@ public class DefaultStyledDocument extends AbstractDocument
*
* @param el the Element to possibly split
* @param offset the offset at which to possibly split
+ * @param space the amount of space to create between the splitted parts
+ *
+ * @return An array of elements which represent the split result. This
+ * array has two elements, the two parts of the split. The first
+ * element might be null, which means that the element which should
+ * be splitted can remain in place. The second element might also
+ * be null, which means that the offset is already at an element
+ * boundary and the element doesn't need to be splitted.
+ *
*/
- void split(Element el, int offset)
+ private Element[] split(Element el, int offset, int space)
{
- if (el instanceof AbstractElement)
+ // If we are at an element boundary, then return an empty array.
+ if ((offset == el.getStartOffset() || offset == el.getEndOffset())
+ && space == 0 && el.isLeaf())
+ return new Element[2];
+
+ // If the element is an instance of BranchElement, then we recursivly
+ // call this method to perform the split.
+ Element[] res = new Element[2];
+ if (el instanceof BranchElement)
{
- AbstractElement ael = (AbstractElement) el;
- int startOffset = ael.getStartOffset();
- int endOffset = ael.getEndOffset();
- int len = endOffset - startOffset;
- if (startOffset != offset && endOffset != offset)
+ int index = el.getElementIndex(offset);
+ Element child = el.getElement(index);
+ Element[] result = split(child, offset, space);
+ Element[] removed;
+ Element[] added;
+ Element[] newAdded;
+
+ int count = el.getElementCount();
+ if (!(result[1] == null))
{
- Element paragraph = ael.getParentElement();
- if (paragraph instanceof BranchElement)
+ // This is the case when we can keep the first element.
+ if (result[0] == null)
{
- BranchElement par = (BranchElement) paragraph;
- Element child1 = createLeafElement(par, ael, startOffset,
- offset);
- Element child2 = createLeafElement(par, ael, offset,
- endOffset);
- int index = par.getElementIndex(startOffset);
- Element[] add = new Element[]{ child1, child2 };
- par.replace(index, 1, add);
- documentEvent.addEdit(new ElementEdit(par, index,
- new Element[]{ el },
- add));
+ removed = new Element[count - index - 1];
+ newAdded = new Element[count - index - 1];
+ added = new Element[]{};
}
+ // This is the case when we may not keep the first element.
else
- throw new AssertionError("paragraph elements are expected to "
- + "be instances of "
- + "javax.swing.text.AbstractDocument.BranchElement");
+ {
+ removed = new Element[count - index];
+ newAdded = new Element[count - index];
+ added = new Element[]{result[0]};
+ }
+ newAdded[0] = result[1];
+ for (int i = index; i < count; i++)
+ {
+ Element el2 = el.getElement(i);
+ int ind = i - count + removed.length;
+ removed[ind] = el2;
+ if (ind != 0)
+ newAdded[ind] = el2;
+ }
+
+ ((BranchElement) el).replace(index, removed.length, added);
+ addEdit(el, index, removed, added);
+ BranchElement newPar =
+ (BranchElement) createBranchElement(el.getParentElement(),
+ el.getAttributes());
+ newPar.replace(0, 0, newAdded);
+ res = new Element[]{ null, newPar };
+ }
+ else
+ {
+ removed = new Element[count - index];
+ for (int i = index; i < count; ++i)
+ removed[i - index] = el.getElement(i);
+ added = new Element[0];
+ ((BranchElement) el).replace(index, removed.length,
+ added);
+ addEdit(el, index, removed, added);
+ BranchElement newPar =
+ (BranchElement) createBranchElement(el.getParentElement(),
+ el.getAttributes());
+ newPar.replace(0, 0, removed);
+ res = new Element[]{ null, newPar };
}
}
- else
- throw new AssertionError("content elements are expected to be "
- + "instances of "
- + "javax.swing.text.AbstractDocument.AbstractElement");
+ else if (el instanceof LeafElement)
+ {
+ BranchElement par = (BranchElement) el.getParentElement();
+ Element el1 = createLeafElement(par, el.getAttributes(),
+ el.getStartOffset(), offset);
+ Element el2 = createLeafElement(par, el.getAttributes(),
+ offset + space, el.getEndOffset());
+ res = new Element[]{ el1, el2 };
+ }
+ return res;
}
/**
@@ -560,9 +757,18 @@ public class DefaultStyledDocument extends AbstractDocument
public void insert(int offset, int length, ElementSpec[] data,
DefaultDocumentEvent ev)
{
+ if (length == 0)
+ return;
this.offset = offset;
this.length = length;
+ this.endOffset = offset + length;
documentEvent = ev;
+ // Push the root and the paragraph at offset onto the element stack.
+ elementStack.clear();
+ elementStack.push(root);
+ elementStack.push(root.getElement(root.getElementIndex(offset)));
+ numEndTags = 0;
+ numStartTags = 0;
insertUpdate(data);
}
@@ -573,202 +779,348 @@ public class DefaultStyledDocument extends AbstractDocument
* {@link #insert}.
*
* @param data the element specifications for the elements to be inserte
- */
+ */
protected void insertUpdate(ElementSpec[] data)
{
+ if (data[0].getType() == ElementSpec.EndTagType)
+ {
+ // fracture deepest child here
+ BranchElement paragraph = (BranchElement) elementStack.peek();
+ Element curr = paragraph.getParentElement();
+ int index = curr.getElementIndex(offset);
+ while (!curr.isLeaf())
+ {
+ index = curr.getElementIndex(offset);
+ curr = curr.getElement(index);
+ }
+ Element parent = curr.getParentElement();
+ Element newEl1 = createLeafElement(parent,
+ curr.getAttributes(),
+ curr.getStartOffset(), offset);
+ Element grandParent = parent.getParentElement();
+ BranchElement nextBranch =
+ (BranchElement) grandParent.getElement
+ (grandParent.getElementIndex(parent.getEndOffset()));
+ Element firstLeaf = nextBranch.getElement(0);
+ while (!firstLeaf.isLeaf())
+ {
+ firstLeaf = firstLeaf.getElement(0);
+ }
+ BranchElement parent2 = (BranchElement) firstLeaf.getParentElement();
+ Element newEl2 =
+ createLeafElement(parent2,
+ firstLeaf.getAttributes(),
+ offset, firstLeaf.getEndOffset());
+ parent2.replace(0, 1, new Element[] { newEl2 });
+
+
+ ((BranchElement) parent).
+ replace(index, 1, new Element[] { newEl1 });
+ }
+
for (int i = 0; i < data.length; i++)
{
+ BranchElement paragraph = (BranchElement) elementStack.peek();
switch (data[i].getType())
{
case ElementSpec.StartTagType:
- insertStartTag(data[i]);
+ switch (data[i].getDirection())
+ {
+ case ElementSpec.JoinFractureDirection:
+ insertFracture(data[i]);
+ break;
+ case ElementSpec.JoinNextDirection:
+ int index = paragraph.getElementIndex(offset);
+ elementStack.push(paragraph.getElement(index));
+ break;
+ case ElementSpec.OriginateDirection:
+ Element current = (Element) elementStack.peek();
+ Element newParagraph =
+ insertParagraph((BranchElement) current, offset);
+ elementStack.push(newParagraph);
+ break;
+ default:
+ break;
+ }
break;
case ElementSpec.EndTagType:
- insertEndTag(data[i]);
+ elementStack.pop();
break;
- default:
+ case ElementSpec.ContentType:
insertContentTag(data[i]);
break;
}
}
+ endEdit();
}
/**
- * Insert a new paragraph after the paragraph at the current position.
- *
- * @param tag the element spec that describes the element to be inserted
+ * Finishes an insertion by possibly evaluating the outstanding start and
+ * end tags. However, this is only performed if the event has received any
+ * modifications.
+ */
+ private void endEdit()
+ {
+ if (documentEvent.modified)
+ prepareContentInsertion();
+ }
+
+ /**
+ * Evaluates the number of inserted end tags and performs the corresponding
+ * structural changes.
*/
- void insertStartTag(ElementSpec tag)
+ private void prepareContentInsertion()
{
- BranchElement root = (BranchElement) getDefaultRootElement();
- int index = root.getElementIndex(offset);
- if (index == -1)
- index = 0;
-
- BranchElement newParagraph =
- (BranchElement) createBranchElement(root, tag.getAttributes());
- newParagraph.setResolveParent(getStyle(StyleContext.DEFAULT_STYLE));
-
- // Add new paragraph into document structure.
- Element[] added = new Element[]{newParagraph};
- root.replace(index + 1, 0, added);
- ElementEdit edit = new ElementEdit(root, index + 1, new Element[0],
- added);
- documentEvent.addEdit(edit);
-
- // Maybe add fractured elements.
- if (tag.getDirection() == ElementSpec.JoinFractureDirection)
+ while (numEndTags > 0)
+ {
+ elementStack.pop();
+ numEndTags--;
+ }
+
+ while (numStartTags > 0)
{
- Element[] newFracture = new Element[fracture.length];
- for (int i = 0; i < fracture.length; i++)
+ Element current = (Element) elementStack.peek();
+ Element newParagraph =
+ insertParagraph((BranchElement) current, offset);
+ elementStack.push(newParagraph);
+ numStartTags--;
+ }
+ }
+
+ private Element insertParagraph(BranchElement par, int offset)
+ {
+ Element current = par.getElement(par.getElementIndex(offset));
+ Element[] res = split(current, offset, 0);
+ int index = par.getElementIndex(offset);
+ Element ret;
+ if (res[1] != null)
+ {
+ Element[] removed;
+ Element[] added;
+ if (res[0] == null)
{
- Element oldLeaf = fracture[i];
- Element newLeaf = createLeafElement(newParagraph,
- oldLeaf.getAttributes(),
- oldLeaf.getStartOffset(),
- oldLeaf.getEndOffset());
- newFracture[i] = newLeaf;
+ removed = new Element[0];
+ if (res[1] instanceof BranchElement)
+ {
+ added = new Element[]{ res[1] };
+ ret = res[1];
+ }
+ else
+ {
+ ret = createBranchElement(par, null);
+ added = new Element[]{ ret, res[1] };
+ }
+ index++;
+ }
+ else
+ {
+ removed = new Element[]{ current };
+ if (res[1] instanceof BranchElement)
+ {
+ ret = res[1];
+ added = new Element[]{ res[0], res[1] };
+ }
+ else
+ {
+ ret = createBranchElement(par, null);
+ added = new Element[]{ res[0], ret, res[1] };
+ }
}
- newParagraph.replace(0, 0, newFracture);
- edit = new ElementEdit(newParagraph, 0, new Element[0],
- fracture);
- documentEvent.addEdit(edit);
- fracture = new Element[0];
+ par.replace(index, removed.length, added);
+ addEdit(par, index, removed, added);
}
+ else
+ {
+ ret = createBranchElement(par, null);
+ Element[] added = new Element[]{ ret };
+ par.replace(index, 0, added);
+ addEdit(par, index, new Element[0], added);
+ }
+ return ret;
}
-
+
/**
- * Inserts an end tag into the document structure. This cuts of the
- * current paragraph element, possibly fracturing it's child elements.
- * The fractured elements are saved so that they can be joined later
- * with a new paragraph element.
+ * Inserts a fracture into the document structure.
+ *
+ * @param tag - the element spec.
*/
- void insertEndTag(ElementSpec tag)
+ private void insertFracture(ElementSpec tag)
{
- BranchElement root = (BranchElement) getDefaultRootElement();
- int parIndex = root.getElementIndex(offset);
- BranchElement paragraph = (BranchElement) root.getElement(parIndex);
-
- int index = paragraph.getElementIndex(offset);
- LeafElement content = (LeafElement) paragraph.getElement(index);
- // We might have to split the element at offset.
- split(content, offset);
- index = paragraph.getElementIndex(offset);
-
- int count = paragraph.getElementCount();
- // Store fractured elements.
- fracture = new Element[count - index];
- for (int i = index; i < count; ++i)
- fracture[i - index] = paragraph.getElement(i);
-
- // Delete fractured elements.
- paragraph.replace(index, count - index, new Element[0]);
-
- // Add this action to the document event.
- ElementEdit edit = new ElementEdit(paragraph, index, fracture,
- new Element[0]);
- documentEvent.addEdit(edit);
+ // This is the parent of the paragraph about to be fractured. We will
+ // create a new child of this parent.
+ BranchElement parent = (BranchElement) elementStack.peek();
+ int parentIndex = parent.getElementIndex(offset);
+
+ // This is the old paragraph. We must remove all its children that
+ // occur after offset and move them to a new paragraph. We must
+ // also recreate its child that occurs at offset to have the proper
+ // end offset. The remainder of this child will also go in the new
+ // paragraph.
+ BranchElement previous = (BranchElement) parent.getElement(parentIndex);
+
+ // This is the new paragraph.
+ BranchElement newBranch =
+ (BranchElement) createBranchElement(parent, previous.getAttributes());
+
+
+ // The steps we must take to properly fracture are:
+ // 1. Recreate the LeafElement at offset to have the correct end offset.
+ // 2. Create a new LeafElement with the remainder of the LeafElement in
+ // #1 ==> this is whatever was in that LeafElement to the right of the
+ // inserted newline.
+ // 3. Find the paragraph at offset and remove all its children that
+ // occur _after_ offset. These will be moved to the newly created
+ // paragraph.
+ // 4. Move the LeafElement created in #2 and all the LeafElements removed
+ // in #3 to the newly created paragraph.
+ // 5. Add the new paragraph to the parent.
+ int previousIndex = previous.getElementIndex(offset);
+ int numReplaced = previous.getElementCount() - previousIndex;
+ Element previousLeaf = previous.getElement(previousIndex);
+ AttributeSet prevLeafAtts = previous.getAttributes();
+
+ // This recreates the child at offset to have the proper end offset.
+ // (Step 1).
+ Element newPreviousLeaf =
+ createLeafElement(previous,
+ prevLeafAtts, previousLeaf.getStartOffset(),
+ offset);
+ // This creates the new child, which is the remainder of the old child.
+ // (Step 2).
+
+ Element firstLeafInNewBranch =
+ createLeafElement(newBranch, prevLeafAtts,
+ offset, previousLeaf.getEndOffset());
+
+ // Now we move the new LeafElement and all the old children that occurred
+ // after the offset to the new paragraph. (Step 4).
+ Element[] newLeaves = new Element[numReplaced];
+ newLeaves[0] = firstLeafInNewBranch;
+ for (int i = 1; i < numReplaced; i++)
+ newLeaves[i] = previous.getElement(previousIndex + i);
+ newBranch.replace(0, 0, newLeaves);
+ addEdit(newBranch, 0, null, newLeaves);
+
+ // Now we remove the children after the offset from the previous
+ // paragraph. (Step 3).
+ int removeSize = previous.getElementCount() - previousIndex;
+ Element[] add = new Element[] { newPreviousLeaf };
+ Element[] remove = new Element[removeSize];
+ for (int j = 0; j < removeSize; j++)
+ remove[j] = previous.getElement(previousIndex + j);
+ previous.replace(previousIndex, removeSize, add);
+ addEdit(previous, previousIndex, remove, add);
+
+ // Finally we add the new paragraph to the parent. (Step 5).
+ Element[] nb = new Element[] { newBranch };
+ int index = parentIndex + 1;
+ parent.replace(index, 0, nb);
+ addEdit(parent, index, null, nb);
}
-
+
/**
* Inserts a content element into the document structure.
- *
+ *
* @param tag the element spec
*/
- void insertContentTag(ElementSpec tag)
+ private void insertContentTag(ElementSpec tag)
{
+ prepareContentInsertion();
int len = tag.getLength();
int dir = tag.getDirection();
+ AttributeSet tagAtts = tag.getAttributes();
if (dir == ElementSpec.JoinPreviousDirection)
{
- Element prev = getCharacterElement(offset);
- BranchElement prevParent = (BranchElement) prev.getParentElement();
- Element join = createLeafElement(prevParent, tag.getAttributes(),
- prev.getStartOffset(),
- Math.max(prev.getEndOffset(),
- offset + len));
- int ind = prevParent.getElementIndex(offset);
- if (ind == -1)
- ind = 0;
- Element[] add = new Element[]{join};
- prevParent.replace(ind, 1, add);
-
- // Add this action to the document event.
- ElementEdit edit = new ElementEdit(prevParent, ind,
- new Element[]{prev}, add);
- documentEvent.addEdit(edit);
+ // The mauve tests to this class show that a JoinPrevious insertion
+ // does not add any edits to the document event. To me this means
+ // that nothing is done here. The previous element naturally should
+ // expand so that it covers the new characters.
}
else if (dir == ElementSpec.JoinNextDirection)
{
- Element next = getCharacterElement(offset + len);
- BranchElement nextParent = (BranchElement) next.getParentElement();
- Element join = createLeafElement(nextParent, tag.getAttributes(),
- offset,
- next.getEndOffset());
- int ind = nextParent.getElementIndex(offset + len);
- if (ind == -1)
- ind = 0;
- Element[] add = new Element[]{join};
- nextParent.replace(ind, 1, add);
-
- // Add this action to the document event.
- ElementEdit edit = new ElementEdit(nextParent, ind,
- new Element[]{next}, add);
- documentEvent.addEdit(edit);
+ // FIXME:
+ // Have to handle JoinNext differently depending on whether
+ // or not it comes after a fracture. If comes after a fracture,
+ // the insertFracture method takes care of everything and nothing
+ // needs to be done here. Otherwise, we need to adjust the
+ // Element structure. For now, I check if the elementStack's
+ // top Element is the immediate parent of the LeafElement at
+ // offset - if so, we did not come immediately after a
+ // fracture. This seems awkward and should probably be improved.
+ // We may be doing too much in insertFracture because we are
+ // adjusting the offsets, the correct thing to do may be to
+ // create a new branch element and push it on to element stack
+ // and then this method here can be more general.
+
+ BranchElement paragraph = (BranchElement) elementStack.peek();
+ int index = paragraph.getElementIndex(offset);
+ Element target = paragraph.getElement(index);
+ if (target.isLeaf() && paragraph.getElementCount() > (index + 1))
+ {
+ Element next = paragraph.getElement(index + 1);
+ Element newEl1 = createLeafElement(paragraph,
+ target.getAttributes(),
+ target.getStartOffset(),
+ offset);
+ Element newEl2 = createLeafElement(paragraph,
+ next.getAttributes(), offset,
+ next.getEndOffset());
+ Element[] add = new Element[] { newEl1, newEl2 };
+ paragraph.replace (index, 2, add);
+ addEdit(paragraph, index, new Element[] { target, next }, add);
+ }
}
- else
+ else if (dir == ElementSpec.OriginateDirection)
{
- BranchElement par = (BranchElement) getParagraphElement(offset);
-
- int ind = par.getElementIndex(offset);
-
- // Make room for the element.
- // Cut previous element.
- Element prev = par.getElement(ind);
- if (prev != null && prev.getStartOffset() < offset)
+ BranchElement paragraph = (BranchElement) elementStack.peek();
+ int index = paragraph.getElementIndex(offset);
+ Element current = paragraph.getElement(index);
+
+ Element[] added;
+ Element[] removed = new Element[] {current};
+ Element[] splitRes = split(current, offset, length);
+ if (splitRes[0] == null)
{
- Element cutPrev = createLeafElement(par, prev.getAttributes(),
- prev.getStartOffset(),
- offset);
- Element[] remove = new Element[]{prev};
- Element[] add = new Element[]{cutPrev};
- if (prev.getEndOffset() > offset + len)
- {
- Element rem = createLeafElement(par, prev.getAttributes(),
- offset + len,
- prev.getEndOffset());
- add = new Element[]{cutPrev, rem};
- }
-
- par.replace(ind, 1, add);
- documentEvent.addEdit(new ElementEdit(par, ind, remove, add));
- ind++;
+ added = new Element[2];
+ added[0] = createLeafElement(paragraph, tagAtts,
+ offset, endOffset);
+ added[1] = splitRes[1];
+ removed = new Element[0];
+ index++;
}
- // ind now points to the next element.
-
- // Cut next element if necessary.
- Element next = par.getElement(ind);
- if (next != null && next.getStartOffset() < offset + len)
+ else if (current.getStartOffset() == offset)
+ {
+ // This is if the new insertion happens immediately before
+ // the <code>current</code> Element. In this case there are 2
+ // resulting Elements.
+ added = new Element[2];
+ added[0] = createLeafElement(paragraph, tagAtts, offset,
+ endOffset);
+ added[1] = splitRes[1];
+ }
+ else if (current.getEndOffset() == endOffset)
{
- Element cutNext = createLeafElement(par, next.getAttributes(),
- offset + len,
- next.getEndOffset());
- Element[] remove = new Element[]{next};
- Element[] add = new Element[]{cutNext};
- par.replace(ind, 1, add);
- documentEvent.addEdit(new ElementEdit(par, ind, remove,
- add));
+ // This is if the new insertion happens right at the end of
+ // the <code>current</code> Element. In this case there are
+ // 2 resulting Elements.
+ added = new Element[2];
+ added[0] = splitRes[0];
+ added[1] = createLeafElement(paragraph, tagAtts, offset,
+ endOffset);
}
-
- // Insert new element.
- Element newEl = createLeafElement(par, tag.getAttributes(),
- offset, offset + len);
- Element[] added = new Element[]{newEl};
- par.replace(ind, 0, added);
- // Add this action to the document event.
- ElementEdit edit = new ElementEdit(par, ind, new Element[0],
- added);
- documentEvent.addEdit(edit);
+ else
+ {
+ // This is if the new insertion is in the middle of the
+ // <code>current</code> Element. In this case
+ // there will be 3 resulting Elements.
+ added = new Element[3];
+ added[0] = splitRes[0];
+ added[1] = createLeafElement(paragraph, tagAtts, offset,
+ endOffset);
+ added[2] = splitRes[1];
+ }
+ paragraph.replace(index, removed.length, added);
+ addEdit(paragraph, index, removed, added);
}
offset += len;
}
@@ -800,6 +1152,79 @@ public class DefaultStyledDocument extends AbstractDocument
result.replace(0, 0, children);
return result;
}
+
+ /**
+ * Adds an ElementChange for a given element modification to the document
+ * event. If there already is an ElementChange registered for this element,
+ * this method tries to merge the ElementChanges together. However, this
+ * is only possible if the indices of the new and old ElementChange are
+ * equal.
+ *
+ * @param e the element
+ * @param i the index of the change
+ * @param removed the removed elements, or <code>null</code>
+ * @param added the added elements, or <code>null</code>
+ */
+ private void addEdit(Element e, int i, Element[] removed, Element[] added)
+ {
+ // Perform sanity check first.
+ DocumentEvent.ElementChange ec = documentEvent.getChange(e);
+
+ // Merge the existing stuff with the new stuff.
+ Element[] oldAdded = ec == null ? null: ec.getChildrenAdded();
+ Element[] newAdded;
+ if (oldAdded != null && added != null)
+ {
+ if (ec.getIndex() <= i)
+ {
+ int index = i - ec.getIndex();
+ // Merge adds together.
+ newAdded = new Element[oldAdded.length + added.length];
+ System.arraycopy(oldAdded, 0, newAdded, 0, index);
+ System.arraycopy(added, 0, newAdded, index, added.length);
+ System.arraycopy(oldAdded, index, newAdded, index + added.length,
+ oldAdded.length - index);
+ i = ec.getIndex();
+ }
+ else
+ throw new AssertionError("Not yet implemented case.");
+ }
+ else if (added != null)
+ newAdded = added;
+ else if (oldAdded != null)
+ newAdded = oldAdded;
+ else
+ newAdded = new Element[0];
+
+ Element[] oldRemoved = ec == null ? null: ec.getChildrenRemoved();
+ Element[] newRemoved;
+ if (oldRemoved != null && removed != null)
+ {
+ if (ec.getIndex() <= i)
+ {
+ int index = i - ec.getIndex();
+ // Merge removes together.
+ newRemoved = new Element[oldRemoved.length + removed.length];
+ System.arraycopy(oldAdded, 0, newRemoved, 0, index);
+ System.arraycopy(removed, 0, newRemoved, index, removed.length);
+ System.arraycopy(oldRemoved, index, newRemoved,
+ index + removed.length,
+ oldRemoved.length - index);
+ i = ec.getIndex();
+ }
+ else
+ throw new AssertionError("Not yet implemented case.");
+ }
+ else if (removed != null)
+ newRemoved = removed;
+ else if (oldRemoved != null)
+ newRemoved = oldRemoved;
+ else
+ newRemoved = new Element[0];
+
+ // Replace the existing edit for the element with the merged.
+ documentEvent.addEdit(new ElementEdit(e, i, newRemoved, newAdded));
+ }
}
/**
@@ -824,7 +1249,7 @@ public class DefaultStyledDocument extends AbstractDocument
*/
public String getName()
{
- return "section";
+ return SectionElementName;
}
}
@@ -945,9 +1370,7 @@ public class DefaultStyledDocument extends AbstractDocument
// Use createBranchElement() and createLeafElement instead.
SectionElement section = new SectionElement();
- BranchElement paragraph =
- (BranchElement) createBranchElement(section, null);
- paragraph.setResolveParent(getStyle(StyleContext.DEFAULT_STYLE));
+ BranchElement paragraph = new BranchElement(section, null);
tmp = new Element[1];
tmp[0] = paragraph;
section.replace(0, 0, tmp);
@@ -1043,7 +1466,11 @@ public class DefaultStyledDocument extends AbstractDocument
{
Element paragraph = getParagraphElement(position);
AttributeSet attributes = paragraph.getAttributes();
- return (Style) attributes.getResolveParent();
+ AttributeSet a = attributes.getResolveParent();
+ // If the resolve parent is not of type Style, we return null.
+ if (a instanceof Style)
+ return (Style) a;
+ return null;
}
/**
@@ -1112,50 +1539,54 @@ public class DefaultStyledDocument extends AbstractDocument
AttributeSet attributes,
boolean replace)
{
- DefaultDocumentEvent ev =
- new DefaultDocumentEvent(offset, length,
- DocumentEvent.EventType.CHANGE);
-
- // Modify the element structure so that the interval begins at an element
- // start and ends at an element end.
- buffer.change(offset, length, ev);
-
- Element root = getDefaultRootElement();
- // Visit all paragraph elements within the specified interval
- int paragraphCount = root.getElementCount();
- for (int pindex = 0; pindex < paragraphCount; pindex++)
+ // Exit early if length is 0, so no DocumentEvent is created or fired.
+ if (length == 0)
+ return;
+ try
{
- Element paragraph = root.getElement(pindex);
- // Skip paragraphs that lie outside the interval.
- if ((paragraph.getStartOffset() > offset + length)
- || (paragraph.getEndOffset() < offset))
- continue;
-
- // Visit content elements within this paragraph
- int contentCount = paragraph.getElementCount();
- for (int cindex = 0; cindex < contentCount; cindex++)
+ // Must obtain a write lock for this method. writeLock() and
+ // writeUnlock() should always be in try/finally block to make
+ // sure that locking happens in a balanced manner.
+ writeLock();
+ DefaultDocumentEvent ev =
+ new DefaultDocumentEvent(
+ offset,
+ length,
+ DocumentEvent.EventType.CHANGE);
+
+ // Modify the element structure so that the interval begins at an
+ // element
+ // start and ends at an element end.
+ buffer.change(offset, length, ev);
+
+ Element root = getDefaultRootElement();
+ // Visit all paragraph elements within the specified interval
+ int end = offset + length;
+ Element curr;
+ for (int pos = offset; pos < end; )
{
- Element content = paragraph.getElement(cindex);
- // Skip content that lies outside the interval.
- if ((content.getStartOffset() > offset + length)
- || (content.getEndOffset() < offset))
- continue;
-
- if (content instanceof AbstractElement)
- {
- AbstractElement el = (AbstractElement) content;
- if (replace)
- el.removeAttributes(el);
- el.addAttributes(attributes);
- }
- else
- throw new AssertionError("content elements are expected to be"
- + "instances of "
- + "javax.swing.text.AbstractDocument.AbstractElement");
+ // Get the CharacterElement at offset pos.
+ curr = getCharacterElement(pos);
+ if (pos == curr.getEndOffset())
+ break;
+
+ MutableAttributeSet a = (MutableAttributeSet) curr.getAttributes();
+ ev.addEdit(new AttributeUndoableEdit(curr, attributes, replace));
+ // If replace is true, remove all the old attributes.
+ if (replace)
+ a.removeAttributes(a);
+ // Add all the new attributes.
+ a.addAttributes(attributes);
+ // Increment pos so we can check the next CharacterElement.
+ pos = curr.getEndOffset();
}
+ fireChangedUpdate(ev);
+ fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
+ }
+ finally
+ {
+ writeUnlock();
}
-
- fireChangedUpdate(ev);
}
/**
@@ -1167,14 +1598,36 @@ public class DefaultStyledDocument extends AbstractDocument
public void setLogicalStyle(int position, Style style)
{
Element el = getParagraphElement(position);
- if (el instanceof AbstractElement)
- {
- AbstractElement ael = (AbstractElement) el;
- ael.setResolveParent(style);
- }
- else
- throw new AssertionError("paragraph elements are expected to be"
- + "instances of javax.swing.text.AbstractDocument.AbstractElement");
+ // getParagraphElement doesn't return null but subclasses might so
+ // we check for null here.
+ if (el == null)
+ return;
+ try
+ {
+ writeLock();
+ if (el instanceof AbstractElement)
+ {
+ AbstractElement ael = (AbstractElement) el;
+ ael.setResolveParent(style);
+ int start = el.getStartOffset();
+ int end = el.getEndOffset();
+ DefaultDocumentEvent ev =
+ new DefaultDocumentEvent (
+ start,
+ end - start,
+ DocumentEvent.EventType.CHANGE);
+ // FIXME: Add an UndoableEdit to this event and fire it.
+ fireChangedUpdate(ev);
+ }
+ else
+ throw new
+ AssertionError("paragraph elements are expected to be"
+ + "instances of AbstractDocument.AbstractElement");
+ }
+ finally
+ {
+ writeUnlock();
+ }
}
/**
@@ -1190,15 +1643,47 @@ public class DefaultStyledDocument extends AbstractDocument
AttributeSet attributes,
boolean replace)
{
- int index = offset;
- while (index < offset + length)
+ try
+ {
+ // Must obtain a write lock for this method. writeLock() and
+ // writeUnlock() should always be in try/finally blocks to make
+ // sure that locking occurs in a balanced manner.
+ writeLock();
+
+ // Create a DocumentEvent to use for changedUpdate().
+ DefaultDocumentEvent ev =
+ new DefaultDocumentEvent (
+ offset,
+ length,
+ DocumentEvent.EventType.CHANGE);
+
+ // Have to iterate through all the _paragraph_ elements that are
+ // contained or partially contained in the interval
+ // (offset, offset + length).
+ Element rootElement = getDefaultRootElement();
+ int startElement = rootElement.getElementIndex(offset);
+ int endElement = rootElement.getElementIndex(offset + length - 1);
+ if (endElement < startElement)
+ endElement = startElement;
+
+ for (int i = startElement; i <= endElement; i++)
+ {
+ Element par = rootElement.getElement(i);
+ MutableAttributeSet a = (MutableAttributeSet) par.getAttributes();
+ // Add the change to the DocumentEvent.
+ ev.addEdit(new AttributeUndoableEdit(par, attributes, replace));
+ // If replace is true remove the old attributes.
+ if (replace)
+ a.removeAttributes(a);
+ // Add the new attributes.
+ a.addAttributes(attributes);
+ }
+ fireChangedUpdate(ev);
+ fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
+ }
+ finally
{
- AbstractElement par = (AbstractElement) getParagraphElement(index);
- AttributeContext ctx = getAttributeContext();
- if (replace)
- par.removeAttributes(par);
- par.addAttributes(attributes);
- index = par.getElementCount();
+ writeUnlock();
}
}
@@ -1212,9 +1697,14 @@ public class DefaultStyledDocument extends AbstractDocument
protected void insertUpdate(DefaultDocumentEvent ev, AttributeSet attr)
{
super.insertUpdate(ev, attr);
+ // If the attribute set is null, use an empty attribute set.
+ if (attr == null)
+ attr = SimpleAttributeSet.EMPTY;
int offset = ev.getOffset();
int length = ev.getLength();
int endOffset = offset + length;
+ AttributeSet paragraphAttributes =
+ getParagraphElement(endOffset).getAttributes();
Segment txt = new Segment();
try
{
@@ -1229,66 +1719,141 @@ public class DefaultStyledDocument extends AbstractDocument
int len = 0;
Vector specs = new Vector();
-
+ ElementSpec finalStartTag = null;
+ short finalStartDirection = ElementSpec.OriginateDirection;
+ boolean prevCharWasNewline = false;
Element prev = getCharacterElement(offset);
- Element next = getCharacterElement(endOffset);
+ Element next = getCharacterElement(endOffset);
+ Element prevParagraph = getParagraphElement(offset);
+ Element paragraph = getParagraphElement(endOffset);
+
+ int segmentEnd = txt.offset + txt.count;
+
+ // Check to see if we're inserting immediately after a newline.
+ if (offset > 0)
+ {
+ try
+ {
+ String s = getText(offset - 1, 1);
+ if (s.equals("\n"))
+ {
+ finalStartDirection =
+ handleInsertAfterNewline(specs, offset, endOffset,
+ prevParagraph,
+ paragraph,
+ paragraphAttributes);
+
+ prevCharWasNewline = true;
+ // Find the final start tag from the ones just created.
+ for (int i = 0; i < specs.size(); i++)
+ if (((ElementSpec) specs.get(i)).getType()
+ == ElementSpec.StartTagType)
+ finalStartTag = (ElementSpec)specs.get(i);
+ }
+ }
+ catch (BadLocationException ble)
+ {
+ // This shouldn't happen.
+ AssertionError ae = new AssertionError();
+ ae.initCause(ble);
+ throw ae;
+ }
+ }
- for (int i = offset; i < endOffset; ++i)
+
+ for (int i = txt.offset; i < segmentEnd; ++i)
{
len++;
if (txt.array[i] == '\n')
{
- ElementSpec spec = new ElementSpec(attr, ElementSpec.ContentType,
- len);
-
- // If we are at the last index, then check if we could probably be
- // joined with the next element.
- if (i == endOffset - 1)
- {
- if (next.getAttributes().isEqual(attr))
- spec.setDirection(ElementSpec.JoinNextDirection);
- }
- // If we are at the first new element, then check if it could be
- // joined with the previous element.
- else if (specs.size() == 0)
- {
- if (prev.getAttributes().isEqual(attr))
- spec.setDirection(ElementSpec.JoinPreviousDirection);
- }
-
- specs.add(spec);
+ // Add the ElementSpec for the content.
+ specs.add(new ElementSpec(attr, ElementSpec.ContentType, len));
// Add ElementSpecs for the newline.
- ElementSpec endTag = new ElementSpec(null, ElementSpec.EndTagType);
- specs.add(endTag);
- ElementSpec startTag = new ElementSpec(null,
+ specs.add(new ElementSpec(null, ElementSpec.EndTagType));
+ finalStartTag = new ElementSpec(paragraphAttributes,
ElementSpec.StartTagType);
- startTag.setDirection(ElementSpec.JoinFractureDirection);
- specs.add(startTag);
-
+ specs.add(finalStartTag);
len = 0;
- offset += len;
}
}
// Create last element if last character hasn't been a newline.
- if (len > 0)
+ if (len > 0)
+ specs.add(new ElementSpec(attr, ElementSpec.ContentType, len));
+
+ // Set the direction of the last spec of type StartTagType.
+ // If we are inserting after a newline then this value comes from
+ // handleInsertAfterNewline.
+ if (finalStartTag != null)
+ {
+ if (prevCharWasNewline)
+ finalStartTag.setDirection(finalStartDirection);
+ else if (prevParagraph.getEndOffset() != endOffset)
+ {
+ try
+ {
+ String last = getText(endOffset - 1, 1);
+ if (!last.equals("\n"))
+ finalStartTag.setDirection(ElementSpec.JoinFractureDirection);
+ }
+ catch (BadLocationException ble)
+ {
+ // This shouldn't happen.
+ AssertionError ae = new AssertionError();
+ ae.initCause(ble);
+ throw ae;
+ }
+ }
+ else
+ {
+ // If there is an element AFTER this one, then set the
+ // direction to JoinNextDirection.
+ Element parent = prevParagraph.getParentElement();
+ int index = parent.getElementIndex(offset);
+ if (index + 1 < parent.getElementCount()
+ && !parent.getElement(index + 1).isLeaf())
+ finalStartTag.setDirection(ElementSpec.JoinNextDirection);
+ }
+ }
+
+ // If we are at the last index, then check if we could probably be
+ // joined with the next element.
+ // This means:
+ // - we must be a ContentTag
+ // - if there is a next Element, we must have the same attributes
+ // - if there is no next Element, but one will be created,
+ // we must have the same attributes as the higher-level run.
+ ElementSpec last = (ElementSpec) specs.lastElement();
+ if (last.getType() == ElementSpec.ContentType)
{
- ElementSpec spec = new ElementSpec(attr, ElementSpec.ContentType, len);
- // If we are at the first new element, then check if it could be
- // joined with the previous element.
- if (specs.size() == 0)
+ Element currentRun =
+ prevParagraph.getElement(prevParagraph.getElementIndex(offset));
+ if (currentRun.getEndOffset() == endOffset)
{
- if (prev.getAttributes().isEqual(attr))
- spec.setDirection(ElementSpec.JoinPreviousDirection);
+ if (endOffset < getLength() && next.getAttributes().isEqual(attr)
+ && last.getType() == ElementSpec.ContentType)
+ last.setDirection(ElementSpec.JoinNextDirection);
+ }
+ else
+ {
+ if (finalStartTag != null
+ && finalStartTag.getDirection() ==
+ ElementSpec.JoinFractureDirection
+ && currentRun.getAttributes().isEqual(attr))
+ {
+ last.setDirection(ElementSpec.JoinNextDirection);
+ }
}
- // Check if we could probably be joined with the next element.
- else if (next.getAttributes().isEqual(attr))
- spec.setDirection(ElementSpec.JoinNextDirection);
-
- specs.add(spec);
}
-
+
+ // If we are at the first new element, then check if it could be
+ // joined with the previous element.
+ ElementSpec first = (ElementSpec) specs.firstElement();
+ if (prev.getAttributes().isEqual(attr)
+ && first.getType() == ElementSpec.ContentType)
+ first.setDirection(ElementSpec.JoinPreviousDirection);
+
ElementSpec[] elSpecs =
(ElementSpec[]) specs.toArray(new ElementSpec[specs.size()]);
@@ -1296,6 +1861,47 @@ public class DefaultStyledDocument extends AbstractDocument
}
/**
+ * A helper method to set up the ElementSpec buffer for the special
+ * case of an insertion occurring immediately after a newline.
+ * @param specs the ElementSpec buffer to initialize.
+ */
+ short handleInsertAfterNewline(Vector specs, int offset, int endOffset,
+ Element prevParagraph, Element paragraph,
+ AttributeSet a)
+ {
+ if (prevParagraph.getParentElement() == paragraph.getParentElement())
+ {
+ specs.add(new ElementSpec(a, ElementSpec.EndTagType));
+ specs.add(new ElementSpec(a, ElementSpec.StartTagType));
+ if (prevParagraph.getEndOffset() != endOffset)
+ return ElementSpec.JoinFractureDirection;
+ // If there is an Element after this one, use JoinNextDirection.
+ Element parent = paragraph.getParentElement();
+ if (parent.getElementCount() > parent.getElementIndex(offset) + 1)
+ return ElementSpec.JoinNextDirection;
+ }
+ else
+ {
+ // TODO: What to do here?
+ }
+ return ElementSpec.OriginateDirection;
+ }
+
+ /**
+ * Updates the document structure in response to text removal. This is
+ * forwarded to the {@link ElementBuffer} of this document. Any changes to
+ * the document structure are added to the specified document event and
+ * sent to registered listeners.
+ *
+ * @param ev the document event that records the changes to the document
+ */
+ protected void removeUpdate(DefaultDocumentEvent ev)
+ {
+ super.removeUpdate(ev);
+ buffer.remove(ev.getOffset(), ev.getLength(), ev);
+ }
+
+ /**
* Returns an enumeration of all style names.
*
* @return an enumeration of all style names
@@ -1316,6 +1922,35 @@ public class DefaultStyledDocument extends AbstractDocument
// Nothing to do here. This is intended to be overridden by subclasses.
}
+ void printElements (Element start, int pad)
+ {
+ for (int i = 0; i < pad; i++)
+ System.out.print(" ");
+ if (pad == 0)
+ System.out.println ("ROOT ELEMENT ("+start.getStartOffset()+", "+start.getEndOffset()+")");
+ else if (start instanceof AbstractDocument.BranchElement)
+ System.out.println ("BranchElement ("+start.getStartOffset()+", "+start.getEndOffset()+")");
+ else
+ {
+ {
+ try
+ {
+ System.out.println ("LeafElement ("+start.getStartOffset()+", "
+ + start.getEndOffset()+"): "+
+ start.getDocument().
+ getText(start.getStartOffset(),
+ start.getEndOffset() -
+ start.getStartOffset()));
+ }
+ catch (BadLocationException ble)
+ {
+ }
+ }
+ }
+ for (int i = 0; i < start.getElementCount(); i ++)
+ printElements (start.getElement(i), pad+3);
+ }
+
/**
* Inserts a bulk of structured content at once.
*
@@ -1325,36 +1960,53 @@ public class DefaultStyledDocument extends AbstractDocument
protected void insert(int offset, ElementSpec[] data)
throws BadLocationException
{
- writeLock();
- // First we insert the content.
- int index = offset;
- for (int i = 0; i < data.length; i++)
+ if (data == null || data.length == 0)
+ return;
+ try
{
- ElementSpec spec = data[i];
- if (spec.getArray() != null && spec.getLength() > 0)
+ // writeLock() and writeUnlock() should always be in a try/finally
+ // block so that locking balance is guaranteed even if some
+ // exception is thrown.
+ writeLock();
+
+ // First we collect the content to be inserted.
+ StringBuffer contentBuffer = new StringBuffer();
+ for (int i = 0; i < data.length; i++)
{
- String insertString = new String(spec.getArray(), spec.getOffset(),
- spec.getLength());
- content.insertString(index, insertString);
+ // Collect all inserts into one so we can get the correct
+ // ElementEdit
+ ElementSpec spec = data[i];
+ if (spec.getArray() != null && spec.getLength() > 0)
+ contentBuffer.append(spec.getArray(), spec.getOffset(),
+ spec.getLength());
}
- index += spec.getLength();
+
+ int length = contentBuffer.length();
+
+ // If there was no content inserted then exit early.
+ if (length == 0)
+ return;
+
+ UndoableEdit edit = content.insertString(offset,
+ contentBuffer.toString());
+
+ // Create the DocumentEvent with the ElementEdit added
+ DefaultDocumentEvent ev =
+ new DefaultDocumentEvent(offset,
+ length,
+ DocumentEvent.EventType.INSERT);
+ ev.addEdit(edit);
+
+ // Finally we must update the document structure and fire the insert
+ // update event.
+ buffer.insert(offset, length, data, ev);
+ fireInsertUpdate(ev);
+ fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
}
- // Update the view structure.
- DefaultDocumentEvent ev = new DefaultDocumentEvent(offset, index - offset,
- DocumentEvent.EventType.INSERT);
- for (int i = 0; i < data.length; i++)
+ finally
{
- ElementSpec spec = data[i];
- AttributeSet atts = spec.getAttributes();
- if (atts != null)
- insertUpdate(ev, atts);
+ writeUnlock();
}
-
- // Finally we must update the document structure and fire the insert update
- // event.
- buffer.insert(offset, index - offset, data, ev);
- fireInsertUpdate(ev);
- writeUnlock();
}
/**
@@ -1382,4 +2034,9 @@ public class DefaultStyledDocument extends AbstractDocument
throw err;
}
}
+
+ static boolean attributeSetsAreSame (AttributeSet a, AttributeSet b)
+ {
+ return (a == null && b == null) || (a != null && a.isEqual(b));
+ }
}
diff --git a/libjava/classpath/javax/swing/text/FlowView.java b/libjava/classpath/javax/swing/text/FlowView.java
index 765f515..6d4b9cd 100644
--- a/libjava/classpath/javax/swing/text/FlowView.java
+++ b/libjava/classpath/javax/swing/text/FlowView.java
@@ -396,37 +396,6 @@ public abstract class FlowView extends BoxView
throw new AssertionError("This method must not be called in "
+ "LogicalView.");
}
-
- /**
- * Returns the document position that is (visually) nearest to the given
- * document position <code>pos</code> in the given direction <code>d</code>.
- *
- * @param c the text component
- * @param pos the document position
- * @param b the bias for <code>pos</code>
- * @param d the direction, must be either {@link SwingConstants#NORTH},
- * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or
- * {@link SwingConstants#EAST}
- * @param biasRet an array of {@link Position.Bias} that can hold at least
- * one element, which is filled with the bias of the return position
- * on method exit
- *
- * @return the document position that is (visually) nearest to the given
- * document position <code>pos</code> in the given direction
- * <code>d</code>
- *
- * @throws BadLocationException if <code>pos</code> is not a valid offset in
- * the document model
- */
- public int getNextVisualPositionFrom(JTextComponent c, int pos,
- Position.Bias b, int d,
- Position.Bias[] biasRet)
- throws BadLocationException
- {
- assert false : "getNextVisualPositionFrom() must not be called in "
- + "LogicalView";
- return 0;
- }
}
/**
diff --git a/libjava/classpath/javax/swing/text/GapContent.java b/libjava/classpath/javax/swing/text/GapContent.java
index 4c65de0..80dcfa5 100644
--- a/libjava/classpath/javax/swing/text/GapContent.java
+++ b/libjava/classpath/javax/swing/text/GapContent.java
@@ -64,6 +64,7 @@ import javax.swing.undo.UndoableEdit;
public class GapContent
implements AbstractDocument.Content, Serializable
{
+
/**
* A {@link Position} implementation for <code>GapContent</code>.
*/
@@ -100,15 +101,15 @@ public class GapContent
public int compareTo(Object o)
{
if (o instanceof Integer)
- {
- int otherMark = ((Integer) o).intValue();
- return mark - otherMark;
- }
+ {
+ int otherMark = ((Integer) o).intValue();
+ return mark - otherMark;
+ }
else
- {
- GapContentPosition other = (GapContentPosition) o;
- return mark - other.mark;
- }
+ {
+ GapContentPosition other = (GapContentPosition) o;
+ return mark - other.mark;
+ }
}
/**
@@ -122,7 +123,6 @@ public class GapContent
assert mark <= gapStart || mark >= gapEnd : "mark: " + mark
+ ", gapStart: " + gapStart
+ ", gapEnd: " + gapEnd;
-
if (mark <= gapStart)
return mark;
else
@@ -130,11 +130,11 @@ public class GapContent
}
}
- class UndoInsertString extends AbstractUndoableEdit
+ class InsertUndo extends AbstractUndoableEdit
{
public int where, length;
String text;
- public UndoInsertString(int start, int len)
+ public InsertUndo(int start, int len)
{
where = start;
length = len;
@@ -316,7 +316,7 @@ public class GapContent
replace(where, 0, str.toCharArray(), strLen);
- return new UndoInsertString(where, strLen);
+ return new InsertUndo(where, strLen);
}
/**
@@ -449,7 +449,7 @@ public class GapContent
// We store the actual array index in the GapContentPosition. The real
// offset is then calculated in the GapContentPosition.
int mark = offset;
- if (offset > gapStart)
+ if (offset >= gapStart)
mark += gapEnd - gapStart;
GapContentPosition pos = new GapContentPosition(mark);
@@ -584,8 +584,9 @@ public class GapContent
{
if (gapStart != position)
shiftGap(position);
+
// Remove content
- if (rmSize > 0)
+ if (rmSize > 0)
shiftGapEndUp(gapEnd + rmSize);
// If gap is too small, enlarge the gap.
@@ -644,6 +645,13 @@ public class GapContent
new GapContentPosition(offset));
if (index1 < 0)
index1 = -(index1 + 1);
+
+ // Search the first index with the specified offset. The binarySearch does
+ // not necessarily find the first one.
+ while (index1 > 0
+ && ((GapContentPosition) positions.get(index1 - 1)).mark == offset)
+ index1--;
+
for (ListIterator i = positions.listIterator(index1); i.hasNext();)
{
GapContentPosition p = (GapContentPosition) i.next();
@@ -672,6 +680,13 @@ public class GapContent
new GapContentPosition(offset));
if (index1 < 0)
index1 = -(index1 + 1);
+
+ // Search the first index with the specified offset. The binarySearch does
+ // not necessarily find the first one.
+ while (index1 > 0
+ && ((GapContentPosition) positions.get(index1 - 1)).mark == offset)
+ index1--;
+
for (ListIterator i = positions.listIterator(index1); i.hasNext();)
{
GapContentPosition p = (GapContentPosition) i.next();
@@ -700,6 +715,12 @@ public class GapContent
new GapContentPosition(offset));
if (index1 < 0)
index1 = -(index1 + 1);
+
+ // Search the first index with the specified offset. The binarySearch does
+ // not necessarily find the first one.
+ while (index1 > 0
+ && ((GapContentPosition) positions.get(index1 - 1)).mark == offset)
+ index1--;
for (ListIterator i = positions.listIterator(index1); i.hasNext();)
{
GapContentPosition p = (GapContentPosition) i.next();
diff --git a/libjava/classpath/javax/swing/text/GlyphView.java b/libjava/classpath/javax/swing/text/GlyphView.java
index d3dd75e..47deb50 100644
--- a/libjava/classpath/javax/swing/text/GlyphView.java
+++ b/libjava/classpath/javax/swing/text/GlyphView.java
@@ -1057,34 +1057,4 @@ public class GlyphView extends View implements TabableView, Cloneable
return painter.getNextVisualPositionFrom(this, pos, bias, a, direction,
biasRet);
}
-
- /**
- * Returns the document position that is (visually) nearest to the given
- * document position <code>pos</code> in the given direction <code>d</code>.
- *
- * @param c the text component
- * @param pos the document position
- * @param b the bias for <code>pos</code>
- * @param d the direction, must be either {@link SwingConstants#NORTH},
- * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or
- * {@link SwingConstants#EAST}
- * @param biasRet an array of {@link Position.Bias} that can hold at least
- * one element, which is filled with the bias of the return position
- * on method exit
- *
- * @return the document position that is (visually) nearest to the given
- * document position <code>pos</code> in the given direction
- * <code>d</code>
- *
- * @throws BadLocationException if <code>pos</code> is not a valid offset in
- * the document model
- */
- public int getNextVisualPositionFrom(JTextComponent c, int pos,
- Position.Bias b, int d,
- Position.Bias[] biasRet)
- throws BadLocationException
- {
- // TODO: Implement this properly.
- throw new AssertionError("Not implemented yet.");
- }
}
diff --git a/libjava/classpath/javax/swing/text/IconView.java b/libjava/classpath/javax/swing/text/IconView.java
index 86c27dd..af2581a 100644
--- a/libjava/classpath/javax/swing/text/IconView.java
+++ b/libjava/classpath/javax/swing/text/IconView.java
@@ -158,34 +158,4 @@ public class IconView
return el.getStartOffset();
}
- /**
- * Returns the document position that is (visually) nearest to the given
- * document position <code>pos</code> in the given direction <code>d</code>.
- *
- * @param c the text component
- * @param pos the document position
- * @param b the bias for <code>pos</code>
- * @param d the direction, must be either {@link SwingConstants#NORTH},
- * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or
- * {@link SwingConstants#EAST}
- * @param biasRet an array of {@link Position.Bias} that can hold at least
- * one element, which is filled with the bias of the return position
- * on method exit
- *
- * @return the document position that is (visually) nearest to the given
- * document position <code>pos</code> in the given direction
- * <code>d</code>
- *
- * @throws BadLocationException if <code>pos</code> is not a valid offset in
- * the document model
- */
- public int getNextVisualPositionFrom(JTextComponent c, int pos,
- Position.Bias b, int d,
- Position.Bias[] biasRet)
- throws BadLocationException
- {
- // TODO: Implement this properly.
- throw new AssertionError("Not implemented yet.");
- }
-
}
diff --git a/libjava/classpath/javax/swing/text/InternationalFormatter.java b/libjava/classpath/javax/swing/text/InternationalFormatter.java
index 86300a7..ba3cffa 100644
--- a/libjava/classpath/javax/swing/text/InternationalFormatter.java
+++ b/libjava/classpath/javax/swing/text/InternationalFormatter.java
@@ -78,6 +78,8 @@ public class InternationalFormatter
minimum = null;
maximum = null;
format = null;
+ setCommitsOnValidEdit(false);
+ setOverwriteMode(false);
}
/**
@@ -226,6 +228,8 @@ public class InternationalFormatter
public String valueToString(Object value)
throws ParseException
{
+ if (value == null)
+ return "";
if (format != null)
return format.format(value);
else
diff --git a/libjava/classpath/javax/swing/text/JTextComponent.java b/libjava/classpath/javax/swing/text/JTextComponent.java
index 83966bb..afa1f24 100644
--- a/libjava/classpath/javax/swing/text/JTextComponent.java
+++ b/libjava/classpath/javax/swing/text/JTextComponent.java
@@ -380,12 +380,18 @@ public abstract class JTextComponent extends JComponent
public KeyStroke[] allKeys()
{
KeyStroke[] superKeys = super.allKeys();
- KeyStroke[] mapKeys = map.getBoundKeyStrokes();
- KeyStroke[] bothKeys = new KeyStroke[superKeys.length + mapKeys.length];
- for (int i = 0; i < superKeys.length; ++i)
+ KeyStroke[] mapKeys = map.getBoundKeyStrokes();
+ int skl = 0;
+ int mkl = 0;
+ if (superKeys != null)
+ skl = superKeys.length;
+ if (mapKeys != null)
+ mkl = mapKeys.length;
+ KeyStroke[] bothKeys = new KeyStroke[skl + mkl];
+ for (int i = 0; i < skl; ++i)
bothKeys[i] = superKeys[i];
- for (int i = 0; i < mapKeys.length; ++i)
- bothKeys[i + superKeys.length] = mapKeys[i];
+ for (int i = 0; i < mkl; ++i)
+ bothKeys[i + skl] = mapKeys[i];
return bothKeys;
}
}
@@ -864,7 +870,7 @@ public abstract class JTextComponent extends JComponent
Hashtable acts = new Hashtable(actions.length);
for (int i = 0; i < actions.length; ++i)
acts.put(actions[i].getValue(Action.NAME), actions[i]);
- for (int i = 0; i < bindings.length; ++i)
+ for (int i = 0; i < bindings.length; ++i)
if (acts.containsKey(bindings[i].actionName))
map.addActionForKeyStroke(bindings[i].key, (Action) acts.get(bindings[i].actionName));
}
@@ -906,33 +912,16 @@ public abstract class JTextComponent extends JComponent
public JTextComponent()
{
Keymap defkeymap = getKeymap(DEFAULT_KEYMAP);
- boolean creatingKeymap = false;
if (defkeymap == null)
{
defkeymap = addKeymap(DEFAULT_KEYMAP, null);
defkeymap.setDefaultAction(new DefaultEditorKit.DefaultKeyTypedAction());
- creatingKeymap = true;
}
setFocusable(true);
setEditable(true);
enableEvents(AWTEvent.KEY_EVENT_MASK);
updateUI();
-
- // need to do this after updateUI()
- if (creatingKeymap)
- loadKeymap(defkeymap,
- new KeyBinding[] {
- new KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0),
- DefaultEditorKit.backwardAction),
- new KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0),
- DefaultEditorKit.forwardAction),
- new KeyBinding(KeyStroke.getKeyStroke("typed \b"),
- DefaultEditorKit.deletePrevCharAction),
- new KeyBinding(KeyStroke.getKeyStroke("typed \u007f"),
- DefaultEditorKit.deleteNextCharAction)
- },
- getActions());
}
public void setDocument(Document newDoc)
diff --git a/libjava/classpath/javax/swing/text/MaskFormatter.java b/libjava/classpath/javax/swing/text/MaskFormatter.java
new file mode 100644
index 0000000..d12b9ea
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/MaskFormatter.java
@@ -0,0 +1,583 @@
+/* MaskFormatter.java --
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text;
+
+import java.text.ParseException;
+
+import javax.swing.JFormattedTextField;
+
+/**
+ * @author Anthony Balkissoon abalkiss at redhat dot com
+ *
+ */
+public class MaskFormatter extends DefaultFormatter
+{
+ // The declaration of the valid mask characters
+ private static final char NUM_CHAR = '#';
+ private static final char ESCAPE_CHAR = '\'';
+ private static final char UPPERCASE_CHAR = 'U';
+ private static final char LOWERCASE_CHAR = 'L';
+ private static final char ALPHANUM_CHAR = 'A';
+ private static final char LETTER_CHAR = '?';
+ private static final char ANYTHING_CHAR = '*';
+ private static final char HEX_CHAR = 'H';
+
+ /** The mask for this MaskFormatter **/
+ private String mask;
+
+ /**
+ * A String made up of the characters that are not valid for input for
+ * this MaskFormatter.
+ */
+ private String invalidChars;
+
+ /**
+ * A String made up of the characters that are valid for input for
+ * this MaskFormatter.
+ */
+ private String validChars;
+
+ /** A String used in place of missing chracters if the value does not
+ * completely fill in the spaces in the mask.
+ */
+ private String placeHolder;
+
+ /** A character used in place of missing characters if the value does
+ * not completely fill in the spaces in the mask.
+ */
+ private char placeHolderChar = ' ';
+
+ /**
+ * Whether or not stringToValue should return literal characters in the mask.
+ */
+ private boolean valueContainsLiteralCharacters = true;
+
+ /** A String used for easy access to valid HEX characters **/
+ private static String hexString = "0123456789abcdefABCDEF";
+
+ /** An int to hold the length of the mask, accounting for escaped characters **/
+ int maskLength = 0;
+
+ public MaskFormatter ()
+ {
+ // Override super's default behaviour, in MaskFormatter the default
+ // is not to allow invalid values
+ setAllowsInvalid(false);
+ }
+
+ /**
+ * Creates a MaskFormatter with the specified mask.
+ * @specnote doesn't actually throw a ParseException although it
+ * is declared to do so
+ * @param mask
+ * @throws java.text.ParseException
+ */
+ public MaskFormatter (String mask) throws java.text.ParseException
+ {
+ // Override super's default behaviour, in MaskFormatter the default
+ // is not to allow invalid values
+ setAllowsInvalid(false);
+ setMask (mask);
+ }
+
+ /**
+ * Returns the mask used in this MaskFormatter.
+ * @return the mask used in this MaskFormatter.
+ */
+ public String getMask()
+ {
+ return mask;
+ }
+
+ /**
+ * Returns a String containing the characters that are not valid for input
+ * for this MaskFormatter.
+ * @return a String containing the invalid characters.
+ */
+ public String getInvalidCharacters()
+ {
+ return invalidChars;
+ }
+
+ /**
+ * Sets characters that are not valid for input. If
+ * <code>invalidCharacters</code> is non-null then no characters contained
+ * in it will be allowed to be input.
+ *
+ * @param invalidCharacters the String specifying invalid characters.
+ */
+ public void setInvalidCharacters (String invalidCharacters)
+ {
+ this.invalidChars = invalidCharacters;
+ }
+
+ /**
+ * Returns a String containing the characters that are valid for input
+ * for this MaskFormatter.
+ * @return a String containing the valid characters.
+ */
+ public String getValidCharacters()
+ {
+ return validChars;
+ }
+
+ /**
+ * Sets characters that are valid for input. If
+ * <code>validCharacters</code> is non-null then no characters that are
+ * not contained in it will be allowed to be input.
+ *
+ * @param validCharacters the String specifying valid characters.
+ */
+ public void setValidCharacters (String validCharacters)
+ {
+ this.validChars = validCharacters;
+ }
+
+ /**
+ * Returns the place holder String that is used in place of missing
+ * characters when the value doesn't completely fill in the spaces
+ * in the mask.
+ * @return the place holder String.
+ */
+ public String getPlaceholder()
+ {
+ return placeHolder;
+ }
+
+ /**
+ * Sets the string to use if the value does not completely fill in the mask.
+ * If this is null, the place holder character will be used instead.
+ * @param placeholder the String to use if the value doesn't completely
+ * fill in the mask.
+ */
+ public void setPlaceholder (String placeholder)
+ {
+ this.placeHolder = placeholder;
+ }
+
+ /**
+ * Returns the character used in place of missing characters when the
+ * value doesn't completely fill the mask.
+ * @return the place holder character
+ */
+ public char getPlaceholderCharacter()
+ {
+ return placeHolderChar;
+ }
+
+ /**
+ * Sets the char to use if the value does not completely fill in the mask.
+ * This is only used if the place holder String has not been set or does
+ * not completely fill in the mask.
+ * @param placeholder the char to use if the value doesn't completely
+ * fill in the mask.
+ */
+ public void setPlaceholderCharacter (char placeholder)
+ {
+ this.placeHolderChar = placeholder;
+ }
+
+ /**
+ * Returns true if stringToValue should return the literal
+ * characters in the mask.
+ * @return true if stringToValue should return the literal
+ * characters in the mask
+ */
+ public boolean getValueContainsLiteralCharacters()
+ {
+ return valueContainsLiteralCharacters;
+ }
+
+ /**
+ * Determines whether stringToValue will return literal characters or not.
+ * @param containsLiteralChars if true, stringToValue will return the
+ * literal characters in the mask, otherwise it will not.
+ */
+ public void setValueContainsLiteralCharacters (boolean containsLiteralChars)
+ {
+ this.valueContainsLiteralCharacters = containsLiteralChars;
+ }
+
+ /**
+ * Sets the mask for this MaskFormatter.
+ * @specnote doesn't actually throw a ParseException even though it is
+ * declared to do so
+ * @param mask the new mask for this MaskFormatter
+ * @throws ParseException if <code>mask</code> is not valid.
+ */
+ public void setMask (String mask) throws ParseException
+ {
+ this.mask = mask;
+
+ // Update the cached maskLength.
+ int end = mask.length() - 1;
+ maskLength = 0;
+ for (int i = 0; i <= end; i++)
+ {
+ // Handle escape characters properly - they don't add to the maskLength
+ // but 2 escape characters in a row is really one escape character and
+ // one literal single quote, so that does add 1 to the maskLength.
+ if (mask.charAt(i) == '\'')
+ {
+ // Escape characters at the end of the mask don't do anything.
+ if (i != end)
+ maskLength++;
+ i++;
+ }
+ else
+ maskLength++;
+ }
+ }
+
+ /**
+ * Installs this MaskFormatter on the JFormattedTextField.
+ * Invokes valueToString to convert the current value from the
+ * JFormattedTextField to a String, then installs the Actions from
+ * getActions, the DocumentFilter from getDocumentFilter, and the
+ * NavigationFilter from getNavigationFilter.
+ *
+ * If valueToString throws a ParseException, this method sets the text
+ * to an empty String and marks the JFormattedTextField as invalid.
+ */
+ public void install (JFormattedTextField ftf)
+ {
+ super.install(ftf);
+ if (ftf != null)
+ {
+ try
+ {
+ valueToString(ftf.getValue());
+ }
+ catch (ParseException pe)
+ {
+ // Set the text to an empty String and mark the JFormattedTextField
+ // as invalid.
+ ftf.setText("");
+ setEditValid(false);
+ }
+ }
+ }
+
+ /**
+ * Parses the text using the mask, valid characters, and invalid characters
+ * to determine the appropriate Object to return. This strips the literal
+ * characters if necessary and invokes super.stringToValue. If the paramter
+ * is invalid for the current mask and valid/invalid character sets this
+ * method will throw a ParseException.
+ *
+ * @param value the String to parse
+ * @throws ParseException if value doesn't match the mask and valid/invalid
+ * character sets
+ */
+ public Object stringToValue (String value) throws ParseException
+ {
+ int vLength = value.length();
+
+ // For value to be a valid it must be the same length as the mask
+ // note this doesn't take into account symbols that occupy more than
+ // one character, this is something we may possibly need to fix.
+ if (maskLength != vLength)
+ throw new ParseException ("stringToValue passed invalid value", vLength);
+
+ // Check if the value is valid according to the mask and valid/invalid
+ // sets.
+ try
+ {
+ convertValue(value, false);
+ }
+ catch (ParseException pe)
+ {
+ throw new ParseException("stringToValue passed invalid value",
+ pe.getErrorOffset());
+ }
+
+ if (!getValueContainsLiteralCharacters())
+ value = stripLiterals(value);
+ return super.stringToValue(value);
+ }
+
+ /**
+ * Strips the literal characters from the given String.
+ * @param value the String to strip
+ * @return the stripped String
+ */
+ String stripLiterals(String value)
+ {
+ StringBuffer result = new StringBuffer();
+ for (int i = 0; i < value.length(); i++)
+ {
+ // Only append the characters that don't correspond to literal
+ // characters in the mask.
+ switch (mask.charAt(i))
+ {
+ case NUM_CHAR:
+ case UPPERCASE_CHAR:
+ case LOWERCASE_CHAR:
+ case ALPHANUM_CHAR:
+ case LETTER_CHAR:
+ case HEX_CHAR:
+ case ANYTHING_CHAR:
+ result.append(value.charAt(i));
+ break;
+ default:
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * Returns a String representation of the Object value based on the mask.
+ *
+ * @param value the value to convert
+ * @throws ParseException if value is invalid for this mask and valid/invalid
+ * character sets
+ */
+ public String valueToString (Object value) throws ParseException
+ {
+ String result = super.valueToString(value);
+ int rLength = result.length();
+
+ // If value is longer than the mask, truncate it. Note we may need to
+ // account for symbols that are more than one character long.
+ if (rLength > maskLength)
+ result = result.substring(0, maskLength);
+
+ // Verify the validity and convert to upper/lowercase as needed.
+ result = convertValue(result, true);
+ if (rLength < maskLength)
+ return pad(result, rLength);
+ return result;
+ }
+
+ /**
+ * This method takes in a String and runs it through the mask to make
+ * sure that it is valid. If <code>convert</code> is true, it also
+ * converts letters to upper/lowercase as required by the mask.
+ * @param value the String to convert
+ * @param convert true if we should convert letters to upper/lowercase
+ * @return the converted String
+ * @throws ParseException if the given String isn't valid for the mask
+ */
+ String convertValue(String value, boolean convert) throws ParseException
+ {
+ StringBuffer result = new StringBuffer(value);
+ char markChar;
+ char resultChar;
+ boolean literal;
+
+ // this boolean is specifically to avoid calling the isCharValid method
+ // when neither invalidChars or validChars has been set
+ boolean checkCharSets = (invalidChars != null || validChars != null);
+
+ for (int i = 0, j = 0; i < value.length(); i++, j++)
+ {
+ literal = false;
+ resultChar = result.charAt(i);
+ // This switch block on the mask character checks that the character
+ // within <code>value</code> at that point is valid according to the
+ // mask and also converts to upper/lowercase as needed.
+ switch (mask.charAt(j))
+ {
+ case NUM_CHAR:
+ if (!Character.isDigit(resultChar))
+ throw new ParseException("Number expected", i);
+ break;
+ case UPPERCASE_CHAR:
+ if (!Character.isLetter(resultChar))
+ throw new ParseException("Letter expected", i);
+ if (convert)
+ result.setCharAt(i, Character.toUpperCase(resultChar));
+ break;
+ case LOWERCASE_CHAR:
+ if (!Character.isLetter(resultChar))
+ throw new ParseException("Letter expected", i);
+ if (convert)
+ result.setCharAt(i, Character.toLowerCase(resultChar));
+ break;
+ case ALPHANUM_CHAR:
+ if (!Character.isLetterOrDigit(resultChar))
+ throw new ParseException("Letter or number expected", i);
+ break;
+ case LETTER_CHAR:
+ if (!Character.isLetter(resultChar))
+ throw new ParseException("Letter expected", i);
+ break;
+ case HEX_CHAR:
+ if (hexString.indexOf(resultChar) == -1)
+ throw new ParseException("Hexadecimal character expected", i);
+ break;
+ case ANYTHING_CHAR:
+ break;
+ case ESCAPE_CHAR:
+ // Escape character, check the next character to make sure that
+ // the literals match
+ j++;
+ literal = true;
+ if (resultChar != mask.charAt(j))
+ throw new ParseException ("Invalid character: "+resultChar, i);
+ break;
+ default:
+ literal = true;
+ if (!getValueContainsLiteralCharacters() && convert)
+ throw new ParseException ("Invalid character: "+resultChar, i);
+ else if (resultChar != mask.charAt(j))
+ throw new ParseException ("Invalid character: "+resultChar, i);
+ }
+ // If necessary, check if the character is valid.
+ if (!literal && checkCharSets && !isCharValid(resultChar))
+ throw new ParseException("invalid character: "+resultChar, i);
+
+ }
+ return result.toString();
+ }
+
+ /**
+ * Convenience method used by many other methods to check if a character is
+ * valid according to the mask, the validChars, and the invalidChars. To
+ * be valid a character must:
+ * 1. be allowed by the mask
+ * 2. be present in any non-null validChars String
+ * 3. not be present in any non-null invalidChars String
+ * @param testChar the character to test
+ * @return true if the character is valid
+ */
+ boolean isCharValid(char testChar)
+ {
+ char lower = Character.toLowerCase(testChar);
+ char upper = Character.toUpperCase(testChar);
+ // If validChars isn't null, the character must appear in it.
+ if (validChars != null)
+ if (validChars.indexOf(lower) == -1 && validChars.indexOf(upper) == -1)
+ return false;
+ // If invalidChars isn't null, the character must not appear in it.
+ if (invalidChars != null)
+ if (invalidChars.indexOf(lower) != -1
+ || invalidChars.indexOf(upper) != -1)
+ return false;
+ return true;
+ }
+
+ /**
+ * Pads the value with literals, the placeholder String and/or placeholder
+ * character as appropriate.
+ * @param value the value to pad
+ * @param currLength the current length of the value
+ * @return the padded String
+ */
+ String pad (String value, int currLength)
+ {
+ StringBuffer result = new StringBuffer(value);
+ int index = currLength;
+ while (result.length() < maskLength)
+ {
+ // The character used to pad may be a literal, a character from the
+ // place holder string, or the place holder character. getPadCharAt
+ // will find the proper one for us.
+ result.append (getPadCharAt(index));
+ index++;
+ }
+ return result.toString();
+ }
+
+ /**
+ * Returns the character with which to pad the value at the given index
+ * position. If the mask has a literal at this position, this is returned
+ * otherwise if the place holder string is initialized and is longer than
+ * <code>i</code> characters then the character at position <code>i</code>
+ * from this String is returned. Else, the place holder character is
+ * returned.
+ * @param i the index at which we want to pad the value
+ * @return the character with which we should pad the value
+ */
+ char getPadCharAt(int i)
+ {
+ boolean escaped = false;
+ int target = i;
+ char maskChar;
+ int holderLength = placeHolder == null ? -1 : placeHolder.length();
+ // We must iterate through the mask from the beginning, because the given
+ // index doesn't account for escaped characters. For example, with the
+ // mask "1A'A''A1" index 2 refers to the literalized A, not to the
+ // single quotation.
+ for (int n = 0; n < mask.length(); n++)
+ {
+ maskChar = mask.charAt(n);
+ if (maskChar == ESCAPE_CHAR && !escaped)
+ {
+ target++;
+ escaped = true;
+ }
+ else if (escaped == true)
+ {
+ // Check if target == n which means we've come to the character
+ // we want to return and since it is a literal (because escaped
+ // is true), we return it.
+ if (target == n)
+ return maskChar;
+ escaped = false;
+ }
+ if (target == n)
+ {
+ // We've come to the character we want to return. It wasn't
+ // escaped so if it isn't a literal we should return either
+ // the character from place holder string or the place holder
+ // character, depending on whether or not the place holder
+ // string is long enough.
+ switch (maskChar)
+ {
+ case NUM_CHAR:
+ case UPPERCASE_CHAR:
+ case LOWERCASE_CHAR:
+ case ALPHANUM_CHAR:
+ case LETTER_CHAR:
+ case HEX_CHAR:
+ case ANYTHING_CHAR:
+ if (holderLength > i)
+ return placeHolder.charAt(i);
+ else
+ return placeHolderChar;
+ default:
+ return maskChar;
+ }
+ }
+ }
+ // This shouldn't happen
+ throw new AssertionError("MaskFormatter.getMaskCharAt failed");
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/NumberFormatter.java b/libjava/classpath/javax/swing/text/NumberFormatter.java
new file mode 100644
index 0000000..a858ff4
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/NumberFormatter.java
@@ -0,0 +1,86 @@
+/* NumberFormatter.java --
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text;
+
+import java.text.Format;
+import java.text.NumberFormat;
+
+/**
+ * <code>NumberFormatter</code> is an {@link InternationalFormatter}
+ * that implements value to string and string to value conversion via
+ * an instance of {@link NumberFormat}.
+ *
+ * @author Anthony Balkissoon abalkiss at redhat dot com
+ * @since 1.4
+ */
+public class NumberFormatter extends InternationalFormatter
+{
+
+ /**
+ * Creates a NumberFormatter with the default NumberFormat from
+ * NumberFormat.getNumberInstance().
+ */
+ public NumberFormatter ()
+ {
+ this (NumberFormat.getNumberInstance());
+ }
+
+ /**
+ * Creates a NumberFormatter with the specified NumberFormat.
+ * @param format the NumberFormat to use for this NumberFormatter.
+ */
+ public NumberFormatter (NumberFormat format)
+ {
+ super(format);
+ setFormat(format);
+ }
+
+ /**
+ * Sets the NumberFormat that this NumberFormatter will use to determine
+ * legal values for editing and displaying.
+ *
+ * @param format the Format to use to determine legal values.
+ */
+ public void setFormat (Format format)
+ {
+ // TODO: This should be different from the super implementation
+ // but I don't yet know how.
+ super.setFormat(format);
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/PasswordView.java b/libjava/classpath/javax/swing/text/PasswordView.java
index c3aa66c..e54331c5 100644
--- a/libjava/classpath/javax/swing/text/PasswordView.java
+++ b/libjava/classpath/javax/swing/text/PasswordView.java
@@ -41,6 +41,7 @@ package javax.swing.text;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
+import java.awt.Rectangle;
import java.awt.Shape;
import javax.swing.JPasswordField;
@@ -211,7 +212,10 @@ public class PasswordView
/**
* Provides a mapping from the document model coordinate space to the
* coordinate space of the view mapped to it.
- *
+ *
+ * This method is overridden to provide a correct mapping with respect to the
+ * echo char and not to the real content.
+ *
* @param pos - the position to convert >= 0
* @param a - the allocated region to render into
* @param b - typesafe enumeration to indicate bias to a position in the model.
@@ -222,7 +226,35 @@ public class PasswordView
public Shape modelToView(int pos, Shape a, Position.Bias b)
throws BadLocationException
{
- return super.modelToView(pos, a, b);
+ Shape newAlloc = adjustAllocation(a);
+
+ // Ensure metrics are up-to-date.
+ updateMetrics();
+
+ // Get rectangle of the line containing position.
+ int lineIndex = getElement().getElementIndex(pos);
+ Rectangle rect = lineToRect(newAlloc, lineIndex);
+
+ // Get the rectangle for position.
+ Element line = getElement().getElement(lineIndex);
+ int lineStart = line.getStartOffset();
+ Segment segment = getLineBuffer();
+ segment.array = new char[pos - lineStart];
+ char echoChar = getEchoChar();
+ for (int i = 0; i < segment.array.length; ++i)
+ segment.array[i] = echoChar;
+ segment.offset = 0;
+ segment.count = segment.array.length;
+
+ int xoffset = Utilities.getTabbedTextWidth(segment, metrics, rect.x,
+ this, lineStart);
+
+ // Calc the real rectangle.
+ rect.x += xoffset;
+ rect.width = 1;
+ rect.height = metrics.getHeight();
+
+ return rect;
}
/**
@@ -239,6 +271,8 @@ public class PasswordView
*/
public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias)
{
+ // FIXME: This only provides a view->model mapping for the real text
+ // content and does not respect the echo char.
return super.viewToModel(fx, fy, a, bias);
}
}
diff --git a/libjava/classpath/javax/swing/text/PlainDocument.java b/libjava/classpath/javax/swing/text/PlainDocument.java
index 0c00a06..2200cae 100644
--- a/libjava/classpath/javax/swing/text/PlainDocument.java
+++ b/libjava/classpath/javax/swing/text/PlainDocument.java
@@ -113,6 +113,46 @@ public class PlainDocument extends AbstractDocument
int elementIndex = rootElement.getElementIndex(offset);
Element firstElement = rootElement.getElement(elementIndex);
+ // If we're inserting immediately after a newline we have to fix the
+ // Element structure.
+ if (offset > 0)
+ {
+ try
+ {
+ String s = getText(offset - 1, 1);
+ if (s.equals("\n"))
+ {
+ int newEl2EndOffset = end;
+ boolean replaceNext = false;
+ if (rootElement.getElementCount() > elementIndex + 1)
+ {
+ replaceNext = true;
+ newEl2EndOffset =
+ rootElement.getElement(elementIndex + 1).getEndOffset();
+ }
+ Element newEl1 =
+ createLeafElement(rootElement, firstElement.getAttributes(),
+ firstElement.getStartOffset(), offset);
+ Element newEl2 =
+ createLeafElement (rootElement, firstElement.getAttributes(),
+ offset, newEl2EndOffset);
+ if (replaceNext)
+ rootElement.replace(elementIndex, 2, new Element[] { newEl1, newEl2 });
+ else
+ rootElement.replace(elementIndex, 1, new Element[] { newEl1, newEl2 });
+ firstElement = newEl2;
+ elementIndex ++;
+ }
+ }
+ catch (BadLocationException ble)
+ {
+ // This shouldn't happen.
+ AssertionError ae = new AssertionError();
+ ae.initCause(ble);
+ throw ae;
+ }
+ }
+
// added and removed are Element arrays used to add an ElementEdit
// to the DocumentEvent if there were entire lines added or removed.
Element[] removed = new Element[1];
diff --git a/libjava/classpath/javax/swing/text/PlainView.java b/libjava/classpath/javax/swing/text/PlainView.java
index 9f5ee8ad..a318ee7 100644
--- a/libjava/classpath/javax/swing/text/PlainView.java
+++ b/libjava/classpath/javax/swing/text/PlainView.java
@@ -185,7 +185,6 @@ public class PlainView extends View implements TabExpander
JTextComponent textComponent = (JTextComponent) getContainer();
- g.setFont(textComponent.getFont());
selectedColor = textComponent.getSelectedTextColor();
unselectedColor = textComponent.getForeground();
disabledColor = textComponent.getDisabledTextColor();
@@ -513,7 +512,8 @@ public class PlainView extends View implements TabExpander
else
{
Rectangle repaintRec = rec0.union(rec1);
- host.repaint();
+ host.repaint(repaintRec.x, repaintRec.y, repaintRec.width,
+ repaintRec.height);
}
}
@@ -530,35 +530,5 @@ public class PlainView extends View implements TabExpander
lineBuffer = new Segment();
return lineBuffer;
}
-
- /**
- * Returns the document position that is (visually) nearest to the given
- * document position <code>pos</code> in the given direction <code>d</code>.
- *
- * @param c the text component
- * @param pos the document position
- * @param b the bias for <code>pos</code>
- * @param d the direction, must be either {@link SwingConstants#NORTH},
- * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or
- * {@link SwingConstants#EAST}
- * @param biasRet an array of {@link Position.Bias} that can hold at least
- * one element, which is filled with the bias of the return position
- * on method exit
- *
- * @return the document position that is (visually) nearest to the given
- * document position <code>pos</code> in the given direction
- * <code>d</code>
- *
- * @throws BadLocationException if <code>pos</code> is not a valid offset in
- * the document model
- */
- public int getNextVisualPositionFrom(JTextComponent c, int pos,
- Position.Bias b, int d,
- Position.Bias[] biasRet)
- throws BadLocationException
- {
- // TODO: Implement this properly.
- throw new AssertionError("Not implemented yet.");
- }
}
diff --git a/libjava/classpath/javax/swing/text/StyleContext.java b/libjava/classpath/javax/swing/text/StyleContext.java
index 6c4e2994..dabc0ba 100644
--- a/libjava/classpath/javax/swing/text/StyleContext.java
+++ b/libjava/classpath/javax/swing/text/StyleContext.java
@@ -362,9 +362,8 @@ public class StyleContext
public boolean isEqual(AttributeSet attr)
{
- return attr != null
- && attr.containsAttributes(this)
- && this.containsAttributes(attr);
+ return getAttributeCount() == attr.getAttributeCount()
+ && this.containsAttributes(attr);
}
public String toString()
diff --git a/libjava/classpath/javax/swing/text/StyledEditorKit.java b/libjava/classpath/javax/swing/text/StyledEditorKit.java
index e71f992..c4eef44 100644
--- a/libjava/classpath/javax/swing/text/StyledEditorKit.java
+++ b/libjava/classpath/javax/swing/text/StyledEditorKit.java
@@ -67,7 +67,7 @@ public class StyledEditorKit extends DefaultEditorKit
*/
public UnderlineAction()
{
- super("TODO"); // TODO: Figure out name for this action.
+ super("font-underline");
}
/**
@@ -97,7 +97,7 @@ public class StyledEditorKit extends DefaultEditorKit
*/
public ItalicAction()
{
- super("TODO"); // TODO: Figure out correct name of this Action.
+ super("font-italic");
}
/**
@@ -127,7 +127,7 @@ public class StyledEditorKit extends DefaultEditorKit
*/
public BoldAction()
{
- super("TODO"); // TODO: Figure out correct name of this Action.
+ super("font-bold");
}
/**
@@ -585,8 +585,26 @@ public class StyledEditorKit extends DefaultEditorKit
public Action[] getActions()
{
Action[] actions1 = super.getActions();
- Action[] myActions = new Action[] { new BoldAction(), new ItalicAction(),
- new UnderlineAction() };
+ Action[] myActions = new Action[] {
+ new FontSizeAction("font-size-8", 8),
+ new FontSizeAction("font-size-10", 10),
+ new FontSizeAction("font-size-12", 12),
+ new FontSizeAction("font-size-14", 14),
+ new FontSizeAction("font-size-16", 16),
+ new FontSizeAction("font-size-18", 18),
+ new FontSizeAction("font-size-24", 24),
+ new FontSizeAction("font-size-36", 36),
+ new FontSizeAction("font-size-48", 48),
+ new FontFamilyAction("font-family-Serif", "Serif"),
+ new FontFamilyAction("font-family-Monospaced", "Monospaced"),
+ new FontFamilyAction("font-family-SansSerif", "SansSerif"),
+ new AlignmentAction("left-justify", StyleConstants.ALIGN_LEFT),
+ new AlignmentAction("center-justify", StyleConstants.ALIGN_CENTER),
+ new AlignmentAction("right-justify", StyleConstants.ALIGN_RIGHT),
+ new BoldAction(),
+ new ItalicAction(),
+ new UnderlineAction()
+ };
return TextAction.augmentList(actions1, myActions);
}
@@ -696,9 +714,8 @@ public class StyledEditorKit extends DefaultEditorKit
protected void createInputAttributes(Element element,
MutableAttributeSet set)
{
- AttributeSet atts = element.getAttributes();
- set.removeAttributes(set);
// FIXME: Filter out component, icon and element name attributes.
- set.addAttributes(atts);
+ set.removeAttributes(set);
+ set.addAttributes(element.getAttributes());
}
}
diff --git a/libjava/classpath/javax/swing/text/TableView.java b/libjava/classpath/javax/swing/text/TableView.java
new file mode 100644
index 0000000..d3113b8
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/TableView.java
@@ -0,0 +1,465 @@
+/* TableView.java -- A view impl for tables inside styled text
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text;
+
+import java.awt.Rectangle;
+import java.awt.Shape;
+
+import javax.swing.SizeRequirements;
+import javax.swing.event.DocumentEvent;
+
+/**
+ * A {@link View} implementation for rendering tables inside styled text.
+ * Tables are rendered as vertical boxes (see {@link BoxView}). These boxes
+ * have a number of child views, which are the rows of the table. These are
+ * horizontal boxes containing the actuall cells of the table. These cells
+ * can be arbitrary view implementations and are fetched via the
+ * {@link ViewFactory} returned by {@link View#getViewFactory}.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class TableView
+ extends BoxView
+{
+
+ /**
+ * A view implementation that renders a row of a <code>TableView</code>.
+ * This is implemented as a horizontal box that contains the actual cells
+ * of the table.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ public class TableRow
+ extends BoxView
+ {
+ /**
+ * Creates a new instance of <code>TableRow</code>.
+ *
+ * @param el the element for which to create a row view
+ */
+ public TableRow(Element el)
+ {
+ super(el, X_AXIS);
+ }
+
+ /**
+ * Replaces some child views with a new set of child views. This is
+ * implemented to call the superclass behaviour and invalidates the row
+ * grid so that rows and columns will be recalculated.
+ *
+ * @param offset the start offset at which to replace views
+ * @param length the number of views to remove
+ * @param views the new set of views
+ */
+ public void replace(int offset, int length, View[] views)
+ {
+ super.replace(offset, length, views);
+ layoutChanged(X_AXIS);
+ }
+
+ /**
+ * Lays out the box's child views along the major axis. This is
+ * reimplemented so that the child views all have the width of their
+ * column.
+ *
+ * @param targetSpan the total span of the view
+ * @param axis the axis that is laid out
+ * @param offsets an array that holds the offsets of the child views after
+ * this method returned
+ * @param spans an array that holds the spans of the child views after this
+ * method returned
+ */
+ protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets,
+ int[] spans)
+ {
+ // TODO: Maybe prepare columnSpans and columnOffsets.
+
+ // Some sanity checks. If these preconditions are not met, then the
+ // following code will not work. Also, there must be something
+ // seriously wrong then.
+ assert(offsets.length == columnOffsets.length);
+ assert(spans.length == columnSpans.length);
+ assert(offsets.length == spans.length);
+ for (int i = 0; i < offsets.length; ++i)
+ {
+ offsets[i] = columnOffsets[i];
+ spans[i] = columnSpans[i];
+ }
+ }
+
+ /**
+ * Lays out the box's child views along the minor axis (the orthogonal axis
+ * to the major axis). This is reimplemented to call the super behaviour
+ * and then adjust the span of the child views that span multiple rows.
+ *
+ * @param targetSpan the total span of the view
+ * @param axis the axis that is laid out
+ * @param offsets an array that holds the offsets of the child views after
+ * this method returned
+ * @param spans an array that holds the spans of the child views after this
+ * method returned
+ */
+ protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets,
+ int[] spans)
+ {
+ // FIXME: Figure out how to fetch the row heights from the TableView's
+ // element.
+ super.layoutMajorAxis(targetSpan, axis, offsets, spans);
+ }
+
+ /**
+ * Determines the resizeability of this view along the specified axis.
+ *
+ * @param axis the axis of which to fetch the resizability
+ *
+ * @return the resize weight or &lt;= 0 if this view is not resizable
+ *
+ * @throws IllegalArgumentException when an illegal axis is specified
+ */
+ public int getResizeWeight(int axis)
+ {
+ // TODO: Figure out if this is ok. I would think so, but better test
+ // this.
+ return 0;
+ }
+
+ /**
+ * Returns the child view that represents the specified position in the
+ * model. This is reimplemented because in this view we do not necessarily
+ * have a one to one mapping of child elements to child views.
+ *
+ * @param pos the model position for which to query the view
+ * @param a the allocation of this view
+ *
+ * @return the view that corresponds to the specified model position or
+ * <code>null</code> if there is none
+ */
+ protected View getViewAtPosition(int pos, Rectangle a)
+ {
+ // FIXME: Do not call super here. Instead walk through the child views
+ // and look for a range that contains the given position.
+ return super.getViewAtPosition(pos, a);
+ }
+ }
+
+ /**
+ * This class is deprecated and not used anymore. Table cells are
+ * rendered by an arbitrary <code>View</code> implementation.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ *
+ * @deprecated Table cells are now rendered by an arbitrary <code>View</code>
+ * implementation.
+ */
+ public class TableCell
+ extends BoxView
+ {
+
+ /**
+ * The row number of this cell.
+ */
+ private int row;
+
+ /**
+ * The column number of this cell.
+ */
+ private int column;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param el the element
+ *
+ * @deprecated Table cells are now rendered by an arbitrary
+ * <code>View</code> implementation.
+ */
+ public TableCell(Element el)
+ {
+ super(el, X_AXIS);
+ }
+
+ /**
+ * Returns the number of columns that this cell spans.
+ *
+ * @return the number of columns that this cell spans
+ *
+ * @deprecated Table cells are now rendered by an arbitrary
+ * <code>View</code> implementation.
+ */
+ public int getColumnCount()
+ {
+ // TODO: Figure out if this is right. However, this is not so important
+ // since this class isn't used anyway (except maybe be application code
+ // that still uses this deprecated class).
+ return 1;
+ }
+
+ /**
+ * Returns the number of rows that this cell spans.
+ *
+ * @return the number of rows that this cell spans
+ *
+ * @deprecated Table cells are now rendered by an arbitrary
+ * <code>View</code> implementation.
+ */
+ public int getRowCount()
+ {
+ // TODO: Figure out if this is right. However, this is not so important
+ // since this class isn't used anyway (except maybe be application code
+ // that still uses this deprecated class).
+ return 1;
+ }
+
+ /**
+ * Sets the grid location of this table cell.
+ *
+ * @param r the row of this cell
+ * @param c the column of this cell
+ *
+ * @deprecated Table cells are now rendered by an arbitrary
+ * <code>View</code> implementation.
+ */
+ public void setGridLocation(int r, int c)
+ {
+ row = r;
+ column = c;
+ }
+
+ /**
+ * Returns the row number of this cell.
+ *
+ * @return the row number of this cell
+ *
+ * @deprecated Table cells are now rendered by an arbitrary
+ * <code>View</code> implementation.
+ */
+ public int getGridRow()
+ {
+ return row;
+ }
+
+ /**
+ * Returns the column number of this cell.
+ *
+ * @return the column number of this cell
+ *
+ * @deprecated Table cells are now rendered by an arbitrary
+ * <code>View</code> implementation.
+ */
+ public int getGridColumn()
+ {
+ return column;
+ }
+ }
+
+ /**
+ * The offsets of the columns of this table. Package private to avoid
+ * synthetic accessor methods.
+ */
+ int[] columnOffsets;
+
+ /**
+ * The spans of the columns of this table. Package private to avoid
+ * synthetic accessor methods.
+ */
+ int[] columnSpans;
+
+ /**
+ * The size requirements of the columns.
+ */
+ private SizeRequirements[] columnRequirements;
+
+ /**
+ * Creates a new instance of <code>TableView</code>.
+ *
+ * @param el the element for which to create a table view
+ */
+ public TableView(Element el)
+ {
+ super(el, Y_AXIS);
+ int numChildren = el.getElementCount();
+ View[] rows = new View[numChildren];
+ for (int i = 0; i < numChildren; ++i)
+ {
+ Element rowEl = el.getElement(i);
+ TableRow rowView = createTableRow(rowEl);
+ rows[i] = rowView;
+ }
+ replace(0, 0, rows);
+ }
+
+ /**
+ * Replaces a number of child views with a set of new child views. This is
+ * implemented to call the superclass behaviour and invalidate the layout.
+ *
+ * @param offset the offset at which to replace child views
+ * @param length the number of child views to remove
+ * @param views the new set of views
+ */
+ public void replace(int offset, int length, View[] views)
+ {
+ super.replace(offset, length, views);
+ layoutChanged(Y_AXIS);
+ }
+
+ /**
+ * Creates a view for a table row.
+ *
+ * @param el the element that represents the table row
+ *
+ * @return a view for rendering the table row
+ */
+ protected TableRow createTableRow(Element el)
+ {
+ return new TableRow(el);
+ }
+
+ /**
+ * Creates a view for a table cell. This method is deprecated and not used
+ * anymore.
+ *
+ * @param el the element that represents the table cell
+ *
+ * @return a view for rendering the table cell
+ *
+ * @deprecated Table cells are now rendered by an arbitrary
+ * <code>View</code> implementation.
+ */
+ protected TableCell createTableCell(Element el)
+ {
+ return new TableCell(el);
+ }
+
+ protected void forwardUpdate(DocumentEvent.ElementChange ec, DocumentEvent e,
+ Shape a, ViewFactory vf)
+ {
+ // TODO: Figure out what to do here.
+ }
+
+ /**
+ * Lays out the columns to fit within the specified target span.
+ *
+ * @param targetSpan the total span for the columns
+ * @param offsets an array that holds the offsets of the columns when this
+ * method returns
+ * @param spans an array that holds the spans of the columns when this method
+ * returns
+ * @param reqs the size requirements for each column
+ */
+ protected void layoutColumns(int targetSpan, int[] offsets, int spans[],
+ SizeRequirements[] reqs)
+ {
+ // TODO: Figure out what exactly to do here.
+ }
+
+ /**
+ * Lays out the child views along the minor axis of the table (that is the
+ * horizontal axis). This is implemented to call {@link #layoutColumns} to
+ * layout the column layout of this table, and then forward to the superclass
+ * to actually lay out the rows.
+ *
+ * @param targetSpan the available span along the minor (horizontal) axis
+ * @param axis the axis
+ * @param offsets an array that holds the offsets of the columns when this
+ * method returns
+ * @param spans an array that holds the spans of the columns when this method
+ * returns
+ */
+ protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets,
+ int[] spans)
+ {
+ // TODO: Prepare size requirements for the columns.
+ layoutColumns(targetSpan, columnOffsets, columnSpans, columnRequirements);
+ super.layoutMinorAxis(targetSpan, axis, offsets, spans);
+ }
+
+ /**
+ * Calculates the requirements of this view for the minor (== horizontal)
+ * axis.
+ *
+ * This is reimplemented to calculate the requirements as the sum of the
+ * size requirements of the columns.
+ *
+ * @param axis the axis
+ * @param req the size requirements object to use, if <code>null</code> a new
+ * one will be created
+ */
+ protected SizeRequirements calculateMinorAxisRequirements(int axis,
+ SizeRequirements req)
+ {
+ // TODO: Maybe prepare columnRequirements.
+ SizeRequirements res = req;
+ if (res == null)
+ res = new SizeRequirements();
+ else
+ {
+ res.alignment = 0.5f;
+ res.maximum = 0;
+ res.minimum = 0;
+ res.preferred = 0;
+ }
+
+ for (int i = 0; i < columnRequirements.length; ++i)
+ {
+ res.minimum += columnRequirements[i].minimum;
+ res.preferred += columnRequirements[i].preferred;
+ res.maximum += columnRequirements[i].maximum;
+ // TODO: Do we have to handle alignment somehow?
+ }
+ return res;
+ }
+
+ /**
+ * Returns the child view that represents the specified position in the
+ * model. This is reimplemented because in this view we do not necessarily
+ * have a one to one mapping of child elements to child views.
+ *
+ * @param pos the model position for which to query the view
+ * @param a the allocation of this view
+ *
+ * @return the view that corresponds to the specified model position or
+ * <code>null</code> if there is none
+ */
+ protected View getViewAtPosition(int pos, Rectangle a)
+ {
+ // FIXME: Do not call super here. Instead walk through the child views
+ // and look for a range that contains the given position.
+ return super.getViewAtPosition(pos, a);
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/Utilities.java b/libjava/classpath/javax/swing/text/Utilities.java
index 7830b2f..1adc8ff 100644
--- a/libjava/classpath/javax/swing/text/Utilities.java
+++ b/libjava/classpath/javax/swing/text/Utilities.java
@@ -45,6 +45,7 @@ import java.awt.Rectangle;
import java.text.BreakIterator;
import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
/**
* A set of utilities to deal with text. This is used by several other classes
@@ -573,10 +574,11 @@ public class Utilities
View rootView = c.getUI().getRootView(c);
Rectangle r = c.modelToView(offset);
int offs = c.viewToModel(new Point(x, r.y));
- int pos = rootView.getNextVisualPositionFrom(c, offs,
- Position.Bias.Forward,
- SwingConstants.NORTH,
- new Position.Bias[1]);
+ int pos = rootView.getNextVisualPositionFrom(offs,
+ Position.Bias.Forward,
+ SwingUtilities.calculateInnerArea(c, null),
+ SwingConstants.NORTH,
+ new Position.Bias[1]);
return pos;
}
@@ -599,10 +601,11 @@ public class Utilities
View rootView = c.getUI().getRootView(c);
Rectangle r = c.modelToView(offset);
int offs = c.viewToModel(new Point(x, r.y));
- int pos = rootView.getNextVisualPositionFrom(c, offs,
- Position.Bias.Forward,
- SwingConstants.SOUTH,
- new Position.Bias[1]);
+ int pos = rootView.getNextVisualPositionFrom(offs,
+ Position.Bias.Forward,
+ SwingUtilities.calculateInnerArea(c, null),
+ SwingConstants.SOUTH,
+ new Position.Bias[1]);
return pos;
}
}
diff --git a/libjava/classpath/javax/swing/text/View.java b/libjava/classpath/javax/swing/text/View.java
index daab347..b835842 100644
--- a/libjava/classpath/javax/swing/text/View.java
+++ b/libjava/classpath/javax/swing/text/View.java
@@ -447,9 +447,11 @@ public abstract class View implements SwingConstants
protected void updateLayout(DocumentEvent.ElementChange ec,
DocumentEvent ev, Shape shape)
{
- Rectangle b = shape.getBounds();
- if (ec != null)
- preferenceChanged(this, true, true);
+ if (ec != null && shape != null)
+ preferenceChanged(null, true, true);
+ Container c = getContainer();
+ if (c != null)
+ c.repaint();
}
/**
@@ -599,9 +601,9 @@ public abstract class View implements SwingConstants
* Returns the document position that is (visually) nearest to the given
* document position <code>pos</code> in the given direction <code>d</code>.
*
- * @param c the text component
* @param pos the document position
* @param b the bias for <code>pos</code>
+ * @param a the allocation for this view
* @param d the direction, must be either {@link SwingConstants#NORTH},
* {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or
* {@link SwingConstants#EAST}
@@ -615,9 +617,31 @@ public abstract class View implements SwingConstants
*
* @throws BadLocationException if <code>pos</code> is not a valid offset in
* the document model
+ * @throws IllegalArgumentException if <code>d</code> is not a valid direction
*/
- public abstract int getNextVisualPositionFrom(JTextComponent c, int pos,
- Position.Bias b, int d,
- Position.Bias[] biasRet)
- throws BadLocationException;
+ public int getNextVisualPositionFrom(int pos, Position.Bias b,
+ Shape a, int d,
+ Position.Bias[] biasRet)
+ throws BadLocationException
+ {
+ int ret = pos;
+ switch (d)
+ {
+ case WEST:
+ ret = pos - 1;
+ break;
+ case EAST:
+ ret = pos + 1;
+ break;
+ case NORTH:
+ // TODO: Implement this
+ break;
+ case SOUTH:
+ // TODO: Implement this
+ break;
+ default:
+ throw new IllegalArgumentException("Illegal value for d");
+ }
+ return ret;
+ }
}
diff --git a/libjava/classpath/javax/swing/text/WrappedPlainView.java b/libjava/classpath/javax/swing/text/WrappedPlainView.java
index b03399d..baba343 100644
--- a/libjava/classpath/javax/swing/text/WrappedPlainView.java
+++ b/libjava/classpath/javax/swing/text/WrappedPlainView.java
@@ -620,36 +620,6 @@ public class WrappedPlainView extends BoxView implements TabExpander
currLineStart = currLineEnd;
}
}
-
- /**
- * Returns the document position that is (visually) nearest to the given
- * document position <code>pos</code> in the given direction <code>d</code>.
- *
- * @param c the text component
- * @param pos the document position
- * @param b the bias for <code>pos</code>
- * @param d the direction, must be either {@link SwingConstants#NORTH},
- * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or
- * {@link SwingConstants#EAST}
- * @param biasRet an array of {@link Position.Bias} that can hold at least
- * one element, which is filled with the bias of the return position
- * on method exit
- *
- * @return the document position that is (visually) nearest to the given
- * document position <code>pos</code> in the given direction
- * <code>d</code>
- *
- * @throws BadLocationException if <code>pos</code> is not a valid offset
- * in the document model
- */
- public int getNextVisualPositionFrom(JTextComponent c, int pos,
- Position.Bias b, int d,
- Position.Bias[] biasRet)
- throws BadLocationException
- {
- // TODO: Implement this properly.
- throw new AssertionError("Not implemented yet.");
- }
/**
* This method is called from insertUpdate and removeUpdate.
diff --git a/libjava/classpath/javax/swing/text/html/BlockView.java b/libjava/classpath/javax/swing/text/html/BlockView.java
new file mode 100644
index 0000000..6274e7b
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/BlockView.java
@@ -0,0 +1,301 @@
+/* BlockView.java --
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text.html;
+
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+
+import javax.swing.SizeRequirements;
+import javax.swing.event.DocumentEvent;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BoxView;
+import javax.swing.text.Element;
+import javax.swing.text.View;
+import javax.swing.text.ViewFactory;
+
+/**
+ * @author Lillian Angel <langel@redhat.com>
+ */
+public class BlockView extends BoxView
+{
+
+ /**
+ * Creates a new view that represents an html box.
+ * This can be used for a number of elements.
+ *
+ * @param elem - the element to create a view for
+ * @param axis - either View.X_AXIS or View.Y_AXIS
+ */
+ public BlockView(Element elem, int axis)
+ {
+ super(elem, axis);
+ }
+
+ /**
+ * Creates the parent view for this. It is called before
+ * any other methods, if the parent view is working properly.
+ * Implemented to forward to the superclass and call
+ * setPropertiesFromAttributes to set the paragraph
+ * properties.
+ *
+ * @param parent - the new parent, or null if the view
+ * is being removed from a parent it was added to.
+ */
+ public void setParent(View parent)
+ {
+ super.setParent(parent);
+
+ if (parent != null)
+ setPropertiesFromAttributes();
+ }
+
+ /**
+ * Calculates the requirements along the major axis.
+ * This is implemented to call the superclass and then
+ * adjust it if the CSS width or height attribute is specified
+ * and applicable.
+ *
+ * @param axis - the axis to check the requirements for.
+ * @param r - the SizeRequirements. If null, one is created.
+ * @return the new SizeRequirements object.
+ */
+ protected SizeRequirements calculateMajorAxisRequirements(int axis,
+ SizeRequirements r)
+ {
+ SizeRequirements sr = super.calculateMajorAxisRequirements(axis, r);
+ // FIXME: adjust it if the CSS width or height attribute is specified
+ // and applicable
+ return sr;
+ }
+
+ /**
+ * Calculates the requirements along the minor axis.
+ * This is implemented to call the superclass and then
+ * adjust it if the CSS width or height attribute is specified
+ * and applicable.
+ *
+ * @param axis - the axis to check the requirements for.
+ * @param r - the SizeRequirements. If null, one is created.
+ * @return the new SizeRequirements object.
+ */
+ protected SizeRequirements calculateMinorAxisRequirements(int axis,
+ SizeRequirements r)
+ {
+ SizeRequirements sr = super.calculateMinorAxisRequirements(axis, r);
+ // FIXME: adjust it if the CSS width or height attribute is specified
+ // and applicable.
+ return sr;
+ }
+
+ /**
+ * Lays out the box along the minor axis (the axis that is
+ * perpendicular to the axis that it represents). The results
+ * of the layout are placed in the given arrays which are
+ * the allocations to the children along the minor axis.
+ *
+ * @param targetSpan - the total span given to the view, also
+ * used to layout the children.
+ * @param axis - the minor axis
+ * @param offsets - the offsets from the origin of the view for
+ * all the child views. This is a return value and is filled in by this
+ * function.
+ * @param spans - the span of each child view. This is a return value and is
+ * filled in by this function.
+ */
+ protected void layoutMinorAxis(int targetSpan, int axis,
+ int[] offsets, int[] spans)
+ {
+ // FIXME: Not implemented.
+ super.layoutMinorAxis(targetSpan, axis, offsets, spans);
+ }
+
+ /**
+ * Paints using the given graphics configuration and shape.
+ * This delegates to the css box painter to paint the
+ * border and background prior to the interior.
+ *
+ * @param g - Graphics configuration
+ * @param a - the Shape to render into.
+ */
+ public void paint(Graphics g, Shape a)
+ {
+ Rectangle rect = (Rectangle) a;
+ // FIXME: not fully implemented
+ getStyleSheet().getBoxPainter(getAttributes()).paint(g, rect.x, rect.y,
+ rect.width,
+ rect.height, this);
+ super.paint(g, a);
+ }
+
+ /**
+ * Fetches the attributes to use when painting.
+ *
+ * @return the attributes of this model.
+ */
+ public AttributeSet getAttributes()
+ {
+ return getStyleSheet().getViewAttributes(this);
+ }
+
+ /**
+ * Gets the resize weight.
+ *
+ * @param axis - the axis to get the resize weight for.
+ * @return the resize weight.
+ * @throws IllegalArgumentException - for an invalid axis
+ */
+ public int getResizeWeight(int axis) throws IllegalArgumentException
+ {
+ // Can't resize the Y_AXIS
+ if (axis == Y_AXIS)
+ return 0;
+ if (axis == X_AXIS)
+ return 1;
+ throw new IllegalArgumentException("Invalid Axis");
+ }
+
+ /**
+ * Gets the alignment.
+ *
+ * @param axis - the axis to get the alignment for.
+ * @return the alignment.
+ */
+ public float getAlignment(int axis)
+ {
+ if (axis == X_AXIS)
+ return 0.0F;
+ if (axis == Y_AXIS)
+ {
+ if (getViewCount() == 0)
+ return 0.0F;
+ float prefHeight = getPreferredSpan(Y_AXIS);
+ float firstRowHeight = getView(0).getPreferredSpan(Y_AXIS);
+ return (firstRowHeight / 2.F) / prefHeight;
+ }
+ throw new IllegalArgumentException("Invalid Axis");
+ }
+
+ /**
+ * Gives notification from the document that attributes were
+ * changed in a location that this view is responsible for.
+ *
+ * @param ev - the change information
+ * @param a - the current shape of the view
+ * @param f - the factory to use to rebuild if the view has children.
+ */
+ public void changedUpdate(DocumentEvent ev,
+ Shape a, ViewFactory f)
+ {
+ super.changedUpdate(ev, a, f);
+
+ // If more elements were added, then need to set the properties for them
+ int currPos = ev.getOffset();
+ if (currPos <= getStartOffset() && (currPos + ev.getLength()) >= getEndOffset())
+ setPropertiesFromAttributes();
+ }
+
+ /**
+ * Determines the preferred span along the axis.
+ *
+ * @param axis - the view to get the preferred span for.
+ * @return the span the view would like to be painted into >=0/
+ * The view is usually told to paint into the span that is returned,
+ * although the parent may choose to resize or break the view.
+ * @throws IllegalArgumentException - for an invalid axis
+ */
+ public float getPreferredSpan(int axis) throws IllegalArgumentException
+ {
+ if (axis == X_AXIS || axis == Y_AXIS)
+ return super.getPreferredSpan(axis);
+ throw new IllegalArgumentException("Invalid Axis");
+ }
+
+ /**
+ * Determines the minimum span along the axis.
+ *
+ * @param axis - the axis to get the minimum span for.
+ * @return the span the view would like to be painted into >=0/
+ * The view is usually told to paint into the span that is returned,
+ * although the parent may choose to resize or break the view.
+ * @throws IllegalArgumentException - for an invalid axis
+ */
+ public float getMinimumSpan(int axis) throws IllegalArgumentException
+ {
+ if (axis == X_AXIS || axis == Y_AXIS)
+ return super.getMinimumSpan(axis);
+ throw new IllegalArgumentException("Invalid Axis");
+ }
+
+ /**
+ * Determines the maximum span along the axis.
+ *
+ * @param axis - the axis to get the maximum span for.
+ * @return the span the view would like to be painted into >=0/
+ * The view is usually told to paint into the span that is returned,
+ * although the parent may choose to resize or break the view.
+ * @throws IllegalArgumentException - for an invalid axis
+ */
+ public float getMaximumSpan(int axis) throws IllegalArgumentException
+ {
+ if (axis == X_AXIS || axis == Y_AXIS)
+ return super.getMaximumSpan(axis);
+ throw new IllegalArgumentException("Invalid Axis");
+ }
+
+ /**
+ * Updates any cached values that come from attributes.
+ */
+ protected void setPropertiesFromAttributes()
+ {
+ // FIXME: Not implemented (need to use StyleSheet).
+ }
+
+ /**
+ * Gets the default style sheet.
+ *
+ * @return the style sheet
+ */
+ protected StyleSheet getStyleSheet()
+ {
+ StyleSheet styleSheet = new StyleSheet();
+ styleSheet.importStyleSheet(getClass().getResource(HTMLEditorKit.DEFAULT_CSS));
+ return styleSheet;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/CSS.java b/libjava/classpath/javax/swing/text/html/CSS.java
index 029ad26..c248e75 100644
--- a/libjava/classpath/javax/swing/text/html/CSS.java
+++ b/libjava/classpath/javax/swing/text/html/CSS.java
@@ -37,6 +37,7 @@ exception statement from your version. */
package javax.swing.text.html;
+import java.io.Serializable;
import java.util.HashMap;
/**
@@ -46,7 +47,7 @@ import java.util.HashMap;
*
* @author Roman Kennke (kennke@aicas.com)
*/
-public class CSS
+public class CSS implements Serializable
{
/**
* Returns an array of all CSS attributes.
diff --git a/libjava/classpath/javax/swing/text/html/CSSParser.java b/libjava/classpath/javax/swing/text/html/CSSParser.java
new file mode 100644
index 0000000..0bf76eb
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/CSSParser.java
@@ -0,0 +1,568 @@
+/* CSSParser.java --
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text.html;
+
+import java.io.*;
+
+/**
+ * Parses a CSS document. This works by way of a delegate that implements the
+ * CSSParserCallback interface. The delegate is notified of the following
+ * events:
+ * - Import statement: handleImport
+ * - Selectors handleSelector. This is invoked for each string. For example if
+ * the Reader contained p, bar , a {}, the delegate would be notified 4 times,
+ * for 'p,' 'bar' ',' and 'a'.
+ * - When a rule starts, startRule
+ * - Properties in the rule via the handleProperty. This
+ * is invoked one per property/value key, eg font size: foo;, would cause the
+ * delegate to be notified once with a value of 'font size'.
+ * - Values in the rule via the handleValue, this is notified for the total value.
+ * - When a rule ends, endRule
+ *
+ * @author Lillian Angel (langel@redhat.com)
+ */
+class CSSParser
+{
+
+ /**
+ * Receives all information about the CSS document structure while parsing it.
+ * The methods are invoked by parser.
+ */
+ static interface CSSParserCallback
+ {
+ /**
+ * Handles the import statment in the document.
+ *
+ * @param imp - the import string
+ */
+ public abstract void handleImport(String imp);
+
+ /**
+ * Called when the start of a rule is encountered.
+ */
+ public abstract void startRule();
+
+ /**
+ * Called when the end of a rule is encountered.
+ */
+ public abstract void endRule();
+
+ /**
+ * Handles the selector of a rule.
+ *
+ * @param selector - the selector in the rule
+ */
+ public abstract void handleSelector(String selector);
+
+ /**
+ * Handles the properties in the document.
+ *
+ * @param property - the property in the document.
+ */
+ public abstract void handleProperty(String property);
+
+ /**
+ * Handles the values in the document.
+ *
+ * @param value - the value to handle.
+ */
+ public abstract void handleValue(String value);
+
+ }
+
+ /**
+ * The identifier of the rule.
+ */
+ private static final int IDENTIFIER = 1;
+
+ /**
+ * The open bracket.
+ */
+ private static final int BRACKET_OPEN = 2;
+
+ /**
+ * The close bracket.
+ */
+ private static final int BRACKET_CLOSE = 3;
+
+ /**
+ * The open brace.
+ */
+ private static final int BRACE_OPEN = 4;
+
+ /**
+ * The close brace.
+ */
+ private static final int BRACE_CLOSE = 5;
+
+ /**
+ * The open parentheses.
+ */
+ private static final int PAREN_OPEN = 6;
+
+ /**
+ * The close parentheses.
+ */
+ private static final int PAREN_CLOSE = 7;
+
+ /**
+ * The end of the document.
+ */
+ private static final int END = -1;
+
+ /**
+ * The character mapping in the document.
+ */
+ // FIXME: What is this used for?
+ private static final char[] charMapping = null;
+
+ /**
+ * Set to true if one character has been read ahead.
+ */
+ private boolean didPushChar;
+
+ /**
+ * The read ahead character.
+ */
+ private int pushedChar;
+
+ /**
+ * Temporary place to hold identifiers.
+ */
+ private StringBuffer unitBuffer;
+
+ /**
+ * Used to indicate blocks.
+ */
+ private int[] unitStack;
+
+ /**
+ * Number of valid blocks.
+ */
+ private int stackCount;
+
+ /**
+ * Holds the incoming CSS rules.
+ */
+ private Reader reader;
+
+ /**
+ * Set to true when the first non @ rule is encountered.
+ */
+ private boolean encounteredRuleSet;
+
+ /**
+ * The call back used to parse.
+ */
+ private CSSParser.CSSParserCallback callback;
+
+ /**
+ * nextToken() inserts the string here.
+ */
+ private char[] tokenBuffer;
+
+ /**
+ * Current number of chars in tokenBufferLength.
+ */
+ private int tokenBufferLength;
+
+ /**
+ * Set to true if any whitespace is read.
+ */
+ private boolean readWS;
+
+ /**
+ * Constructor
+ */
+ CSSParser()
+ {
+ unitBuffer = new StringBuffer();
+ tokenBuffer = new char[10];
+ }
+
+ /**
+ * Appends a character to the token buffer.
+ *
+ * @param c - the character to append
+ */
+ private void append(char c)
+ {
+ if (tokenBuffer.length >= tokenBufferLength)
+ {
+ char[] temp = new char[tokenBufferLength * 2];
+ if (tokenBuffer != null)
+ System.arraycopy(tokenBuffer, 0, temp, 0, tokenBufferLength);
+
+ temp[tokenBufferLength] = c;
+ tokenBuffer = temp;
+ }
+ else
+ tokenBuffer[tokenBufferLength] = c;
+ tokenBufferLength++;
+ }
+
+ /**
+ * Fetches the next token.
+ *
+ * @param c - the character to fetch.
+ * @return the location
+ * @throws IOException - any i/o error encountered while reading
+ */
+ private int nextToken(char c) throws IOException
+ {
+ readWS = false;
+ int next = readWS();
+
+ switch (next)
+ {
+ case '\"':
+ if (tokenBufferLength > 0)
+ tokenBufferLength--;
+ return IDENTIFIER;
+ case '\'':
+ if (tokenBufferLength > 0)
+ tokenBufferLength--;
+ return IDENTIFIER;
+ case '(':
+ return PAREN_OPEN;
+ case ')':
+ return PAREN_CLOSE;
+ case '{':
+ return BRACE_OPEN;
+ case '}':
+ return BRACE_CLOSE;
+ case '[':
+ return BRACKET_OPEN;
+ case ']':
+ return BRACKET_CLOSE;
+ case -1:
+ return END;
+ default:
+ pushChar(next);
+ getIdentifier(c);
+ return IDENTIFIER;
+ }
+ }
+
+ /**
+ * Reads a character from the stream.
+ *
+ * @return the number of characters read or -1 if end of stream is reached.
+ * @throws IOException - any i/o encountered while reading
+ */
+ private int readChar() throws IOException
+ {
+ if (didPushChar)
+ {
+ didPushChar = false;
+ return pushedChar;
+ }
+ return reader.read();
+ }
+
+ /**
+ * Parses the the contents of the reader using the
+ * callback.
+ *
+ * @param reader - the reader to read from
+ * @param callback - the callback instance
+ * @param parsingDeclaration - true if parsing a declaration
+ * @throws IOException - any i/o error from the reader
+ */
+ void parse(Reader reader, CSSParser.CSSParserCallback callback,
+ boolean parsingDeclaration)
+ throws IOException
+ {
+ this.reader = reader;
+ this.callback = callback;
+
+ try
+ {
+ if (!parsingDeclaration)
+ while(getNextStatement());
+ else
+ parseDeclarationBlock();
+ }
+ catch (IOException ioe)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * Skips any white space, returning the character after the white space.
+ *
+ * @return the character after the whitespace
+ * @throws IOException - any i/o error from the reader
+ */
+ private int readWS() throws IOException
+ {
+ int next = readChar();
+ while (Character.isWhitespace((char) next))
+ {
+ readWS = true;
+ int tempNext = readChar();
+ if (tempNext == END)
+ return next;
+ next = tempNext;
+ }
+
+ // Its all whitespace
+ return END;
+ }
+
+ /**
+ * Gets the next statement, returning false if the end is reached.
+ * A statement is either an At-rule, or a ruleset.
+ *
+ * @return false if the end is reached
+ * @throws IOException - any i/o error from the reader
+ */
+ private boolean getNextStatement() throws IOException
+ {
+ int c = nextToken((char) 0);
+ switch (c)
+ {
+ case PAREN_OPEN:
+ case BRACE_OPEN:
+ case BRACKET_OPEN:
+ parseTillClosed(c);
+ break;
+ case BRACKET_CLOSE:
+ case BRACE_CLOSE:
+ case PAREN_CLOSE:
+ throw new IOException("Not a proper statement.");
+ case IDENTIFIER:
+ if (tokenBuffer[0] == ('@'))
+ parseAtRule();
+ else
+ parseRuleSet();
+ break;
+ case END:
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Parses an @ rule, stopping at a matching brace pair, or ;.
+ *
+ * @throws IOException - any i/o error from the reader
+ */
+ private void parseAtRule() throws IOException
+ {
+ // An At-Rule begins with the "@" character followed immediately by a keyword.
+ // Following the keyword separated by a space is an At-rule statement appropriate
+ // to the At-keyword used. If the At-Rule is a simple declarative statement
+ // (charset, import, fontdef), it is terminated by a semi-colon (";".)
+ // If the At-Rule is a conditional or informative statement (media, page, font-face),
+ // it is followed by optional arguments and then a style declaration block inside matching
+ // curly braces ("{", "}".) At-Rules are sometimes nestable, depending on the context.
+ // If any part of an At-Rule is not understood, it should be ignored.
+
+ // FIXME: Not Implemented
+ // call handleimport
+ }
+
+ /**
+ * Parses the next rule set, which is a selector followed by a declaration
+ * block.
+ *
+ * @throws IOException - any i/o error from the reader
+ */
+ private void parseRuleSet() throws IOException
+ {
+ // call parseDeclarationBlock
+ // call parse selectors
+ // call parse identifiers
+ // call startrule/endrule
+ // FIXME: Not Implemented
+ }
+
+ /**
+ * Parses a set of selectors, returning false if the end of the stream is
+ * reached.
+ *
+ * @return false if the end of stream is reached
+ * @throws IOException - any i/o error from the reader
+ */
+ private boolean parseSelectors() throws IOException
+ {
+ // FIXME: Not Implemented
+ // call handleselector
+ return false;
+ }
+
+ /**
+ * Parses a declaration block. Which a number of declarations followed by a
+ * })].
+ *
+ * @throws IOException - any i/o error from the reader
+ */
+ private void parseDeclarationBlock() throws IOException
+ {
+ // call parseDeclaration
+ // FIXME: Not Implemented
+ }
+
+ /**
+ * Parses a single declaration, which is an identifier a : and another identifier.
+ * This returns the last token seen.
+ *
+ * @returns the last token
+ * @throws IOException - any i/o error from the reader
+ */
+ private int parseDeclaration() throws IOException
+ {
+ // call handleValue
+ // FIXME: Not Implemented
+ return 0;
+ }
+
+ /**
+ * Parses identifiers until c is encountered, returning the ending token,
+ * which will be IDENTIFIER if c is found.
+ *
+ * @param c - the stop character
+ * @param wantsBlocks - true if blocks are wanted
+ * @return the ending token
+ * @throws IOException - any i/o error from the reader
+ */
+ private int parseIdentifiers(char c, boolean wantsBlocks) throws IOException
+ {
+ // FIXME: Not implemented
+ // call handleproperty?
+ return 0;
+ }
+
+ /**
+ * Parses till a matching block close is encountered. This is only appropriate
+ * to be called at the top level (no nesting).
+ *
+ * @param i - FIXME
+ * @throws IOException - any i/o error from the reader
+ */
+ private void parseTillClosed(int i) throws IOException
+ {
+ // FIXME: Not Implemented
+ }
+
+ /**
+ * Gets an identifier, returning true if the length of the string is greater
+ * than 0, stopping when c, whitespace, or one of {}()[] is hit.
+ *
+ * @param c - the stop character
+ * @return returns true if the length of the string > 0
+ * @throws IOException - any i/o error from the reader
+ */
+ private boolean getIdentifier(char c) throws IOException
+ {
+ // FIXME: Not Implemented
+ return false;
+ }
+
+ /**
+ * Reads till c is encountered, escaping characters as necessary.
+ *
+ * @param c - the stop character
+ * @throws IOException - any i/o error from the reader
+ */
+ private void readTill(char c) throws IOException
+ {
+ // FIXME: Not Implemented
+ }
+
+ /**
+ * Parses a comment block.
+ *
+ * @throws IOException - any i/o error from the reader
+ */
+ private void readComment() throws IOException
+ {
+ // Should ignore comments. Read until end of comment.
+ // FIXME: Not implemented
+ }
+
+ /**
+ * Called when a block start is encountered ({[.
+ *
+ * @param start of block
+ */
+ private void startBlock(int start)
+ {
+ // FIXME: Not Implemented
+ }
+
+ /**
+ * Called when an end block is encountered )]}
+ *
+ * @param end of block
+ */
+ private void endBlock(int end)
+ {
+ // FIXME: Not Implemented
+ }
+
+ /**
+ * Checks if currently in a block.
+ *
+ * @return true if currently in a block.
+ */
+ private boolean inBlock()
+ {
+ // FIXME: Not Implemented
+ return false;
+ }
+
+ /**
+ * Supports one character look ahead, this will throw if called twice in a row.
+ *
+ * @param c - the character to push.
+ * @throws IOException - if called twice in a row
+ */
+ private void pushChar(int c) throws IOException
+ {
+ if (didPushChar)
+ throw new IOException("pushChar called twice.");
+ didPushChar = true;
+ pushedChar = c;
+ }
+}
+
+ \ No newline at end of file
diff --git a/libjava/classpath/javax/swing/text/html/HTMLDocument.java b/libjava/classpath/javax/swing/text/html/HTMLDocument.java
index d048a04..5b2452b 100644
--- a/libjava/classpath/javax/swing/text/html/HTMLDocument.java
+++ b/libjava/classpath/javax/swing/text/html/HTMLDocument.java
@@ -40,17 +40,32 @@ package javax.swing.text.html;
import java.net.URL;
+import java.io.IOException;
+
+import java.util.HashMap;
+import java.util.Stack;
+import java.util.Vector;
+
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.UndoableEditEvent;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.Element;
import javax.swing.text.ElementIterator;
+import javax.swing.text.GapContent;
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.StyleConstants;
import javax.swing.text.html.HTML.Tag;
/**
- * TODO: This class is not yet completetely implemented.
- *
+ * TODO: Add more comments here
+ *
* @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org)
+ * @author Anthony Balkissoon (abalkiss@redhat.com)
+ * @author Lillian Angel (langel@redhat.com)
*/
public class HTMLDocument extends DefaultStyledDocument
{
@@ -60,6 +75,195 @@ public class HTMLDocument extends DefaultStyledDocument
public static final String AdditionalComments = "AdditionalComments";
URL baseURL = null;
boolean preservesUnknownTags = true;
+ int tokenThreshold = Integer.MAX_VALUE;
+ HTMLEditorKit.Parser parser;
+ StyleSheet styleSheet;
+ AbstractDocument.Content content;
+
+ /**
+ * Constructs an HTML document using the default buffer size and a default
+ * StyleSheet.
+ */
+ public HTMLDocument()
+ {
+ this(null);
+ }
+
+ /**
+ * Constructs an HTML document with the default content storage
+ * implementation and the specified style/attribute storage mechanism.
+ *
+ * @param styles - the style sheet
+ */
+ public HTMLDocument(StyleSheet styles)
+ {
+ this(new GapContent(BUFFER_SIZE_DEFAULT), styles);
+ }
+
+ /**
+ * Constructs an HTML document with the given content storage implementation
+ * and the given style/attribute storage mechanism.
+ *
+ * @param c - the document's content
+ * @param styles - the style sheet
+ */
+ public HTMLDocument(AbstractDocument.Content c, StyleSheet styles)
+ {
+ this.content = c;
+ if (styles == null)
+ {
+ styles = new StyleSheet();
+ styles.importStyleSheet(getClass().getResource(HTMLEditorKit.
+ DEFAULT_CSS));
+ }
+ this.styleSheet = styles;
+ }
+
+ /**
+ * Gets the style sheet with the document display rules (CSS) that were specified
+ * in the HTML document.
+ *
+ * @return - the style sheet
+ */
+ public StyleSheet getStyleSheet()
+ {
+ return styleSheet;
+ }
+
+ /**
+ * Replaces the contents of the document with the given element specifications.
+ * This is called before insert if the loading is done in bursts. This is the
+ * only method called if loading the document entirely in one burst.
+ *
+ * @param data - the date that replaces the content of the document
+ */
+ protected void create(DefaultStyledDocument.ElementSpec[] data)
+ {
+ // FIXME: Not implemented
+ System.out.println("create not implemented");
+ super.create(data);
+ }
+
+ /**
+ * This method creates a root element for the new document.
+ *
+ * @return the new default root
+ */
+ protected AbstractDocument.AbstractElement createDefaultRoot()
+ {
+ // FIXME: Not implemented
+ System.out.println("createDefaultRoot not implemented");
+ return super.createDefaultRoot();
+ }
+
+ /**
+ * This method returns an HTMLDocument.RunElement object attached to
+ * parent representing a run of text from p0 to p1. The run has
+ * attributes described by a.
+ *
+ * @param parent - the parent element
+ * @param a - the attributes for the element
+ * @param p0 - the beginning of the range >= 0
+ * @param p1 - the end of the range >= p0
+ * @return the new element
+ */
+ protected Element createLeafElement(Element parent, AttributeSet a, int p0,
+ int p1)
+ {
+ // FIXME: Not implemented
+ System.out.println("createLeafElement not implemented");
+ return super.createLeafElement(parent, a, p0, p1);
+ }
+
+ /** This method returns an HTMLDocument.BlockElement object representing the
+ * attribute set a and attached to parent.
+ *
+ * @param parent - the parent element
+ * @param a - the attributes for the element
+ * @return the new element
+ */
+ protected Element createBranchElement(Element parent, AttributeSet a)
+ {
+ // FIXME: Not implemented
+ System.out.println("createBranchElement not implemented");
+ return super.createBranchElement(parent, a);
+ }
+
+ /**
+ * Inserts new elements in bulk. This is how elements get created in the
+ * document. The parsing determines what structure is needed and creates the
+ * specification as a set of tokens that describe the edit while leaving the
+ * document free of a write-lock. This method can then be called in bursts by
+ * the reader to acquire a write-lock for a shorter duration (i.e. while the
+ * document is actually being altered).
+ *
+ * @param offset - the starting offset
+ * @param data - the element data
+ * @throws BadLocationException - if the given position does not
+ * represent a valid location in the associated document.
+ */
+ protected void insert(int offset, DefaultStyledDocument.ElementSpec[] data)
+ throws BadLocationException
+ {
+ super.insert(offset, data);
+ }
+
+ /**
+ * Updates document structure as a result of text insertion. This will happen
+ * within a write lock. This implementation simply parses the inserted content
+ * for line breaks and builds up a set of instructions for the element buffer.
+ *
+ * @param chng - a description of the document change
+ * @param attr - the attributes
+ */
+ protected void insertUpdate(AbstractDocument.DefaultDocumentEvent chng,
+ AttributeSet attr)
+ {
+ // FIXME: Not implemented
+ System.out.println("insertUpdate not implemented");
+ super.insertUpdate(chng, attr);
+ }
+
+ /**
+ * Returns the parser used by this HTMLDocument to insert HTML.
+ *
+ * @return the parser used by this HTMLDocument to insert HTML.
+ */
+ public HTMLEditorKit.Parser getParser()
+ {
+ return parser;
+ }
+
+ /**
+ * Sets the parser used by this HTMLDocument to insert HTML.
+ *
+ * @param p the parser to use
+ */
+ public void setParser (HTMLEditorKit.Parser p)
+ {
+ parser = p;
+ }
+ /**
+ * Sets the number of tokens to buffer before trying to display the
+ * Document.
+ *
+ * @param n the number of tokens to buffer
+ */
+ public void setTokenThreshold (int n)
+ {
+ tokenThreshold = n;
+ }
+
+ /**
+ * Returns the number of tokens that are buffered before the document
+ * is rendered.
+ *
+ * @return the number of tokens buffered
+ */
+ public int getTokenThreshold ()
+ {
+ return tokenThreshold;
+ }
/**
* Returns the location against which to resolve relative URLs.
@@ -79,7 +283,7 @@ public class HTMLDocument extends DefaultStyledDocument
public void setBase(URL u)
{
baseURL = u;
- //TODO: also set the base of the StyleSheet
+ styleSheet.setBase(u);
}
/**
@@ -259,10 +463,1133 @@ public class HTMLDocument extends DefaultStyledDocument
return null;
}
+ /**
+ * Gets the name of the element.
+ *
+ * @return the name of the element if it exists, null otherwise.
+ */
public String getName()
{
- //FIXME: this is supposed to do something different from the super class
- return super.getName();
+ return (String) getAttribute(StyleConstants.NameAttribute);
+ }
+ }
+
+ /**
+ * RunElement represents a section of text that has a set of
+ * HTML character level attributes assigned to it.
+ */
+ public class RunElement extends AbstractDocument.LeafElement
+ {
+
+ /**
+ * Constructs an element that has no children. It represents content
+ * within the document.
+ *
+ * @param parent - parent of this
+ * @param a - elements attributes
+ * @param start - the start offset >= 0
+ * @param end - the end offset
+ */
+ public RunElement(Element parent, AttributeSet a, int start, int end)
+ {
+ super(parent, a, start, end);
+ }
+
+ /**
+ * Gets the name of the element.
+ *
+ * @return the name of the element if it exists, null otherwise.
+ */
+ public String getName()
+ {
+ return (String) getAttribute(StyleConstants.NameAttribute);
+ }
+
+ /**
+ * Gets the resolving parent. HTML attributes do not inherit at the
+ * model level, so this method returns null.
+ *
+ * @return null
+ */
+ public AttributeSet getResolveParent()
+ {
+ return null;
+ }
+ }
+
+ /**
+ * A reader to load an HTMLDocument with HTML structure.
+ *
+ * @author Anthony Balkissoon abalkiss at redhat dot com
+ */
+ public class HTMLReader extends HTMLEditorKit.ParserCallback
+ {
+ /** Holds the current character attribute set **/
+ protected MutableAttributeSet charAttr = new SimpleAttributeSet();
+
+ protected Vector parseBuffer = new Vector();
+
+ /** A stack for character attribute sets **/
+ Stack charAttrStack = new Stack();
+
+ /** A mapping between HTML.Tag objects and the actions that handle them **/
+ HashMap tagToAction;
+
+ /** Tells us whether we've received the '</html>' tag yet **/
+ boolean endHTMLEncountered = false;
+
+ /** Variables related to the constructor with explicit insertTag **/
+ int popDepth, pushDepth, offset;
+ HTML.Tag insertTag;
+ boolean insertTagEncountered = false;
+
+ /** A temporary variable that helps with the printing out of debug information **/
+ boolean debug = false;
+
+ void print (String line)
+ {
+ if (debug)
+ System.out.println (line);
+ }
+
+ public class TagAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action. By default this does nothing.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action. By default does nothing.
+ */
+ public void end(HTML.Tag t)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ public class BlockAction extends TagAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ // Tell the parse buffer to open a new block for this tag.
+ blockOpen(t, a);
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ {
+ // Tell the parse buffer to close this block.
+ blockClose(t);
+ }
+ }
+
+ public class CharacterAction extends TagAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ // Put the old attribute set on the stack.
+ pushCharacterStyle();
+
+ // And create the new one by adding the attributes in <code>a</code>.
+ if (a != null)
+ charAttr.addAttribute(t, a.copyAttributes());
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ {
+ popCharacterStyle();
+ }
+ }
+
+ public class FormAction extends SpecialAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ // FIXME: Implement.
+ print ("FormAction.start not implemented");
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ {
+ // FIXME: Implement.
+ print ("FormAction.end not implemented");
+ }
+ }
+
+ public class HiddenAction extends TagAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ // FIXME: Implement.
+ print ("HiddenAction.start not implemented");
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ {
+ // FIXME: Implement.
+ print ("HiddenAction.end not implemented");
+ }
+ }
+
+ public class IsindexAction extends TagAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ // FIXME: Implement.
+ print ("IsindexAction.start not implemented");
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ {
+ // FIXME: Implement.
+ print ("IsindexAction.end not implemented");
+ }
+ }
+
+ public class ParagraphAction extends BlockAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ // FIXME: Implement.
+ print ("ParagraphAction.start not implemented");
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ {
+ // FIXME: Implement.
+ print ("ParagraphAction.end not implemented");
+ }
+ }
+
+ public class PreAction extends BlockAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ // FIXME: Implement.
+ print ("PreAction.start not implemented");
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ {
+ // FIXME: Implement.
+ print ("PreAction.end not implemented");
+ }
+ }
+
+ public class SpecialAction extends TagAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ // FIXME: Implement.
+ print ("SpecialAction.start not implemented");
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ {
+ // FIXME: Implement.
+ print ("SpecialAction.end not implemented");
+ }
+ }
+
+ class AreaAction extends TagAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ // FIXME: Implement.
+ print ("AreaAction.start not implemented");
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ {
+ // FIXME: Implement.
+ print ("AreaAction.end not implemented");
+ }
+ }
+
+ class BaseAction extends TagAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ // FIXME: Implement.
+ print ("BaseAction.start not implemented");
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ {
+ // FIXME: Implement.
+ print ("BaseAction.end not implemented");
+ }
+ }
+
+ class HeadAction extends BlockAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ // FIXME: Implement.
+ print ("HeadAction.start not implemented: "+t);
+ super.start(t, a);
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ {
+ // FIXME: Implement.
+ print ("HeadAction.end not implemented: "+t);
+ super.end(t);
+ }
+ }
+
+ class LinkAction extends TagAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ // FIXME: Implement.
+ print ("LinkAction.start not implemented");
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ {
+ // FIXME: Implement.
+ print ("LinkAction.end not implemented");
+ }
+ }
+
+ class MapAction extends TagAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ // FIXME: Implement.
+ print ("MapAction.start not implemented");
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ {
+ // FIXME: Implement.
+ print ("MapAction.end not implemented");
+ }
+ }
+
+ class MetaAction extends TagAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ // FIXME: Implement.
+ print ("MetaAction.start not implemented");
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ {
+ // FIXME: Implement.
+ print ("MetaAction.end not implemented");
+ }
+ }
+
+ class StyleAction extends TagAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ // FIXME: Implement.
+ print ("StyleAction.start not implemented");
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ {
+ // FIXME: Implement.
+ print ("StyleAction.end not implemented");
+ }
+ }
+
+ class TitleAction extends TagAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ // FIXME: Implement.
+ print ("TitleAction.start not implemented");
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ {
+ // FIXME: Implement.
+ print ("TitleAction.end not implemented");
+ }
+ }
+
+ public HTMLReader(int offset)
+ {
+ this (offset, 0, 0, null);
+ }
+
+ public HTMLReader(int offset, int popDepth, int pushDepth,
+ HTML.Tag insertTag)
+ {
+ print ("HTMLReader created with pop: "+popDepth
+ + " push: "+pushDepth + " offset: "+offset
+ + " tag: "+insertTag);
+ this.insertTag = insertTag;
+ this.offset = offset;
+ this.popDepth = popDepth;
+ this.pushDepth = pushDepth;
+ initTags();
+ }
+
+ void initTags()
+ {
+ tagToAction = new HashMap(72);
+ CharacterAction characterAction = new CharacterAction();
+ HiddenAction hiddenAction = new HiddenAction();
+ AreaAction areaAction = new AreaAction();
+ BaseAction baseAction = new BaseAction();
+ BlockAction blockAction = new BlockAction();
+ SpecialAction specialAction = new SpecialAction();
+ ParagraphAction paragraphAction = new ParagraphAction();
+ HeadAction headAction = new HeadAction();
+ FormAction formAction = new FormAction();
+ IsindexAction isindexAction = new IsindexAction();
+ LinkAction linkAction = new LinkAction();
+ MapAction mapAction = new MapAction();
+ PreAction preAction = new PreAction();
+ MetaAction metaAction = new MetaAction();
+ StyleAction styleAction = new StyleAction();
+ TitleAction titleAction = new TitleAction();
+
+
+ tagToAction.put(HTML.Tag.A, characterAction);
+ tagToAction.put(HTML.Tag.ADDRESS, characterAction);
+ tagToAction.put(HTML.Tag.APPLET, hiddenAction);
+ tagToAction.put(HTML.Tag.AREA, areaAction);
+ tagToAction.put(HTML.Tag.B, characterAction);
+ tagToAction.put(HTML.Tag.BASE, baseAction);
+ tagToAction.put(HTML.Tag.BASEFONT, characterAction);
+ tagToAction.put(HTML.Tag.BIG, characterAction);
+ tagToAction.put(HTML.Tag.BLOCKQUOTE, blockAction);
+ tagToAction.put(HTML.Tag.BODY, blockAction);
+ tagToAction.put(HTML.Tag.BR, specialAction);
+ tagToAction.put(HTML.Tag.CAPTION, blockAction);
+ tagToAction.put(HTML.Tag.CENTER, blockAction);
+ tagToAction.put(HTML.Tag.CITE, characterAction);
+ tagToAction.put(HTML.Tag.CODE, characterAction);
+ tagToAction.put(HTML.Tag.DD, blockAction);
+ tagToAction.put(HTML.Tag.DFN, characterAction);
+ tagToAction.put(HTML.Tag.DIR, blockAction);
+ tagToAction.put(HTML.Tag.DIV, blockAction);
+ tagToAction.put(HTML.Tag.DL, blockAction);
+ tagToAction.put(HTML.Tag.DT, paragraphAction);
+ tagToAction.put(HTML.Tag.EM, characterAction);
+ tagToAction.put(HTML.Tag.FONT, characterAction);
+ tagToAction.put(HTML.Tag.FORM, blockAction);
+ tagToAction.put(HTML.Tag.FRAME, specialAction);
+ tagToAction.put(HTML.Tag.FRAMESET, blockAction);
+ tagToAction.put(HTML.Tag.H1, paragraphAction);
+ tagToAction.put(HTML.Tag.H2, paragraphAction);
+ tagToAction.put(HTML.Tag.H3, paragraphAction);
+ tagToAction.put(HTML.Tag.H4, paragraphAction);
+ tagToAction.put(HTML.Tag.H5, paragraphAction);
+ tagToAction.put(HTML.Tag.H6, paragraphAction);
+ tagToAction.put(HTML.Tag.HEAD, headAction);
+ tagToAction.put(HTML.Tag.HR, specialAction);
+ tagToAction.put(HTML.Tag.HTML, blockAction);
+ tagToAction.put(HTML.Tag.I, characterAction);
+ tagToAction.put(HTML.Tag.IMG, specialAction);
+ tagToAction.put(HTML.Tag.INPUT, formAction);
+ tagToAction.put(HTML.Tag.ISINDEX, isindexAction);
+ tagToAction.put(HTML.Tag.KBD, characterAction);
+ tagToAction.put(HTML.Tag.LI, blockAction);
+ tagToAction.put(HTML.Tag.LINK, linkAction);
+ tagToAction.put(HTML.Tag.MAP, mapAction);
+ tagToAction.put(HTML.Tag.MENU, blockAction);
+ tagToAction.put(HTML.Tag.META, metaAction);
+ tagToAction.put(HTML.Tag.NOFRAMES, blockAction);
+ tagToAction.put(HTML.Tag.OBJECT, specialAction);
+ tagToAction.put(HTML.Tag.OL, blockAction);
+ tagToAction.put(HTML.Tag.OPTION, formAction);
+ tagToAction.put(HTML.Tag.P, paragraphAction);
+ tagToAction.put(HTML.Tag.PARAM, hiddenAction);
+ tagToAction.put(HTML.Tag.PRE, preAction);
+ tagToAction.put(HTML.Tag.SAMP, characterAction);
+ tagToAction.put(HTML.Tag.SCRIPT, hiddenAction);
+ tagToAction.put(HTML.Tag.SELECT, formAction);
+ tagToAction.put(HTML.Tag.SMALL, characterAction);
+ tagToAction.put(HTML.Tag.STRIKE, characterAction);
+ tagToAction.put(HTML.Tag.S, characterAction);
+ tagToAction.put(HTML.Tag.STRONG, characterAction);
+ tagToAction.put(HTML.Tag.STYLE, styleAction);
+ tagToAction.put(HTML.Tag.SUB, characterAction);
+ tagToAction.put(HTML.Tag.SUP, characterAction);
+ tagToAction.put(HTML.Tag.TABLE, blockAction);
+ tagToAction.put(HTML.Tag.TD, blockAction);
+ tagToAction.put(HTML.Tag.TEXTAREA, formAction);
+ tagToAction.put(HTML.Tag.TH, blockAction);
+ tagToAction.put(HTML.Tag.TITLE, titleAction);
+ tagToAction.put(HTML.Tag.TR, blockAction);
+ tagToAction.put(HTML.Tag.TT, characterAction);
+ tagToAction.put(HTML.Tag.U, characterAction);
+ tagToAction.put(HTML.Tag.UL, blockAction);
+ tagToAction.put(HTML.Tag.VAR, characterAction);
+ }
+
+ /**
+ * Pushes the current character style onto the stack.
+ *
+ */
+ protected void pushCharacterStyle()
+ {
+ charAttrStack.push(charAttr);
+ }
+
+ /**
+ * Pops a character style off of the stack and uses it as the
+ * current character style.
+ *
+ */
+ protected void popCharacterStyle()
+ {
+ if (!charAttrStack.isEmpty())
+ charAttr = (MutableAttributeSet) charAttrStack.pop();
+ }
+
+ /**
+ * Registers a given tag with a given Action. All of the well-known tags
+ * are registered by default, but this method can change their behaviour
+ * or add support for custom or currently unsupported tags.
+ *
+ * @param t the Tag to register
+ * @param a the Action for the Tag
+ */
+ protected void registerTag(HTML.Tag t, HTMLDocument.HTMLReader.TagAction a)
+ {
+ tagToAction.put (t, a);
+ }
+
+ /**
+ * This is the last method called on the HTMLReader, allowing any pending
+ * changes to be flushed to the HTMLDocument.
+ */
+ public void flush() throws BadLocationException
+ {
+ DefaultStyledDocument.ElementSpec[] elements;
+ elements = new DefaultStyledDocument.ElementSpec[parseBuffer.size()];
+ parseBuffer.copyInto(elements);
+ parseBuffer.removeAllElements();
+ insert(offset, elements);
+ offset += HTMLDocument.this.getLength() - offset;
+ }
+
+ /**
+ * This method is called by the parser to indicate a block of
+ * text was encountered. Should insert the text appropriately.
+ *
+ * @param data the text that was inserted
+ * @param pos the position at which the text was inserted
+ */
+ public void handleText(char[] data, int pos)
+ {
+ if (data != null && data.length > 0)
+ addContent(data, 0, data.length);
+ }
+
+ /**
+ * This method is called by the parser and should route the call to
+ * the proper handler for the tag.
+ *
+ * @param t the HTML.Tag
+ * @param a the attribute set
+ * @param pos the position at which the tag was encountered
+ */
+ public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos)
+ {
+ // Don't call the Action if we've already seen </html>.
+ if (endHTMLEncountered)
+ return;
+
+ TagAction action = (TagAction) tagToAction.get(t);
+ if (action != null)
+ action.start(t, a);
+ }
+
+ /**
+ * This method called by parser to handle a comment block.
+ *
+ * @param data the comment
+ * @param pos the position at which the comment was encountered
+ */
+ public void handleComment(char[] data, int pos)
+ {
+ // Don't call the Action if we've already seen </html>.
+ if (endHTMLEncountered)
+ return;
+
+ TagAction action = (TagAction) tagToAction.get(HTML.Tag.COMMENT);
+ if (action != null)
+ {
+ action.start(HTML.Tag.COMMENT, new SimpleAttributeSet());
+ action.end (HTML.Tag.COMMENT);
+ }
+ }
+
+ /**
+ * This method is called by the parser and should route the call to
+ * the proper handler for the tag.
+ *
+ * @param t the HTML.Tag
+ * @param pos the position at which the tag was encountered
+ */
+ public void handleEndTag(HTML.Tag t, int pos)
+ {
+ // Don't call the Action if we've already seen </html>.
+ if (endHTMLEncountered)
+ return;
+
+ // If this is the </html> tag we need to stop calling the Actions
+ if (t == HTML.Tag.HTML)
+ endHTMLEncountered = true;
+
+ TagAction action = (TagAction) tagToAction.get(t);
+ if (action != null)
+ action.end(t);
+ }
+
+ /**
+ * This is a callback from the parser that should be routed to the
+ * appropriate handler for the tag.
+ *
+ * @param t the HTML.Tag that was encountered
+ * @param a the attribute set
+ * @param pos the position at which the tag was encountered
+ */
+ public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos)
+ {
+ // Don't call the Action if we've already seen </html>.
+ if (endHTMLEncountered)
+ return;
+
+ TagAction action = (TagAction) tagToAction.get (t);
+ if (action != null)
+ {
+ action.start(t, a);
+ action.end(t);
+ }
+ }
+
+ /**
+ * This is invoked after the stream has been parsed but before it has been
+ * flushed.
+ *
+ * @param eol one of \n, \r, or \r\n, whichever was encountered the most in
+ * parsing the stream
+ * @since 1.3
+ */
+ public void handleEndOfLineString(String eol)
+ {
+ // FIXME: Implement.
+ print ("HTMLReader.handleEndOfLineString not implemented yet");
+ }
+
+ /**
+ * Adds the given text to the textarea document. Called only when we are
+ * within a textarea.
+ *
+ * @param data the text to add to the textarea
+ */
+ protected void textAreaContent(char[] data)
+ {
+ // FIXME: Implement.
+ print ("HTMLReader.textAreaContent not implemented yet");
+ }
+
+ /**
+ * Adds the given text that was encountered in a <PRE> element.
+ *
+ * @param data the text
+ */
+ protected void preContent(char[] data)
+ {
+ // FIXME: Implement
+ print ("HTMLReader.preContent not implemented yet");
+ }
+
+ /**
+ * Instructs the parse buffer to create a block element with the given
+ * attributes.
+ *
+ * @param t the tag that requires opening a new block
+ * @param attr the attribute set for the new block
+ */
+ protected void blockOpen(HTML.Tag t, MutableAttributeSet attr)
+ {
+ printBuffer();
+ DefaultStyledDocument.ElementSpec element;
+ element = new DefaultStyledDocument.ElementSpec(attr.copyAttributes(),
+ DefaultStyledDocument.ElementSpec.StartTagType);
+ parseBuffer.addElement(element);
+ printBuffer();
+ }
+
+ /**
+ * Instructs the parse buffer to close the block element associated with
+ * the given HTML.Tag
+ *
+ * @param t the HTML.Tag that is closing its block
+ */
+ protected void blockClose(HTML.Tag t)
+ {
+ printBuffer();
+ DefaultStyledDocument.ElementSpec element;
+ element = new DefaultStyledDocument.ElementSpec(null,
+ DefaultStyledDocument.ElementSpec.EndTagType);
+ parseBuffer.addElement(element);
+ printBuffer();
}
+
+ /**
+ * Adds text to the appropriate context using the current character
+ * attribute set.
+ *
+ * @param data the text to add
+ * @param offs the offset at which to add it
+ * @param length the length of the text to add
+ */
+ protected void addContent(char[] data, int offs, int length)
+ {
+ addContent(data, offs, length, true);
+ }
+
+ /**
+ * Adds text to the appropriate context using the current character
+ * attribute set, and possibly generating an IMPLIED Tag if necessary.
+ *
+ * @param data the text to add
+ * @param offs the offset at which to add it
+ * @param length the length of the text to add
+ * @param generateImpliedPIfNecessary whether or not we should generate
+ * an HTML.Tag.IMPLIED tag if necessary
+ */
+ protected void addContent(char[] data, int offs, int length,
+ boolean generateImpliedPIfNecessary)
+ {
+ // Copy the attribute set, don't use the same object because
+ // it may change
+ AttributeSet attributes = null;
+ if (charAttr != null)
+ attributes = charAttr.copyAttributes();
+
+ DefaultStyledDocument.ElementSpec element;
+ element = new DefaultStyledDocument.ElementSpec(attributes,
+ DefaultStyledDocument.ElementSpec.ContentType,
+ data, offs, length);
+
+ printBuffer();
+ // Add the element to the buffer
+ parseBuffer.addElement(element);
+ printBuffer();
+
+ if (parseBuffer.size() > HTMLDocument.this.getTokenThreshold())
+ {
+ try
+ {
+ flush();
+ }
+ catch (BadLocationException ble)
+ {
+ // TODO: what to do here?
+ }
+ }
+ }
+
+ /**
+ * Adds content that is specified in the attribute set.
+ *
+ * @param t the HTML.Tag
+ * @param a the attribute set specifying the special content
+ */
+ protected void addSpecialElement(HTML.Tag t, MutableAttributeSet a)
+ {
+ // FIXME: Implement
+ print ("HTMLReader.addSpecialElement not implemented yet");
+ }
+
+ void printBuffer()
+ {
+ print ("\n*********BUFFER**********");
+ for (int i = 0; i < parseBuffer.size(); i ++)
+ print (" "+parseBuffer.get(i));
+ print ("***************************");
+ }
+ }
+
+ /**
+ * Gets the reader for the parser to use when loading the document with HTML.
+ *
+ * @param pos - the starting position
+ * @return - the reader
+ */
+ public HTMLEditorKit.ParserCallback getReader(int pos)
+ {
+ return new HTMLReader(pos);
+ }
+
+ /**
+ * Gets the reader for the parser to use when loading the document with HTML.
+ *
+ * @param pos - the starting position
+ * @param popDepth - the number of EndTagTypes to generate before inserting
+ * @param pushDepth - the number of StartTagTypes with a direction
+ * of JoinNextDirection that should be generated before inserting,
+ * but after the end tags have been generated.
+ * @param insertTag - the first tag to start inserting into document
+ * @return - the reader
+ */
+ public HTMLEditorKit.ParserCallback getReader(int pos,
+ int popDepth,
+ int pushDepth,
+ HTML.Tag insertTag)
+ {
+ return new HTMLReader(pos, popDepth, pushDepth, insertTag);
+ }
+
+ /**
+ * Gets the child element that contains the attribute with the value or null.
+ * Not thread-safe.
+ *
+ * @param e - the element to begin search at
+ * @param attribute - the desired attribute
+ * @param value - the desired value
+ * @return the element found with the attribute and value specified or null
+ * if it is not found.
+ */
+ public Element getElement(Element e, Object attribute, Object value)
+ {
+ if (e != null)
+ {
+ if (e.getAttributes().containsAttribute(attribute, value))
+ return e;
+
+ int count = e.getElementCount();
+ for (int j = 0; j < count; j++)
+ {
+ Element child = e.getElement(j);
+ if (child.getAttributes().containsAttribute(attribute, value))
+ return child;
+
+ Element grandChild = getElement(child, attribute, value);
+ if (grandChild != null)
+ return grandChild;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the element that has the given id Attribute. If it is not found,
+ * null is returned. This method works on an Attribute, not a character tag.
+ * This is not thread-safe.
+ *
+ * @param attrId - the Attribute id to look for
+ * @return the element that has the given id.
+ */
+ public Element getElement(String attrId)
+ {
+ Element root = getDefaultRootElement();
+ return getElement(root, HTML.getAttributeKey(attrId) , attrId);
+ }
+
+ /**
+ * Replaces the children of the given element with the contents of
+ * the string. The document must have an HTMLEditorKit.Parser set.
+ * This will be seen as at least two events, n inserts followed by a remove.
+ *
+ * @param elem - the brance element whose children will be replaced
+ * @param htmlText - the string to be parsed and assigned to element.
+ * @throws BadLocationException
+ * @throws IOException
+ * @throws IllegalArgumentException - if elem is a leaf
+ * @throws IllegalStateException - if an HTMLEditorKit.Parser has not been set
+ */
+ public void setInnerHTML(Element elem, String htmlText)
+ throws BadLocationException, IOException
+ {
+ if (elem.isLeaf())
+ throw new IllegalArgumentException("Element is a leaf");
+ if (parser == null)
+ throw new IllegalStateException("Parser has not been set");
+ // FIXME: Not implemented fully, use InsertHTML* in HTMLEditorKit?
+ System.out.println("setInnerHTML not implemented");
+ }
+
+ /**
+ * Replaces the given element in the parent with the string. When replacing
+ * a leaf, this will attempt to make sure there is a newline present if one is
+ * needed. This may result in an additional element being inserted.
+ * This will be seen as at least two events, n inserts followed by a remove.
+ * The HTMLEditorKit.Parser must be set.
+ *
+ * @param elem - the branch element whose parent will be replaced
+ * @param htmlText - the string to be parsed and assigned to elem
+ * @throws BadLocationException
+ * @throws IOException
+ * @throws IllegalStateException - if parser is not set
+ */
+ public void setOuterHTML(Element elem, String htmlText)
+ throws BadLocationException, IOException
+ {
+ if (parser == null)
+ throw new IllegalStateException("Parser has not been set");
+ // FIXME: Not implemented fully, use InsertHTML* in HTMLEditorKit?
+ System.out.println("setOuterHTML not implemented");
+ }
+
+ /**
+ * Inserts the string before the start of the given element.
+ * The parser must be set.
+ *
+ * @param elem - the element to be the root for the new text.
+ * @param htmlText - the string to be parsed and assigned to elem
+ * @throws BadLocationException
+ * @throws IOException
+ * @throws IllegalStateException - if parser has not been set
+ */
+ public void insertBeforeStart(Element elem, String htmlText)
+ throws BadLocationException, IOException
+ {
+ if (parser == null)
+ throw new IllegalStateException("Parser has not been set");
+ // FIXME: Not implemented fully, use InsertHTML* in HTMLEditorKit?
+ System.out.println("insertBeforeStart not implemented");
+ }
+
+ /**
+ * Inserts the string at the end of the element. If elem's children
+ * are leaves, and the character at elem.getEndOffset() - 1 is a newline,
+ * then it will be inserted before the newline. The parser must be set.
+ *
+ * @param elem - the element to be the root for the new text
+ * @param htmlText - the text to insert
+ * @throws BadLocationException
+ * @throws IOException
+ * @throws IllegalStateException - if parser is not set
+ */
+ public void insertBeforeEnd(Element elem, String htmlText)
+ throws BadLocationException, IOException
+ {
+ if (parser == null)
+ throw new IllegalStateException("Parser has not been set");
+ // FIXME: Not implemented fully, use InsertHTML* in HTMLEditorKit?
+ System.out.println("insertBeforeEnd not implemented");
+ }
+
+ /**
+ * Inserts the string after the end of the given element.
+ * The parser must be set.
+ *
+ * @param elem - the element to be the root for the new text
+ * @param htmlText - the text to insert
+ * @throws BadLocationException
+ * @throws IOException
+ * @throws IllegalStateException - if parser is not set
+ */
+ public void insertAfterEnd(Element elem, String htmlText)
+ throws BadLocationException, IOException
+ {
+ if (parser == null)
+ throw new IllegalStateException("Parser has not been set");
+ // FIXME: Not implemented fully, use InsertHTML* in HTMLEditorKit?
+ System.out.println("insertAfterEnd not implemented");
+ }
+
+ /**
+ * Inserts the string at the start of the element.
+ * The parser must be set.
+ *
+ * @param elem - the element to be the root for the new text
+ * @param htmlText - the text to insert
+ * @throws BadLocationException
+ * @throws IOException
+ * @throws IllegalStateException - if parser is not set
+ */
+ public void insertAfterStart(Element elem, String htmlText)
+ throws BadLocationException, IOException
+ {
+ if (parser == null)
+ throw new IllegalStateException("Parser has not been set");
+ // FIXME: Not implemented fully, use InsertHTML* in HTMLEditorKit?
+ System.out.println("insertAfterStart not implemented");
+ }
+
+ /**
+ * This method sets the attributes associated with the paragraph containing
+ * offset. If replace is false, s is merged with existing attributes. The
+ * length argument determines how many characters are affected by the new
+ * attributes. This is often the entire paragraph.
+ *
+ * @param offset -
+ * the offset into the paragraph (must be at least 0)
+ * @param length -
+ * the number of characters affected (must be at least 0)
+ * @param s -
+ * the attributes
+ * @param replace -
+ * whether to replace existing attributes, or merge them
+ */
+ public void setParagraphAttributes(int offset, int length, AttributeSet s,
+ boolean replace)
+ {
+ // FIXME: Not implemented.
+ System.out.println("setParagraphAttributes not implemented");
+ super.setParagraphAttributes(offset, length, s, replace);
+ }
+
+ /**
+ * This method flags a change in the document.
+ *
+ * @param e - the Document event
+ */
+ protected void fireChangedUpdate(DocumentEvent e)
+ {
+ // FIXME: Not implemented.
+ System.out.println("fireChangedUpdate not implemented");
+ super.fireChangedUpdate(e);
+ }
+
+ /**
+ * This method fires an event intended to be caught by Undo listeners. It
+ * simply calls the super version inherited from DefaultStyledDocument. With
+ * this method, an HTML editor could easily provide undo support.
+ *
+ * @param e - the UndoableEditEvent
+ */
+ protected void fireUndoableEditUpdate(UndoableEditEvent e)
+ {
+ super.fireUndoableEditUpdate(e);
}
}
diff --git a/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java b/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java
index 5189c77..1ef9768 100644
--- a/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java
+++ b/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java
@@ -38,28 +38,563 @@ exception statement from your version. */
package javax.swing.text.html;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseMotionListener;
+import java.awt.Cursor;
+
import java.io.IOException;
import java.io.Reader;
import java.io.Serializable;
+import java.io.StringReader;
+import java.io.Writer;
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+
+import javax.swing.Action;
+import javax.swing.JEditorPane;
+import javax.swing.text.AbstractDocument;
import javax.swing.text.BadLocationException;
+import javax.swing.text.BoxView;
+import javax.swing.text.ComponentView;
import javax.swing.text.Document;
+import javax.swing.text.EditorKit;
+import javax.swing.text.Element;
+import javax.swing.text.IconView;
+import javax.swing.text.LabelView;
import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.ParagraphView;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.StyleContext;
import javax.swing.text.StyledEditorKit;
+import javax.swing.text.TextAction;
+import javax.swing.text.View;
+import javax.swing.text.ViewFactory;
import javax.swing.text.html.parser.ParserDelegator;
/**
- * This class is NOT implemented. This file currently holds only
- * declarations of the two enclosing classes, necessary for testing
- * the implemented javax.swing.text.html.parser package.
- *
- * @author No authorship is taken, implement the class and be!
- * TODO: replace this header after implementing the class.
+ * @author Lillian Angel (langel at redhat dot com)
*/
public class HTMLEditorKit
extends StyledEditorKit
- implements Serializable, Cloneable
+ implements Serializable, Cloneable, Accessible
{
+
+ /**
+ * Fires the hyperlink events on the associated component
+ * when needed.
+ */
+ public static class LinkController
+ extends MouseAdapter
+ implements MouseMotionListener, Serializable
+ {
+
+ /**
+ * Constructor
+ */
+ public LinkController()
+ {
+ super();
+ }
+
+ /**
+ * Dispatched when the mouse is clicked. If the component
+ * is read-only, then the clicked event is used to drive an
+ * attempt to follow the reference specified by a link
+ *
+ * @param e - the mouse event
+ */
+ public void mouseClicked(MouseEvent e)
+ {
+ /*
+ These MouseInputAdapter methods generate mouse appropriate events around
+ hyperlinks (entering, exiting, and activating).
+ */
+ // FIXME: Not implemented.
+ }
+
+ /**
+ * Dispatched when the mouse is dragged on a component.
+ *
+ * @param e - the mouse event.
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ /*
+ These MouseInputAdapter methods generate mouse appropriate events around
+ hyperlinks (entering, exiting, and activating).
+ */
+ // FIXME: Not implemented.
+ }
+
+ /**
+ * Dispatched when the mouse cursor has moved into the component.
+ *
+ * @param e - the mouse event.
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ /*
+ These MouseInputAdapter methods generate mouse appropriate events around
+ hyperlinks (entering, exiting, and activating).
+ */
+ // FIXME: Not implemented.
+ }
+
+ /**
+ * If the given position represents a link, then linkActivated is called
+ * on the JEditorPane. Implemented to forward to the method with the same
+ * name, but pos == editor == -1.
+ *
+ * @param pos - the position
+ * @param editor - the editor pane
+ */
+ protected void activateLink(int pos,
+ JEditorPane editor)
+ {
+ /*
+ This method creates and fires a HyperlinkEvent if the document is an
+ instance of HTMLDocument and the href tag of the link is not null.
+ */
+ // FIXME: Not implemented.
+ }
+ }
+
+ /**
+ * This class is used to insert a string of HTML into an existing
+ * document. At least 2 HTML.Tags need to be supplied. The first Tag (parentTag)
+ * identifies the parent in the document to add the elements to. The second, (addTag),
+ * identifies that the first tag should be added to the document as seen in the string.
+ * The parser will generate all appropriate (opening/closing tags_ even if they are not
+ * in the HTML string passed in.
+ */
+ public static class InsertHTMLTextAction
+ extends HTMLTextAction
+ {
+
+ /**
+ * Tag in HTML to start adding tags from.
+ */
+ protected HTML.Tag addTag;
+
+ /**
+ * Alternate tag in HTML to start adding tags from if parentTag is
+ * not found and alternateParentTag is not found.
+ */
+ protected HTML.Tag alternateAddTag;
+
+ /**
+ * Alternate tag to check if parentTag is not found.
+ */
+ protected HTML.Tag alternateParentTag;
+
+ /**
+ * HTML to insert.
+ */
+ protected String html;
+
+ /**
+ * Tag to check for in the document.
+ */
+ protected HTML.Tag parentTag;
+
+ /**
+ * Initializes all fields.
+ *
+ * @param name - the name of the document.
+ * @param html - the html to insert
+ * @param parentTag - the parent tag to check for
+ * @param addTag - the tag to start adding from
+ */
+ public InsertHTMLTextAction(String name, String html,
+ HTML.Tag parentTag, HTML.Tag addTag)
+ {
+ this(name, html, parentTag, addTag, null, null);
+ }
+
+ /**
+ * Initializes all fields and calls super
+ *
+ * @param name - the name of the document.
+ * @param html - the html to insert
+ * @param parentTag - the parent tag to check for
+ * @param addTag - the tag to start adding from
+ * @param alternateParentTag - the alternate parent tag
+ * @param alternateAddTag - the alternate add tag
+ */
+ public InsertHTMLTextAction(String name, String html, HTML.Tag parentTag,
+ HTML.Tag addTag, HTML.Tag alternateParentTag,
+ HTML.Tag alternateAddTag)
+ {
+ super(name);
+ // Fields are for easy access when the action is applied to an actual
+ // document.
+ this.html = html;
+ this.parentTag = parentTag;
+ this.addTag = addTag;
+ this.alternateParentTag = alternateParentTag;
+ this.alternateAddTag = alternateAddTag;
+ }
+
+ /**
+ * HTMLEditorKit.insertHTML is called. If an exception is
+ * thrown, it is wrapped in a RuntimeException and thrown.
+ *
+ * @param editor - the editor to use to get the editorkit
+ * @param doc -
+ * the Document to insert the HTML into.
+ * @param offset -
+ * where to begin inserting the HTML.
+ * @param html -
+ * the String to insert
+ * @param popDepth -
+ * the number of ElementSpec.EndTagTypes to generate before
+ * inserting
+ * @param pushDepth -
+ * the number of ElementSpec.StartTagTypes with a direction of
+ * ElementSpec.JoinNextDirection that should be generated before
+ * @param addTag -
+ * the first tag to start inserting into document
+ */
+ protected void insertHTML(JEditorPane editor, HTMLDocument doc, int offset,
+ String html, int popDepth, int pushDepth,
+ HTML.Tag addTag)
+ {
+ try
+ {
+ super.getHTMLEditorKit(editor).insertHTML(doc, offset, html,
+ popDepth, pushDepth, addTag);
+ }
+ catch (IOException e)
+ {
+ throw (RuntimeException) new RuntimeException("Parser is null.").initCause(e);
+ }
+ catch (BadLocationException ex)
+ {
+ throw (RuntimeException) new RuntimeException("BadLocationException: "
+ + offset).initCause(ex);
+ }
+ }
+
+ /**
+ * Invoked when inserting at a boundary. Determines the number of pops,
+ * and then the number of pushes that need to be performed. The it calls
+ * insertHTML.
+ *
+ * @param editor -
+ * the editor to use to get the editorkit
+ * @param doc -
+ * the Document to insert the HTML into.
+ * @param offset -
+ * where to begin inserting the HTML.
+ * @param insertElement -
+ * the element to insert
+ * @param html -
+ * the html to insert
+ * @param parentTag -
+ * the parent tag
+ * @param addTag -
+ * the first tag
+ */
+ protected void insertAtBoundary(JEditorPane editor,
+ HTMLDocument doc, int offset,
+ Element insertElement,
+ String html, HTML.Tag parentTag,
+ HTML.Tag addTag)
+ {
+ /*
+ As its name implies, this protected method is used when HTML is inserted at a
+ boundary. (A boundary in this case is an offset in doc that exactly matches the
+ beginning offset of the parentTag.) It performs the extra work required to keep
+ the tag stack in shape and then calls insertHTML(). The editor and doc argu-
+ ments are the editor pane and document where the HTML should go. The offset
+ argument represents the cursor location or selection start in doc. The insert-
+ Element and parentTag arguments are used to calculate the proper number of
+ tag pops and pushes before inserting the HTML (via html and addTag, which are
+ passed directly to insertHTML()).
+ */
+ // FIXME: not implemented
+ }
+
+ /**
+ * Invoked when inserting at a boundary. Determines the number of pops,
+ * and then the number of pushes that need to be performed. The it calls
+ * insertHTML.
+ *
+ * @param editor - the editor to use to get the editorkit
+ * @param doc -
+ * the Document to insert the HTML into.
+ * @param offset -
+ * where to begin inserting the HTML.
+ * @param insertElement - the element to insert
+ * @param html - the html to insert
+ * @param parentTag - the parent tag
+ * @param addTag - the first tag
+ *
+ * @deprecated as of v1.3, use insertAtBoundary
+ */
+ protected void insertAtBoundry(JEditorPane editor,
+ HTMLDocument doc,
+ int offset, Element insertElement,
+ String html, HTML.Tag parentTag,
+ HTML.Tag addTag)
+ {
+ insertAtBoundary(editor, doc, offset, insertElement,
+ html, parentTag, addTag);
+ }
+
+ /**
+ * Inserts the HTML.
+ *
+ * @param ae - the action performed
+ */
+ public void actionPerformed(ActionEvent ae)
+ {
+ Object source = ae.getSource();
+ if (source instanceof JEditorPane)
+ {
+ JEditorPane pane = ((JEditorPane) source);
+ Document d = pane.getDocument();
+ if (d instanceof HTMLDocument)
+ insertHTML(pane, (HTMLDocument) d, 0, html, 0, 0, addTag);
+ // FIXME: is this correct parameters?
+ }
+ // FIXME: else not implemented
+ }
+ }
+
+ /**
+ * Abstract Action class that helps inserting HTML into an existing document.
+ */
+ public abstract static class HTMLTextAction
+ extends StyledEditorKit.StyledTextAction
+ {
+
+ /**
+ * Constructor
+ */
+ public HTMLTextAction(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Gets the HTMLDocument from the JEditorPane.
+ *
+ * @param e - the editor pane
+ * @return the html document.
+ */
+ protected HTMLDocument getHTMLDocument(JEditorPane e)
+ {
+ Document d = e.getDocument();
+ if (d instanceof HTMLDocument)
+ return (HTMLDocument) d;
+ throw new IllegalArgumentException("Document is not a HTMLDocument.");
+ }
+
+ /**
+ * Gets the HTMLEditorKit
+ *
+ * @param e - the JEditorPane to get the HTMLEditorKit from.
+ * @return the HTMLEditorKit
+ */
+ protected HTMLEditorKit getHTMLEditorKit(JEditorPane e)
+ {
+ EditorKit d = e.getEditorKit();
+ if (d instanceof HTMLEditorKit)
+ return (HTMLEditorKit) d;
+ throw new IllegalArgumentException("EditorKit is not a HTMLEditorKit.");
+ }
+
+ /**
+ * Returns an array of Elements that contain the offset.
+ * The first elements corresponds to the roots of the doc.
+ *
+ * @param doc - the document to get the Elements from.
+ * @param offset - the offset the Elements must contain
+ * @return an array of all the elements containing the offset.
+ */
+ protected Element[] getElementsAt(HTMLDocument doc,
+ int offset)
+ {
+ return getElementsAt(doc.getDefaultRootElement(), offset, 0);
+ }
+
+ /**
+ * Helper function to get all elements using recursion.
+ */
+ private Element[] getElementsAt(Element root, int offset, int depth)
+ {
+ Element[] elements = null;
+ if (root != null)
+ {
+ if (root.isLeaf())
+ {
+ elements = new Element[depth + 1];
+ elements[depth] = root;
+ return elements;
+ }
+ elements = getElementsAt(root.getElement(root.getElementIndex(offset)),
+ offset, depth + 1);
+ elements[depth] = root;
+ }
+ return elements;
+ }
+
+ /**
+ * Returns the number of elements, starting at the deepest point, needed
+ * to get an element representing tag. -1 if no elements are found, 0 if
+ * the parent of the leaf at offset represents the tag.
+ *
+ * @param doc -
+ * the document to search
+ * @param offset -
+ * the offset to check
+ * @param tag -
+ * the tag to look for
+ * @return - the number of elements needed to get an element representing
+ * tag.
+ */
+ protected int elementCountToTag(HTMLDocument doc,
+ int offset, HTML.Tag tag)
+ {
+ Element root = doc.getDefaultRootElement();
+ int num = -1;
+ Element next = root.getElement(root.getElementIndex(offset));
+
+ while (!next.isLeaf())
+ {
+ num++;
+ if (next.getAttributes().
+ getAttribute(StyleConstants.NameAttribute).equals(tag))
+ return num;
+ next = next.getElement(next.getElementIndex(offset));
+ }
+ return num;
+ }
+
+ /**
+ * Gets the deepest element at offset with the
+ * matching tag.
+ *
+ * @param doc - the document to search
+ * @param offset - the offset to check for
+ * @param tag - the tag to match
+ * @return - the element that is found, null if not found.
+ */
+ protected Element findElementMatchingTag(HTMLDocument doc,
+ int offset, HTML.Tag tag)
+ {
+ Element element = doc.getDefaultRootElement();
+ Element tagElement = null;
+
+ while (element != null)
+ {
+ Object otag = element.getAttributes().getAttribute(
+ StyleConstants.NameAttribute);
+ if (otag instanceof HTML.Tag && otag.equals(tag))
+ tagElement = element;
+ element = element.getElement(element.getElementIndex(offset));
+ }
+
+ return tagElement;
+ }
+ }
+
+ /**
+ * A {@link ViewFactory} that is able to create {@link View}s for
+ * the <code>Element</code>s that are supported.
+ */
+ public static class HTMLFactory
+ implements ViewFactory
+ {
+
+ /**
+ * Constructor
+ */
+ public HTMLFactory()
+ {
+ // Do Nothing here.
+ }
+
+ /**
+ * Creates a {@link View} for the specified <code>Element</code>.
+ *
+ * @param element the <code>Element</code> to create a <code>View</code>
+ * for
+ * @return the <code>View</code> for the specified <code>Element</code>
+ * or <code>null</code> if the type of <code>element</code> is
+ * not supported
+ */
+ public View create(Element element)
+ {
+ View view = null;
+ Object attr = element.getAttributes().getAttribute(
+ StyleConstants.NameAttribute);
+ if (attr instanceof HTML.Tag)
+ {
+ HTML.Tag tag = (HTML.Tag) attr;
+
+ if (tag.equals(HTML.Tag.IMPLIED) || tag.equals(HTML.Tag.P)
+ || tag.equals(HTML.Tag.H1) || tag.equals(HTML.Tag.H2)
+ || tag.equals(HTML.Tag.H3) || tag.equals(HTML.Tag.H4)
+ || tag.equals(HTML.Tag.H5) || tag.equals(HTML.Tag.H6)
+ || tag.equals(HTML.Tag.DT))
+ view = new ParagraphView(element);
+ else if (tag.equals(HTML.Tag.LI) || tag.equals(HTML.Tag.DL)
+ || tag.equals(HTML.Tag.DD) || tag.equals(HTML.Tag.BODY)
+ || tag.equals(HTML.Tag.HTML) || tag.equals(HTML.Tag.CENTER)
+ || tag.equals(HTML.Tag.DIV)
+ || tag.equals(HTML.Tag.BLOCKQUOTE)
+ || tag.equals(HTML.Tag.PRE))
+ view = new BlockView(element, View.Y_AXIS);
+
+ // FIXME: Uncomment when the views have been implemented
+ /* else if (tag.equals(HTML.Tag.CONTENT))
+ view = new InlineView(element);
+ else if (tag.equals(HTML.Tag.MENU) || tag.equals(HTML.Tag.DIR)
+ || tag.equals(HTML.Tag.UL) || tag.equals(HTML.Tag.OL))
+ view = new ListView(element);
+ else if (tag.equals(HTML.Tag.IMG))
+ view = new ImageView(element);
+ else if (tag.equals(HTML.Tag.HR))
+ view = new HRuleView(element);
+ else if (tag.equals(HTML.Tag.BR))
+ view = new BRView(element);
+ else if (tag.equals(HTML.Tag.TABLE))
+ view = new TableView(element);
+ else if (tag.equals(HTML.Tag.INPUT) || tag.equals(HTML.Tag.SELECT)
+ || tag.equals(HTML.Tag.TEXTAREA))
+ view = new FormView(element);
+ else if (tag.equals(HTML.Tag.OBJECT))
+ view = new ObjectView(element);
+ else if (tag.equals(HTML.Tag.FRAMESET))
+ view = new FrameSetView(element);
+ else if (tag.equals(HTML.Tag.FRAME))
+ view = new FrameView(element); */
+ }
+
+ if (view == null)
+ {
+ String name = element.getName();
+ if (name.equals(AbstractDocument.ContentElementName))
+ view = new LabelView(element);
+ else if (name.equals(AbstractDocument.ParagraphElementName))
+ view = new ParagraphView(element);
+ else if (name.equals(AbstractDocument.SectionElementName))
+ view = new BoxView(element, View.Y_AXIS);
+ else if (name.equals(StyleConstants.ComponentElementName))
+ view = new ComponentView(element);
+ else if (name.equals(StyleConstants.IconElementName))
+ view = new IconView(element);
+ }
+ return view;
+ }
+ }
+
/**
* The abstract HTML parser declaration.
*/
@@ -76,9 +611,7 @@ public class HTMLEditorKit
* @throws IOException, normally if the reader throws one.
*/
public abstract void parse(Reader reader, ParserCallback callback,
- boolean ignoreCharSet
- )
- throws IOException;
+ boolean ignoreCharSet) throws IOException;
}
/**
@@ -96,11 +629,19 @@ public class HTMLEditorKit
public static final Object IMPLIED = "_implied_";
/**
+ * Constructor
+ */
+ public ParserCallback()
+ {
+ // Nothing to do here.
+ }
+
+ /**
* The parser calls this method after it finishes parsing the document.
*/
public void flush() throws BadLocationException
{
- // TODO: What to do here, if anything?
+ // Nothing to do here.
}
/**
@@ -110,7 +651,7 @@ public class HTMLEditorKit
*/
public void handleComment(char[] comment, int position)
{
- // TODO: What to do here, if anything?
+ // Nothing to do here.
}
/**
@@ -121,7 +662,7 @@ public class HTMLEditorKit
*/
public void handleEndOfLineString(String end_of_line)
{
- // TODO: What to do here, if anything?
+ // Nothing to do here.
}
/**
@@ -133,7 +674,7 @@ public class HTMLEditorKit
*/
public void handleEndTag(HTML.Tag tag, int position)
{
- // TODO: What to do here, if anything?
+ // Nothing to do here.
}
/**
@@ -144,7 +685,7 @@ public class HTMLEditorKit
*/
public void handleError(String message, int position)
{
- // TODO: What to do here, if anything?
+ // Nothing to do here.
}
/**
@@ -157,7 +698,7 @@ public class HTMLEditorKit
public void handleSimpleTag(HTML.Tag tag, MutableAttributeSet attributes,
int position)
{
- // TODO: What to do here, if anything?
+ // Nothing to do here.
}
/**
@@ -168,10 +709,9 @@ public class HTMLEditorKit
* @param position The tag position in the text being parsed
*/
public void handleStartTag(HTML.Tag tag, MutableAttributeSet attributes,
- int position
- )
+ int position)
{
- // TODO: What to do here, if anything?
+ // Nothing to do here.
}
/**
@@ -181,7 +721,7 @@ public class HTMLEditorKit
*/
public void handleText(char[] text, int position)
{
- // TODO: What to do here, if anything?
+ // Nothing to do here.
}
}
@@ -255,7 +795,83 @@ public class HTMLEditorKit
* The "ident paragraph right" action.
*/
public static final String PARA_INDENT_RIGHT = "html-para-indent-right";
-
+
+ /**
+ * Actions for HTML
+ */
+ private static final Action[] defaultActions = {
+ // FIXME: Add default actions for html
+ };
+
+ /**
+ * The current style sheet.
+ */
+ StyleSheet styleSheet;
+
+ /**
+ * The ViewFactory for HTMLFactory.
+ */
+ HTMLFactory viewFactory;
+
+ /**
+ * The Cursor for links.
+ */
+ Cursor linkCursor;
+
+ /**
+ * The default cursor.
+ */
+ Cursor defaultCursor;
+
+ /**
+ * The parser.
+ */
+ Parser parser;
+
+ /**
+ * The mouse listener used for links.
+ */
+ LinkController mouseListener;
+
+ /**
+ * Style context for this editor.
+ */
+ StyleContext styleContext;
+
+ /** The content type */
+ String contentType = "text/html";
+
+ /** The input attributes defined by default.css */
+ MutableAttributeSet inputAttributes;
+
+ /** The editor pane used. */
+ JEditorPane editorPane;
+
+ /**
+ * Constructs an HTMLEditorKit, creates a StyleContext, and loads the style sheet.
+ */
+ public HTMLEditorKit()
+ {
+ super();
+ styleContext = new StyleContext();
+ styleSheet = new StyleSheet();
+ styleSheet.importStyleSheet(getClass().getResource(DEFAULT_CSS));
+ // FIXME: Set inputAttributes with default.css
+ }
+
+ /**
+ * Gets a factory suitable for producing views of any
+ * models that are produced by this kit.
+ *
+ * @return the view factory suitable for producing views.
+ */
+ public ViewFactory getViewFactory()
+ {
+ if (viewFactory == null)
+ viewFactory = new HTMLFactory();
+ return viewFactory;
+ }
+
/**
* Create a text storage model for this type of editor.
*
@@ -263,18 +879,297 @@ public class HTMLEditorKit
*/
public Document createDefaultDocument()
{
- HTMLDocument document = new HTMLDocument();
+ HTMLDocument document = new HTMLDocument(getStyleSheet());
+ document.setParser(getParser());
return document;
}
/**
* Get the parser that this editor kit uses for reading HTML streams. This
* method can be overridden to use the alternative parser.
- *
+ *
* @return the HTML parser (by default, {@link ParserDelegator}).
*/
protected Parser getParser()
{
- return new ParserDelegator();
+ if (parser == null)
+ parser = new ParserDelegator();
+ return parser;
+ }
+
+ /**
+ * Inserts HTML into an existing document.
+ *
+ * @param doc - the Document to insert the HTML into.
+ * @param offset - where to begin inserting the HTML.
+ * @param html - the String to insert
+ * @param popDepth - the number of ElementSpec.EndTagTypes
+ * to generate before inserting
+ * @param pushDepth - the number of ElementSpec.StartTagTypes
+ * with a direction of ElementSpec.JoinNextDirection that
+ * should be generated before
+ * @param insertTag - the first tag to start inserting into document
+ * @throws IOException - on any I/O error
+ * @throws BadLocationException - if pos represents an invalid location
+ * within the document
+ */
+ public void insertHTML(HTMLDocument doc, int offset, String html,
+ int popDepth, int pushDepth, HTML.Tag insertTag)
+ throws BadLocationException, IOException
+ {
+ Parser parser = getParser();
+ if (offset < 0 || offset > doc.getLength())
+ throw new BadLocationException("Bad location", offset);
+ if (parser == null)
+ throw new IOException("Parser is null.");
+
+ ParserCallback pc = ((HTMLDocument) doc).getReader
+ (offset, popDepth, pushDepth, insertTag);
+
+ // FIXME: What should ignoreCharSet be set to?
+
+ // parser.parse inserts html into the buffer
+ parser.parse(new StringReader(html), pc, false);
+ pc.flush();
+ }
+
+ /**
+ * Inserts content from the given stream. Inserting HTML into a non-empty
+ * document must be inside the body Element, if you do not insert into
+ * the body an exception will be thrown. When inserting into a non-empty
+ * document all tags outside of the body (head, title) will be dropped.
+ *
+ * @param in - the stream to read from
+ * @param doc - the destination for the insertion
+ * @param pos - the location in the document to place the content
+ * @throws IOException - on any I/O error
+ * @throws BadLocationException - if pos represents an invalid location
+ * within the document
+ */
+ public void read(Reader in, Document doc, int pos) throws IOException,
+ BadLocationException
+ {
+ if (doc instanceof HTMLDocument)
+ {
+ Parser parser = getParser();
+ if (pos < 0 || pos > doc.getLength())
+ throw new BadLocationException("Bad location", pos);
+ if (parser == null)
+ throw new IOException("Parser is null.");
+
+ HTMLDocument hd = ((HTMLDocument) doc);
+ hd.setBase(editorPane.getPage());
+ ParserCallback pc = hd.getReader(pos);
+
+ // FIXME: What should ignoreCharSet be set to?
+
+ // parser.parse inserts html into the buffer
+ parser.parse(in, pc, false);
+ pc.flush();
+ }
+ else
+ // read in DefaultEditorKit is called.
+ // the string is inserted in the document as usual.
+ super.read(in, doc, pos);
+ }
+
+ /**
+ * Writes content from a document to the given stream in
+ * an appropriate format.
+ *
+ * @param out - the stream to write to
+ * @param doc - the source for the write
+ * @param pos - the location in the document to get the content.
+ * @param len - the amount to write out
+ * @throws IOException - on any I/O error
+ * @throws BadLocationException - if pos represents an invalid location
+ * within the document
+ */
+ public void write(Writer out, Document doc, int pos, int len)
+ throws IOException, BadLocationException
+ {
+ if (doc instanceof HTMLDocument)
+ {
+ // FIXME: Not implemented. Use HTMLWriter.
+ out.write(doc.getText(pos, len));
+ }
+ else
+ super.write(out, doc, pos, len);
+ }
+
+ /**
+ * Gets the content type that the kit supports.
+ * This kit supports the type text/html.
+ *
+ * @returns the content type supported.
+ */
+ public String getContentType()
+ {
+ return contentType;
+ }
+
+ /**
+ * Creates a copy of the editor kit.
+ *
+ * @return a copy of this.
+ */
+ public Object clone()
+ {
+ // FIXME: Need to clone all fields
+ return (HTMLEditorKit) super.clone();
+ }
+
+ /**
+ * Copies the key/values in elements AttributeSet into set.
+ * This does not copy component, icon, or element names attributes.
+ * This is called anytime the caret moves over a different location.
+ *
+ * @param element - the element to create the input attributes for.
+ * @param set - the set to copy the values into.
+ */
+ protected void createInputAttributes(Element element,
+ MutableAttributeSet set)
+ {
+ set.removeAttributes(set);
+ set.addAttributes(element.getAttributes());
+ // FIXME: Not fully implemented.
+ }
+
+ /**
+ * Called when this is installed into the JEditorPane.
+ *
+ * @param c - the JEditorPane installed into.
+ */
+ public void install(JEditorPane c)
+ {
+ super.install(c);
+ mouseListener = new LinkController();
+ c.addMouseListener(mouseListener);
+ editorPane = c;
+ // FIXME: need to set up hyperlinklistener object
+ }
+
+ /**
+ * Called when the this is removed from the JEditorPane.
+ * It unregisters any listeners.
+ *
+ * @param c - the JEditorPane being removed from.
+ */
+ public void deinstall(JEditorPane c)
+ {
+ super.deinstall(c);
+ c.removeMouseListener(mouseListener);
+ mouseListener = null;
+ editorPane = null;
+ }
+
+ /**
+ * Gets the AccessibleContext associated with this.
+ *
+ * @return the AccessibleContext for this.
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ // FIXME: Should return an instance of
+ // javax.swing.text.html.AccessibleHTML$RootHTMLAccessibleContext
+ // Not implemented yet.
+ return null;
+ }
+
+ /**
+ * Gets the action list. This list is supported by the superclass
+ * augmented by the collection of actions defined locally for style
+ * operations.
+ *
+ * @return an array of all the actions
+ */
+ public Action[] getActions()
+ {
+ return TextAction.augmentList(super.getActions(), defaultActions);
+ }
+
+ /**
+ * Returns the default cursor.
+ *
+ * @return the default cursor
+ */
+ public Cursor getDefaultCursor()
+ {
+ if (defaultCursor == null)
+ defaultCursor = Cursor.getDefaultCursor();
+ return defaultCursor;
+ }
+
+ /**
+ * Returns the cursor for links.
+ *
+ * @return the cursor for links.
+ */
+ public Cursor getLinkCursor()
+ {
+ if (linkCursor == null)
+ linkCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
+ return linkCursor;
+ }
+
+ /**
+ * Sets the Cursor for links.
+ *
+ * @param cursor - the new cursor for links.
+ */
+ public void setLinkCursor(Cursor cursor)
+ {
+ linkCursor = cursor;
+ }
+
+ /**
+ * Sets the default cursor.
+ *
+ * @param cursor - the new default cursor.
+ */
+ public void setDefaultCursor(Cursor cursor)
+ {
+ defaultCursor = cursor;
+ }
+
+ /**
+ * Gets the input attributes used for the styled editing actions.
+ *
+ * @return the attribute set
+ */
+ public MutableAttributeSet getInputAttributes()
+ {
+ return inputAttributes;
+ }
+
+ /**
+ * Get the set of styles currently being used to render the HTML elements.
+ * By default the resource specified by DEFAULT_CSS gets loaded, and is
+ * shared by all HTMLEditorKit instances.
+ *
+ * @return the style sheet.
+ */
+ public StyleSheet getStyleSheet()
+ {
+ if (styleSheet == null)
+ {
+ styleSheet = new StyleSheet();
+ styleSheet.importStyleSheet(getClass().getResource(DEFAULT_CSS));
+ }
+ return styleSheet;
+ }
+
+ /**
+ * Set the set of styles to be used to render the various HTML elements.
+ * These styles are specified in terms of CSS specifications. Each document
+ * produced by the kit will have a copy of the sheet which it can add the
+ * document specific styles to. By default, the StyleSheet specified is shared
+ * by all HTMLEditorKit instances.
+ *
+ * @param s - the new style sheet
+ */
+ public void setStyleSheet(StyleSheet s)
+ {
+ styleSheet = s;
}
-} \ No newline at end of file
+}
diff --git a/libjava/classpath/javax/swing/text/html/StyleSheet.java b/libjava/classpath/javax/swing/text/html/StyleSheet.java
new file mode 100644
index 0000000..2466a28
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/StyleSheet.java
@@ -0,0 +1,937 @@
+/* StyleSheet.java --
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text.html;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Serializable;
+import java.io.StringReader;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import javax.swing.text.AttributeSet;
+import javax.swing.text.Element;
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.Style;
+import javax.swing.text.StyleContext;
+import javax.swing.text.View;
+
+
+/**
+ * This class adds support for defining the visual characteristics of HTML views
+ * being rendered. This enables views to be customized by a look-and-feel, mulitple
+ * views over the same model can be rendered differently. Each EditorPane has its
+ * own StyleSheet, but by default one sheet will be shared by all of the HTMLEditorKit
+ * instances. An HTMLDocument can also have a StyleSheet, which holds specific CSS
+ * specs.
+ *
+ * In order for Views to store less state and therefore be more lightweight,
+ * the StyleSheet can act as a factory for painters that handle some of the
+ * rendering tasks. Since the StyleSheet may be used by views over multiple
+ * documents the HTML attributes don't effect the selector being used.
+ *
+ * The rules are stored as named styles, and other information is stored to
+ * translate the context of an element to a rule.
+ *
+ * @author Lillian Angel (langel@redhat.com)
+ */
+public class StyleSheet extends StyleContext
+{
+
+ /** The base URL */
+ URL base;
+
+ /** Base font size (int) */
+ int baseFontSize;
+
+ /** The style sheets stored. */
+ StyleSheet[] styleSheet;
+
+ /**
+ * Constructs a StyleSheet.
+ */
+ public StyleSheet()
+ {
+ super();
+ baseFontSize = 4; // Default font size from CSS
+ }
+
+ /**
+ * Gets the style used to render the given tag. The element represents the tag
+ * and can be used to determine the nesting, where the attributes will differ
+ * if there is nesting inside of elements.
+ *
+ * @param t - the tag to translate to visual attributes
+ * @param e - the element representing the tag
+ * @return the set of CSS attributes to use to render the tag.
+ */
+ public Style getRule(HTML.Tag t, Element e)
+ {
+ // FIXME: Not implemented.
+ return null;
+ }
+
+ /**
+ * Gets the rule that best matches the selector. selector is a space
+ * separated String of element names. The attributes of the returned
+ * Style will change as rules are added and removed.
+ *
+ * @param selector - the element names separated by spaces
+ * @return the set of CSS attributes to use to render
+ */
+ public Style getRule(String selector)
+ {
+ // FIXME: Not implemented.
+ return null;
+ }
+
+ /**
+ * Adds a set if rules to the sheet. The rules are expected to be in valid
+ * CSS format. This is called as a result of parsing a <style> tag
+ *
+ * @param rule - the rule to add to the sheet
+ */
+ public void addRule(String rule)
+ {
+ CssParser cp = new CssParser();
+ try
+ {
+ cp.parse(base, new StringReader(rule), false, false);
+ }
+ catch (IOException io)
+ {
+ // Do nothing here.
+ }
+ }
+
+ /**
+ * Translates a CSS declaration into an AttributeSet. This is called
+ * as a result of encountering an HTML style attribute.
+ *
+ * @param decl - the declaration to get
+ * @return the AttributeSet representing the declaration
+ */
+ public AttributeSet getDeclaration(String decl)
+ {
+ if (decl == null)
+ return SimpleAttributeSet.EMPTY;
+ // FIXME: Not implemented.
+ return null;
+ }
+
+ /**
+ * Loads a set of rules that have been specified in terms of CSS grammar.
+ * If there are any conflicts with existing rules, the new rule is added.
+ *
+ * @param in - the stream to read the CSS grammar from.
+ * @param ref - the reference URL. It is the location of the stream, it may
+ * be null. All relative URLs specified in the stream will be based upon this
+ * parameter.
+ * @throws IOException - For any IO error while reading
+ */
+ public void loadRules(Reader in, URL ref) throws IOException
+ {
+ CssParser cp = new CssParser();
+ cp.parse(ref, in, false, false);
+ }
+
+ /**
+ * Gets a set of attributes to use in the view. This is a set of
+ * attributes that can be used for View.getAttributes
+ *
+ * @param v - the view to get the set for
+ * @return the AttributeSet to use in the view.
+ */
+ public AttributeSet getViewAttributes(View v)
+ {
+ // FIXME: Not implemented.
+ return null;
+ }
+
+ /**
+ * Removes a style previously added.
+ *
+ * @param nm - the name of the style to remove
+ */
+ public void removeStyle(String nm)
+ {
+ // FIXME: Not implemented.
+ super.removeStyle(nm);
+ }
+
+ /**
+ * Adds the rules from ss to those of the receiver. ss's rules will
+ * override the old rules. An added StyleSheet will never override the rules
+ * of the receiving style sheet.
+ *
+ * @param ss - the new StyleSheet.
+ */
+ public void addStyleSheet(StyleSheet ss)
+ {
+ if (styleSheet == null)
+ styleSheet = new StyleSheet[] {ss};
+ else
+ System.arraycopy(new StyleSheet[] {ss}, 0, styleSheet,
+ styleSheet.length, 1);
+ }
+
+ /**
+ * Removes ss from those of the receiver
+ *
+ * @param ss - the StyleSheet to remove.
+ */
+ public void removeStyleSheet(StyleSheet ss)
+ {
+ if (styleSheet.length == 1 && styleSheet[0].equals(ss))
+ styleSheet = null;
+ else
+ {
+ for (int i = 0; i < styleSheet.length; i++)
+ {
+ StyleSheet curr = styleSheet[i];
+ if (curr.equals(ss))
+ {
+ StyleSheet[] tmp = new StyleSheet[styleSheet.length - 1];
+ if (i != 0 && i != (styleSheet.length - 1))
+ {
+ System.arraycopy(styleSheet, 0, tmp, 0, i);
+ System.arraycopy(styleSheet, i + 1, tmp, i,
+ styleSheet.length - i - 1);
+ }
+ else if (i == 0)
+ System.arraycopy(styleSheet, 1, tmp, 0, styleSheet.length - 1);
+ else
+ System.arraycopy(styleSheet, 0, tmp, 0, styleSheet.length - 1);
+
+ styleSheet = tmp;
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns an array of the linked StyleSheets. May return null.
+ *
+ * @return - An array of the linked StyleSheets.
+ */
+ public StyleSheet[] getStyleSheets()
+ {
+ return styleSheet;
+ }
+
+ /**
+ * Imports a style sheet from the url. The rules are directly added to the
+ * receiver.
+ *
+ * @param url - the URL to import the StyleSheet from.
+ */
+ public void importStyleSheet(URL url)
+ {
+ // FIXME: Not implemented
+ }
+
+ /**
+ * Sets the base url. All import statements that are relative, will be
+ * relative to base.
+ *
+ * @param base -
+ * the base URL.
+ */
+ public void setBase(URL base)
+ {
+ this.base = base;
+ }
+
+ /**
+ * Gets the base url.
+ *
+ * @return - the base
+ */
+ public URL getBase()
+ {
+ return base;
+ }
+
+ /**
+ * Adds a CSS attribute to the given set.
+ *
+ * @param attr - the attribute set
+ * @param key - the attribute to add
+ * @param value - the value of the key
+ */
+ public void addCSSAttribute(MutableAttributeSet attr, CSS.Attribute key,
+ String value)
+ {
+ attr.addAttribute(key, value);
+ }
+
+ /**
+ * Adds a CSS attribute to the given set.
+ * This method parses the value argument from HTML based on key.
+ * Returns true if it finds a valid value for the given key,
+ * and false otherwise.
+ *
+ * @param attr - the attribute set
+ * @param key - the attribute to add
+ * @param value - the value of the key
+ * @return true if a valid value was found.
+ */
+ public boolean addCSSAttributeFromHTML(MutableAttributeSet attr, CSS.Attribute key,
+ String value)
+ {
+ // FIXME: Need to parse value from HTML based on key.
+ attr.addAttribute(key, value);
+ return attr.containsAttribute(key, value);
+ }
+
+ /**
+ * Converts a set of HTML attributes to an equivalent set of CSS attributes.
+ *
+ * @param htmlAttrSet - the set containing the HTML attributes.
+ * @return the set of CSS attributes
+ */
+ public AttributeSet translateHTMLToCSS(AttributeSet htmlAttrSet)
+ {
+ // FIXME: Not implemented.
+ return null;
+ }
+
+ /**
+ * Adds an attribute to the given set and returns a new set. This is implemented
+ * to convert StyleConstants attributes to CSS before forwarding them to the superclass.
+ * The StyleConstants attribute do not have corresponding CSS entry, the attribute
+ * is stored (but will likely not be used).
+ *
+ * @param old - the old set
+ * @param key - the non-null attribute key
+ * @param value - the attribute value
+ * @return the updated set
+ */
+ public AttributeSet addAttribute(AttributeSet old, Object key,
+ Object value)
+ {
+ // FIXME: Not implemented.
+ return super.addAttribute(old, key, value);
+ }
+
+ /**
+ * Adds a set of attributes to the element. If any of these attributes are
+ * StyleConstants, they will be converted to CSS before forwarding to the
+ * superclass.
+ *
+ * @param old - the old set
+ * @param attr - the attributes to add
+ * @return the updated attribute set
+ */
+ public AttributeSet addAttributes(AttributeSet old, AttributeSet attr)
+ {
+ // FIXME: Not implemented.
+ return super.addAttributes(old, attr);
+ }
+
+ /**
+ * Removes an attribute from the set. If the attribute is a
+ * StyleConstants, it will be converted to CSS before forwarding to the
+ * superclass.
+ *
+ * @param old - the old set
+ * @param key - the non-null attribute key
+ * @return the updated set
+ */
+ public AttributeSet removeAttribute(AttributeSet old, Object key)
+ {
+ // FIXME: Not implemented.
+ return super.removeAttribute(old, key);
+ }
+
+ /**
+ * Removes an attribute from the set. If any of the attributes are
+ * StyleConstants, they will be converted to CSS before forwarding to the
+ * superclass.
+ *
+ * @param old - the old set
+ * @param attrs - the attributes to remove
+ * @return the updated set
+ */
+ public AttributeSet removeAttributes(AttributeSet old, AttributeSet attrs)
+ {
+ // FIXME: Not implemented.
+ return super.removeAttributes(old, attrs);
+ }
+
+ /**
+ * Removes a set of attributes for the element. If any of the attributes is a
+ * StyleConstants, they will be converted to CSS before forwarding to the
+ * superclass.
+ *
+ * @param old - the old attribute set
+ * @param names - the attribute names
+ * @return the update attribute set
+ */
+ public AttributeSet removeAttributes(AttributeSet old, Enumeration names)
+ {
+ // FIXME: Not implemented.
+ return super.removeAttributes(old, names);
+ }
+
+ /**
+ * Creates a compact set of attributes that might be shared. This is a hook
+ * for subclasses that want to change the behaviour of SmallAttributeSet.
+ *
+ * @param a - the set of attributes to be represented in the compact form.
+ * @return the set of attributes created
+ */
+ protected StyleContext.SmallAttributeSet createSmallAttributeSet(AttributeSet a)
+ {
+ return super.createSmallAttributeSet(a);
+ }
+
+ /**
+ * Creates a large set of attributes. This set is not shared. This is a hook
+ * for subclasses that want to change the behaviour of the larger attribute
+ * storage format.
+ *
+ * @param a - the set of attributes to be represented in the larger form.
+ * @return the large set of attributes.
+ */
+ protected MutableAttributeSet createLargeAttributeSet(AttributeSet a)
+ {
+ return super.createLargeAttributeSet(a);
+ }
+
+ /**
+ * Gets the font to use for the given set.
+ *
+ * @param a - the set to get the font for.
+ * @return the font for the set
+ */
+ public Font getFont(AttributeSet a)
+ {
+ return super.getFont(a);
+ }
+
+ /**
+ * Takes a set of attributes and turns it into a foreground
+ * color specification. This is used to specify things like, brigher, more hue
+ * etc.
+ *
+ * @param a - the set to get the foreground color for
+ * @return the foreground color for the set
+ */
+ public Color getForeground(AttributeSet a)
+ {
+ return super.getForeground(a);
+ }
+
+ /**
+ * Takes a set of attributes and turns it into a background
+ * color specification. This is used to specify things like, brigher, more hue
+ * etc.
+ *
+ * @param a - the set to get the background color for
+ * @return the background color for the set
+ */
+ public Color getBackground(AttributeSet a)
+ {
+ return super.getBackground(a);
+ }
+
+ /**
+ * Gets the box formatter to use for the given set of CSS attributes.
+ *
+ * @param a - the given set
+ * @return the box formatter
+ */
+ public BoxPainter getBoxPainter(AttributeSet a)
+ {
+ return new BoxPainter(a);
+ }
+
+ /**
+ * Gets the list formatter to use for the given set of CSS attributes.
+ *
+ * @param a - the given set
+ * @return the list formatter
+ */
+ public ListPainter getListPainter(AttributeSet a)
+ {
+ return new ListPainter(a);
+ }
+
+ /**
+ * Sets the base font size between 1 and 7.
+ *
+ * @param sz - the new font size for the base.
+ */
+ public void setBaseFontSize(int sz)
+ {
+ if (sz <= 7 && sz >= 1)
+ baseFontSize = sz;
+ }
+
+ /**
+ * Sets the base font size from the String. It can either identify
+ * a specific font size (between 1 and 7) or identify a relative
+ * font size such as +1 or -2.
+ *
+ * @param size - the new font size as a String.
+ */
+ public void setBaseFontSize(String size)
+ {
+ size.trim();
+ int temp = 0;
+ try
+ {
+ if (size.length() == 2)
+ {
+ int i = new Integer(size.substring(1)).intValue();
+ if (size.startsWith("+"))
+ temp = baseFontSize + i;
+ else if (size.startsWith("-"))
+ temp = baseFontSize - i;
+ }
+ else if (size.length() == 1)
+ temp = new Integer(size.substring(0)).intValue();
+
+ if (temp <= 7 && temp >= 1)
+ baseFontSize = temp;
+ }
+ catch (NumberFormatException nfe)
+ {
+ // Do nothing here
+ }
+ }
+
+ /**
+ * TODO
+ *
+ * @param pt - TODO
+ * @return TODO
+ */
+ public static int getIndexOfSize(float pt)
+ {
+ // FIXME: Not implemented.
+ return 0;
+ }
+
+ /**
+ * Gets the point size, given a size index.
+ *
+ * @param index - the size index
+ * @return the point size.
+ */
+ public float getPointSize(int index)
+ {
+ // FIXME: Not implemented.
+ return 0;
+ }
+
+ /**
+ * Given the string of the size, returns the point size value.
+ *
+ * @param size - the string representation of the size.
+ * @return - the point size value.
+ */
+ public float getPointSize(String size)
+ {
+ // FIXME: Not implemented.
+ return 0;
+ }
+
+ /**
+ * Converst a color string to a color. If it is not found, null is returned.
+ *
+ * @param color - the color string such as "RED" or "#NNNNNN"
+ * @return the Color, or null if not found.
+ */
+ public Color stringToColor(String color)
+ {
+ color = color.toLowerCase();
+ if (color.equals("black") || color.equals("#000000"))
+ return Color.BLACK;
+ else if (color.equals("aqua") || color.equals("#00FFFF"))
+ return new Color(127, 255, 212);
+ else if (color.equals("gray") || color.equals("#808080"))
+ return Color.GRAY;
+ else if (color.equals("navy") || color.equals("#000080"))
+ return new Color(0, 0, 128);
+ else if (color.equals("silver") || color.equals("#C0C0C0"))
+ return Color.LIGHT_GRAY;
+ else if (color.equals("green") || color.equals("#008000"))
+ return Color.GREEN;
+ else if (color.equals("olive") || color.equals("#808000"))
+ return new Color(128, 128, 0);
+ else if (color.equals("teal") || color.equals("#008080"))
+ return new Color(0, 128, 128);
+ else if (color.equals("blue") || color.equals("#0000FF"))
+ return Color.BLUE;
+ else if (color.equals("lime") || color.equals("#00FF00"))
+ return new Color(0, 255, 0);
+ else if (color.equals("purple") || color.equals("#800080"))
+ return new Color(128, 0, 128);
+ else if (color.equals("white") || color.equals("#FFFFFF"))
+ return Color.WHITE;
+ else if (color.equals("fuchsia") || color.equals("#FF00FF"))
+ return Color.MAGENTA;
+ else if (color.equals("maroon") || color.equals("#800000"))
+ return new Color(128, 0, 0);
+ else if (color.equals("Red") || color.equals("#FF0000"))
+ return Color.RED;
+ else if (color.equals("Yellow") || color.equals("#FFFF00"))
+ return Color.YELLOW;
+ return null;
+ }
+
+ /**
+ * This class carries out some of the duties of CSS formatting. This enables views
+ * to present the CSS formatting while not knowing how the CSS values are cached.
+ *
+ * This object is reponsible for the insets of a View and making sure
+ * the background is maintained according to the CSS attributes.
+ *
+ * @author Lillian Angel (langel@redhat.com)
+ */
+ public static class BoxPainter extends Object implements Serializable
+ {
+
+ /**
+ * Attribute set for painter
+ */
+ AttributeSet as;
+
+ /**
+ * Package-private constructor.
+ *
+ * @param as - AttributeSet for painter
+ */
+ BoxPainter(AttributeSet as)
+ {
+ this.as = as;
+ }
+
+ /**
+ * Gets the inset needed on a given side to account for the margin, border
+ * and padding.
+ *
+ * @param size - the size of the box to get the inset for. View.TOP, View.LEFT,
+ * View.BOTTOM or View.RIGHT.
+ * @param v - the view making the request. This is used to get the AttributeSet,
+ * amd may be used to resolve percentage arguments.
+ * @return the inset
+ * @throws IllegalArgumentException - for an invalid direction.
+ */
+ public float getInset(int size, View v)
+ {
+ // FIXME: Not implemented.
+ return 0;
+ }
+
+ /**
+ * Paints the CSS box according to the attributes given. This should
+ * paint the border, padding and background.
+ *
+ * @param g - the graphics configuration
+ * @param x - the x coordinate
+ * @param y - the y coordinate
+ * @param w - the width of the allocated area
+ * @param h - the height of the allocated area
+ * @param v - the view making the request
+ */
+ public void paint(Graphics g, float x, float y, float w, float h, View v)
+ {
+ // FIXME: Not implemented.
+ }
+ }
+
+ /**
+ * This class carries out some of the CSS list formatting duties. Implementations
+ * of this class enable views to present the CSS formatting while not knowing anything
+ * about how the CSS values are being cached.
+ *
+ * @author Lillian Angel (langel@redhat.com)
+ */
+ public static class ListPainter extends Object implements Serializable
+ {
+
+ /**
+ * Attribute set for painter
+ */
+ AttributeSet as;
+
+ /**
+ * Package-private constructor.
+ *
+ * @param as - AttributeSet for painter
+ */
+ ListPainter(AttributeSet as)
+ {
+ this.as = as;
+ }
+
+ /**
+ * Paints the CSS list decoration according to the attributes given.
+ *
+ * @param g - the graphics configuration
+ * @param x - the x coordinate
+ * @param y - the y coordinate
+ * @param w - the width of the allocated area
+ * @param h - the height of the allocated area
+ * @param v - the view making the request
+ * @param item - the list item to be painted >=0.
+ */
+ public void paint(Graphics g, float x, float y, float w, float h, View v,
+ int item)
+ {
+ // FIXME: Not implemented.
+ }
+ }
+
+ /**
+ * The parser callback for the CSSParser.
+ */
+ class CssParser implements CSSParser.CSSParserCallback
+ {
+ /**
+ * A vector of all the selectors.
+ * Each element is an array of all the selector tokens
+ * in a single rule.
+ */
+ Vector selectors;
+
+ /** A vector of all the selector tokens in a rule. */
+ Vector selectorTokens;
+
+ /** Name of the current property. */
+ String propertyName;
+
+ /** The set of CSS declarations */
+ MutableAttributeSet declaration;
+
+ /**
+ * True if parsing a declaration, that is the Reader will not
+ * contain a selector.
+ */
+ boolean parsingDeclaration;
+
+ /** True if the attributes are coming from a linked/imported style. */
+ boolean isLink;
+
+ /** The base URL */
+ URL base;
+
+ /** The parser */
+ CSSParser parser;
+
+ /**
+ * Constructor
+ */
+ CssParser()
+ {
+ selectors = new Vector();
+ selectorTokens = new Vector();
+ parser = new CSSParser();
+ base = StyleSheet.this.base;
+ declaration = new SimpleAttributeSet();
+ }
+
+ /**
+ * Parses the passed in CSS declaration into an AttributeSet.
+ *
+ * @param s - the declaration
+ * @return the set of attributes containing the property and value.
+ */
+ public AttributeSet parseDeclaration(String s)
+ {
+ try
+ {
+ return parseDeclaration(new StringReader(s));
+ }
+ catch (IOException e)
+ {
+ // Do nothing here.
+ }
+ return null;
+ }
+
+ /**
+ * Parses the passed in CSS declaration into an AttributeSet.
+ *
+ * @param r - the reader
+ * @return the attribute set
+ * @throws IOException from the reader
+ */
+ public AttributeSet parseDeclaration(Reader r) throws IOException
+ {
+ parse(base, r, true, false);
+ return declaration;
+ }
+
+ /**
+ * Parse the given CSS stream
+ *
+ * @param base - the url
+ * @param r - the reader
+ * @param parseDec - True if parsing a declaration
+ * @param isLink - True if parsing a link
+ */
+ public void parse(URL base, Reader r, boolean parseDec, boolean isLink) throws IOException
+ {
+ parsingDeclaration = parseDec;
+ this.isLink = isLink;
+ this.base = base;
+
+ // flush out all storage
+ propertyName = null;
+ selectors.clear();
+ selectorTokens.clear();
+ declaration.removeAttributes(declaration);
+
+ parser.parse(r, this, parseDec);
+ }
+
+ /**
+ * Invoked when a valid @import is encountered,
+ * will call importStyleSheet if a MalformedURLException
+ * is not thrown in creating the URL.
+ *
+ * @param s - the string after @import
+ */
+ public void handleImport(String s)
+ {
+ if (s != null)
+ {
+ try
+ {
+ if (s.startsWith("url(") && s.endsWith(")"))
+ s = s.substring(4, s.length() - 1);
+ if (s.indexOf("\"") >= 0)
+ s = s.replaceAll("\"","");
+
+ URL url = new URL(s);
+ if (url == null && base != null)
+ url = new URL(base, s);
+
+ importStyleSheet(url);
+ }
+ catch (MalformedURLException e)
+ {
+ // Do nothing here.
+ }
+ }
+ }
+
+ /**
+ * A selector has been encountered.
+ *
+ * @param s - a selector (e.g. P or UL or even P,)
+ */
+ public void handleSelector(String s)
+ {
+ if (s.endsWith(","))
+ s = s.substring(0, s.length() - 1);
+
+ selectorTokens.addElement(s);
+ addSelector();
+ }
+
+ /**
+ * Invoked when the start of a rule is encountered.
+ */
+ public void startRule()
+ {
+ addSelector();
+ }
+
+ /**
+ * Invoked when a property name is encountered.
+ *
+ * @param s - the property
+ */
+ public void handleProperty(String s)
+ {
+ propertyName = s;
+ }
+
+ /**
+ * Invoked when a property value is encountered.
+ *
+ * @param s - the value
+ */
+ public void handleValue(String s)
+ {
+ // call addCSSAttribute
+ // FIXME: Not implemented
+ }
+
+ /**
+ * Invoked when the end of a rule is encountered.
+ */
+ public void endRule()
+ {
+ // FIXME: Not implemented
+ // add rules
+ propertyName = null;
+ }
+
+ /**
+ * Adds the selector to the vector.
+ */
+ private void addSelector()
+ {
+ int length = selectorTokens.size();
+ if (length > 0)
+ {
+ Object[] sel = new Object[length];
+ System.arraycopy(selectorTokens.toArray(), 0, sel, 0, length);
+ selectors.add(sel);
+ selectorTokens.clear();
+ }
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/default.css b/libjava/classpath/javax/swing/text/html/default.css
new file mode 100644
index 0000000..f2a44f8
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/default.css
@@ -0,0 +1,378 @@
+/* default.css --
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+nobr {
+ white-space: nowrap;
+}
+
+ol {
+ margin-right: 50px;
+ margin-top: 10px;
+ margin-left: 50px;
+ margin-bottom: 10px;
+ list-style-type: decimal;
+}
+
+u {
+ text-decoration: underline;
+}
+
+s {
+ text-decoration: line-through;
+}
+
+p {
+ margin-top: 15px;
+}
+
+dd p {
+ margin-left: 0px;
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+
+ol li p {
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+
+
+address {
+ font-style: italic;
+ color: blue;
+}
+
+i {
+ font-style: italic;
+}
+
+h6 {
+ margin-top: 10px;
+ font-size: xx-small;
+ font-weight: bold;
+ margin-bottom: 10px;
+}
+
+h5 {
+ margin-top: 10px;
+ font-size: x-small;
+ font-weight: bold;
+ margin-bottom: 10px;
+}
+
+h4 {
+ margin-top: 10px;
+ font-size: small;
+ font-weight: bold;
+ margin-bottom: 10px;
+}
+
+h3 {
+ margin-top: 10px;
+ font-size: medium;
+ font-weight: bold;
+ margin-bottom: 10px;
+}
+
+dir li p {
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+
+h2 {
+ margin-top: 10px;
+ font-size: large;
+ font-weight: bold;
+ margin-bottom: 10px;
+}
+
+b {
+ font-weight: bold;
+}
+
+h1 {
+ margin-top: 10px;
+ font-size: x-large;
+ font-weight: bold;
+ margin-bottom: 10px;
+}
+
+caption {
+ text-align: center;
+ caption-side: top;
+}
+
+a {
+ text-decoration: underline;
+ color: blue;
+}
+
+ul li ul li ul li {
+ margin-left: 0px;
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+
+menu {
+ margin-right: 40px;
+ margin-top: 10px;
+ margin-left: 40px;
+ margin-bottom: 10px;
+}
+
+menu li p {
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+
+sup {
+ vertical-align: super;
+}
+
+body {
+ margin-right: 0px;
+ margin-left: 0px;
+ font-family: Serif;
+ font-size: 14pt;
+ font-weight: normal;
+ color: black;
+}
+
+ul li ul li ul {
+ margin-right: 25px;
+ margin-left: 25px;
+ list-style-type: square;
+}
+
+blockquote {
+ margin-right: 35px;
+ margin-left: 35px;
+ margin-top: 5px;
+ margin-bottom: 5px;
+}
+
+samp {
+ font-family: Monospaced;
+ font-size: small;
+}
+
+cite {
+ font-style: italic;
+}
+
+sub {
+ vertical-align: sub;
+}
+
+em {
+ font-style: italic;
+}
+
+ul li p {
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+
+ul li ul li {
+ margin-right: 0px;
+ margin-left: 0px;
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+
+var {
+ font-style: italic;
+ font-weight: bold;
+}
+
+table {
+ border-color: Gray;
+ border-style: outset;
+}
+
+dfn {
+ font-style: italic;
+}
+
+menu li {
+ margin-right: 0px;
+ margin-left: 0px;
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+
+strong {
+ font-weight: bold;
+}
+
+ul {
+ margin-right: 50px;
+ margin-top: 10px;
+ margin-left: 50px;
+ margin-bottom: 10px;
+ list-style-type: disc;
+}
+
+center {
+ text-align: center;
+}
+
+ul li ul {
+ margin-right: 25px;
+ margin-left: 25px;
+ list-style-type: circle;
+}
+
+kbd {
+ font-family: Monospaced;
+ font-size: small;
+}
+
+dir li {
+ margin-right: 0px;
+ margin-left: 0px;
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+
+ul li menu {
+ margin-right: 25px;
+ margin-left: 25px;
+ list-style-type: circle;
+}
+
+dt {
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+
+ol li {
+ margin-right: 0px;
+ margin-left: 0px;
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+
+li p {
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+
+default {
+}
+
+strike {
+ text-decoration: line-through;
+}
+
+dl {
+ margin-left: 0px;
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+
+tt {
+ font-family: Monospaced;
+}
+
+ul li {
+ margin-right: 0px;
+ margin-left: 0px;
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+
+dir {
+ margin-right: 40px;
+ margin-top: 10px;
+ margin-left: 40px;
+ margin-bottom: 10px;
+}
+
+tr {
+ text-align: left;
+}
+
+pre p {
+ margin-top: 0px;
+}
+
+dd {
+ margin-right: 40px;
+ margin-top: 0px;
+ margin-left: 40px;
+ margin-bottom: 0px;
+}
+
+th {
+ padding-bottom: 3px;
+ text-align: center;
+ padding-top: 3px;
+ padding-right: 3px;
+ padding-left: 3px;
+ font-weight: bold;
+ border-color: Gray;
+ border-style: inset;
+}
+
+pre {
+ margin-top: 5px;
+ font-family: Monospaced;
+ margin-bottom: 5px;
+}
+
+td {
+ padding-bottom: 3px;
+ padding-top: 3px;
+ padding-right: 3px;
+ padding-left: 3px;
+ border-color: Gray;
+ border-style: inset;
+}
+
+code {
+ font-family: Monospaced;
+ font-size: small;
+}
+
+small {
+ font-size: x-small;
+}
+
+big {
+ font-size: x-large;
+}