KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > jac > aspects > gui > swing > SHEditor


1
2 package org.objectweb.jac.aspects.gui.swing;
3
4 import java.awt.Color JavaDoc;
5 import java.awt.Cursor JavaDoc;
6 import java.awt.Dimension JavaDoc;
7 import java.awt.Font JavaDoc;
8 import java.awt.FontMetrics JavaDoc;
9 import java.awt.Graphics JavaDoc;
10 import java.awt.Rectangle JavaDoc;
11 import java.awt.datatransfer.Clipboard JavaDoc;
12 import java.awt.datatransfer.ClipboardOwner JavaDoc;
13 import java.awt.datatransfer.DataFlavor JavaDoc;
14 import java.awt.datatransfer.StringSelection JavaDoc;
15 import java.awt.datatransfer.Transferable JavaDoc;
16 import java.awt.event.FocusEvent JavaDoc;
17 import java.awt.event.FocusListener JavaDoc;
18 import java.awt.event.KeyEvent JavaDoc;
19 import java.awt.event.KeyListener JavaDoc;
20 import java.awt.event.MouseEvent JavaDoc;
21 import java.awt.event.MouseListener JavaDoc;
22 import java.awt.event.MouseMotionListener JavaDoc;
23 import java.awt.event.TextEvent JavaDoc;
24 import java.awt.event.TextListener JavaDoc;
25 import java.io.BufferedInputStream JavaDoc;
26 import java.io.BufferedReader JavaDoc;
27 import java.io.File JavaDoc;
28 import java.io.FileInputStream JavaDoc;
29 import java.io.FileWriter JavaDoc;
30 import java.io.IOException JavaDoc;
31 import java.io.InputStreamReader JavaDoc;
32 import java.io.PrintWriter JavaDoc;
33 import java.util.HashSet JavaDoc;
34 import java.util.Set JavaDoc;
35 import java.util.Vector JavaDoc;
36 import javax.swing.JPanel JavaDoc;
37 import javax.swing.JScrollBar JavaDoc;
38 import javax.swing.JScrollPane JavaDoc;
39 import javax.swing.JViewport JavaDoc;
40 import javax.swing.Scrollable JavaDoc;
41 import javax.swing.SwingConstants JavaDoc;
42 import javax.swing.event.CaretEvent JavaDoc;
43 import javax.swing.event.CaretListener JavaDoc;
44 import org.apache.log4j.Logger;
45 import org.objectweb.jac.util.Strings;
46
47 /**
48  * @author Lars-Erik H. Bergland <a
49 //href="http://www.mycgiserver.com/~hbergla/">www.mycgiserver.com/~hbergla</a>
50 //<a
51 //href="mailto:vagastorm@microsnyft.com">vagastorm@microsnyft.com</a>
52  * @version 1.0
53  *
54  * The CodeEditor is constructed to display java code with syntax
55  * coloring. It was created to be used in the <a
56  * HREF="http://www.mycgiserver.com/~hbergla/SimpleJavaEditor/about.html">SimpleJavaEditor</a>
57  * but it can easily be adjusted so it can be used with any SWING
58  * application that needs to display syntax colored java code. Please
59  * use it in any leagal way you whant, but if you find it usefull tell
60  * me aboute it, along with any wishes you migt have for changes and
61  * I'll see what I can do. */

