1 19 20 package org.netbeans.modules.ruby.rhtml; 21 22 import java.awt.datatransfer.DataFlavor ; 23 import java.awt.datatransfer.Transferable ; 24 import java.awt.datatransfer.UnsupportedFlavorException ; 25 import java.awt.event.ActionEvent ; 26 import java.awt.im.InputContext ; 27 import java.io.ByteArrayInputStream ; 28 import java.io.IOException ; 29 import java.io.InputStream ; 30 import java.io.Reader ; 31 import java.io.StringReader ; 32 import java.io.StringWriter ; 33 import javax.swing.Action ; 34 import javax.swing.JComponent ; 35 import javax.swing.JEditorPane ; 36 import javax.swing.JPasswordField ; 37 import javax.swing.TransferHandler ; 38 import javax.swing.plaf.UIResource ; 39 import javax.swing.text.BadLocationException ; 40 import javax.swing.text.Caret ; 41 import javax.swing.text.Document ; 42 import javax.swing.text.EditorKit ; 43 import javax.swing.text.Position ; 44 import javax.swing.text.TextAction ; 45 import javax.swing.text.JTextComponent ; 46 import org.netbeans.api.lexer.Language; 47 import org.netbeans.editor.*; 48 import org.netbeans.editor.ext.*; 49 import org.netbeans.modules.editor.NbEditorDocument; 50 import org.netbeans.modules.editor.NbEditorKit; 51 52 58 59 public class RhtmlKit extends NbEditorKit implements org.openide.util.HelpCtx.Provider { 60 61 public org.openide.util.HelpCtx getHelpCtx() { 62 return new org.openide.util.HelpCtx(RhtmlKit.class); 63 } 64 65 static final long serialVersionUID =-1381945567613910297L; 66 67 public static final String RHTML_MIME_TYPE = "application/x-httpd-eruby"; 69 public static final String shiftInsertBreakAction = "shift-insert-break"; 71 public static final String collapseAllCommentsAction = "collapse-all-comment-folds"; 74 public static final String expandAllCommentsAction = "expand-all-comment-folds"; 76 private static boolean setupReadersInitialized = false; 77 78 private static final boolean J2EE_LEXER_COLORING = true; 81 82 public RhtmlKit(){ 83 } 88 89 public String getContentType() { 90 return RHTML_MIME_TYPE; 91 } 92 93 public CompletionJavaDoc createCompletionJavaDoc(ExtEditorUI extEditorUI) { 94 return null; 95 } 96 97 protected void initDocument(BaseDocument doc) { 98 } 100 101 105 114 115 public Completion createCompletion(ExtEditorUI extEditorUI) { 116 return null; 117 } 118 119 126 129 public Document createDefaultDocument() { 130 Document doc = new RhtmlEditorDocument(this.getClass()); 131 Object mimeType = doc.getProperty("mimeType"); if (mimeType == null){ 133 doc.putProperty("mimeType", getContentType()); } 135 doc.putProperty(Language.class, RhtmlTokenId.language()); 136 return doc; 137 } 138 139 144 145 public void install(javax.swing.JEditorPane c) { 146 super.install(c); 147 c.setTransferHandler(new HTMLTransferHandler()); 148 } 149 150 protected Action [] createActions() { 151 Action [] HTMLActions = new Action [] { 152 new HTMLDefaultKeyTypedAction(), 153 new HTMLDeleteCharAction(deletePrevCharAction, false), 154 new HTMLDeleteCharAction(deleteNextCharAction, true), 155 new HTMLShiftBreakAction(), 156 new MatchBraceAction(ExtKit.matchBraceAction, false), 158 new MatchBraceAction(ExtKit.selectionMatchBraceAction, true), 159 }; 163 return TextAction.augmentList(super.createActions(), HTMLActions); 164 } 165 166 public static class HTMLDefaultKeyTypedAction extends ExtDefaultKeyTypedAction { 167 168 protected void insertString(BaseDocument doc, int dotPos, 169 Caret caret, String str, 170 boolean overwrite) throws BadLocationException { 171 super.insertString(doc, dotPos, caret, str, overwrite); 172 } 174 175 } 176 177 public static class HTMLDeleteCharAction extends DeleteCharAction { 178 179 public HTMLDeleteCharAction(String name, boolean nextChar) { 180 super(name, nextChar); 181 } 182 183 protected void charBackspaced(BaseDocument doc, int dotPos, Caret caret, char ch) throws BadLocationException { 184 super.charBackspaced(doc, dotPos, caret, ch); 185 } 187 } 188 189 190 public static class HTMLShiftBreakAction extends BaseAction { 191 192 static final long serialVersionUID =4004043376345356061L; 193 194 public HTMLShiftBreakAction() { 195 super( shiftInsertBreakAction, ABBREV_RESET 196 | MAGIC_POSITION_RESET | UNDO_MERGE_RESET); 197 } 198 199 public void actionPerformed(ActionEvent evt, JTextComponent target) { 200 if (target != null) { 201 Completion completion = ExtUtilities.getCompletion(target); 202 if (completion != null && completion.isPaneVisible()) { 203 if (completion.substituteText( true )) { 204 } else { 206 completion.refresh(false); 207 } 208 } 209 } 210 } 211 212 } 213 214 216 public static class MatchBraceAction extends ExtKit.MatchBraceAction { 217 218 private boolean select; 220 public MatchBraceAction(String name, boolean select) { 221 super(name, select); 222 this.select = select; 223 } 224 225 public void actionPerformed(ActionEvent evt, JTextComponent target) { 226 if (target != null) { 227 try { 228 Caret caret = target.getCaret(); 229 BaseDocument doc = Utilities.getDocument(target); 230 int dotPos = caret.getDot(); 231 ExtSyntaxSupport sup = (ExtSyntaxSupport)doc.getSyntaxSupport(); 232 233 if (dotPos > 0) { 235 int[] matchBlk = sup.findMatchingBlock(dotPos - 1, false); 236 if (matchBlk != null) { 237 if (select) { 238 caret.moveDot(matchBlk[1]); 239 } else { 240 caret.setDot(matchBlk[1]); 241 } 242 } 243 244 } else{ super.actionPerformed(evt, target); 246 } 247 248 } catch (BadLocationException e) { 249 target.getToolkit().beep(); 250 } 251 } 252 } 253 } 254 255 292 293 294 295 302 static class HTMLTransferHandler extends TransferHandler implements UIResource { 303 304 private JTextComponent exportComp; 305 private boolean shouldRemove; 306 private int p0; 307 private int p1; 308 309 321 protected DataFlavor getImportFlavor(DataFlavor [] flavors, JTextComponent c) { 322 DataFlavor plainFlavor = null; 323 DataFlavor refFlavor = null; 324 DataFlavor stringFlavor = null; 325 326 if (c instanceof JEditorPane ) { 327 for (int i = 0; i < flavors.length; i++) { 328 String mime = flavors[i].getMimeType(); 329 if (mime.startsWith(((JEditorPane )c).getEditorKit().getContentType())) { 330 } else if (plainFlavor == null && mime.startsWith("text/plain")) { plainFlavor = flavors[i]; 333 } else if (refFlavor == null && mime.startsWith("application/x-java-jvm-local-objectref") && flavors[i].getRepresentationClass() == java.lang.String .class) { 335 refFlavor = flavors[i]; 336 } else if (stringFlavor == null && flavors[i].equals(DataFlavor.stringFlavor)) { 337 stringFlavor = flavors[i]; 338 } 339 } 340 if (plainFlavor != null) { 341 return plainFlavor; 342 } else if (refFlavor != null) { 343 return refFlavor; 344 } else if (stringFlavor != null) { 345 return stringFlavor; 346 } 347 return null; 348 } 349 350 351 for (int i = 0; i < flavors.length; i++) { 352 String mime = flavors[i].getMimeType(); 353 if (mime.startsWith("text/plain")) { return flavors[i]; 355 } else if (refFlavor == null && mime.startsWith("application/x-java-jvm-local-objectref") && flavors[i].getRepresentationClass() == java.lang.String .class) { 357 refFlavor = flavors[i]; 358 } else if (stringFlavor == null && flavors[i].equals(DataFlavor.stringFlavor)) { 359 stringFlavor = flavors[i]; 360 } 361 } 362 if (refFlavor != null) { 363 return refFlavor; 364 } else if (stringFlavor != null) { 365 return stringFlavor; 366 } 367 return null; 368 } 369 370 373 protected void handleReaderImport(Reader in, JTextComponent c, boolean useRead) 374 throws BadLocationException , IOException { 375 if (useRead) { 376 int startPosition = c.getSelectionStart(); 377 int endPosition = c.getSelectionEnd(); 378 int length = endPosition - startPosition; 379 EditorKit kit = c.getUI().getEditorKit(c); 380 Document doc = c.getDocument(); 381 if (length > 0) { 382 doc.remove(startPosition, length); 383 } 384 kit.read(in, doc, startPosition); 385 } else { 386 char[] buff = new char[1024]; 387 int nch; 388 boolean lastWasCR = false; 389 int last; 390 StringBuffer sbuff = null; 391 392 while ((nch = in.read(buff, 0, buff.length)) != -1) { 395 if (sbuff == null) { 396 sbuff = new StringBuffer (nch); 397 } 398 last = 0; 399 for(int counter = 0; counter < nch; counter++) { 400 switch(buff[counter]) { 401 case '\r': 402 if (lastWasCR) { 403 if (counter == 0) { 404 sbuff.append('\n'); 405 } else { 406 buff[counter - 1] = '\n'; 407 } 408 } else { 409 lastWasCR = true; 410 } 411 break; 412 case '\n': 413 if (lastWasCR) { 414 if (counter > (last + 1)) { 415 sbuff.append(buff, last, counter - last - 1); 416 } 417 lastWasCR = false; 420 last = counter; 421 } 422 break; 423 default: 424 if (lastWasCR) { 425 if (counter == 0) { 426 sbuff.append('\n'); 427 } else { 428 buff[counter - 1] = '\n'; 429 } 430 lastWasCR = false; 431 } 432 break; 433 } 434 } 435 if (last < nch) { 436 if (lastWasCR) { 437 if (last < (nch - 1)) { 438 sbuff.append(buff, last, nch - last - 1); 439 } 440 } else { 441 sbuff.append(buff, last, nch - last); 442 } 443 } 444 } 445 if (lastWasCR) { 446 sbuff.append('\n'); 447 } 448 c.replaceSelection(sbuff != null ? sbuff.toString() : ""); } 450 } 451 452 454 466 public int getSourceActions(JComponent c) { 467 int actions = NONE; 468 if (! (c instanceof JPasswordField )) { 469 if (((JTextComponent )c).isEditable()) { 470 actions = COPY_OR_MOVE; 471 } else { 472 actions = COPY; 473 } 474 } 475 return actions; 476 } 477 478 487 protected Transferable createTransferable(JComponent comp) { 488 exportComp = (JTextComponent )comp; 489 shouldRemove = true; 490 p0 = exportComp.getSelectionStart(); 491 p1 = exportComp.getSelectionEnd(); 492 return (p0 != p1) ? (new HTMLTransferable(exportComp, p0, p1)) : null; 493 } 494 495 504 protected void exportDone(JComponent source, Transferable data, int action) { 505 if (shouldRemove && action == MOVE) { 508 HTMLTransferable t = (HTMLTransferable)data; 509 t.removeText(); 510 } 511 512 exportComp = null; 513 } 514 515 526 public boolean importData(JComponent comp, Transferable t) { 527 JTextComponent c = (JTextComponent )comp; 528 529 if (c == exportComp && c.getCaretPosition() >= p0 && c.getCaretPosition() <= p1) { 534 shouldRemove = false; 535 return true; 536 } 537 538 boolean imported = false; 539 DataFlavor importFlavor = getImportFlavor(t.getTransferDataFlavors(), c); 540 if (importFlavor != null) { 541 try { 542 boolean useRead = false; 543 if (comp instanceof JEditorPane ) { 544 JEditorPane ep = (JEditorPane )comp; 545 if (!ep.getContentType().startsWith("text/plain") && importFlavor.getMimeType().startsWith(ep.getContentType())) { 547 useRead = true; 548 } 549 } 550 InputContext ic = c.getInputContext(); 551 if (ic != null) { 552 ic.endComposition(); 553 } 554 Reader r = importFlavor.getReaderForText(t); 555 handleReaderImport(r, c, useRead); 556 imported = true; 557 } catch (UnsupportedFlavorException ufe) { 558 } catch (BadLocationException ble) { 560 } catch (IOException ioe) { 562 } 564 } 565 return imported; 566 } 567 568 578 public boolean canImport(JComponent comp, DataFlavor [] flavors) { 579 JTextComponent c = (JTextComponent )comp; 580 if (!(c.isEditable() && c.isEnabled())) { 581 return false; 582 } 583 return (getImportFlavor(flavors, c) != null); 584 } 585 586 595 static class HTMLTransferable extends BasicTransferable { 596 597 HTMLTransferable(JTextComponent c, int start, int end) { 598 super(null, null); 599 600 this.c = c; 601 602 Document doc = c.getDocument(); 603 604 try { 605 p0 = doc.createPosition(start); 606 p1 = doc.createPosition(end); 607 608 plainData = c.getSelectedText(); 609 610 if (c instanceof JEditorPane ) { 611 JEditorPane ep = (JEditorPane )c; 612 613 mimeType = ep.getContentType(); 614 615 if (mimeType.startsWith("text/plain")) { return; 617 } 618 619 StringWriter sw = new StringWriter (p1.getOffset() - p0.getOffset()); 620 ep.getEditorKit().write(sw, doc, p0.getOffset(), p1.getOffset() - p0.getOffset()); 621 622 if (mimeType.startsWith("text/html")) { htmlData = sw.toString(); 624 } else { 625 richText = sw.toString(); 626 } 627 } 628 } catch (BadLocationException ble) { 629 } catch (IOException ioe) { 630 } 631 } 632 633 void removeText() { 634 if ((p0 != null) && (p1 != null) && (p0.getOffset() != p1.getOffset())) { 635 try { 636 Document doc = c.getDocument(); 637 doc.remove(p0.getOffset(), p1.getOffset() - p0.getOffset()); 638 } catch (BadLocationException e) { 639 } 640 } 641 } 642 643 645 649 protected DataFlavor [] getRicherFlavors() { 650 if (richText == null) { 651 return null; 652 } 653 654 try { 655 DataFlavor [] flavors = new DataFlavor [3]; 656 flavors[0] = new DataFlavor (mimeType + ";class=java.lang.String"); flavors[1] = new DataFlavor (mimeType + ";class=java.io.Reader"); flavors[2] = new DataFlavor (mimeType + ";class=java.io.InputStream;charset=unicode"); return flavors; 660 } catch (ClassNotFoundException cle) { 661 } 663 664 return null; 665 } 666 667 670 protected Object getRicherData(DataFlavor flavor) throws UnsupportedFlavorException { 671 if (richText == null) { 672 return null; 673 } 674 675 if (String .class.equals(flavor.getRepresentationClass())) { 676 return richText; 677 } else if (Reader .class.equals(flavor.getRepresentationClass())) { 678 return new StringReader (richText); 679 } else if (InputStream .class.equals(flavor.getRepresentationClass())) { 680 return new ByteArrayInputStream (richText.getBytes()); 681 } 682 throw new UnsupportedFlavorException (flavor); 683 } 684 685 Position p0; 686 Position p1; 687 String mimeType; 688 String richText; 689 JTextComponent c; 690 } 691 692 } 693 694 private static class BasicTransferable implements Transferable , UIResource { 695 696 protected String plainData; 697 protected String htmlData; 698 699 private static DataFlavor [] htmlFlavors; 700 private static DataFlavor [] stringFlavors; 701 private static DataFlavor [] plainFlavors; 702 703 static { 704 try { 705 htmlFlavors = new DataFlavor [3]; 706 htmlFlavors[0] = new DataFlavor ("text/html;class=java.lang.String"); htmlFlavors[1] = new DataFlavor ("text/html;class=java.io.Reader"); htmlFlavors[2] = new DataFlavor ("text/html;charset=unicode;class=java.io.InputStream"); 710 plainFlavors = new DataFlavor [3]; 711 plainFlavors[0] = new DataFlavor ("text/plain;class=java.lang.String"); plainFlavors[1] = new DataFlavor ("text/plain;class=java.io.Reader"); plainFlavors[2] = new DataFlavor ("text/plain;charset=unicode;class=java.io.InputStream"); 715 stringFlavors = new DataFlavor [2]; 716 stringFlavors[0] = new DataFlavor (DataFlavor.javaJVMLocalObjectMimeType+";class=java.lang.String"); stringFlavors[1] = DataFlavor.stringFlavor; 718 719 } catch (ClassNotFoundException cle) { 720 System.err.println("error initializing javax.swing.plaf.basic.BasicTranserable"); } 722 } 723 724 public BasicTransferable(String plainData, String htmlData) { 725 this.plainData = plainData; 726 this.htmlData = htmlData; 727 } 728 729 730 736 public DataFlavor [] getTransferDataFlavors() { 737 DataFlavor [] richerFlavors = getRicherFlavors(); 738 int nRicher = (richerFlavors != null) ? richerFlavors.length : 0; 739 int nHTML = (isHTMLSupported()) ? htmlFlavors.length : 0; 740 int nPlain = (isPlainSupported()) ? plainFlavors.length: 0; 741 int nString = (isPlainSupported()) ? stringFlavors.length : 0; 742 int nFlavors = nRicher + nHTML + nPlain + nString; 743 DataFlavor [] flavors = new DataFlavor [nFlavors]; 744 745 int nDone = 0; 747 if (nRicher > 0) { 748 System.arraycopy(richerFlavors, 0, flavors, nDone, nRicher); 749 nDone += nRicher; 750 } 751 if (nHTML > 0) { 752 System.arraycopy(htmlFlavors, 0, flavors, nDone, nHTML); 753 nDone += nHTML; 754 } 755 if (nPlain > 0) { 756 System.arraycopy(plainFlavors, 0, flavors, nDone, nPlain); 757 nDone += nPlain; 758 } 759 if (nString > 0) { 760 System.arraycopy(stringFlavors, 0, flavors, nDone, nString); 761 nDone += nString; 762 } 763 return flavors; 764 } 765 766 772 public boolean isDataFlavorSupported(DataFlavor flavor) { 773 DataFlavor [] flavors = getTransferDataFlavors(); 774 for (int i = 0; i < flavors.length; i++) { 775 if (flavors[i].equals(flavor)) { 776 return true; 777 } 778 } 779 return false; 780 } 781 782 793 public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException , IOException { 794 DataFlavor [] richerFlavors = getRicherFlavors(); 795 if (isRicherFlavor(flavor)) { 796 return getRicherData(flavor); 797 } else if (isHTMLFlavor(flavor)) { 798 String data = getHTMLData(); 799 data = (data == null) ? "" : data; if (String .class.equals(flavor.getRepresentationClass())) { 801 return data; 802 } else if (Reader .class.equals(flavor.getRepresentationClass())) { 803 return new StringReader (data); 804 } else if (InputStream .class.equals(flavor.getRepresentationClass())) { 805 return new ByteArrayInputStream (data.getBytes()); 806 } 807 } else if (isPlainFlavor(flavor)) { 809 String data = getPlainData(); 810 data = (data == null) ? "" : data; 811 if (String .class.equals(flavor.getRepresentationClass())) { 812 return data; 813 } else if (Reader .class.equals(flavor.getRepresentationClass())) { 814 return new StringReader (data); 815 } else if (InputStream .class.equals(flavor.getRepresentationClass())) { 816 return new ByteArrayInputStream (data.getBytes()); 817 } 818 820 } else if (isStringFlavor(flavor)) { 821 String data = getPlainData(); 822 data = (data == null) ? "" : data; return data; 824 } 825 throw new UnsupportedFlavorException (flavor); 826 } 827 828 830 protected boolean isRicherFlavor(DataFlavor flavor) { 831 DataFlavor [] richerFlavors = getRicherFlavors(); 832 int nFlavors = (richerFlavors != null) ? richerFlavors.length : 0; 833 for (int i = 0; i < nFlavors; i++) { 834 if (richerFlavors[i].equals(flavor)) { 835 return true; 836 } 837 } 838 return false; 839 } 840 841 846 protected DataFlavor [] getRicherFlavors() { 847 return null; 848 } 849 850 protected Object getRicherData(DataFlavor flavor) throws UnsupportedFlavorException { 851 return null; 852 } 853 854 856 862 protected boolean isHTMLFlavor(DataFlavor flavor) { 863 DataFlavor [] flavors = htmlFlavors; 864 for (int i = 0; i < flavors.length; i++) { 865 if (flavors[i].equals(flavor)) { 866 return true; 867 } 868 } 869 return false; 870 } 871 872 876 protected boolean isHTMLSupported() { 877 return htmlData != null; 878 } 879 880 883 protected String getHTMLData() { 884 return htmlData; 885 } 886 887 889 895 protected boolean isPlainFlavor(DataFlavor flavor) { 896 DataFlavor [] flavors = plainFlavors; 897 for (int i = 0; i < flavors.length; i++) { 898 if (flavors[i].equals(flavor)) { 899 return true; 900 } 901 } 902 return false; 903 } 904 905 909 protected boolean isPlainSupported() { 910 return plainData != null; 911 } 912 913 916 protected String getPlainData() { 917 return plainData; 918 } 919 920 922 928 protected boolean isStringFlavor(DataFlavor flavor) { 929 DataFlavor [] flavors = stringFlavors; 930 for (int i = 0; i < flavors.length; i++) { 931 if (flavors[i].equals(flavor)) { 932 return true; 933 } 934 } 935 return false; 936 } 937 938 939 } 940 941 943 public class RhtmlEditorDocument extends NbEditorDocument { 944 public RhtmlEditorDocument(Class kitClass) { 945 super(kitClass); 946 } 947 948 public boolean addLayer(DrawLayer layer, int visibility) { 949 if(!(layer instanceof DrawLayerFactory.SyntaxLayer)) { 951 return super.addLayer(layer, visibility); 952 } else { 953 return false; 954 } 955 } 956 } 957 958 959 } 960 961 | Popular Tags |