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