001: /**
002: * Caption: Zaval Java Resource Editor
003: * $Revision: 0.37 $
004: * $Date: 2002/03/28 9:24:42 $
005: *
006: * @author: Victor Krapivin
007: * @version: 2.0
008: *
009: * Zaval JRC Editor is a visual editor which allows you to manipulate
010: * localization strings for all Java based software with appropriate
011: * support embedded.
012: *
013: * For more info on this product read Zaval Java Resource Editor User's Guide
014: * (It comes within this package).
015: * The latest product version is always available from the product's homepage:
016: * http://www.zaval.org/products/jrc-editor/
017: * and from the SourceForge:
018: * http://sourceforge.net/projects/zaval0002/
019: *
020: * Contacts:
021: * Support : support@zaval.org
022: * Change Requests : change-request@zaval.org
023: * Feedback : feedback@zaval.org
024: * Other : info@zaval.org
025: *
026: * Copyright (C) 2001-2002 Zaval Creative Engineering Group (http://www.zaval.org)
027: *
028: * This program is free software; you can redistribute it and/or
029: * modify it under the terms of the GNU General Public License
030: * (version 2) as published by the Free Software Foundation.
031: *
032: * This program is distributed in the hope that it will be useful,
033: * but WITHOUT ANY WARRANTY; without even the implied warranty of
034: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
035: * GNU General Public License for more details.
036: *
037: * You should have received a copy of the GNU General Public License
038: * along with this program; if not, write to the Free Software
039: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
040: *
041: */package org.zaval.awt;
042:
043: import java.awt.*;
044: import java.awt.event.*;
045: import java.util.*;
046: import org.zaval.awt.ScrollObject;
047:
048: //import org.zaval.awt.ScrollPanel;
049:
050: public class EmulatedTextArea extends EmulatedTextField implements
051: ScrollObject {
052: boolean wordWrap = false;
053: int[] lineStart = new int[LINE_INCR];
054: Vector lineText = new Vector();
055: int maxTextWidth = 0;
056: int rowsNumber = 0; // = 0 - preferredSize.height is determined by the actual number of rows
057: int prefWidth = 0; // = 0 - preferredSize.width is determined by it if it is assigned a positive value
058: int upRowNum = 0;
059: int baseTextIndent = 0;
060: int viewWidth = 0;
061: int viewHeight = 0;
062: Image internImg = null;
063: int textWidth = 0;
064: int textHeight = 0;
065: int lastVisLine = 0;
066: boolean addLineFeed = true;
067: boolean noFontMetric = false;
068:
069: public static final int LINE_INCR = 20;
070:
071: public EmulatedTextArea() {
072: super ();
073: lineText.addElement("");
074: }
075:
076: public EmulatedTextArea(boolean ww, boolean lf, int rN, int pW) {
077: this ();
078: wordWrap = ww;
079: rowsNumber = rN;
080: prefWidth = pW;
081: addLineFeed = lf;
082: }
083:
084: public void setLineFeed(boolean b) {
085: addLineFeed = b;
086: }
087:
088: public void setWordWrap(boolean b) {
089: wordWrap = b;
090: }
091:
092: public void setPrefWidth(int p) {
093: prefWidth = p;
094: }
095:
096: public void setRowsNumber(int n) {
097: rowsNumber = n;
098: }
099:
100: public void setText(String s) {
101: super .setText(s);
102: recalcLines(0);
103: }
104:
105: public void focusGained(FocusEvent e) {
106: int wasCP = cursorPos;
107: super .focusGained(e);
108: selPos = 0;
109: selWidth = 0;
110: cursorPos = wasCP;
111: }
112:
113: protected boolean controlKey(int key, boolean shift) {
114: switch (key) {
115: case KeyEvent.VK_DOWN:
116: seek(vertPosShift(cursorPos, 1) - cursorPos, shift);
117: break;
118: case KeyEvent.VK_UP:
119: seek(vertPosShift(cursorPos, -1) - cursorPos, shift);
120: break;
121: case KeyEvent.VK_HOME:
122: seek(lineStart[lineFromPos(cursorPos)] - cursorPos, shift);
123: break;
124: case KeyEvent.VK_END:
125: int ln = lineFromPos(cursorPos);
126: int newPos = buffer.toString().length();
127: if (ln < lineText.size() - 1)
128: newPos = adjustPos(lineStart[ln + 1] - 1, false);
129: seek(newPos - cursorPos, shift);
130: break;
131: case KeyEvent.VK_ENTER:
132: return false;
133: case KeyEvent.VK_PAGE_UP:
134: upRowNum -= lastVisLine;
135: if (upRowNum < 0)
136: upRowNum = 0;
137: seek(vertPosShift(cursorPos, -lastVisLine) - cursorPos,
138: shift);
139: break;
140: case KeyEvent.VK_PAGE_DOWN:
141: upRowNum += lastVisLine;
142: if (upRowNum + lastVisLine >= lineText.size())
143: upRowNum = lineText.size() - lastVisLine - 1;
144: seek(vertPosShift(cursorPos, lastVisLine) - cursorPos,
145: shift);
146: break;
147: default:
148: return super .controlKey(key, shift);
149: }
150: if (!shift)
151: clear();
152: return true;
153: }
154:
155: protected boolean write(char key) {
156: super .write(key);
157: if (addLineFeed && key == '\n')
158: super .write('\r');
159: recalcLines(cursorPos);
160: return true;
161: }
162:
163: protected String filterSymbols(String s) {
164: return s;
165: }
166:
167: protected void repaintPart() {
168: repaint();
169: }
170:
171: protected void remove(int pos, int size) {
172: if (pos + size > buffer.length())
173: size = buffer.length() - pos;
174: if (pos > buffer.length() || size <= 0)
175: return;
176: if (pos > 0 && buffer.charAt(pos) == '\n'
177: && buffer.charAt(pos - 1) == '\r') {
178: pos--;
179: size++;
180: }
181: if (pos + size < buffer.length()
182: && buffer.charAt(pos + size - 1) == '\r'
183: && buffer.charAt(pos + size) == '\n')
184: size++;
185: super .remove(pos, size);
186: recalcLines(pos);
187: }
188:
189: public void insert(int pos, String str) {
190: super .insert(pos, str);
191: recalcLines(pos);
192: }
193:
194: public void update(Graphics g) {
195: paint(g);
196: }
197:
198: public void paint(Graphics g) {
199: Dimension d = size();
200: if ((d.width != viewWidth) || (d.height != viewHeight)
201: || internImg == null) {
202: if (d.width * d.height <= 0)
203: return;
204: internImg = createImage(d.width, d.height);
205: viewWidth = d.width;
206: viewHeight = d.height;
207: }
208: Graphics internGr = internImg.getGraphics();
209: recalc();
210: internGr.clearRect(0, 0, d.width, d.height);
211: drawBorder(internGr);
212: internGr.clipRect(insets.left, insets.top, d.width
213: - insets.left - insets.right + 1, d.height - insets.top
214: - insets.bottom);
215: drawCursor(internGr);
216: drawText(internGr);
217: drawBlock(internGr);
218: g.drawImage(internImg, 0, 0, this );
219: internGr.dispose();
220: }
221:
222: protected void drawBlock(Graphics g) {
223: if (!isSelected())
224: return;
225: FontMetrics fm = getFontMetrics(getFont());
226: int l1 = lineFromPos(selPos);
227: int l2 = lineFromPos(selPos + selWidth);
228: for (int i = l1; i <= l2; i++) {
229: String s = (String) lineText.elementAt(i);
230: int beg = 0;
231: int begPos = 0;
232: if (i == l1) {
233: begPos = selPos - lineStart[i];
234: beg = fm.stringWidth(s.substring(0, begPos));
235: }
236: int end = fm.stringWidth(s);
237: int endPos = s.length();
238: if (i == l2) {
239: endPos = selPos + selWidth - lineStart[i];
240: end = fm.stringWidth(s.substring(0, endPos));
241: }
242: g.setColor(Color.blue);
243: g.fillRect(textLocation.x + shift.x + beg, insets.top
244: + (i - upRowNum) * fm.getHeight(), end - beg,
245: textSize.height);
246: g.setColor(Color.white);
247: g.drawString(s.substring(begPos, endPos), insets.left + beg
248: + shift.x, insets.top + baseTextIndent
249: + (i - upRowNum) * fm.getHeight());
250: }
251: }
252:
253: protected void drawText(Graphics g) {
254: Dimension d = size();
255: g.setColor(getForeground());
256: for (int i = upRowNum; i < lineText.size(); i++)
257: g.drawString((String) lineText.elementAt(i), insets.left
258: + shift.x, insets.top + baseTextIndent
259: + (i - upRowNum) * textSize.height);
260: }
261:
262: public Dimension preferredSize() {
263: int w = 0, h = 0;
264: int rows = 1;
265:
266: Font f = getFont();
267: if (f == null)
268: return new Dimension(0, 0);
269: FontMetrics m = getFontMetrics(f);
270: if (m == null) {
271: Toolkit k = Toolkit.getDefaultToolkit();
272: m = k.getFontMetrics(f);
273: if (m == null)
274: return new Dimension(0, 0);
275: }
276:
277: String text = getText();
278: for (int j = 0; j < text.length(); ++j)
279: if (text.charAt(j) == '\n')
280: ++rows;
281: StringTokenizer st = new StringTokenizer(text, "\n");
282: h = m.getHeight() * (rows + 1);
283: while (st.hasMoreTokens()) {
284: String s = st.nextToken();
285: w = Math.max(w, m.stringWidth(s));
286: }
287: Insets i = insets();
288: Dimension ask = new Dimension(i.left + i.right + w, i.top
289: + i.bottom + h);
290: return ask;
291: }
292:
293: public Point getSOLocation() {
294: return new Point(-shift.x, upRowNum * textSize.height);
295: }
296:
297: public void setSOLocation(int x, int y) {
298: shift.x = -x;
299: if (textSize.height > 0)
300: upRowNum = y / textSize.height;
301: repaint();
302: }
303:
304: public Dimension getSOSize() {
305: return new Dimension(maxTextWidth + insets.left + insets.right,
306: lineText.size() * textSize.height + insets.top
307: + insets.bottom);
308: }
309:
310: public Component getScrollComponent() {
311: return this ;
312: }
313:
314: protected void setLineStart(int pos, int value) {
315: if (pos >= lineStart.length) {
316: int[] nls = new int[(pos / LINE_INCR + 1) * LINE_INCR];
317: System.arraycopy(lineStart, 0, nls, 0, lineStart.length);
318: lineStart = nls;
319: }
320: lineStart[pos] = value;
321: }
322:
323: protected int indexOfBlank(String s, int i) {
324: int i1 = s.indexOf(' ', i);
325: if (i1 < 0)
326: i1 = s.length() - 1;
327: int i2 = s.indexOf('\t', i);
328: if (i2 < 0)
329: i2 = s.length() - 1;
330: return Math.min(i1, i2);
331: }
332:
333: protected void recalcLines(int position) {
334: int rowNum = lineFromPos(position);
335: int oldLN = lineText.size();
336: int oldMW = maxTextWidth;
337: Dimension d = size();
338: Insets i = insets();
339: textWidth = d.width - i.left - i.right;
340: textHeight = d.height - i.top - i.bottom;
341: if (textWidth <= 0 || textHeight <= 0)
342: return;
343: noFontMetric = true;
344: Font f = getFont();
345: if (f == null)
346: return;
347: FontMetrics m = getFontMetrics(f);
348: if (m == null)
349: return;
350: noFontMetric = false;
351: if (rowNum > lineText.size())
352: return;
353: String allText = buffer.toString();
354: int currLine = rowNum;
355: for (int j = lineText.size() - 1; j >= currLine; j--)
356: lineText.removeElementAt(j);
357: setLineStart(0, 0);
358: int currPos = lineStart[currLine];
359: int ind;
360: do {
361: ind = allText.indexOf('\n', currPos);
362: int startNext = ind + 1;
363: if (ind < 0)
364: ind = allText.length();
365: if (ind > 0 && allText.charAt(ind - 1) == '\r')
366: ind--;
367: String sl = allText.substring(currPos, ind);
368: if (wordWrap && m.stringWidth(sl) > textWidth) {
369: int blankInd = indexOfBlank(allText, currPos);
370: if (blankInd < ind) {
371: ind = blankInd + 1;
372: sl = allText.substring(currPos, ind);
373: String tempSl = sl;
374: while (m.stringWidth(tempSl) < textWidth) {
375: sl = tempSl;
376: ind = blankInd + 1;
377: blankInd = indexOfBlank(allText, ind);
378: tempSl = allText.substring(currPos,
379: blankInd + 1);
380: }
381: startNext = ind;
382: }
383: }
384: lineText.addElement(sl);
385: setLineStart(currLine, currPos);
386: currPos = startNext;
387: currLine++;
388: } while (ind < allText.length());
389: maxTextWidth = 0;
390: for (int j = 0; j < lineText.size(); j++) {
391: int len = m.stringWidth((String) lineText.elementAt(j));
392: if (maxTextWidth < len)
393: maxTextWidth = len;
394: }
395: /* if ( getParent() != null && ( oldLN != lineText.size() || maxTextWidth != oldMW ) )
396: {
397: Event e = new Event(this, ScrollPanel.RECALC_LAYOUT, this);
398: getParent().postEvent(e);
399: }
400: */}
401:
402: protected void setPos(int p) {
403: super .setPos(adjustPos(p, true));
404: }
405:
406: public void select(int pos, int w) {
407: int ap = adjustPos(pos, true);
408: int aw = adjustPos(pos + w, true) - ap;
409: super .select(ap, aw);
410: }
411:
412: protected boolean seek(int shift, boolean b) {
413: return super .seek(adjustPos(cursorPos + shift, shift > 0)
414: - cursorPos, b);
415: }
416:
417: protected int adjustPos(int pos, boolean incr) {
418: int l = lineFromPos(pos);
419: int sl = ((String) lineText.elementAt(l)).length();
420: if (l < lineText.size() - 1 && pos - lineStart[l] > sl)
421: if (incr)
422: return lineStart[l + 1];
423: else
424: return lineStart[l] + sl;
425: return pos;
426: }
427:
428: protected boolean recalc() {
429: int wasShiftX = shift.x;
430: if (noFontMetric)
431: recalcLines(0);
432: boolean res = super .recalc();
433: shift.x = wasShiftX;
434: int l = lineFromPos(cursorPos);
435: String s = ((String) lineText.elementAt(l));
436: s = s.substring(0, cursorPos - lineStart[l]);
437: FontMetrics m = getFontMetrics(getFont());
438: shift.y = 0;
439: baseTextIndent = m.getHeight() - m.getDescent();
440: int cursLine = lineFromPos(cursorPos);
441: if (cursLine < upRowNum)
442: upRowNum = cursLine;
443: lastVisLine = textHeight / m.getHeight() - 1;
444: if (lastVisLine < 0)
445: lastVisLine = 0;
446: if (cursLine > lastVisLine + upRowNum)
447: upRowNum = cursLine - lastVisLine;
448: cursorLocation.x = insets.left + m.stringWidth(s);
449: cursorLocation.y = insets.top + (l - upRowNum)
450: * textSize.height;
451: if ((cursorLocation.x + shift.x) < insets.left)
452: shift.x = insets.left - cursorLocation.x;
453: else {
454: int w = size().width - insets.right;
455: if ((cursorLocation.x + shift.x) > w)
456: shift.x = w - cursorLocation.x;
457: }
458: /* if ( getParent() != null )
459: {
460: Event e = new Event(this, Event.SCROLL_ABSOLUTE, this);
461: getParent().postEvent(e);
462: }
463: */return res;
464: }
465:
466: protected int getLinePos(int ln, FontMetrics fm, int pix) {
467: if (ln < 0)
468: ln = 0;
469: if (ln >= lineText.size())
470: ln = lineText.size() - 1;
471: String s = (String) lineText.elementAt(ln);
472: for (int i = 0; i < s.length(); i++)
473: if (fm.stringWidth(s.substring(0, i)) > pix)
474: return lineStart[ln] + i - 1;
475: int res = lineStart[ln] + s.length();
476: if (pix > 0 && ln < lineText.size() - 1
477: && buffer.charAt(lineStart[ln + 1] - 1) != '\n')
478: res--;
479: return res;
480: }
481:
482: protected int calcTextPos(int x, int y) {
483: FontMetrics fm = getFontMetrics(getFont());
484: return getLinePos((y - insets.top) / fm.getHeight() + upRowNum,
485: fm, x - insets.left - shift.x);
486: }
487:
488: protected int vertPosShift(int currPos, int vertShift) {
489: int currLine = lineFromPos(currPos);
490: FontMetrics fm = getFontMetrics(getFont());
491: int pixW = fm.stringWidth(((String) lineText
492: .elementAt(currLine)).substring(0, currPos
493: - lineStart[currLine]));
494: return getLinePos(currLine + vertShift, fm, pixW);
495: }
496:
497: protected int lineFromPos(int pos) {
498: for (int i = lineText.size() - 1; i >= 0; i--)
499: if (lineStart[i] <= pos)
500: return i;
501: return 0;
502: }
503:
504: public void resize(int w, int h) {
505: super .resize(w, h);
506: recalcLines(0);
507: }
508:
509: public void reshape(int x, int y, int w, int h) {
510: Dimension d = size();
511: super .reshape(x, y, w, h);
512: if (d.width != w)
513: recalcLines(0);
514: }
515:
516: }
|