Line 0
Link Here
|
|
|
1 |
/* |
2 |
* JEditTextArea.java - jEdit's text component |
3 |
* Copyright (C) 1999 Slava Pestov |
4 |
* |
5 |
* You may use and modify this package for any purpose. Redistribution is |
6 |
* permitted, in both source and binary form, provided that this notice |
7 |
* remains intact in all source distributions of this package. |
8 |
*/ |
9 |
package org.apache.batik.apps.svgbrowser.srcview; |
10 |
|
11 |
import javax.swing.event.*; |
12 |
import javax.swing.text.*; |
13 |
import javax.swing.undo.*; |
14 |
import javax.swing.*; |
15 |
import java.awt.datatransfer.*; |
16 |
import java.awt.event.*; |
17 |
import java.awt.*; |
18 |
import java.util.Enumeration; |
19 |
import java.util.Vector; |
20 |
|
21 |
/** |
22 |
* jEdit's text area component. It is more suited for editing program |
23 |
* source code than JEditorPane, because it drops the unnecessary features |
24 |
* (images, variable-width lines, and so on) and adds a whole bunch of |
25 |
* useful goodies such as: |
26 |
* <ul> |
27 |
* <li>More flexible key binding scheme |
28 |
* <li>Supports macro recorders |
29 |
* <li>Rectangular selection |
30 |
* <li>Bracket highlighting |
31 |
* <li>Syntax highlighting |
32 |
* <li>Command repetition |
33 |
* <li>Block caret can be enabled |
34 |
* </ul> |
35 |
* It is also faster and doesn't have as many problems. It can be used |
36 |
* in other applications; the only other part of jEdit it depends on is |
37 |
* the syntax package.<p> |
38 |
* |
39 |
* To use it in your app, treat it like any other component, for example: |
40 |
* <pre>JEditTextArea ta = new JEditTextArea(); |
41 |
* ta.setTokenMarker(new JavaTokenMarker()); |
42 |
* ta.setText("public class Test {\n" |
43 |
* + " public static void main(String[] args) {\n" |
44 |
* + " System.out.println(\"Hello World\");\n" |
45 |
* + " }\n" |
46 |
* + "}");</pre> |
47 |
* |
48 |
* @author Slava Pestov |
49 |
* @version $Id: JEditTextArea.java,v 1.36 1999/12/13 03:40:30 sp Exp $ |
50 |
*/ |
51 |
public class JEditTextArea extends JComponent |
52 |
{ |
53 |
/** |
54 |
* Adding components with this name to the text area will place |
55 |
* them left of the horizontal scroll bar. In jEdit, the status |
56 |
* bar is added this way. |
57 |
*/ |
58 |
public static String LEFT_OF_SCROLLBAR = "los"; |
59 |
|
60 |
/** |
61 |
* Creates a new JEditTextArea with the default settings. |
62 |
*/ |
63 |
public JEditTextArea() |
64 |
{ |
65 |
this(TextAreaDefaults.getDefaults()); |
66 |
} |
67 |
|
68 |
/** |
69 |
* Creates a new JEditTextArea with the specified settings. |
70 |
* @param defaults The default settings |
71 |
*/ |
72 |
public JEditTextArea(TextAreaDefaults defaults) |
73 |
{ |
74 |
// Enable the necessary events |
75 |
enableEvents(AWTEvent.KEY_EVENT_MASK); |
76 |
|
77 |
// Initialize some misc. stuff |
78 |
painter = new TextAreaPainter(this,defaults); |
79 |
documentHandler = new DocumentHandler(); |
80 |
listenerList = new EventListenerList(); |
81 |
caretEvent = new MutableCaretEvent(); |
82 |
lineSegment = new Segment(); |
83 |
bracketLine = bracketPosition = -1; |
84 |
blink = true; |
85 |
|
86 |
// Initialize the GUI |
87 |
setLayout(new ScrollLayout()); |
88 |
add(CENTER,painter); |
89 |
add(RIGHT,vertical = new JScrollBar(JScrollBar.VERTICAL)); |
90 |
add(BOTTOM,horizontal = new JScrollBar(JScrollBar.HORIZONTAL)); |
91 |
|
92 |
// Add some event listeners |
93 |
vertical.addAdjustmentListener(new AdjustHandler()); |
94 |
horizontal.addAdjustmentListener(new AdjustHandler()); |
95 |
painter.addComponentListener(new ComponentHandler()); |
96 |
painter.addMouseListener(new MouseHandler()); |
97 |
painter.addMouseMotionListener(new DragHandler()); |
98 |
addFocusListener(new FocusHandler()); |
99 |
|
100 |
// Load the defaults |
101 |
setInputHandler(defaults.inputHandler); |
102 |
setDocument(defaults.document); |
103 |
editable = defaults.editable; |
104 |
caretVisible = defaults.caretVisible; |
105 |
caretBlinks = defaults.caretBlinks; |
106 |
electricScroll = defaults.electricScroll; |
107 |
|
108 |
popup = defaults.popup; |
109 |
|
110 |
// We don't seem to get the initial focus event? |
111 |
focusedComponent = this; |
112 |
} |
113 |
|
114 |
/** |
115 |
* Returns if this component can be traversed by pressing |
116 |
* the Tab key. This returns false. |
117 |
*/ |
118 |
public final boolean isManagingFocus() |
119 |
{ |
120 |
return true; |
121 |
} |
122 |
|
123 |
/** |
124 |
* Returns the object responsible for painting this text area. |
125 |
*/ |
126 |
public final TextAreaPainter getPainter() |
127 |
{ |
128 |
return painter; |
129 |
} |
130 |
|
131 |
/** |
132 |
* Returns the input handler. |
133 |
*/ |
134 |
public final InputHandler getInputHandler() |
135 |
{ |
136 |
return inputHandler; |
137 |
} |
138 |
|
139 |
/** |
140 |
* Sets the input handler. |
141 |
* @param inputHandler The new input handler |
142 |
*/ |
143 |
public void setInputHandler(InputHandler inputHandler) |
144 |
{ |
145 |
this.inputHandler = inputHandler; |
146 |
} |
147 |
|
148 |
/** |
149 |
* Returns true if the caret is blinking, false otherwise. |
150 |
*/ |
151 |
public final boolean isCaretBlinkEnabled() |
152 |
{ |
153 |
return caretBlinks; |
154 |
} |
155 |
|
156 |
/** |
157 |
* Toggles caret blinking. |
158 |
* @param caretBlinks True if the caret should blink, false otherwise |
159 |
*/ |
160 |
public void setCaretBlinkEnabled(boolean caretBlinks) |
161 |
{ |
162 |
this.caretBlinks = caretBlinks; |
163 |
if(!caretBlinks) |
164 |
blink = false; |
165 |
|
166 |
painter.invalidateSelectedLines(); |
167 |
} |
168 |
|
169 |
/** |
170 |
* Returns true if the caret is visible, false otherwise. |
171 |
*/ |
172 |
public final boolean isCaretVisible() |
173 |
{ |
174 |
return (!caretBlinks || blink) && caretVisible; |
175 |
} |
176 |
|
177 |
/** |
178 |
* Sets if the caret should be visible. |
179 |
* @param caretVisible True if the caret should be visible, false |
180 |
* otherwise |
181 |
*/ |
182 |
public void setCaretVisible(boolean caretVisible) |
183 |
{ |
184 |
this.caretVisible = caretVisible; |
185 |
blink = true; |
186 |
|
187 |
painter.invalidateSelectedLines(); |
188 |
} |
189 |
|
190 |
/** |
191 |
* Blinks the caret. |
192 |
*/ |
193 |
public final void blinkCaret() |
194 |
{ |
195 |
if(caretBlinks) |
196 |
{ |
197 |
blink = !blink; |
198 |
painter.invalidateSelectedLines(); |
199 |
} |
200 |
else |
201 |
blink = true; |
202 |
} |
203 |
|
204 |
/** |
205 |
* Returns the number of lines from the top and button of the |
206 |
* text area that are always visible. |
207 |
*/ |
208 |
public final int getElectricScroll() |
209 |
{ |
210 |
return electricScroll; |
211 |
} |
212 |
|
213 |
/** |
214 |
* Sets the number of lines from the top and bottom of the text |
215 |
* area that are always visible |
216 |
* @param electricScroll The number of lines always visible from |
217 |
* the top or bottom |
218 |
*/ |
219 |
public final void setElectricScroll(int electricScroll) |
220 |
{ |
221 |
this.electricScroll = electricScroll; |
222 |
} |
223 |
|
224 |
/** |
225 |
* Updates the state of the scroll bars. This should be called |
226 |
* if the number of lines in the document changes, or when the |
227 |
* size of the text are changes. |
228 |
*/ |
229 |
public void updateScrollBars() |
230 |
{ |
231 |
if(vertical != null && visibleLines != 0) |
232 |
{ |
233 |
vertical.setValues(firstLine,visibleLines,0,getLineCount()); |
234 |
vertical.setUnitIncrement(2); |
235 |
vertical.setBlockIncrement(visibleLines); |
236 |
} |
237 |
|
238 |
int width = painter.getWidth(); |
239 |
if(horizontal != null && width != 0) |
240 |
{ |
241 |
horizontal.setValues(-horizontalOffset,width,0,width * 5); |
242 |
horizontal.setUnitIncrement(painter.getFontMetrics() |
243 |
.charWidth('w')); |
244 |
horizontal.setBlockIncrement(width / 2); |
245 |
} |
246 |
} |
247 |
|
248 |
/** |
249 |
* Returns the line displayed at the text area's origin. |
250 |
*/ |
251 |
public final int getFirstLine() |
252 |
{ |
253 |
return firstLine; |
254 |
} |
255 |
|
256 |
/** |
257 |
* Sets the line displayed at the text area's origin without |
258 |
* updating the scroll bars. |
259 |
*/ |
260 |
public void setFirstLine(int firstLine) |
261 |
{ |
262 |
if(firstLine == this.firstLine) |
263 |
return; |
264 |
int oldFirstLine = this.firstLine; |
265 |
this.firstLine = firstLine; |
266 |
if(firstLine != vertical.getValue()) |
267 |
updateScrollBars(); |
268 |
painter.repaint(); |
269 |
} |
270 |
|
271 |
/** |
272 |
* Returns the number of lines visible in this text area. |
273 |
*/ |
274 |
public final int getVisibleLines() |
275 |
{ |
276 |
return visibleLines; |
277 |
} |
278 |
|
279 |
/** |
280 |
* Recalculates the number of visible lines. This should not |
281 |
* be called directly. |
282 |
*/ |
283 |
public final void recalculateVisibleLines() |
284 |
{ |
285 |
if(painter == null) |
286 |
return; |
287 |
int height = painter.getHeight(); |
288 |
int lineHeight = painter.getFontMetrics().getHeight(); |
289 |
int oldVisibleLines = visibleLines; |
290 |
visibleLines = height / lineHeight; |
291 |
updateScrollBars(); |
292 |
} |
293 |
|
294 |
/** |
295 |
* Returns the horizontal offset of drawn lines. |
296 |
*/ |
297 |
public final int getHorizontalOffset() |
298 |
{ |
299 |
return horizontalOffset; |
300 |
} |
301 |
|
302 |
/** |
303 |
* Sets the horizontal offset of drawn lines. This can be used to |
304 |
* implement horizontal scrolling. |
305 |
* @param horizontalOffset offset The new horizontal offset |
306 |
*/ |
307 |
public void setHorizontalOffset(int horizontalOffset) |
308 |
{ |
309 |
if(horizontalOffset == this.horizontalOffset) |
310 |
return; |
311 |
this.horizontalOffset = horizontalOffset; |
312 |
if(horizontalOffset != horizontal.getValue()) |
313 |
updateScrollBars(); |
314 |
painter.repaint(); |
315 |
} |
316 |
|
317 |
/** |
318 |
* A fast way of changing both the first line and horizontal |
319 |
* offset. |
320 |
* @param firstLine The new first line |
321 |
* @param horizontalOffset The new horizontal offset |
322 |
* @return True if any of the values were changed, false otherwise |
323 |
*/ |
324 |
public boolean setOrigin(int firstLine, int horizontalOffset) |
325 |
{ |
326 |
boolean changed = false; |
327 |
int oldFirstLine = this.firstLine; |
328 |
|
329 |
if(horizontalOffset != this.horizontalOffset) |
330 |
{ |
331 |
this.horizontalOffset = horizontalOffset; |
332 |
changed = true; |
333 |
} |
334 |
|
335 |
if(firstLine != this.firstLine) |
336 |
{ |
337 |
this.firstLine = firstLine; |
338 |
changed = true; |
339 |
} |
340 |
|
341 |
if(changed) |
342 |
{ |
343 |
updateScrollBars(); |
344 |
painter.repaint(); |
345 |
} |
346 |
|
347 |
return changed; |
348 |
} |
349 |
|
350 |
/** |
351 |
* Ensures that the caret is visible by scrolling the text area if |
352 |
* necessary. |
353 |
* @return True if scrolling was actually performed, false if the |
354 |
* caret was already visible |
355 |
*/ |
356 |
public boolean scrollToCaret() |
357 |
{ |
358 |
int line = getCaretLine(); |
359 |
int lineStart = getLineStartOffset(line); |
360 |
int offset = Math.max(0,Math.min(getLineLength(line) - 1, |
361 |
getCaretPosition() - lineStart)); |
362 |
|
363 |
return scrollTo(line,offset); |
364 |
} |
365 |
|
366 |
/** |
367 |
* Ensures that the specified line and offset is visible by scrolling |
368 |
* the text area if necessary. |
369 |
* @param line The line to scroll to |
370 |
* @param offset The offset in the line to scroll to |
371 |
* @return True if scrolling was actually performed, false if the |
372 |
* line and offset was already visible |
373 |
*/ |
374 |
public boolean scrollTo(int line, int offset) |
375 |
{ |
376 |
// visibleLines == 0 before the component is realized |
377 |
// we can't do any proper scrolling then, so we have |
378 |
// this hack... |
379 |
if(visibleLines == 0) |
380 |
{ |
381 |
setFirstLine(Math.max(0,line - electricScroll)); |
382 |
return true; |
383 |
} |
384 |
|
385 |
int newFirstLine = firstLine; |
386 |
int newHorizontalOffset = horizontalOffset; |
387 |
|
388 |
if(line < firstLine + electricScroll) |
389 |
{ |
390 |
newFirstLine = Math.max(0,line - electricScroll); |
391 |
} |
392 |
else if(line + electricScroll >= firstLine + visibleLines) |
393 |
{ |
394 |
newFirstLine = (line - visibleLines) + electricScroll + 1; |
395 |
if(newFirstLine + visibleLines >= getLineCount()) |
396 |
newFirstLine = getLineCount() - visibleLines; |
397 |
if(newFirstLine < 0) |
398 |
newFirstLine = 0; |
399 |
} |
400 |
|
401 |
int x = _offsetToX(line,offset); |
402 |
int width = painter.getFontMetrics().charWidth('w'); |
403 |
|
404 |
if(x < 0) |
405 |
{ |
406 |
newHorizontalOffset = Math.min(0,horizontalOffset |
407 |
- x + width + 5); |
408 |
} |
409 |
else if(x + width >= painter.getWidth()) |
410 |
{ |
411 |
newHorizontalOffset = horizontalOffset + |
412 |
(painter.getWidth() - x) - width - 5; |
413 |
} |
414 |
|
415 |
return setOrigin(newFirstLine,newHorizontalOffset); |
416 |
} |
417 |
|
418 |
/** |
419 |
* Converts a line index to a y co-ordinate. |
420 |
* @param line The line |
421 |
*/ |
422 |
public int lineToY(int line) |
423 |
{ |
424 |
FontMetrics fm = painter.getFontMetrics(); |
425 |
return (line - firstLine) * fm.getHeight() |
426 |
- (fm.getLeading() + fm.getMaxDescent()); |
427 |
} |
428 |
|
429 |
/** |
430 |
* Converts a y co-ordinate to a line index. |
431 |
* @param y The y co-ordinate |
432 |
*/ |
433 |
public int yToLine(int y) |
434 |
{ |
435 |
FontMetrics fm = painter.getFontMetrics(); |
436 |
int height = fm.getHeight(); |
437 |
return Math.max(0,Math.min(getLineCount() - 1, |
438 |
y / height + firstLine)); |
439 |
} |
440 |
|
441 |
/** |
442 |
* Converts an offset in a line into an x co-ordinate. This is a |
443 |
* slow version that can be used any time. |
444 |
* @param line The line |
445 |
* @param offset The offset, from the start of the line |
446 |
*/ |
447 |
public final int offsetToX(int line, int offset) |
448 |
{ |
449 |
// don't use cached tokens |
450 |
painter.currentLineTokens = null; |
451 |
return _offsetToX(line,offset); |
452 |
} |
453 |
|
454 |
/** |
455 |
* Converts an offset in a line into an x co-ordinate. This is a |
456 |
* fast version that should only be used if no changes were made |
457 |
* to the text since the last repaint. |
458 |
* @param line The line |
459 |
* @param offset The offset, from the start of the line |
460 |
*/ |
461 |
public int _offsetToX(int line, int offset) |
462 |
{ |
463 |
TokenMarker tokenMarker = getTokenMarker(); |
464 |
|
465 |
/* Use painter's cached info for speed */ |
466 |
FontMetrics fm = painter.getFontMetrics(); |
467 |
|
468 |
getLineText(line,lineSegment); |
469 |
|
470 |
int segmentOffset = lineSegment.offset; |
471 |
int x = horizontalOffset; |
472 |
|
473 |
/* If syntax coloring is disabled, do simple translation */ |
474 |
if(tokenMarker == null) |
475 |
{ |
476 |
lineSegment.count = offset; |
477 |
return x + Utilities.getTabbedTextWidth(lineSegment, |
478 |
fm,x,painter,0); |
479 |
} |
480 |
/* If syntax coloring is enabled, we have to do this because |
481 |
* tokens can vary in width */ |
482 |
else |
483 |
{ |
484 |
Token tokens; |
485 |
if(painter.currentLineIndex == line |
486 |
&& painter.currentLineTokens != null) |
487 |
tokens = painter.currentLineTokens; |
488 |
else |
489 |
{ |
490 |
painter.currentLineIndex = line; |
491 |
tokens = painter.currentLineTokens |
492 |
= tokenMarker.markTokens(lineSegment,line); |
493 |
} |
494 |
|
495 |
Toolkit toolkit = painter.getToolkit(); |
496 |
Font defaultFont = painter.getFont(); |
497 |
SyntaxStyle[] styles = painter.getStyles(); |
498 |
|
499 |
for(;;) |
500 |
{ |
501 |
byte id = tokens.id; |
502 |
if(id == Token.END) |
503 |
{ |
504 |
return x; |
505 |
} |
506 |
|
507 |
if(id == Token.NULL) |
508 |
fm = painter.getFontMetrics(); |
509 |
else |
510 |
fm = styles[id].getFontMetrics(defaultFont); |
511 |
|
512 |
int length = tokens.length; |
513 |
|
514 |
if(offset + segmentOffset < lineSegment.offset + length) |
515 |
{ |
516 |
lineSegment.count = offset - (lineSegment.offset - segmentOffset); |
517 |
return x + Utilities.getTabbedTextWidth( |
518 |
lineSegment,fm,x,painter,0); |
519 |
} |
520 |
else |
521 |
{ |
522 |
lineSegment.count = length; |
523 |
x += Utilities.getTabbedTextWidth( |
524 |
lineSegment,fm,x,painter,0); |
525 |
lineSegment.offset += length; |
526 |
} |
527 |
tokens = tokens.next; |
528 |
} |
529 |
} |
530 |
} |
531 |
|
532 |
/** |
533 |
* Converts an x co-ordinate to an offset within a line. |
534 |
* @param line The line |
535 |
* @param x The x co-ordinate |
536 |
*/ |
537 |
public int xToOffset(int line, int x) |
538 |
{ |
539 |
TokenMarker tokenMarker = getTokenMarker(); |
540 |
|
541 |
/* Use painter's cached info for speed */ |
542 |
FontMetrics fm = painter.getFontMetrics(); |
543 |
|
544 |
getLineText(line,lineSegment); |
545 |
|
546 |
char[] segmentArray = lineSegment.array; |
547 |
int segmentOffset = lineSegment.offset; |
548 |
int segmentCount = lineSegment.count; |
549 |
|
550 |
int width = horizontalOffset; |
551 |
|
552 |
if(tokenMarker == null) |
553 |
{ |
554 |
for(int i = 0; i < segmentCount; i++) |
555 |
{ |
556 |
char c = segmentArray[i + segmentOffset]; |
557 |
int charWidth; |
558 |
if(c == '\t') |
559 |
charWidth = (int)painter.nextTabStop(width,i) |
560 |
- width; |
561 |
else |
562 |
charWidth = fm.charWidth(c); |
563 |
|
564 |
if(painter.isBlockCaretEnabled()) |
565 |
{ |
566 |
if(x - charWidth <= width) |
567 |
return i; |
568 |
} |
569 |
else |
570 |
{ |
571 |
if(x - charWidth / 2 <= width) |
572 |
return i; |
573 |
} |
574 |
|
575 |
width += charWidth; |
576 |
} |
577 |
|
578 |
return segmentCount; |
579 |
} |
580 |
else |
581 |
{ |
582 |
Token tokens; |
583 |
if(painter.currentLineIndex == line && painter |
584 |
.currentLineTokens != null) |
585 |
tokens = painter.currentLineTokens; |
586 |
else |
587 |
{ |
588 |
painter.currentLineIndex = line; |
589 |
tokens = painter.currentLineTokens |
590 |
= tokenMarker.markTokens(lineSegment,line); |
591 |
} |
592 |
|
593 |
int offset = 0; |
594 |
Toolkit toolkit = painter.getToolkit(); |
595 |
Font defaultFont = painter.getFont(); |
596 |
SyntaxStyle[] styles = painter.getStyles(); |
597 |
|
598 |
for(;;) |
599 |
{ |
600 |
byte id = tokens.id; |
601 |
if(id == Token.END) |
602 |
return offset; |
603 |
|
604 |
if(id == Token.NULL) |
605 |
fm = painter.getFontMetrics(); |
606 |
else |
607 |
fm = styles[id].getFontMetrics(defaultFont); |
608 |
|
609 |
int length = tokens.length; |
610 |
|
611 |
for(int i = 0; i < length; i++) |
612 |
{ |
613 |
char c = segmentArray[segmentOffset + offset + i]; |
614 |
int charWidth; |
615 |
if(c == '\t') |
616 |
charWidth = (int)painter.nextTabStop(width,offset + i) |
617 |
- width; |
618 |
else |
619 |
charWidth = fm.charWidth(c); |
620 |
|
621 |
if(painter.isBlockCaretEnabled()) |
622 |
{ |
623 |
if(x - charWidth <= width) |
624 |
return offset + i; |
625 |
} |
626 |
else |
627 |
{ |
628 |
if(x - charWidth / 2 <= width) |
629 |
return offset + i; |
630 |
} |
631 |
|
632 |
width += charWidth; |
633 |
} |
634 |
|
635 |
offset += length; |
636 |
tokens = tokens.next; |
637 |
} |
638 |
} |
639 |
} |
640 |
|
641 |
/** |
642 |
* Converts a point to an offset, from the start of the text. |
643 |
* @param x The x co-ordinate of the point |
644 |
* @param y The y co-ordinate of the point |
645 |
*/ |
646 |
public int xyToOffset(int x, int y) |
647 |
{ |
648 |
int line = yToLine(y); |
649 |
int start = getLineStartOffset(line); |
650 |
return start + xToOffset(line,x); |
651 |
} |
652 |
|
653 |
/** |
654 |
* Returns the document this text area is editing. |
655 |
*/ |
656 |
public final SyntaxDocument getDocument() |
657 |
{ |
658 |
return document; |
659 |
} |
660 |
|
661 |
/** |
662 |
* Sets the document this text area is editing. |
663 |
* @param document The document |
664 |
*/ |
665 |
public void setDocument(SyntaxDocument document) |
666 |
{ |
667 |
if(this.document == document) |
668 |
return; |
669 |
if(this.document != null) |
670 |
this.document.removeDocumentListener(documentHandler); |
671 |
this.document = document; |
672 |
|
673 |
document.addDocumentListener(documentHandler); |
674 |
|
675 |
select(0,0); |
676 |
updateScrollBars(); |
677 |
painter.repaint(); |
678 |
} |
679 |
|
680 |
/** |
681 |
* Returns the document's token marker. Equivalent to calling |
682 |
* <code>getDocument().getTokenMarker()</code>. |
683 |
*/ |
684 |
public final TokenMarker getTokenMarker() |
685 |
{ |
686 |
return document.getTokenMarker(); |
687 |
} |
688 |
|
689 |
/** |
690 |
* Sets the document's token marker. Equivalent to caling |
691 |
* <code>getDocument().setTokenMarker()</code>. |
692 |
* @param tokenMarker The token marker |
693 |
*/ |
694 |
public final void setTokenMarker(TokenMarker tokenMarker) |
695 |
{ |
696 |
document.setTokenMarker(tokenMarker); |
697 |
} |
698 |
|
699 |
/** |
700 |
* Returns the length of the document. Equivalent to calling |
701 |
* <code>getDocument().getLength()</code>. |
702 |
*/ |
703 |
public final int getDocumentLength() |
704 |
{ |
705 |
return document.getLength(); |
706 |
} |
707 |
|
708 |
/** |
709 |
* Returns the number of lines in the document. |
710 |
*/ |
711 |
public final int getLineCount() |
712 |
{ |
713 |
return document.getDefaultRootElement().getElementCount(); |
714 |
} |
715 |
|
716 |
/** |
717 |
* Returns the line containing the specified offset. |
718 |
* @param offset The offset |
719 |
*/ |
720 |
public final int getLineOfOffset(int offset) |
721 |
{ |
722 |
return document.getDefaultRootElement().getElementIndex(offset); |
723 |
} |
724 |
|
725 |
/** |
726 |
* Returns the start offset of the specified line. |
727 |
* @param line The line |
728 |
* @return The start offset of the specified line, or -1 if the line is |
729 |
* invalid |
730 |
*/ |
731 |
public int getLineStartOffset(int line) |
732 |
{ |
733 |
Element lineElement = document.getDefaultRootElement() |
734 |
.getElement(line); |
735 |
if(lineElement == null) |
736 |
return -1; |
737 |
else |
738 |
return lineElement.getStartOffset(); |
739 |
} |
740 |
|
741 |
/** |
742 |
* Returns the end offset of the specified line. |
743 |
* @param line The line |
744 |
* @return The end offset of the specified line, or -1 if the line is |
745 |
* invalid. |
746 |
*/ |
747 |
public int getLineEndOffset(int line) |
748 |
{ |
749 |
Element lineElement = document.getDefaultRootElement() |
750 |
.getElement(line); |
751 |
if(lineElement == null) |
752 |
return -1; |
753 |
else |
754 |
return lineElement.getEndOffset(); |
755 |
} |
756 |
|
757 |
/** |
758 |
* Returns the length of the specified line. |
759 |
* @param line The line |
760 |
*/ |
761 |
public int getLineLength(int line) |
762 |
{ |
763 |
Element lineElement = document.getDefaultRootElement() |
764 |
.getElement(line); |
765 |
if(lineElement == null) |
766 |
return -1; |
767 |
else |
768 |
return lineElement.getEndOffset() |
769 |
- lineElement.getStartOffset() - 1; |
770 |
} |
771 |
|
772 |
/** |
773 |
* Returns the entire text of this text area. |
774 |
*/ |
775 |
public String getText() |
776 |
{ |
777 |
try |
778 |
{ |
779 |
return document.getText(0,document.getLength()); |
780 |
} |
781 |
catch(BadLocationException bl) |
782 |
{ |
783 |
bl.printStackTrace(); |
784 |
return null; |
785 |
} |
786 |
} |
787 |
|
788 |
/** |
789 |
* Sets the entire text of this text area. |
790 |
*/ |
791 |
public void setText(String text) |
792 |
{ |
793 |
try |
794 |
{ |
795 |
document.beginCompoundEdit(); |
796 |
document.remove(0,document.getLength()); |
797 |
document.insertString(0,text,null); |
798 |
} |
799 |
catch(BadLocationException bl) |
800 |
{ |
801 |
bl.printStackTrace(); |
802 |
} |
803 |
finally |
804 |
{ |
805 |
document.endCompoundEdit(); |
806 |
} |
807 |
} |
808 |
|
809 |
/** |
810 |
* Returns the specified substring of the document. |
811 |
* @param start The start offset |
812 |
* @param len The length of the substring |
813 |
* @return The substring, or null if the offsets are invalid |
814 |
*/ |
815 |
public final String getText(int start, int len) |
816 |
{ |
817 |
try |
818 |
{ |
819 |
return document.getText(start,len); |
820 |
} |
821 |
catch(BadLocationException bl) |
822 |
{ |
823 |
bl.printStackTrace(); |
824 |
return null; |
825 |
} |
826 |
} |
827 |
|
828 |
/** |
829 |
* Copies the specified substring of the document into a segment. |
830 |
* If the offsets are invalid, the segment will contain a null string. |
831 |
* @param start The start offset |
832 |
* @param len The length of the substring |
833 |
* @param segment The segment |
834 |
*/ |
835 |
public final void getText(int start, int len, Segment segment) |
836 |
{ |
837 |
try |
838 |
{ |
839 |
document.getText(start,len,segment); |
840 |
} |
841 |
catch(BadLocationException bl) |
842 |
{ |
843 |
bl.printStackTrace(); |
844 |
segment.offset = segment.count = 0; |
845 |
} |
846 |
} |
847 |
|
848 |
/** |
849 |
* Returns the text on the specified line. |
850 |
* @param lineIndex The line |
851 |
* @return The text, or null if the line is invalid |
852 |
*/ |
853 |
public final String getLineText(int lineIndex) |
854 |
{ |
855 |
int start = getLineStartOffset(lineIndex); |
856 |
return getText(start,getLineEndOffset(lineIndex) - start - 1); |
857 |
} |
858 |
|
859 |
/** |
860 |
* Copies the text on the specified line into a segment. If the line |
861 |
* is invalid, the segment will contain a null string. |
862 |
* @param lineIndex The line |
863 |
*/ |
864 |
public final void getLineText(int lineIndex, Segment segment) |
865 |
{ |
866 |
int start = getLineStartOffset(lineIndex); |
867 |
getText(start,getLineEndOffset(lineIndex) - start - 1,segment); |
868 |
} |
869 |
|
870 |
/** |
871 |
* Returns the selection start offset. |
872 |
*/ |
873 |
public final int getSelectionStart() |
874 |
{ |
875 |
return selectionStart; |
876 |
} |
877 |
|
878 |
/** |
879 |
* Returns the offset where the selection starts on the specified |
880 |
* line. |
881 |
*/ |
882 |
public int getSelectionStart(int line) |
883 |
{ |
884 |
if(line == selectionStartLine) |
885 |
return selectionStart; |
886 |
else if(rectSelect) |
887 |
{ |
888 |
Element map = document.getDefaultRootElement(); |
889 |
int start = selectionStart - map.getElement(selectionStartLine) |
890 |
.getStartOffset(); |
891 |
|
892 |
Element lineElement = map.getElement(line); |
893 |
int lineStart = lineElement.getStartOffset(); |
894 |
int lineEnd = lineElement.getEndOffset() - 1; |
895 |
return Math.min(lineEnd,lineStart + start); |
896 |
} |
897 |
else |
898 |
return getLineStartOffset(line); |
899 |
} |
900 |
|
901 |
/** |
902 |
* Returns the selection start line. |
903 |
*/ |
904 |
public final int getSelectionStartLine() |
905 |
{ |
906 |
return selectionStartLine; |
907 |
} |
908 |
|
909 |
/** |
910 |
* Sets the selection start. The new selection will be the new |
911 |
* selection start and the old selection end. |
912 |
* @param selectionStart The selection start |
913 |
* @see #select(int,int) |
914 |
*/ |
915 |
public final void setSelectionStart(int selectionStart) |
916 |
{ |
917 |
select(selectionStart,selectionEnd); |
918 |
} |
919 |
|
920 |
/** |
921 |
* Returns the selection end offset. |
922 |
*/ |
923 |
public final int getSelectionEnd() |
924 |
{ |
925 |
return selectionEnd; |
926 |
} |
927 |
|
928 |
/** |
929 |
* Returns the offset where the selection ends on the specified |
930 |
* line. |
931 |
*/ |
932 |
public int getSelectionEnd(int line) |
933 |
{ |
934 |
if(line == selectionEndLine) |
935 |
return selectionEnd; |
936 |
else if(rectSelect) |
937 |
{ |
938 |
Element map = document.getDefaultRootElement(); |
939 |
int end = selectionEnd - map.getElement(selectionEndLine) |
940 |
.getStartOffset(); |
941 |
|
942 |
Element lineElement = map.getElement(line); |
943 |
int lineStart = lineElement.getStartOffset(); |
944 |
int lineEnd = lineElement.getEndOffset() - 1; |
945 |
return Math.min(lineEnd,lineStart + end); |
946 |
} |
947 |
else |
948 |
return getLineEndOffset(line) - 1; |
949 |
} |
950 |
|
951 |
/** |
952 |
* Returns the selection end line. |
953 |
*/ |
954 |
public final int getSelectionEndLine() |
955 |
{ |
956 |
return selectionEndLine; |
957 |
} |
958 |
|
959 |
/** |
960 |
* Sets the selection end. The new selection will be the old |
961 |
* selection start and the bew selection end. |
962 |
* @param selectionEnd The selection end |
963 |
* @see #select(int,int) |
964 |
*/ |
965 |
public final void setSelectionEnd(int selectionEnd) |
966 |
{ |
967 |
select(selectionStart,selectionEnd); |
968 |
} |
969 |
|
970 |
/** |
971 |
* Returns the caret position. This will either be the selection |
972 |
* start or the selection end, depending on which direction the |
973 |
* selection was made in. |
974 |
*/ |
975 |
public final int getCaretPosition() |
976 |
{ |
977 |
return (biasLeft ? selectionStart : selectionEnd); |
978 |
} |
979 |
|
980 |
/** |
981 |
* Returns the caret line. |
982 |
*/ |
983 |
public final int getCaretLine() |
984 |
{ |
985 |
return (biasLeft ? selectionStartLine : selectionEndLine); |
986 |
} |
987 |
|
988 |
/** |
989 |
* Returns the mark position. This will be the opposite selection |
990 |
* bound to the caret position. |
991 |
* @see #getCaretPosition() |
992 |
*/ |
993 |
public final int getMarkPosition() |
994 |
{ |
995 |
return (biasLeft ? selectionEnd : selectionStart); |
996 |
} |
997 |
|
998 |
/** |
999 |
* Returns the mark line. |
1000 |
*/ |
1001 |
public final int getMarkLine() |
1002 |
{ |
1003 |
return (biasLeft ? selectionEndLine : selectionStartLine); |
1004 |
} |
1005 |
|
1006 |
/** |
1007 |
* Sets the caret position. The new selection will consist of the |
1008 |
* caret position only (hence no text will be selected) |
1009 |
* @param caret The caret position |
1010 |
* @see #select(int,int) |
1011 |
*/ |
1012 |
public final void setCaretPosition(int caret) |
1013 |
{ |
1014 |
select(caret,caret); |
1015 |
} |
1016 |
|
1017 |
/** |
1018 |
* Selects all text in the document. |
1019 |
*/ |
1020 |
public final void selectAll() |
1021 |
{ |
1022 |
select(0,getDocumentLength()); |
1023 |
} |
1024 |
|
1025 |
/** |
1026 |
* Moves the mark to the caret position. |
1027 |
*/ |
1028 |
public final void selectNone() |
1029 |
{ |
1030 |
select(getCaretPosition(),getCaretPosition()); |
1031 |
} |
1032 |
|
1033 |
/** |
1034 |
* Selects from the start offset to the end offset. This is the |
1035 |
* general selection method used by all other selecting methods. |
1036 |
* The caret position will be start if start < end, and end |
1037 |
* if end > start. |
1038 |
* @param start The start offset |
1039 |
* @param end The end offset |
1040 |
*/ |
1041 |
public void select(int start, int end) |
1042 |
{ |
1043 |
int newStart, newEnd; |
1044 |
boolean newBias; |
1045 |
if(start <= end) |
1046 |
{ |
1047 |
newStart = start; |
1048 |
newEnd = end; |
1049 |
newBias = false; |
1050 |
} |
1051 |
else |
1052 |
{ |
1053 |
newStart = end; |
1054 |
newEnd = start; |
1055 |
newBias = true; |
1056 |
} |
1057 |
|
1058 |
if(newStart < 0 || newEnd > getDocumentLength()) |
1059 |
{ |
1060 |
throw new IllegalArgumentException("Bounds out of" |
1061 |
+ " range: " + newStart + "," + |
1062 |
newEnd); |
1063 |
} |
1064 |
|
1065 |
// If the new position is the same as the old, we don't |
1066 |
// do all this crap, however we still do the stuff at |
1067 |
// the end (clearing magic position, scrolling) |
1068 |
if(newStart != selectionStart || newEnd != selectionEnd |
1069 |
|| newBias != biasLeft) |
1070 |
{ |
1071 |
int newStartLine = getLineOfOffset(newStart); |
1072 |
int newEndLine = getLineOfOffset(newEnd); |
1073 |
|
1074 |
if(painter.isBracketHighlightEnabled()) |
1075 |
{ |
1076 |
if(bracketLine != -1) |
1077 |
painter.invalidateLine(bracketLine); |
1078 |
updateBracketHighlight(end); |
1079 |
if(bracketLine != -1) |
1080 |
painter.invalidateLine(bracketLine); |
1081 |
} |
1082 |
|
1083 |
painter.invalidateLineRange(selectionStartLine,selectionEndLine); |
1084 |
painter.invalidateLineRange(newStartLine,newEndLine); |
1085 |
|
1086 |
document.addUndoableEdit(new CaretUndo( |
1087 |
selectionStart,selectionEnd)); |
1088 |
|
1089 |
selectionStart = newStart; |
1090 |
selectionEnd = newEnd; |
1091 |
selectionStartLine = newStartLine; |
1092 |
selectionEndLine = newEndLine; |
1093 |
biasLeft = newBias; |
1094 |
|
1095 |
fireCaretEvent(); |
1096 |
} |
1097 |
|
1098 |
// When the user is typing, etc, we don't want the caret |
1099 |
// to blink |
1100 |
blink = true; |
1101 |
caretTimer.restart(); |
1102 |
|
1103 |
// Disable rectangle select if selection start = selection end |
1104 |
if(selectionStart == selectionEnd) |
1105 |
rectSelect = false; |
1106 |
|
1107 |
// Clear the `magic' caret position used by up/down |
1108 |
magicCaret = -1; |
1109 |
|
1110 |
scrollToCaret(); |
1111 |
} |
1112 |
|
1113 |
/** |
1114 |
* Returns the selected text, or null if no selection is active. |
1115 |
*/ |
1116 |
public final String getSelectedText() |
1117 |
{ |
1118 |
if(selectionStart == selectionEnd) |
1119 |
return null; |
1120 |
|
1121 |
if(rectSelect) |
1122 |
{ |
1123 |
// Return each row of the selection on a new line |
1124 |
|
1125 |
Element map = document.getDefaultRootElement(); |
1126 |
|
1127 |
int start = selectionStart - map.getElement(selectionStartLine) |
1128 |
.getStartOffset(); |
1129 |
int end = selectionEnd - map.getElement(selectionEndLine) |
1130 |
.getStartOffset(); |
1131 |
|
1132 |
// Certain rectangles satisfy this condition... |
1133 |
if(end < start) |
1134 |
{ |
1135 |
int tmp = end; |
1136 |
end = start; |
1137 |
start = tmp; |
1138 |
} |
1139 |
|
1140 |
StringBuffer buf = new StringBuffer(); |
1141 |
Segment seg = new Segment(); |
1142 |
|
1143 |
for(int i = selectionStartLine; i <= selectionEndLine; i++) |
1144 |
{ |
1145 |
Element lineElement = map.getElement(i); |
1146 |
int lineStart = lineElement.getStartOffset(); |
1147 |
int lineEnd = lineElement.getEndOffset() - 1; |
1148 |
int lineLen = lineEnd - lineStart; |
1149 |
|
1150 |
lineStart = Math.min(lineStart + start,lineEnd); |
1151 |
lineLen = Math.min(end - start,lineEnd - lineStart); |
1152 |
|
1153 |
getText(lineStart,lineLen,seg); |
1154 |
buf.append(seg.array,seg.offset,seg.count); |
1155 |
|
1156 |
if(i != selectionEndLine) |
1157 |
buf.append('\n'); |
1158 |
} |
1159 |
|
1160 |
return buf.toString(); |
1161 |
} |
1162 |
else |
1163 |
{ |
1164 |
return getText(selectionStart, |
1165 |
selectionEnd - selectionStart); |
1166 |
} |
1167 |
} |
1168 |
|
1169 |
/** |
1170 |
* Replaces the selection with the specified text. |
1171 |
* @param selectedText The replacement text for the selection |
1172 |
*/ |
1173 |
public void setSelectedText(String selectedText) |
1174 |
{ |
1175 |
if(!editable) |
1176 |
{ |
1177 |
throw new InternalError("Text component" |
1178 |
+ " read only"); |
1179 |
} |
1180 |
|
1181 |
document.beginCompoundEdit(); |
1182 |
|
1183 |
try |
1184 |
{ |
1185 |
if(rectSelect) |
1186 |
{ |
1187 |
Element map = document.getDefaultRootElement(); |
1188 |
|
1189 |
int start = selectionStart - map.getElement(selectionStartLine) |
1190 |
.getStartOffset(); |
1191 |
int end = selectionEnd - map.getElement(selectionEndLine) |
1192 |
.getStartOffset(); |
1193 |
|
1194 |
// Certain rectangles satisfy this condition... |
1195 |
if(end < start) |
1196 |
{ |
1197 |
int tmp = end; |
1198 |
end = start; |
1199 |
start = tmp; |
1200 |
} |
1201 |
|
1202 |
int lastNewline = 0; |
1203 |
int currNewline = 0; |
1204 |
|
1205 |
for(int i = selectionStartLine; i <= selectionEndLine; i++) |
1206 |
{ |
1207 |
Element lineElement = map.getElement(i); |
1208 |
int lineStart = lineElement.getStartOffset(); |
1209 |
int lineEnd = lineElement.getEndOffset() - 1; |
1210 |
int rectStart = Math.min(lineEnd,lineStart + start); |
1211 |
|
1212 |
document.remove(rectStart,Math.min(lineEnd - rectStart, |
1213 |
end - start)); |
1214 |
|
1215 |
if(selectedText == null) |
1216 |
continue; |
1217 |
|
1218 |
currNewline = selectedText.indexOf('\n',lastNewline); |
1219 |
if(currNewline == -1) |
1220 |
currNewline = selectedText.length(); |
1221 |
|
1222 |
document.insertString(rectStart,selectedText |
1223 |
.substring(lastNewline,currNewline),null); |
1224 |
|
1225 |
lastNewline = Math.min(selectedText.length(), |
1226 |
currNewline + 1); |
1227 |
} |
1228 |
|
1229 |
if(selectedText != null && |
1230 |
currNewline != selectedText.length()) |
1231 |
{ |
1232 |
int offset = map.getElement(selectionEndLine) |
1233 |
.getEndOffset() - 1; |
1234 |
document.insertString(offset,"\n",null); |
1235 |
document.insertString(offset + 1,selectedText |
1236 |
.substring(currNewline + 1),null); |
1237 |
} |
1238 |
} |
1239 |
else |
1240 |
{ |
1241 |
document.remove(selectionStart, |
1242 |
selectionEnd - selectionStart); |
1243 |
if(selectedText != null) |
1244 |
{ |
1245 |
document.insertString(selectionStart, |
1246 |
selectedText,null); |
1247 |
} |
1248 |
} |
1249 |
} |
1250 |
catch(BadLocationException bl) |
1251 |
{ |
1252 |
bl.printStackTrace(); |
1253 |
throw new InternalError("Cannot replace" |
1254 |
+ " selection"); |
1255 |
} |
1256 |
// No matter what happends... stops us from leaving document |
1257 |
// in a bad state |
1258 |
finally |
1259 |
{ |
1260 |
document.endCompoundEdit(); |
1261 |
} |
1262 |
|
1263 |
setCaretPosition(selectionEnd); |
1264 |
} |
1265 |
|
1266 |
/** |
1267 |
* Returns true if this text area is editable, false otherwise. |
1268 |
*/ |
1269 |
public final boolean isEditable() |
1270 |
{ |
1271 |
return editable; |
1272 |
} |
1273 |
|
1274 |
/** |
1275 |
* Sets if this component is editable. |
1276 |
* @param editable True if this text area should be editable, |
1277 |
* false otherwise |
1278 |
*/ |
1279 |
public final void setEditable(boolean editable) |
1280 |
{ |
1281 |
this.editable = editable; |
1282 |
} |
1283 |
|
1284 |
/** |
1285 |
* Returns the right click popup menu. |
1286 |
*/ |
1287 |
public final JPopupMenu getRightClickPopup() |
1288 |
{ |
1289 |
return popup; |
1290 |
} |
1291 |
|
1292 |
/** |
1293 |
* Sets the right click popup menu. |
1294 |
* @param popup The popup |
1295 |
*/ |
1296 |
public final void setRightClickPopup(JPopupMenu popup) |
1297 |
{ |
1298 |
this.popup = popup; |
1299 |
} |
1300 |
|
1301 |
/** |
1302 |
* Returns the `magic' caret position. This can be used to preserve |
1303 |
* the column position when moving up and down lines. |
1304 |
*/ |
1305 |
public final int getMagicCaretPosition() |
1306 |
{ |
1307 |
return magicCaret; |
1308 |
} |
1309 |
|
1310 |
/** |
1311 |
* Sets the `magic' caret position. This can be used to preserve |
1312 |
* the column position when moving up and down lines. |
1313 |
* @param magicCaret The magic caret position |
1314 |
*/ |
1315 |
public final void setMagicCaretPosition(int magicCaret) |
1316 |
{ |
1317 |
this.magicCaret = magicCaret; |
1318 |
} |
1319 |
|
1320 |
/** |
1321 |
* Similar to <code>setSelectedText()</code>, but overstrikes the |
1322 |
* appropriate number of characters if overwrite mode is enabled. |
1323 |
* @param str The string |
1324 |
* @see #setSelectedText(String) |
1325 |
* @see #isOverwriteEnabled() |
1326 |
*/ |
1327 |
public void overwriteSetSelectedText(String str) |
1328 |
{ |
1329 |
// Don't overstrike if there is a selection |
1330 |
if(!overwrite || selectionStart != selectionEnd) |
1331 |
{ |
1332 |
setSelectedText(str); |
1333 |
return; |
1334 |
} |
1335 |
|
1336 |
// Don't overstrike if we're on the end of |
1337 |
// the line |
1338 |
int caret = getCaretPosition(); |
1339 |
int caretLineEnd = getLineEndOffset(getCaretLine()); |
1340 |
if(caretLineEnd - caret <= str.length()) |
1341 |
{ |
1342 |
setSelectedText(str); |
1343 |
return; |
1344 |
} |
1345 |
|
1346 |
document.beginCompoundEdit(); |
1347 |
|
1348 |
try |
1349 |
{ |
1350 |
document.remove(caret,str.length()); |
1351 |
document.insertString(caret,str,null); |
1352 |
} |
1353 |
catch(BadLocationException bl) |
1354 |
{ |
1355 |
bl.printStackTrace(); |
1356 |
} |
1357 |
finally |
1358 |
{ |
1359 |
document.endCompoundEdit(); |
1360 |
} |
1361 |
} |
1362 |
|
1363 |
/** |
1364 |
* Returns true if overwrite mode is enabled, false otherwise. |
1365 |
*/ |
1366 |
public final boolean isOverwriteEnabled() |
1367 |
{ |
1368 |
return overwrite; |
1369 |
} |
1370 |
|
1371 |
/** |
1372 |
* Sets if overwrite mode should be enabled. |
1373 |
* @param overwrite True if overwrite mode should be enabled, |
1374 |
* false otherwise. |
1375 |
*/ |
1376 |
public final void setOverwriteEnabled(boolean overwrite) |
1377 |
{ |
1378 |
this.overwrite = overwrite; |
1379 |
painter.invalidateSelectedLines(); |
1380 |
} |
1381 |
|
1382 |
/** |
1383 |
* Returns true if the selection is rectangular, false otherwise. |
1384 |
*/ |
1385 |
public final boolean isSelectionRectangular() |
1386 |
{ |
1387 |
return rectSelect; |
1388 |
} |
1389 |
|
1390 |
/** |
1391 |
* Sets if the selection should be rectangular. |
1392 |
* @param overwrite True if the selection should be rectangular, |
1393 |
* false otherwise. |
1394 |
*/ |
1395 |
public final void setSelectionRectangular(boolean rectSelect) |
1396 |
{ |
1397 |
this.rectSelect = rectSelect; |
1398 |
painter.invalidateSelectedLines(); |
1399 |
} |
1400 |
|
1401 |
/** |
1402 |
* Returns the position of the highlighted bracket (the bracket |
1403 |
* matching the one before the caret) |
1404 |
*/ |
1405 |
public final int getBracketPosition() |
1406 |
{ |
1407 |
return bracketPosition; |
1408 |
} |
1409 |
|
1410 |
/** |
1411 |
* Returns the line of the highlighted bracket (the bracket |
1412 |
* matching the one before the caret) |
1413 |
*/ |
1414 |
public final int getBracketLine() |
1415 |
{ |
1416 |
return bracketLine; |
1417 |
} |
1418 |
|
1419 |
/** |
1420 |
* Adds a caret change listener to this text area. |
1421 |
* @param listener The listener |
1422 |
*/ |
1423 |
public final void addCaretListener(CaretListener listener) |
1424 |
{ |
1425 |
listenerList.add(CaretListener.class,listener); |
1426 |
} |
1427 |
|
1428 |
/** |
1429 |
* Removes a caret change listener from this text area. |
1430 |
* @param listener The listener |
1431 |
*/ |
1432 |
public final void removeCaretListener(CaretListener listener) |
1433 |
{ |
1434 |
listenerList.remove(CaretListener.class,listener); |
1435 |
} |
1436 |
|
1437 |
/** |
1438 |
* Deletes the selected text from the text area and places it |
1439 |
* into the clipboard. |
1440 |
*/ |
1441 |
public void cut() |
1442 |
{ |
1443 |
if(editable) |
1444 |
{ |
1445 |
copy(); |
1446 |
setSelectedText(""); |
1447 |
} |
1448 |
} |
1449 |
|
1450 |
/** |
1451 |
* Places the selected text into the clipboard. |
1452 |
*/ |
1453 |
public void copy() |
1454 |
{ |
1455 |
if(selectionStart != selectionEnd) |
1456 |
{ |
1457 |
Clipboard clipboard = getToolkit().getSystemClipboard(); |
1458 |
|
1459 |
String selection = getSelectedText(); |
1460 |
|
1461 |
int repeatCount = inputHandler.getRepeatCount(); |
1462 |
StringBuffer buf = new StringBuffer(); |
1463 |
for(int i = 0; i < repeatCount; i++) |
1464 |
buf.append(selection); |
1465 |
|
1466 |
clipboard.setContents(new StringSelection(buf.toString()),null); |
1467 |
} |
1468 |
} |
1469 |
|
1470 |
/** |
1471 |
* Inserts the clipboard contents into the text. |
1472 |
*/ |
1473 |
public void paste() |
1474 |
{ |
1475 |
if(editable) |
1476 |
{ |
1477 |
Clipboard clipboard = getToolkit().getSystemClipboard(); |
1478 |
try |
1479 |
{ |
1480 |
// The MacOS MRJ doesn't convert \r to \n, |
1481 |
// so do it here |
1482 |
String selection = ((String)clipboard |
1483 |
.getContents(this).getTransferData( |
1484 |
DataFlavor.stringFlavor)) |
1485 |
.replace('\r','\n'); |
1486 |
|
1487 |
int repeatCount = inputHandler.getRepeatCount(); |
1488 |
StringBuffer buf = new StringBuffer(); |
1489 |
for(int i = 0; i < repeatCount; i++) |
1490 |
buf.append(selection); |
1491 |
selection = buf.toString(); |
1492 |
setSelectedText(selection); |
1493 |
} |
1494 |
catch(Exception e) |
1495 |
{ |
1496 |
getToolkit().beep(); |
1497 |
System.err.println("Clipboard does not" |
1498 |
+ " contain a string"); |
1499 |
} |
1500 |
} |
1501 |
} |
1502 |
|
1503 |
/** |
1504 |
* Called by the AWT when this component is removed from it's parent. |
1505 |
* This stops clears the currently focused component. |
1506 |
*/ |
1507 |
public void removeNotify() |
1508 |
{ |
1509 |
super.removeNotify(); |
1510 |
if(focusedComponent == this) |
1511 |
focusedComponent = null; |
1512 |
} |
1513 |
|
1514 |
/** |
1515 |
* Forwards key events directly to the input handler. |
1516 |
* This is slightly faster than using a KeyListener |
1517 |
* because some Swing overhead is avoided. |
1518 |
*/ |
1519 |
public void processKeyEvent(KeyEvent evt) |
1520 |
{ |
1521 |
if(inputHandler == null) |
1522 |
return; |
1523 |
switch(evt.getID()) |
1524 |
{ |
1525 |
case KeyEvent.KEY_TYPED: |
1526 |
inputHandler.keyTyped(evt); |
1527 |
break; |
1528 |
case KeyEvent.KEY_PRESSED: |
1529 |
inputHandler.keyPressed(evt); |
1530 |
break; |
1531 |
case KeyEvent.KEY_RELEASED: |
1532 |
inputHandler.keyReleased(evt); |
1533 |
break; |
1534 |
} |
1535 |
} |
1536 |
|
1537 |
// protected members |
1538 |
protected static String CENTER = "center"; |
1539 |
protected static String RIGHT = "right"; |
1540 |
protected static String BOTTOM = "bottom"; |
1541 |
|
1542 |
protected static JEditTextArea focusedComponent; |
1543 |
protected static Timer caretTimer; |
1544 |
|
1545 |
protected TextAreaPainter painter; |
1546 |
|
1547 |
protected JPopupMenu popup; |
1548 |
|
1549 |
protected EventListenerList listenerList; |
1550 |
protected MutableCaretEvent caretEvent; |
1551 |
|
1552 |
protected boolean caretBlinks; |
1553 |
protected boolean caretVisible; |
1554 |
protected boolean blink; |
1555 |
|
1556 |
protected boolean editable; |
1557 |
|
1558 |
protected int firstLine; |
1559 |
protected int visibleLines; |
1560 |
protected int electricScroll; |
1561 |
|
1562 |
protected int horizontalOffset; |
1563 |
|
1564 |
protected JScrollBar vertical; |
1565 |
protected JScrollBar horizontal; |
1566 |
protected boolean scrollBarsInitialized; |
1567 |
|
1568 |
protected InputHandler inputHandler; |
1569 |
protected SyntaxDocument document; |
1570 |
protected DocumentHandler documentHandler; |
1571 |
|
1572 |
protected Segment lineSegment; |
1573 |
|
1574 |
protected int selectionStart; |
1575 |
protected int selectionStartLine; |
1576 |
protected int selectionEnd; |
1577 |
protected int selectionEndLine; |
1578 |
protected boolean biasLeft; |
1579 |
|
1580 |
protected int bracketPosition; |
1581 |
protected int bracketLine; |
1582 |
|
1583 |
protected int magicCaret; |
1584 |
protected boolean overwrite; |
1585 |
protected boolean rectSelect; |
1586 |
|
1587 |
protected void fireCaretEvent() |
1588 |
{ |
1589 |
Object[] listeners = listenerList.getListenerList(); |
1590 |
for(int i = listeners.length - 2; i >= 0; i--) |
1591 |
{ |
1592 |
if(listeners[i] == CaretListener.class) |
1593 |
{ |
1594 |
((CaretListener)listeners[i+1]).caretUpdate(caretEvent); |
1595 |
} |
1596 |
} |
1597 |
} |
1598 |
|
1599 |
protected void updateBracketHighlight(int newCaretPosition) |
1600 |
{ |
1601 |
if(newCaretPosition == 0) |
1602 |
{ |
1603 |
bracketPosition = bracketLine = -1; |
1604 |
return; |
1605 |
} |
1606 |
|
1607 |
try |
1608 |
{ |
1609 |
int offset = TextUtilities.findMatchingBracket( |
1610 |
document,newCaretPosition - 1); |
1611 |
if(offset != -1) |
1612 |
{ |
1613 |
bracketLine = getLineOfOffset(offset); |
1614 |
bracketPosition = offset - getLineStartOffset(bracketLine); |
1615 |
return; |
1616 |
} |
1617 |
} |
1618 |
catch(BadLocationException bl) |
1619 |
{ |
1620 |
bl.printStackTrace(); |
1621 |
} |
1622 |
|
1623 |
bracketLine = bracketPosition = -1; |
1624 |
} |
1625 |
|
1626 |
protected void documentChanged(DocumentEvent evt) |
1627 |
{ |
1628 |
DocumentEvent.ElementChange ch = evt.getChange( |
1629 |
document.getDefaultRootElement()); |
1630 |
|
1631 |
int count; |
1632 |
if(ch == null) |
1633 |
count = 0; |
1634 |
else |
1635 |
count = ch.getChildrenAdded().length - |
1636 |
ch.getChildrenRemoved().length; |
1637 |
|
1638 |
int line = getLineOfOffset(evt.getOffset()); |
1639 |
if(count == 0) |
1640 |
{ |
1641 |
painter.invalidateLine(line); |
1642 |
} |
1643 |
// do magic stuff |
1644 |
else if(line < firstLine) |
1645 |
{ |
1646 |
setFirstLine(firstLine + count); |
1647 |
} |
1648 |
// end of magic stuff |
1649 |
else |
1650 |
{ |
1651 |
painter.invalidateLineRange(line,firstLine + visibleLines); |
1652 |
updateScrollBars(); |
1653 |
} |
1654 |
} |
1655 |
|
1656 |
class ScrollLayout implements LayoutManager |
1657 |
{ |
1658 |
public void addLayoutComponent(String name, Component comp) |
1659 |
{ |
1660 |
if(name.equals(CENTER)) |
1661 |
center = comp; |
1662 |
else if(name.equals(RIGHT)) |
1663 |
right = comp; |
1664 |
else if(name.equals(BOTTOM)) |
1665 |
bottom = comp; |
1666 |
else if(name.equals(LEFT_OF_SCROLLBAR)) |
1667 |
leftOfScrollBar.addElement(comp); |
1668 |
} |
1669 |
|
1670 |
public void removeLayoutComponent(Component comp) |
1671 |
{ |
1672 |
if(center == comp) |
1673 |
center = null; |
1674 |
if(right == comp) |
1675 |
right = null; |
1676 |
if(bottom == comp) |
1677 |
bottom = null; |
1678 |
else |
1679 |
leftOfScrollBar.removeElement(comp); |
1680 |
} |
1681 |
|
1682 |
public Dimension preferredLayoutSize(Container parent) |
1683 |
{ |
1684 |
Dimension dim = new Dimension(); |
1685 |
Insets insets = getInsets(); |
1686 |
dim.width = insets.left + insets.right; |
1687 |
dim.height = insets.top + insets.bottom; |
1688 |
|
1689 |
Dimension centerPref = center.getPreferredSize(); |
1690 |
dim.width += centerPref.width; |
1691 |
dim.height += centerPref.height; |
1692 |
Dimension rightPref = right.getPreferredSize(); |
1693 |
dim.width += rightPref.width; |
1694 |
Dimension bottomPref = bottom.getPreferredSize(); |
1695 |
dim.height += bottomPref.height; |
1696 |
|
1697 |
return dim; |
1698 |
} |
1699 |
|
1700 |
public Dimension minimumLayoutSize(Container parent) |
1701 |
{ |
1702 |
Dimension dim = new Dimension(); |
1703 |
Insets insets = getInsets(); |
1704 |
dim.width = insets.left + insets.right; |
1705 |
dim.height = insets.top + insets.bottom; |
1706 |
|
1707 |
Dimension centerPref = center.getMinimumSize(); |
1708 |
dim.width += centerPref.width; |
1709 |
dim.height += centerPref.height; |
1710 |
Dimension rightPref = right.getMinimumSize(); |
1711 |
dim.width += rightPref.width; |
1712 |
Dimension bottomPref = bottom.getMinimumSize(); |
1713 |
dim.height += bottomPref.height; |
1714 |
|
1715 |
return dim; |
1716 |
} |
1717 |
|
1718 |
public void layoutContainer(Container parent) |
1719 |
{ |
1720 |
Dimension size = parent.getSize(); |
1721 |
Insets insets = parent.getInsets(); |
1722 |
int itop = insets.top; |
1723 |
int ileft = insets.left; |
1724 |
int ibottom = insets.bottom; |
1725 |
int iright = insets.right; |
1726 |
|
1727 |
int rightWidth = right.getPreferredSize().width; |
1728 |
int bottomHeight = bottom.getPreferredSize().height; |
1729 |
int centerWidth = size.width - rightWidth - ileft - iright; |
1730 |
int centerHeight = size.height - bottomHeight - itop - ibottom; |
1731 |
|
1732 |
center.setBounds( |
1733 |
ileft, |
1734 |
itop, |
1735 |
centerWidth, |
1736 |
centerHeight); |
1737 |
|
1738 |
right.setBounds( |
1739 |
ileft + centerWidth, |
1740 |
itop, |
1741 |
rightWidth, |
1742 |
centerHeight); |
1743 |
|
1744 |
// Lay out all status components, in order |
1745 |
Enumeration status = leftOfScrollBar.elements(); |
1746 |
while(status.hasMoreElements()) |
1747 |
{ |
1748 |
Component comp = (Component)status.nextElement(); |
1749 |
Dimension dim = comp.getPreferredSize(); |
1750 |
comp.setBounds(ileft, |
1751 |
itop + centerHeight, |
1752 |
dim.width, |
1753 |
bottomHeight); |
1754 |
ileft += dim.width; |
1755 |
} |
1756 |
|
1757 |
bottom.setBounds( |
1758 |
ileft, |
1759 |
itop + centerHeight, |
1760 |
size.width - rightWidth - ileft - iright, |
1761 |
bottomHeight); |
1762 |
} |
1763 |
|
1764 |
// private members |
1765 |
private Component center; |
1766 |
private Component right; |
1767 |
private Component bottom; |
1768 |
private Vector leftOfScrollBar = new Vector(); |
1769 |
} |
1770 |
|
1771 |
static class CaretBlinker implements ActionListener |
1772 |
{ |
1773 |
public void actionPerformed(ActionEvent evt) |
1774 |
{ |
1775 |
if(focusedComponent != null |
1776 |
&& focusedComponent.hasFocus()) |
1777 |
focusedComponent.blinkCaret(); |
1778 |
} |
1779 |
} |
1780 |
|
1781 |
class MutableCaretEvent extends CaretEvent |
1782 |
{ |
1783 |
MutableCaretEvent() |
1784 |
{ |
1785 |
super(JEditTextArea.this); |
1786 |
} |
1787 |
|
1788 |
public int getDot() |
1789 |
{ |
1790 |
return getCaretPosition(); |
1791 |
} |
1792 |
|
1793 |
public int getMark() |
1794 |
{ |
1795 |
return getMarkPosition(); |
1796 |
} |
1797 |
} |
1798 |
|
1799 |
class AdjustHandler implements AdjustmentListener |
1800 |
{ |
1801 |
public void adjustmentValueChanged(final AdjustmentEvent evt) |
1802 |
{ |
1803 |
if(!scrollBarsInitialized) |
1804 |
return; |
1805 |
|
1806 |
// If this is not done, mousePressed events accumilate |
1807 |
// and the result is that scrolling doesn't stop after |
1808 |
// the mouse is released |
1809 |
SwingUtilities.invokeLater(new Runnable() { |
1810 |
public void run() |
1811 |
{ |
1812 |
if(evt.getAdjustable() == vertical) |
1813 |
setFirstLine(vertical.getValue()); |
1814 |
else |
1815 |
setHorizontalOffset(-horizontal.getValue()); |
1816 |
} |
1817 |
}); |
1818 |
} |
1819 |
} |
1820 |
|
1821 |
class ComponentHandler extends ComponentAdapter |
1822 |
{ |
1823 |
public void componentResized(ComponentEvent evt) |
1824 |
{ |
1825 |
recalculateVisibleLines(); |
1826 |
scrollBarsInitialized = true; |
1827 |
} |
1828 |
} |
1829 |
|
1830 |
class DocumentHandler implements DocumentListener |
1831 |
{ |
1832 |
public void insertUpdate(DocumentEvent evt) |
1833 |
{ |
1834 |
documentChanged(evt); |
1835 |
|
1836 |
int offset = evt.getOffset(); |
1837 |
int length = evt.getLength(); |
1838 |
|
1839 |
int newStart; |
1840 |
int newEnd; |
1841 |
|
1842 |
if(selectionStart > offset || (selectionStart |
1843 |
== selectionEnd && selectionStart == offset)) |
1844 |
newStart = selectionStart + length; |
1845 |
else |
1846 |
newStart = selectionStart; |
1847 |
|
1848 |
if(selectionEnd >= offset) |
1849 |
newEnd = selectionEnd + length; |
1850 |
else |
1851 |
newEnd = selectionEnd; |
1852 |
|
1853 |
select(newStart,newEnd); |
1854 |
} |
1855 |
|
1856 |
public void removeUpdate(DocumentEvent evt) |
1857 |
{ |
1858 |
documentChanged(evt); |
1859 |
|
1860 |
int offset = evt.getOffset(); |
1861 |
int length = evt.getLength(); |
1862 |
|
1863 |
int newStart; |
1864 |
int newEnd; |
1865 |
|
1866 |
if(selectionStart > offset) |
1867 |
{ |
1868 |
if(selectionStart > offset + length) |
1869 |
newStart = selectionStart - length; |
1870 |
else |
1871 |
newStart = offset; |
1872 |
} |
1873 |
else |
1874 |
newStart = selectionStart; |
1875 |
|
1876 |
if(selectionEnd > offset) |
1877 |
{ |
1878 |
if(selectionEnd > offset + length) |
1879 |
newEnd = selectionEnd - length; |
1880 |
else |
1881 |
newEnd = offset; |
1882 |
} |
1883 |
else |
1884 |
newEnd = selectionEnd; |
1885 |
|
1886 |
select(newStart,newEnd); |
1887 |
} |
1888 |
|
1889 |
public void changedUpdate(DocumentEvent evt) |
1890 |
{ |
1891 |
} |
1892 |
} |
1893 |
|
1894 |
class DragHandler implements MouseMotionListener |
1895 |
{ |
1896 |
public void mouseDragged(MouseEvent evt) |
1897 |
{ |
1898 |
if(popup != null && popup.isVisible()) |
1899 |
return; |
1900 |
|
1901 |
setSelectionRectangular((evt.getModifiers() |
1902 |
& InputEvent.CTRL_MASK) != 0); |
1903 |
select(getMarkPosition(),xyToOffset(evt.getX(),evt.getY())); |
1904 |
} |
1905 |
|
1906 |
public void mouseMoved(MouseEvent evt) {} |
1907 |
} |
1908 |
|
1909 |
class FocusHandler implements FocusListener |
1910 |
{ |
1911 |
public void focusGained(FocusEvent evt) |
1912 |
{ |
1913 |
setCaretVisible(true); |
1914 |
focusedComponent = JEditTextArea.this; |
1915 |
} |
1916 |
|
1917 |
public void focusLost(FocusEvent evt) |
1918 |
{ |
1919 |
setCaretVisible(false); |
1920 |
focusedComponent = null; |
1921 |
} |
1922 |
} |
1923 |
|
1924 |
class MouseHandler extends MouseAdapter |
1925 |
{ |
1926 |
public void mousePressed(MouseEvent evt) |
1927 |
{ |
1928 |
requestFocus(); |
1929 |
|
1930 |
// Focus events not fired sometimes? |
1931 |
setCaretVisible(true); |
1932 |
focusedComponent = JEditTextArea.this; |
1933 |
|
1934 |
if((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0 |
1935 |
&& popup != null) |
1936 |
{ |
1937 |
popup.show(painter,evt.getX(),evt.getY()); |
1938 |
return; |
1939 |
} |
1940 |
|
1941 |
int line = yToLine(evt.getY()); |
1942 |
int offset = xToOffset(line,evt.getX()); |
1943 |
int dot = getLineStartOffset(line) + offset; |
1944 |
|
1945 |
switch(evt.getClickCount()) |
1946 |
{ |
1947 |
case 1: |
1948 |
doSingleClick(evt,line,offset,dot); |
1949 |
break; |
1950 |
case 2: |
1951 |
// It uses the bracket matching stuff, so |
1952 |
// it can throw a BLE |
1953 |
try |
1954 |
{ |
1955 |
doDoubleClick(evt,line,offset,dot); |
1956 |
} |
1957 |
catch(BadLocationException bl) |
1958 |
{ |
1959 |
bl.printStackTrace(); |
1960 |
} |
1961 |
break; |
1962 |
case 3: |
1963 |
doTripleClick(evt,line,offset,dot); |
1964 |
break; |
1965 |
} |
1966 |
} |
1967 |
|
1968 |
private void doSingleClick(MouseEvent evt, int line, |
1969 |
int offset, int dot) |
1970 |
{ |
1971 |
if((evt.getModifiers() & InputEvent.SHIFT_MASK) != 0) |
1972 |
{ |
1973 |
rectSelect = (evt.getModifiers() & InputEvent.CTRL_MASK) != 0; |
1974 |
select(getMarkPosition(),dot); |
1975 |
} |
1976 |
else |
1977 |
setCaretPosition(dot); |
1978 |
} |
1979 |
|
1980 |
private void doDoubleClick(MouseEvent evt, int line, |
1981 |
int offset, int dot) throws BadLocationException |
1982 |
{ |
1983 |
// Ignore empty lines |
1984 |
if(getLineLength(line) == 0) |
1985 |
return; |
1986 |
|
1987 |
try |
1988 |
{ |
1989 |
int bracket = TextUtilities.findMatchingBracket( |
1990 |
document,Math.max(0,dot - 1)); |
1991 |
if(bracket != -1) |
1992 |
{ |
1993 |
int mark = getMarkPosition(); |
1994 |
// Hack |
1995 |
if(bracket > mark) |
1996 |
{ |
1997 |
bracket++; |
1998 |
mark--; |
1999 |
} |
2000 |
select(mark,bracket); |
2001 |
return; |
2002 |
} |
2003 |
} |
2004 |
catch(BadLocationException bl) |
2005 |
{ |
2006 |
bl.printStackTrace(); |
2007 |
} |
2008 |
|
2009 |
// Ok, it's not a bracket... select the word |
2010 |
String lineText = getLineText(line); |
2011 |
char ch = lineText.charAt(Math.max(0,offset - 1)); |
2012 |
|
2013 |
String noWordSep = (String)document.getProperty("noWordSep"); |
2014 |
if(noWordSep == null) |
2015 |
noWordSep = ""; |
2016 |
|
2017 |
// If the user clicked on a non-letter char, |
2018 |
// we select the surrounding non-letters |
2019 |
boolean selectNoLetter = (!Character |
2020 |
.isLetterOrDigit(ch) |
2021 |
&& noWordSep.indexOf(ch) == -1); |
2022 |
|
2023 |
int wordStart = 0; |
2024 |
|
2025 |
for(int i = offset - 1; i >= 0; i--) |
2026 |
{ |
2027 |
ch = lineText.charAt(i); |
2028 |
if(selectNoLetter ^ (!Character |
2029 |
.isLetterOrDigit(ch) && |
2030 |
noWordSep.indexOf(ch) == -1)) |
2031 |
{ |
2032 |
wordStart = i + 1; |
2033 |
break; |
2034 |
} |
2035 |
} |
2036 |
|
2037 |
int wordEnd = lineText.length(); |
2038 |
for(int i = offset; i < lineText.length(); i++) |
2039 |
{ |
2040 |
ch = lineText.charAt(i); |
2041 |
if(selectNoLetter ^ (!Character |
2042 |
.isLetterOrDigit(ch) && |
2043 |
noWordSep.indexOf(ch) == -1)) |
2044 |
{ |
2045 |
wordEnd = i; |
2046 |
break; |
2047 |
} |
2048 |
} |
2049 |
|
2050 |
int lineStart = getLineStartOffset(line); |
2051 |
select(lineStart + wordStart,lineStart + wordEnd); |
2052 |
|
2053 |
/* |
2054 |
String lineText = getLineText(line); |
2055 |
String noWordSep = (String)document.getProperty("noWordSep"); |
2056 |
int wordStart = TextUtilities.findWordStart(lineText,offset,noWordSep); |
2057 |
int wordEnd = TextUtilities.findWordEnd(lineText,offset,noWordSep); |
2058 |
|
2059 |
int lineStart = getLineStartOffset(line); |
2060 |
select(lineStart + wordStart,lineStart + wordEnd); |
2061 |
*/ |
2062 |
} |
2063 |
|
2064 |
private void doTripleClick(MouseEvent evt, int line, |
2065 |
int offset, int dot) |
2066 |
{ |
2067 |
select(getLineStartOffset(line),getLineEndOffset(line)-1); |
2068 |
} |
2069 |
} |
2070 |
|
2071 |
class CaretUndo extends AbstractUndoableEdit |
2072 |
{ |
2073 |
private int start; |
2074 |
private int end; |
2075 |
|
2076 |
CaretUndo(int start, int end) |
2077 |
{ |
2078 |
this.start = start; |
2079 |
this.end = end; |
2080 |
} |
2081 |
|
2082 |
public boolean isSignificant() |
2083 |
{ |
2084 |
return false; |
2085 |
} |
2086 |
|
2087 |
public String getPresentationName() |
2088 |
{ |
2089 |
return "caret move"; |
2090 |
} |
2091 |
|
2092 |
public void undo() throws CannotUndoException |
2093 |
{ |
2094 |
super.undo(); |
2095 |
|
2096 |
select(start,end); |
2097 |
} |
2098 |
|
2099 |
public void redo() throws CannotRedoException |
2100 |
{ |
2101 |
super.redo(); |
2102 |
|
2103 |
select(start,end); |
2104 |
} |
2105 |
|
2106 |
public boolean addEdit(UndoableEdit edit) |
2107 |
{ |
2108 |
if(edit instanceof CaretUndo) |
2109 |
{ |
2110 |
CaretUndo cedit = (CaretUndo)edit; |
2111 |
start = cedit.start; |
2112 |
end = cedit.end; |
2113 |
cedit.die(); |
2114 |
|
2115 |
return true; |
2116 |
} |
2117 |
else |
2118 |
return false; |
2119 |
} |
2120 |
} |
2121 |
|
2122 |
static |
2123 |
{ |
2124 |
caretTimer = new Timer(500,new CaretBlinker()); |
2125 |
caretTimer.setInitialDelay(500); |
2126 |
caretTimer.start(); |
2127 |
} |
2128 |
} |