001: /*
002: * Javu WingS - Lightweight Java Component Set
003: * Copyright (c) 2005-2007 Krzysztof A. Sadlocha
004: * e-mail: ksadlocha@programics.com
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: */
020:
021: package com.javujavu.javux.wings.text;
022:
023: import java.awt.Graphics;
024: import java.awt.Rectangle;
025: import java.util.Vector;
026: import com.javujavu.javux.wings.Style;
027: import com.javujavu.javux.wings.WingConst;
028: import com.javujavu.javux.wings.WingFont;
029:
030: /**
031: * A class displaying multiline text with a specified <code>Style</code>
032: * in <code>WingTextPane</code>
033: * <br><br>
034: * <b>This class is not thread safe
035: * synchronization is provided by WingTextPane</b>
036: */
037: public class TextNode extends TextPaneNode {
038: public String text;
039: protected int[] textWidth;
040: protected Vector cells;
041: // protected Dimension size;
042: private int lineHeight;
043: protected int ascent;
044: private boolean layFirst;
045:
046: public TextNode(String text, Style style) {
047: super (style);
048: this .text = text;
049: cells = new Vector();
050: }
051:
052: public int length() {
053: return text.length();
054: }
055:
056: public void setText(String text) {
057: this .text = text;
058: invalidate();
059: }
060:
061: public boolean insert(int pos, TextPaneNode n) {
062: if (pos >= 0 && pos <= length()
063: && (n instanceof TextNode && n.style == this .style)) {
064: StringBuffer t = new StringBuffer();
065: if (pos > 0)
066: t.append(text.substring(0, pos));
067: t.append(((TextNode) n).text);
068: if (pos < text.length())
069: t.append(text.substring(pos));
070: setText(t.toString());
071: return true;
072: } else if (pos > 0 && pos < length()) {
073: // split this node
074: TextNode s2 = new TextNode(text.substring(pos), style);
075: setText(text.substring(0, pos));
076: TextPaneNode[] r = new TextPaneNode[3];
077: r[0] = this ;
078: r[1] = n;
079: r[2] = s2;
080: parent.replaceNode(this , r);
081: return true;
082: }
083: return false;
084: }
085:
086: public void remove(int start, int end) {
087: StringBuffer t = new StringBuffer();
088: if (start > 0)
089: t.append(text.substring(0, start));
090: if (end < text.length())
091: t.append(text.substring(end));
092: setText(t.toString());
093: }
094:
095: public String getText(int start, int end) {
096: if (start < 0)
097: start = 0;
098: if (end > text.length())
099: end = text.length();
100: return text.substring(start, end);
101: }
102:
103: protected void invalidate() {
104: textWidth = null;
105: super .invalidate();
106: }
107:
108: public void layout(LayoutContext lc) {
109: if (textWidth == null || lc.revalidate) {
110: textWidth = new int[text.length()];
111: int w = 0;
112: WingFont font = getFont();
113: char[] ca = text.toCharArray();
114: for (int i = 0; i < text.length(); i++) {
115: w += textWidth[i] = font.charsWidth(ca, i, 1);
116: }
117: ascent = font.getAscent();
118: lineHeight = font.getHeight();
119: }
120:
121: cells.removeAllElements();
122:
123: this .layFirst = true;
124:
125: int j = 0, i = 0, w = 0, lastBreak = 0, lastBreakWidth = 0, available, prefWidth = lc.prefWidth, textLength = text
126: .length();
127:
128: available = prefWidth - lc.width;
129: while (i < textLength) {
130: while (i < textLength) {
131: if (i > j && text.charAt(i) == ' ') {
132: lastBreak = i - 1;
133: lastBreakWidth = w;
134: }
135: if (w + textWidth[i] > available
136: && i > j
137: && (lc.wrap == WingConst.LINE_WRAP || (lc.wrap == WingConst.WORD_WRAP && (lastBreak > 0 || available != prefWidth)))) {
138: break;
139: }
140: w += textWidth[i];
141: if (text.charAt(i) == ' ') {
142: lastBreak = i;
143: lastBreakWidth = w;
144: }
145: i++;
146: }
147: if (available != prefWidth && lastBreak == 0
148: && i < textLength) {
149: //skip first incomplete row
150: lc.endRow();
151: available = prefWidth;
152: } else {
153: if (lastBreak > 0 && i < textLength) {
154: i = lastBreak + 1;
155: w = lastBreakWidth;
156: }
157:
158: TextNodeCell sc = new TextNodeCell(this , j, i, w,
159: lineHeight);
160: cells.addElement(sc);
161: lc.add(sc, true);
162: if (i < textLength)
163: lc.endRow();
164: j = i;
165: w = lastBreak = lastBreakWidth = 0;
166: }
167: }
168: valid = true;
169: }
170:
171: public void paint(Graphics g, int x, int y, int selectionStart,
172: int selectionEnd, Style stSelected) {
173: // g.setColor(Color.gray);
174: // g.drawRect(x+bounds.x, y+bounds.y, bounds.width, bounds.height);
175: for (int i = 0; i < cells.size(); i++) {
176: TextNodeCell n = (TextNodeCell) cells.elementAt(i);
177: n.paint(g, x, y, selectionStart, selectionEnd, stSelected);
178: selectionStart -= n.length();
179: selectionEnd -= n.length();
180: }
181: }
182:
183: public Rectangle getCharBounds(int pos) {
184: // if(pos>=length()) return null;
185: if (!valid)
186: return null;
187: for (int i = 0; i < cells.size(); i++) {
188: TextNodeCell sc = (TextNodeCell) cells.elementAt(i);
189: if (pos < sc.length())
190: return sc.getCharBounds(pos);
191: pos -= sc.length();
192: }
193: return null;
194: }
195:
196: public void unionBounds(Rectangle b) {
197: if (layFirst)
198: this .bounds.setBounds(b);
199: else {
200: int x0 = (bounds.x < b.x) ? bounds.x : b.x;
201: int y0 = (bounds.y < b.y) ? bounds.y : b.y;
202: int xe = bounds.x + bounds.width;
203: if (xe < b.x + b.width)
204: xe = b.x + b.width;
205: int ye = bounds.y + bounds.height;
206: if (ye < b.y + b.height)
207: ye = b.y + b.height;
208: bounds.setBounds(x0, y0, xe - x0, ye - y0);
209: }
210: layFirst = false;
211: }
212:
213: public int posAtPoint(int x, int y) {
214: if (!bounds.contains(x, y) || !valid)
215: return -1;
216:
217: int pos, dpos = 0;
218: TextPaneNode p = null;
219: for (int i = 0; i < cells.size(); i++) {
220: TextPaneNode n = (TextPaneNode) cells.elementAt(i);
221: if (n.bounds.y > y && p != null) {
222: pos = p.posAtPoint(nearestX(p.bounds, x), nearestY(
223: p.bounds, y));
224: return ((pos != -1) ? (pos - p.length()) : 0) + dpos
225: - 1;
226: }
227: if (n.bounds.y + n.bounds.height > y) {
228: if (n.bounds.x + n.bounds.width > x) {
229: pos = n.posAtPoint(nearestX(n.bounds, x), nearestY(
230: n.bounds, y));
231: if (pos != -1)
232: return pos + dpos;
233: }
234: }
235: dpos += n.length();
236: p = n;
237: }
238: return -1;
239: }
240:
241: }
|