KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > syntax > jedit > JEditTextArea


1 /*
2  * Copyright (C) 2005 - 2006 JasperSoft Corporation. All rights reserved.
3  * http://www.jaspersoft.com.
4  *
5  * Unless you have purchased a commercial license agreement from JasperSoft,
6  * the following license terms apply:
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as published by
10  * the Free Software Foundation.
11  *
12  * This program is distributed WITHOUT ANY WARRANTY; and without the
13  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  * See the GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see http://www.gnu.org/licenses/gpl.txt
18  * or write to:
19  *
20  * Free Software Foundation, Inc.,
21  * 59 Temple Place - Suite 330,
22  * Boston, MA USA 02111-1307
23  *
24  *
25  *
26  *
27  * JEditTextArea.java
28  *
29  */

30
31 package org.syntax.jedit;
32
33 import org.syntax.jedit.tokenmarker.*;
34 import javax.swing.event.*;
35 import javax.swing.text.*;
36 import javax.swing.undo.*;
37 import javax.swing.*;
38 import java.awt.datatransfer.*;
39 import java.awt.event.*;
40 import java.awt.*;
41 import java.util.Enumeration JavaDoc;
42 import java.util.Vector JavaDoc;
43
44 /**
45  * jEdit's text area component. It is more suited for editing program
46  * source code than JEditorPane, because it drops the unnecessary features
47  * (images, variable-width lines, and so on) and adds a whole bunch of
48  * useful goodies such as:
49  * <ul>
50  * <li>More flexible key binding scheme
51  * <li>Supports macro recorders
52  * <li>Rectangular selection
53  * <li>Bracket highlighting
54  * <li>Syntax highlighting
55  * <li>Command repetition
56  * <li>Block caret can be enabled
57  * </ul>
58  * It is also faster and doesn't have as many problems. It can be used
59  * in other applications; the only other part of jEdit it depends on is
60  * the syntax package.<p>
61  *
62  * To use it in your app, treat it like any other component, for example:
63  * <pre>JEditTextArea ta = new JEditTextArea();
64  * ta.setTokenMarker(new JavaTokenMarker());
65  * ta.setText("public class Test {\n"
66  * + " public static void main(String[] args) {\n"
67  * + " System.out.println(\"Hello World\");\n"
68  * + " }\n"
69  * + "}");</pre>
70  *
71  * @author Slava Pestov
72  * @version $Id: JEditTextArea.java 932 2006-10-20 09:32:45Z gtoffoli $
73  */

