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 }