001    
002    package org.objectweb.jac.aspects.gui.swing;
003    
004    import java.awt.Color;
005    import java.awt.Cursor;
006    import java.awt.Dimension;
007    import java.awt.Font;
008    import java.awt.FontMetrics;
009    import java.awt.Graphics;
010    import java.awt.Rectangle;
011    import java.awt.datatransfer.Clipboard;
012    import java.awt.datatransfer.ClipboardOwner;
013    import java.awt.datatransfer.DataFlavor;
014    import java.awt.datatransfer.StringSelection;
015    import java.awt.datatransfer.Transferable;
016    import java.awt.event.FocusEvent;
017    import java.awt.event.FocusListener;
018    import java.awt.event.KeyEvent;
019    import java.awt.event.KeyListener;
020    import java.awt.event.MouseEvent;
021    import java.awt.event.MouseListener;
022    import java.awt.event.MouseMotionListener;
023    import java.awt.event.TextEvent;
024    import java.awt.event.TextListener;
025    import java.io.BufferedInputStream;
026    import java.io.BufferedReader;
027    import java.io.File;
028    import java.io.FileInputStream;
029    import java.io.FileWriter;
030    import java.io.IOException;
031    import java.io.InputStreamReader;
032    import java.io.PrintWriter;
033    import java.util.HashSet;
034    import java.util.Set;
035    import java.util.Vector;
036    import javax.swing.JPanel;
037    import javax.swing.JScrollBar;
038    import javax.swing.JScrollPane;
039    import javax.swing.JViewport;
040    import javax.swing.Scrollable;
041    import javax.swing.SwingConstants;
042    import javax.swing.event.CaretEvent;
043    import javax.swing.event.CaretListener;
044    import org.apache.log4j.Logger;
045    import org.objectweb.jac.util.Strings;
046    
047    /** 
048     * @author Lars-Erik H. Bergland <a
049    //href="http://www.mycgiserver.com/~hbergla/">www.mycgiserver.com/~hbergla</a>
050    //<a
051    //href="mailto:vagastorm@microsnyft.com">vagastorm@microsnyft.com</a>
052     * @version 1.0
053     *
054     * The CodeEditor is constructed to display java code with syntax
055     * coloring.  It was created to be used in the <a
056     * href="http://www.mycgiserver.com/~hbergla/SimpleJavaEditor/about.html">SimpleJavaEditor</a>
057     * but it can easily be adjusted so it can be used with any SWING
058     * application that needs to display syntax colored java code.  Please
059     * use it in any leagal way you whant, but if you find it usefull tell
060     * me aboute it, along with any wishes you migt have for changes and
061     * I'll see what I can do.  */
062    
063    public class SHEditor extends JPanel 
064        implements KeyListener, MouseListener, MouseMotionListener, 
065                  Scrollable, ClipboardOwner, FocusListener
066    {
067        static Logger logger = Logger.getLogger("gui.sheditor");
068        static Logger loggerComp = Logger.getLogger("completion");
069        static Logger loggerClip = Logger.getLogger("clipboard");
070    
071        private String text = "";   
072        private FontMetrics metrics;
073        int lineHeight; // height of line of text
074    
075        private int caretPosition = 0;
076        private int selectionStart = 0;
077        private int selectionEnd = 0;
078        /** Whether the selection is some text added by the completion engine */
079        protected boolean isSelectionCompletion = false;
080        
081        /** position of opening char ('(','{' or '[') to highlight */
082        private int openPos = -1;
083        /** position of closing char (')','}' or ']') to highlight */
084        private int closePos = -1;
085       
086        private int lineToMark = -1;
087       
088        private boolean showCaret = true;
089       
090        protected SHEditorConfig conf = new SHEditorConfig();
091        public SHEditorConfig getConfig() {
092            return conf;
093        }
094        public void setConfig(SHEditorConfig conf) {
095            this.conf = conf;
096        }
097        
098        public static final int DEFAULT = 0;
099        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 doneActions = new Vector();
110        private Vector redoneActions = new Vector();
111       
112        private Rectangle car = new Rectangle(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("MonoSpaced", Font.PLAIN, 12));
136            metrics = getFontMetrics(getFont());
137            setCursor(new Cursor(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 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 file)
163        {
164            this();
165            readFromFile(file);
166        }
167    
168        /**
169         * Adds a caret listener for notification of any changes
170         * to the caret.
171         *
172         * @param listener the listener to be added
173         * @see javax.swing.event.CaretEvent
174         */
175        public void addCaretListener(CaretListener listener) {
176            listenerList.add(CaretListener.class, listener);
177        }
178       
179        /**
180         * Removes a caret listener.
181         *
182         * @param listener the listener to be removed
183         * @see javax.swing.event.CaretEvent
184         */
185        public void removeCaretListener(CaretListener listener) {
186            listenerList.remove(CaretListener.class, listener);
187        }
188    
189    
190        /**
191         * Adds a text listener for notification of any changes
192         * to the text.
193         *
194         * @param listener the listener to be added
195         * @see javax.swing.event.CaretEvent
196         */
197        public void addTextListener(TextListener listener) {
198            listenerList.add(TextListener.class, listener);
199        }
200       
201        /**
202         * Removes a text listener.
203         *
204         * @param listener the listener to be removed
205         * @see javax.swing.event.CaretEvent
206         */
207        public void removeTextListener(TextListener listener) {
208            listenerList.remove(TextListener.class, listener);
209        }
210    
211        protected void fireTextUpdate() {
212            TextEvent e = new TextEvent(this,TextEvent.TEXT_VALUE_CHANGED);
213            // Guaranteed to return a non-null array
214            Object[] listeners = listenerList.getListenerList();
215            // Process the listeners last to first, notifying
216            // those that are interested in this event
217            for (int i = listeners.length-2; i>=0; i-=2) {
218                if (listeners[i]==TextListener.class) {
219                    ((TextListener)listeners[i+1]).textValueChanged(e);
220                }
221            }
222        }
223    
224        /** 
225         * Returns the a String containing the java file.
226         * @return A string containing the content of the current java file.
227         */
228        public String getText()
229        {
230            return text;
231        }
232    
233        public void setText(String t) {
234            text=t;
235            fireTextUpdate();
236        }
237    
238        /**
239         * Tells wether the caret is at the end of the text
240         */
241        boolean eot() {
242            return caretPosition >= text.length();
243        }
244    
245        /**
246         * Tells wether the caret is at the end of the selection
247         */
248        boolean endOfSelection() {
249            return caretPosition == selectionEnd;
250        }
251    
252        /**
253         * Tells wether the caret is at the start of the selection
254         */
255        boolean startOfSelection() {
256            return caretPosition == selectionStart;
257        }
258    
259        /**
260         * Sets the start of the selection. It ensures that selectionStart<selectionEnd.
261         * @param position new start of selection
262         */
263        public void setSelectionStart(int position) {
264            if (position>selectionEnd) {
265                int oldStart = selectionStart;
266                selectionStart = selectionEnd;
267                selectionEnd = position;
268                repaintChars(selectionStart,position);
269            } else {
270                repaintChars(selectionStart,position);
271                selectionStart = position;
272            }
273        }
274    
275        /**
276         * Sets the end of the selection. It ensures that selectionStart<selectionEnd.
277         * @param position new end of selection
278         */
279        public void setSelectionEnd(int position) {
280            if (position<selectionStart) {
281                int oldEnd = selectionEnd;
282                selectionEnd = selectionStart;
283                selectionStart = position;
284                repaintChars(selectionEnd,position);
285            } else {
286                repaintChars(selectionEnd,position);
287                selectionEnd = position;
288            }
289        }
290    
291        /**
292         * Sets the selection
293         * @param start position where selection starts
294         * @param end position where selection ends
295         */ 
296        public void setSelection(int start, int end) {
297            int oldStart = selectionStart;
298            int oldEnd = selectionEnd;
299            if (start<=end) {
300                selectionStart = start;
301                selectionEnd = end;
302            } else {
303                selectionStart = end;
304                selectionEnd = start;
305            }
306            repaintChars(oldStart,oldEnd);
307            repaintChars(selectionStart,selectionEnd);
308        }
309    
310        /**
311         * Updates the selection after a backward move
312         * @param select wether the move was a selecting one
313         */
314        void backwardMove(boolean select) {
315            if (select) {
316                if (caretPosition<selectionStart)
317                    setSelectionStart(caretPosition);
318                else
319                    setSelectionEnd(caretPosition);
320            } else {
321                resetSelection();
322            }
323        }
324    
325        /**
326         * Updates the selection after a backward move
327         * @param select wether the move was a selecting one
328         */
329        void forwardMove(boolean select) {
330            if (select) {
331                if (caretPosition>selectionEnd)
332                    setSelectionEnd(caretPosition);
333                else
334                    setSelectionStart(caretPosition);
335            } else {
336                resetSelection();
337            }
338        }
339    
340        /**
341         * Moves forward n characters
342         * @param n number of characters to move forward
343         * @param select wether to add the text moved over by the caret to
344         * the selection
345         */
346        public void forwardChar(int n, boolean select) {
347            setCaretPosition(caretPosition + n);
348            forwardMove(select);
349            positionVisible();
350        }
351        
352        /**
353         * Moves backward n characters
354         * @param n number of characters to move backward
355         * @param select wether to add the text moved over by the caret to
356         * the selection
357         */
358        public void backwardChar(int n, boolean select) {
359            setCaretPosition(caretPosition - n);
360            backwardMove(select);
361            positionVisible();
362        }
363    
364        /**
365         * Moves forward n words
366         * @param n number of words to move forward
367         * @param select wether to add the text moved over by the caret to
368         * the selection
369         */
370        public void forwardWord(int n, boolean select) {
371            boolean endOfSelection = endOfSelection();
372            int newPos = caretPosition;
373            while(!eot() && isDivider(text.charAt(newPos))) {
374                newPos++;
375            }
376            for (; n>0; n--) {
377                while(!eot() && !isDivider(text.charAt(newPos))) {
378                    newPos++;
379                }
380            }
381            setCaretPosition(newPos);
382            forwardMove(select);
383            positionVisible();
384        }
385    
386        /**
387         * Moves backward n words
388         * @param n number of words to move backward
389         * @param select wether to add the text moved over by the caret to
390         * the selection
391         */
392        public void backwardWord(int n, boolean select) {
393            int newPos = caretPosition;
394            if (newPos>0) {
395                newPos--;
396            }
397            while (newPos>0 && isDivider(text.charAt(newPos))) {
398                newPos--;
399            }
400            for (; n>0; n--) {
401                while(newPos>0 && !isDivider(text.charAt(newPos))) {
402                    newPos--;
403                }
404            }
405            if (!eot() && newPos>0)
406                newPos++;
407            setCaretPosition(newPos);
408            backwardMove(select);
409            positionVisible();
410        }
411    
412        /**
413         * Move to the next line 
414         * @param n move to previous line n times
415         * @param select wether to add the text moved over by the caret to
416         * the selection
417         */
418        public void nextLine(int n, boolean select) 
419        {
420            boolean moved = false;        
421            int newPos = caretPosition;
422            for (; n>0; n--) {
423                int posInLine = getPosInLine(newPos);
424                int nextLineStart = text.indexOf('\n', newPos);
425                if (nextLineStart != -1)
426                {
427                    nextLineStart ++;
428                    int i = 0;
429                    while(i < posInLine && i + nextLineStart < text.length() && 
430                          text.charAt(i + nextLineStart) != '\n')
431                    {
432                        i ++;
433                    }
434                    if ((nextLineStart+i) >newPos)
435                        moved = true;
436                    newPos = nextLineStart + i;
437                }
438            }
439            if (moved) {
440                setCaretPosition(newPos);
441                forwardMove(select);
442            }
443    
444            positionVisible();
445        }
446    
447        /**
448         * Move to the previous line 
449         * @param n move to previous line n times
450         * @param select wether to add the text moved over by the caret to
451         * the selection
452         */
453        public void previousLine(int n, boolean select) 
454        {
455            boolean moved = false;
456            int newPos = caretPosition;
457            for (; n>0; n--) {
458                int posInLine = getPosInLine(newPos);
459                int prevLineStart = 
460                    text.lastIndexOf('\n', newPos - posInLine - 2) + 1;
461                int i = 0;
462                while(i < posInLine && i + prevLineStart < text.length() && 
463                      text.charAt(i + prevLineStart) != '\n')
464                {
465                    i ++;
466                }
467                if (prevLineStart+i<newPos)
468                    moved = true;
469                newPos = prevLineStart+i;
470            }
471            if (moved) {
472                setCaretPosition(newPos);
473                backwardMove(select);
474            }
475    
476            positionVisible();           
477        }
478    
479        /**
480         * Moves the caret to the beginning of the text.
481         * @param select wether to add the text moved over by the caret to
482         * the selection      
483         */
484        public void beginningOfText(boolean select)
485        {
486            setCaretPosition(0);
487            backwardMove(select);
488            positionVisible();
489        }
490    
491    
492        /**
493         * Moves the caret to the end of the text.
494         * @param select wether to add the text moved over by the caret to
495         * the selection      
496         */
497        public void endOfText(boolean select)
498        {
499            setCaretPosition(text.length());
500            backwardMove(select);
501            positionVisible();
502        }
503    
504        /**
505         * Moves the caret to the beginning of the current line.
506         * @param select wether to add the text moved over by the caret to
507         * the selection      
508         */
509        public void beginningOfLine(boolean select)
510        {
511            int newPos = caretPosition;
512            if (getPosInLine(newPos) <= getWhiteAtLineStart(newPos)) {
513                newPos -= getPosInLine(newPos);
514            } else {
515                newPos -= 
516                    getPosInLine(newPos) - 
517                    getWhiteAtLineStart(newPos);
518            }
519            setCaretPosition(newPos);
520            backwardMove(select);
521            positionVisible();
522    
523        }
524    
525        public void realBeginningOfLine(boolean select)
526        {
527            int newPos = caretPosition;
528            if (getPosInLine(newPos) <= getWhiteAtLineStart(newPos)) {
529                newPos -= getPosInLine(newPos);
530            }
531            setCaretPosition(newPos);
532            backwardMove(select);
533            positionVisible();
534        }
535    
536        /**
537         * Moves the caret to the end of the current line.
538         * @param select wether to add the text moved over by the caret to
539         * the selection      
540         */
541        public void endOfLine(boolean select) {
542            setCaretPosition(caretPosition +
543                getLineWidth(caretPosition) - getPosInLine(caretPosition));
544            forwardMove(select);
545            positionVisible();
546        }
547    
548        /**
549         * Sets the caret at the beginning of a line
550         * @param lineNumber number of the line (starts with 1)
551         */
552        public void gotoLine(int lineNumber) {
553            beginningOfText(false);
554            if (lineNumber>1) {
555                nextLine(lineNumber-1,false);
556            }
557        }
558    
559        /**
560         * Resets the selection. Sets both its start and end to caretPosition.
561         */
562        void resetSelection() {
563            int oldStart = selectionStart;
564            int oldEnd = selectionEnd;
565            selectionEnd = caretPosition;
566            selectionStart = caretPosition;
567            for (int p=oldStart; p<oldEnd; p++)
568                repaintCharAt(p);
569        }
570       
571        /**
572         * Selects the word around a position
573         */
574        void selectWord(int position) {
575            int oldStart = selectionStart;
576            int oldEnd = selectionEnd;
577    
578            selectionEnd = caretPosition;
579            selectionStart = caretPosition;
580            while (selectionEnd<text.length() 
581                   && Character.isLetterOrDigit(text.charAt(selectionEnd))) {
582                selectionEnd++;
583            }
584            while (selectionStart>=0 && selectionStart<text.length() &&
585                   Character.isLetterOrDigit(text.charAt(selectionStart))) {
586                selectionStart--;
587            }
588            if (selectionStart>=0 && selectionStart<text.length() &&
589                !Character.isLetterOrDigit(text.charAt(selectionStart)))
590                selectionStart++;
591    
592            for (int p=oldStart; p<oldEnd; p++)
593                repaintCharAt(p);
594        }
595    
596        CompletionEngine completionEngine;
597    
598        public void setCompletionEngine(CompletionEngine ce) {
599            completionEngine = ce;
600        }
601    
602        public CompletionEngine getCompletionEngine() {
603            return completionEngine;
604        }
605    
606        void runCompletionEngine(int direction) {
607    
608            if (completionEngine!=null) {
609    
610                String writtenText = "";
611                int pos;
612                if (selectionStart!=selectionEnd) {
613                    pos = selectionStart;
614                } else {
615                    pos = caretPosition;
616                }
617                int beginWritten = pos;
618                StringBuffer currentProposal = new StringBuffer();
619                // test if the user has already typed something
620                if (pos>0 && !isDivider(text.charAt(pos-1))) {
621                    loggerComp.debug("written word found");
622                    beginWritten--;
623                    // go to the begining of the word
624                    while (beginWritten>0 && !isDivider(text.charAt(beginWritten-1))) {
625                        beginWritten--;
626                    }
627                    writtenText = text.substring(beginWritten, pos);
628                    if (selectionStart!=selectionEnd) {
629                        currentProposal.append(writtenText);
630                    }
631                }
632    
633                currentProposal.append(text.substring(selectionStart, selectionEnd));
634                int initPosition = caretPosition;
635    
636                // removes the current proposal
637                initPosition = selectionStart;
638                remove(selectionStart, selectionEnd - selectionStart);
639                resetSelection();
640                isSelectionCompletion = true;
641    
642                String proposedText = 
643                    completionEngine.getProposal(
644                        text,beginWritten,writtenText,
645                        currentProposal.toString(),
646                        direction);
647                if (proposedText.length()>0) {
648                    insertString(caretPosition,proposedText.substring(writtenText.length()));
649                }
650    
651                setSelectionStart(initPosition);
652                setSelectionEnd(caretPosition);
653    
654            }
655        }
656    
657        KeyListener toolKeyListener;
658        public void toolDone() {
659            toolKeyListener = null;
660        }
661    
662        /** 
663         * Key Pressed
664         */
665        public void keyPressed(KeyEvent e)
666        {
667            if (toolKeyListener!=null) {
668                toolKeyListener.keyPressed(e);
669                return;
670            }
671            if (e.isControlDown())
672            {
673                switch (e.getKeyCode()) {
674                    case KeyEvent.VK_C:
675                        copy(); 
676                        break;
677                    case KeyEvent.VK_V:
678                        paste(); 
679                        break;
680                    case KeyEvent.VK_X:
681                        cut(); 
682                        break;
683                    case KeyEvent.VK_Z:
684                        undo(); 
685                        break;
686                    case KeyEvent.VK_Y:
687                        redo();
688                        break;
689                    case KeyEvent.VK_S:
690                        toolKeyListener = new SearchTool(this,caretPosition);
691                        break;
692                    case KeyEvent.VK_RIGHT:
693                        forwardWord(1, e.isShiftDown());
694                        break;
695                    case KeyEvent.VK_LEFT:
696                        backwardWord(1, e.isShiftDown());
697                        break;
698                    case KeyEvent.VK_HOME:
699                        beginningOfText(e.isShiftDown());
700                        break;
701                    case KeyEvent.VK_END:
702                        endOfText(e.isShiftDown());
703                        break;
704                    case KeyEvent.VK_SPACE:
705                        runCompletionEngine(
706                            e.isShiftDown()?CompletionEngine.BACKWARD:CompletionEngine.FORWARD);
707                        break;
708                    default:
709                }
710            }
711            else if ((e.getModifiers() & KeyEvent.ALT_MASK) > 0)
712            {
713            }
714            else if ((e.getModifiers() & KeyEvent.ALT_GRAPH_MASK) > 0)
715            {
716            }
717            else if (e.isShiftDown())
718            {
719                switch (e.getKeyCode()) {
720                    case KeyEvent.VK_LEFT:
721                        backwardChar(1,e.isShiftDown());
722                        e.consume();
723                        break;
724                    case KeyEvent.VK_RIGHT:
725                        forwardChar(1,e.isShiftDown());
726                        e.consume();
727                        break;
728                    case KeyEvent.VK_UP: 
729                        previousLine(1,e.isShiftDown());
730                        e.consume();
731                        break;
732                    case KeyEvent.VK_DOWN:
733                        nextLine(1,e.isShiftDown());
734                        e.consume();
735                        break;
736                    case KeyEvent.VK_HOME:
737                        beginningOfLine(e.isShiftDown());
738                        e.consume();
739                        break;
740                    case KeyEvent.VK_END:
741                        endOfLine(e.isShiftDown());
742                        e.consume();
743                        break;
744                    case KeyEvent.VK_PAGE_UP:
745                        previousLine(15,e.isShiftDown());
746                        e.consume();
747                        break;
748                    case KeyEvent.VK_PAGE_DOWN:
749                        nextLine(15,e.isShiftDown());
750                        e.consume();
751                        break;
752                    case KeyEvent.VK_TAB:
753                        {
754                            if (selectionStart != selectionEnd)
755                            {
756                                int start = selectionStart;
757                                int end = selectionEnd;
758                         
759                                for(int pos = end; 
760                                    pos >= start - getPosInLine(start) && pos >= 0; 
761                                    pos--)
762                                {
763                                    if (pos == 0 || text.charAt(pos - 1) == '\n')
764                                    {
765                                        for(int i=0; 
766                                            i < conf.getTabWidth() && text.charAt(pos) == ' '; 
767                                            i++)
768                                        {
769                                            remove(pos, 1);
770                                            end--;
771                                        }
772                                    }
773                                }
774                                setSelection(start,end);
775                            }
776                            else
777                            {
778                            }            
779                            e.consume();
780                        }
781                        break;
782                    default:
783                }
784            }
785            else
786            {
787                switch (e.getKeyCode()) {
788                    case KeyEvent.VK_LEFT:
789                        backwardChar(1,e.isShiftDown());
790                        e.consume();
791                        break;
792                    case KeyEvent.VK_RIGHT:
793                        forwardChar(1,e.isShiftDown());
794                        e.consume();
795                        break;
796                    case KeyEvent.VK_UP:
797                        previousLine(1,e.isShiftDown());
798                        e.consume();
799                        break;
800                    case KeyEvent.VK_DOWN:
801                        nextLine(1,e.isShiftDown());
802                        e.consume();
803                        break;
804                    case KeyEvent.VK_TAB:
805                        if (selectionStart == selectionEnd)
806                        {
807                            insertTab(caretPosition);
808                        }
809                        else
810                        {                  
811                            int start = selectionStart;
812                            int end = selectionEnd;
813                         
814                            for(int pos=end; 
815                                pos>=start-getPosInLine(start) && pos>=0; 
816                                pos--)
817                            {
818                                if (pos == 0 || text.charAt(pos - 1) == '\n')
819                                {
820                                    end += conf.getTabWidth();
821                                    insertTab(pos);
822                                }
823                            }
824                            setSelection(start,end);
825                        }
826                        e.consume();
827                        break;
828                    case KeyEvent.VK_DELETE:
829                        if (selectionStart == selectionEnd)
830                        {
831                            if (caretPosition < text.length()) {
832                                remove(caretPosition, 1);
833                            }
834                        }
835                        else
836                        {
837                            remove(selectionStart, selectionEnd - selectionStart);
838                        }
839                        resetSelection();
840                        positionVisible();
841                        e.consume();
842                        break;
843                    case KeyEvent.VK_HOME:
844                        beginningOfLine(e.isShiftDown());
845                        e.consume();
846                        break;
847                    case KeyEvent.VK_END:
848                        endOfLine(e.isShiftDown());
849                        e.consume();
850                        break;
851                    case KeyEvent.VK_PAGE_UP:
852                        previousLine(15,e.isShiftDown());
853                        e.consume();
854                        break;
855                    case KeyEvent.VK_PAGE_DOWN:
856                        nextLine(15,e.isShiftDown());
857                        e.consume();
858                        break;
859                    default:
860                        e.consume();
861                }
862            }
863        }
864    
865        /** 
866         * Key Typed
867         */
868        public void keyTyped(KeyEvent e)
869        {
870            if (toolKeyListener!=null) {
871                toolKeyListener.keyTyped(e);
872                return;
873            }
874            if (e.isControlDown())
875            {
876            }
877            else if ((e.getModifiers() & KeyEvent.ALT_MASK) > 0)
878            {
879            }
880            else
881            {
882                switch (e.getKeyChar()) {
883                    case KeyEvent.VK_TAB:
884                    case KeyEvent.VK_DELETE:
885                        e.consume();
886                        break;
887                    case KeyEvent.VK_BACK_SPACE:
888                        if (selectionStart == selectionEnd && caretPosition > 0)
889                        {
890                            remove(caretPosition - 1, 1);
891                            resetSelection();
892                        }
893                        else
894                        {
895                            remove(selectionStart, selectionEnd - selectionStart);
896                            resetSelection();
897                        }
898                        e.consume();
899                        break;
900                    case KeyEvent.VK_ENTER:
901                        {
902                            if (selectionStart != selectionEnd)
903                                remove(selectionStart, selectionEnd - selectionStart);
904                            insertReturn();
905                            e.consume();
906                        }
907                        break;
908                    case '}':
909                        {
910                            if (selectionStart != selectionEnd)
911                                remove(selectionStart, selectionEnd - selectionStart);
912                            insertCloseCBracket();
913                        }
914                        break;
915                    default:
916                        if (selectionStart != selectionEnd && 
917                            (!isSelectionCompletion || !isAcceptCompletionChar(e.getKeyChar())))
918                            remove(selectionStart, selectionEnd - selectionStart);
919                        isSelectionCompletion = false;
920                        insertChar(e.getKeyChar());
921                        // automatically run completion at the end of words
922                        if (!isDivider(e.getKeyChar()) && conf.isAutoComplete() &&
923                            syntaxUnderCaret!=COMMENT && syntaxUnderCaret!=STRING &&
924                            (eot() || isDivider(text.charAt(caretPosition)))) {
925                            runCompletionEngine(CompletionEngine.FORWARD);
926                        } else if (completionEngine!=null && 
927                                   completionEngine.isAutomaticCompletionChar(e.getKeyChar()))
928                        {
929                            int i=caretPosition;
930                            completionEngine.runAutomaticCompletion(
931                                this,text,caretPosition,e.getKeyChar());
932                            caretPosition=i;
933                        }
934                        //repaint(); 
935                        positionVisible();
936                }
937            }
938        }
939    
940        /**
941         * Wether to accept proposed completion when a char is typed
942         */
943        boolean isAcceptCompletionChar(char c) {
944            return isDivider(c) || c=='.';
945        }
946    
947        /**
948         * Inserts a closing curly bracket and takes indentation into
949         * account. 
950         */
951        public void insertCloseCBracket() {
952            int white = getWhiteAtLineStart(caretPosition);
953            int lineWidth = getLineWidth(caretPosition);
954            int posInLine = getPosInLine(caretPosition);
955            int neededWhite = 0;
956          
957            int count = 1;
958            int pos = getCaretPosition() - 1;
959          
960            if (pos > 0)
961            {
962                do
963                {
964                    if (text.charAt(pos) == '}')
965                        count ++;
966                    else if (text.charAt(pos) == '{')
967                        count --;                                 
968                
969                    if (count != 0) 
970                        pos --;
971                }
972                while (pos > -1 && count > 0);
973            }
974             
975            // array style (={a,b})
976            if (pos==text.length()-1 || text.charAt(pos+1)!='\n') {
977                insertChar('}');
978                return;
979            }
980             
981            // block style
982            while (pos > 0 && text.charAt(pos - 1) != '\n')
983            {
984                neededWhite ++;
985                pos --;
986                if(text.charAt(pos)!=' ') neededWhite=0;
987            }
988          
989            if (white == posInLine && white > neededWhite)
990            {
991                remove(caretPosition - posInLine + neededWhite, 
992                       white - neededWhite);
993                insertChar('}');
994            }
995            else if (white == posInLine)
996            {
997                insertChar('}');
998            }
999            else
1000            {
1001                insertChar('\n');
1002                for(int i=0; i<neededWhite; i++)
1003                    insertChar(' ');
1004                insertChar('}');
1005            }                        
1006        }
1007    
1008        /**
1009         * Inserts a carriage return and takes indentation into account if
1010         * at the end of a line.
1011         */
1012        public void insertReturn() {
1013            int white = 0;
1014            if (caretPosition==text.length() || text.charAt(caretPosition)=='\n') {
1015                white = getWhiteAtLineStart(selectionStart);
1016                if (selectionStart > 0 && text.charAt(selectionStart - 1) == '{') {
1017                    white += 4;
1018                }
1019            } else {
1020                int savedCaret = caretPosition;
1021                int white2 = getWhiteAtLineStart(caretPosition);
1022                realBeginningOfLine(false);
1023                if (caretPosition>0) {
1024                    white = getWhiteAtLineStart(caretPosition-1) - white2;
1025                    if (savedCaret - caretPosition<=white2)
1026                        white += savedCaret - caretPosition;
1027                }
1028                caretPosition = savedCaret;
1029            }
1030            String ins = "" + '\n';
1031            for(int i=0; i<white; i++) ins += " ";
1032            insertString(caretPosition, ins);
1033        }
1034       
1035        /** 
1036         * Inserts a white spaces in the text insted of a tab.
1037         * @param pos The position in the text where the white spaces will be added.
1038         */
1039        void insertTab(int pos)
1040        {
1041            insertString(pos, Strings.newString(' ',conf.getTabWidth()));
1042        }
1043    
1044        /** 
1045         * Key Released
1046         */
1047        public void keyReleased(KeyEvent e)
1048        {
1049            testOposing();
1050        }
1051    
1052        /** 
1053         * Used to get the position of a point in a line.
1054         * @param pos the position in text to calculate poition from.
1055         * @return The amount of caracters from the previous \n or start of text to pos
1056         */
1057        int getPosInLine(int pos)
1058        {
1059            int ret = text.lastIndexOf('\n', pos - 1);
1060            ret ++;
1061            ret = pos - ret;   
1062            return ret;
1063        }
1064    
1065        /** 
1066         * Used to get the width of a line.
1067         * @patam A position in the text, that is in the line.
1068         * @return the width of the line where the point is located.
1069         */
1070        int getLineWidth(int pos)
1071        {
1072            int start = 0;
1073            int end = text.indexOf('\n', pos);
1074            if (pos > 0) {
1075                start = text.lastIndexOf('\n',pos-1) + 1;
1076            }
1077          
1078            if (end == -1) 
1079                end = text.length();
1080          
1081            return end - start;
1082        }
1083    
1084    
1085        /** 
1086         * Used to get the content of a line.
1087         * @patam A position in the text, that is in the line.
1088         * @return the line's content
1089         */
1090        String getLineText(int pos)
1091        {
1092            int start = 0;
1093            int end = text.indexOf('\n', pos);
1094            if (pos > 0) {
1095                start = text.lastIndexOf('\n', pos-1) + 1;
1096            }
1097          
1098            if (end == -1) 
1099                end = text.length();
1100          
1101            return text.substring(start,end);
1102        }
1103    
1104        /** 
1105         * Returns the number of white spaces at the begining of a line.
1106         * @param pos A position in the line where you whant to find the white spaces.
1107         * @return the number of white spaces at the begining of a line.
1108         */
1109        int getWhiteAtLineStart(int pos)
1110        {
1111            int ret = 0;
1112          
1113            for(int i = pos - getPosInLine(pos); 
1114                i >= 0 && i < text.length() && text.charAt(i) == ' '; 
1115                i++)
1116            {
1117                ret ++;
1118            }
1119          
1120            return ret;
1121        }
1122    
1123        /** 
1124         * Inserts a char at caretPosition.
1125         */
1126        void insertChar(char c)
1127        {
1128            changed = true;
1129            doneActions.add(0, new TextAction(TextAction.INSERT, caretPosition, 1, ""));
1130            text = text.substring(0, caretPosition) + c + 
1131                text.substring(caretPosition, text.length());
1132            caretPosition ++;
1133            resetSelection();
1134    
1135            fireTextUpdate();
1136            repaint(); 
1137            positionVisible();
1138        }
1139    
1140        /** 
1141         * Inserts a String s into the text at pos.
1142         * @param pos The position where you whant to insert a String.
1143         * @param str The String you whant to insert into the text.
1144         */
1145        public void insertString(int pos, String str)
1146        {
1147            changed = true;
1148            doneActions.add(0, new TextAction(TextAction.INSERT, pos, str.length(), ""));
1149            text = text.substring(0, pos) + str + text.substring(pos, text.length());
1150            if (caretPosition >= pos)
1151            {
1152                caretPosition += str.length();
1153                resetSelection();
1154            }
1155            fireTextUpdate();
1156            repaint(); 
1157            positionVisible();
1158        }
1159    
1160        /** 
1161         * Removes the content of the text from offset to offset + length.
1162         * @param offset Where to start removing text.
1163         * @param length length of the text you whant to remove.
1164         */
1165        public void remove(int offset, int length)
1166        {
1167            changed = true;
1168            if (offset >= 0 && offset + length <= text.length())
1169            {
1170                doneActions.add(
1171                    0, 
1172                    new TextAction(TextAction.REMOVE, offset, length, 
1173                                   text.substring(offset, offset + length)));
1174             
1175                text = text.substring(0,offset) + text.substring(offset+length,text.length());
1176                if (caretPosition > offset && caretPosition <= offset + length)
1177                {
1178                    setCaretPosition(offset);
1179                }
1180                else if (caretPosition > offset)
1181                {
1182                    setCaretPosition(caretPosition-length);
1183                }
1184                repaint();
1185                fireTextUpdate();
1186                positionVisible();
1187            }
1188        }
1189    
1190        /** 
1191         * Tels the SHEditor to undo the last action.
1192         */
1193        public void undo()
1194        {
1195            if (doneActions.size() > 0)
1196            {
1197                changed = true;
1198                TextAction ta = (TextAction)doneActions.remove(0);
1199                try
1200                {
1201                    if (ta.action == TextAction.INSERT) {
1202                        redoneActions.add(
1203                            0, 
1204                            new TextAction(TextAction.REMOVE, 
1205                                           ta.position, 
1206                                           ta.length, 
1207                                           text.substring(ta.position, ta.position + ta.length)));
1208                        text = text.substring(0, ta.position) + 
1209                            text.substring(ta.position + ta.length, text.length());
1210                    } else if (ta.action == TextAction.REMOVE) {
1211                        redoneActions.add(
1212                            0, 
1213                            new TextAction(TextAction.INSERT, ta.position, ta.length, ""));
1214                        text = text.substring(0, ta.position) + ta.string + 
1215                            text.substring(ta.position, text.length());
1216                    }
1217                    caretPosition = ta.position;
1218                }
1219                catch(Exception e)
1220                {
1221                }
1222                fireTextUpdate();
1223                repaint();
1224            }
1225        }
1226    
1227        /** 
1228         * Tels the SHEditor to redo the last undone action.
1229         */
1230        public void redo()
1231        {
1232            if (redoneActions.size() > 0)
1233            {
1234                changed = true;
1235                TextAction ta = (TextAction)redoneActions.remove(0);
1236                try
1237                {
1238                    if (ta.action == TextAction.INSERT)
1239                    {
1240                        doneActions.add(
1241                            0, 
1242                            new TextAction(
1243                                TextAction.REMOVE, ta.position, ta.length, 
1244                                text.substring(ta.position, ta.position + ta.length)));
1245                        text = text.substring(0, ta.position) + 
1246                            text.substring(ta.position + ta.length, text.length());
1247                    }
1248                    else
1249                    {
1250                        doneActions.add(
1251                            0, 
1252                            new TextAction(TextAction.INSERT, ta.position, ta.length, ""));
1253                        text = text.substring(0, ta.position) + 
1254                            ta.string + text.substring(ta.position, text.length());
1255                    }
1256                    caretPosition = ta.position;
1257                }
1258                catch(Exception e)
1259                {
1260                }
1261                fireTextUpdate();
1262                repaint();
1263            }
1264        }
1265    
1266        /** 
1267         * Returns the number of lines to a point.
1268         * @param pos the position to where you whant to count the lines.
1269         * @return The numbers of lines to the pos.
1270         */
1271        int getLine(int pos)
1272        {
1273            int ret = 0;
1274          
1275            int brPos = 0;
1276            boolean done = false;
1277          
1278            while (brPos != -1 && !done)
1279            {
1280                brPos = text.indexOf('\n', brPos);
1281             
1282                if (pos <= brPos)
1283                {
1284                    done = true;
1285                }
1286            }
1287            return ret;
1288        }
1289    
1290        /** 
1291         * Preformes a cut action on the SHEditor.
1292         */
1293        public void cut()
1294        {
1295            if (selectionStart != selectionEnd)
1296            {
1297                try
1298                {
1299                    Clipboard clipboard = getToolkit().getSystemClipboard();
1300                    String post = text.substring(selectionStart, selectionEnd);
1301                    clipboard.setContents(new StringSelection(post), this);
1302                    remove(selectionStart,selectionEnd-selectionStart);
1303                    resetSelection();
1304                    setCaretPosition(selectionStart);
1305                    positionVisible();
1306                }
1307                catch(Exception e)
1308                {
1309                }
1310            }
1311        }
1312    
1313        /** 
1314         * Preformes a copy action on the JJAvaPane.
1315         */
1316        public void copy()
1317        {
1318            if (selectionStart != selectionEnd)
1319            {
1320                try
1321                {
1322                    Clipboard clipboard = getToolkit().getSystemClipboard();
1323                    String post = text.substring(selectionStart, selectionEnd);
1324                    clipboard.setContents(new StringSelection(post), this);
1325                }
1326                catch(Exception e)
1327                {
1328                }
1329            }
1330        }
1331    
1332        /** 
1333         * Preformes a paste action on the SHEditor.
1334         */
1335        public void paste()
1336        {
1337            loggerClip.debug("paste...");
1338            if (selectionStart != selectionEnd)
1339            {
1340                remove(selectionStart,selectionEnd-selectionStart);
1341                resetSelection();
1342                setCaretPosition(selectionStart);
1343            }
1344            Clipboard clipboard = getToolkit().getSystemClipboard();
1345            Transferable content = clipboard.getContents(this);
1346            if (content != null)
1347            {
1348                try
1349                {
1350                    insertString(caretPosition, 
1351                                 (String)content.getTransferData(DataFlavor.stringFlavor));
1352                }
1353                catch (Exception e)
1354                {            
1355                    loggerClip.error("error in copying",e);
1356                }
1357            } else {
1358                loggerClip.debug("content is null");
1359            }
1360            fireTextUpdate();
1361        }
1362    
1363        /**
1364         * Returns the number of a char in a the text between to positions.
1365         * @param c the char to count.
1366         * @param offset offset where to start looking.
1367         * @param length length the length of the area to search.
1368         */
1369        public int countChar(char c, int offset, int length)
1370        {
1371            int ret = 0;
1372            for (int i=offset; i < offset+length; i++) {
1373                if (text.charAt(i) == c) 
1374                    ret ++;
1375            }
1376            return c;
1377        }
1378    
1379        public void repaint() {
1380            super.repaint();
1381            //logger.debug("repaint",new Exception());
1382        }
1383    
1384        int getCharWidth(char c) {
1385            if (Character.isDefined(c))
1386                return metrics.charWidth(c);
1387            else 
1388                return 0;
1389        }
1390    
1391        /** 
1392         * Paints the SHEditor. syntaxUnderCaret is updated.
1393         * @param g the graphics to draw the SHEditor on.
1394         */
1395        public void paintComponent(Graphics g)
1396        {
1397            super.paintComponent(g);
1398    
1399            g.setFont(getFont());                              
1400            lineHeight = metrics.getHeight();
1401    
1402            Rectangle rect = getVisibleRect();
1403            logger.debug("SHEditor.paintComponent on "+rect+" / "+g.getClip());
1404            rect = g.getClipBounds();
1405    
1406            g.setColor(conf.getBackgroundColor());
1407            g.fillRect(rect.x + getMarginLeft(), rect.y, 
1408                       rect.width - getMarginLeft(), rect.height);
1409            g.setColor(new Color(180, 180, 180));
1410            g.fillRect(rect.x, rect.y, getMarginLeft(), rect.height);
1411          
1412            int line = 1;
1413            int pos = 0;
1414            boolean done = false;
1415            boolean ignore = false;
1416    
1417            StringBuffer word = new StringBuffer(); // Current word to be displayed
1418          
1419            int posXStart = getMarginLeft();
1420            int posX = posXStart;
1421            int maxPosX = posX;
1422            int posY = lineHeight;
1423    
1424            // Calculate start line and position
1425            while(posY + lineHeight < rect.y)
1426            {
1427                posY += lineHeight;
1428                if (pos + 1 < text.length()) {
1429                    pos = text.indexOf("\n", pos);
1430                    pos ++;
1431                    line ++;
1432                } else {
1433                    break;
1434                }
1435            }
1436    
1437            if (text.lastIndexOf("/ *", pos) > text.lastIndexOf("* /", pos))
1438            {
1439                int lastPos = text.lastIndexOf("/*", pos);
1440                int posInLine = getPosInLine(lastPos);          
1441                int count = countChar('"', lastPos - posInLine, posInLine);          
1442                ignore = (count%2 == 0);
1443            }          
1444    
1445            syntaxUnderCaret = DEFAULT;
1446            while (pos < text.length())
1447            {
1448                done = false;
1449                g.setColor(conf.getTextColor());
1450             
1451                if (ignore)
1452                {
1453                    word.append(text.charAt(pos));
1454                    pos ++;
1455          
1456                    while (pos < text.length() && !done)
1457                    {
1458                        if (text.charAt(pos) == '/' && text.charAt(pos - 1) == '*') {
1459                            done = true;
1460                        }
1461                        word.append(text.charAt(pos));
1462                        pos ++;               
1463                    }
1464                }
1465                // Double-quoted strings ("blabla")
1466                else if (word.length() == 0 && text.charAt(pos) == '"')
1467                {
1468                    int startp = pos;
1469                    word.append(text.charAt(pos));
1470                    pos ++;
1471                
1472                    while (pos < text.length() && !done)
1473                    {
1474                        if (text.charAt(pos) == '"') {
1475                            if (text.charAt(pos - 1) != '\\' || 
1476                                text.charAt(pos - 2) == '\\') 
1477                            {
1478                                done = true;
1479                            }
1480                        } else if (text.charAt(pos) == '\n') {
1481                            // Quoted Strings are not multiline
1482                            done = true;
1483                        }
1484             
1485                        word.append(text.charAt(pos));
1486                        pos ++;               
1487                    }
1488                    if (caretPosition<pos && caretPosition>startp)
1489                        syntaxUnderCaret = STRING;
1490                    g.setColor(conf.getStringColor());
1491                }
1492                // Single-quoted strings ('blabla')
1493                else if (word.length() == 0 && text.charAt(pos) == '\'')
1494                {
1495                    int startp = pos;
1496                    word.append(text.charAt(pos));
1497                    pos ++;
1498          
1499                    while (pos < text.length() && !done)
1500                    {
1501                        if (text.charAt(pos) == '\'') {
1502                            if (text.charAt(pos - 1) != '\\' || 
1503                                text.charAt(pos - 2) == '\\') 
1504                            {
1505                                done = true;
1506                            }
1507                        } else if (text.charAt(pos) == '\n') {
1508                            // Quoted Strings are not multiline
1509                            done = true;
1510                        }
1511             
1512                        word.append(text.charAt(pos));
1513                        pos ++;               
1514                    }
1515                    if (caretPosition<pos && caretPosition>startp)
1516                        syntaxUnderCaret = STRING;
1517                    g.setColor(conf.getStringColor());
1518                }
1519                // Single line comments (// comment blabla)
1520                else if (text.charAt(pos) == '/' && 
1521                         pos < text.length() - 1 && 
1522                         text.charAt(pos + 1) == '/' && 
1523                         (pos == 0 || text.charAt(pos - 1) != '*'))
1524                {
1525                    int startp = pos;
1526                    word.append(text.charAt(pos));
1527                    pos ++;
1528          
1529                    while (pos < text.length() && !done)
1530                    {
1531                        if (text.charAt(pos) == '\n') {
1532                            done = true;
1533                        }
1534                        word.append(text.charAt(pos));
1535                        pos ++;               
1536                    }
1537                    if (caretPosition<=pos && caretPosition>startp)
1538                        syntaxUnderCaret = COMMENT;
1539                    g.setColor(conf.getIgnoreColor());
1540                }
1541                // Multiline comments (/* comment blabla */)
1542                else if (text.charAt(pos) == '/' && 
1543                         pos < text.length() - 1 && 
1544                         text.charAt(pos + 1) == '*' && 
1545                         (pos == 0 || text.charAt(pos - 1) != '/'))
1546                {
1547                    int startp = pos;
1548                    word.append(text.charAt(pos));
1549                    pos ++;
1550          
1551                    while (pos < text.length() && !done)
1552                    {
1553                        if (text.charAt(pos) == '/' && 
1554                            text.charAt(pos - 1) == '*') 
1555                        {
1556                            done = true;
1557                        }
1558                        word.append(text.charAt(pos));
1559                        pos ++;               
1560                    }
1561                    if (caretPosition<pos && caretPosition>startp)
1562                        syntaxUnderCaret = COMMENT;
1563                    g.setColor(conf.getIgnoreColor());
1564                }
1565                // Word separator
1566                else if (isDivider(text.charAt(pos)))
1567                {
1568                    if (word.length() == 0) {
1569                        word.append(text.charAt(pos));
1570                        pos ++;
1571                    }
1572                    done = true;
1573                }
1574                // Normal word
1575                else
1576                {          
1577                    while (pos < text.length() && !done)
1578                    {
1579                        if (isDivider(text.charAt(pos))) {
1580                            done = true;
1581                        } else {
1582                            word.append(text.charAt(pos));
1583                            pos ++;
1584                        }
1585                    }
1586                    if (isKeyword(word.toString())) {
1587                        g.setColor(conf.getKeywordColor());
1588                    } else if (isModifier(word.toString())) {
1589                        g.setColor(conf.getModifierColor());
1590                    } else if (isType(word.toString())) {
1591                        g.setColor(conf.getTypeColor());
1592                    } else {
1593                        g.setColor(conf.getTextColor());
1594                    }
1595                }
1596    
1597                if (ignore) {
1598                    g.setColor(conf.getIgnoreColor());
1599                    ignore = false;
1600                } else if (word.toString().equals("{") || 
1601                           word.toString().equals("}")) {
1602                    g.setColor(conf.getClampColor());
1603                }
1604    
1605                for(int i=0; i<word.length(); i++)
1606                {
1607                    char c = word.charAt(i);
1608                    int charWidth = getCharWidth(c);
1609    
1610                    if (posY + lineHeight > rect.y && 
1611                        pos == openPos + 1 || pos == closePos + 1)
1612                    {
1613                        // Poink background for openin gor closing chars ('(','[',')',']',...)
1614                        Color tmpColor = g.getColor();
1615                        g.setColor(Color.pink);
1616                        if (posX >= rect.x && posX < rect.x + rect.width)
1617                            g.fillRect(posX, posY - metrics.getAscent(),
1618                                       charWidth,
1619                                       metrics.getAscent());
1620                        g.setColor(tmpColor);
1621                    } 
1622                    else if (posY + lineHeight > rect.y && 
1623                             pos - word.length() + i < selectionEnd && 
1624                             pos - word.length() + i >= selectionStart)
1625                    {
1626                        // draw background for char
1627                        Color tmpColor = g.getColor();
1628                        g.setColor(isSelectionCompletion ?
1629                                   conf.getCompletionColor() : conf.getSelectionColor());
1630                        if (posX >= rect.x && posX < rect.x + rect.width)
1631                            g.fillRect(posX, posY - metrics.getAscent(), 
1632                                       charWidth, 
1633                                       metrics.getAscent());
1634                        g.setColor(tmpColor);
1635                    }
1636    
1637                    if (posY + lineHeight > rect.y && 
1638                        hasFocus() && 
1639                        showCaret && 
1640                        caretPosition == pos - word.length() + i)
1641                    {
1642                        car.x = posX;
1643                        car.y = posY - lineHeight;
1644                        car.height = lineHeight;
1645                    }
1646                    if (c == '\n')
1647                    {
1648                        Color tmpColor = g.getColor();
1649                        g.setColor(new Color(180, 180, 180));
1650                        g.fillRect(rect.x, posY - lineHeight, 
1651                                   getMarginLeft(), lineHeight);
1652                        g.setColor(new Color(200, 0, 0));
1653                        g.drawString(
1654                            "" + line, 
1655                            rect.x + (getMarginLeft()-10)
1656                            - metrics.charsWidth(("" + line).toCharArray(), 
1657                                                 0, ("" + line).length()), 
1658                            posY - metrics.getDescent());
1659                
1660                        if (lineToMark == line)
1661                        {
1662                            g.setColor(Color.green);
1663                            g.fillPolygon(
1664                                new int[] { rect.x + 58, rect.x + 63, rect.x + 58 }, 
1665                                new int[] { posY - (int)(lineHeight / 1.5d), 
1666                                            posY - (int)(lineHeight / 3d), 
1667                                            posY },
1668                                3);
1669                        }
1670                        g.setColor(tmpColor);
1671                
1672                        posY += lineHeight;
1673                        posX = posXStart;
1674                        line ++;               
1675                    }
1676                    else if (c == '\t')
1677                    {
1678                        posX += getCharWidth(' ') * conf.getTabWidth();
1679                    }
1680                    else if (charWidth > 0 && posY + lineHeight > rect.y)
1681                    {
1682                        if ((posX+charWidth) >= rect.x && posX < rect.x + rect.width)
1683                            g.drawString("" + c, posX, posY - metrics.getDescent());
1684                        posX += charWidth;
1685                    }           
1686                }
1687             
1688                if (posX > maxPosX) {
1689                    maxPosX = posX;
1690                }
1691    
1692                // If we're out of the viewport, skip until the end
1693                if (posY - lineHeight > rect.y + rect.height)
1694                {
1695                    for(int p = pos; p != -1; p = text.indexOf('\n', p + 1))
1696                    {
1697                        posY += lineHeight;
1698                   
1699                        int width = getPixelTextWidth(getLineText(p));
1700                        if (width > maxPosX) {
1701                            maxPosX = width;
1702                        }
1703                    }
1704                
1705                    pos = text.length();
1706                }
1707             
1708                word.delete(0, word.length());
1709            }
1710          
1711            if (conf.getShowLineNumbers() && 
1712                (posY - lineHeight <= rect.y + rect.height))
1713            {
1714                g.setColor(conf.getLineNrBgColor());
1715                g.fillRect(rect.x, posY - lineHeight, 
1716                           getMarginLeft(), lineHeight);
1717                g.setColor(conf.getLineNrColor());
1718                g.drawString("" + line, 
1719                             rect.x + (getMarginLeft()-10) 
1720                             - metrics.charsWidth(("" + line).toCharArray(), 
1721                                                  0, ("" + line).length()), 
1722                             posY - metrics.getDescent());
1723             
1724                if (lineToMark == line)
1725                {
1726                    g.setColor(Color.green);
1727                    g.fillPolygon(
1728                        new int[] { rect.x + 58, rect.x + 63, rect.x + 58 },
1729                        new int[] { posY - (int)(lineHeight / 1.5d),
1730                                    posY - (int)(lineHeight / 3d),
1731                                    posY },
1732                        3);
1733                }
1734            }
1735          
1736            if (hasFocus() && 
1737                posY - lineHeight <= rect.y + rect.height && 
1738                showCaret && pos == text.length() && 
1739                pos == caretPosition)
1740            {
1741                car.x = posX;
1742                car.y = posY - lineHeight;
1743                car.height = lineHeight;
1744            }
1745    
1746            if (hasFocus())
1747                g.setColor(Color.black);
1748            else
1749                g.setColor(Color.gray);
1750            g.fillRect(car.x, car.y, car.width, car.height);
1751          
1752            if (posY + 10 != getSize().height || maxPosX + 10 != getSize().width)
1753            {
1754                setMinimumSize(
1755                    new Dimension(
1756                        (maxPosX+10 > getSize().width ? maxPosX+10 : getSize().width),
1757                        posY+10));
1758                setPreferredSize(
1759                    new Dimension(maxPosX+10,posY+10));
1760                logger.debug("revalidate");
1761                revalidate();
1762            }
1763        }
1764    
1765        /**
1766         * Returns the pixel width of a string
1767         * @param text the string to get the width of
1768         * @return the width in number of pixels the text will have when drawn.
1769         */
1770        protected int getPixelTextWidth(String text) {
1771            int width = 0;
1772            for (int i=0; i<text.length(); i++) {
1773                if (text.charAt(i)==KeyEvent.VK_TAB)
1774                    width += getCharWidth(' ') * conf.getTabWidth();
1775                else 
1776                    width += getCharWidth(text.charAt(i));
1777             
1778            }
1779            return width;
1780        }
1781    
1782        /** 
1783         * Updates the JJAvaPane (cals paint(Graphics g));
1784         */
1785        public void update(Graphics g)
1786        {
1787            paint(g);
1788        }
1789    
1790        /**
1791         * Sets a line as marked, and scrols to that line if needed.
1792         */
1793        public void scrollToLine(int line)
1794        {         
1795            lineToMark = line;
1796          
1797            int l = 1;
1798            int pos = 0;
1799            while (l <= line && pos != -1)
1800            {
1801                pos = text.indexOf('\n', pos + 1);
1802                l ++;
1803            }
1804            setCaretPosition(pos);
1805            requestFocus();
1806            //repaint();
1807            positionVisible();
1808        }    
1809    
1810        /** 
1811         * Tests oposing (), [] and {}, and makes sure they are marked if
1812         * the caret is behind one of them.  
1813         */
1814        public void testOposing()
1815        {  
1816            //unmarkOposing();
1817            if (getCaretPosition() > -1 && getCaretPosition() - 1 < text.length())
1818            {
1819                if (getCaretPosition() > 0)
1820                {
1821                    char c = text.charAt(getCaretPosition() - 1);
1822                    if (c == '}') {
1823                        testOpening('{','}');
1824                    } else if (c == '{') {
1825                        testClosing('{','}');
1826                    } else if (c == ')') {
1827                        testOpening('(',')');
1828                    } else if (c == '(') {
1829                        testClosing('(',')');
1830                    } else if (c == ']') {
1831                        testOpening('[',']');
1832                    } else if (c == '[') {
1833                        testClosing('[',']');
1834                    } else {
1835                        unmarkOposing();
1836                    }
1837                } else {
1838                    repaint();
1839                }
1840            }
1841        }
1842    
1843        public void testClosing(char opening, char closing) {
1844            if (text.charAt(getCaretPosition() - 1 ) == opening)
1845            {
1846                int oldOpenPos = openPos;
1847                int oldClosePos = closePos;
1848    
1849                openPos = getCaretPosition() - 1;
1850             
1851                int count = 1;
1852                int pos = getCaretPosition() - 1;
1853                
1854                while (pos < text.length() - 1 && count > 0) {
1855                    pos ++;
1856                    if (text.charAt(pos) == opening)
1857                        count ++;
1858                    else if (text.charAt(pos) == closing)
1859                        count --;
1860                }
1861                
1862                if (count > 0) {
1863                    closePos = -1;
1864                    openPos = -1;
1865                } else {
1866                    closePos = pos;
1867                }
1868             
1869                repaintCharAt(oldOpenPos);
1870                repaintCharAt(oldClosePos);
1871                repaintCharAt(openPos);
1872                repaintCharAt(closePos);
1873                repaintCharAt(caretPosition);
1874            }
1875        }
1876    
1877        public void testOpening(char opening, char closing) {
1878            if (text.charAt(getCaretPosition() - 1) == closing)
1879            {
1880                int oldOpenPos = openPos;
1881                int oldClosePos = closePos;
1882    
1883                openPos = getCaretPosition() - 1;
1884                
1885                int count = 0;
1886                int pos = getCaretPosition() - 1;
1887                
1888                if (getCaretPosition() > 0) {
1889                    do {
1890                        if (text.charAt(pos) == closing)
1891                            count ++;
1892                        else if (text.charAt(pos) == opening)
1893                            count --;                                 
1894                      
1895                        if (count != 0)
1896                            pos --;
1897                    } while (pos > -1 && count > 0);
1898                }
1899    
1900                closePos = pos;
1901                   
1902                if (closePos == -1)
1903                    openPos = -1;
1904                
1905                repaintCharAt(oldOpenPos);
1906                repaintCharAt(oldClosePos);
1907                repaintCharAt(openPos);
1908                repaintCharAt(closePos);
1909                repaintCharAt(caretPosition);
1910            }
1911        }
1912    
1913        /**
1914         * Unmarks oposing (), {} or [] given by openPos and closePos.
1915         */
1916        public void unmarkOposing()
1917        {
1918            repaintCharAt(openPos);
1919            repaintCharAt(closePos);
1920            openPos = -1;
1921            closePos = -1;
1922        }
1923    
1924        /** 
1925         * Makes sure the caret is visible on screen.
1926         */
1927        public void positionVisible()
1928        {
1929            positionVisible(caretPosition);
1930        }
1931    
1932        /** 
1933         * Makes sure the selection is visible on screen.
1934         */
1935        public void selectionVisible()
1936        {
1937            positionVisible(selectionStart);
1938        }
1939    
1940        /**
1941         * Makes sure a position is visible
1942         * @param position the position that must be visible
1943         */
1944        public void positionVisible(int position)
1945        {
1946            if (getParent() instanceof JViewport)
1947            {
1948                Rectangle rect = getVisibleRect();
1949                Rectangle car = getCaretPos(position);
1950             
1951                try
1952                {
1953                    JScrollPane scrollPane = (JScrollPane)((JViewport)getParent()).getParent();
1954                    JScrollBar hs = scrollPane.getHorizontalScrollBar();
1955                    JScrollBar vs = scrollPane.getVerticalScrollBar();
1956                
1957                    int newX = hs.getValue();
1958                    int newY = vs.getValue();
1959             
1960                    if (rect.x + rect.width - getMarginLeft() <= car.x)
1961                        newX = car.x + 75 - rect.width;
1962                    else if (rect.x > car.x)
1963                        newX = car.x - 10;
1964             
1965                    if (rect.y + rect.height <= car.y + car.height)
1966                        newY = car.y + 20 + car.height - rect.height;
1967                    else if (rect.y >= car.y) 
1968                        newY = car.y - 20;
1969             
1970                    if (newX != hs.getValue()) {
1971                        hs.setValue(newX);
1972                    }
1973                    if (newY != vs.getValue()) {
1974                        vs.setValue(newY);
1975                    }
1976                }
1977                catch(Exception e)
1978                {
1979                    logger.error("positionVisible "+position, e);
1980                }
1981            } 
1982        }
1983       
1984        /** 
1985         * Returns a rectangle representation of the caret at a given position.
1986         * @param position position of the caret
1987         * @return a Rectangle representation of the caret.
1988         */
1989        public Rectangle getCaretPos(int position)
1990        {
1991            Rectangle ret = new Rectangle(0, 0, 0 ,0);
1992          
1993            if (metrics != null)
1994            {
1995                int lineStart = 0;
1996                int posY = lineHeight;
1997                int i = 0;
1998                for(; i < text.length() && i < position; i ++)
1999                {
2000                    if (text.charAt(i) == '\n')
2001                    {
2002                        lineStart = i + 1;
2003                        posY += lineHeight;
2004                    }            
2005                }
2006             
2007                ret.x = metrics.stringWidth(text.substring(lineStart, i));
2008                ret.y = posY - lineHeight;
2009                ret.height = lineHeight;
2010                ret.width = getCharWidth('m');
2011            }
2012    
2013            return ret;
2014        }
2015    
2016        /**
2017         * Sets the font of the SHEditor and repaqints it.
2018         * @param font the new font.
2019         */
2020        public void setFont(Font font)
2021        {
2022            super.setFont(font);
2023            repaint();
2024        }
2025    
2026        int OFFSET = 35;
2027        protected int getMarginLeft() {
2028            return conf.getShowLineNumbers()?OFFSET:0;
2029        }
2030    
2031        /** 
2032         * Returns position in the text based on coordinates x and y.
2033         * @param mousePosX the x coordinate of the position in the text.
2034         * @param mousePosY the y coordinate of the position in the text.
2035         * @return the position in the text represented by pint x, y.
2036         */
2037        public int getCharPos(int mousePosX, int mousePosY)
2038        {
2039            if (metrics != null)
2040            {
2041                Rectangle rect = getVisibleRect();
2042          
2043                int pos = 0;
2044                boolean done = false;
2045    
2046                String word = "";
2047          
2048                int posXStart = getMarginLeft();
2049                int posX = posXStart;
2050                int posY = lineHeight;
2051             
2052                while (posY + lineHeight < rect.y)
2053                {
2054                    posY += lineHeight;
2055                    if (pos + 1 < text.length())
2056                    {
2057                        pos = text.indexOf('\n', pos);
2058                        pos ++;
2059                    }
2060                    else
2061                    {
2062                        break;
2063                    }           
2064                }
2065             
2066                if (mousePosY < posY - lineHeight)
2067                {
2068                    return -1;
2069                }
2070             
2071                while (pos < text.length()) 
2072                {
2073                    if (text.charAt(pos) == '\n')
2074                    {
2075                        if (mousePosX > posX && 
2076                            mousePosY < posY && 
2077                            mousePosY >= posY - lineHeight)
2078                        {
2079                            return pos;
2080                        }
2081                        else if (mousePosX <= posXStart && 
2082                                 mousePosY < posY && 
2083                                 mousePosY >= posY - lineHeight)
2084                        {
2085                            if (pos + 1 < text.length())
2086                            {
2087                                return pos;
2088                            }
2089                        }
2090                        posY += lineHeight;
2091                        posX = posXStart;
2092                   
2093                    }
2094                    else if (posX < mousePosX && 
2095                             posX + (getCharWidth(text.charAt(pos)) / 2) >= mousePosX && 
2096                             mousePosY < posY && 
2097                             mousePosY >= posY - lineHeight + 2)
2098                    {
2099                        return pos;
2100                    }
2101                    else if (posX < mousePosX && 
2102                             posX + getCharWidth(text.charAt(pos)) >= mousePosX && 
2103                             mousePosY < posY && 
2104                             mousePosY >= posY - lineHeight)
2105                    {
2106                        if (pos + 1 < text.length())
2107                        {
2108                            return pos + 1;
2109                        }
2110                        posX += getCharWidth(text.charAt(pos));
2111                    }
2112                    else if (mousePosX <= posXStart && 
2113                             mousePosY < posY && 
2114                             mousePosY >= posY - lineHeight)
2115                    {
2116                        if (pos + 1 < text.length())
2117                        {
2118                            return pos;
2119                        }
2120                        posX += getCharWidth(text.charAt(pos));
2121                    }
2122                    else
2123                    {
2124                        posX += getCharWidth(text.charAt(pos));
2125                    }
2126                    pos ++;
2127                }
2128            }
2129          
2130            return text.length();
2131        }
2132    
2133        /** 
2134         * Tests if a character is a divider.
2135         * @param c the char to test.
2136         * @return true if the char is a divider, meaning it might divide
2137         * two java words, returns false othervise.  */
2138        public boolean isDivider(char c)
2139        {
2140            for(int i=0; i<separators.length; i++) {
2141                if (c == separators[i])
2142                    return true;
2143            }
2144            return false;
2145        }
2146    
2147        Set keywords = new HashSet();
2148        /**
2149         * Define some new keywords
2150         * @param addedKeywords the new keywords
2151         */
2152        public void addKeywords(String[] addedKeywords) {
2153            for(int i=0; i<addedKeywords.length; i++) {
2154                keywords.add(addedKeywords[i]);
2155            }
2156        }
2157    
2158        /**
2159         * Define some new keywords
2160         * @param addedKeywords the new keywords
2161         */
2162        public void addKeywords(Set addedKeywords) {
2163            keywords.addAll(addedKeywords);
2164        }
2165    
2166        public void clearKeywords() {
2167            keywords.clear();
2168        }
2169    
2170        /**
2171         * Tells wether a word is keyword or not
2172         */
2173        protected boolean isKeyword(String word) {
2174            return keywords.contains(word);
2175        }
2176    
2177        Set modifiers = new HashSet();
2178        /**
2179         * Define some new keywords
2180         * @param addedModifiers the new keywords
2181         */
2182        public void addModifiers(String[] addedModifiers) {
2183            for(int i=0; i<addedModifiers.length; i++) {
2184                modifiers.add(addedModifiers[i]);
2185            }
2186        }
2187        /**
2188         * Tells wether a word is a modifier or not
2189         */
2190        public boolean isModifier(String word) {
2191            return modifiers.contains(word);
2192        }
2193    
2194        Set types = new HashSet();
2195        public void addTypes(String[] addedTypes) {
2196            for(int i=0; i<addedTypes.length; i++) {
2197                types.add(addedTypes[i]);
2198            }      
2199        }
2200        /**
2201         * Tells wether a word is a type or not
2202         */
2203        public boolean isType(String word) {
2204            if (types.contains(word))
2205                return true;
2206            int index = word.lastIndexOf('.');
2207            if (index!=-1) {
2208                word = word.substring(index+1);
2209            }
2210            return word.length()>1 && 
2211                Character.isUpperCase(word.charAt(0)) && 
2212                Character.isLowerCase(word.charAt(1));
2213        }
2214       
2215        protected void fireCaretUpdate() {
2216            CaretEvent e = new MutableCaretEvent(this,caretPosition);
2217            // Guaranteed to return a non-null array
2218            Object[] listeners = listenerList.getListenerList();
2219            // Process the listeners last to first, notifying
2220            // those that are interested in this event
2221            for (int i = listeners.length-2; i>=0; i-=2) {
2222                if (listeners[i]==CaretListener.class) {
2223                    ((CaretListener)listeners[i+1]).caretUpdate(e);
2224                }
2225            }
2226        }
2227    
2228        public void setCaretPosition(int caretPosition) {
2229            repaintCharAt(this.caretPosition);
2230            if (caretPosition<0)
2231                caretPosition = 0;
2232            else if (caretPosition>text.length())
2233                caretPosition = text.length();
2234            this.caretPosition = caretPosition;
2235            repaintCharAt(caretPosition);
2236            fireCaretUpdate();
2237        }
2238    
2239        protected void repaintCharAt(int pos) {
2240            if (pos!=-1) {
2241                Rectangle rect = getCaretPos(pos);
2242                logger.debug("repaintCharAt "+pos+" -> "+rect);
2243                repaint(rect);
2244            }
2245        }
2246    
2247        protected void repaintChars(int start, int end) {
2248            if (start<end)
2249                for (int p=start; p<=end; p++) 
2250                    repaintCharAt(p);
2251            else
2252                for (int p=end; p<=start; p++) 
2253                    repaintCharAt(p);
2254        }
2255    
2256        /** 
2257         * Returns the caretPosition.
2258         * @return the position of the caret in the text.
2259         */
2260        public int getCaretPosition()
2261        {
2262            return caretPosition;
2263        }
2264    
2265        /** 
2266         * Returns selection start position
2267         * @return the position of the selectionStart in the text.
2268         */
2269        public int getSelectionStart()
2270        {
2271            return selectionStart;
2272        }
2273       
2274        /** 
2275         * Returns selection end position.
2276         * @return the position of the selectionEnd in the text.
2277         */
2278        public int getSelectionEnd()
2279        {
2280            return selectionEnd;
2281        }
2282    
2283        /** 
2284         * Mouse has been pressed.
2285         */
2286        public void mousePressed(MouseEvent e)
2287        {
2288            requestFocus();
2289            int tmp = getCharPos(e.getX(), e.getY());
2290            mousePressPos = tmp;
2291            if (tmp != -1)
2292            {
2293                isSelectionCompletion = false;
2294                if ((e.getModifiers() & MouseEvent.SHIFT_MASK) > 0)
2295                {
2296                    if (caretPosition == selectionStart && tmp <= selectionEnd) {
2297                        setCaretPosition(tmp);
2298                        selectionStart = caretPosition;
2299                    } else if (caretPosition == selectionStart) {
2300                        selectionStart = selectionEnd;
2301                        setCaretPosition(tmp);
2302                        selectionEnd = caretPosition;
2303                    } else if (caretPosition == selectionEnd && tmp >= selectionStart) {
2304                        setCaretPosition(tmp);
2305                        selectionEnd = caretPosition;
2306                    } else {
2307                        selectionEnd = selectionStart;
2308                        setCaretPosition(tmp);
2309                        selectionStart = caretPosition;
2310                    }            
2311                }
2312                else
2313                {
2314                    setCaretPosition(tmp);
2315                    if (e.getClickCount()==1) {
2316                        selectionEnd = caretPosition;
2317                        selectionStart = caretPosition;
2318                    }
2319                }
2320             
2321                repaint(); 
2322                positionVisible();
2323            }
2324        }
2325    
2326        /** 
2327         * MouseMoved.
2328         */
2329        public void mouseMoved(MouseEvent e)
2330        {
2331        }
2332    
2333        /** 
2334         * MouseDragged.
2335         */
2336        public void mouseDragged(MouseEvent e)
2337        {
2338            int tmp = getCharPos(e.getX(), e.getY());
2339            if (mousePressPos != -1) {
2340                if (mousePressPos <= tmp) {
2341                    selectionStart = mousePressPos;
2342                    selectionEnd = tmp;
2343                } else {
2344                    selectionEnd = mousePressPos;
2345                    selectionStart = tmp;
2346                }
2347                setCaretPosition(tmp);
2348                repaint(); 
2349                positionVisible();
2350            } else {
2351                repaint();
2352            }
2353        }
2354    
2355        /** 
2356         * MouseReleased.
2357         */
2358        public void mouseReleased(MouseEvent e)
2359        {
2360            mousePressPos = -1;
2361            testOposing();
2362        }
2363    
2364        /** 
2365         * MouseClicked.
2366         */
2367        public void mouseClicked(MouseEvent e)
2368        {
2369            if (e.getClickCount()==2) {
2370                selectWord(caretPosition);
2371            }
2372        }
2373    
2374        /** 
2375         * MouseEntered.
2376         */
2377        public void mouseEntered(MouseEvent e)
2378        {
2379        }
2380    
2381        /** 
2382         * MouseExited.
2383         */
2384        public void mouseExited(MouseEvent e)
2385        {
2386        }
2387    
2388        protected int minDisplayedLines = 4;
2389        /**
2390         * Gets the minimum number of lines to display
2391         */
2392        public int getMinDisplayedLines() {
2393            return minDisplayedLines;
2394        }
2395        public void setMinDisplayedLines(int n) {
2396            this.minDisplayedLines = n;
2397        }
2398    
2399        public Dimension getPreferredScrollableViewportSize()
2400        {
2401            int pos = 0;
2402            int lineStart = 0;
2403            int width = 30;
2404            int height;
2405            int lines = 1;
2406            
2407            while (pos < text.length())
2408            {
2409                if (text.charAt(pos)=='\n' || pos==(text.length()-1)) {
2410                    int lineWidth =
2411                        metrics.stringWidth(
2412                            Strings.replace(text.substring(lineStart,pos),
2413                                            "\t",
2414                                            Strings.newString(' ',conf.getTabWidth())))
2415                        + metrics.charWidth('m');
2416                    if (lineWidth>width)
2417                        width = lineWidth;
2418                    lineStart = pos+1;
2419                    lines++;
2420                }
2421                pos++;
2422            }
2423            if (lines<minDisplayedLines)
2424                lines = minDisplayedLines;
2425            height = lines * metrics.getHeight();
2426            return new Dimension(width,height);
2427        }
2428    
2429        public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) 
2430        {
2431            return metrics.getHeight();
2432        }
2433    
2434        /************************************************** 
2435         * Copied from JTextComponent.
2436         */
2437        public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction)
2438        {
2439            switch(orientation)
2440            {
2441                case SwingConstants.VERTICAL:
2442                    return visibleRect.height;
2443                case SwingConstants.HORIZONTAL:
2444                    return visibleRect.width;
2445                default:
2446                    throw new IllegalArgumentException("Invalid orientation: " + orientation);
2447            }
2448        }
2449    
2450    
2451        public boolean getScrollableTracksViewportWidth()
2452        {   
2453            if (getParent() instanceof JViewport)
2454            {
2455                return (((JViewport)getParent()).getWidth() > getPreferredSize().width);
2456            }
2457            return false;
2458        }
2459        public boolean getScrollableTracksViewportHeight()
2460        {
2461            if (getParent() instanceof JViewport) 
2462            {
2463                return (((JViewport)getParent()).getHeight() > getPreferredSize().height);
2464            }
2465            return false;
2466        }
2467    
2468        /**********************/   
2469       
2470        /** 
2471         * Fiered if the SHEditor loses ownership of a Clipboard
2472         */
2473        public void lostOwnership(Clipboard cb, Transferable tr)
2474        {
2475        }
2476    
2477        /**
2478         * The SHEditor has lost focus.
2479         */
2480        public void focusLost(FocusEvent e)
2481        {
2482            repaintCharAt(caretPosition);
2483        }
2484    
2485        /**
2486         * The SHEditor has gained focus.
2487         */
2488        public void focusGained(FocusEvent e)
2489        {
2490            repaintCharAt(caretPosition);
2491        }
2492    
2493        /** 
2494         * Reads a file into SHEditor.
2495         * @param f the file to read into the SHEditor.
2496         */
2497        public void readFromFile(File f)
2498        {
2499            try
2500            {
2501                StringBuffer tmp = new StringBuffer();
2502                if (f.exists())
2503                {
2504                    BufferedReader in = 
2505                        new BufferedReader(
2506                            new InputStreamReader(
2507                                new BufferedInputStream(
2508                                    new FileInputStream(f))));
2509                
2510                    String read = in.readLine();
2511                    boolean first = true;
2512                
2513                    while (read != null)
2514                    {
2515                        if (!first)
2516                        {
2517                            tmp.append("\n" + read);
2518                        }
2519                        else 
2520                        {
2521                            first = false;
2522                            tmp.append(read);
2523                        }
2524                        read = in.readLine();
2525                    }
2526                    in.close();  
2527                    text = tmp.toString();          
2528                }         
2529            }
2530            catch(IOException e)
2531            {
2532                System.out.println("Error: " + e);
2533            }
2534            changed = false;
2535        }
2536    
2537        /** 
2538         * Saves the file in SHEditor to a file.
2539         * @param f the file where the content of the SHEditor should be saved.
2540         */
2541        public void saveToFile(File f)
2542        {
2543            try
2544            {
2545                PrintWriter out = new PrintWriter(new FileWriter(f));
2546                out.print(text);
2547                out.close();
2548            }
2549            catch(IOException e)
2550            {
2551                System.out.println("Error: " + e);
2552            }
2553            changed = false;
2554        }
2555    
2556        /** 
2557         * This class is a text action, it is used to undo redo actions..
2558         */
2559        static class TextAction
2560        {
2561            /**
2562             * The action is a remove action.
2563             */
2564            public final static int REMOVE = 0;
2565            /**
2566             * The action is a insert action.
2567             */
2568            public final static int INSERT = 1;
2569          
2570            int action;
2571            int position;
2572            int length;
2573            String string;
2574          
2575            /** 
2576             * Constructs a new TextActiont.
2577             * @param a action type REMOVE or INSERT.
2578             * @param p the position of the action.
2579             * @param l the length of the action, only needed by a insert action.
2580             * @param s the string content of the action, only needed by a remove action.
2581             */
2582            TextAction(int a, int p, int l, String s)
2583            {
2584                action = a;
2585                position = p;
2586                length = l;
2587                string = s;
2588            }
2589        }
2590    
2591        static class MutableCaretEvent extends CaretEvent {
2592            public MutableCaretEvent(Object source, int caretPosition) {
2593                super(source);
2594                this.caretPosition = caretPosition;
2595            }
2596            int caretPosition;
2597            public int getDot() {
2598                return caretPosition;
2599            }
2600            public int getMark() {
2601                return -1;
2602            }
2603        }
2604    }