74 public class JEditTextArea extends JComponent
75 {
76     /**
77      * Adding components with this name to the text area will place
78      * them left of the horizontal scroll bar. In jEdit, the status
79      * bar is added this way.
80      */

81     public static String JavaDoc LEFT_OF_SCROLLBAR = "los";
82
83     /**
84      * Creates a new JEditTextArea with the default settings.
85      */

86     public JEditTextArea()
87     {
88         this(TextAreaDefaults.getDefaults());
89     }
90         
91         private JButton editButton = null;
92         private JPanel rightpanel = null;
93         
94         public void editButtonActionPerformed(ActionEvent e)
95         {
96             
97         }
98         
99         public void setEditButtonVisible(boolean b)
100         {
101             editButton.setVisible(b);
102             rightpanel.updateUI();
103             this.updateUI();
104         }
105
106     /**
107      * Creates a new JEditTextArea with the specified settings.
108      * @param defaults The default settings
109      */

110     public JEditTextArea(TextAreaDefaults defaults)
111     {
112         // Enable the necessary events
113
enableEvents(AWTEvent.KEY_EVENT_MASK);
114
115         // Initialize some misc. stuff
116
painter = new TextAreaPainter(this,defaults);
117         documentHandler = new DocumentHandler();
118         listenerList = new EventListenerList();
119         caretEvent = new MutableCaretEvent();
120         lineSegment = new Segment();
121         bracketLine = bracketPosition = -1;
122         blink = true;
123
124         // Initialize the GUI
125
setLayout(new ScrollLayout());
126         add(CENTER,painter);
127                 
128                 rightpanel = new JPanel();
129                 rightpanel.setLayout(new GridBagLayout()); //new BorderLayout());
130

131                 java.awt.GridBagConstraints JavaDoc gridBagConstraints = new java.awt.GridBagConstraints JavaDoc();
132                 gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
133                 gridBagConstraints.weightx = 1.0;
134                 gridBagConstraints.weighty = 1.0;
135                 rightpanel.add(vertical = new JScrollBar(JScrollBar.VERTICAL), gridBagConstraints);
136         
137                 
138                 editButton = new JButton( new javax.swing.ImageIcon JavaDoc(getClass().getResource("/it/businesslogic/ireport/icons/text_edit.png")) );
139                 editButton.setPreferredSize(new java.awt.Dimension JavaDoc(22,22) );
140                 editButton.setMinimumSize(new java.awt.Dimension JavaDoc(22,22) );
141                 editButton.setMaximumSize(new java.awt.Dimension JavaDoc(22,22) );
142                 
143                 editButton.setToolTipText("Open the expression editor");
144                 editButton.setBorder(null);
145                 editButton.setBorderPainted(false);
146                 
147                 gridBagConstraints = new java.awt.GridBagConstraints JavaDoc();
148                 gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
149                 gridBagConstraints.weighty = 1.0;
150                 gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
151                 
152                 rightpanel.add(editButton, gridBagConstraints);
153                 
154                 //rightpanel.add(editButton, BorderLayout.EAST );
155
add(RIGHT,rightpanel);
156                 
157                 editButton.setVisible(false);
158                 editButton.addActionListener( new ActionListener() {
159                     public void actionPerformed(ActionEvent e) {
160                         editButtonActionPerformed(e);
161                     }
162                 });
163         
164         
165                 //add(RIGHT,vertical = new JScrollBar(JScrollBar.VERTICAL) ));
166
add(BOTTOM,horizontal = new JScrollBar(JScrollBar.HORIZONTAL));
167         // Add some event listeners
168
vertical.addAdjustmentListener(new AdjustHandler());
169         horizontal.addAdjustmentListener(new AdjustHandler());
170         painter.addComponentListener(new ComponentHandler());
171         painter.addMouseListener(new MouseHandler());
172         painter.addMouseMotionListener(new DragHandler());
173         addFocusListener(new FocusHandler());
174
175         // Load the defaults
176
setInputHandler(defaults.inputHandler);
177         setDocument(defaults.document);
178         editable = defaults.editable;
179         caretVisible = defaults.caretVisible;
180         caretBlinks = defaults.caretBlinks;
181         electricScroll = defaults.electricScroll;
182
183         popup = defaults.popup;
184
185         // We don't seem to get the initial focus event?
186
focusedComponent = this;
187     }
188
189     /**
190      * Returns if this component can be traversed by pressing
191      * the Tab key. This returns false.
192      */

193     public final boolean isManagingFocus()
194     {
195         return true;
196     }
197
198     /**
199      * Returns the object responsible for painting this text area.
200      */

201     public final TextAreaPainter getPainter()
202     {
203         return painter;
204     }
205
206     /**
207      * Returns the input handler.
208      */

209     public final InputHandler getInputHandler()
210     {
211         return inputHandler;
212     }
213
214     /**
215      * Sets the input handler.
216      * @param inputHandler The new input handler
217      */

218     public void setInputHandler(InputHandler inputHandler)
219     {
220         this.inputHandler = inputHandler;
221     }
222
223     /**
224      * Returns true if the caret is blinking, false otherwise.
225      */

226     public final boolean isCaretBlinkEnabled()
227     {
228         return caretBlinks;
229     }
230
231     /**
232      * Toggles caret blinking.
233      * @param caretBlinks True if the caret should blink, false otherwise
234      */

235     public void setCaretBlinkEnabled(boolean caretBlinks)
236     {
237         this.caretBlinks = caretBlinks;
238         if(!caretBlinks)
239             blink = false;
240
241         painter.invalidateSelectedLines();
242     }
243
244     /**
245      * Returns true if the caret is visible, false otherwise.
246      */

247     public final boolean isCaretVisible()
248     {
249         return (!caretBlinks || blink) && caretVisible;
250     }
251
252     /**
253      * Sets if the caret should be visible.
254      * @param caretVisible True if the caret should be visible, false
255      * otherwise
256      */

257     public void setCaretVisible(boolean caretVisible)
258     {
259         this.caretVisible = caretVisible;
260         blink = true;
261
262         painter.invalidateSelectedLines();
263     }
264
265     /**
266      * Blinks the caret.
267      */

268     public final void blinkCaret()
269     {
270         if(caretBlinks)
271         {
272             blink = !blink;
273             painter.invalidateSelectedLines();
274         }
275         else
276             blink = true;
277     }
278
279     /**
280      * Returns the number of lines from the top and button of the
281      * text area that are always visible.
282      */

283     public final int getElectricScroll()
284     {
285         return electricScroll;
286     }
287
288     /**
289      * Sets the number of lines from the top and bottom of the text
290      * area that are always visible
291      * @param electricScroll The number of lines always visible from
292      * the top or bottom
293      */

294     public final void setElectricScroll(int electricScroll)
295     {
296         this.electricScroll = electricScroll;
297     }
298
299     /**
300      * Updates the state of the scroll bars. This should be called
301      * if the number of lines in the document changes, or when the
302      * size of the text are changes.
303      */

304     public void updateScrollBars()
305     {
306         if(vertical != null && visibleLines != 0)
307         {
308             vertical.setValues(firstLine,visibleLines,0,getLineCount());
309             vertical.setUnitIncrement(2);
310             vertical.setBlockIncrement(visibleLines);
311         }
312
313         int width = this.getWidth() - vertical.getWidth();
314                 
315                 if (editButton != null) width -= editButton.getWidth();
316                 
317         if(horizontal != null && width != 0)
318         {
319             horizontal.setValues(-horizontalOffset,width,0,width * 5);
320             horizontal.setUnitIncrement(painter.getFontMetrics()
321                 .charWidth('w'));
322             horizontal.setBlockIncrement(width / 2);
323         }
324                 
325                 if (this.getVisibleLines() <= this.getLineCount())
326                 {
327                    if (!vertical.isVisible())
328                    {
329                        vertical.setPreferredSize(new Dimension(16,0));
330                        vertical.setVisible(true);
331                    }
332                 }
333                 else
334                 {
335                     if (vertical.isVisible())
336                    {
337                        vertical.setPreferredSize(new Dimension(0,0));
338                        vertical.setVisible(false);
339                    }
340                    
341                 }
342                 
343                 // Trova la riga piu' linga...
344
int max = 0;
345                 int tmp_max = 0;
346                 for (int i=0 ; i<this.getLineCount(); ++i)
347                 {
348                     tmp_max = this.getLineText(i).length(); // getLineLength(i);
349
max = (max < tmp_max) ? tmp_max : max;
350                     //System.out.println(this.getLineText(i));
351
}
352                 if (max * painter.getFontMetrics().charWidth('w') > this.getWidth() )
353                 {
354                    if (!horizontal.isVisible())
355                    {
356                        horizontal.setPreferredSize(new Dimension(20,16));
357                        horizontal.setVisible(true);
358                    }
359                 }
360                 else
361                 {
362                     if (horizontal.isVisible())
363                    {
364                        horizontal.setPreferredSize(new Dimension(0,0));
365                        horizontal.setVisible(false);
366                    }
367                    
368                 }
369                // System.out.println(""+ this.getWidth() + "<" + this.getLineCount()+" "+max+" "+ painter.getFontMetrics().charWidth('w') );
370
}
371
372     /**
373      * Returns the line displayed at the text area's origin.
374      */

375     public final int getFirstLine()
376     {
377         return firstLine;
378     }
379
380     /**
381      * Sets the line displayed at the text area's origin without
382      * updating the scroll bars.
383      */

384     public void setFirstLine(int firstLine)
385     {
386         if(firstLine == this.firstLine)
387             return;
388         int oldFirstLine = this.firstLine;
389         this.firstLine = firstLine;
390         if(firstLine != vertical.getValue())
391             updateScrollBars();
392         painter.repaint();
393     }
394
395     /**
396      * Returns the number of lines visible in this text area.
397      */

398     public final int getVisibleLines()
399     {
400         return visibleLines;
401     }
402
403     /**
404      * Recalculates the number of visible lines. This should not
405      * be called directly.
406      */

407     public final void recalculateVisibleLines()
408     {
409         if(painter == null)
410             return;
411         int height = painter.getHeight();
412         int lineHeight = painter.getFontMetrics().getHeight();
413         int oldVisibleLines = visibleLines;
414         visibleLines = height / lineHeight;
415         updateScrollBars();
416     }
417
418     /**
419      * Returns the horizontal offset of drawn lines.
420      */

421     public final int getHorizontalOffset()
422     {
423         return horizontalOffset;
424     }
425
426     /**
427      * Sets the horizontal offset of drawn lines. This can be used to
428      * implement horizontal scrolling.
429      * @param horizontalOffset offset The new horizontal offset
430      */

431     public void setHorizontalOffset(int horizontalOffset)
432     {
433         if(horizontalOffset == this.horizontalOffset)
434             return;
435         this.horizontalOffset = horizontalOffset;
436         if(horizontalOffset != horizontal.getValue())
437             updateScrollBars();
438         painter.repaint();
439     }
440
441     /**
442      * A fast way of changing both the first line and horizontal
443      * offset.
444      * @param firstLine The new first line
445      * @param horizontalOffset The new horizontal offset
446      * @return True if any of the values were changed, false otherwise
447      */

448     public boolean setOrigin(int firstLine, int horizontalOffset)
449     {
450         boolean changed = false;
451         int oldFirstLine = this.firstLine;
452
453         if(horizontalOffset != this.horizontalOffset)
454         {
455             this.horizontalOffset = horizontalOffset;
456             changed = true;
457         }
458
459         if(firstLine != this.firstLine)
460         {
461             this.firstLine = firstLine;
462             changed = true;
463         }
464
465         if(changed)
466         {
467             updateScrollBars();
468             painter.repaint();
469         }
470
471         return changed;
472     }
473
474     /**
475      * Ensures that the caret is visible by scrolling the text area if
476      * necessary.
477      * @return True if scrolling was actually performed, false if the
478      * caret was already visible
479      */

480     public boolean scrollToCaret()
481     {
482         int line = getCaretLine();
483         int lineStart = getLineStartOffset(line);
484         int offset = Math.max(0,Math.min(getLineLength(line) - 1,
485             getCaretPosition() - lineStart));
486
487         return scrollTo(line,offset);
488     }
489
490     /**
491      * Ensures that the specified line and offset is visible by scrolling
492      * the text area if necessary.
493      * @param line The line to scroll to
494      * @param offset The offset in the line to scroll to
495      * @return True if scrolling was actually performed, false if the
496      * line and offset was already visible
497      */

498     public boolean scrollTo(int line, int offset)
499     {
500         // visibleLines == 0 before the component is realized
501
// we can't do any proper scrolling then, so we have
502
// this hack...
503
if(visibleLines == 0)
504         {
505             setFirstLine(Math.max(0,line - electricScroll));
506             return true;
507         }
508
509         int newFirstLine = firstLine;
510         int newHorizontalOffset = horizontalOffset;
511
512         if(line < firstLine + electricScroll)
513         {
514             newFirstLine = Math.max(0,line - electricScroll);
515         }
516         else if(line + electricScroll >= firstLine + visibleLines)
517         {
518             newFirstLine = (line - visibleLines) + electricScroll + 1;
519             if(newFirstLine + visibleLines >= getLineCount())
520                 newFirstLine = getLineCount() - visibleLines;
521             if(newFirstLine < 0)
522                 newFirstLine = 0;
523         }
524
525         int x = _offsetToX(line,offset);
526         int width = painter.getFontMetrics().charWidth('w');
527
528         if(x < 0)
529         {
530             newHorizontalOffset = Math.min(0,horizontalOffset
531                 - x + width + 5);
532         }
533         else if(x + width >= painter.getWidth())
534         {
535             newHorizontalOffset = horizontalOffset +
536                 (painter.getWidth() - x) - width - 5;
537         }
538
539         return setOrigin(newFirstLine,newHorizontalOffset);
540     }
541
542     /**
543      * Converts a line index to a y co-ordinate.
544      * @param line The line
545      */

546     public int lineToY(int line)
547     {
548         FontMetrics fm = painter.getFontMetrics();
549         return (line - firstLine) * fm.getHeight()
550             - (fm.getLeading() + fm.getMaxDescent());
551     }
552
553     /**
554      * Converts a y co-ordinate to a line index.
555      * @param y The y co-ordinate
556      */

557     public int yToLine(int y)
558     {
559         FontMetrics fm = painter.getFontMetrics();
560         int height = fm.getHeight();
561         return Math.max(0,Math.min(getLineCount() - 1,
562             y / height + firstLine));
563     }
564
565     /**
566      * Converts an offset in a line into an x co-ordinate. This is a
567      * slow version that can be used any time.
568      * @param line The line
569      * @param offset The offset, from the start of the line
570      */

571     public final int offsetToX(int line, int offset)
572     {
573         // don't use cached tokens
574
painter.currentLineTokens = null;
575         return _offsetToX(line,offset);
576     }
577
578     /**
579      * Converts an offset in a line into an x co-ordinate. This is a
580      * fast version that should only be used if no changes were made
581      * to the text since the last repaint.
582      * @param line The line
583      * @param offset The offset, from the start of the line
584      */

585     public int _offsetToX(int line, int offset)
586     {
587         TokenMarker tokenMarker = getTokenMarker();
588
589         /* Use painter's cached info for speed */
590         FontMetrics fm = painter.getFontMetrics();
591
592         getLineText(line,lineSegment);
593
594         int segmentOffset = lineSegment.offset;
595         int x = horizontalOffset;
596
597         /* If syntax coloring is disabled, do simple translation */
598         if(tokenMarker == null)
599         {
600             lineSegment.count = offset;
601             return x + Utilities.getTabbedTextWidth(lineSegment,
602                 fm,x,painter,0);
603         }
604         /* If syntax coloring is enabled, we have to do this because
605          * tokens can vary in width */

606         else
607         {
608             Token tokens;
609             if(painter.currentLineIndex == line
610                 && painter.currentLineTokens != null)
611                 tokens = painter.currentLineTokens;
612             else
613             {
614                 painter.currentLineIndex = line;
615                 tokens = painter.currentLineTokens
616                     = tokenMarker.markTokens(lineSegment,line);
617             }
618
619             Toolkit toolkit = painter.getToolkit();
620             Font defaultFont = painter.getFont();
621             SyntaxStyle[] styles = painter.getStyles();
622
623             for(;;)
624             {
625                 byte id = tokens.id;
626                 if(id == Token.END)
627                 {
628                     return x;
629                 }
630
631                 if(id == Token.NULL)
632                     fm = painter.getFontMetrics();
633                 else
634                     fm = styles[id].getFontMetrics(defaultFont);
635
636                 int length = tokens.length;
637
638                 if(offset + segmentOffset < lineSegment.offset + length)
639                 {
640                     lineSegment.count = offset - (lineSegment.offset - segmentOffset);
641                     return x + Utilities.getTabbedTextWidth(
642                         lineSegment,fm,x,painter,0);
643                 }
644                 else
645                 {
646                     lineSegment.count = length;
647                     x += Utilities.getTabbedTextWidth(
648                         lineSegment,fm,x,painter,0);
649                     lineSegment.offset += length;
650                 }
651                 tokens = tokens.next;
652             }
653         }
654     }
655
656     /**
657      * Converts an x co-ordinate to an offset within a line.
658      * @param line The line
659      * @param x The x co-ordinate
660      */

661     public int xToOffset(int line, int x)
662     {
663         TokenMarker tokenMarker = getTokenMarker();
664
665         /* Use painter's cached info for speed */
666         FontMetrics fm = painter.getFontMetrics();
667
668         getLineText(line,lineSegment);
669
670         char[] segmentArray = lineSegment.array;
671         int segmentOffset = lineSegment.offset;
672         int segmentCount = lineSegment.count;
673
674         int width = horizontalOffset;
675
676         if(tokenMarker == null)
677         {
678             for(int i = 0; i < segmentCount; i++)
679             {
680                 char c = segmentArray[i + segmentOffset];
681                 int charWidth;
682                 if(c == '\t')
683                     charWidth = (int)painter.nextTabStop(width,i)
684                         - width;
685                 else
686                     charWidth = fm.charWidth(c);
687
688                 if(painter.isBlockCaretEnabled())
689                 {
690                     if(x - charWidth <= width)
691                         return i;
692                 }
693                 else
694                 {
695                     if(x - charWidth / 2 <= width)
696                         return i;
697                 }
698
699                 width += charWidth;
700             }
701
702             return segmentCount;
703         }
704         else
705         {
706             Token tokens;
707             if(painter.currentLineIndex == line && painter
708                 .currentLineTokens != null)
709                 tokens = painter.currentLineTokens;
710             else
711             {
712                 painter.currentLineIndex = line;
713                 tokens = painter.currentLineTokens
714                     = tokenMarker.markTokens(lineSegment,line);
715             }
716
717             int offset = 0;
718             Toolkit toolkit = painter.getToolkit();
719             Font defaultFont = painter.getFont();
720             SyntaxStyle[] styles = painter.getStyles();
721
722             for(;;)
723             {
724                 byte id = tokens.id;
725                 if(id == Token.END)
726                     return offset;
727
728                 if(id == Token.NULL)
729                     fm = painter.getFontMetrics();
730                 else
731                     fm = styles[id].getFontMetrics(defaultFont);
732
733                 int length = tokens.length;
734
735                 for(int i = 0; i < length; i++)
736                 {
737                     char c = segmentArray[segmentOffset + offset + i];
738                     int charWidth;
739                     if(c == '\t')
740                         charWidth = (int)painter.nextTabStop(width,offset + i)
741                             - width;
742                     else
743                         charWidth = fm.charWidth(c);
744
745                     if(painter.isBlockCaretEnabled())
746                     {
747                         if(x - charWidth <= width)
748                             return offset + i;
749                     }
750                     else
751                     {
752                         if(x - charWidth / 2 <= width)
753                             return offset + i;
754                     }
755
756                     width += charWidth;
757                 }
758
759                 offset += length;
760                 tokens = tokens.next;
761             }
762         }
763     }
764
765     /**
766      * Converts a point to an offset, from the start of the text.
767      * @param x The x co-ordinate of the point
768      * @param y The y co-ordinate of the point
769      */

770     public int xyToOffset(int x, int y)
771     {
772         int line = yToLine(y);
773         int start = getLineStartOffset(line);
774         return start + xToOffset(line,x);
775     }
776
777     /**
778      * Returns the document this text area is editing.
779      */

780     public final SyntaxDocument getDocument()
781     {
782         return document;
783     }
784
785     /**
786      * Sets the document this text area is editing.
787      * @param document The document
788      */

789     public void setDocument(SyntaxDocument document)
790     {
791         if(this.document == document)
792             return;
793         if(this.document != null)
794             this.document.removeDocumentListener(documentHandler);
795         this.document = document;
796
797         document.addDocumentListener(documentHandler);
798
799         select(0,0);
800         updateScrollBars();
801         painter.repaint();
802     }
803
804     /**
805      * Returns the document's token marker. Equivalent to calling
806      * <code>getDocument().getTokenMarker()</code>.
807      */

808     public final TokenMarker getTokenMarker()
809     {
810         return document.getTokenMarker();
811     }
812
813     /**
814      * Sets the document's token marker. Equivalent to caling
815      * <code>getDocument().setTokenMarker()</code>.
816      * @param tokenMarker The token marker
817      */

818     public final void setTokenMarker(TokenMarker tokenMarker)
819     {
820         document.setTokenMarker(tokenMarker);
821     }
822
823     /**
824      * Returns the length of the document. Equivalent to calling
825      * <code>getDocument().getLength()</code>.
826      */

827     public final int getDocumentLength()
828     {
829         return document.getLength();
830     }
831
832     /**
833      * Returns the number of lines in the document.
834      */

835     public final int getLineCount()
836     {
837         return document.getDefaultRootElement().getElementCount();
838     }
839
840     /**
841      * Returns the line containing the specified offset.
842      * @param offset The offset
843      */

844     public final int getLineOfOffset(int offset)
845     {
846         return document.getDefaultRootElement().getElementIndex(offset);
847     }
848
849     /**
850      * Returns the start offset of the specified line.
851      * @param line The line
852      * @return The start offset of the specified line, or -1 if the line is
853      * invalid
854      */

855     public int getLineStartOffset(int line)
856     {
857         Element lineElement = document.getDefaultRootElement()
858             .getElement(line);
859         if(lineElement == null)
860             return -1;
861         else
862             return lineElement.getStartOffset();
863     }
864
865     /**
866      * Returns the end offset of the specified line.
867      * @param line The line
868      * @return The end offset of the specified line, or -1 if the line is
869      * invalid.
870      */

871     public int getLineEndOffset(int line)
872     {
873         Element lineElement = document.getDefaultRootElement()
874             .getElement(line);
875         if(lineElement == null)
876             return -1;
877         else
878             return lineElement.getEndOffset();
879     }
880
881     /**
882      * Returns the length of the specified line.
883      * @param line The line
884      */

885     public int getLineLength(int line)
886     {
887         Element lineElement = document.getDefaultRootElement()
888             .getElement(line);
889         if(lineElement == null)
890             return -1;
891         else
892             return lineElement.getEndOffset()
893                 - lineElement.getStartOffset() - 1;
894     }
895
896     /**
897      * Returns the entire text of this text area.
898      */

899     public String JavaDoc getText()
900     {
901         try
902         {
903             return document.getText(0,document.getLength());
904         }
905         catch(BadLocationException bl)
906         {
907             bl.printStackTrace();
908             return null;
909         }
910     }
911
912     /**
913      * Sets the entire text of this text area.
914      */

915     public void setText(String JavaDoc text)
916     {
917         try
918         {
919             document.beginCompoundEdit();
920             document.remove(0,document.getLength());
921             document.insertString(0,text,null);
922         }
923         catch(BadLocationException bl)
924         {
925             bl.printStackTrace();
926         }
927         finally
928         {
929             document.endCompoundEdit();
930         }
931     }
932
933     /**
934      * Returns the specified substring of the document.
935      * @param start The start offset
936      * @param len The length of the substring
937      * @return The substring, or null if the offsets are invalid
938      */

939     public final String JavaDoc getText(int start, int len)
940     {
941         try
942         {
943             return document.getText(start,len);
944         }
945         catch(BadLocationException bl)
946         {
947             bl.printStackTrace();
948             return null;
949         }
950     }
951
952     /**
953      * Copies the specified substring of the document into a segment.
954      * If the offsets are invalid, the segment will contain a null string.
955      * @param start The start offset
956      * @param len The length of the substring
957      * @param segment The segment
958      */

959     public final void getText(int start, int len, Segment segment)
960     {
961         try
962         {
963             document.getText(start,len,segment);
964         }
965         catch(BadLocationException bl)
966         {
967             bl.printStackTrace();
968             segment.offset = segment.count = 0;
969         }
970     }
971
972     /**
973      * Returns the text on the specified line.
974      * @param lineIndex The line
975      * @return The text, or null if the line is invalid
976      */

977     public final String JavaDoc getLineText(int lineIndex)
978     {
979         int start = getLineStartOffset(lineIndex);
980         return getText(start,getLineEndOffset(lineIndex) - start - 1);
981     }
982
983     /**
984      * Copies the text on the specified line into a segment. If the line
985      * is invalid, the segment will contain a null string.
986      * @param lineIndex The line
987      */

988     public final void getLineText(int lineIndex, Segment segment)
989     {
990         int start = getLineStartOffset(lineIndex);
991         getText(start,getLineEndOffset(lineIndex) - start - 1,segment);
992     }
993
994     /**
995      * Returns the selection start offset.
996      */

997     public final int getSelectionStart()
998     {
999         return selectionStart;
1000    }
1001
1002    /**
1003     * Returns the offset where the selection starts on the specified
1004     * line.
1005     */

1006    public int getSelectionStart(int line)
1007    {
1008        if(line == selectionStartLine)
1009            return selectionStart;
1010        else if(rectSelect)
1011        {
1012            Element map = document.getDefaultRootElement();
1013            int start = selectionStart - map.getElement(selectionStartLine)
1014                .getStartOffset();
1015
1016            Element lineElement = map.getElement(line);
1017            int lineStart = lineElement.getStartOffset();
1018            int lineEnd = lineElement.getEndOffset() - 1;
1019            return Math.min(lineEnd,lineStart + start);
1020        }
1021        else
1022            return getLineStartOffset(line);
1023    }
1024
1025    /**
1026     * Returns the selection start line.
1027     */

1028    public final int getSelectionStartLine()
1029    {
1030        return selectionStartLine;
1031    }
1032
1033    /**
1034     * Sets the selection start. The new selection will be the new
1035     * selection start and the old selection end.
1036     * @param selectionStart The selection start
1037     * @see #select(int,int)
1038     */

1039    public final void setSelectionStart(int selectionStart)
1040    {
1041        select(selectionStart,selectionEnd);
1042    }
1043
1044    /**
1045     * Returns the selection end offset.
1046     */

1047    public final int getSelectionEnd()
1048    {
1049        return selectionEnd;
1050    }
1051
1052    /**
1053     * Returns the offset where the selection ends on the specified
1054     * line.
1055     */

1056    public int getSelectionEnd(int line)
1057    {
1058        if(line == selectionEndLine)
1059            return selectionEnd;
1060        else if(rectSelect)
1061        {
1062            Element map = document.getDefaultRootElement();
1063            int end = selectionEnd - map.getElement(selectionEndLine)
1064                .getStartOffset();
1065
1066            Element lineElement = map.getElement(line);
1067            int lineStart = lineElement.getStartOffset();
1068            int lineEnd = lineElement.getEndOffset() - 1;
1069            return Math.min(lineEnd,lineStart + end);
1070        }
1071        else
1072            return getLineEndOffset(line) - 1;
1073    }
1074
1075    /**
1076     * Returns the selection end line.
1077     */

1078    public final int getSelectionEndLine()
1079    {
1080        return selectionEndLine;
1081    }
1082
1083    /**
1084     * Sets the selection end. The new selection will be the old
1085     * selection start and the bew selection end.
1086     * @param selectionEnd The selection end
1087     * @see #select(int,int)
1088     */

1089    public final void setSelectionEnd(int selectionEnd)
1090    {
1091        select(selectionStart,selectionEnd);
1092    }
1093
1094    /**
1095     * Returns the caret position. This will either be the selection
1096     * start or the selection end, depending on which direction the
1097     * selection was made in.
1098     */

1099    public final int getCaretPosition()
1100    {
1101        return (biasLeft ? selectionStart : selectionEnd);
1102    }
1103
1104    /**
1105     * Returns the caret line.
1106     */

1107    public final int getCaretLine()
1108    {
1109        return (biasLeft ? selectionStartLine : selectionEndLine);
1110    }
1111
1112    /**
1113     * Returns the mark position. This will be the opposite selection
1114     * bound to the caret position.
1115     * @see #getCaretPosition()
1116     */

1117    public final int getMarkPosition()
1118    {
1119        return (biasLeft ? selectionEnd : selectionStart);
1120    }
1121
1122    /**
1123     * Returns the mark line.
1124     */

1125    public final int getMarkLine()
1126    {
1127        return (biasLeft ? selectionEndLine : selectionStartLine);
1128    }
1129
1130    /**
1131     * Sets the caret position. The new selection will consist of the
1132     * caret position only (hence no text will be selected)
1133     * @param caret The caret position
1134     * @see #select(int,int)
1135     */

1136    public final void setCaretPosition(int caret)
1137    {
1138        select(caret,caret);
1139    }
1140
1141    /**
1142     * Selects all text in the document.
1143     */

1144    public final void selectAll()
1145    {
1146        select(0,getDocumentLength());
1147    }
1148
1149    /**
1150     * Moves the mark to the caret position.
1151     */

1152    public final void selectNone()
1153    {
1154        select(getCaretPosition(),getCaretPosition());
1155    }
1156
1157    /**
1158     * Selects from the start offset to the end offset. This is the
1159     * general selection method used by all other selecting methods.
1160     * The caret position will be start if start &lt; end, and end
1161     * if end &gt; start.
1162     * @param start The start offset
1163     * @param end The end offset
1164     */

1165    public void select(int start, int end)
1166    {
1167        int newStart, newEnd;
1168        boolean newBias;
1169        if(start <= end)
1170        {
1171            newStart = start;
1172            newEnd = end;
1173            newBias = false;
1174        }
1175        else
1176        {
1177            newStart = end;
1178            newEnd = start;
1179            newBias = true;
1180        }
1181
1182        if(newStart < 0 || newEnd > getDocumentLength())
1183        {
1184            throw new IllegalArgumentException JavaDoc("Bounds out of"
1185                + " range: " + newStart + "," +
1186                newEnd);
1187        }
1188
1189        // If the new position is the same as the old, we don't
1190
// do all this crap, however we still do the stuff at
1191
// the end (clearing magic position, scrolling)
1192
if(newStart != selectionStart || newEnd != selectionEnd
1193            || newBias != biasLeft)
1194        {
1195            int newStartLine = getLineOfOffset(newStart);
1196            int newEndLine = getLineOfOffset(newEnd);
1197
1198            if(painter.isBracketHighlightEnabled())
1199            {
1200                if(bracketLine != -1)
1201                    painter.invalidateLine(bracketLine);
1202                updateBracketHighlight(end);
1203                if(bracketLine != -1)
1204                    painter.invalidateLine(bracketLine);
1205            }
1206
1207            painter.invalidateLineRange(selectionStartLine,selectionEndLine);
1208            painter.invalidateLineRange(newStartLine,newEndLine);
1209
1210            document.addUndoableEdit(new CaretUndo(
1211                selectionStart,selectionEnd));
1212
1213            selectionStart = newStart;
1214            selectionEnd = newEnd;
1215            selectionStartLine = newStartLine;
1216            selectionEndLine = newEndLine;
1217            biasLeft = newBias;
1218
1219            fireCaretEvent();
1220        }
1221
1222        // When the user is typing, etc, we don't want the caret
1223
// to blink
1224
blink = true;
1225        caretTimer.restart();
1226
1227        // Disable rectangle select if selection start = selection end
1228
if(selectionStart == selectionEnd)
1229            rectSelect = false;
1230
1231        // Clear the `magic' caret position used by up/down
1232
magicCaret = -1;
1233
1234        scrollToCaret();
1235    }
1236
1237    /**
1238     * Returns the selected text, or null if no selection is active.
1239     */

1240    public final String JavaDoc getSelectedText()
1241    {
1242        if(selectionStart == selectionEnd)
1243            return null;
1244
1245        if(rectSelect)
1246        {
1247            // Return each row of the selection on a new line
1248

1249            Element map = document.getDefaultRootElement();
1250
1251            int start = selectionStart - map.getElement(selectionStartLine)
1252                .getStartOffset();
1253            int end = selectionEnd - map.getElement(selectionEndLine)
1254                .getStartOffset();
1255
1256            // Certain rectangles satisfy this condition...
1257
if(end < start)
1258            {
1259                int tmp = end;
1260                end = start;
1261                start = tmp;
1262            }
1263
1264            StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
1265            Segment seg = new Segment();
1266
1267            for(int i = selectionStartLine; i <= selectionEndLine; i++)
1268            {
1269                Element lineElement = map.getElement(i);
1270                int lineStart = lineElement.getStartOffset();
1271                int lineEnd = lineElement.getEndOffset() - 1;
1272                int lineLen = lineEnd - lineStart;
1273
1274                lineStart = Math.min(lineStart + start,lineEnd);
1275                lineLen = Math.min(end - start,lineEnd - lineStart);
1276
1277                getText(lineStart,lineLen,seg);
1278                buf.append(seg.array,seg.offset,seg.count);
1279
1280                if(i != selectionEndLine)
1281                    buf.append('\n');
1282            }
1283
1284            return buf.toString();
1285        }
1286        else
1287        {
1288            return getText(selectionStart,
1289                selectionEnd - selectionStart);
1290        }
1291    }
1292
1293    /**
1294     * Replaces the selection with the specified text.
1295     * @param selectedText The replacement text for the selection
1296     */

1297    public void setSelectedText(String JavaDoc selectedText)
1298    {
1299        if(!editable)
1300        {
1301            throw new InternalError JavaDoc("Text component"
1302                + " read only");
1303        }
1304
1305        document.beginCompoundEdit();
1306
1307        try
1308        {
1309            if(rectSelect)
1310            {
1311                Element map = document.getDefaultRootElement();
1312
1313                int start = selectionStart - map.getElement(selectionStartLine)
1314                    .getStartOffset();
1315                int end = selectionEnd - map.getElement(selectionEndLine)
1316                    .getStartOffset();
1317
1318                // Certain rectangles satisfy this condition...
1319
if(end < start)
1320                {
1321                    int tmp = end;
1322                    end = start;
1323                    start = tmp;
1324                }
1325
1326                int lastNewline = 0;
1327                int currNewline = 0;
1328
1329                for(int i = selectionStartLine; i <= selectionEndLine; i++)
1330                {
1331                    Element lineElement = map.getElement(i);
1332                    int lineStart = lineElement.getStartOffset();
1333                    int lineEnd = lineElement.getEndOffset() - 1;
1334                    int rectStart = Math.min(lineEnd,lineStart + start);
1335
1336                    document.remove(rectStart,Math.min(lineEnd - rectStart,
1337                        end - start));
1338
1339                    if(selectedText == null)
1340                        continue;
1341
1342                    currNewline = selectedText.indexOf('\n',lastNewline);
1343                    if(currNewline == -1)
1344                        currNewline = selectedText.length();
1345
1346                    document.insertString(rectStart,selectedText
1347                        .substring(lastNewline,currNewline),null);
1348
1349                    lastNewline = Math.min(selectedText.length(),
1350                        currNewline + 1);
1351                }
1352
1353                if(selectedText != null &&
1354                    currNewline != selectedText.length())
1355                {
1356                    int offset = map.getElement(selectionEndLine)
1357                        .getEndOffset() - 1;
1358                    document.insertString(offset,"\n",null);
1359                    document.insertString(offset + 1,selectedText
1360                        .substring(currNewline + 1),null);
1361                }
1362            }
1363            else
1364            {
1365                document.remove(selectionStart,
1366                    selectionEnd - selectionStart);
1367                if(selectedText != null)
1368                {
1369                    document.insertString(selectionStart,
1370                        selectedText,null);
1371                }
1372            }
1373        }
1374        catch(BadLocationException bl)
1375        {
1376            bl.printStackTrace();
1377            throw new InternalError JavaDoc("Cannot replace"
1378                + " selection");
1379        }
1380        // No matter what happends... stops us from leaving document
1381
// in a bad state
1382
finally
1383        {
1384            document.endCompoundEdit();
1385        }
1386
1387        setCaretPosition(selectionEnd);
1388    }
1389
1390    /**
1391     * Returns true if this text area is editable, false otherwise.
1392     */

1393    public final boolean isEditable()
1394    {
1395        return editable;
1396    }
1397
1398    /**
1399     * Sets if this component is editable.
1400     * @param editable True if this text area should be editable,
1401     * false otherwise
1402     */

1403    public final void setEditable(boolean editable)
1404    {
1405        this.editable = editable;
1406    }
1407
1408    /**
1409     * Returns the right click popup menu.
1410     */

1411    public final JPopupMenu getRightClickPopup()
1412    {
1413        return popup;
1414    }
1415
1416    /**
1417     * Sets the right click popup menu.
1418     * @param popup The popup
1419     */

1420    public final void setRightClickPopup(JPopupMenu popup)
1421    {
1422        this.popup = popup;
1423    }
1424
1425    /**
1426     * Returns the `magic' caret position. This can be used to preserve
1427     * the column position when moving up and down lines.
1428     */

1429    public final int getMagicCaretPosition()
1430    {
1431        return magicCaret;
1432    }
1433
1434    /**
1435     * Sets the `magic' caret position. This can be used to preserve
1436     * the column position when moving up and down lines.
1437     * @param magicCaret The magic caret position
1438     */

1439    public final void setMagicCaretPosition(int magicCaret)
1440    {
1441        this.magicCaret = magicCaret;
1442    }
1443
1444    /**
1445     * Similar to <code>setSelectedText()</code>, but overstrikes the
1446     * appropriate number of characters if overwrite mode is enabled.
1447     * @param str The string
1448     * @see #setSelectedText(String)
1449     * @see #isOverwriteEnabled()
1450     */

1451    public void overwriteSetSelectedText(String JavaDoc str)
1452    {
1453        // Don't overstrike if there is a selection
1454
if(!overwrite || selectionStart != selectionEnd)
1455        {
1456            setSelectedText(str);
1457            return;
1458        }
1459
1460        // Don't overstrike if we're on the end of
1461
// the line
1462
int caret = getCaretPosition();
1463        int caretLineEnd = getLineEndOffset(getCaretLine());
1464        if(caretLineEnd - caret <= str.length())
1465        {
1466            setSelectedText(str);
1467            return;
1468        }
1469
1470        document.beginCompoundEdit();
1471
1472        try
1473        {
1474            document.remove(caret,str.length());
1475            document.insertString(caret,str,null);
1476        }
1477        catch(BadLocationException bl)
1478        {
1479            bl.printStackTrace();
1480        }
1481        finally
1482        {
1483            document.endCompoundEdit();
1484        }
1485    }
1486
1487    /**
1488     * Returns true if overwrite mode is enabled, false otherwise.
1489     */

1490    public final boolean isOverwriteEnabled()
1491    {
1492        return overwrite;
1493    }
1494
1495    /**
1496     * Sets if overwrite mode should be enabled.
1497     * @param overwrite True if overwrite mode should be enabled,
1498     * false otherwise.
1499     */

1500    public final void setOverwriteEnabled(boolean overwrite)
1501    {
1502        this.overwrite = overwrite;
1503        painter.invalidateSelectedLines();
1504    }
1505
1506    /**
1507     * Returns true if the selection is rectangular, false otherwise.
1508     */

1509    public final boolean isSelectionRectangular()
1510    {
1511        return rectSelect;
1512    }
1513
1514    /**
1515     * Sets if the selection should be rectangular.
1516     * @param overwrite True if the selection should be rectangular,
1517     * false otherwise.
1518     */

1519    public final void setSelectionRectangular(boolean rectSelect)
1520    {
1521        this.rectSelect = rectSelect;
1522        painter.invalidateSelectedLines();
1523    }
1524
1525    /**
1526     * Returns the position of the highlighted bracket (the bracket
1527     * matching the one before the caret)
1528     */

1529    public final int getBracketPosition()
1530    {
1531        return bracketPosition;
1532    }
1533
1534    /**
1535     * Returns the line of the highlighted bracket (the bracket
1536     * matching the one before the caret)
1537     */

1538    public final int getBracketLine()
1539    {
1540        return bracketLine;
1541    }
1542
1543    /**
1544     * Adds a caret change listener to this text area.
1545     * @param listener The listener
1546     */

1547    public final void addCaretListener(CaretListener listener)
1548    {
1549        listenerList.add(CaretListener.class,listener);
1550    }
1551
1552    /**
1553     * Removes a caret change listener from this text area.
1554     * @param listener The listener
1555     */

1556    public final void removeCaretListener(CaretListener listener)
1557    {
1558        listenerList.remove(CaretListener.class,listener);
1559    }
1560
1561    /**
1562     * Deletes the selected text from the text area and places it
1563     * into the clipboard.
1564     */

1565    public void cut()
1566    {
1567        if(editable)
1568        {
1569            copy();
1570            setSelectedText("");
1571        }
1572    }
1573
1574    /**
1575     * Places the selected text into the clipboard.
1576     */

1577    public void copy()
1578    {
1579        if(selectionStart != selectionEnd)
1580        {
1581            Clipboard clipboard = getToolkit().getSystemClipboard();
1582
1583            String JavaDoc selection = getSelectedText();
1584
1585            int repeatCount = inputHandler.getRepeatCount();
1586            StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
1587            for(int i = 0; i < repeatCount; i++)
1588                buf.append(selection);
1589
1590            clipboard.setContents(new StringSelection(buf.toString()),null);
1591        }
1592    }
1593
1594    /**
1595     * Inserts the clipboard contents into the text.
1596     */

1597    public void paste()
1598    {
1599        if(editable)
1600        {
1601            Clipboard clipboard = getToolkit().getSystemClipboard();
1602            try
1603            {
1604                // The MacOS MRJ doesn't convert \r to \n,
1605
// so do it here
1606
String JavaDoc selection = ((String JavaDoc)clipboard
1607                    .getContents(this).getTransferData(
1608                    DataFlavor.stringFlavor))
1609                    .replace('\r','\n');
1610
1611                int repeatCount = inputHandler.getRepeatCount();
1612                StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
1613                for(int i = 0; i < repeatCount; i++)
1614                    buf.append(selection);
1615                selection = buf.toString();
1616                setSelectedText(selection);
1617            }
1618            catch(Exception JavaDoc e)
1619            {
1620                getToolkit().beep();
1621                //System.err.println("Clipboard does not" + " contain a string");
1622
}
1623        }
1624    }
1625
1626    /**
1627     * Called by the AWT when this component is removed from it's parent.
1628     * This stops clears the currently focused component.
1629     */

1630    public void removeNotify()
1631    {
1632        super.removeNotify();
1633        if(focusedComponent == this)
1634            focusedComponent = null;
1635    }
1636
1637    /**
1638     * Forwards key events directly to the input handler.
1639     * This is slightly faster than using a KeyListener
1640     * because some Swing overhead is avoided.
1641     */

1642    public void processKeyEvent(KeyEvent evt)
1643    {
1644        if(inputHandler == null)
1645            return;
1646        switch(evt.getID())
1647        {
1648        case KeyEvent.KEY_TYPED:
1649            inputHandler.keyTyped(evt);
1650            break;
1651        case KeyEvent.KEY_PRESSED:
1652            inputHandler.keyPressed(evt);
1653            break;
1654        case KeyEvent.KEY_RELEASED:
1655            inputHandler.keyReleased(evt);
1656            break;
1657        }
1658    }
1659
1660    // protected members
1661
protected static String JavaDoc CENTER = "center";
1662    protected static String JavaDoc RIGHT = "right";
1663    protected static String JavaDoc BOTTOM = "bottom";
1664
1665    public static JEditTextArea focusedComponent;
1666    protected static Timer caretTimer;
1667    
1668    protected TextAreaPainter painter;
1669
1670    protected JPopupMenu popup;
1671
1672    protected EventListenerList listenerList;
1673    protected MutableCaretEvent caretEvent;
1674
1675    protected boolean caretBlinks;
1676    protected boolean caretVisible;
1677    protected boolean blink;
1678
1679    protected boolean editable;
1680
1681    protected int firstLine;
1682    protected int visibleLines;
1683    protected int electricScroll;
1684
1685    protected int horizontalOffset;
1686    
1687    protected JScrollBar vertical;
1688    protected JScrollBar horizontal;
1689    protected boolean scrollBarsInitialized;
1690
1691    protected InputHandler inputHandler;
1692    protected SyntaxDocument document;
1693    protected DocumentHandler documentHandler;
1694
1695    protected Segment lineSegment;
1696
1697    protected int selectionStart;
1698    protected int selectionStartLine;
1699    protected int selectionEnd;
1700    protected int selectionEndLine;
1701    protected boolean biasLeft;
1702
1703    protected int bracketPosition;
1704    protected int bracketLine;
1705
1706    protected int magicCaret;
1707    protected boolean overwrite;
1708    protected boolean rectSelect;
1709
1710    protected void fireCaretEvent()
1711    {
1712        Object JavaDoc[] listeners = listenerList.getListenerList();
1713        for(int i = listeners.length - 2; i >= 0; i--)
1714        {
1715            if(listeners[i] == CaretListener.class)
1716            {
1717                ((CaretListener)listeners[i+1]).caretUpdate(caretEvent);
1718            }
1719        }
1720    }
1721
1722    protected void updateBracketHighlight(int newCaretPosition)
1723    {
1724        if(newCaretPosition == 0)
1725        {
1726            bracketPosition = bracketLine = -1;
1727            return;
1728        }
1729
1730        try
1731        {
1732            int offset = TextUtilities.findMatchingBracket(
1733                document,newCaretPosition - 1);
1734            if(offset != -1)
1735            {
1736                bracketLine = getLineOfOffset(offset);
1737                bracketPosition = offset - getLineStartOffset(bracketLine);
1738                return;
1739            }
1740        }
1741        catch(BadLocationException bl)
1742        {
1743            bl.printStackTrace();
1744        }
1745
1746        bracketLine = bracketPosition = -1;
1747    }
1748
1749    protected void documentChanged(DocumentEvent evt)
1750    {
1751        DocumentEvent.ElementChange ch = evt.getChange(
1752            document.getDefaultRootElement());
1753
1754        int count;
1755        if(ch == null)
1756            count = 0;
1757        else
1758            count = ch.getChildrenAdded().length -
1759                ch.getChildrenRemoved().length;
1760
1761        int line = getLineOfOffset(evt.getOffset());
1762        if(count == 0)
1763        {
1764            painter.invalidateLine(line);
1765        }
1766        // do magic stuff
1767
else if(line < firstLine)
1768        {
1769            setFirstLine(firstLine + count);
1770        }
1771        // end of magic stuff
1772
else
1773        {
1774            painter.invalidateLineRange(line,firstLine + visibleLines);
1775            updateScrollBars();
1776        }
1777    }
1778
1779    class ScrollLayout implements LayoutManager
1780    {
1781        public void addLayoutComponent(String JavaDoc name, Component comp)
1782        {
1783            if(name.equals(CENTER))
1784                center = comp;
1785            else if(name.equals(RIGHT))
1786                right = comp;
1787            else if(name.equals(BOTTOM))
1788                bottom = comp;
1789            else if(name.equals(LEFT_OF_SCROLLBAR))
1790                leftOfScrollBar.addElement(comp);
1791        }
1792
1793        public void removeLayoutComponent(Component comp)
1794        {
1795            if(center == comp)
1796                center = null;
1797            if(right == comp)
1798                right = null;
1799            if(bottom == comp)
1800                bottom = null;
1801            else
1802                leftOfScrollBar.removeElement(comp);
1803        }
1804
1805        public Dimension preferredLayoutSize(Container parent)
1806        {
1807            if( center==null ) return new Dimension(300,50); // This helps Netbeans GUI designer
1808

1809            Dimension dim = new Dimension();
1810            Insets insets = getInsets();
1811            dim.width = insets.left + insets.right;
1812            dim.height = insets.top + insets.bottom;
1813
1814            Dimension centerPref = center.getPreferredSize();
1815            dim.width += centerPref.width;
1816            dim.height += centerPref.height;
1817            Dimension rightPref = right.getPreferredSize();
1818            dim.width += rightPref.width;
1819            Dimension bottomPref = bottom.getPreferredSize();
1820            dim.height += bottomPref.height;
1821
1822            return dim;
1823        }
1824
1825        public Dimension minimumLayoutSize(Container parent)
1826        {
1827            if( center==null ) return new Dimension(0,0); // This helps Netbeans GUI designer
1828

1829            Dimension dim = new Dimension();
1830            Insets insets = getInsets();
1831            dim.width = insets.left + insets.right;
1832            dim.height = insets.top + insets.bottom;
1833
1834            Dimension centerPref = center.getMinimumSize();
1835            dim.width += centerPref.width;
1836            dim.height += centerPref.height;
1837            Dimension rightPref = right.getMinimumSize();
1838            dim.width += rightPref.width;
1839            Dimension bottomPref = bottom.getMinimumSize();
1840            dim.height += bottomPref.height;
1841
1842            return dim;
1843        }
1844
1845        public void layoutContainer(Container parent)
1846        {
1847            Dimension size = parent.getSize();
1848            Insets insets = parent.getInsets();
1849            int itop = insets.top;
1850            int ileft = insets.left;
1851            int ibottom = insets.bottom;
1852            int iright = insets.right;
1853
1854            int rightWidth = right.getPreferredSize().width;
1855            int bottomHeight = bottom.getPreferredSize().height;
1856            int centerWidth = size.width - rightWidth - ileft - iright;
1857            int centerHeight = size.height - bottomHeight - itop - ibottom;
1858
1859            center.setBounds(
1860                ileft,
1861                itop,
1862                centerWidth,
1863                centerHeight);
1864
1865            right.setBounds(
1866                ileft + centerWidth,
1867                itop,
1868                rightWidth,
1869                centerHeight);
1870
1871            // Lay out all status components, in order
1872
Enumeration JavaDoc status = leftOfScrollBar.elements();
1873            while(status.hasMoreElements())
1874            {
1875                Component comp = (Component)status.nextElement();
1876                Dimension dim = comp.getPreferredSize();
1877                comp.setBounds(ileft,
1878                    itop + centerHeight,
1879                    dim.width,
1880                    bottomHeight);
1881                ileft += dim.width;
1882            }
1883
1884            bottom.setBounds(
1885                ileft,
1886                itop + centerHeight,
1887                size.width - rightWidth - ileft - iright,
1888                bottomHeight);
1889        }
1890
1891        // private members
1892
private Component center;
1893        private Component right;
1894        private Component bottom;
1895        private Vector JavaDoc leftOfScrollBar = new Vector JavaDoc();
1896    }
1897
1898    static class CaretBlinker implements ActionListener
1899    {
1900        public void actionPerformed(ActionEvent evt)
1901        {
1902            if(focusedComponent != null
1903                && focusedComponent.hasFocus())
1904                focusedComponent.blinkCaret();
1905        }
1906    }
1907
1908    class MutableCaretEvent extends CaretEvent
1909    {
1910        MutableCaretEvent()
1911        {
1912            super(JEditTextArea.this);
1913        }
1914
1915        public int getDot()
1916        {
1917            return getCaretPosition();
1918        }
1919
1920        public int getMark()
1921        {
1922            return getMarkPosition();
1923        }
1924    }
1925
1926    class AdjustHandler implements AdjustmentListener
1927    {
1928        public void adjustmentValueChanged(final AdjustmentEvent evt)
1929        {
1930            if(!scrollBarsInitialized)
1931                return;
1932
1933            // If this is not done, mousePressed events accumilate
1934
// and the result is that scrolling doesn't stop after
1935
// the mouse is released
1936
SwingUtilities.invokeLater(new Runnable JavaDoc() {
1937                public void run()
1938                {
1939                    if(evt.getAdjustable() == vertical)
1940                        setFirstLine(vertical.getValue());
1941                    else
1942                        setHorizontalOffset(-horizontal.getValue());
1943                }
1944            });
1945        }
1946    }
1947
1948    class ComponentHandler extends ComponentAdapter
1949    {
1950        public void componentResized(ComponentEvent evt)
1951        {
1952            recalculateVisibleLines();
1953            scrollBarsInitialized = true;
1954                        updateScrollBars();
1955        }
1956    }
1957
1958    class DocumentHandler implements DocumentListener
1959    {
1960        public void insertUpdate(DocumentEvent evt)
1961        {
1962            documentChanged(evt);
1963
1964            int offset = evt.getOffset();
1965            int length = evt.getLength();
1966
1967            int newStart;
1968            int newEnd;
1969
1970            if(selectionStart > offset || (selectionStart
1971                == selectionEnd && selectionStart == offset))
1972                newStart = selectionStart + length;
1973            else
1974                newStart = selectionStart;
1975
1976            if(selectionEnd >= offset)
1977                newEnd = selectionEnd + length;
1978            else
1979                newEnd = selectionEnd;
1980
1981            select(newStart,newEnd);
1982        }
1983    
1984        public void removeUpdate(DocumentEvent evt)
1985        {
1986            documentChanged(evt);
1987
1988            int offset = evt.getOffset();
1989            int length = evt.getLength();
1990
1991            int newStart;
1992            int newEnd;
1993
1994            if(selectionStart > offset)
1995            {
1996                if(selectionStart > offset + length)
1997                    newStart = selectionStart - length;
1998                else
1999                    newStart = offset;
2000            }
2001            else
2002                newStart = selectionStart;
2003
2004            if(selectionEnd > offset)
2005            {
2006                if(selectionEnd > offset + length)
2007                    newEnd = selectionEnd - length;
2008                else
2009                    newEnd = offset;
2010            }
2011            else
2012                newEnd = selectionEnd;
2013
2014            select(newStart,newEnd);
2015        }
2016
2017        public void changedUpdate(DocumentEvent evt)
2018        {
2019        }
2020    }
2021
2022    class DragHandler implements MouseMotionListener
2023    {
2024        public void mouseDragged(MouseEvent evt)
2025        {
2026            if(popup != null && popup.isVisible())
2027                return;
2028
2029            setSelectionRectangular((evt.getModifiers()
2030                & InputEvent.CTRL_MASK) != 0);
2031            select(getMarkPosition(),xyToOffset(evt.getX(),evt.getY()));
2032        }
2033
2034        public void mouseMoved(MouseEvent evt) {}
2035    }
2036
2037    class FocusHandler implements FocusListener
2038    {
2039        public void focusGained(FocusEvent evt)
2040        {
2041            setCaretVisible(true);
2042            focusedComponent = JEditTextArea.this;
2043        }
2044
2045        public void focusLost(FocusEvent evt)
2046        {
2047            setCaretVisible(false);
2048            focusedComponent = null;
2049        }
2050    }
2051
2052    class MouseHandler extends MouseAdapter
2053    {
2054        public void mousePressed(MouseEvent evt)
2055        {
2056            requestFocus();
2057
2058            // Focus events not fired sometimes?
2059
setCaretVisible(true);
2060            focusedComponent = JEditTextArea.this;
2061
2062            if((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0
2063                && popup != null)
2064            {
2065                popup.show(painter,evt.getX(),evt.getY());
2066                return;
2067            }
2068
2069            int line = yToLine(evt.getY());
2070            int offset = xToOffset(line,evt.getX());
2071            int dot = getLineStartOffset(line) + offset;
2072
2073            switch(evt.getClickCount())
2074            {
2075            case 1:
2076                doSingleClick(evt,line,offset,dot);
2077                break;
2078            case 2:
2079                // It uses the bracket matching stuff, so
2080
// it can throw a BLE
2081
try
2082                {
2083                    doDoubleClick(evt,line,offset,dot);
2084                }
2085                catch(BadLocationException bl)
2086                {
2087                    bl.printStackTrace();
2088                }
2089                break;
2090            case 3:
2091                doTripleClick(evt,line,offset,dot);
2092                break;
2093            }
2094        }
2095
2096        private void doSingleClick(MouseEvent evt, int line,
2097            int offset, int dot)
2098        {
2099            if((evt.getModifiers() & InputEvent.SHIFT_MASK) != 0)
2100            {
2101                rectSelect = (evt.getModifiers() & InputEvent.CTRL_MASK) != 0;
2102                select(getMarkPosition(),dot);
2103            }
2104            else
2105                setCaretPosition(dot);
2106        }
2107
2108        private void doDoubleClick(MouseEvent evt, int line,
2109            int offset, int dot) throws BadLocationException
2110        {
2111            // Ignore empty lines
2112
if(getLineLength(line) == 0)
2113                return;
2114
2115            try
2116            {
2117                int bracket = TextUtilities.findMatchingBracket(
2118                    document,Math.max(0,dot - 1));
2119                if(bracket != -1)
2120                {
2121                    int mark = getMarkPosition();
2122                    // Hack
2123
if(bracket > mark)
2124                    {
2125                        bracket++;
2126                        mark--;
2127                    }
2128                    select(mark,bracket);
2129                    return;
2130                }
2131            }
2132            catch(BadLocationException bl)
2133            {
2134                bl.printStackTrace();
2135            }
2136
2137            // Ok, it's not a bracket... select the word
2138
String JavaDoc lineText = getLineText(line);
2139            char ch = lineText.charAt(Math.max(0,offset - 1));
2140
2141            String JavaDoc noWordSep = (String JavaDoc)document.getProperty("noWordSep");
2142            if(noWordSep == null)
2143                noWordSep = "";
2144
2145            // If the user clicked on a non-letter char,
2146
// we select the surrounding non-letters
2147
boolean selectNoLetter = (!Character
2148                .isLetterOrDigit(ch)
2149                && noWordSep.indexOf(ch) == -1);
2150
2151            int wordStart = 0;
2152
2153            for(int i = offset - 1; i >= 0; i--)
2154            {
2155                ch = lineText.charAt(i);
2156                if(selectNoLetter ^ (!Character
2157                    .isLetterOrDigit(ch) &&
2158                    noWordSep.indexOf(ch) == -1))
2159                {
2160                    wordStart = i + 1;
2161                    break;
2162                }
2163            }
2164
2165            int wordEnd = lineText.length();
2166            for(int i = offset; i < lineText.length(); i++)
2167            {
2168                ch = lineText.charAt(i);
2169                if(selectNoLetter ^ (!Character
2170                    .isLetterOrDigit(ch) &&
2171                    noWordSep.indexOf(ch) == -1))
2172                {
2173                    wordEnd = i;
2174                    break;
2175                }
2176            }
2177
2178            int lineStart = getLineStartOffset(line);
2179            select(lineStart + wordStart,lineStart + wordEnd);
2180
2181            /*
2182            String lineText = getLineText(line);
2183            String noWordSep = (String)document.getProperty("noWordSep");
2184            int wordStart = TextUtilities.findWordStart(lineText,offset,noWordSep);
2185            int wordEnd = TextUtilities.findWordEnd(lineText,offset,noWordSep);
2186
2187            int lineStart = getLineStartOffset(line);
2188            select(lineStart + wordStart,lineStart + wordEnd);
2189            */

2190        }
2191
2192        private void doTripleClick(MouseEvent evt, int line,
2193            int offset, int dot)
2194        {
2195            select(getLineStartOffset(line),getLineEndOffset(line)-1);
2196        }
2197    }
2198
2199    class CaretUndo extends AbstractUndoableEdit
2200    {
2201        private int start;
2202        private int end;
2203
2204        CaretUndo(int start, int end)
2205        {
2206            this.start = start;
2207            this.end = end;
2208        }
2209
2210        public boolean isSignificant()
2211        {
2212            return false;
2213        }
2214
2215        public String JavaDoc getPresentationName()
2216        {
2217            return "caret move";
2218        }
2219
2220        public void undo() throws CannotUndoException
2221        {
2222            super.undo();
2223
2224            select(start,end);
2225        }
2226
2227        public void redo() throws CannotRedoException
2228        {
2229            super.redo();
2230
2231            select(start,end);
2232        }
2233
2234        public boolean addEdit(UndoableEdit edit)
2235        {
2236            if(edit instanceof CaretUndo)
2237            {
2238                CaretUndo cedit = (CaretUndo)edit;
2239                start = cedit.start;
2240                end = cedit.end;
2241                cedit.die();
2242
2243                return true;
2244            }
2245            else
2246                return false;
2247        }
2248    }
2249
2250    static
2251    {
2252        caretTimer = new Timer(500,new CaretBlinker());
2253        caretTimer.setInitialDelay(500);
2254        caretTimer.start();
2255    }
2256}
2257
Popular Tags