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.WingToolkit;
029:
030: /**
031: * A class representing frame containing other text elements
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 BoxNode extends TextPaneNode {
038: protected Vector childs;
039:
040: protected int alignment;
041: protected int length;
042:
043: // protected Dimension size;
044: // protected LayoutContext lastLc;
045:
046: public BoxNode(Style style) {
047: this (style, WingConst.LEFT);
048: }
049:
050: public BoxNode(Style style, int alignment) {
051: super (style);
052: childs = new Vector();
053: this .alignment = alignment;
054: length = -1;
055: }
056:
057: public boolean breaksRow() {
058: return true;
059: }
060:
061: public int length() {
062: if (length == -1) {
063: int l = 0;
064: for (int i = 0; i < childs.size(); i++) {
065: l += ((TextPaneNode) childs.elementAt(i)).length();
066: }
067: length = l + 2;
068: }
069: return length;
070: }
071:
072: protected void invalidate() {
073: length = -1;
074: super .invalidate();
075: }
076:
077: public boolean insert(int pos, TextPaneNode node) {
078: if (pos < 1 || pos >= length())
079: return false;
080:
081: int i, len = 0;
082: TextPaneNode n = null;
083: //prefix correction
084: pos -= 1;
085: for (i = 0; i < childs.size(); i++) {
086: n = (TextPaneNode) childs.elementAt(i);
087: len = n.length();
088: if (pos <= len)
089: break;
090: pos -= len;
091: }
092: if (n == null) {
093: insertNode(node, 0);
094: } else if (!n.insert(pos, node)) {
095: if (pos == 0)
096: insertNode(node, 0);
097: else {
098: TextPaneNode n2 = null;
099: if (i + 1 < childs.size()) {
100: n2 = (TextPaneNode) childs.elementAt(i + 1);
101: if (n2 == null || !n2.insert(0, node)) {
102: insertNode(node, i + 1);
103: }
104: } else
105: insertNode(node, childs.size());
106: }
107: }
108: return true;
109: }
110:
111: protected void insertNode(TextPaneNode node, int index) {
112: node.parent = this ;
113: childs.insertElementAt(node, index);
114: node.invalidate();
115: }
116:
117: public void replaceNode(TextPaneNode node, TextPaneNode[] r) {
118: for (int i = 0; i < childs.size(); i++) {
119: if (childs.elementAt(i) == node) {
120: node.parent = null;
121: childs.removeElementAt(i);
122: for (int j = 0; j < r.length; j++) {
123: childs.insertElementAt(r[j], i + j);
124: r[j].parent = this ;
125: r[j].invalidate();
126: }
127: return;
128: }
129: }
130: }
131:
132: public String getText(int start, int end) {
133: StringBuffer r = new StringBuffer();
134:
135: int i, len;
136: TextPaneNode n;
137: // prefix correction
138: start -= 1;
139: end -= 1;
140:
141: for (i = 0; i < childs.size() && end > 0; i++) {
142: n = (TextPaneNode) childs.elementAt(i);
143: len = n.length();
144: if (start < len) {
145: r.append(n.getText(start, end));
146: }
147: start -= len;
148: end -= len;
149: }
150: if (end > 0)
151: r.append("\n");
152: return r.toString();
153: }
154:
155: public void remove(int start, int end) {
156: int i, len;
157: TextPaneNode n;
158: boolean removeThis = (start <= 0 || end >= length());
159: // prefix correction
160: start -= 1;
161: end -= 1;
162: for (i = 0; i < childs.size() && end > 0; i++) {
163: n = (TextPaneNode) childs.elementAt(i);
164: len = n.length();
165: if (start <= 0 && end >= len) {
166: n.parent = null;
167: childs.removeElementAt(i--);
168: } else if (start < len) {
169: n.remove(start, end);
170: }
171: start -= len;
172: end -= len;
173: }
174: invalidate();
175: if (removeThis) {
176: TextPaneNode[] r = new TextPaneNode[childs.size()];
177: childs.copyInto(r);
178: parent.replaceNode(this , r);
179: }
180: }
181:
182: public void layout(LayoutContext lc) {
183: // if(!valid || lastLc==null
184: // || lc.prefWidth!=lastLc.prefWidth || lc.wrap!=lastLc.wrap
185: // || lc.dx!=lastLc.dx || lc.dy!=lastLc.dy)
186: {
187: Style s = getStyle();
188: int prefWidth = lc.prefWidth - s.margin.left
189: - s.margin.right;
190: LayoutContext myLc = null;
191: if (!lc.fixed) {
192: myLc = new LayoutContext(prefWidth, s.margin.left,
193: s.margin.top, alignment, false, lc.wrap,
194: lc.revalidate);
195: for (int i = 0; i < childs.size(); i++) {
196: ((TextPaneNode) childs.elementAt(i)).layout(myLc);
197: }
198: myLc.endRow();
199: if (myLc.realWidth != myLc.prefWidth) {
200: prefWidth = myLc.realWidth;
201: }
202: }
203: myLc = new LayoutContext(prefWidth, s.margin.left,
204: s.margin.top, alignment, true, lc.wrap,
205: lc.revalidate);
206: for (int i = 0; i < childs.size(); i++) {
207: ((TextPaneNode) childs.elementAt(i)).layout(myLc);
208: }
209: myLc.endRow();
210:
211: int lastLine = 0;
212: if (childs.size() == 0
213: || ((TextPaneNode) childs.lastElement())
214: .breaksRow()) {
215: lastLine = getFont().getHeight();
216: }
217: if (!lc.fixed || myLc.realWidth > myLc.prefWidth) {
218: bounds.width = myLc.realWidth + s.margin.left
219: + s.margin.right;
220: } else
221: bounds.width = lc.prefWidth;
222: // bounds.width= myLc.realWidth+s.margin.left+s.margin.right;
223: // if(lc.fixed && bounds.width<lc.prefWidth)
224: // bounds.width= lc.prefWidth;
225: bounds.height = myLc.realHeight + lastLine + s.margin.top
226: + s.margin.bottom;
227:
228: lc.endRow();
229: lc.add(this , true);
230: lc.endRow();
231:
232: valid = true;
233: }
234: }
235:
236: public void paint(Graphics g, int x, int y, int selectionStart,
237: int selectionEnd, Style stSelected) {
238: x += this .bounds.x;
239: y += this .bounds.y;
240: Rectangle cb = g.getClipBounds();
241: if (cb.x > x + bounds.width || cb.y > y + bounds.height
242: || cb.x + cb.width < x || cb.y + cb.height < y) {
243: return;
244: }
245: Style st = (selectionStart <= 0 && selectionEnd >= length()) ? stSelected
246: : style;
247: if (st != null) {
248: WingToolkit.drawBackground(g, bounds.x, bounds.y,
249: bounds.width, bounds.height, st, getOwner());
250: }
251:
252: //prefix correction
253: selectionStart -= 1;
254: selectionEnd -= 1;
255:
256: for (int i = 0; i < childs.size(); i++) {
257: TextPaneNode n = (TextPaneNode) childs.elementAt(i);
258: n.paint(g, x, y, selectionStart, selectionEnd, stSelected);
259: selectionStart -= n.length();
260: selectionEnd -= n.length();
261: }
262: }
263:
264: public Rectangle getCharBounds(int pos) {
265: if (!valid)
266: return null;
267:
268: if (pos == length() - 1) {
269: if (childs.size() == 0
270: || ((TextPaneNode) childs.lastElement())
271: .breaksRow()) {
272: int h = getFont().getHeight();
273:
274: Style s = getStyle();
275: return new Rectangle(bounds.x + s.margin.left, bounds.y
276: + bounds.height - s.margin.bottom - h, 1, h);
277: } else {
278: Rectangle r = getCharBounds(pos - 1);
279: r.x += r.width - 1;
280: r.width = 1;
281: return r;
282: }
283: } else if (pos == 0) {
284: return new Rectangle(bounds.x, bounds.y, 1, bounds.height);
285: } else {
286: //prefix correction
287: pos -= 1;
288: for (int i = 0; i < childs.size(); i++) {
289: TextPaneNode n = (TextPaneNode) childs.elementAt(i);
290: if (pos < n.length()) {
291: Rectangle r = n.getCharBounds(pos);
292: if (r != null) {
293: r.x += bounds.x;
294: r.y += bounds.y;
295: }
296: return r;
297: }
298: pos -= n.length();
299: }
300: }
301: return null;
302: }
303:
304: public int posAtPoint(int x, int y) {
305: if (!bounds.contains(x, y) || !valid)
306: return -1;
307:
308: y -= bounds.y;
309: x -= bounds.x;
310: //prefix correction
311: int pos, dpos = 1;
312: TextPaneNode prev = null;
313: for (int i = 0; i < childs.size(); i++) {
314: TextPaneNode node = (TextPaneNode) childs.elementAt(i);
315: if (node.bounds.y > y && prev != null
316: && prev.bounds.y < node.bounds.y) {
317: pos = prev.posAtPoint(nearestX(prev.bounds, x),
318: nearestY(prev.bounds, y));
319: if (pos != -1)
320: return pos + dpos - prev.length();
321: }
322: if (node.bounds.y + node.bounds.height > y) {
323: if (node.bounds.x + node.bounds.width > x) {
324: pos = node.posAtPoint(nearestX(node.bounds, x),
325: nearestY(node.bounds, y));
326: if (pos != -1)
327: return pos + dpos;
328: }
329: }
330: dpos += node.length();
331: prev = node;
332: }
333: if (prev != null) {
334: pos = prev.posAtPoint(nearestX(prev.bounds, x), (prev
335: .breaksRow()) ? y : nearestY(prev.bounds, y));
336: if (pos != -1)
337: return pos + dpos - prev.length();
338: }
339: return dpos;
340: }
341:
342: public TextPaneNode nodeAt(int pos, boolean prefTextNode) {
343: if (pos == 0)
344: return this ;
345: //prefix correction
346: pos -= 1;
347: for (int i = 0; i < childs.size(); i++) {
348: TextPaneNode n = (TextPaneNode) childs.elementAt(i);
349: if (pos < n.length()) {
350: if (prefTextNode
351: && pos == 0
352: && i > 0
353: && !(n instanceof TextNode)
354: && (childs.elementAt(i - 1) instanceof TextNode)) {
355: return (TextPaneNode) childs.elementAt(i - 1);
356: }
357: return n.nodeAt(pos, prefTextNode);
358: }
359: pos -= n.length();
360: }
361: return this;
362: }
363:
364: }
|