1 /* |
|
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 |
|
26 package sun.awt.motif; |
|
27 |
|
28 import java.awt.*; |
|
29 import java.awt.peer.*; |
|
30 import java.awt.event.TextEvent; |
|
31 import java.awt.event.MouseEvent; |
|
32 import java.awt.event.MouseWheelEvent; |
|
33 import java.awt.datatransfer.*; |
|
34 import java.io.BufferedReader; |
|
35 import java.io.StringReader; |
|
36 import java.io.IOException; |
|
37 import java.util.Vector; |
|
38 import java.awt.im.InputMethodRequests; |
|
39 |
|
40 |
|
41 public 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 |
|
552 class TextLine { |
|
553 String text; |
|
554 int pos; |
|
555 } |
|