62
63 public class SHEditor extends JPanel JavaDoc
64     implements KeyListener JavaDoc, MouseListener JavaDoc, MouseMotionListener JavaDoc,
65               Scrollable JavaDoc, ClipboardOwner JavaDoc, FocusListener JavaDoc
66 {
67     static Logger logger = Logger.getLogger("gui.sheditor");
68     static Logger loggerComp = Logger.getLogger("completion");
69     static Logger loggerClip = Logger.getLogger("clipboard");
70
71     private String JavaDoc text = "";
72     private FontMetrics JavaDoc metrics;
73     int lineHeight; // height of line of text
74

75     private int caretPosition = 0;
76     private int selectionStart = 0;
77     private int selectionEnd = 0;
78     /** Whether the selection is some text added by the completion engine */
79     protected boolean isSelectionCompletion = false;
80     
81     /** position of opening char ('(','{' or '[') to highlight */
82     private int openPos = -1;
83     /** position of closing char (')','}' or ']') to highlight */
84     private int closePos = -1;
85    
86     private int lineToMark = -1;
87    
88     private boolean showCaret = true;
89    
90     protected SHEditorConfig conf = new SHEditorConfig();
91     public SHEditorConfig getConfig() {
92         return conf;
93     }
94     public void setConfig(SHEditorConfig conf) {
95         this.conf = conf;
96     }
97     
98     public static final int DEFAULT = 0;
99     public static final int COMMENT = 1;
100     public static final int STRING = 2;
101
102     protected int syntaxUnderCaret;
103     public int getSyntaxUnderCaret() {
104         return syntaxUnderCaret;
105     }
106
107     private int mousePressPos = 0;
108    
109     private Vector JavaDoc doneActions = new Vector JavaDoc();
110     private Vector JavaDoc redoneActions = new Vector JavaDoc();
111    
112     private Rectangle JavaDoc car = new Rectangle JavaDoc(0, 0, 2, 0);
113    
114     boolean changed = false;
115
116     private char separators[] = new char[] {
117         '\n', ' ', '.' , ',', '(', ')', '{', '}', '[', ']', '/', '-', '+', '*',
118         '<', '>', '=', ';', '"', '\'', '&', '|', '!'
119     };
120
121     /**
122      * Sets the characters considered as word separators
123      */

124     public void setWordSeparators(char[] separators) {
125         this.separators = separators;
126     }
127
128     /**
129      * Constructs a empty SHEditor.
130      */

131     public SHEditor()
132     {
133         super();
134       
135         setFont(new Font JavaDoc("MonoSpaced", Font.PLAIN, 12));
136         metrics = getFontMetrics(getFont());
137         setCursor(new Cursor JavaDoc(Cursor.TEXT_CURSOR));
138         addKeyListener(this);
139         addMouseListener(this);
140         addMouseMotionListener(this);
141         addFocusListener(this);
142         if (org.objectweb.jac.core.Jac.getMainJavaVersion().compareTo("1.4")>=0) {
143             setFocusTraversalKeysEnabled(false);
144         }
145         setBackground(Color.white);
146     }
147
148     /**
149      * Constructs a SHEditor where the content is set by a string.
150      * @param txt A String representing initial java file.
151      */

152     public SHEditor(String JavaDoc txt)
153     {
154         this();
155         text = txt;
156     }
157
158     /**
159      * Constructs a SHEditor and reads the content of a file into it.
160      * @param file The initial java file.
161      */

162     public SHEditor(File JavaDoc file)
163     {
164         this();
165         readFromFile(file);
166     }
167
168     Vector JavaDoc caretListeners = new Vector JavaDoc();
169     /**
170      * Adds a caret listener for notification of any changes
171      * to the caret.
172      *
173      * @param listener the listener to be added
174      * @see javax.swing.event.CaretEvent
175      */

176     public void addCaretListener(CaretListener JavaDoc listener) {
177         caretListeners.add(listener);
178     }
179    
180     /**
181      * Removes a caret listener.
182      *
183      * @param listener the listener to be removed
184      * @see javax.swing.event.CaretEvent
185      */

186     public void removeCaretListener(CaretListener JavaDoc listener) {
187         caretListeners.remove(listener);
188     }
189
190     protected void fireCaretUpdate() {
191         CaretEvent JavaDoc e = new MutableCaretEvent(this,caretPosition);
192         for (int i = caretListeners.size()-1; i>=0; i--) {
193             ((CaretListener JavaDoc)textListeners.get(i)).caretUpdate(e);
194         }
195     }
196
197     Vector JavaDoc textListeners = new Vector JavaDoc();
198
199     /**
200      * Adds a text listener for notification of any changes
201      * to the text.
202      *
203      * @param listener the listener to be added
204      * @see javax.swing.event.CaretEvent
205      */

206     public void addTextListener(TextListener JavaDoc listener) {
207         textListeners.add(listener);
208     }
209    
210     /**
211      * Removes a text listener.
212      *
213      * @param listener the listener to be removed
214      * @see javax.swing.event.CaretEvent
215      */

216     public void removeTextListener(TextListener JavaDoc listener) {
217         textListeners.remove(listener);
218     }
219
220     protected void fireTextUpdate() {
221         TextEvent JavaDoc e = new TextEvent JavaDoc(this,TextEvent.TEXT_VALUE_CHANGED);
222         for (int i = textListeners.size()-1; i>=0; i--) {
223             ((TextListener JavaDoc)textListeners.get(i)).textValueChanged(e);
224         }
225     }
226
227     /**
228      * Returns the a String containing the java file.
229      * @return A string containing the content of the current java file.
230      */

231     public String JavaDoc getText()
232     {
233         return text;
234     }
235
236     public void setText(String JavaDoc t) {
237         text=t;
238         fireTextUpdate();
239     }
240
241     /**
242      * Tells wether the caret is at the end of the text
243      */

244     boolean eot() {
245         return caretPosition >= text.length();
246     }
247
248     /**
249      * Tells wether the caret is at the end of the selection
250      */

251     boolean endOfSelection() {
252         return caretPosition == selectionEnd;
253     }
254
255     /**
256      * Tells wether the caret is at the start of the selection
257      */

258     boolean startOfSelection() {
259         return caretPosition == selectionStart;
260     }
261
262     /**
263      * Sets the start of the selection. It ensures that selectionStart<selectionEnd.
264      * @param position new start of selection
265      */

266     public void setSelectionStart(int position) {
267         if (position>selectionEnd) {
268             int oldStart = selectionStart;
269             selectionStart = selectionEnd;
270             selectionEnd = position;
271             repaintChars(selectionStart,position);
272         } else {
273             repaintChars(selectionStart,position);
274             selectionStart = position;
275         }
276     }
277
278     /**
279      * Sets the end of the selection. It ensures that selectionStart<selectionEnd.
280      * @param position new end of selection
281      */

282     public void setSelectionEnd(int position) {
283         if (position<selectionStart) {
284             int oldEnd = selectionEnd;
285             selectionEnd = selectionStart;
286             selectionStart = position;
287             repaintChars(selectionEnd,position);
288         } else {
289             repaintChars(selectionEnd,position);
290             selectionEnd = position;
291         }
292     }
293
294     /**
295      * Sets the selection
296      * @param start position where selection starts
297      * @param end position where selection ends
298      */

299     public void setSelection(int start, int end) {
300         int oldStart = selectionStart;
301         int oldEnd = selectionEnd;
302         if (start<=end) {
303             selectionStart = start;
304             selectionEnd = end;
305         } else {
306             selectionStart = end;
307             selectionEnd = start;
308         }
309         repaintChars(oldStart,oldEnd);
310         repaintChars(selectionStart,selectionEnd);
311     }
312
313     /**
314      * Updates the selection after a backward move
315      * @param select wether the move was a selecting one
316      */

317     void backwardMove(boolean select) {
318         if (select) {
319             if (caretPosition<selectionStart)
320                 setSelectionStart(caretPosition);
321             else
322                 setSelectionEnd(caretPosition);
323         } else {
324             resetSelection();
325         }
326     }
327
328     /**
329      * Updates the selection after a backward move
330      * @param select wether the move was a selecting one
331      */

332     void forwardMove(boolean select) {
333         if (select) {
334             if (caretPosition>selectionEnd)
335                 setSelectionEnd(caretPosition);
336             else
337                 setSelectionStart(caretPosition);
338         } else {
339             resetSelection();
340         }
341     }
342
343     /**
344      * Moves forward n characters
345      * @param n number of characters to move forward
346      * @param select wether to add the text moved over by the caret to
347      * the selection
348      */

349     public void forwardChar(int n, boolean select) {
350         setCaretPosition(caretPosition + n);
351         forwardMove(select);
352         positionVisible();
353     }
354     
355     /**
356      * Moves backward n characters
357      * @param n number of characters to move backward
358      * @param select wether to add the text moved over by the caret to
359      * the selection
360      */

361     public void backwardChar(int n, boolean select) {
362         setCaretPosition(caretPosition - n);
363         backwardMove(select);
364         positionVisible();
365     }
366
367     /**
368      * Moves forward n words
369      * @param n number of words to move forward
370      * @param select wether to add the text moved over by the caret to
371      * the selection
372      */

373     public void forwardWord(int n, boolean select) {
374         boolean endOfSelection = endOfSelection();
375         int newPos = caretPosition;
376         while(!eot() && isDivider(text.charAt(newPos))) {
377             newPos++;
378         }
379         for (; n>0; n--) {
380             while(!eot() && !isDivider(text.charAt(newPos))) {
381                 newPos++;
382             }
383         }
384         setCaretPosition(newPos);
385         forwardMove(select);
386         positionVisible();
387     }
388
389     /**
390      * Moves backward n words
391      * @param n number of words to move backward
392      * @param select wether to add the text moved over by the caret to
393      * the selection
394      */

395     public void backwardWord(int n, boolean select) {
396         int newPos = caretPosition;
397         if (newPos>0) {
398             newPos--;
399         }
400         while (newPos>0 && isDivider(text.charAt(newPos))) {
401             newPos--;
402         }
403         for (; n>0; n--) {
404             while(newPos>0 && !isDivider(text.charAt(newPos))) {
405                 newPos--;
406             }
407         }
408         if (!eot() && newPos>0)
409             newPos++;
410         setCaretPosition(newPos);
411         backwardMove(select);
412         positionVisible();
413     }
414
415     /**
416      * Move to the next line
417      * @param n move to previous line n times
418      * @param select wether to add the text moved over by the caret to
419      * the selection
420      */

421     public void nextLine(int n, boolean select)
422     {
423         boolean moved = false;
424         int newPos = caretPosition;
425         for (; n>0; n--) {
426             int posInLine = getPosInLine(newPos);
427             int nextLineStart = text.indexOf('\n', newPos);
428             if (nextLineStart != -1)
429             {
430                 nextLineStart ++;
431                 int i = 0;
432                 while(i < posInLine && i + nextLineStart < text.length() &&
433                       text.charAt(i + nextLineStart) != '\n')
434                 {
435                     i ++;
436                 }
437                 if ((nextLineStart+i) >newPos)
438                     moved = true;
439                 newPos = nextLineStart + i;
440             }
441         }
442         if (moved) {
443             setCaretPosition(newPos);
444             forwardMove(select);
445         }
446
447         positionVisible();
448     }
449
450     /**
451      * Move to the previous line
452      * @param n move to previous line n times
453      * @param select wether to add the text moved over by the caret to
454      * the selection
455      */

456     public void previousLine(int n, boolean select)
457     {
458         boolean moved = false;
459         int newPos = caretPosition;
460         for (; n>0; n--) {
461             int posInLine = getPosInLine(newPos);
462             int prevLineStart =
463                 text.lastIndexOf('\n', newPos - posInLine - 2) + 1;
464             int i = 0;
465             while(i < posInLine && i + prevLineStart < text.length() &&
466                   text.charAt(i + prevLineStart) != '\n')
467             {
468                 i ++;
469             }
470             if (prevLineStart+i<newPos)
471                 moved = true;
472             newPos = prevLineStart+i;
473         }
474         if (moved) {
475             setCaretPosition(newPos);
476             backwardMove(select);
477         }
478
479         positionVisible();
480     }
481
482     /**
483      * Moves the caret to the beginning of the text.
484      * @param select wether to add the text moved over by the caret to
485      * the selection
486      */

487     public void beginningOfText(boolean select)
488     {
489         setCaretPosition(0);
490         backwardMove(select);
491         positionVisible();
492     }
493
494
495     /**
496      * Moves the caret to the end of the text.
497      * @param select wether to add the text moved over by the caret to
498      * the selection
499      */

500     public void endOfText(boolean select)
501     {
502         setCaretPosition(text.length());
503         backwardMove(select);
504         positionVisible();
505     }
506
507     /**
508      * Moves the caret to the beginning of the current line.
509      * @param select wether to add the text moved over by the caret to
510      * the selection
511      */

512     public void beginningOfLine(boolean select)
513     {
514         int newPos = caretPosition;
515         if (getPosInLine(newPos) <= getWhiteAtLineStart(newPos)) {
516             newPos -= getPosInLine(newPos);
517         } else {
518             newPos -=
519                 getPosInLine(newPos) -
520                 getWhiteAtLineStart(newPos);
521         }
522         setCaretPosition(newPos);
523         backwardMove(select);
524         positionVisible();
525
526     }
527
528     public void realBeginningOfLine(boolean select)
529     {
530         int newPos = caretPosition;
531         if (getPosInLine(newPos) <= getWhiteAtLineStart(newPos)) {
532             newPos -= getPosInLine(newPos);
533         }
534         setCaretPosition(newPos);
535         backwardMove(select);
536         positionVisible();
537     }
538
539     /**
540      * Moves the caret to the end of the current line.
541      * @param select wether to add the text moved over by the caret to
542      * the selection
543      */

544     public void endOfLine(boolean select) {
545         setCaretPosition(caretPosition +
546             getLineWidth(caretPosition) - getPosInLine(caretPosition));
547         forwardMove(select);
548         positionVisible();
549     }
550
551     /**
552      * Sets the caret at the beginning of a line
553      * @param lineNumber number of the line (starts with 1)
554      */

555     public void gotoLine(int lineNumber) {
556         beginningOfText(false);
557         if (lineNumber>1) {
558             nextLine(lineNumber-1,false);
559         }
560     }
561
562     /**
563      * Resets the selection. Sets both its start and end to caretPosition.
564      */

565     void resetSelection() {
566         int oldStart = selectionStart;
567         int oldEnd = selectionEnd;
568         selectionEnd = caretPosition;
569         selectionStart = caretPosition;
570         for (int p=oldStart; p<oldEnd; p++)
571             repaintCharAt(p);
572     }
573    
574     /**
575      * Selects the word around a position
576      */

577     void selectWord(int position) {
578         int oldStart = selectionStart;
579         int oldEnd = selectionEnd;
580
581         selectionEnd = caretPosition;
582         selectionStart = caretPosition;
583         while (selectionEnd<text.length()
584                && Character.isLetterOrDigit(text.charAt(selectionEnd))) {
585             selectionEnd++;
586         }
587         while (selectionStart>=0 && selectionStart<text.length() &&
588                Character.isLetterOrDigit(text.charAt(selectionStart))) {
589             selectionStart--;
590         }
591         if (selectionStart>=0 && selectionStart<text.length() &&
592             !Character.isLetterOrDigit(text.charAt(selectionStart)))
593             selectionStart++;
594
595         for (int p=oldStart; p<oldEnd; p++)
596             repaintCharAt(p);
597     }
598
599     CompletionEngine completionEngine;
600
601     public void setCompletionEngine(CompletionEngine ce) {
602         completionEngine = ce;
603     }
604
605     public CompletionEngine getCompletionEngine() {
606         return completionEngine;
607     }
608
609     void runCompletionEngine(int direction) {
610
611         if (completionEngine!=null) {
612
613             String JavaDoc writtenText = "";
614             int pos;
615             if (selectionStart!=selectionEnd) {
616                 pos = selectionStart;
617             } else {
618                 pos = caretPosition;
619             }
620             int beginWritten = pos;
621             StringBuffer JavaDoc currentProposal = new StringBuffer JavaDoc();
622             // test if the user has already typed something
623
if (pos>0 && !isDivider(text.charAt(pos-1))) {
624                 loggerComp.debug("written word found");
625                 beginWritten--;
626                 // go to the begining of the word
627
while (beginWritten>0 && !isDivider(text.charAt(beginWritten-1))) {
628                     beginWritten--;
629                 }
630                 writtenText = text.substring(beginWritten, pos);
631                 if (selectionStart!=selectionEnd) {
632                     currentProposal.append(writtenText);
633                 }
634             }
635
636             currentProposal.append(text.substring(selectionStart, selectionEnd));
637             int initPosition = caretPosition;
638
639             // removes the current proposal
640
initPosition = selectionStart;
641             remove(selectionStart, selectionEnd - selectionStart);
642             resetSelection();
643             isSelectionCompletion = true;
644
645             String JavaDoc proposedText =
646                 completionEngine.getProposal(
647                     text,beginWritten,writtenText,
648                     currentProposal.toString(),
649                     direction);
650             if (proposedText.length()>0) {
651                 insertString(caretPosition,proposedText.substring(writtenText.length()));
652             }
653
654             setSelectionStart(initPosition);
655             setSelectionEnd(caretPosition);
656
657         }
658     }
659
660     KeyListener JavaDoc toolKeyListener;
661     public void toolDone() {
662         toolKeyListener = null;
663     }
664
665     /**
666      * Key Pressed
667      */

668     public void keyPressed(KeyEvent JavaDoc e)
669     {
670         if (toolKeyListener!=null) {
671             toolKeyListener.keyPressed(e);
672             return;
673         }
674         if (e.isControlDown())
675         {
676             switch (e.getKeyCode()) {
677                 case KeyEvent.VK_C:
678                     copy();
679                     break;
680                 case KeyEvent.VK_V:
681                     paste();
682                     break;
683                 case KeyEvent.VK_X:
684                     cut();
685                     break;
686                 case KeyEvent.VK_Z:
687                     undo();
688                     break;
689                 case KeyEvent.VK_Y:
690                     redo();
691                     break;
692                 case KeyEvent.VK_S:
693                     toolKeyListener = new SearchTool(this,caretPosition);
694                     break;
695                 case KeyEvent.VK_RIGHT:
696                     forwardWord(1, e.isShiftDown());
697                     break;
698                 case KeyEvent.VK_LEFT:
699                     backwardWord(1, e.isShiftDown());
700                     break;
701                 case KeyEvent.VK_HOME:
702                     beginningOfText(e.isShiftDown());
703                     break;
704                 case KeyEvent.VK_END:
705                     endOfText(e.isShiftDown());
706                     break;
707                 case KeyEvent.VK_SPACE:
708                     runCompletionEngine(
709                         e.isShiftDown()?CompletionEngine.BACKWARD:CompletionEngine.FORWARD);
710                     break;
711                 default:
712             }
713         }
714         else if ((e.getModifiers() & KeyEvent.ALT_MASK) > 0)
715         {
716         }
717         else if ((e.getModifiers() & KeyEvent.ALT_GRAPH_MASK) > 0)
718         {
719         }
720         else if (e.isShiftDown())
721         {
722             switch (e.getKeyCode()) {
723                 case KeyEvent.VK_LEFT:
724                     backwardChar(1,e.isShiftDown());
725                     e.consume();
726                     break;
727                 case KeyEvent.VK_RIGHT:
728                     forwardChar(1,e.isShiftDown());
729                     e.consume();
730                     break;
731                 case KeyEvent.VK_UP:
732                     previousLine(1,e.isShiftDown());
733                     e.consume();
734                     break;
735                 case KeyEvent.VK_DOWN:
736                     nextLine(1,e.isShiftDown());
737                     e.consume();
738                     break;
739                 case KeyEvent.VK_HOME:
740                     beginningOfLine(e.isShiftDown());
741                     e.consume();
742                     break;
743                 case KeyEvent.VK_END:
744                     endOfLine(e.isShiftDown());
745                     e.consume();
746                     break;
747                 case KeyEvent.VK_PAGE_UP:
748                     previousLine(15,e.isShiftDown());
749                     e.consume();
750                     break;
751                 case KeyEvent.VK_PAGE_DOWN:
752                     nextLine(15,e.isShiftDown());
753                     e.consume();
754                     break;
755                 case KeyEvent.VK_TAB:
756                     {
757                         if (selectionStart != selectionEnd)
758                         {
759                             int start = selectionStart;
760                             int end = selectionEnd;
761                      
762                             for(int pos = end;
763                                 pos >= start - getPosInLine(start) && pos >= 0;
764                                 pos--)
765                             {
766                                 if (pos == 0 || text.charAt(pos - 1) == '\n')
767                                 {
768                                     for(int i=0;
769                                         i < conf.getTabWidth() && text.charAt(pos) == ' ';
770                                         i++)
771                                     {
772                                         remove(pos, 1);
773                                         end--;
774                                     }
775                                 }
776                             }
777                             setSelection(start,end);
778                         }
779                         else
780                         {
781                         }
782                         e.consume();
783                     }
784                     break;
785                 default:
786             }
787         }
788         else
789         {
790             switch (e.getKeyCode()) {
791                 case KeyEvent.VK_LEFT:
792                     backwardChar(1,e.isShiftDown());
793                     e.consume();
794                     break;
795                 case KeyEvent.VK_RIGHT:
796                     forwardChar(1,e.isShiftDown());
797                     e.consume();
798                     break;
799                 case KeyEvent.VK_UP:
800                     previousLine(1,e.isShiftDown());
801                     e.consume();
802                     break;
803                 case KeyEvent.VK_DOWN:
804                     nextLine(1,e.isShiftDown());
805                     e.consume();
806                     break;
807                 case KeyEvent.VK_TAB:
808                     if (selectionStart == selectionEnd)
809                     {
810                         insertTab(caretPosition);
811                     }
812                     else
813                     {
814                         int start = selectionStart;
815                         int end = selectionEnd;
816                      
817                         for(int pos=end;
818                             pos>=start-getPosInLine(start) && pos>=0;
819                             pos--)
820