You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pivot.apache.org by rw...@apache.org on 2013/12/17 21:17:49 UTC
svn commit: r1551679 - in /pivot/trunk/wtk/src/org/apache/pivot/wtk: ./
skin/ text/
Author: rwhitcomb
Date: Tue Dec 17 20:17:48 2013
New Revision: 1551679
URL: http://svn.apache.org/r1551679
Log:
PIVOT-696: Support expansion of tabs in TextPane in the same ways as
with TextArea.
Add new style: tabWidth and property expandTabs.
This also required changing the way we get a hold of the TextPane.Skin
from within the skin view classes. Also PlainTextSerializer needs the
tab settings.
Modified:
pivot/trunk/wtk/src/org/apache/pivot/wtk/TextPane.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkin.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBlockView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBulletedListView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinComponentNodeView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinDocumentView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinElementView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinImageNodeView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListItemView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNodeView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNumberedListView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinParagraphView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinSpanView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinTextNodeView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinVerticalElementView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/text/PlainTextSerializer.java
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/TextPane.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/TextPane.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/TextPane.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/TextPane.java Tue Dec 17 20:17:48 2013
@@ -17,8 +17,12 @@
package org.apache.pivot.wtk;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
+import java.net.URL;
import org.apache.pivot.beans.DefaultProperty;
import org.apache.pivot.collections.LinkedList;
@@ -96,6 +100,12 @@ public class TextPane extends Container
* @return The bounds of the character at the given offset.
*/
public Bounds getCharacterBounds(int offset);
+
+ /**
+ * Returns the current setting of the "tabWidth" style (so "setText"
+ * uses the same value as Ctrl-Tab from user).
+ */
+ public int getTabWidth();
}
private interface Edit {
@@ -199,6 +209,8 @@ public class TextPane extends Container
private int selectionStart = 0;
private int selectionLength = 0;
+ private boolean expandTabs = false;
+
private boolean editable = true;
private boolean undoingHistory = false;
private boolean bulkOperation = false;
@@ -643,6 +655,8 @@ public class TextPane extends Container
try {
PlainTextSerializer serializer = new PlainTextSerializer();
StringReader reader = new StringReader(text);
+ serializer.setExpandTabs(this.expandTabs);
+ serializer.setTabWidth(((TextPane.Skin) getSkin()).getTabWidth());
documentLocal = serializer.readObject(reader);
n = documentLocal.getCharacterCount();
@@ -716,14 +730,78 @@ public class TextPane extends Container
/**
* Convenience method to create a text-only document consisting of one
* paragraph per line of the given text.
+ *
+ * @param text
*/
public void setText(String text) {
+ if (text == null) {
+ throw new IllegalArgumentException();
+ }
+
+ try {
+ setText(new StringReader(text));
+ } catch (IOException exception) {
+ throw new RuntimeException(exception);
+ }
+ }
+
+ public void setText(URL textURL) throws IOException {
+ if (textURL == null) {
+ throw new IllegalArgumentException();
+ }
+
+ InputStream inputStream = null;
+ try {
+ inputStream = textURL.openStream();
+ setText(new InputStreamReader(inputStream));
+ } finally {
+ if (inputStream != null) {
+ inputStream.close();
+ }
+ }
+ }
+
+ public void setText(Reader textReader) throws IOException {
+ if (textReader == null) {
+ throw new IllegalArgumentException();
+ }
+
+ int tabPosition = 0;
+ int tabWidth = ((TextPane.Skin) getSkin()).getTabWidth();
+
Document doc = new Document();
- String[] lines = text.split("\r?\n");
- for (int i = 0; i < lines.length; i++) {
- Paragraph paragraph = new Paragraph(lines[i]);
+ StringBuilder text = new StringBuilder();
+
+ int c = textReader.read();
+ while (c != -1) {
+ if (c == '\n') {
+ Paragraph paragraph = new Paragraph(text.toString());
+ doc.add(paragraph);
+ text.setLength(0);
+ tabPosition = 0;
+ } else if (c == '\t') {
+ if (expandTabs) {
+ int spaces = tabWidth - (tabPosition % tabWidth);
+ for (int i = 0; i < spaces; i++) {
+ text.append(' ');
+ }
+ tabPosition += spaces;
+ } else {
+ text.append('\t');
+ }
+ } else {
+ text.append((char) c);
+ tabPosition++;
+ }
+
+ c = textReader.read();
+ }
+
+ if (text.length() != 0) {
+ Paragraph paragraph = new Paragraph(text.toString());
doc.add(paragraph);
}
+
setDocument(doc);
}
@@ -876,6 +954,27 @@ public class TextPane extends Container
}
}
+ public boolean getExpandTabs() {
+ return expandTabs;
+ }
+
+ /**
+ * Sets whether tab characters (<code>\t</code>) are expanded to an
+ * appropriate number of spaces during {@link #setText} and
+ * {@link #paste} operations. Note: doing this for keyboard input
+ * is handled in the skin.
+ *
+ * @param expandTabs <code>true</code> to replace tab characters with space
+ * characters (depending on the setting of the
+ * {@link TextPane.Skin#getTabWidth} value) or <code>false</code> to leave
+ * tabs alone. Note: this only affects tabs encountered during program
+ * operations; tabs entered via the keyboard by the user are always
+ * expanded, regardless of this setting.
+ */
+ public void setExpandTabs(boolean expandTabs) {
+ this.expandTabs = expandTabs;
+ }
+
public int getInsertionPoint(int x, int y) {
TextPane.Skin textPaneSkin = (TextPane.Skin) getSkin();
return textPaneSkin.getInsertionPoint(x, y);
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkin.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkin.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkin.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkin.java Tue Dec 17 20:17:48 2013
@@ -39,16 +39,9 @@ import org.apache.pivot.wtk.TextPane;
import org.apache.pivot.wtk.TextPaneListener;
import org.apache.pivot.wtk.TextPaneSelectionListener;
import org.apache.pivot.wtk.Theme;
-import org.apache.pivot.wtk.text.BulletedList;
-import org.apache.pivot.wtk.text.ComponentNode;
import org.apache.pivot.wtk.text.Document;
-import org.apache.pivot.wtk.text.ImageNode;
-import org.apache.pivot.wtk.text.List;
import org.apache.pivot.wtk.text.Node;
-import org.apache.pivot.wtk.text.NumberedList;
import org.apache.pivot.wtk.text.Paragraph;
-import org.apache.pivot.wtk.text.TextNode;
-import org.apache.pivot.wtk.text.TextSpan;
/**
* Text pane skin.
@@ -145,6 +138,8 @@ public class TextPaneSkin extends Contai
private Insets margin = new Insets(4);
private boolean wrapText = true;
+ private int tabWidth = 4;
+ private boolean acceptsTab = false;
private static final int SCROLL_RATE = 30;
@@ -171,7 +166,7 @@ public class TextPaneSkin extends Contai
Document document = textPane.getDocument();
if (document != null) {
- documentView = (TextPaneSkinDocumentView) createNodeView(document);
+ documentView = (TextPaneSkinDocumentView) TextPaneSkinNodeView.createNodeView(this, document);
documentView.attach();
updateSelection();
}
@@ -386,6 +381,45 @@ public class TextPaneSkin extends Contai
return characterBounds;
}
+ /**
+ * Gets current value of style that determines the behavior of <tt>TAB</tt>
+ * and <tt>Ctrl-TAB</tt> characters.
+ *
+ * @return <tt>true</tt> if <tt>TAB</tt> inserts an appropriate number of
+ * spaces, while <tt>Ctrl-TAB</tt> shifts focus to next component.
+ * <tt>false</tt> (default) means <tt>TAB</tt> shifts focus and
+ * <tt>Ctrl-TAB</tt> inserts spaces.
+ */
+ public boolean getAcceptsTab() {
+ return acceptsTab;
+ }
+
+ /**
+ * Sets current value of style that determines the behavior of <tt>TAB</tt>
+ * and <tt>Ctrl-TAB</tt> characters.
+ *
+ * @param acceptsTab <tt>true</tt> if <tt>TAB</tt> inserts an appropriate
+ * number of spaces, while <tt>Ctrl-TAB</tt> shifts focus to next component.
+ * <tt>false</tt> (default) means <tt>TAB</tt> shifts focus and
+ * <tt>Ctrl-TAB</tt> inserts spaces.
+ */
+ public void setAcceptsTab(boolean acceptsTab) {
+ this.acceptsTab = acceptsTab;
+ }
+
+ @Override
+ public int getTabWidth() {
+ return tabWidth;
+ }
+
+ public void setTabWidth(int tabWidth) {
+ if (tabWidth < 0) {
+ throw new IllegalArgumentException("tabWidth is negative.");
+ }
+
+ this.tabWidth = tabWidth;
+ }
+
private void scrollCharacterToVisible(int offset) {
TextPane textPane = (TextPane) getComponent();
Bounds characterBounds = getCharacterBounds(offset);
@@ -797,6 +831,35 @@ public class TextPaneSkin extends Contai
return consumed;
}
+ private int getRowOffset(Document document, int index) {
+ if (document != null) {
+ Node node = document.getDescendantAt(index);
+ while (node != null && !(node instanceof Paragraph)) {
+ node = node.getParent();
+ }
+ // TODO: doesn't take into account the line wrapping within a paragraph
+ if (node != null) {
+ return node.getDocumentOffset();
+ }
+ }
+ return 0;
+ }
+
+ private int getRowLength(Document document, int index) {
+ if (document != null) {
+ Node node = document.getDescendantAt(index);
+ while (node != null && !(node instanceof Paragraph)) {
+ node = node.getParent();
+ }
+ // TODO: doesn't take into account the line wrapping within a paragraph
+ // Assuming the node is a Paragraph, the count includes the trailing \n, so discount it
+ if (node != null) {
+ return node.getCharacterCount() - 1;
+ }
+ }
+ return 0;
+ }
+
@Override
public boolean keyPressed(final Component component, int keyCode,
Keyboard.KeyLocation keyLocation) {
@@ -805,25 +868,88 @@ public class TextPaneSkin extends Contai
final TextPane textPane = (TextPane) getComponent();
Document document = textPane.getDocument();
+ int selectionStart = textPane.getSelectionStart();
+ int selectionLength = textPane.getSelectionLength();
+
Keyboard.Modifier commandModifier = Platform.getCommandModifier();
+ boolean commandPressed = Keyboard.isPressed(commandModifier);
+ boolean wordNavPressed = Keyboard.isPressed(Platform.getWordNavigationModifier());
+ boolean shiftPressed = Keyboard.isPressed(Keyboard.Modifier.SHIFT);
+ boolean ctrlPressed = Keyboard.isPressed(Keyboard.Modifier.CTRL);
+ boolean metaPressed = Keyboard.isPressed(Keyboard.Modifier.META);
+ boolean isEditable = textPane.isEditable();
+
if (document != null) {
- if (keyCode == Keyboard.KeyCode.ENTER && textPane.isEditable()) {
+ if (keyCode == Keyboard.KeyCode.ENTER && isEditable) {
textPane.insertParagraph();
consumed = true;
- } else if (keyCode == Keyboard.KeyCode.DELETE && textPane.isEditable()) {
+ } else if (keyCode == Keyboard.KeyCode.DELETE && isEditable) {
textPane.delete(false);
consumed = true;
- } else if (keyCode == Keyboard.KeyCode.BACKSPACE && textPane.isEditable()) {
+ } else if (keyCode == Keyboard.KeyCode.BACKSPACE && isEditable) {
textPane.delete(true);
consumed = true;
- } else if (keyCode == Keyboard.KeyCode.LEFT) {
- int selectionStart = textPane.getSelectionStart();
- int selectionLength = textPane.getSelectionLength();
+ } else if (keyCode == Keyboard.KeyCode.HOME
+ || (keyCode == Keyboard.KeyCode.LEFT && metaPressed)) {
+ int start;
+ if (commandPressed) {
+ // Move the caret to the beginning of the text
+ start = 0;
+ } else {
+ // Move the caret to the beginning of the line
+ start = getRowOffset(document, selectionStart);
+ }
- if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
+ if (shiftPressed) {
+ selectionLength += selectionStart - start;
+ } else {
+ selectionLength = 0;
+ }
+
+ if (selectionStart >= 0) {
+ textPane.setSelection(start, selectionLength);
+ scrollCharacterToVisible(start);
+
+ consumed = true;
+ }
+ } else if (keyCode == Keyboard.KeyCode.END
+ || (keyCode == Keyboard.KeyCode.RIGHT && metaPressed)) {
+ int end;
+ int index = selectionStart + selectionLength;
+
+ if (commandPressed) {
+ // Move the caret to end of the text
+ end = textPane.getCharacterCount() - 1;
+ } else {
+ // Move the caret to the end of the line
+ int rowOffset = getRowOffset(document, index);
+ int rowLength = getRowLength(document, index);
+ end = rowOffset + rowLength;
+ }
+
+ if (shiftPressed) {
+ selectionLength += end - index;
+ } else {
+ selectionStart = end;
+ if (selectionStart < textPane.getCharacterCount()
+ && document.getCharacterAt(selectionStart) == '\n') {
+ selectionStart--;
+ }
+
+ selectionLength = 0;
+ }
+
+ if (selectionStart + selectionLength <= textPane.getCharacterCount()) {
+ textPane.setSelection(selectionStart, selectionLength);
+ scrollCharacterToVisible(selectionStart + selectionLength);
+
+ consumed = true;
+ }
+ } else if (keyCode == Keyboard.KeyCode.LEFT) {
+ if (shiftPressed) {
// Add the previous character to the selection
if (selectionStart > 0) {
selectionStart--;
@@ -862,10 +988,7 @@ public class TextPaneSkin extends Contai
consumed = true;
} else if (keyCode == Keyboard.KeyCode.RIGHT) {
- int selectionStart = textPane.getSelectionStart();
- int selectionLength = textPane.getSelectionLength();
-
- if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
+ if (shiftPressed) {
// Add the next character to the selection
if (selectionStart + selectionLength < document.getCharacterCount()) {
selectionLength++;
@@ -911,8 +1034,6 @@ public class TextPaneSkin extends Contai
consumed = true;
} else if (keyCode == Keyboard.KeyCode.UP) {
- int selectionStart = textPane.getSelectionStart();
-
int offset = getNextInsertionPoint(caretX, selectionStart,
TextPane.ScrollDirection.UP);
@@ -920,10 +1041,8 @@ public class TextPaneSkin extends Contai
offset = 0;
}
- int selectionLength;
- if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
- int selectionEnd = selectionStart + textPane.getSelectionLength() - 1;
- selectionLength = selectionEnd - offset + 1;
+ if (shiftPressed) {
+ selectionLength = selectionStart + selectionLength - offset;
} else {
selectionLength = 0;
}
@@ -933,10 +1052,8 @@ public class TextPaneSkin extends Contai
consumed = true;
} else if (keyCode == Keyboard.KeyCode.DOWN) {
- int selectionStart = textPane.getSelectionStart();
- int selectionLength = textPane.getSelectionLength();
- if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
+ if (shiftPressed) {
int from;
int x;
if (selectionLength == 0) {
@@ -993,27 +1110,42 @@ public class TextPaneSkin extends Contai
}
consumed = true;
- } else if (Keyboard.isPressed(commandModifier) && keyCode == Keyboard.KeyCode.TAB
- && textPane.isEditable()) {
- textPane.insert("\t");
+ } else if (keyCode == Keyboard.KeyCode.TAB
+ && (acceptsTab != Keyboard.isPressed(Keyboard.Modifier.CTRL))
+ && isEditable) {
+ if (textPane.getExpandTabs()) {
+ int linePos = selectionStart - getRowOffset(document, selectionStart);
+ StringBuilder tabBuilder = new StringBuilder(tabWidth);
+ for (int i = 0; i < tabWidth - (linePos % tabWidth); i++) {
+ tabBuilder.append(" ");
+ }
+ textPane.insert(tabBuilder.toString());
+ } else {
+ textPane.insert("\t");
+ }
showCaret(true);
consumed = true;
- } else if (Keyboard.isPressed(commandModifier)) {
+ } else if (keyCode == Keyboard.KeyCode.INSERT) {
+ if (shiftPressed && isEditable) {
+ textPane.paste();
+ consumed = true;
+ }
+ } else if (commandPressed) {
if (keyCode == Keyboard.KeyCode.A) {
textPane.setSelection(0, document.getCharacterCount());
consumed = true;
- } else if (keyCode == Keyboard.KeyCode.X && textPane.isEditable()) {
+ } else if (keyCode == Keyboard.KeyCode.X && isEditable) {
textPane.cut();
consumed = true;
} else if (keyCode == Keyboard.KeyCode.C) {
textPane.copy();
consumed = true;
- } else if (keyCode == Keyboard.KeyCode.V && textPane.isEditable()) {
+ } else if (keyCode == Keyboard.KeyCode.V && isEditable) {
textPane.paste();
consumed = true;
- } else if (keyCode == Keyboard.KeyCode.Z && textPane.isEditable()) {
- if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
+ } else if (keyCode == Keyboard.KeyCode.Z && isEditable) {
+ if (shiftPressed) {
textPane.redo();
} else {
textPane.undo();
@@ -1021,33 +1153,6 @@ public class TextPaneSkin extends Contai
consumed = true;
}
- } else if (keyCode == Keyboard.KeyCode.HOME) {
- // Move the caret to the beginning of the text
- if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
- textPane.setSelection(0, textPane.getSelectionStart());
- } else {
- textPane.setSelection(0, 0);
- }
- scrollCharacterToVisible(0);
-
- consumed = true;
- } else if (keyCode == Keyboard.KeyCode.END) {
- // Move the caret to the end of the text
- if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
- int selectionStart = textPane.getSelectionStart();
- textPane.setSelection(selectionStart, textPane.getCharacterCount()
- - selectionStart);
- } else {
- textPane.setSelection(textPane.getCharacterCount() - 1, 0);
- }
- scrollCharacterToVisible(textPane.getCharacterCount() - 1);
-
- consumed = true;
- } else if (keyCode == Keyboard.KeyCode.INSERT) {
- if (Keyboard.isPressed(Keyboard.Modifier.SHIFT) && textPane.isEditable()) {
- textPane.paste();
- consumed = true;
- }
} else {
consumed = super.keyPressed(component, keyCode, keyLocation);
}
@@ -1089,7 +1194,7 @@ public class TextPaneSkin extends Contai
Document document = textPane.getDocument();
if (document != null) {
- documentView = (TextPaneSkinDocumentView) createNodeView(document);
+ documentView = (TextPaneSkinDocumentView) TextPaneSkinNodeView.createNodeView(this, document);
documentView.attach();
}
@@ -1132,35 +1237,6 @@ public class TextPaneSkin extends Contai
}
}
- TextPaneSkinNodeView createNodeView(Node node) {
- TextPaneSkinNodeView nodeView = null;
-
- if (node instanceof Document) {
- nodeView = new TextPaneSkinDocumentView(this, (Document) node);
- } else if (node instanceof Paragraph) {
- nodeView = new TextPaneSkinParagraphView((Paragraph) node);
- } else if (node instanceof TextNode) {
- nodeView = new TextPaneSkinTextNodeView((TextNode) node);
- } else if (node instanceof ImageNode) {
- nodeView = new TextPaneSkinImageNodeView((ImageNode) node);
- } else if (node instanceof ComponentNode) {
- nodeView = new TextPaneSkinComponentNodeView((ComponentNode) node);
- } else if (node instanceof TextSpan) {
- nodeView = new TextPaneSkinSpanView((TextSpan) node);
- } else if (node instanceof NumberedList) {
- nodeView = new TextPaneSkinNumberedListView((NumberedList) node);
- } else if (node instanceof BulletedList) {
- nodeView = new TextPaneSkinBulletedListView((BulletedList) node);
- } else if (node instanceof List.Item) {
- nodeView = new TextPaneSkinListItemView((List.Item) node);
- } else {
- throw new IllegalArgumentException("Unsupported node type: "
- + node.getClass().getName());
- }
-
- return nodeView;
- }
-
private void updateSelection() {
if (documentView.getCharacterCount() > 0) {
TextPane textPane = (TextPane) getComponent();
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBlockView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBlockView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBlockView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBlockView.java Tue Dec 17 20:17:48 2013
@@ -22,8 +22,8 @@ import org.apache.pivot.wtk.text.BlockLi
abstract class TextPaneSkinBlockView extends TextPaneSkinElementView implements BlockListener {
- public TextPaneSkinBlockView(Block block) {
- super(block);
+ public TextPaneSkinBlockView(TextPaneSkin textPaneSkin, Block block) {
+ super(textPaneSkin, block);
}
@Override
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBulletedListView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBulletedListView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBulletedListView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBulletedListView.java Tue Dec 17 20:17:48 2013
@@ -21,8 +21,8 @@ import org.apache.pivot.wtk.text.Bullete
class TextPaneSkinBulletedListView extends TextPaneSkinListView implements BulletedListListener {
- public TextPaneSkinBulletedListView(BulletedList bulletedList) {
- super(bulletedList);
+ public TextPaneSkinBulletedListView(TextPaneSkin textPaneSkin, BulletedList bulletedList) {
+ super(textPaneSkin, bulletedList);
}
@Override
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinComponentNodeView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinComponentNodeView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinComponentNodeView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinComponentNodeView.java Tue Dec 17 20:17:48 2013
@@ -35,8 +35,8 @@ class TextPaneSkinComponentNodeView exte
}
};
- public TextPaneSkinComponentNodeView(ComponentNode componentNode) {
- super(componentNode);
+ public TextPaneSkinComponentNodeView(TextPaneSkin textPaneSkin, ComponentNode componentNode) {
+ super(textPaneSkin, componentNode);
}
@Override
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinDocumentView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinDocumentView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinDocumentView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinDocumentView.java Tue Dec 17 20:17:48 2013
@@ -23,11 +23,8 @@ import org.apache.pivot.wtk.text.Documen
*/
class TextPaneSkinDocumentView extends TextPaneSkinVerticalElementView {
- protected final TextPaneSkin textPaneSkin;
-
public TextPaneSkinDocumentView(TextPaneSkin textPaneSkin, Document document) {
- super(document);
- this.textPaneSkin = textPaneSkin;
+ super(textPaneSkin, document);
}
@Override
@@ -43,8 +40,4 @@ class TextPaneSkinDocumentView extends T
textPaneSkin.invalidateComponent();
}
- @Override
- public TextPaneSkin getTextPaneSkin() {
- return textPaneSkin;
- }
}
\ No newline at end of file
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinElementView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinElementView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinElementView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinElementView.java Tue Dec 17 20:17:48 2013
@@ -41,8 +41,8 @@ abstract class TextPaneSkinElementView e
private int skinX = 0;
private int skinY = 0;
- public TextPaneSkinElementView(Element element) {
- super(element);
+ public TextPaneSkinElementView(TextPaneSkin textPaneSkin, Element element) {
+ super(textPaneSkin, element);
}
@Override
@@ -54,7 +54,7 @@ abstract class TextPaneSkinElementView e
// Attach child node views
for (Node node : element) {
- add(getTextPaneSkin().createNodeView(node));
+ add(createNodeView(getTextPaneSkin(), node));
}
}
@@ -235,7 +235,7 @@ abstract class TextPaneSkinElementView e
int nodeViewOffset = nodeView.getOffset();
int characterCount = nodeView.getCharacterCount();
- if (offset >= nodeViewOffset && offset < nodeViewOffset + characterCount) {
+ if (offset >= nodeViewOffset && offset <= nodeViewOffset + characterCount) {
characterBounds = nodeView.getCharacterBounds(offset - nodeViewOffset);
if (characterBounds != null) {
@@ -255,7 +255,7 @@ abstract class TextPaneSkinElementView e
@Override
public void nodeInserted(Element element, int index) {
- insert(getTextPaneSkin().createNodeView(element.get(index)), index);
+ insert(createNodeView(getTextPaneSkin(), element.get(index)), index);
invalidateUpTree();
}
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinImageNodeView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinImageNodeView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinImageNodeView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinImageNodeView.java Tue Dec 17 20:17:48 2013
@@ -28,8 +28,8 @@ import org.apache.pivot.wtk.text.ImageNo
class TextPaneSkinImageNodeView extends TextPaneSkinNodeView implements ImageNodeListener,
ImageListener {
- public TextPaneSkinImageNodeView(ImageNode imageNode) {
- super(imageNode);
+ public TextPaneSkinImageNodeView(TextPaneSkin textPaneSkin, ImageNode imageNode) {
+ super(textPaneSkin, imageNode);
}
@Override
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListItemView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListItemView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListItemView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListItemView.java Tue Dec 17 20:17:48 2013
@@ -18,6 +18,7 @@ package org.apache.pivot.wtk.skin;
import java.util.Iterator;
+import org.apache.pivot.wtk.text.List;
import org.apache.pivot.wtk.text.TextNode;
class TextPaneSkinListItemView extends TextPaneSkinVerticalElementView {
@@ -25,8 +26,8 @@ class TextPaneSkinListItemView extends T
private TextNode indexTextNode;
private TextPaneSkinTextNodeView indexTextNodeView;
- public TextPaneSkinListItemView(org.apache.pivot.wtk.text.List.Item listItem) {
- super(listItem);
+ public TextPaneSkinListItemView(TextPaneSkin textPaneSkin, List.Item listItem) {
+ super(textPaneSkin, listItem);
this.indexTextNode = new TextNode("");
}
@@ -36,7 +37,7 @@ class TextPaneSkinListItemView extends T
super.attach();
// add an extra TextNodeView to render the index text
- indexTextNodeView = new TextPaneSkinTextNodeView(indexTextNode);
+ indexTextNodeView = new TextPaneSkinTextNodeView(getTextPaneSkin(), indexTextNode);
indexTextNodeView.setLocation(0, 0);
insert(indexTextNodeView, 0);
}
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListView.java Tue Dec 17 20:17:48 2013
@@ -22,8 +22,8 @@ class TextPaneSkinListView extends TextP
protected int maxIndexTextWidth;
- public TextPaneSkinListView(List list) {
- super(list);
+ public TextPaneSkinListView(TextPaneSkin textPaneSkin, List list) {
+ super(textPaneSkin, list);
}
public int getMaxIndexTextWidth() {
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNodeView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNodeView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNodeView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNodeView.java Tue Dec 17 20:17:48 2013
@@ -18,19 +18,33 @@ package org.apache.pivot.wtk.skin;
import java.awt.Graphics2D;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+import org.apache.pivot.collections.HashMap;
import org.apache.pivot.collections.Sequence;
import org.apache.pivot.wtk.Bounds;
import org.apache.pivot.wtk.Dimensions;
import org.apache.pivot.wtk.Point;
import org.apache.pivot.wtk.TextPane;
+import org.apache.pivot.wtk.text.BulletedList;
+import org.apache.pivot.wtk.text.ComponentNode;
+import org.apache.pivot.wtk.text.Document;
import org.apache.pivot.wtk.text.Element;
+import org.apache.pivot.wtk.text.ImageNode;
+import org.apache.pivot.wtk.text.List;
import org.apache.pivot.wtk.text.Node;
import org.apache.pivot.wtk.text.NodeListener;
+import org.apache.pivot.wtk.text.NumberedList;
+import org.apache.pivot.wtk.text.Paragraph;
+import org.apache.pivot.wtk.text.TextNode;
+import org.apache.pivot.wtk.text.TextSpan;
/**
* Abstract base class for node views.
*/
abstract class TextPaneSkinNodeView implements NodeListener {
+ protected final TextPaneSkin textPaneSkin;
private Node node = null;
private TextPaneSkinElementView parent = null;
@@ -42,7 +56,8 @@ abstract class TextPaneSkinNodeView impl
private boolean valid = false;
- public TextPaneSkinNodeView(Node node) {
+ public TextPaneSkinNodeView(TextPaneSkin textPaneSkin, Node node) {
+ this.textPaneSkin = textPaneSkin;
this.node = node;
}
@@ -59,7 +74,7 @@ abstract class TextPaneSkinNodeView impl
}
protected TextPaneSkin getTextPaneSkin() {
- return getParent().getTextPaneSkin();
+ return textPaneSkin;
}
protected void attach() {
@@ -232,4 +247,41 @@ abstract class TextPaneSkinNodeView impl
// No-op
}
+ private static HashMap<Class<? extends Node>, Class<? extends TextPaneSkinNodeView>>
+ nodeViewSkinMap = new HashMap<>();
+ static {
+ nodeViewSkinMap.put(Document.class, TextPaneSkinDocumentView.class);
+ nodeViewSkinMap.put(Paragraph.class, TextPaneSkinParagraphView.class);
+ nodeViewSkinMap.put(TextNode.class, TextPaneSkinTextNodeView.class);
+ nodeViewSkinMap.put(ImageNode.class, TextPaneSkinImageNodeView.class);
+ nodeViewSkinMap.put(ComponentNode.class, TextPaneSkinComponentNodeView.class);
+ nodeViewSkinMap.put(TextSpan.class, TextPaneSkinSpanView.class);
+ nodeViewSkinMap.put(NumberedList.class, TextPaneSkinNumberedListView.class);
+ nodeViewSkinMap.put(BulletedList.class, TextPaneSkinBulletedListView.class);
+ nodeViewSkinMap.put(List.Item.class, TextPaneSkinListItemView.class);
+ }
+
+ public static TextPaneSkinNodeView createNodeView(TextPaneSkin textPaneSkin, Node node) {
+ TextPaneSkinNodeView nodeView = null;
+
+ Class<? extends Node> nodeClass = node.getClass();
+ Class<? extends TextPaneSkinNodeView> skinClass = nodeViewSkinMap.get(nodeClass);
+ if (skinClass != null) {
+ try {
+ Constructor<?> constructor = skinClass.getConstructor(TextPaneSkin.class, nodeClass);
+ nodeView = (TextPaneSkinNodeView)constructor.newInstance(textPaneSkin, node);
+ } catch (NoSuchMethodException | InstantiationException
+ | IllegalAccessException | InvocationTargetException ex) {
+ throw new RuntimeException("Error instantiating node view for "
+ + nodeClass.getName(), ex);
+ }
+ }
+ if (nodeView == null) {
+ throw new IllegalArgumentException("Unsupported node type: "
+ + nodeClass.getName());
+ }
+
+ return nodeView;
+ }
+
}
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNumberedListView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNumberedListView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNumberedListView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNumberedListView.java Tue Dec 17 20:17:48 2013
@@ -59,8 +59,8 @@ class TextPaneSkinNumberedListView exten
return (char) ('A' + n - 1) + "";
}
- public TextPaneSkinNumberedListView(NumberedList numberedList) {
- super(numberedList);
+ public TextPaneSkinNumberedListView(TextPaneSkin textPaneSkin, NumberedList numberedList) {
+ super(textPaneSkin, numberedList);
}
@Override
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinParagraphView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinParagraphView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinParagraphView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinParagraphView.java Tue Dec 17 20:17:48 2013
@@ -54,8 +54,8 @@ class TextPaneSkinParagraphView extends
private ArrayList<Row> rows = null;
private Bounds terminatorBounds = new Bounds(0, 0, 0, 0);
- public TextPaneSkinParagraphView(Paragraph paragraph) {
- super(paragraph);
+ public TextPaneSkinParagraphView(TextPaneSkin textPaneSkin, Paragraph paragraph) {
+ super(textPaneSkin, paragraph);
}
@Override
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinSpanView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinSpanView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinSpanView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinSpanView.java Tue Dec 17 20:17:48 2013
@@ -25,8 +25,8 @@ import org.apache.pivot.wtk.text.TextSpa
*/
class TextPaneSkinSpanView extends TextPaneSkinElementView {
- public TextPaneSkinSpanView(TextSpan span) {
- super(span);
+ public TextPaneSkinSpanView(TextPaneSkin textPaneSkin, TextSpan span) {
+ super(textPaneSkin, span);
}
@Override
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinTextNodeView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinTextNodeView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinTextNodeView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinTextNodeView.java Tue Dec 17 20:17:48 2013
@@ -47,12 +47,12 @@ class TextPaneSkinTextNodeView extends T
private GlyphVector glyphVector = null;
private TextPaneSkinTextNodeView next = null;
- public TextPaneSkinTextNodeView(TextNode textNode) {
- this(textNode, 0);
+ public TextPaneSkinTextNodeView(TextPaneSkin textPaneSkin, TextNode textNode) {
+ this(textPaneSkin, textNode, 0);
}
- public TextPaneSkinTextNodeView(TextNode textNode, int start) {
- super(textNode);
+ public TextPaneSkinTextNodeView(TextPaneSkin textPaneSkin, TextNode textNode, int start) {
+ super(textPaneSkin, textNode);
this.start = start;
}
@@ -134,7 +134,7 @@ class TextPaneSkinTextNodeView extends T
if (end < ci.getEndIndex()) {
length = end - start;
- next = new TextPaneSkinTextNodeView(textNode, end);
+ next = new TextPaneSkinTextNodeView(getTextPaneSkin(), textNode, end);
next.setParent(getParent());
} else {
length = ci.getEndIndex() - start;
@@ -473,11 +473,18 @@ class TextPaneSkinTextNodeView extends T
@Override
public Bounds getCharacterBounds(int offset) {
- Shape glyphBounds = glyphVector.getGlyphLogicalBounds(offset);
+ // If the offest == length, then use the right hand edge of the previous
+ // offset instead -- this is for positioning the caret at the end of the text
+ Shape glyphBounds = glyphVector.getGlyphLogicalBounds(offset == length ? offset - 1 : offset);
Rectangle2D glyphBounds2D = glyphBounds.getBounds2D();
- return new Bounds((int) Math.floor(glyphBounds2D.getX()), 0,
- (int) Math.ceil(glyphBounds2D.getWidth()), getHeight());
+ if (offset == length) {
+ return new Bounds((int) Math.ceil(glyphBounds2D.getX() + glyphBounds2D.getWidth()), 0,
+ 1, getHeight());
+ } else {
+ return new Bounds((int) Math.floor(glyphBounds2D.getX()), 0,
+ (int) Math.ceil(glyphBounds2D.getWidth()), getHeight());
+ }
}
@Override
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinVerticalElementView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinVerticalElementView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinVerticalElementView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinVerticalElementView.java Tue Dec 17 20:17:48 2013
@@ -27,8 +27,8 @@ import org.apache.pivot.wtk.text.Element
*/
abstract class TextPaneSkinVerticalElementView extends TextPaneSkinElementView {
- public TextPaneSkinVerticalElementView(Element element) {
- super(element);
+ public TextPaneSkinVerticalElementView(TextPaneSkin textPaneSkin, Element element) {
+ super(textPaneSkin, element);
}
@Override
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/text/PlainTextSerializer.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/text/PlainTextSerializer.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/text/PlainTextSerializer.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/text/PlainTextSerializer.java Tue Dec 17 20:17:48 2013
@@ -39,6 +39,9 @@ public class PlainTextSerializer impleme
public static final String MIME_TYPE = "text/plain";
public static final int BUFFER_SIZE = 2048;
+ private boolean expandTabs = false;
+ private int tabWidth = 4;
+
public PlainTextSerializer() {
this(Charset.defaultCharset());
}
@@ -55,6 +58,35 @@ public class PlainTextSerializer impleme
this.charset = charset;
}
+ public int getTabWidth() {
+ return tabWidth;
+ }
+
+ public void setTabWidth(int tabWidth) {
+ if (tabWidth < 0) {
+ throw new IllegalArgumentException("tabWidth is negative.");
+ }
+
+ this.tabWidth = tabWidth;
+ }
+
+ public boolean getExpandTabs() {
+ return expandTabs;
+ }
+
+ /**
+ * Sets whether tab characters (<code>\t</code>) are expanded to an
+ * appropriate number of spaces while reading the text.
+ *
+ * @param expandTabs <code>true</code> to replace tab characters with space
+ * characters (depending on the setting of the {@link #getTabWidth} value)
+ * or <code>false</code> to leave tabs alone.
+ */
+ public void setExpandTabs(boolean expandTabs) {
+ this.expandTabs = expandTabs;
+ }
+
+
@Override
public Document readObject(InputStream inputStream) throws IOException {
Reader reader = new InputStreamReader(inputStream, charset);
@@ -70,6 +102,18 @@ public class PlainTextSerializer impleme
String line = bufferedReader.readLine();
while (line != null) {
+ if (expandTabs) {
+ int ix = 0;
+ StringBuilder buf = new StringBuilder(line);
+ while ((ix = buf.indexOf("\t", ix)) >= 0) {
+ buf.deleteCharAt(ix);
+ int spaces = tabWidth - (ix % tabWidth);
+ for (int j = 0; j < spaces; j++) {
+ buf.insert(ix++, ' ');
+ }
+ }
+ line = buf.toString();
+ }
document.add(new Paragraph(line));
line = bufferedReader.readLine();
}
RE: svn commit: r1551679 - in /pivot/trunk/wtk/src/org/apache/pivot/wtk:
./ skin/ text/
Posted by Sandro Martini <sa...@gmail.com>.
Hi Roger,
no problem for me, proceed as you prefer ...
Bye
Il 17/dic/2013 21:33 "Roger L. Whitcomb" <Ro...@actian.com> ha
scritto:
> As I was reviewing this commit, I realized there are additional changes in
> here not related strictly to tab handling (namely other keyboard handling
> changes), that are, in fact, not complete, and likely not correct as-is.
>
> Given that this is "trunk" and not released yet, and that I am committed
> to getting this right sooner rather than later, I'm inclined to leave the
> code as-is and fix it up from here. BUT, if anyone doesn't think that's a
> good idea, I can revert those parts that are not good (esp. in
> TextPaneSkin.java) and make another commit later once the keyboard stuff is
> working better. Everything builds, so that shouldn't be a problem.
>
> Comments?
>
> Thanks,
> ~Roger
>
> -----Original Message-----
> From: rwhitcomb@apache.org [mailto:rwhitcomb@apache.org]
> Sent: Tuesday, December 17, 2013 12:18 PM
> To: commits@pivot.apache.org
> Subject: svn commit: r1551679 - in
> /pivot/trunk/wtk/src/org/apache/pivot/wtk: ./ skin/ text/
>
> Author: rwhitcomb
> Date: Tue Dec 17 20:17:48 2013
> New Revision: 1551679
>
> URL: http://svn.apache.org/r1551679
> Log:
> PIVOT-696: Support expansion of tabs in TextPane in the same ways as with
> TextArea.
>
> Add new style: tabWidth and property expandTabs.
>
> This also required changing the way we get a hold of the TextPane.Skin
> from within the skin view classes. Also PlainTextSerializer needs the tab
> settings.
>
> Modified:
> pivot/trunk/wtk/src/org/apache/pivot/wtk/TextPane.java
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkin.java
>
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBlockView.java
>
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBulletedListView.java
>
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinComponentNodeView.java
>
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinDocumentView.java
>
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinElementView.java
>
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinImageNodeView.java
>
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListItemView.java
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListView.java
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNodeView.java
>
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNumberedListView.java
>
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinParagraphView.java
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinSpanView.java
>
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinTextNodeView.java
>
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinVerticalElementView.java
> pivot/trunk/wtk/src/org/apache/pivot/wtk/text/PlainTextSerializer.java
>
> Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/TextPane.java
> URL:
> http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/TextPane.java?rev=1551679&r1=1551678&r2=1551679&view=diff
>
> ==============================================================================
> --- pivot/trunk/wtk/src/org/apache/pivot/wtk/TextPane.java (original)
> +++ pivot/trunk/wtk/src/org/apache/pivot/wtk/TextPane.java Tue Dec 17
> +++ 20:17:48 2013
> @@ -17,8 +17,12 @@
> package org.apache.pivot.wtk;
>
> import java.io.IOException;
> +import java.io.InputStream;
> +import java.io.InputStreamReader;
> +import java.io.Reader;
> import java.io.StringReader;
> import java.io.StringWriter;
> +import java.net.URL;
>
> import org.apache.pivot.beans.DefaultProperty;
> import org.apache.pivot.collections.LinkedList;
> @@ -96,6 +100,12 @@ public class TextPane extends Container
> * @return The bounds of the character at the given offset.
> */
> public Bounds getCharacterBounds(int offset);
> +
> + /**
> + * Returns the current setting of the "tabWidth" style (so
> "setText"
> + * uses the same value as Ctrl-Tab from user).
> + */
> + public int getTabWidth();
> }
>
> private interface Edit {
> @@ -199,6 +209,8 @@ public class TextPane extends Container
> private int selectionStart = 0;
> private int selectionLength = 0;
>
> + private boolean expandTabs = false;
> +
> private boolean editable = true;
> private boolean undoingHistory = false;
> private boolean bulkOperation = false; @@ -643,6 +655,8 @@ public
> class TextPane extends Container
> try {
> PlainTextSerializer serializer = new
> PlainTextSerializer();
> StringReader reader = new StringReader(text);
> + serializer.setExpandTabs(this.expandTabs);
> + serializer.setTabWidth(((TextPane.Skin)
> + getSkin()).getTabWidth());
> documentLocal = serializer.readObject(reader);
> n = documentLocal.getCharacterCount();
>
> @@ -716,14 +730,78 @@ public class TextPane extends Container
> /**
> * Convenience method to create a text-only document consisting of one
> * paragraph per line of the given text.
> + *
> + * @param text
> */
> public void setText(String text) {
> + if (text == null) {
> + throw new IllegalArgumentException();
> + }
> +
> + try {
> + setText(new StringReader(text));
> + } catch (IOException exception) {
> + throw new RuntimeException(exception);
> + }
> + }
> +
> + public void setText(URL textURL) throws IOException {
> + if (textURL == null) {
> + throw new IllegalArgumentException();
> + }
> +
> + InputStream inputStream = null;
> + try {
> + inputStream = textURL.openStream();
> + setText(new InputStreamReader(inputStream));
> + } finally {
> + if (inputStream != null) {
> + inputStream.close();
> + }
> + }
> + }
> +
> + public void setText(Reader textReader) throws IOException {
> + if (textReader == null) {
> + throw new IllegalArgumentException();
> + }
> +
> + int tabPosition = 0;
> + int tabWidth = ((TextPane.Skin) getSkin()).getTabWidth();
> +
> Document doc = new Document();
> - String[] lines = text.split("\r?\n");
> - for (int i = 0; i < lines.length; i++) {
> - Paragraph paragraph = new Paragraph(lines[i]);
> + StringBuilder text = new StringBuilder();
> +
> + int c = textReader.read();
> + while (c != -1) {
> + if (c == '\n') {
> + Paragraph paragraph = new Paragraph(text.toString());
> + doc.add(paragraph);
> + text.setLength(0);
> + tabPosition = 0;
> + } else if (c == '\t') {
> + if (expandTabs) {
> + int spaces = tabWidth - (tabPosition % tabWidth);
> + for (int i = 0; i < spaces; i++) {
> + text.append(' ');
> + }
> + tabPosition += spaces;
> + } else {
> + text.append('\t');
> + }
> + } else {
> + text.append((char) c);
> + tabPosition++;
> + }
> +
> + c = textReader.read();
> + }
> +
> + if (text.length() != 0) {
> + Paragraph paragraph = new Paragraph(text.toString());
> doc.add(paragraph);
> }
> +
> setDocument(doc);
> }
>
> @@ -876,6 +954,27 @@ public class TextPane extends Container
> }
> }
>
> + public boolean getExpandTabs() {
> + return expandTabs;
> + }
> +
> + /**
> + * Sets whether tab characters (<code>\t</code>) are expanded to an
> + * appropriate number of spaces during {@link #setText} and
> + * {@link #paste} operations. Note: doing this for keyboard input
> + * is handled in the skin.
> + *
> + * @param expandTabs <code>true</code> to replace tab characters with
> space
> + * characters (depending on the setting of the
> + * {@link TextPane.Skin#getTabWidth} value) or <code>false</code> to
> leave
> + * tabs alone. Note: this only affects tabs encountered during program
> + * operations; tabs entered via the keyboard by the user are always
> + * expanded, regardless of this setting.
> + */
> + public void setExpandTabs(boolean expandTabs) {
> + this.expandTabs = expandTabs;
> + }
> +
> public int getInsertionPoint(int x, int y) {
> TextPane.Skin textPaneSkin = (TextPane.Skin) getSkin();
> return textPaneSkin.getInsertionPoint(x, y);
>
> Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkin.java
> URL:
> http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkin.java?rev=1551679&r1=1551678&r2=1551679&view=diff
>
> ==============================================================================
> --- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkin.java
> (original)
> +++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkin.java Tue
> +++ Dec 17 20:17:48 2013
> @@ -39,16 +39,9 @@ import org.apache.pivot.wtk.TextPane; import
> org.apache.pivot.wtk.TextPaneListener;
> import org.apache.pivot.wtk.TextPaneSelectionListener;
> import org.apache.pivot.wtk.Theme;
> -import org.apache.pivot.wtk.text.BulletedList;
> -import org.apache.pivot.wtk.text.ComponentNode;
> import org.apache.pivot.wtk.text.Document;
> -import org.apache.pivot.wtk.text.ImageNode;
> -import org.apache.pivot.wtk.text.List;
> import org.apache.pivot.wtk.text.Node;
> -import org.apache.pivot.wtk.text.NumberedList;
> import org.apache.pivot.wtk.text.Paragraph;
> -import org.apache.pivot.wtk.text.TextNode;
> -import org.apache.pivot.wtk.text.TextSpan;
>
> /**
> * Text pane skin.
> @@ -145,6 +138,8 @@ public class TextPaneSkin extends Contai
> private Insets margin = new Insets(4);
>
> private boolean wrapText = true;
> + private int tabWidth = 4;
> + private boolean acceptsTab = false;
>
> private static final int SCROLL_RATE = 30;
>
> @@ -171,7 +166,7 @@ public class TextPaneSkin extends Contai
>
> Document document = textPane.getDocument();
> if (document != null) {
> - documentView = (TextPaneSkinDocumentView)
> createNodeView(document);
> + documentView = (TextPaneSkinDocumentView)
> + TextPaneSkinNodeView.createNodeView(this, document);
> documentView.attach();
> updateSelection();
> }
> @@ -386,6 +381,45 @@ public class TextPaneSkin extends Contai
> return characterBounds;
> }
>
> + /**
> + * Gets current value of style that determines the behavior of
> <tt>TAB</tt>
> + * and <tt>Ctrl-TAB</tt> characters.
> + *
> + * @return <tt>true</tt> if <tt>TAB</tt> inserts an appropriate
> number of
> + * spaces, while <tt>Ctrl-TAB</tt> shifts focus to next component.
> + * <tt>false</tt> (default) means <tt>TAB</tt> shifts focus and
> + * <tt>Ctrl-TAB</tt> inserts spaces.
> + */
> + public boolean getAcceptsTab() {
> + return acceptsTab;
> + }
> +
> + /**
> + * Sets current value of style that determines the behavior of
> <tt>TAB</tt>
> + * and <tt>Ctrl-TAB</tt> characters.
> + *
> + * @param acceptsTab <tt>true</tt> if <tt>TAB</tt> inserts an
> appropriate
> + * number of spaces, while <tt>Ctrl-TAB</tt> shifts focus to next
> component.
> + * <tt>false</tt> (default) means <tt>TAB</tt> shifts focus and
> + * <tt>Ctrl-TAB</tt> inserts spaces.
> + */
> + public void setAcceptsTab(boolean acceptsTab) {
> + this.acceptsTab = acceptsTab;
> + }
> +
> + @Override
> + public int getTabWidth() {
> + return tabWidth;
> + }
> +
> + public void setTabWidth(int tabWidth) {
> + if (tabWidth < 0) {
> + throw new IllegalArgumentException("tabWidth is negative.");
> + }
> +
> + this.tabWidth = tabWidth;
> + }
> +
> private void scrollCharacterToVisible(int offset) {
> TextPane textPane = (TextPane) getComponent();
> Bounds characterBounds = getCharacterBounds(offset); @@ -797,6
> +831,35 @@ public class TextPaneSkin extends Contai
> return consumed;
> }
>
> + private int getRowOffset(Document document, int index) {
> + if (document != null) {
> + Node node = document.getDescendantAt(index);
> + while (node != null && !(node instanceof Paragraph)) {
> + node = node.getParent();
> + }
> + // TODO: doesn't take into account the line wrapping within a
> paragraph
> + if (node != null) {
> + return node.getDocumentOffset();
> + }
> + }
> + return 0;
> + }
> +
> + private int getRowLength(Document document, int index) {
> + if (document != null) {
> + Node node = document.getDescendantAt(index);
> + while (node != null && !(node instanceof Paragraph)) {
> + node = node.getParent();
> + }
> + // TODO: doesn't take into account the line wrapping within a
> paragraph
> + // Assuming the node is a Paragraph, the count includes the
> trailing \n, so discount it
> + if (node != null) {
> + return node.getCharacterCount() - 1;
> + }
> + }
> + return 0;
> + }
> +
> @Override
> public boolean keyPressed(final Component component, int keyCode,
> Keyboard.KeyLocation keyLocation) { @@ -805,25 +868,88 @@ public
> class TextPaneSkin extends Contai
> final TextPane textPane = (TextPane) getComponent();
> Document document = textPane.getDocument();
>
> + int selectionStart = textPane.getSelectionStart();
> + int selectionLength = textPane.getSelectionLength();
> +
> Keyboard.Modifier commandModifier = Platform.getCommandModifier();
> + boolean commandPressed = Keyboard.isPressed(commandModifier);
> + boolean wordNavPressed =
> Keyboard.isPressed(Platform.getWordNavigationModifier());
> + boolean shiftPressed =
> Keyboard.isPressed(Keyboard.Modifier.SHIFT);
> + boolean ctrlPressed = Keyboard.isPressed(Keyboard.Modifier.CTRL);
> + boolean metaPressed = Keyboard.isPressed(Keyboard.Modifier.META);
> + boolean isEditable = textPane.isEditable();
> +
> if (document != null) {
> - if (keyCode == Keyboard.KeyCode.ENTER &&
> textPane.isEditable()) {
> + if (keyCode == Keyboard.KeyCode.ENTER && isEditable) {
> textPane.insertParagraph();
>
> consumed = true;
> - } else if (keyCode == Keyboard.KeyCode.DELETE &&
> textPane.isEditable()) {
> + } else if (keyCode == Keyboard.KeyCode.DELETE &&
> + isEditable) {
> textPane.delete(false);
>
> consumed = true;
> - } else if (keyCode == Keyboard.KeyCode.BACKSPACE &&
> textPane.isEditable()) {
> + } else if (keyCode == Keyboard.KeyCode.BACKSPACE &&
> + isEditable) {
> textPane.delete(true);
>
> consumed = true;
> - } else if (keyCode == Keyboard.KeyCode.LEFT) {
> - int selectionStart = textPane.getSelectionStart();
> - int selectionLength = textPane.getSelectionLength();
> + } else if (keyCode == Keyboard.KeyCode.HOME
> + || (keyCode == Keyboard.KeyCode.LEFT && metaPressed)) {
> + int start;
> + if (commandPressed) {
> + // Move the caret to the beginning of the text
> + start = 0;
> + } else {
> + // Move the caret to the beginning of the line
> + start = getRowOffset(document, selectionStart);
> + }
>
> - if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
> + if (shiftPressed) {
> + selectionLength += selectionStart - start;
> + } else {
> + selectionLength = 0;
> + }
> +
> + if (selectionStart >= 0) {
> + textPane.setSelection(start, selectionLength);
> + scrollCharacterToVisible(start);
> +
> + consumed = true;
> + }
> + } else if (keyCode == Keyboard.KeyCode.END
> + || (keyCode == Keyboard.KeyCode.RIGHT && metaPressed))
> {
> + int end;
> + int index = selectionStart + selectionLength;
> +
> + if (commandPressed) {
> + // Move the caret to end of the text
> + end = textPane.getCharacterCount() - 1;
> + } else {
> + // Move the caret to the end of the line
> + int rowOffset = getRowOffset(document, index);
> + int rowLength = getRowLength(document, index);
> + end = rowOffset + rowLength;
> + }
> +
> + if (shiftPressed) {
> + selectionLength += end - index;
> + } else {
> + selectionStart = end;
> + if (selectionStart < textPane.getCharacterCount()
> + && document.getCharacterAt(selectionStart) ==
> '\n') {
> + selectionStart--;
> + }
> +
> + selectionLength = 0;
> + }
> +
> + if (selectionStart + selectionLength <=
> textPane.getCharacterCount()) {
> + textPane.setSelection(selectionStart,
> selectionLength);
> + scrollCharacterToVisible(selectionStart +
> + selectionLength);
> +
> + consumed = true;
> + }
> + } else if (keyCode == Keyboard.KeyCode.LEFT) {
> + if (shiftPressed) {
> // Add the previous character to the selection
> if (selectionStart > 0) {
> selectionStart--; @@ -862,10 +988,7 @@ public
> class TextPaneSkin extends Contai
>
> consumed = true;
> } else if (keyCode == Keyboard.KeyCode.RIGHT) {
> - int selectionStart = textPane.getSelectionStart();
> - int selectionLength = textPane.getSelectionLength();
> -
> - if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
> + if (shiftPressed) {
> // Add the next character to the selection
> if (selectionStart + selectionLength <
> document.getCharacterCount()) {
> selectionLength++; @@ -911,8 +1034,6 @@ public
> class TextPaneSkin extends Contai
>
> consumed = true;
> } else if (keyCode == Keyboard.KeyCode.UP) {
> - int selectionStart = textPane.getSelectionStart();
> -
> int offset = getNextInsertionPoint(caretX, selectionStart,
> TextPane.ScrollDirection.UP);
>
> @@ -920,10 +1041,8 @@ public class TextPaneSkin extends Contai
> offset = 0;
> }
>
> - int selectionLength;
> - if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
> - int selectionEnd = selectionStart +
> textPane.getSelectionLength() - 1;
> - selectionLength = selectionEnd - offset + 1;
> + if (shiftPressed) {
> + selectionLength = selectionStart + selectionLength
> + - offset;
> } else {
> selectionLength = 0;
> }
> @@ -933,10 +1052,8 @@ public class TextPaneSkin extends Contai
>
> consumed = true;
> } else if (keyCode == Keyboard.KeyCode.DOWN) {
> - int selectionStart = textPane.getSelectionStart();
> - int selectionLength = textPane.getSelectionLength();
>
> - if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
> + if (shiftPressed) {
> int from;
> int x;
> if (selectionLength == 0) { @@ -993,27 +1110,42 @@
> public class TextPaneSkin extends Contai
> }
>
> consumed = true;
> - } else if (Keyboard.isPressed(commandModifier) && keyCode ==
> Keyboard.KeyCode.TAB
> - && textPane.isEditable()) {
> - textPane.insert("\t");
> + } else if (keyCode == Keyboard.KeyCode.TAB
> + && (acceptsTab !=
> Keyboard.isPressed(Keyboard.Modifier.CTRL))
> + && isEditable) {
> + if (textPane.getExpandTabs()) {
> + int linePos = selectionStart - getRowOffset(document,
> selectionStart);
> + StringBuilder tabBuilder = new
> StringBuilder(tabWidth);
> + for (int i = 0; i < tabWidth - (linePos % tabWidth);
> i++) {
> + tabBuilder.append(" ");
> + }
> + textPane.insert(tabBuilder.toString());
> + } else {
> + textPane.insert("\t");
> + }
> showCaret(true);
>
> consumed = true;
> - } else if (Keyboard.isPressed(commandModifier)) {
> + } else if (keyCode == Keyboard.KeyCode.INSERT) {
> + if (shiftPressed && isEditable) {
> + textPane.paste();
> + consumed = true;
> + }
> + } else if (commandPressed) {
> if (keyCode == Keyboard.KeyCode.A) {
> textPane.setSelection(0,
> document.getCharacterCount());
> consumed = true;
> - } else if (keyCode == Keyboard.KeyCode.X &&
> textPane.isEditable()) {
> + } else if (keyCode == Keyboard.KeyCode.X && isEditable)
> + {
> textPane.cut();
> consumed = true;
> } else if (keyCode == Keyboard.KeyCode.C) {
> textPane.copy();
> consumed = true;
> - } else if (keyCode == Keyboard.KeyCode.V &&
> textPane.isEditable()) {
> + } else if (keyCode == Keyboard.KeyCode.V && isEditable)
> + {
> textPane.paste();
> consumed = true;
> - } else if (keyCode == Keyboard.KeyCode.Z &&
> textPane.isEditable()) {
> - if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
> + } else if (keyCode == Keyboard.KeyCode.Z && isEditable) {
> + if (shiftPressed) {
> textPane.redo();
> } else {
> textPane.undo(); @@ -1021,33 +1153,6 @@ public
> class TextPaneSkin extends Contai
>
> consumed = true;
> }
> - } else if (keyCode == Keyboard.KeyCode.HOME) {
> - // Move the caret to the beginning of the text
> - if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
> - textPane.setSelection(0,
> textPane.getSelectionStart());
> - } else {
> - textPane.setSelection(0, 0);
> - }
> - scrollCharacterToVisible(0);
> -
> - consumed = true;
> - } else if (keyCode == Keyboard.KeyCode.END) {
> - // Move the caret to the end of the text
> - if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
> - int selectionStart = textPane.getSelectionStart();
> - textPane.setSelection(selectionStart,
> textPane.getCharacterCount()
> - - selectionStart);
> - } else {
> - textPane.setSelection(textPane.getCharacterCount() -
> 1, 0);
> - }
> - scrollCharacterToVisible(textPane.getCharacterCount() -
> 1);
> -
> - consumed = true;
> - } else if (keyCode == Keyboard.KeyCode.INSERT) {
> - if (Keyboard.isPressed(Keyboard.Modifier.SHIFT) &&
> textPane.isEditable()) {
> - textPane.paste();
> - consumed = true;
> - }
> } else {
> consumed = super.keyPressed(component, keyCode,
> keyLocation);
> }
> @@ -1089,7 +1194,7 @@ public class TextPaneSkin extends Contai
>
> Document document = textPane.getDocument();
> if (document != null) {
> - documentView = (TextPaneSkinDocumentView)
> createNodeView(document);
> + documentView = (TextPaneSkinDocumentView)
> + TextPaneSkinNodeView.createNodeView(this, document);
> documentView.attach();
> }
>
> @@ -1132,35 +1237,6 @@ public class TextPaneSkin extends Contai
> }
> }
>
> - TextPaneSkinNodeView createNodeView(Node node) {
> - TextPaneSkinNodeView nodeView = null;
> -
> - if (node instanceof Document) {
> - nodeView = new TextPaneSkinDocumentView(this, (Document)
> node);
> - } else if (node instanceof Paragraph) {
> - nodeView = new TextPaneSkinParagraphView((Paragraph) node);
> - } else if (node instanceof TextNode) {
> - nodeView = new TextPaneSkinTextNodeView((TextNode) node);
> - } else if (node instanceof ImageNode) {
> - nodeView = new TextPaneSkinImageNodeView((ImageNode) node);
> - } else if (node instanceof ComponentNode) {
> - nodeView = new TextPaneSkinComponentNodeView((ComponentNode)
> node);
> - } else if (node instanceof TextSpan) {
> - nodeView = new TextPaneSkinSpanView((TextSpan) node);
> - } else if (node instanceof NumberedList) {
> - nodeView = new TextPaneSkinNumberedListView((NumberedList)
> node);
> - } else if (node instanceof BulletedList) {
> - nodeView = new TextPaneSkinBulletedListView((BulletedList)
> node);
> - } else if (node instanceof List.Item) {
> - nodeView = new TextPaneSkinListItemView((List.Item) node);
> - } else {
> - throw new IllegalArgumentException("Unsupported node type: "
> - + node.getClass().getName());
> - }
> -
> - return nodeView;
> - }
> -
> private void updateSelection() {
> if (documentView.getCharacterCount() > 0) {
> TextPane textPane = (TextPane) getComponent();
>
> Modified:
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBlockView.java
> URL:
> http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBlockView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
>
> ==============================================================================
> ---
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBlockView.java
> (original)
> +++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBlockView.
> +++ java Tue Dec 17 20:17:48 2013
> @@ -22,8 +22,8 @@ import org.apache.pivot.wtk.text.BlockLi
>
> abstract class TextPaneSkinBlockView extends TextPaneSkinElementView
> implements BlockListener {
>
> - public TextPaneSkinBlockView(Block block) {
> - super(block);
> + public TextPaneSkinBlockView(TextPaneSkin textPaneSkin, Block block) {
> + super(textPaneSkin, block);
> }
>
> @Override
>
> Modified:
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBulletedListView.java
> URL:
> http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBulletedListView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
>
> ==============================================================================
> ---
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBulletedListView.java
> (original)
> +++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBulletedLi
> +++ stView.java Tue Dec 17 20:17:48 2013
> @@ -21,8 +21,8 @@ import org.apache.pivot.wtk.text.Bullete
>
> class TextPaneSkinBulletedListView extends TextPaneSkinListView
> implements BulletedListListener {
>
> - public TextPaneSkinBulletedListView(BulletedList bulletedList) {
> - super(bulletedList);
> + public TextPaneSkinBulletedListView(TextPaneSkin textPaneSkin,
> BulletedList bulletedList) {
> + super(textPaneSkin, bulletedList);
> }
>
> @Override
>
> Modified:
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinComponentNodeView.java
> URL:
> http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinComponentNodeView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
>
> ==============================================================================
> ---
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinComponentNodeView.java
> (original)
> +++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinComponentN
> +++ odeView.java Tue Dec 17 20:17:48 2013
> @@ -35,8 +35,8 @@ class TextPaneSkinComponentNodeView exte
> }
> };
>
> - public TextPaneSkinComponentNodeView(ComponentNode componentNode) {
> - super(componentNode);
> + public TextPaneSkinComponentNodeView(TextPaneSkin textPaneSkin,
> ComponentNode componentNode) {
> + super(textPaneSkin, componentNode);
> }
>
> @Override
>
> Modified:
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinDocumentView.java
> URL:
> http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinDocumentView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
>
> ==============================================================================
> ---
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinDocumentView.java
> (original)
> +++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinDocumentVi
> +++ ew.java Tue Dec 17 20:17:48 2013
> @@ -23,11 +23,8 @@ import org.apache.pivot.wtk.text.Documen
> */
> class TextPaneSkinDocumentView extends TextPaneSkinVerticalElementView {
>
> - protected final TextPaneSkin textPaneSkin;
> -
> public TextPaneSkinDocumentView(TextPaneSkin textPaneSkin, Document
> document) {
> - super(document);
> - this.textPaneSkin = textPaneSkin;
> + super(textPaneSkin, document);
> }
>
> @Override
> @@ -43,8 +40,4 @@ class TextPaneSkinDocumentView extends T
> textPaneSkin.invalidateComponent();
> }
>
> - @Override
> - public TextPaneSkin getTextPaneSkin() {
> - return textPaneSkin;
> - }
> }
> \ No newline at end of file
>
> Modified:
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinElementView.java
> URL:
> http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinElementView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
>
> ==============================================================================
> ---
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinElementView.java
> (original)
> +++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinElementVie
> +++ w.java Tue Dec 17 20:17:48 2013
> @@ -41,8 +41,8 @@ abstract class TextPaneSkinElementView e
> private int skinX = 0;
> private int skinY = 0;
>
> - public TextPaneSkinElementView(Element element) {
> - super(element);
> + public TextPaneSkinElementView(TextPaneSkin textPaneSkin, Element
> element) {
> + super(textPaneSkin, element);
> }
>
> @Override
> @@ -54,7 +54,7 @@ abstract class TextPaneSkinElementView e
>
> // Attach child node views
> for (Node node : element) {
> - add(getTextPaneSkin().createNodeView(node));
> + add(createNodeView(getTextPaneSkin(), node));
> }
> }
>
> @@ -235,7 +235,7 @@ abstract class TextPaneSkinElementView e
> int nodeViewOffset = nodeView.getOffset();
> int characterCount = nodeView.getCharacterCount();
>
> - if (offset >= nodeViewOffset && offset < nodeViewOffset +
> characterCount) {
> + if (offset >= nodeViewOffset && offset <= nodeViewOffset +
> + characterCount) {
> characterBounds = nodeView.getCharacterBounds(offset -
> nodeViewOffset);
>
> if (characterBounds != null) { @@ -255,7 +255,7 @@
> abstract class TextPaneSkinElementView e
>
> @Override
> public void nodeInserted(Element element, int index) {
> - insert(getTextPaneSkin().createNodeView(element.get(index)),
> index);
> + insert(createNodeView(getTextPaneSkin(), element.get(index)),
> + index);
> invalidateUpTree();
> }
>
>
> Modified:
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinImageNodeView.java
> URL:
> http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinImageNodeView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
>
> ==============================================================================
> ---
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinImageNodeView.java
> (original)
> +++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinImageNodeV
> +++ iew.java Tue Dec 17 20:17:48 2013
> @@ -28,8 +28,8 @@ import org.apache.pivot.wtk.text.ImageNo
>
> class TextPaneSkinImageNodeView extends TextPaneSkinNodeView implements
> ImageNodeListener,
> ImageListener {
> - public TextPaneSkinImageNodeView(ImageNode imageNode) {
> - super(imageNode);
> + public TextPaneSkinImageNodeView(TextPaneSkin textPaneSkin, ImageNode
> imageNode) {
> + super(textPaneSkin, imageNode);
> }
>
> @Override
>
> Modified:
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListItemView.java
> URL:
> http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListItemView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
>
> ==============================================================================
> ---
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListItemView.java
> (original)
> +++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListItemVi
> +++ ew.java Tue Dec 17 20:17:48 2013
> @@ -18,6 +18,7 @@ package org.apache.pivot.wtk.skin;
>
> import java.util.Iterator;
>
> +import org.apache.pivot.wtk.text.List;
> import org.apache.pivot.wtk.text.TextNode;
>
> class TextPaneSkinListItemView extends TextPaneSkinVerticalElementView {
> @@ -25,8 +26,8 @@ class TextPaneSkinListItemView extends T
> private TextNode indexTextNode;
> private TextPaneSkinTextNodeView indexTextNodeView;
>
> - public TextPaneSkinListItemView(org.apache.pivot.wtk.text.List.Item
> listItem) {
> - super(listItem);
> + public TextPaneSkinListItemView(TextPaneSkin textPaneSkin, List.Item
> listItem) {
> + super(textPaneSkin, listItem);
>
> this.indexTextNode = new TextNode("");
> }
> @@ -36,7 +37,7 @@ class TextPaneSkinListItemView extends T
> super.attach();
>
> // add an extra TextNodeView to render the index text
> - indexTextNodeView = new TextPaneSkinTextNodeView(indexTextNode);
> + indexTextNodeView = new
> + TextPaneSkinTextNodeView(getTextPaneSkin(), indexTextNode);
> indexTextNodeView.setLocation(0, 0);
> insert(indexTextNodeView, 0);
> }
>
> Modified:
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListView.java
> URL:
> http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
>
> ==============================================================================
> ---
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListView.java
> (original)
> +++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListView.j
> +++ ava Tue Dec 17 20:17:48 2013
> @@ -22,8 +22,8 @@ class TextPaneSkinListView extends TextP
>
> protected int maxIndexTextWidth;
>
> - public TextPaneSkinListView(List list) {
> - super(list);
> + public TextPaneSkinListView(TextPaneSkin textPaneSkin, List list) {
> + super(textPaneSkin, list);
> }
>
> public int getMaxIndexTextWidth() {
>
> Modified:
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNodeView.java
> URL:
> http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNodeView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
>
> ==============================================================================
> ---
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNodeView.java
> (original)
> +++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNodeView.j
> +++ ava Tue Dec 17 20:17:48 2013
> @@ -18,19 +18,33 @@ package org.apache.pivot.wtk.skin;
>
> import java.awt.Graphics2D;
>
> +import java.lang.reflect.Constructor;
> +import java.lang.reflect.InvocationTargetException;
> +
> +import org.apache.pivot.collections.HashMap;
> import org.apache.pivot.collections.Sequence;
> import org.apache.pivot.wtk.Bounds;
> import org.apache.pivot.wtk.Dimensions; import
> org.apache.pivot.wtk.Point; import org.apache.pivot.wtk.TextPane;
> +import org.apache.pivot.wtk.text.BulletedList;
> +import org.apache.pivot.wtk.text.ComponentNode;
> +import org.apache.pivot.wtk.text.Document;
> import org.apache.pivot.wtk.text.Element;
> +import org.apache.pivot.wtk.text.ImageNode;
> +import org.apache.pivot.wtk.text.List;
> import org.apache.pivot.wtk.text.Node;
> import org.apache.pivot.wtk.text.NodeListener;
> +import org.apache.pivot.wtk.text.NumberedList;
> +import org.apache.pivot.wtk.text.Paragraph;
> +import org.apache.pivot.wtk.text.TextNode;
> +import org.apache.pivot.wtk.text.TextSpan;
>
> /**
> * Abstract base class for node views.
> */
> abstract class TextPaneSkinNodeView implements NodeListener {
> + protected final TextPaneSkin textPaneSkin;
> private Node node = null;
> private TextPaneSkinElementView parent = null;
>
> @@ -42,7 +56,8 @@ abstract class TextPaneSkinNodeView impl
>
> private boolean valid = false;
>
> - public TextPaneSkinNodeView(Node node) {
> + public TextPaneSkinNodeView(TextPaneSkin textPaneSkin, Node node) {
> + this.textPaneSkin = textPaneSkin;
> this.node = node;
> }
>
> @@ -59,7 +74,7 @@ abstract class TextPaneSkinNodeView impl
> }
>
> protected TextPaneSkin getTextPaneSkin() {
> - return getParent().getTextPaneSkin();
> + return textPaneSkin;
> }
>
> protected void attach() {
> @@ -232,4 +247,41 @@ abstract class TextPaneSkinNodeView impl
> // No-op
> }
>
> + private static HashMap<Class<? extends Node>, Class<? extends
> TextPaneSkinNodeView>>
> + nodeViewSkinMap = new HashMap<>();
> + static {
> + nodeViewSkinMap.put(Document.class,
> TextPaneSkinDocumentView.class);
> + nodeViewSkinMap.put(Paragraph.class,
> TextPaneSkinParagraphView.class);
> + nodeViewSkinMap.put(TextNode.class,
> TextPaneSkinTextNodeView.class);
> + nodeViewSkinMap.put(ImageNode.class,
> TextPaneSkinImageNodeView.class);
> + nodeViewSkinMap.put(ComponentNode.class,
> TextPaneSkinComponentNodeView.class);
> + nodeViewSkinMap.put(TextSpan.class, TextPaneSkinSpanView.class);
> + nodeViewSkinMap.put(NumberedList.class,
> TextPaneSkinNumberedListView.class);
> + nodeViewSkinMap.put(BulletedList.class,
> TextPaneSkinBulletedListView.class);
> + nodeViewSkinMap.put(List.Item.class,
> TextPaneSkinListItemView.class);
> + }
> +
> + public static TextPaneSkinNodeView createNodeView(TextPaneSkin
> textPaneSkin, Node node) {
> + TextPaneSkinNodeView nodeView = null;
> +
> + Class<? extends Node> nodeClass = node.getClass();
> + Class<? extends TextPaneSkinNodeView> skinClass =
> nodeViewSkinMap.get(nodeClass);
> + if (skinClass != null) {
> + try {
> + Constructor<?> constructor =
> skinClass.getConstructor(TextPaneSkin.class, nodeClass);
> + nodeView =
> (TextPaneSkinNodeView)constructor.newInstance(textPaneSkin, node);
> + } catch (NoSuchMethodException | InstantiationException
> + | IllegalAccessException | InvocationTargetException
> ex) {
> + throw new RuntimeException("Error instantiating node view
> for "
> + + nodeClass.getName(), ex);
> + }
> + }
> + if (nodeView == null) {
> + throw new IllegalArgumentException("Unsupported node type: "
> + + nodeClass.getName());
> + }
> +
> + return nodeView;
> + }
> +
> }
>
> Modified:
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNumberedListView.java
> URL:
> http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNumberedListView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
>
> ==============================================================================
> ---
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNumberedListView.java
> (original)
> +++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNumberedLi
> +++ stView.java Tue Dec 17 20:17:48 2013
> @@ -59,8 +59,8 @@ class TextPaneSkinNumberedListView exten
> return (char) ('A' + n - 1) + "";
> }
>
> - public TextPaneSkinNumberedListView(NumberedList numberedList) {
> - super(numberedList);
> + public TextPaneSkinNumberedListView(TextPaneSkin textPaneSkin,
> NumberedList numberedList) {
> + super(textPaneSkin, numberedList);
> }
>
> @Override
>
> Modified:
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinParagraphView.java
> URL:
> http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinParagraphView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
>
> ==============================================================================
> ---
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinParagraphView.java
> (original)
> +++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinParagraphV
> +++ iew.java Tue Dec 17 20:17:48 2013
> @@ -54,8 +54,8 @@ class TextPaneSkinParagraphView extends
> private ArrayList<Row> rows = null;
> private Bounds terminatorBounds = new Bounds(0, 0, 0, 0);
>
> - public TextPaneSkinParagraphView(Paragraph paragraph) {
> - super(paragraph);
> + public TextPaneSkinParagraphView(TextPaneSkin textPaneSkin, Paragraph
> paragraph) {
> + super(textPaneSkin, paragraph);
> }
>
> @Override
>
> Modified:
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinSpanView.java
> URL:
> http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinSpanView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
>
> ==============================================================================
> ---
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinSpanView.java
> (original)
> +++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinSpanView.j
> +++ ava Tue Dec 17 20:17:48 2013
> @@ -25,8 +25,8 @@ import org.apache.pivot.wtk.text.TextSpa
> */
> class TextPaneSkinSpanView extends TextPaneSkinElementView {
>
> - public TextPaneSkinSpanView(TextSpan span) {
> - super(span);
> + public TextPaneSkinSpanView(TextPaneSkin textPaneSkin, TextSpan span)
> {
> + super(textPaneSkin, span);
> }
>
> @Override
>
> Modified:
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinTextNodeView.java
> URL:
> http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinTextNodeView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
>
> ==============================================================================
> ---
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinTextNodeView.java
> (original)
> +++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinTextNodeVi
> +++ ew.java Tue Dec 17 20:17:48 2013
> @@ -47,12 +47,12 @@ class TextPaneSkinTextNodeView extends T
> private GlyphVector glyphVector = null;
> private TextPaneSkinTextNodeView next = null;
>
> - public TextPaneSkinTextNodeView(TextNode textNode) {
> - this(textNode, 0);
> + public TextPaneSkinTextNodeView(TextPaneSkin textPaneSkin, TextNode
> textNode) {
> + this(textPaneSkin, textNode, 0);
> }
>
> - public TextPaneSkinTextNodeView(TextNode textNode, int start) {
> - super(textNode);
> + public TextPaneSkinTextNodeView(TextPaneSkin textPaneSkin, TextNode
> textNode, int start) {
> + super(textPaneSkin, textNode);
> this.start = start;
> }
>
> @@ -134,7 +134,7 @@ class TextPaneSkinTextNodeView extends T
>
> if (end < ci.getEndIndex()) {
> length = end - start;
> - next = new TextPaneSkinTextNodeView(textNode, end);
> + next = new TextPaneSkinTextNodeView(getTextPaneSkin(),
> + textNode, end);
> next.setParent(getParent());
> } else {
> length = ci.getEndIndex() - start; @@ -473,11 +473,18 @@
> class TextPaneSkinTextNodeView extends T
>
> @Override
> public Bounds getCharacterBounds(int offset) {
> - Shape glyphBounds = glyphVector.getGlyphLogicalBounds(offset);
> + // If the offest == length, then use the right hand edge of the
> previous
> + // offset instead -- this is for positioning the caret at the end
> of the text
> + Shape glyphBounds = glyphVector.getGlyphLogicalBounds(offset ==
> + length ? offset - 1 : offset);
> Rectangle2D glyphBounds2D = glyphBounds.getBounds2D();
>
> - return new Bounds((int) Math.floor(glyphBounds2D.getX()), 0,
> - (int) Math.ceil(glyphBounds2D.getWidth()), getHeight());
> + if (offset == length) {
> + return new Bounds((int) Math.ceil(glyphBounds2D.getX() +
> glyphBounds2D.getWidth()), 0,
> + 1, getHeight());
> + } else {
> + return new Bounds((int) Math.floor(glyphBounds2D.getX()), 0,
> + (int) Math.ceil(glyphBounds2D.getWidth()), getHeight());
> + }
> }
>
> @Override
>
> Modified:
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinVerticalElementView.java
> URL:
> http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinVerticalElementView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
>
> ==============================================================================
> ---
> pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinVerticalElementView.java
> (original)
> +++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinVerticalEl
> +++ ementView.java Tue Dec 17 20:17:48 2013
> @@ -27,8 +27,8 @@ import org.apache.pivot.wtk.text.Element
> */
> abstract class TextPaneSkinVerticalElementView extends
> TextPaneSkinElementView {
>
> - public TextPaneSkinVerticalElementView(Element element) {
> - super(element);
> + public TextPaneSkinVerticalElementView(TextPaneSkin textPaneSkin,
> Element element) {
> + super(textPaneSkin, element);
> }
>
> @Override
>
> Modified:
> pivot/trunk/wtk/src/org/apache/pivot/wtk/text/PlainTextSerializer.java
> URL:
> http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/text/PlainTextSerializer.java?rev=1551679&r1=1551678&r2=1551679&view=diff
>
> ==============================================================================
> --- pivot/trunk/wtk/src/org/apache/pivot/wtk/text/PlainTextSerializer.java
> (original)
> +++ pivot/trunk/wtk/src/org/apache/pivot/wtk/text/PlainTextSerializer.ja
> +++ va Tue Dec 17 20:17:48 2013
> @@ -39,6 +39,9 @@ public class PlainTextSerializer impleme
> public static final String MIME_TYPE = "text/plain";
> public static final int BUFFER_SIZE = 2048;
>
> + private boolean expandTabs = false;
> + private int tabWidth = 4;
> +
> public PlainTextSerializer() {
> this(Charset.defaultCharset());
> }
> @@ -55,6 +58,35 @@ public class PlainTextSerializer impleme
> this.charset = charset;
> }
>
> + public int getTabWidth() {
> + return tabWidth;
> + }
> +
> + public void setTabWidth(int tabWidth) {
> + if (tabWidth < 0) {
> + throw new IllegalArgumentException("tabWidth is negative.");
> + }
> +
> + this.tabWidth = tabWidth;
> + }
> +
> + public boolean getExpandTabs() {
> + return expandTabs;
> + }
> +
> + /**
> + * Sets whether tab characters (<code>\t</code>) are expanded to an
> + * appropriate number of spaces while reading the text.
> + *
> + * @param expandTabs <code>true</code> to replace tab characters with
> space
> + * characters (depending on the setting of the {@link #getTabWidth}
> value)
> + * or <code>false</code> to leave tabs alone.
> + */
> + public void setExpandTabs(boolean expandTabs) {
> + this.expandTabs = expandTabs;
> + }
> +
> +
> @Override
> public Document readObject(InputStream inputStream) throws
> IOException {
> Reader reader = new InputStreamReader(inputStream, charset); @@
> -70,6 +102,18 @@ public class PlainTextSerializer impleme
>
> String line = bufferedReader.readLine();
> while (line != null) {
> + if (expandTabs) {
> + int ix = 0;
> + StringBuilder buf = new StringBuilder(line);
> + while ((ix = buf.indexOf("\t", ix)) >= 0) {
> + buf.deleteCharAt(ix);
> + int spaces = tabWidth - (ix % tabWidth);
> + for (int j = 0; j < spaces; j++) {
> + buf.insert(ix++, ' ');
> + }
> + }
> + line = buf.toString();
> + }
> document.add(new Paragraph(line));
> line = bufferedReader.readLine();
> }
>
>
>
>
RE: svn commit: r1551679 - in
/pivot/trunk/wtk/src/org/apache/pivot/wtk: ./ skin/ text/
Posted by "Roger L. Whitcomb" <Ro...@actian.com>.
As I was reviewing this commit, I realized there are additional changes in here not related strictly to tab handling (namely other keyboard handling changes), that are, in fact, not complete, and likely not correct as-is.
Given that this is "trunk" and not released yet, and that I am committed to getting this right sooner rather than later, I'm inclined to leave the code as-is and fix it up from here. BUT, if anyone doesn't think that's a good idea, I can revert those parts that are not good (esp. in TextPaneSkin.java) and make another commit later once the keyboard stuff is working better. Everything builds, so that shouldn't be a problem.
Comments?
Thanks,
~Roger
-----Original Message-----
From: rwhitcomb@apache.org [mailto:rwhitcomb@apache.org]
Sent: Tuesday, December 17, 2013 12:18 PM
To: commits@pivot.apache.org
Subject: svn commit: r1551679 - in /pivot/trunk/wtk/src/org/apache/pivot/wtk: ./ skin/ text/
Author: rwhitcomb
Date: Tue Dec 17 20:17:48 2013
New Revision: 1551679
URL: http://svn.apache.org/r1551679
Log:
PIVOT-696: Support expansion of tabs in TextPane in the same ways as with TextArea.
Add new style: tabWidth and property expandTabs.
This also required changing the way we get a hold of the TextPane.Skin from within the skin view classes. Also PlainTextSerializer needs the tab settings.
Modified:
pivot/trunk/wtk/src/org/apache/pivot/wtk/TextPane.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkin.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBlockView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBulletedListView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinComponentNodeView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinDocumentView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinElementView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinImageNodeView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListItemView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNodeView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNumberedListView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinParagraphView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinSpanView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinTextNodeView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinVerticalElementView.java
pivot/trunk/wtk/src/org/apache/pivot/wtk/text/PlainTextSerializer.java
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/TextPane.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/TextPane.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/TextPane.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/TextPane.java Tue Dec 17
+++ 20:17:48 2013
@@ -17,8 +17,12 @@
package org.apache.pivot.wtk;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
+import java.net.URL;
import org.apache.pivot.beans.DefaultProperty;
import org.apache.pivot.collections.LinkedList;
@@ -96,6 +100,12 @@ public class TextPane extends Container
* @return The bounds of the character at the given offset.
*/
public Bounds getCharacterBounds(int offset);
+
+ /**
+ * Returns the current setting of the "tabWidth" style (so "setText"
+ * uses the same value as Ctrl-Tab from user).
+ */
+ public int getTabWidth();
}
private interface Edit {
@@ -199,6 +209,8 @@ public class TextPane extends Container
private int selectionStart = 0;
private int selectionLength = 0;
+ private boolean expandTabs = false;
+
private boolean editable = true;
private boolean undoingHistory = false;
private boolean bulkOperation = false; @@ -643,6 +655,8 @@ public class TextPane extends Container
try {
PlainTextSerializer serializer = new PlainTextSerializer();
StringReader reader = new StringReader(text);
+ serializer.setExpandTabs(this.expandTabs);
+ serializer.setTabWidth(((TextPane.Skin)
+ getSkin()).getTabWidth());
documentLocal = serializer.readObject(reader);
n = documentLocal.getCharacterCount();
@@ -716,14 +730,78 @@ public class TextPane extends Container
/**
* Convenience method to create a text-only document consisting of one
* paragraph per line of the given text.
+ *
+ * @param text
*/
public void setText(String text) {
+ if (text == null) {
+ throw new IllegalArgumentException();
+ }
+
+ try {
+ setText(new StringReader(text));
+ } catch (IOException exception) {
+ throw new RuntimeException(exception);
+ }
+ }
+
+ public void setText(URL textURL) throws IOException {
+ if (textURL == null) {
+ throw new IllegalArgumentException();
+ }
+
+ InputStream inputStream = null;
+ try {
+ inputStream = textURL.openStream();
+ setText(new InputStreamReader(inputStream));
+ } finally {
+ if (inputStream != null) {
+ inputStream.close();
+ }
+ }
+ }
+
+ public void setText(Reader textReader) throws IOException {
+ if (textReader == null) {
+ throw new IllegalArgumentException();
+ }
+
+ int tabPosition = 0;
+ int tabWidth = ((TextPane.Skin) getSkin()).getTabWidth();
+
Document doc = new Document();
- String[] lines = text.split("\r?\n");
- for (int i = 0; i < lines.length; i++) {
- Paragraph paragraph = new Paragraph(lines[i]);
+ StringBuilder text = new StringBuilder();
+
+ int c = textReader.read();
+ while (c != -1) {
+ if (c == '\n') {
+ Paragraph paragraph = new Paragraph(text.toString());
+ doc.add(paragraph);
+ text.setLength(0);
+ tabPosition = 0;
+ } else if (c == '\t') {
+ if (expandTabs) {
+ int spaces = tabWidth - (tabPosition % tabWidth);
+ for (int i = 0; i < spaces; i++) {
+ text.append(' ');
+ }
+ tabPosition += spaces;
+ } else {
+ text.append('\t');
+ }
+ } else {
+ text.append((char) c);
+ tabPosition++;
+ }
+
+ c = textReader.read();
+ }
+
+ if (text.length() != 0) {
+ Paragraph paragraph = new Paragraph(text.toString());
doc.add(paragraph);
}
+
setDocument(doc);
}
@@ -876,6 +954,27 @@ public class TextPane extends Container
}
}
+ public boolean getExpandTabs() {
+ return expandTabs;
+ }
+
+ /**
+ * Sets whether tab characters (<code>\t</code>) are expanded to an
+ * appropriate number of spaces during {@link #setText} and
+ * {@link #paste} operations. Note: doing this for keyboard input
+ * is handled in the skin.
+ *
+ * @param expandTabs <code>true</code> to replace tab characters with space
+ * characters (depending on the setting of the
+ * {@link TextPane.Skin#getTabWidth} value) or <code>false</code> to leave
+ * tabs alone. Note: this only affects tabs encountered during program
+ * operations; tabs entered via the keyboard by the user are always
+ * expanded, regardless of this setting.
+ */
+ public void setExpandTabs(boolean expandTabs) {
+ this.expandTabs = expandTabs;
+ }
+
public int getInsertionPoint(int x, int y) {
TextPane.Skin textPaneSkin = (TextPane.Skin) getSkin();
return textPaneSkin.getInsertionPoint(x, y);
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkin.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkin.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkin.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkin.java Tue
+++ Dec 17 20:17:48 2013
@@ -39,16 +39,9 @@ import org.apache.pivot.wtk.TextPane; import org.apache.pivot.wtk.TextPaneListener;
import org.apache.pivot.wtk.TextPaneSelectionListener;
import org.apache.pivot.wtk.Theme;
-import org.apache.pivot.wtk.text.BulletedList;
-import org.apache.pivot.wtk.text.ComponentNode;
import org.apache.pivot.wtk.text.Document;
-import org.apache.pivot.wtk.text.ImageNode;
-import org.apache.pivot.wtk.text.List;
import org.apache.pivot.wtk.text.Node;
-import org.apache.pivot.wtk.text.NumberedList;
import org.apache.pivot.wtk.text.Paragraph;
-import org.apache.pivot.wtk.text.TextNode;
-import org.apache.pivot.wtk.text.TextSpan;
/**
* Text pane skin.
@@ -145,6 +138,8 @@ public class TextPaneSkin extends Contai
private Insets margin = new Insets(4);
private boolean wrapText = true;
+ private int tabWidth = 4;
+ private boolean acceptsTab = false;
private static final int SCROLL_RATE = 30;
@@ -171,7 +166,7 @@ public class TextPaneSkin extends Contai
Document document = textPane.getDocument();
if (document != null) {
- documentView = (TextPaneSkinDocumentView) createNodeView(document);
+ documentView = (TextPaneSkinDocumentView)
+ TextPaneSkinNodeView.createNodeView(this, document);
documentView.attach();
updateSelection();
}
@@ -386,6 +381,45 @@ public class TextPaneSkin extends Contai
return characterBounds;
}
+ /**
+ * Gets current value of style that determines the behavior of <tt>TAB</tt>
+ * and <tt>Ctrl-TAB</tt> characters.
+ *
+ * @return <tt>true</tt> if <tt>TAB</tt> inserts an appropriate number of
+ * spaces, while <tt>Ctrl-TAB</tt> shifts focus to next component.
+ * <tt>false</tt> (default) means <tt>TAB</tt> shifts focus and
+ * <tt>Ctrl-TAB</tt> inserts spaces.
+ */
+ public boolean getAcceptsTab() {
+ return acceptsTab;
+ }
+
+ /**
+ * Sets current value of style that determines the behavior of <tt>TAB</tt>
+ * and <tt>Ctrl-TAB</tt> characters.
+ *
+ * @param acceptsTab <tt>true</tt> if <tt>TAB</tt> inserts an appropriate
+ * number of spaces, while <tt>Ctrl-TAB</tt> shifts focus to next component.
+ * <tt>false</tt> (default) means <tt>TAB</tt> shifts focus and
+ * <tt>Ctrl-TAB</tt> inserts spaces.
+ */
+ public void setAcceptsTab(boolean acceptsTab) {
+ this.acceptsTab = acceptsTab;
+ }
+
+ @Override
+ public int getTabWidth() {
+ return tabWidth;
+ }
+
+ public void setTabWidth(int tabWidth) {
+ if (tabWidth < 0) {
+ throw new IllegalArgumentException("tabWidth is negative.");
+ }
+
+ this.tabWidth = tabWidth;
+ }
+
private void scrollCharacterToVisible(int offset) {
TextPane textPane = (TextPane) getComponent();
Bounds characterBounds = getCharacterBounds(offset); @@ -797,6 +831,35 @@ public class TextPaneSkin extends Contai
return consumed;
}
+ private int getRowOffset(Document document, int index) {
+ if (document != null) {
+ Node node = document.getDescendantAt(index);
+ while (node != null && !(node instanceof Paragraph)) {
+ node = node.getParent();
+ }
+ // TODO: doesn't take into account the line wrapping within a paragraph
+ if (node != null) {
+ return node.getDocumentOffset();
+ }
+ }
+ return 0;
+ }
+
+ private int getRowLength(Document document, int index) {
+ if (document != null) {
+ Node node = document.getDescendantAt(index);
+ while (node != null && !(node instanceof Paragraph)) {
+ node = node.getParent();
+ }
+ // TODO: doesn't take into account the line wrapping within a paragraph
+ // Assuming the node is a Paragraph, the count includes the trailing \n, so discount it
+ if (node != null) {
+ return node.getCharacterCount() - 1;
+ }
+ }
+ return 0;
+ }
+
@Override
public boolean keyPressed(final Component component, int keyCode,
Keyboard.KeyLocation keyLocation) { @@ -805,25 +868,88 @@ public class TextPaneSkin extends Contai
final TextPane textPane = (TextPane) getComponent();
Document document = textPane.getDocument();
+ int selectionStart = textPane.getSelectionStart();
+ int selectionLength = textPane.getSelectionLength();
+
Keyboard.Modifier commandModifier = Platform.getCommandModifier();
+ boolean commandPressed = Keyboard.isPressed(commandModifier);
+ boolean wordNavPressed = Keyboard.isPressed(Platform.getWordNavigationModifier());
+ boolean shiftPressed = Keyboard.isPressed(Keyboard.Modifier.SHIFT);
+ boolean ctrlPressed = Keyboard.isPressed(Keyboard.Modifier.CTRL);
+ boolean metaPressed = Keyboard.isPressed(Keyboard.Modifier.META);
+ boolean isEditable = textPane.isEditable();
+
if (document != null) {
- if (keyCode == Keyboard.KeyCode.ENTER && textPane.isEditable()) {
+ if (keyCode == Keyboard.KeyCode.ENTER && isEditable) {
textPane.insertParagraph();
consumed = true;
- } else if (keyCode == Keyboard.KeyCode.DELETE && textPane.isEditable()) {
+ } else if (keyCode == Keyboard.KeyCode.DELETE &&
+ isEditable) {
textPane.delete(false);
consumed = true;
- } else if (keyCode == Keyboard.KeyCode.BACKSPACE && textPane.isEditable()) {
+ } else if (keyCode == Keyboard.KeyCode.BACKSPACE &&
+ isEditable) {
textPane.delete(true);
consumed = true;
- } else if (keyCode == Keyboard.KeyCode.LEFT) {
- int selectionStart = textPane.getSelectionStart();
- int selectionLength = textPane.getSelectionLength();
+ } else if (keyCode == Keyboard.KeyCode.HOME
+ || (keyCode == Keyboard.KeyCode.LEFT && metaPressed)) {
+ int start;
+ if (commandPressed) {
+ // Move the caret to the beginning of the text
+ start = 0;
+ } else {
+ // Move the caret to the beginning of the line
+ start = getRowOffset(document, selectionStart);
+ }
- if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
+ if (shiftPressed) {
+ selectionLength += selectionStart - start;
+ } else {
+ selectionLength = 0;
+ }
+
+ if (selectionStart >= 0) {
+ textPane.setSelection(start, selectionLength);
+ scrollCharacterToVisible(start);
+
+ consumed = true;
+ }
+ } else if (keyCode == Keyboard.KeyCode.END
+ || (keyCode == Keyboard.KeyCode.RIGHT && metaPressed)) {
+ int end;
+ int index = selectionStart + selectionLength;
+
+ if (commandPressed) {
+ // Move the caret to end of the text
+ end = textPane.getCharacterCount() - 1;
+ } else {
+ // Move the caret to the end of the line
+ int rowOffset = getRowOffset(document, index);
+ int rowLength = getRowLength(document, index);
+ end = rowOffset + rowLength;
+ }
+
+ if (shiftPressed) {
+ selectionLength += end - index;
+ } else {
+ selectionStart = end;
+ if (selectionStart < textPane.getCharacterCount()
+ && document.getCharacterAt(selectionStart) == '\n') {
+ selectionStart--;
+ }
+
+ selectionLength = 0;
+ }
+
+ if (selectionStart + selectionLength <= textPane.getCharacterCount()) {
+ textPane.setSelection(selectionStart, selectionLength);
+ scrollCharacterToVisible(selectionStart +
+ selectionLength);
+
+ consumed = true;
+ }
+ } else if (keyCode == Keyboard.KeyCode.LEFT) {
+ if (shiftPressed) {
// Add the previous character to the selection
if (selectionStart > 0) {
selectionStart--; @@ -862,10 +988,7 @@ public class TextPaneSkin extends Contai
consumed = true;
} else if (keyCode == Keyboard.KeyCode.RIGHT) {
- int selectionStart = textPane.getSelectionStart();
- int selectionLength = textPane.getSelectionLength();
-
- if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
+ if (shiftPressed) {
// Add the next character to the selection
if (selectionStart + selectionLength < document.getCharacterCount()) {
selectionLength++; @@ -911,8 +1034,6 @@ public class TextPaneSkin extends Contai
consumed = true;
} else if (keyCode == Keyboard.KeyCode.UP) {
- int selectionStart = textPane.getSelectionStart();
-
int offset = getNextInsertionPoint(caretX, selectionStart,
TextPane.ScrollDirection.UP);
@@ -920,10 +1041,8 @@ public class TextPaneSkin extends Contai
offset = 0;
}
- int selectionLength;
- if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
- int selectionEnd = selectionStart + textPane.getSelectionLength() - 1;
- selectionLength = selectionEnd - offset + 1;
+ if (shiftPressed) {
+ selectionLength = selectionStart + selectionLength
+ - offset;
} else {
selectionLength = 0;
}
@@ -933,10 +1052,8 @@ public class TextPaneSkin extends Contai
consumed = true;
} else if (keyCode == Keyboard.KeyCode.DOWN) {
- int selectionStart = textPane.getSelectionStart();
- int selectionLength = textPane.getSelectionLength();
- if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
+ if (shiftPressed) {
int from;
int x;
if (selectionLength == 0) { @@ -993,27 +1110,42 @@ public class TextPaneSkin extends Contai
}
consumed = true;
- } else if (Keyboard.isPressed(commandModifier) && keyCode == Keyboard.KeyCode.TAB
- && textPane.isEditable()) {
- textPane.insert("\t");
+ } else if (keyCode == Keyboard.KeyCode.TAB
+ && (acceptsTab != Keyboard.isPressed(Keyboard.Modifier.CTRL))
+ && isEditable) {
+ if (textPane.getExpandTabs()) {
+ int linePos = selectionStart - getRowOffset(document, selectionStart);
+ StringBuilder tabBuilder = new StringBuilder(tabWidth);
+ for (int i = 0; i < tabWidth - (linePos % tabWidth); i++) {
+ tabBuilder.append(" ");
+ }
+ textPane.insert(tabBuilder.toString());
+ } else {
+ textPane.insert("\t");
+ }
showCaret(true);
consumed = true;
- } else if (Keyboard.isPressed(commandModifier)) {
+ } else if (keyCode == Keyboard.KeyCode.INSERT) {
+ if (shiftPressed && isEditable) {
+ textPane.paste();
+ consumed = true;
+ }
+ } else if (commandPressed) {
if (keyCode == Keyboard.KeyCode.A) {
textPane.setSelection(0, document.getCharacterCount());
consumed = true;
- } else if (keyCode == Keyboard.KeyCode.X && textPane.isEditable()) {
+ } else if (keyCode == Keyboard.KeyCode.X && isEditable)
+ {
textPane.cut();
consumed = true;
} else if (keyCode == Keyboard.KeyCode.C) {
textPane.copy();
consumed = true;
- } else if (keyCode == Keyboard.KeyCode.V && textPane.isEditable()) {
+ } else if (keyCode == Keyboard.KeyCode.V && isEditable)
+ {
textPane.paste();
consumed = true;
- } else if (keyCode == Keyboard.KeyCode.Z && textPane.isEditable()) {
- if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
+ } else if (keyCode == Keyboard.KeyCode.Z && isEditable) {
+ if (shiftPressed) {
textPane.redo();
} else {
textPane.undo(); @@ -1021,33 +1153,6 @@ public class TextPaneSkin extends Contai
consumed = true;
}
- } else if (keyCode == Keyboard.KeyCode.HOME) {
- // Move the caret to the beginning of the text
- if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
- textPane.setSelection(0, textPane.getSelectionStart());
- } else {
- textPane.setSelection(0, 0);
- }
- scrollCharacterToVisible(0);
-
- consumed = true;
- } else if (keyCode == Keyboard.KeyCode.END) {
- // Move the caret to the end of the text
- if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
- int selectionStart = textPane.getSelectionStart();
- textPane.setSelection(selectionStart, textPane.getCharacterCount()
- - selectionStart);
- } else {
- textPane.setSelection(textPane.getCharacterCount() - 1, 0);
- }
- scrollCharacterToVisible(textPane.getCharacterCount() - 1);
-
- consumed = true;
- } else if (keyCode == Keyboard.KeyCode.INSERT) {
- if (Keyboard.isPressed(Keyboard.Modifier.SHIFT) && textPane.isEditable()) {
- textPane.paste();
- consumed = true;
- }
} else {
consumed = super.keyPressed(component, keyCode, keyLocation);
}
@@ -1089,7 +1194,7 @@ public class TextPaneSkin extends Contai
Document document = textPane.getDocument();
if (document != null) {
- documentView = (TextPaneSkinDocumentView) createNodeView(document);
+ documentView = (TextPaneSkinDocumentView)
+ TextPaneSkinNodeView.createNodeView(this, document);
documentView.attach();
}
@@ -1132,35 +1237,6 @@ public class TextPaneSkin extends Contai
}
}
- TextPaneSkinNodeView createNodeView(Node node) {
- TextPaneSkinNodeView nodeView = null;
-
- if (node instanceof Document) {
- nodeView = new TextPaneSkinDocumentView(this, (Document) node);
- } else if (node instanceof Paragraph) {
- nodeView = new TextPaneSkinParagraphView((Paragraph) node);
- } else if (node instanceof TextNode) {
- nodeView = new TextPaneSkinTextNodeView((TextNode) node);
- } else if (node instanceof ImageNode) {
- nodeView = new TextPaneSkinImageNodeView((ImageNode) node);
- } else if (node instanceof ComponentNode) {
- nodeView = new TextPaneSkinComponentNodeView((ComponentNode) node);
- } else if (node instanceof TextSpan) {
- nodeView = new TextPaneSkinSpanView((TextSpan) node);
- } else if (node instanceof NumberedList) {
- nodeView = new TextPaneSkinNumberedListView((NumberedList) node);
- } else if (node instanceof BulletedList) {
- nodeView = new TextPaneSkinBulletedListView((BulletedList) node);
- } else if (node instanceof List.Item) {
- nodeView = new TextPaneSkinListItemView((List.Item) node);
- } else {
- throw new IllegalArgumentException("Unsupported node type: "
- + node.getClass().getName());
- }
-
- return nodeView;
- }
-
private void updateSelection() {
if (documentView.getCharacterCount() > 0) {
TextPane textPane = (TextPane) getComponent();
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBlockView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBlockView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBlockView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBlockView.
+++ java Tue Dec 17 20:17:48 2013
@@ -22,8 +22,8 @@ import org.apache.pivot.wtk.text.BlockLi
abstract class TextPaneSkinBlockView extends TextPaneSkinElementView implements BlockListener {
- public TextPaneSkinBlockView(Block block) {
- super(block);
+ public TextPaneSkinBlockView(TextPaneSkin textPaneSkin, Block block) {
+ super(textPaneSkin, block);
}
@Override
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBulletedListView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBulletedListView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBulletedListView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinBulletedLi
+++ stView.java Tue Dec 17 20:17:48 2013
@@ -21,8 +21,8 @@ import org.apache.pivot.wtk.text.Bullete
class TextPaneSkinBulletedListView extends TextPaneSkinListView implements BulletedListListener {
- public TextPaneSkinBulletedListView(BulletedList bulletedList) {
- super(bulletedList);
+ public TextPaneSkinBulletedListView(TextPaneSkin textPaneSkin, BulletedList bulletedList) {
+ super(textPaneSkin, bulletedList);
}
@Override
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinComponentNodeView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinComponentNodeView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinComponentNodeView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinComponentN
+++ odeView.java Tue Dec 17 20:17:48 2013
@@ -35,8 +35,8 @@ class TextPaneSkinComponentNodeView exte
}
};
- public TextPaneSkinComponentNodeView(ComponentNode componentNode) {
- super(componentNode);
+ public TextPaneSkinComponentNodeView(TextPaneSkin textPaneSkin, ComponentNode componentNode) {
+ super(textPaneSkin, componentNode);
}
@Override
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinDocumentView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinDocumentView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinDocumentView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinDocumentVi
+++ ew.java Tue Dec 17 20:17:48 2013
@@ -23,11 +23,8 @@ import org.apache.pivot.wtk.text.Documen
*/
class TextPaneSkinDocumentView extends TextPaneSkinVerticalElementView {
- protected final TextPaneSkin textPaneSkin;
-
public TextPaneSkinDocumentView(TextPaneSkin textPaneSkin, Document document) {
- super(document);
- this.textPaneSkin = textPaneSkin;
+ super(textPaneSkin, document);
}
@Override
@@ -43,8 +40,4 @@ class TextPaneSkinDocumentView extends T
textPaneSkin.invalidateComponent();
}
- @Override
- public TextPaneSkin getTextPaneSkin() {
- return textPaneSkin;
- }
}
\ No newline at end of file
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinElementView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinElementView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinElementView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinElementVie
+++ w.java Tue Dec 17 20:17:48 2013
@@ -41,8 +41,8 @@ abstract class TextPaneSkinElementView e
private int skinX = 0;
private int skinY = 0;
- public TextPaneSkinElementView(Element element) {
- super(element);
+ public TextPaneSkinElementView(TextPaneSkin textPaneSkin, Element element) {
+ super(textPaneSkin, element);
}
@Override
@@ -54,7 +54,7 @@ abstract class TextPaneSkinElementView e
// Attach child node views
for (Node node : element) {
- add(getTextPaneSkin().createNodeView(node));
+ add(createNodeView(getTextPaneSkin(), node));
}
}
@@ -235,7 +235,7 @@ abstract class TextPaneSkinElementView e
int nodeViewOffset = nodeView.getOffset();
int characterCount = nodeView.getCharacterCount();
- if (offset >= nodeViewOffset && offset < nodeViewOffset + characterCount) {
+ if (offset >= nodeViewOffset && offset <= nodeViewOffset +
+ characterCount) {
characterBounds = nodeView.getCharacterBounds(offset - nodeViewOffset);
if (characterBounds != null) { @@ -255,7 +255,7 @@ abstract class TextPaneSkinElementView e
@Override
public void nodeInserted(Element element, int index) {
- insert(getTextPaneSkin().createNodeView(element.get(index)), index);
+ insert(createNodeView(getTextPaneSkin(), element.get(index)),
+ index);
invalidateUpTree();
}
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinImageNodeView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinImageNodeView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinImageNodeView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinImageNodeV
+++ iew.java Tue Dec 17 20:17:48 2013
@@ -28,8 +28,8 @@ import org.apache.pivot.wtk.text.ImageNo
class TextPaneSkinImageNodeView extends TextPaneSkinNodeView implements ImageNodeListener,
ImageListener {
- public TextPaneSkinImageNodeView(ImageNode imageNode) {
- super(imageNode);
+ public TextPaneSkinImageNodeView(TextPaneSkin textPaneSkin, ImageNode imageNode) {
+ super(textPaneSkin, imageNode);
}
@Override
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListItemView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListItemView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListItemView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListItemVi
+++ ew.java Tue Dec 17 20:17:48 2013
@@ -18,6 +18,7 @@ package org.apache.pivot.wtk.skin;
import java.util.Iterator;
+import org.apache.pivot.wtk.text.List;
import org.apache.pivot.wtk.text.TextNode;
class TextPaneSkinListItemView extends TextPaneSkinVerticalElementView { @@ -25,8 +26,8 @@ class TextPaneSkinListItemView extends T
private TextNode indexTextNode;
private TextPaneSkinTextNodeView indexTextNodeView;
- public TextPaneSkinListItemView(org.apache.pivot.wtk.text.List.Item listItem) {
- super(listItem);
+ public TextPaneSkinListItemView(TextPaneSkin textPaneSkin, List.Item listItem) {
+ super(textPaneSkin, listItem);
this.indexTextNode = new TextNode("");
}
@@ -36,7 +37,7 @@ class TextPaneSkinListItemView extends T
super.attach();
// add an extra TextNodeView to render the index text
- indexTextNodeView = new TextPaneSkinTextNodeView(indexTextNode);
+ indexTextNodeView = new
+ TextPaneSkinTextNodeView(getTextPaneSkin(), indexTextNode);
indexTextNodeView.setLocation(0, 0);
insert(indexTextNodeView, 0);
}
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinListView.j
+++ ava Tue Dec 17 20:17:48 2013
@@ -22,8 +22,8 @@ class TextPaneSkinListView extends TextP
protected int maxIndexTextWidth;
- public TextPaneSkinListView(List list) {
- super(list);
+ public TextPaneSkinListView(TextPaneSkin textPaneSkin, List list) {
+ super(textPaneSkin, list);
}
public int getMaxIndexTextWidth() {
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNodeView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNodeView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNodeView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNodeView.j
+++ ava Tue Dec 17 20:17:48 2013
@@ -18,19 +18,33 @@ package org.apache.pivot.wtk.skin;
import java.awt.Graphics2D;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+import org.apache.pivot.collections.HashMap;
import org.apache.pivot.collections.Sequence;
import org.apache.pivot.wtk.Bounds;
import org.apache.pivot.wtk.Dimensions; import org.apache.pivot.wtk.Point; import org.apache.pivot.wtk.TextPane;
+import org.apache.pivot.wtk.text.BulletedList;
+import org.apache.pivot.wtk.text.ComponentNode;
+import org.apache.pivot.wtk.text.Document;
import org.apache.pivot.wtk.text.Element;
+import org.apache.pivot.wtk.text.ImageNode;
+import org.apache.pivot.wtk.text.List;
import org.apache.pivot.wtk.text.Node;
import org.apache.pivot.wtk.text.NodeListener;
+import org.apache.pivot.wtk.text.NumberedList;
+import org.apache.pivot.wtk.text.Paragraph;
+import org.apache.pivot.wtk.text.TextNode;
+import org.apache.pivot.wtk.text.TextSpan;
/**
* Abstract base class for node views.
*/
abstract class TextPaneSkinNodeView implements NodeListener {
+ protected final TextPaneSkin textPaneSkin;
private Node node = null;
private TextPaneSkinElementView parent = null;
@@ -42,7 +56,8 @@ abstract class TextPaneSkinNodeView impl
private boolean valid = false;
- public TextPaneSkinNodeView(Node node) {
+ public TextPaneSkinNodeView(TextPaneSkin textPaneSkin, Node node) {
+ this.textPaneSkin = textPaneSkin;
this.node = node;
}
@@ -59,7 +74,7 @@ abstract class TextPaneSkinNodeView impl
}
protected TextPaneSkin getTextPaneSkin() {
- return getParent().getTextPaneSkin();
+ return textPaneSkin;
}
protected void attach() {
@@ -232,4 +247,41 @@ abstract class TextPaneSkinNodeView impl
// No-op
}
+ private static HashMap<Class<? extends Node>, Class<? extends TextPaneSkinNodeView>>
+ nodeViewSkinMap = new HashMap<>();
+ static {
+ nodeViewSkinMap.put(Document.class, TextPaneSkinDocumentView.class);
+ nodeViewSkinMap.put(Paragraph.class, TextPaneSkinParagraphView.class);
+ nodeViewSkinMap.put(TextNode.class, TextPaneSkinTextNodeView.class);
+ nodeViewSkinMap.put(ImageNode.class, TextPaneSkinImageNodeView.class);
+ nodeViewSkinMap.put(ComponentNode.class, TextPaneSkinComponentNodeView.class);
+ nodeViewSkinMap.put(TextSpan.class, TextPaneSkinSpanView.class);
+ nodeViewSkinMap.put(NumberedList.class, TextPaneSkinNumberedListView.class);
+ nodeViewSkinMap.put(BulletedList.class, TextPaneSkinBulletedListView.class);
+ nodeViewSkinMap.put(List.Item.class, TextPaneSkinListItemView.class);
+ }
+
+ public static TextPaneSkinNodeView createNodeView(TextPaneSkin textPaneSkin, Node node) {
+ TextPaneSkinNodeView nodeView = null;
+
+ Class<? extends Node> nodeClass = node.getClass();
+ Class<? extends TextPaneSkinNodeView> skinClass = nodeViewSkinMap.get(nodeClass);
+ if (skinClass != null) {
+ try {
+ Constructor<?> constructor = skinClass.getConstructor(TextPaneSkin.class, nodeClass);
+ nodeView = (TextPaneSkinNodeView)constructor.newInstance(textPaneSkin, node);
+ } catch (NoSuchMethodException | InstantiationException
+ | IllegalAccessException | InvocationTargetException ex) {
+ throw new RuntimeException("Error instantiating node view for "
+ + nodeClass.getName(), ex);
+ }
+ }
+ if (nodeView == null) {
+ throw new IllegalArgumentException("Unsupported node type: "
+ + nodeClass.getName());
+ }
+
+ return nodeView;
+ }
+
}
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNumberedListView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNumberedListView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNumberedListView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinNumberedLi
+++ stView.java Tue Dec 17 20:17:48 2013
@@ -59,8 +59,8 @@ class TextPaneSkinNumberedListView exten
return (char) ('A' + n - 1) + "";
}
- public TextPaneSkinNumberedListView(NumberedList numberedList) {
- super(numberedList);
+ public TextPaneSkinNumberedListView(TextPaneSkin textPaneSkin, NumberedList numberedList) {
+ super(textPaneSkin, numberedList);
}
@Override
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinParagraphView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinParagraphView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinParagraphView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinParagraphV
+++ iew.java Tue Dec 17 20:17:48 2013
@@ -54,8 +54,8 @@ class TextPaneSkinParagraphView extends
private ArrayList<Row> rows = null;
private Bounds terminatorBounds = new Bounds(0, 0, 0, 0);
- public TextPaneSkinParagraphView(Paragraph paragraph) {
- super(paragraph);
+ public TextPaneSkinParagraphView(TextPaneSkin textPaneSkin, Paragraph paragraph) {
+ super(textPaneSkin, paragraph);
}
@Override
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinSpanView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinSpanView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinSpanView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinSpanView.j
+++ ava Tue Dec 17 20:17:48 2013
@@ -25,8 +25,8 @@ import org.apache.pivot.wtk.text.TextSpa
*/
class TextPaneSkinSpanView extends TextPaneSkinElementView {
- public TextPaneSkinSpanView(TextSpan span) {
- super(span);
+ public TextPaneSkinSpanView(TextPaneSkin textPaneSkin, TextSpan span) {
+ super(textPaneSkin, span);
}
@Override
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinTextNodeView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinTextNodeView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinTextNodeView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinTextNodeVi
+++ ew.java Tue Dec 17 20:17:48 2013
@@ -47,12 +47,12 @@ class TextPaneSkinTextNodeView extends T
private GlyphVector glyphVector = null;
private TextPaneSkinTextNodeView next = null;
- public TextPaneSkinTextNodeView(TextNode textNode) {
- this(textNode, 0);
+ public TextPaneSkinTextNodeView(TextPaneSkin textPaneSkin, TextNode textNode) {
+ this(textPaneSkin, textNode, 0);
}
- public TextPaneSkinTextNodeView(TextNode textNode, int start) {
- super(textNode);
+ public TextPaneSkinTextNodeView(TextPaneSkin textPaneSkin, TextNode textNode, int start) {
+ super(textPaneSkin, textNode);
this.start = start;
}
@@ -134,7 +134,7 @@ class TextPaneSkinTextNodeView extends T
if (end < ci.getEndIndex()) {
length = end - start;
- next = new TextPaneSkinTextNodeView(textNode, end);
+ next = new TextPaneSkinTextNodeView(getTextPaneSkin(),
+ textNode, end);
next.setParent(getParent());
} else {
length = ci.getEndIndex() - start; @@ -473,11 +473,18 @@ class TextPaneSkinTextNodeView extends T
@Override
public Bounds getCharacterBounds(int offset) {
- Shape glyphBounds = glyphVector.getGlyphLogicalBounds(offset);
+ // If the offest == length, then use the right hand edge of the previous
+ // offset instead -- this is for positioning the caret at the end of the text
+ Shape glyphBounds = glyphVector.getGlyphLogicalBounds(offset ==
+ length ? offset - 1 : offset);
Rectangle2D glyphBounds2D = glyphBounds.getBounds2D();
- return new Bounds((int) Math.floor(glyphBounds2D.getX()), 0,
- (int) Math.ceil(glyphBounds2D.getWidth()), getHeight());
+ if (offset == length) {
+ return new Bounds((int) Math.ceil(glyphBounds2D.getX() + glyphBounds2D.getWidth()), 0,
+ 1, getHeight());
+ } else {
+ return new Bounds((int) Math.floor(glyphBounds2D.getX()), 0,
+ (int) Math.ceil(glyphBounds2D.getWidth()), getHeight());
+ }
}
@Override
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinVerticalElementView.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinVerticalElementView.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinVerticalElementView.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkinVerticalEl
+++ ementView.java Tue Dec 17 20:17:48 2013
@@ -27,8 +27,8 @@ import org.apache.pivot.wtk.text.Element
*/
abstract class TextPaneSkinVerticalElementView extends TextPaneSkinElementView {
- public TextPaneSkinVerticalElementView(Element element) {
- super(element);
+ public TextPaneSkinVerticalElementView(TextPaneSkin textPaneSkin, Element element) {
+ super(textPaneSkin, element);
}
@Override
Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/text/PlainTextSerializer.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/text/PlainTextSerializer.java?rev=1551679&r1=1551678&r2=1551679&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/text/PlainTextSerializer.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/text/PlainTextSerializer.ja
+++ va Tue Dec 17 20:17:48 2013
@@ -39,6 +39,9 @@ public class PlainTextSerializer impleme
public static final String MIME_TYPE = "text/plain";
public static final int BUFFER_SIZE = 2048;
+ private boolean expandTabs = false;
+ private int tabWidth = 4;
+
public PlainTextSerializer() {
this(Charset.defaultCharset());
}
@@ -55,6 +58,35 @@ public class PlainTextSerializer impleme
this.charset = charset;
}
+ public int getTabWidth() {
+ return tabWidth;
+ }
+
+ public void setTabWidth(int tabWidth) {
+ if (tabWidth < 0) {
+ throw new IllegalArgumentException("tabWidth is negative.");
+ }
+
+ this.tabWidth = tabWidth;
+ }
+
+ public boolean getExpandTabs() {
+ return expandTabs;
+ }
+
+ /**
+ * Sets whether tab characters (<code>\t</code>) are expanded to an
+ * appropriate number of spaces while reading the text.
+ *
+ * @param expandTabs <code>true</code> to replace tab characters with space
+ * characters (depending on the setting of the {@link #getTabWidth} value)
+ * or <code>false</code> to leave tabs alone.
+ */
+ public void setExpandTabs(boolean expandTabs) {
+ this.expandTabs = expandTabs;
+ }
+
+
@Override
public Document readObject(InputStream inputStream) throws IOException {
Reader reader = new InputStreamReader(inputStream, charset); @@ -70,6 +102,18 @@ public class PlainTextSerializer impleme
String line = bufferedReader.readLine();
while (line != null) {
+ if (expandTabs) {
+ int ix = 0;
+ StringBuilder buf = new StringBuilder(line);
+ while ((ix = buf.indexOf("\t", ix)) >= 0) {
+ buf.deleteCharAt(ix);
+ int spaces = tabWidth - (ix % tabWidth);
+ for (int j = 0; j < spaces; j++) {
+ buf.insert(ix++, ' ');
+ }
+ }
+ line = buf.toString();
+ }
document.add(new Paragraph(line));
line = bufferedReader.readLine();
}