KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > antlr > works > ate > ATETextPane


1 /*
2
3 [The "BSD licence"]
4 Copyright (c) 2005 Jean Bovet
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
9 are met:
10
11 1. Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in the
15 documentation and/or other materials provided with the distribution.
16 3. The name of the author may not be used to endorse or promote products
17 derived from this software without specific prior written permission.
18
19 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 */

31
32 package org.antlr.works.ate;
33
34 import org.antlr.works.ate.swing.ATEEditorKit;
35 import org.antlr.works.ate.swing.ATERenderingView;
36 import org.antlr.xjlib.appkit.undo.XJUndo;
37
38 import javax.swing.*;
39 import javax.swing.text.*;
40 import javax.swing.undo.AbstractUndoableEdit JavaDoc;
41 import java.awt.*;
42 import java.awt.event.KeyEvent JavaDoc;
43 import java.awt.event.MouseEvent JavaDoc;
44
45 public class ATETextPane extends JTextPane
46 {
47     public static final String JavaDoc ATTRIBUTE_CHARACTER_FOLDING_PROXY = "char_folding_proxy";
48     public static final String JavaDoc ATTRIBUTE_PARAGRAPH_FOLDING_PROXY = "para_folding_proxy";
49
50     protected ATEPanel textEditor;
51
52     private boolean writable = true;
53
54     protected boolean wrap = false;
55     protected boolean highlightCursorLine = false;
56     private int destinationCursorPosition = -1;
57
58     public ATETextPane(ATEPanel textEditor, StyledEditorKit editorKit) {
59         super(new DefaultStyledDocument());
60         setCaret(new ATECaret());
61         setEditorKit(editorKit==null?new ATEEditorKit(textEditor):editorKit);
62         this.textEditor = textEditor;
63     }
64
65     public void setWritable(boolean flag) {
66         this.writable = flag;
67     }
68
69     public boolean isWritable() {
70         return writable;
71     }
72
73     public void setWordWrap(boolean flag) {
74         this.wrap = flag;
75     }
76
77     public boolean getWordWrap() {
78         return wrap;
79     }
80
81     public void setHighlightCursorLine(boolean flag) {
82         this.highlightCursorLine = flag;
83     }
84
85     public boolean highlightCursorLine() {
86         return highlightCursorLine;
87     }
88
89     /** Override setFont() to apply the font to the coloring view
90      *
91      * @param f The font
92      */

93     public void setFont(Font f) {
94         super.setFont(f);
95         ATERenderingView.DEFAULT_FONT = f;
96     }
97
98     public void setTabSize(int size) {
99         getDocument().putProperty(PlainDocument.tabSizeAttribute, size);
100     }
101
102     public boolean getScrollableTracksViewportWidth() {
103         if(!wrap) {
104             Component parent = getParent();
105             return parent == null || getUI().getPreferredSize(this).width < parent.getSize().width;
106         } else
107             return super.getScrollableTracksViewportWidth();
108     }
109
110     public void setBounds(int x, int y, int width, int height) {
111         if(!wrap) {
112             Dimension size = this.getPreferredSize();
113             super.setBounds(x, y,
114                     Math.max(size.width, width), Math.max(size.height, height));
115         } else {
116             super.setBounds(x, y, width, height);
117         }
118     }
119
120     @Override JavaDoc
121     public void paintComponent(Graphics g) {
122         super.paintComponent(g);
123         paintDestinationCursor(g);
124         textEditor.textPaneDidPaint(g);
125     }
126
127     private void paintDestinationCursor(Graphics g) {
128         if(destinationCursorPosition < 0) return;
129
130         try {
131             Rectangle r = modelToView(destinationCursorPosition);
132             g.setColor(Color.black);
133             g.drawRect(r.x, r.y, r.width, r.height);
134         } catch (BadLocationException e) {
135             // Ignore
136
e.printStackTrace();
137         }
138     }
139
140     /**
141      * Paints the text area for printing. Make sure the caret and the current line background is not painted.
142      *
143      * @param g The Graphics context
144      */

145     public void printPaint(Graphics g) {
146         boolean flag = highlightCursorLine();
147         setHighlightCursorLine(false);
148         boolean caretVisible = getCaret().isVisible();
149         getCaret().setVisible(false);
150         paint(g);
151         getCaret().setVisible(caretVisible);
152         setHighlightCursorLine(flag);
153     }
154
155     @Override JavaDoc
156     protected void processKeyEvent(KeyEvent JavaDoc keyEvent) {
157         // If the document is not writable, emits a beep
158
if(writable) {
159             if(keyEvent.getKeyCode() == KeyEvent.VK_TAB && keyEvent.getID() == KeyEvent.KEY_PRESSED) {
160                 int start = getSelectionStart();
161                 int stop = getSelectionEnd();
162                 if(start != stop) {
163                     // Ident the lines covered by the selection
164
try {
165                         indentText(start, stop, keyEvent.isShiftDown()?-1:1);
166                         keyEvent.consume();
167                     } catch (BadLocationException e) {
168                         e.printStackTrace();
169                     }
170                 } else {
171                     super.processKeyEvent(keyEvent);
172                 }
173             } else {
174                 super.processKeyEvent(keyEvent);
175             }
176         } else {
177             if(keyEvent.isActionKey()) {
178                 super.processKeyEvent(keyEvent);
179             } else if((keyEvent.getModifiers() & KeyEvent.META_MASK) > 0) {
180                 super.processKeyEvent(keyEvent);
181             } else if((keyEvent.getModifiers() & KeyEvent.CTRL_MASK) > 0) {
182                 super.processKeyEvent(keyEvent);
183             } else {
184                 Toolkit.getDefaultToolkit().beep();
185             }
186         }
187     }
188
189     protected void indentText(int start, int stop, int direction) throws BadLocationException {
190         Element paragraph = getDocument().getDefaultRootElement();
191         final int contentCount = paragraph.getElementCount();
192         final String JavaDoc oldText = getText();
193
194         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(oldText);
195
196         int modified = 0;
197         for (int i=contentCount-1; i>=0; i--) {
198             Element e = paragraph.getElement(i);
199             int rangeStart = e.getStartOffset();
200             int rangeEnd = e.getEndOffset();
201             if(start >= rangeStart && start <= rangeEnd ||
202                     rangeStart >= start && rangeStart <= stop)
203             {
204                 if(direction == -1) {
205                     if(sb.charAt(rangeStart) == '\t') {
206                         sb.delete(rangeStart, rangeStart+1);
207                         modified++;
208                     }
209                 } else {
210                     sb.insert(rangeStart, "\t");
211                     modified++;
212                 }
213             }
214         }
215
216         if(modified == 0) return;
217
218         textEditor.disableUndo();
219         XJUndo undo = textEditor.getTextPaneUndo();
220         undo.addEditEvent(new UndoableRefactoringEdit(oldText, sb.toString()));
221         setText(sb.toString());
222         textEditor.enableUndo();
223
224         getCaret().setDot(start+direction);
225         if(modified > 1) {
226             getCaret().moveDot(stop+2*direction);
227         } else {
228             getCaret().moveDot(stop+direction);
229         }
230     }
231
232     protected class UndoableRefactoringEdit extends AbstractUndoableEdit JavaDoc {
233
234         public String JavaDoc oldContent;
235         public String JavaDoc newContent;
236
237         public UndoableRefactoringEdit(String JavaDoc oldContent, String JavaDoc newContent) {
238             this.oldContent = oldContent;
239             this.newContent = newContent;
240         }
241
242         public void redo() {
243             super.redo();
244             refactorReplaceEditorText(newContent);
245         }
246
247         public void undo() {
248             super.undo();
249             refactorReplaceEditorText(oldContent);
250         }
251
252         private void refactorReplaceEditorText(String JavaDoc text) {
253             int old = getCaretPosition();
254             textEditor.disableUndo();
255             setText(text);
256             textEditor.enableUndo();
257             setCaretPosition(Math.min(old, text.length()));
258         }
259     }
260
261     protected class ATECaret extends DefaultCaret {
262
263         public boolean selectingWord = false;
264         public boolean selectingLine = false;
265         public boolean draggingWord = false;
266         public boolean draggingLine = false;
267         public boolean startedDragging = false; //this is true after 'mousepressed' and while 'mousedragging'
268
public int mouseDraggingOffset;
269         public int selectionMovingLineBegin;
270         public int selectionMovingLineEnd;
271         public int selectionAnchorLineBegin;
272         public int selectionAnchorLineEnd;
273         public int selectionStart;
274         public int selectionEnd;
275         public int dragDropCursorPosition = 0;
276
277         public ATECaret() {
278             setBlinkRate(500);
279         }
280
281         public void paint(Graphics g) {
282             if(!isVisible())
283                 return;
284             try {
285                 Rectangle r = ATETextPane.this.modelToView(getDot());
286                 g.setColor(ATETextPane.this.getCaretColor());
287                 g.drawLine(r.x, r.y, r.x, r.y + r.height - 1);
288                 g.drawLine(r.x+1, r.y, r.x+1, r.y + r.height - 1);
289             }
290             catch (BadLocationException e) {
291                 // ignore
292
}
293         }
294
295         protected synchronized void damage(Rectangle r) {
296             if(r == null)
297                 return;
298
299             x = r.x;
300             y = r.y;
301             width = 2;
302             height = r.height;
303             repaint();
304         }
305
306         public void mouseClicked(MouseEvent JavaDoc e) {
307             // Do not call super if more than one click
308
// because it causes the word selection to deselect
309
if(e.getClickCount()<2 || !SwingUtilities.isLeftMouseButton(e)){
310                 super.mouseClicked(e);
311             }
312         }
313
314         public void mousePressed(MouseEvent JavaDoc e) {
315             if(!isWritable() || e.getButton() != MouseEvent.BUTTON1) {
316                 super.mousePressed(e);
317                 return;
318             }
319
320             draggingWord =false;
321             draggingLine =false;
322             selectingWord=false;
323             selectingLine=false;
324             startedDragging=false;
325
326             if(e.getClickCount()%2 == 0) {
327                 selectWord();
328                 selectingWord = true;
329                 selectionStart = getSelectionStart();
330                 selectionEnd = getSelectionEnd();
331                 setDragEnabled(false);
332                 e.consume();
333                 return;
334             }
335             if(e.getClickCount()%3 == 0) {
336                 selectLine();
337                 selectingLine = true;
338                 selectionStart = getSelectionStart();
339                 selectionEnd = getSelectionEnd();
340                 selectionMovingLineBegin=selectionStart;
341                 selectionAnchorLineBegin=selectionStart;
342                 selectionMovingLineEnd=selectionEnd;
343                 selectionAnchorLineEnd=selectionEnd;
344                 setDragEnabled(false);
345                 e.consume();
346                 return;
347             }
348
349             selectionStart = getSelectionStart();
350             selectionEnd = getSelectionEnd();
351             int mouseCharIndex = viewToModel(e.getPoint());
352             if(selectionStart!=selectionEnd&&e.getClickCount()==1&&
353                     mouseCharIndex>=selectionStart&&mouseCharIndex<=selectionEnd){
354                 String JavaDoc s = getText();
355                 if ((selectionStart==0||s.charAt(selectionStart-1)=='\n')&&s.charAt(selectionEnd-1)=='\n'){
356                     draggingLine=true;
357                 } else {
358                     draggingWord=true;
359                 }
360                 mouseDraggingOffset=mouseCharIndex-selectionStart;
361             }
362             // Call super only after handling the double-click otherwise the current
363
// caret position will be already moved due to the super() selection.
364
if (!draggingWord&&!draggingLine)
365                 super.mousePressed(e);
366         }
367
368         public void mouseReleased(MouseEvent JavaDoc e) {
369             if(!isWritable() || e.getButton() != MouseEvent.BUTTON1) {
370                 super.mouseReleased(e);
371                 return;
372             }
373
374             if (draggingWord||draggingLine){
375                 //the text had been selected, so let the regular click happen
376
endDestinationCursor();
377                 super.mousePressed(e);
378             }
379             if (startedDragging){
380                 super.mousePressed(e);
381             }
382             if (draggingWord){
383                 moveSelectionWord(e);
384             }else if (draggingLine){
385                 moveSelectionLine(e);
386             }
387             //all need to release
388
super.mouseReleased(e);
389         }
390
391         public void mouseDragged(MouseEvent JavaDoc e) {
392             if(!isWritable() || e.getButton() != MouseEvent.BUTTON1) {
393                 super.mouseDragged(e);
394                 return;
395             }
396                   
397             //if it's being dragged, paint the selection permanent, and show a cursor where dragging to
398
if (draggingWord ||draggingLine){
399                 dragDropCursorPosition = viewToModel(e.getPoint());
400                 if (!startedDragging){
401                     startedDragging=true;
402                     beginDestinationCursor();
403                 }
404             }
405             if(selectingWord) {
406                 extendSelectionWord(e);
407                 ATETextPane.this.repaint();
408             } else if (selectingLine){
409                 extendSelectionLine(e);
410             } else if (draggingWord){
411                 setDestinationCursorPosition(viewToModel(e.getPoint()));
412             } else if (draggingLine){
413                 int mouseCharIndex = viewToModel(e.getPoint());
414                 int lineBegin = findBeginningLineBoundary(mouseCharIndex);
415                 setDestinationCursorPosition(lineBegin);
416             } else{
417                 super.mouseDragged(e);
418             }
419             // Repaint to avoid leaving trace with the underlying line highlighting
420
ATETextPane.this.repaint();
421         }
422
423         /**
424          * Sets the position of an overlay cursor. It is used mostly to display the destination
425          * when a selection is dragged to another location in the text.
426          *
427          * To disable this cursor, set pos to -1.
428          *
429          * @param pos Position to show. To hide, use -1
430          */

431         private void setDestinationCursorPosition(int pos) {
432             destinationCursorPosition = pos;
433             if(destinationCursorPosition != -1) {
434                 try {
435                     // Make sure to scroll the text in order for the destination cursor to be always visible
436
ATETextPane.this.scrollRectToVisible(modelToView(pos));
437                 } catch (BadLocationException e) {
438                     // ignore
439
}
440             }
441             ATETextPane.this.repaint();
442         }
443
444         private void beginDestinationCursor() {
445             // Disable Swing d&d to avoid conflict between Swing and our own drag stuff
446
setDragEnabled(false);
447             setVisible(false);
448         }
449
450         private void endDestinationCursor() {
451             setDragEnabled(true);
452             setVisible(true);
453             setDestinationCursorPosition(-1);
454         }
455
456         private void moveSelectionLine(MouseEvent JavaDoc e) {
457             int mouseCharIndex = viewToModel(e.getPoint());
458             StyledDocument doc = getStyledDocument();
459             textEditor.getTextPaneUndo().beginUndoGroup("moveSelectionLine");
460             try{
461                 if(mouseCharIndex > selectionEnd) {
462                     int moveTo = findBeginningLineBoundary(mouseCharIndex);
463                     int offset = moveTo-selectionEnd;
464                     String JavaDoc s = doc.getText(selectionEnd, offset);
465                     doc.remove(selectionEnd,offset);
466                     doc.insertString(selectionStart,s,null);
467                     selectionEnd = selectionEnd+offset;
468                     selectionStart = selectionStart+offset;
469                 }
470                 else if(mouseCharIndex < selectionStart) {
471                     int moveFrom = Math.max(0,findBeginningLineBoundary(mouseCharIndex));
472                     int offset = selectionStart - moveFrom;
473                     String JavaDoc s = doc.getText(moveFrom, offset);
474                     doc.insertString(selectionEnd,s,null);
475                     doc.remove(moveFrom,offset);
476                     selectionEnd = selectionEnd-offset;
477                     selectionStart = selectionStart-offset;
478                 }
479             }catch(BadLocationException ex){System.out.println(ex);}
480             textEditor.getTextPaneUndo().endUndoGroup();
481         }
482
483         private void moveSelectionWord(MouseEvent JavaDoc e) {
484             int mouseCharIndex = viewToModel(e.getPoint());
485             StyledDocument doc = getStyledDocument();
486             textEditor.getTextPaneUndo().beginUndoGroup("moveSelectionWord");
487             try{
488                 if(mouseCharIndex > selectionEnd) {
489                     int offset = mouseCharIndex-selectionEnd;
490                     String JavaDoc s = doc.getText(selectionEnd, offset);
491                     doc.remove(selectionEnd,offset);
492                     doc.insertString(selectionStart,s,null);
493                     selectionEnd = selectionEnd+offset;
494                     selectionStart = selectionStart+offset;
495                 }
496                 else if(mouseCharIndex < selectionStart) {
497                     mouseCharIndex = Math.max(0,mouseCharIndex);
498                     int offset = selectionStart - mouseCharIndex;
499                     String JavaDoc s = doc.getText(mouseCharIndex, offset);
500                     doc.insertString(selectionEnd,s,null);
501                     doc.remove(mouseCharIndex,offset);
502                     selectionEnd = selectionEnd-offset;
503                     selectionStart = selectionStart-offset;
504                 }
505             }catch(BadLocationException ex){System.out.println(ex);}
506             textEditor.getTextPaneUndo().endUndoGroup();
507         }
508
509         public void extendSelectionWord(MouseEvent JavaDoc e) {
510             int mouseCharIndex = viewToModel(e.getPoint());
511
512             if(mouseCharIndex > selectionEnd) {
513                 int npos = findNextWordBoundary(mouseCharIndex);
514                 if(npos > selectionEnd)
515                     select(selectionStart, npos);
516             } else if(mouseCharIndex < selectionStart) {
517                 int npos = findPrevWordBoundary(mouseCharIndex);
518                 if(npos < selectionStart)
519                     select(Math.max(0, npos), selectionEnd);
520             } else
521                 select(selectionStart, selectionEnd);
522         }
523
524         public void extendSelectionLine(MouseEvent JavaDoc e) {
525             int mouseCharIndex = viewToModel(e.getPoint());
526             if(mouseCharIndex > selectionMovingLineEnd) {
527                 int npos = findEndLineBoundary(mouseCharIndex);
528                 if(npos > selectionMovingLineEnd){
529                     selectionMovingLineEnd = npos;
530                     selectionMovingLineBegin = findBeginningLineBoundary(mouseCharIndex);
531                     selectionStart = Math.min(selectionMovingLineBegin,selectionAnchorLineBegin);
532                     selectionEnd = Math.max(selectionMovingLineEnd,selectionAnchorLineEnd);
533                     select(selectionStart, selectionEnd);
534                 }
535             } else if(mouseCharIndex < selectionMovingLineBegin) {
536                 int npos = Math.max(0,findBeginningLineBoundary(mouseCharIndex));
537                 if(npos < selectionMovingLineBegin){
538                     selectionMovingLineBegin = npos;
539                     selectionMovingLineEnd = findEndLineBoundary(mouseCharIndex);
540                     selectionEnd = Math.max(selectionMovingLineEnd,selectionAnchorLineEnd);
541                     selectionStart = Math.min(selectionMovingLineBegin,selectionAnchorLineBegin);
542                     select(selectionStart, selectionEnd);
543                 }
544             } else{ //it's somewhere between the selection, so move the line up or down if needed
545
select(selectionStart, selectionEnd);
546             }
547         }
548
549         public void selectWord() {
550             int p = getCaretPosition();
551
552             setCaretPosition(findPrevWordBoundary(p));
553             moveCaretPosition(findNextWordBoundary(p));
554         }
555         public void selectLine() {
556             int p = getCaretPosition();
557
558             setCaretPosition(findBeginningLineBoundary(p));
559             moveCaretPosition(findEndLineBoundary(p));
560         }
561
562         private int findBeginningLineBoundary(int pos) {
563             int index = pos-1;
564             String JavaDoc s = getText();
565             while(index >= 0 && s.charAt(index)!='\n') {
566                 index--;
567             }
568             return index +1;
569         }
570
571         private int findEndLineBoundary(int pos) {
572             int index = pos;
573             String JavaDoc s = getText();
574             while(index < s.length() && s.charAt(index)!='\n') {
575                 index++;
576             }
577             return index+1;
578         }
579
580         public int findPrevWordBoundary(int pos) {
581             int index = pos-1;
582             String JavaDoc s = getText();
583             while(index >= 0 && isWordChar(s.charAt(index))) {
584                 index--;
585             }
586             return index +1;
587         }
588
589         public int findNextWordBoundary(int pos) {
590             int index = pos;
591             String JavaDoc s = getText();
592             while(index < s.length() && isWordChar(s.charAt(index))) {
593                 index++;
594             }
595             return index;
596         }
597
598         public boolean isWordChar(char c) {
599             return Character.isLetterOrDigit(c) || c == '_';
600         }
601     }
602
603 }
604
Popular Tags