Hi Roger, no problem for me, proceed as you prefer ... Bye Il 17/dic/2013 21:33 "Roger L. Whitcomb" <roger.whitc...@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: rwhitc...@apache.org [mailto:rwhitc...@apache.org] > Sent: Tuesday, December 17, 2013 12:18 PM > To: comm...@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(); > } > > > >