001: /*
002: * Copyright 1995-2003 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.awt.motif;
027:
028: import java.awt.*;
029: import java.awt.peer.*;
030: import java.awt.event.TextEvent;
031: import java.awt.event.MouseEvent;
032: import java.awt.event.MouseWheelEvent;
033: import java.awt.datatransfer.*;
034: import java.io.BufferedReader;
035: import java.io.StringReader;
036: import java.io.IOException;
037: import java.util.Vector;
038: import java.awt.im.InputMethodRequests;
039:
040: public class MTextAreaPeer extends MComponentPeer implements
041: TextAreaPeer {
042: native void pCreate(MComponentPeer parent);
043:
044: private boolean firstChangeSkipped;
045:
046: /**
047: * Initialize JNI field and method IDs
048: */
049: private static native void initIDs();
050:
051: static {
052: initIDs();
053: }
054:
055: void create(MComponentPeer parent) {
056: firstChangeSkipped = false;
057: pCreate(parent);
058: }
059:
060: void initialize() {
061: int start, end;
062:
063: TextArea txt = (TextArea) target;
064: String text;
065:
066: if ((text = txt.getText()) != null) {
067: setText(text);
068: }
069:
070: start = txt.getSelectionStart();
071: end = txt.getSelectionEnd();
072:
073: if (end > start) {
074: select(start, end);
075: } else {
076: setCaretPosition(start);
077: }
078:
079: super .pSetScrollbarBackground(getParent_NoClientCode(target)
080: .getBackground());
081:
082: if (!target.isBackgroundSet()) {
083: // This is a way to set the background color of the TextArea
084: // without calling setBackground - go through native C code
085: setTargetBackground(SystemColor.text);
086: }
087: if (!target.isForegroundSet()) {
088: target.setForeground(SystemColor.textText);
089: }
090:
091: setEditable(txt.isEditable());
092:
093: // oldSelectionStart = -1; // accessibility support
094: // oldSelectionEnd = -1; // accessibility support
095:
096: super .initialize();
097: }
098:
099: public MTextAreaPeer(TextArea target) {
100: super (target);
101: }
102:
103: public void setEditable(boolean editable) {
104: pSetEditable(editable);
105:
106: /* 4136955 - Calling setBackground() here works around an Xt
107: * bug by forcing Xt to flush an internal widget cache
108: */
109: setBackground(target.getBackground());
110: }
111:
112: public void setBackground(Color c) {
113: setTextBackground(c);
114: }
115:
116: public void setForeground(Color c) {
117: pSetInnerForeground(c);
118: }
119:
120: native int getExtraWidth();
121:
122: native int getExtraHeight();
123:
124: public native void setTextBackground(Color c);
125:
126: public native void pSetEditable(boolean e);
127:
128: public native void select(int selStart, int selEnd);
129:
130: public native int getSelectionStart();
131:
132: public native int getSelectionEnd();
133:
134: public native void setText(String txt);
135:
136: public native String getText();
137:
138: public native void insert(String txt, int pos);
139:
140: public native void replaceRange(String txt, int start, int end);
141:
142: public native void setFont(Font f);
143:
144: public native void setCaretPosition(int pos);
145:
146: public native int getCaretPosition();
147:
148: public native void pSetCursor(Cursor c);
149:
150: native void pShow2();
151:
152: native void pMakeCursorVisible();
153:
154: public Dimension getMinimumSize() {
155: return getMinimumSize(10, 60);
156: }
157:
158: public Dimension getPreferredSize(int rows, int cols) {
159: return getMinimumSize(rows, cols);
160: }
161:
162: public Dimension getMinimumSize(int rows, int cols) {
163: FontMetrics fm = getFontMetrics(target.getFont());
164:
165: /* Calculate proper size for text area plus scrollbars.
166: * - Motif allows NO leading in its text areas ...
167: * - extra width and height counts everything outside the
168: * usable text space.
169: * (bug 4103248, 4120310):
170: * - Motif uses maxAscent + maxDescent, not ascent + descent.
171: */
172: int colWidth = fm.charWidth('0');
173: int rowHeight = fm.getMaxAscent() + fm.getMaxDescent();
174: return new Dimension(cols * colWidth + getExtraWidth(), rows
175: * rowHeight + getExtraHeight());
176: }
177:
178: public boolean isFocusable() {
179: return true;
180: }
181:
182: // Called from native widget when paste key is pressed and we
183: // already own the selection (prevents Motif from hanging while
184: // waiting for the selection)
185: //
186: public void pasteFromClipboard() {
187: Clipboard clipboard = target.getToolkit().getSystemClipboard();
188:
189: Transferable content = clipboard.getContents(this );
190: if (content != null) {
191: try {
192: String data = (String) (content
193: .getTransferData(DataFlavor.stringFlavor));
194: // fix for 4401853: to clear TextArea selection if null is pasted
195: data = (data == null ? "" : data);
196: replaceRange(data, getSelectionStart(),
197: getSelectionEnd());
198:
199: } catch (Exception e) {
200: }
201: }
202: }
203:
204: /*
205: * Print the native component by rendering the Motif look ourselves.
206: * ToDo(aim): needs to query native motif for more accurate size and
207: * color information, the top/left text offsets, and selected text.
208: */
209: static final int MARGIN = 2;
210: static final int BORDER = 1;
211: static final int SCROLLBAR = 16;
212: int fontHeight;
213: int fontAscent;
214: int fontLeading;
215: int topLine = 0;
216: int numLines = 0;
217: int textLength = 0;
218: Vector lines;
219: int selStart = 0;
220: int selEnd = 0;
221: int movedRight = 0;
222:
223: // the following vars are assigned in print() method
224: transient boolean hscrollbar;
225: transient boolean vscrollbar;
226:
227: public void print(Graphics g) {
228: TextArea area = (TextArea) target;
229: Dimension d = area.size();
230: Color bg = area.getBackground();
231: Color fg = area.getForeground();
232: FontMetrics fm = getFontMetrics(area.getFont());
233: int vmin, vmax, vval, vvis;
234: int hmin, hmax, hval, hvis;
235: int max = 0;
236:
237: /*
238: Doesn't work right yet.
239: selStart = area.getSelectionStart();
240: selEnd = area.getSelectionEnd();
241: */
242:
243: // Figure out number of lines and max line length
244: String text = area.getText();
245: textLength = text.length();
246: BufferedReader is = new BufferedReader(new StringReader(text));
247: String line;
248: int pos = 0;
249: lines = new Vector();
250: int sv = ((TextArea) target).getScrollbarVisibility();
251: vscrollbar = (sv == TextArea.SCROLLBARS_BOTH || sv == TextArea.SCROLLBARS_VERTICAL_ONLY);
252: hscrollbar = (sv == TextArea.SCROLLBARS_BOTH || sv == TextArea.SCROLLBARS_HORIZONTAL_ONLY);
253: boolean wrap = !hscrollbar;
254: int w = d.width - (vscrollbar ? SCROLLBAR : 0);
255: int h = d.height - (hscrollbar ? SCROLLBAR : 0);
256:
257: try {
258: numLines = 0;
259: while ((line = is.readLine()) != null) {
260: int len = fm.stringWidth(line);
261: if (len > w && wrap) {
262: // need to do line wrapping
263: int start = 0;
264: int end = 0;
265: int string_length = line.length();
266: while (true) {
267: int line_width = 0;
268: end = start + 1; // at least one character per line
269: while (end < string_length) {
270: char c = line.charAt(end);
271: int cw = fm.charWidth(c);
272: if (line_width + cw + 10 > w) // +10?
273: break;
274: line_width += cw;
275: end++;
276: }
277: // form a line from start to end (not including end)
278: String substr = line.substring(start, end);
279: // System.out.println("wrap line: " + substr);
280: TextLine tline = new TextLine();
281: tline.text = substr;
282: tline.pos = pos + start;
283: lines.addElement(tline);
284: start = end;
285: max = Math.max(max, len);
286: numLines++;
287: if (end == string_length) {
288: // we have processed the whole string
289: pos += line.length() + 1; // +1 for the ending \n ?
290: break;
291: }
292: }
293: } else {
294: TextLine tline = new TextLine();
295: tline.text = line;
296: tline.pos = pos;
297: lines.addElement(tline);
298: pos += line.length() + 1;
299:
300: max = Math.max(max, len);
301: numLines++;
302: }
303: }
304: is.close();
305:
306: } catch (IOException e) {
307: }
308:
309: fontHeight = fm.getHeight();
310: fontAscent = fm.getAscent();
311: fontLeading = fm.getLeading();
312:
313: hmin = vmin = 0;
314:
315: vvis = linesInWindow(true);
316: vmax = Math.max(numLines - vvis, 0);
317: vval = 0;
318:
319: hvis = w - (2 * MARGIN);
320: hmax = Math.max(max - hvis, 0);
321: hval = 0;
322:
323: g.setColor(bg);
324: g.fillRect(BORDER, BORDER, w, h);
325: if (vscrollbar) {
326: int sbh = d.height - (hscrollbar ? SCROLLBAR : 0);
327: g.fillRect(d.width - SCROLLBAR - 3, 1, SCROLLBAR - 3,
328: sbh - 1);
329: Graphics ng = g.create();
330: try {
331: ng.translate(d.width - (SCROLLBAR - 2), 0);
332: drawScrollbar(ng, bg, SCROLLBAR - 2, sbh, vmin, vmax,
333: vval, vvis, false);
334: } finally {
335: ng.dispose();
336: }
337: }
338: if (hscrollbar) {
339: int sbw = d.width - (vscrollbar ? SCROLLBAR : 0);
340: g.fillRect(1, d.height - SCROLLBAR - 3, sbw - 1,
341: SCROLLBAR - 3);
342: Graphics ng = g.create();
343: try {
344: ng.translate(0, d.height - (SCROLLBAR - 2));
345: drawScrollbar(ng, bg, SCROLLBAR - 2, sbw, hmin, hmax,
346: hval, hvis, true);
347: } finally {
348: ng.dispose();
349: }
350: }
351:
352: draw3DRect(g, bg, 0, 0, w - 1, h - 1, false);
353:
354: if (text != null) {
355: int l = linesInWindow(true);
356: h = d.height - ((2 * MARGIN) + SCROLLBAR);
357: int e = Math.min(numLines - 1, (topLine + l) - 1);
358: paintLines(g, bg, fg, topLine, e);
359: }
360:
361: target.print(g);
362: }
363:
364: int linesInWindow(boolean horizScrollbar) {
365: Dimension d = target.size();
366: int htotal = d.height
367: - ((2 * MARGIN) + (horizScrollbar ? SCROLLBAR : 0));
368: return htotal / fontHeight;
369: }
370:
371: void paintLines(Graphics g, Color bg, Color fg, int s, int e) {
372: Dimension d = target.size();
373: int w = d.width - ((2 * BORDER) + (vscrollbar ? SCROLLBAR : 0));
374: int h = d.height
375: - ((2 * BORDER) + (hscrollbar ? SCROLLBAR : 0));
376: int lm = linesInWindow(true) + topLine;
377: s = Math.max(topLine, s);
378: e = Math.min(e, lm - 1);
379: Graphics ng = g.create();
380: try {
381: ng.clipRect(BORDER + MARGIN, MARGIN + BORDER, w
382: - (2 * MARGIN), h - (2 * MARGIN));
383: ng.setFont(target.getFont());
384: for (int i = s; i <= e; i++) {
385: paintLine(ng, bg, fg, i);
386: }
387: } finally {
388: ng.dispose();
389: }
390: }
391:
392: void paintLine(Graphics g, Color bg, Color fg, int lnr) {
393: Dimension d = target.size();
394: int l = linesInWindow(true);
395:
396: if ((lnr < topLine) || (lnr >= l + topLine)) {
397: return;
398: }
399: int w = d.width - ((2 * BORDER) + (hscrollbar ? SCROLLBAR : 0));
400: int y = MARGIN + fontLeading + ((lnr - topLine) * fontHeight);
401: String text = ((TextLine) lines.elementAt(lnr)).text;
402: int len = text.length();
403:
404: if (lnr > numLines - 1) {
405: g.setColor(bg);
406: g.fillRect(BORDER, y - fontLeading, w, fontHeight);
407: return;
408: }
409: int s = 0;
410: int e = (lnr < numLines - 1) ? len : textLength;
411: int xs = pos2x(selStart) - movedRight;
412: int xe = pos2x(selEnd) - movedRight;
413:
414: Color highlight = bg.brighter();
415: if ((selStart < s) && (selEnd > e)) {
416: g.setColor(highlight);
417: g.fillRect(BORDER, y - fontLeading, w, fontHeight);
418: } else {
419: g.setColor(bg);
420: g.fillRect(BORDER, y - fontLeading, w, fontHeight);
421:
422: if ((selStart >= s) && (selStart <= e)) {
423: g.setColor(highlight);
424:
425: if (selEnd > e) {
426: g.fillRect(xs, y - fontLeading, (w + BORDER) - xs,
427: fontHeight);
428: } else if (selStart == selEnd) {
429: //g.fillRect(xs, y - fontLeading, 1, fontHeight);
430: } else {
431: g
432: .fillRect(xs, y - fontLeading, xe - xs,
433: fontHeight);
434: }
435: } else if ((selEnd >= s) && (selEnd <= e)) {
436: g.setColor(highlight);
437: g.fillRect(BORDER, y - fontLeading, xe - BORDER,
438: fontHeight);
439: }
440: }
441: g.setColor(fg);
442: g.drawString(text, MARGIN - movedRight, y + fontAscent);
443: }
444:
445: int pos2x(int pos) {
446: FontMetrics fm = getFontMetrics(target.getFont());
447: int widths[] = fm.getWidths();
448: TextLine tl1 = (TextLine) lines.elementAt(0);
449: TextLine tl2;
450: int l = 0;
451: for (int i = 0; i < lines.size() - 1; i++) {
452: tl2 = (TextLine) lines.elementAt(i + 1);
453: if (pos >= tl1.pos && pos < tl2.pos) {
454: l = i;
455: break;
456: }
457: tl1 = tl2;
458: }
459: int x = MARGIN;
460: for (int i = 0; i < (pos - tl1.pos - 1); i++) {
461: x += widths[tl1.text.charAt(i)];
462: }
463: return x;
464: }
465:
466: /**
467: * DEPRECATED
468: */
469: public void insertText(String txt, int pos) {
470: insert(txt, pos);
471: }
472:
473: /**
474: * DEPRECATED
475: */
476: public void replaceText(String txt, int start, int end) {
477: replaceRange(txt, start, end);
478: }
479:
480: /**
481: * DEPRECATED
482: */
483: public Dimension minimumSize() {
484: return getMinimumSize();
485: }
486:
487: /**
488: * DEPRECATED
489: */
490: public Dimension preferredSize(int rows, int cols) {
491: return getPreferredSize(rows, cols);
492: }
493:
494: /**
495: * DEPRECATED
496: */
497: public Dimension minimumSize(int rows, int cols) {
498: return getMinimumSize(rows, cols);
499: }
500:
501: /*
502: * Post a new TextEvent when the value of a text component changes.
503: */
504: public void valueChanged() {
505: postEvent(new TextEvent(target, TextEvent.TEXT_VALUE_CHANGED));
506: }
507:
508: void pShow() {
509: pShow2();
510: notifyTextComponentChange(true);
511: }
512:
513: void pHide() {
514: notifyTextComponentChange(false);
515: super .pHide();
516: }
517:
518: void pDispose() {
519: notifyTextComponentChange(false);
520: super .pDispose();
521: }
522:
523: public boolean handlesWheelScrolling() {
524: return true;
525: }
526:
527: public void handleEvent(AWTEvent e) {
528: if (e.getID() == MouseEvent.MOUSE_WHEEL) {
529: MouseWheelEvent mwe = (MouseWheelEvent) e;
530: nativeHandleMouseWheel(mwe.getScrollType(), mwe
531: .getScrollAmount(), mwe.getWheelRotation());
532: } else {
533: super .handleEvent(e);
534: }
535: }
536:
537: public InputMethodRequests getInputMethodRequests() {
538: return null;
539: }
540:
541: native void nativeHandleMouseWheel(int scrollType,
542: int scrollAmount, int wheelRotation);
543:
544: //
545: // Accessibility support
546: //
547:
548: // stub functions: to be fully implemented in a future release
549: public int getIndexAtPoint(int x, int y) {
550: return -1;
551: }
552:
553: public Rectangle getCharacterBounds(int i) {
554: return null;
555: }
556:
557: public long filterEvents(long mask) {
558: return 0;
559: }
560:
561: /* To be fully implemented in a future release
562:
563: int oldSelectionStart;
564: int oldSelectionEnd;
565:
566: public native int getIndexAtPoint(int x, int y);
567: public native Rectangle getCharacterBounds(int i);
568: public native long filterEvents(long mask);
569:
570: /**
571: * Handle a change in the text selection endpoints
572: * (Note: could be simply a change in the caret location)
573: *
574: public void selectionValuesChanged(int start, int end) {
575: return; // Need to write implementation of this.
576: }
577: */
578: }
579:
580: class TextLine {
581: String text;
582: int pos;
583: }
|